Skip to content

Commit 9daac64

Browse files
authored
test: Add a test ensuring the consistency between hashing F and byte slices (#39)
Check whether using `PoseidonHasher::hash` produces the same results as `PoseidonBytesHasher`. Also, add docstrings to the other tests and remove/merge the redundant ones.
1 parent 6b56518 commit 9daac64

File tree

1 file changed

+148
-51
lines changed

1 file changed

+148
-51
lines changed

light-poseidon/tests/bn254_fq_x5.rs

Lines changed: 148 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
use ark_bn254::Fr;
2-
use ark_ff::{BigInteger, BigInteger256, One, PrimeField, Zero};
2+
use ark_ff::{BigInteger, BigInteger256, One, PrimeField, UniformRand, Zero};
33
use light_poseidon::{
44
bytes_to_prime_field_element_be, bytes_to_prime_field_element_le, validate_bytes_length,
55
Poseidon, PoseidonError,
66
};
77
use light_poseidon::{PoseidonBytesHasher, PoseidonHasher};
88
use rand::Rng;
99

10+
/// Checks the hash of `1` as a prime field element.
1011
#[test]
1112
fn test_poseidon_one() {
1213
let mut hasher = Poseidon::<Fr>::new_circom(2).unwrap();
@@ -32,22 +33,42 @@ fn test_poseidon_one() {
3233
assert_eq!(hash.into_bigint().to_bytes_be(), expected);
3334
}
3435

36+
/// Checks the hash of byte slices consistng of ones and twos.
3537
#[test]
3638
fn test_poseidon_bn254_x5_fq_input_ones_twos() {
3739
let input1 = Fr::from_be_bytes_mod_order(&[1u8; 32]);
3840
let input2 = Fr::from_be_bytes_mod_order(&[2u8; 32]);
3941
let mut hasher = Poseidon::<Fr>::new_circom(2).unwrap();
4042
let hash = hasher.hash(&[input1, input2]).unwrap();
41-
4243
assert_eq!(
4344
hash.into_bigint().to_bytes_be(),
4445
[
4546
13, 84, 225, 147, 143, 138, 140, 28, 125, 235, 94, 3, 85, 242, 99, 25, 32, 123, 132,
4647
254, 156, 162, 206, 27, 38, 231, 53, 200, 41, 130, 25, 144
4748
]
4849
);
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+
)
4968
}
5069

70+
/// Checks thebash of bytes slices consisting of ones and twos, with a custom
71+
/// domain tag.
5172
#[test]
5273
fn test_poseidon_bn254_x5_fq_with_domain_tag() {
5374
let input1 = Fr::from_be_bytes_mod_order(&[1u8; 32]);
@@ -68,6 +89,7 @@ fn test_poseidon_bn254_x5_fq_with_domain_tag() {
6889
assert_ne!(hash.into_bigint().to_bytes_be(), expected_tag_zero);
6990
}
7091

92+
/// Checks the hash of one and two.
7193
#[test]
7294
fn test_poseidon_bn254_x5_fq_input_one_two() {
7395
let input1 = Fr::from_be_bytes_mod_order(&[1]);
@@ -109,49 +131,44 @@ fn test_poseidon_bn254_x5_fq_input_random() {
109131
)
110132
}
111133

134+
/// Check whther providing different number of inputs than supported by the
135+
/// hasher results in an error.
112136
#[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();
121139

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();
123142

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());
126148

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());
145156

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+
}
153167
}
154168

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.
155172
#[test]
156173
fn test_poseidon_bn254_x5_fq_smaller_arrays() {
157174
let mut hasher = Poseidon::<Fr>::new_circom(1).unwrap();
@@ -177,6 +194,10 @@ fn test_poseidon_bn254_x5_fq_smaller_arrays() {
177194
}
178195
}
179196

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.
180201
#[test]
181202
fn test_poseidon_bn254_x5_fq_hash_bytes_be_smaller_arrays_random() {
182203
for nr_inputs in 1..12 {
@@ -212,6 +233,10 @@ fn test_poseidon_bn254_x5_fq_hash_bytes_be_smaller_arrays_random() {
212233
}
213234
}
214235

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.
215240
#[test]
216241
fn test_poseidon_bn254_x5_fq_hash_bytes_le_smaller_arrays_random() {
217242
for nr_inputs in 1..12 {
@@ -247,6 +272,8 @@ fn test_poseidon_bn254_x5_fq_hash_bytes_le_smaller_arrays_random() {
247272
}
248273
}
249274

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.
250277
#[test]
251278
fn test_poseidon_bn254_x5_fq_validate_bytes_length() {
252279
for i in 1..32 {
@@ -262,6 +289,9 @@ fn test_poseidon_bn254_x5_fq_validate_bytes_length() {
262289
}
263290
}
264291

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.
265295
#[test]
266296
fn test_poseidon_bn254_x5_fq_validate_bytes_length_fuzz() {
267297
let mut rng = rand::thread_rng();
@@ -275,10 +305,63 @@ fn test_poseidon_bn254_x5_fq_validate_bytes_length_fuzz() {
275305
}
276306
}
277307

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+
278347
macro_rules! test_bytes_to_prime_field_element {
279348
($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.
280354
#[test]
281355
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+
282365
let mut lt = Fr::MODULUS;
283366
lt.sub_with_borrow(&BigInteger256::from(1u64));
284367
let lt = lt.$to_bytes_method();
@@ -316,6 +399,8 @@ test_bytes_to_prime_field_element!(
316399

317400
macro_rules! test_random_input_same_results {
318401
($name:ident, $method:ident) => {
402+
/// Check whether hashing the same input twice, separately, produces the
403+
/// same results.
319404
#[test]
320405
fn $name() {
321406
let input = [1u8; 32];
@@ -349,6 +434,8 @@ test_random_input_same_results!(
349434

350435
macro_rules! test_invalid_input_length {
351436
($name:ident, $method:ident) => {
437+
/// Checks whether hashing byte slices with number of elements larger
438+
/// than indicated by modulus returns an error.
352439
#[test]
353440
fn $name() {
354441
let mut rng = rand::thread_rng();
@@ -391,6 +478,8 @@ test_invalid_input_length!(
391478

392479
macro_rules! test_fuzz_input_gte_field_size {
393480
($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.
394483
#[test]
395484
fn $name() {
396485
let mut greater_than_field_size = Fr::MODULUS;
@@ -430,6 +519,8 @@ test_fuzz_input_gte_field_size!(
430519

431520
macro_rules! test_input_gte_field_size {
432521
($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.
433524
#[test]
434525
fn $name() {
435526
for nr_inputs in 1..12 {
@@ -483,22 +574,24 @@ test_input_gte_field_size!(
483574
]
484575
);
485576

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+
};
492589
}
493590

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);
501593

594+
/// Checks that endianness is honored correctly and produces expected hashes.
502595
#[test]
503596
fn test_endianness() {
504597
let mut hasher = Poseidon::<Fr>::new_circom(2).unwrap();
@@ -549,6 +642,7 @@ fn test_endianness() {
549642
assert_eq!(hash5, hash6);
550643
}
551644

645+
/// Checks whether providing an empty input results in an error.
552646
#[test]
553647
fn test_empty_input() {
554648
let empty: &[u8] = &[];
@@ -660,6 +754,8 @@ fn test_circom_1_to_12_inputs() {
660754
}
661755
}
662756

757+
/// Checks whether creating a hasher for more than 12 inputs results in an
758+
/// error.
663759
#[test]
664760
fn test_circom_solana_t_gt_12_fails() {
665761
use light_poseidon::PoseidonError;
@@ -681,6 +777,7 @@ fn test_circom_solana_t_gt_12_fails() {
681777
}
682778
}
683779

780+
/// Checks whether crating a hasher for 0 inputs results in an error.
684781
#[test]
685782
fn test_circom_t_0_fails() {
686783
use light_poseidon::PoseidonError;

0 commit comments

Comments
 (0)