1
1
use ark_bn254:: Fr ;
2
- use ark_ff:: { BigInteger , BigInteger256 , One , PrimeField , Zero } ;
2
+ use ark_ff:: { BigInteger , BigInteger256 , One , PrimeField , UniformRand , Zero } ;
3
3
use light_poseidon:: {
4
4
bytes_to_prime_field_element_be, bytes_to_prime_field_element_le, validate_bytes_length,
5
5
Poseidon , PoseidonError ,
6
6
} ;
7
7
use light_poseidon:: { PoseidonBytesHasher , PoseidonHasher } ;
8
8
use rand:: Rng ;
9
9
10
+ /// Checks the hash of `1` as a prime field element.
10
11
#[ test]
11
12
fn test_poseidon_one ( ) {
12
13
let mut hasher = Poseidon :: < Fr > :: new_circom ( 2 ) . unwrap ( ) ;
@@ -32,22 +33,42 @@ fn test_poseidon_one() {
32
33
assert_eq ! ( hash. into_bigint( ) . to_bytes_be( ) , expected) ;
33
34
}
34
35
36
+ /// Checks the hash of byte slices consistng of ones and twos.
35
37
#[ test]
36
38
fn test_poseidon_bn254_x5_fq_input_ones_twos ( ) {
37
39
let input1 = Fr :: from_be_bytes_mod_order ( & [ 1u8 ; 32 ] ) ;
38
40
let input2 = Fr :: from_be_bytes_mod_order ( & [ 2u8 ; 32 ] ) ;
39
41
let mut hasher = Poseidon :: < Fr > :: new_circom ( 2 ) . unwrap ( ) ;
40
42
let hash = hasher. hash ( & [ input1, input2] ) . unwrap ( ) ;
41
-
42
43
assert_eq ! (
43
44
hash. into_bigint( ) . to_bytes_be( ) ,
44
45
[
45
46
13 , 84 , 225 , 147 , 143 , 138 , 140 , 28 , 125 , 235 , 94 , 3 , 85 , 242 , 99 , 25 , 32 , 123 , 132 ,
46
47
254 , 156 , 162 , 206 , 27 , 38 , 231 , 53 , 200 , 41 , 130 , 25 , 144
47
48
]
48
49
) ;
50
+
51
+ let hash = hasher. hash_bytes_be ( & [ & [ 1u8 ; 32 ] , & [ 2u8 ; 32 ] ] ) . unwrap ( ) ;
52
+ assert_eq ! (
53
+ hash,
54
+ [
55
+ 13 , 84 , 225 , 147 , 143 , 138 , 140 , 28 , 125 , 235 , 94 , 3 , 85 , 242 , 99 , 25 , 32 , 123 , 132 ,
56
+ 254 , 156 , 162 , 206 , 27 , 38 , 231 , 53 , 200 , 41 , 130 , 25 , 144
57
+ ]
58
+ ) ;
59
+
60
+ let hash = hasher. hash_bytes_le ( & [ & [ 1u8 ; 32 ] , & [ 2u8 ; 32 ] ] ) . unwrap ( ) ;
61
+ assert_eq ! (
62
+ hash,
63
+ [
64
+ 144 , 25 , 130 , 41 , 200 , 53 , 231 , 38 , 27 , 206 , 162 , 156 , 254 , 132 , 123 , 32 , 25 , 99 , 242 ,
65
+ 85 , 3 , 94 , 235 , 125 , 28 , 140 , 138 , 143 , 147 , 225 , 84 , 13
66
+ ]
67
+ )
49
68
}
50
69
70
+ /// Checks thebash of bytes slices consisting of ones and twos, with a custom
71
+ /// domain tag.
51
72
#[ test]
52
73
fn test_poseidon_bn254_x5_fq_with_domain_tag ( ) {
53
74
let input1 = Fr :: from_be_bytes_mod_order ( & [ 1u8 ; 32 ] ) ;
@@ -68,6 +89,7 @@ fn test_poseidon_bn254_x5_fq_with_domain_tag() {
68
89
assert_ne ! ( hash. into_bigint( ) . to_bytes_be( ) , expected_tag_zero) ;
69
90
}
70
91
92
+ /// Checks the hash of one and two.
71
93
#[ test]
72
94
fn test_poseidon_bn254_x5_fq_input_one_two ( ) {
73
95
let input1 = Fr :: from_be_bytes_mod_order ( & [ 1 ] ) ;
@@ -109,49 +131,44 @@ fn test_poseidon_bn254_x5_fq_input_random() {
109
131
)
110
132
}
111
133
134
+ /// Check whther providing different number of inputs than supported by the
135
+ /// hasher results in an error.
112
136
#[ test]
113
- fn test_poseidon_bn254_x5_fq_input_invalid ( ) {
114
- let mut vec = Vec :: new ( ) ;
115
- for _ in 0 ..17 {
116
- vec. push ( Fr :: from_be_bytes_mod_order ( & [ 1u8 ; 32 ] ) ) ;
117
- }
118
- let mut hasher = Poseidon :: < Fr > :: new_circom ( 2 ) . unwrap ( ) ;
119
-
120
- assert ! ( hasher. hash( & vec) . is_err( ) ) ;
137
+ fn test_poseidon_bn254_x5_fq_too_many_inputs ( ) {
138
+ let mut rng = rand:: thread_rng ( ) ;
121
139
122
- vec. push ( Fr :: from_be_bytes_mod_order ( & [ 4u8 ; 32 ] ) ) ;
140
+ for i in 1 ..13 {
141
+ let mut hasher = Poseidon :: < Fr > :: new_circom ( i) . unwrap ( ) ;
123
142
124
- assert ! ( hasher. hash( & vec) . is_err( ) ) ;
125
- }
143
+ for j in 1 ..13 {
144
+ if i != j {
145
+ let inputs: Vec < _ > = ( 0 ..j) . map ( |_| Fr :: rand ( & mut rng) ) . collect ( ) ;
146
+ let res = hasher. hash ( & inputs) ;
147
+ assert ! ( res. is_err( ) ) ;
126
148
127
- #[ test]
128
- fn test_poseidon_bn254_x5_fq_hash_bytes_be ( ) {
129
- let mut hasher = Poseidon :: < Fr > :: new_circom ( 2 ) . unwrap ( ) ;
130
- let hash = hasher. hash_bytes_be ( & [ & [ 1u8 ; 32 ] , & [ 2u8 ; 32 ] ] ) . unwrap ( ) ;
131
-
132
- assert_eq ! (
133
- hash,
134
- [
135
- 13 , 84 , 225 , 147 , 143 , 138 , 140 , 28 , 125 , 235 , 94 , 3 , 85 , 242 , 99 , 25 , 32 , 123 , 132 ,
136
- 254 , 156 , 162 , 206 , 27 , 38 , 231 , 53 , 200 , 41 , 130 , 25 , 144
137
- ]
138
- ) ;
139
- }
140
-
141
- #[ test]
142
- fn test_poseidon_bn254_x5_fq_hash_bytes_le ( ) {
143
- let mut hasher = Poseidon :: < Fr > :: new_circom ( 2 ) . unwrap ( ) ;
144
- let hash = hasher. hash_bytes_le ( & [ & [ 1u8 ; 32 ] , & [ 2u8 ; 32 ] ] ) . unwrap ( ) ;
149
+ let inputs_bytes_be: Vec < _ > = inputs
150
+ . iter ( )
151
+ . map ( |i| i. into_bigint ( ) . to_bytes_be ( ) )
152
+ . collect ( ) ;
153
+ let inputs_bytes_be: Vec < & [ u8 ] > = inputs_bytes_be. iter ( ) . map ( |v| & v[ ..] ) . collect ( ) ;
154
+ let res_bytes_be = hasher. hash_bytes_be ( & inputs_bytes_be) ;
155
+ assert ! ( res_bytes_be. is_err( ) ) ;
145
156
146
- assert_eq ! (
147
- hash,
148
- [
149
- 144 , 25 , 130 , 41 , 200 , 53 , 231 , 38 , 27 , 206 , 162 , 156 , 254 , 132 , 123 , 32 , 25 , 99 , 242 ,
150
- 85 , 3 , 94 , 235 , 125 , 28 , 140 , 138 , 143 , 147 , 225 , 84 , 13
151
- ]
152
- ) ;
157
+ let inputs_bytes_le: Vec < _ > = inputs
158
+ . iter ( )
159
+ . map ( |i| i. into_bigint ( ) . to_bytes_le ( ) )
160
+ . collect ( ) ;
161
+ let inputs_bytes_le: Vec < & [ u8 ] > = inputs_bytes_le. iter ( ) . map ( |v| & v[ ..] ) . collect ( ) ;
162
+ let res_bytes_le = hasher. hash_bytes_le ( & inputs_bytes_le) ;
163
+ assert ! ( res_bytes_le. is_err( ) ) ;
164
+ }
165
+ }
166
+ }
153
167
}
154
168
169
+ /// Check whether byte inputs with length lower than the byte limit indicated
170
+ /// by the modulus produce the same hashes as equivalent byte inputs padded with
171
+ /// zeros. They should be serialized as the same prime field elements.
155
172
#[ test]
156
173
fn test_poseidon_bn254_x5_fq_smaller_arrays ( ) {
157
174
let mut hasher = Poseidon :: < Fr > :: new_circom ( 1 ) . unwrap ( ) ;
@@ -177,6 +194,10 @@ fn test_poseidon_bn254_x5_fq_smaller_arrays() {
177
194
}
178
195
}
179
196
197
+ /// Check whether big-endian byte inputs with length lower than the byte limit
198
+ /// indicated by the modulus produce the same hashes as equivalent byte inputs
199
+ /// padded with zeros. Randomize the byte slices and try all the possible
200
+ /// lengths. They should be serialized as the same prime field elements.
180
201
#[ test]
181
202
fn test_poseidon_bn254_x5_fq_hash_bytes_be_smaller_arrays_random ( ) {
182
203
for nr_inputs in 1 ..12 {
@@ -212,6 +233,10 @@ fn test_poseidon_bn254_x5_fq_hash_bytes_be_smaller_arrays_random() {
212
233
}
213
234
}
214
235
236
+ /// Check whether little-endian byte inputs with length lower than the byte limit
237
+ /// indicated by the modulus produce the same hashes as equivalent byte inputs
238
+ /// padded with zeros. Randomize the byte slices and try all the possible
239
+ /// lengths. They should be serialized as the same prime field elements.
215
240
#[ test]
216
241
fn test_poseidon_bn254_x5_fq_hash_bytes_le_smaller_arrays_random ( ) {
217
242
for nr_inputs in 1 ..12 {
@@ -247,6 +272,8 @@ fn test_poseidon_bn254_x5_fq_hash_bytes_le_smaller_arrays_random() {
247
272
}
248
273
}
249
274
275
+ /// Check whether `validate_bytes_length` returns an error when an input is a
276
+ /// byte slice with greater number of elements than indicated by the modulus.
250
277
#[ test]
251
278
fn test_poseidon_bn254_x5_fq_validate_bytes_length ( ) {
252
279
for i in 1 ..32 {
@@ -262,6 +289,9 @@ fn test_poseidon_bn254_x5_fq_validate_bytes_length() {
262
289
}
263
290
}
264
291
292
+ /// Check whether `validate_bytes_length` returns an error when an input is a
293
+ /// byte slice with greater number of elements than indicated by the modulus.
294
+ /// Randomize the length.
265
295
#[ test]
266
296
fn test_poseidon_bn254_x5_fq_validate_bytes_length_fuzz ( ) {
267
297
let mut rng = rand:: thread_rng ( ) ;
@@ -275,10 +305,63 @@ fn test_poseidon_bn254_x5_fq_validate_bytes_length_fuzz() {
275
305
}
276
306
}
277
307
308
+ /// Checks whether hashes generated by [`PoseidonHasher::hash`],
309
+ /// [`PoseidonBytesHasher::hash_bytes_be`] and [`PoseidonBytesHasher::hash_bytes_le`]
310
+ /// are the same.
311
+ #[ test]
312
+ fn test_poseidon_bn254_x5_fq_bytes ( ) {
313
+ let mut rng = rand:: thread_rng ( ) ;
314
+
315
+ for _ in 0 ..100 {
316
+ for nr_inputs in 1 ..12 {
317
+ let mut hasher = Poseidon :: < Fr > :: new_circom ( nr_inputs) . unwrap ( ) ;
318
+
319
+ // Hash prime field elements.
320
+ let mut inputs = Vec :: with_capacity ( nr_inputs) ;
321
+ for _ in 0 ..nr_inputs {
322
+ inputs. push ( Fr :: rand ( & mut rng) ) ;
323
+ }
324
+ let res = hasher. hash ( & inputs) . unwrap ( ) ;
325
+
326
+ // Hash big-endian bytes. Ensure that the result is the same.
327
+ let inputs_bytes_be: Vec < _ > = inputs
328
+ . iter ( )
329
+ . map ( |i| i. into_bigint ( ) . to_bytes_be ( ) )
330
+ . collect ( ) ;
331
+ let inputs_bytes_be: Vec < & [ u8 ] > = inputs_bytes_be. iter ( ) . map ( |v| & v[ ..] ) . collect ( ) ;
332
+ let res_bytes_be = hasher. hash_bytes_be ( & inputs_bytes_be) . unwrap ( ) ;
333
+ assert_eq ! ( res. into_bigint( ) . to_bytes_be( ) , res_bytes_be) ;
334
+
335
+ // Hash little-endian bytes. Ensure that the result is the same.
336
+ let inputs_bytes_le: Vec < _ > = inputs
337
+ . iter ( )
338
+ . map ( |i| i. into_bigint ( ) . to_bytes_le ( ) )
339
+ . collect ( ) ;
340
+ let inputs_bytes_le: Vec < & [ u8 ] > = inputs_bytes_le. iter ( ) . map ( |v| & v[ ..] ) . collect ( ) ;
341
+ let res_bytes_le = hasher. hash_bytes_le ( & inputs_bytes_le) . unwrap ( ) ;
342
+ assert_eq ! ( res. into_bigint( ) . to_bytes_le( ) , res_bytes_le) ;
343
+ }
344
+ }
345
+ }
346
+
278
347
macro_rules! test_bytes_to_prime_field_element {
279
348
( $name: ident, $to_bytes_method: ident, $fn: ident) => {
349
+ /// Checks whether `bytes_to_prime_field_element_*` functions:
350
+ ///
351
+ /// * Are converting the valid byte slices appropiately.
352
+ /// * Are throwing an error if the input is greater or equal to the
353
+ /// modulus.
280
354
#[ test]
281
355
fn $name( ) {
356
+ // Test conversion of random prime field elements from bytes to `F`.
357
+ let mut rng = rand:: thread_rng( ) ;
358
+ for _ in 0 ..100 {
359
+ let f = Fr :: rand( & mut rng) ;
360
+ let f = f. into_bigint( ) . $to_bytes_method( ) ;
361
+ let res = $fn:: <Fr >( & f) ;
362
+ assert!( res. is_ok( ) ) ;
363
+ }
364
+
282
365
let mut lt = Fr :: MODULUS ;
283
366
lt. sub_with_borrow( & BigInteger256 :: from( 1u64 ) ) ;
284
367
let lt = lt. $to_bytes_method( ) ;
@@ -316,6 +399,8 @@ test_bytes_to_prime_field_element!(
316
399
317
400
macro_rules! test_random_input_same_results {
318
401
( $name: ident, $method: ident) => {
402
+ /// Check whether hashing the same input twice, separately, produces the
403
+ /// same results.
319
404
#[ test]
320
405
fn $name( ) {
321
406
let input = [ 1u8 ; 32 ] ;
@@ -349,6 +434,8 @@ test_random_input_same_results!(
349
434
350
435
macro_rules! test_invalid_input_length {
351
436
( $name: ident, $method: ident) => {
437
+ /// Checks whether hashing byte slices with number of elements larger
438
+ /// than indicated by modulus returns an error.
352
439
#[ test]
353
440
fn $name( ) {
354
441
let mut rng = rand:: thread_rng( ) ;
@@ -391,6 +478,8 @@ test_invalid_input_length!(
391
478
392
479
macro_rules! test_fuzz_input_gte_field_size {
393
480
( $name: ident, $method: ident, $to_bytes_method: ident) => {
481
+ /// Checks whether hashing a byte slice representing an element larger
482
+ /// than modulus returns an error.
394
483
#[ test]
395
484
fn $name( ) {
396
485
let mut greater_than_field_size = Fr :: MODULUS ;
@@ -430,6 +519,8 @@ test_fuzz_input_gte_field_size!(
430
519
431
520
macro_rules! test_input_gte_field_size {
432
521
( $name: ident, $method: ident, $greater_than_field_size: expr) => {
522
+ /// Checks whether hashing a byte slice representing an element larger
523
+ /// than modulus returns an error.
433
524
#[ test]
434
525
fn $name( ) {
435
526
for nr_inputs in 1 ..12 {
@@ -483,22 +574,24 @@ test_input_gte_field_size!(
483
574
]
484
575
) ;
485
576
486
- #[ test]
487
- fn test_input_eq_field_size_be ( ) {
488
- let mut hasher = Poseidon :: < Fr > :: new_circom ( 1 ) . unwrap ( ) ;
489
- let input = Fr :: MODULUS . to_bytes_be ( ) ;
490
- let hash = hasher. hash_bytes_be ( & [ & input] ) ;
491
- assert_eq ! ( hash, Err ( PoseidonError :: InputLargerThanModulus ) ) ;
577
+ macro_rules! test_input_eq_field_size {
578
+ ( $name: ident, $method: ident, $to_bytes_method: ident) => {
579
+ /// Checks whether hashing a byte slice representing a modulus returns
580
+ /// an error.
581
+ #[ test]
582
+ fn $name( ) {
583
+ let mut hasher = Poseidon :: <Fr >:: new_circom( 1 ) . unwrap( ) ;
584
+ let input = Fr :: MODULUS . $to_bytes_method( ) ;
585
+ let hash = hasher. $method( & [ & input] ) ;
586
+ assert_eq!( hash, Err ( PoseidonError :: InputLargerThanModulus ) ) ;
587
+ }
588
+ } ;
492
589
}
493
590
494
- #[ test]
495
- fn test_input_eq_field_size_le ( ) {
496
- let mut hasher = Poseidon :: < Fr > :: new_circom ( 1 ) . unwrap ( ) ;
497
- let input = Fr :: MODULUS . to_bytes_le ( ) ;
498
- let hash = hasher. hash_bytes_le ( & [ & input] ) ;
499
- assert_eq ! ( hash, Err ( PoseidonError :: InputLargerThanModulus ) ) ;
500
- }
591
+ test_input_eq_field_size ! ( test_input_eq_field_size_be, hash_bytes_be, to_bytes_be) ;
592
+ test_input_eq_field_size ! ( test_input_eq_field_size_le, hash_bytes_le, to_bytes_le) ;
501
593
594
+ /// Checks that endianness is honored correctly and produces expected hashes.
502
595
#[ test]
503
596
fn test_endianness ( ) {
504
597
let mut hasher = Poseidon :: < Fr > :: new_circom ( 2 ) . unwrap ( ) ;
@@ -549,6 +642,7 @@ fn test_endianness() {
549
642
assert_eq ! ( hash5, hash6) ;
550
643
}
551
644
645
+ /// Checks whether providing an empty input results in an error.
552
646
#[ test]
553
647
fn test_empty_input ( ) {
554
648
let empty: & [ u8 ] = & [ ] ;
@@ -660,6 +754,8 @@ fn test_circom_1_to_12_inputs() {
660
754
}
661
755
}
662
756
757
+ /// Checks whether creating a hasher for more than 12 inputs results in an
758
+ /// error.
663
759
#[ test]
664
760
fn test_circom_solana_t_gt_12_fails ( ) {
665
761
use light_poseidon:: PoseidonError ;
@@ -681,6 +777,7 @@ fn test_circom_solana_t_gt_12_fails() {
681
777
}
682
778
}
683
779
780
+ /// Checks whether crating a hasher for 0 inputs results in an error.
684
781
#[ test]
685
782
fn test_circom_t_0_fails ( ) {
686
783
use light_poseidon:: PoseidonError ;
0 commit comments