dpp/state_transition/state_transitions/shielded/
common_validation.rs1use crate::consensus::basic::state_transition::{
2 ShieldedEmptyProofError, ShieldedEncryptedNoteSizeMismatchError, ShieldedNoActionsError,
3 ShieldedTooManyActionsError, ShieldedZeroAnchorError,
4};
5use crate::consensus::basic::BasicError;
6use crate::shielded::SerializedAction;
7use crate::validation::SimpleConsensusValidationResult;
8
9pub const ENCRYPTED_NOTE_SIZE: usize = 216;
13
14pub fn validate_actions_count(
16 actions: &[SerializedAction],
17 max_actions: u16,
18) -> SimpleConsensusValidationResult {
19 if actions.is_empty() {
20 SimpleConsensusValidationResult::new_with_error(
21 BasicError::ShieldedNoActionsError(ShieldedNoActionsError::new()).into(),
22 )
23 } else if actions.len() > max_actions as usize {
24 SimpleConsensusValidationResult::new_with_error(
25 BasicError::ShieldedTooManyActionsError(ShieldedTooManyActionsError::new(
26 actions.len().min(u16::MAX as usize) as u16,
27 max_actions,
28 ))
29 .into(),
30 )
31 } else {
32 SimpleConsensusValidationResult::new()
33 }
34}
35
36pub fn validate_proof_not_empty(proof: &[u8]) -> SimpleConsensusValidationResult {
38 if proof.is_empty() {
39 SimpleConsensusValidationResult::new_with_error(
40 BasicError::ShieldedEmptyProofError(ShieldedEmptyProofError::new()).into(),
41 )
42 } else {
43 SimpleConsensusValidationResult::new()
44 }
45}
46
47pub fn validate_anchor_not_zero(anchor: &[u8; 32]) -> SimpleConsensusValidationResult {
49 if *anchor == [0u8; 32] {
50 SimpleConsensusValidationResult::new_with_error(
51 BasicError::ShieldedZeroAnchorError(ShieldedZeroAnchorError::new()).into(),
52 )
53 } else {
54 SimpleConsensusValidationResult::new()
55 }
56}
57
58pub fn validate_encrypted_note_sizes(
62 actions: &[SerializedAction],
63) -> SimpleConsensusValidationResult {
64 for action in actions {
65 if action.encrypted_note.len() != ENCRYPTED_NOTE_SIZE {
66 return SimpleConsensusValidationResult::new_with_error(
67 BasicError::ShieldedEncryptedNoteSizeMismatchError(
68 ShieldedEncryptedNoteSizeMismatchError::new(
69 ENCRYPTED_NOTE_SIZE as u32,
70 action.encrypted_note.len() as u32,
71 ),
72 )
73 .into(),
74 );
75 }
76 }
77 SimpleConsensusValidationResult::new()
78}
79
80#[cfg(test)]
81mod tests {
82 use super::*;
83 use crate::consensus::ConsensusError;
84 use assert_matches::assert_matches;
85
86 fn dummy_action() -> SerializedAction {
87 SerializedAction {
88 nullifier: [1u8; 32],
89 rk: [2u8; 32],
90 cmx: [3u8; 32],
91 encrypted_note: vec![4u8; 216],
92 cv_net: [5u8; 32],
93 spend_auth_sig: [6u8; 64],
94 }
95 }
96
97 #[test]
100 fn validate_actions_count_should_reject_empty_actions() {
101 let result = validate_actions_count(&[], 100);
102 assert_matches!(
103 result.errors.as_slice(),
104 [ConsensusError::BasicError(
105 BasicError::ShieldedNoActionsError(_)
106 )]
107 );
108 }
109
110 #[test]
111 fn validate_actions_count_should_accept_single_action() {
112 let actions = vec![dummy_action()];
113 let result = validate_actions_count(&actions, 100);
114 assert!(
115 result.is_valid(),
116 "Expected valid, got: {:?}",
117 result.errors
118 );
119 }
120
121 #[test]
122 fn validate_actions_count_should_accept_exactly_max_actions() {
123 let actions = vec![dummy_action(); 5];
124 let result = validate_actions_count(&actions, 5);
125 assert!(
126 result.is_valid(),
127 "Expected valid, got: {:?}",
128 result.errors
129 );
130 }
131
132 #[test]
133 fn validate_actions_count_should_reject_more_than_max_actions() {
134 let actions = vec![dummy_action(); 6];
135 let result = validate_actions_count(&actions, 5);
136 assert_matches!(
137 result.errors.as_slice(),
138 [ConsensusError::BasicError(
139 BasicError::ShieldedTooManyActionsError(_)
140 )]
141 );
142 }
143
144 #[test]
147 fn validate_proof_not_empty_should_reject_empty_proof() {
148 let result = validate_proof_not_empty(&[]);
149 assert_matches!(
150 result.errors.as_slice(),
151 [ConsensusError::BasicError(
152 BasicError::ShieldedEmptyProofError(_)
153 )]
154 );
155 }
156
157 #[test]
158 fn validate_proof_not_empty_should_accept_non_empty_proof() {
159 let result = validate_proof_not_empty(&[1u8; 100]);
160 assert!(
161 result.is_valid(),
162 "Expected valid, got: {:?}",
163 result.errors
164 );
165 }
166
167 #[test]
170 fn validate_anchor_not_zero_should_reject_all_zero_anchor() {
171 let result = validate_anchor_not_zero(&[0u8; 32]);
172 assert_matches!(
173 result.errors.as_slice(),
174 [ConsensusError::BasicError(
175 BasicError::ShieldedZeroAnchorError(_)
176 )]
177 );
178 }
179
180 #[test]
181 fn validate_anchor_not_zero_should_accept_non_zero_anchor() {
182 let result = validate_anchor_not_zero(&[7u8; 32]);
183 assert!(
184 result.is_valid(),
185 "Expected valid, got: {:?}",
186 result.errors
187 );
188 }
189
190 #[test]
191 fn validate_anchor_not_zero_should_accept_single_bit_set() {
192 let mut anchor = [0u8; 32];
193 anchor[31] = 1;
194 let result = validate_anchor_not_zero(&anchor);
195 assert!(
196 result.is_valid(),
197 "Expected valid, got: {:?}",
198 result.errors
199 );
200 }
201
202 #[test]
205 fn validate_encrypted_note_sizes_should_accept_correct_size() {
206 let actions = vec![dummy_action()];
207 let result = validate_encrypted_note_sizes(&actions);
208 assert!(
209 result.is_valid(),
210 "Expected valid, got: {:?}",
211 result.errors
212 );
213 }
214
215 #[test]
216 fn validate_encrypted_note_sizes_should_accept_multiple_correct_actions() {
217 let actions = vec![dummy_action(); 3];
218 let result = validate_encrypted_note_sizes(&actions);
219 assert!(
220 result.is_valid(),
221 "Expected valid, got: {:?}",
222 result.errors
223 );
224 }
225
226 #[test]
227 fn validate_encrypted_note_sizes_should_reject_too_short() {
228 let mut action = dummy_action();
229 action.encrypted_note = vec![4u8; 100]; let actions = vec![action];
231 let result = validate_encrypted_note_sizes(&actions);
232 assert_matches!(
233 result.errors.as_slice(),
234 [ConsensusError::BasicError(
235 BasicError::ShieldedEncryptedNoteSizeMismatchError(e)
236 )] => {
237 assert_eq!(e.expected_size(), ENCRYPTED_NOTE_SIZE as u32);
238 assert_eq!(e.actual_size(), 100);
239 }
240 );
241 }
242
243 #[test]
244 fn validate_encrypted_note_sizes_should_reject_too_long() {
245 let mut action = dummy_action();
246 action.encrypted_note = vec![4u8; 300]; let actions = vec![action];
248 let result = validate_encrypted_note_sizes(&actions);
249 assert_matches!(
250 result.errors.as_slice(),
251 [ConsensusError::BasicError(
252 BasicError::ShieldedEncryptedNoteSizeMismatchError(e)
253 )] => {
254 assert_eq!(e.expected_size(), ENCRYPTED_NOTE_SIZE as u32);
255 assert_eq!(e.actual_size(), 300);
256 }
257 );
258 }
259
260 #[test]
261 fn validate_encrypted_note_sizes_should_reject_empty() {
262 let mut action = dummy_action();
263 action.encrypted_note = vec![]; let actions = vec![action];
265 let result = validate_encrypted_note_sizes(&actions);
266 assert_matches!(
267 result.errors.as_slice(),
268 [ConsensusError::BasicError(
269 BasicError::ShieldedEncryptedNoteSizeMismatchError(e)
270 )] => {
271 assert_eq!(e.expected_size(), ENCRYPTED_NOTE_SIZE as u32);
272 assert_eq!(e.actual_size(), 0);
273 }
274 );
275 }
276
277 #[test]
278 fn validate_encrypted_note_sizes_should_reject_second_invalid_action() {
279 let good_action = dummy_action();
280 let mut bad_action = dummy_action();
281 bad_action.encrypted_note = vec![4u8; 100];
282 let actions = vec![good_action, bad_action];
283 let result = validate_encrypted_note_sizes(&actions);
284 assert_matches!(
285 result.errors.as_slice(),
286 [ConsensusError::BasicError(
287 BasicError::ShieldedEncryptedNoteSizeMismatchError(_)
288 )]
289 );
290 }
291
292 #[test]
293 fn validate_encrypted_note_sizes_should_accept_empty_actions_list() {
294 let result = validate_encrypted_note_sizes(&[]);
295 assert!(
296 result.is_valid(),
297 "Expected valid for empty actions list, got: {:?}",
298 result.errors
299 );
300 }
301}