1use crate::{Hasher, HasherError, Poseidon};
2
3pub fn create_hash_chain_from_array<const T: usize>(
11 inputs: [[u8; 32]; T],
12) -> Result<[u8; 32], HasherError> {
13 create_hash_chain_from_slice(&inputs)
14}
15
16pub fn create_hash_chain_from_slice(inputs: &[[u8; 32]]) -> Result<[u8; 32], HasherError> {
24 if inputs.is_empty() {
25 return Ok([0u8; 32]);
26 }
27 let mut hash_chain = inputs[0];
28 for input in inputs.iter().skip(1) {
29 hash_chain = Poseidon::hashv(&[&hash_chain, input])?;
30 }
31 Ok(hash_chain)
32}
33
34pub fn create_two_inputs_hash_chain(
47 hashes_first: &[[u8; 32]],
48 hashes_second: &[[u8; 32]],
49) -> Result<[u8; 32], HasherError> {
50 let first_len = hashes_first.len();
51 if first_len != hashes_second.len() {
52 return Err(HasherError::InvalidInputLength(
53 first_len,
54 hashes_second.len(),
55 ));
56 }
57 if hashes_first.is_empty() {
58 return Ok([0u8; 32]);
59 }
60 let mut hash_chain = Poseidon::hashv(&[&hashes_first[0], &hashes_second[0]])?;
61
62 if first_len == 1 {
63 return Ok(hash_chain);
64 }
65
66 for i in 1..first_len {
67 hash_chain = Poseidon::hashv(&[&hash_chain, &hashes_first[i], &hashes_second[i]])?;
68 }
69 Ok(hash_chain)
70}
71
72#[cfg(test)]
73mod hash_chain_tests {
74 use ark_ff::PrimeField;
75 use light_poseidon::PoseidonError;
76 use num_bigint::BigUint;
77
78 use super::*;
79 use crate::{bigint::bigint_to_be_bytes_array, Hasher, HasherError, Poseidon};
80
81 #[test]
91 fn test_create_hash_chain_from_slice() {
92 {
94 let inputs: [[u8; 32]; 3] = [[1u8; 32], [2u8; 32], [3u8; 32]];
96
97 let intermediate_hash_1 = Poseidon::hashv(&[&inputs[0], &inputs[1]]).unwrap();
102 let expected_hash = Poseidon::hashv(&[&intermediate_hash_1, &inputs[2]]).unwrap();
103
104 let result = create_hash_chain_from_slice(&inputs).unwrap();
106
107 assert_eq!(
109 result, expected_hash,
110 "Functional test with hardcoded values failed."
111 );
112 }
113
114 {
116 let inputs: [[u8; 32]; 2] = [[4u8; 32], [5u8; 32]];
117
118 let expected_hash = Poseidon::hashv(&[&inputs[0], &inputs[1]]).unwrap();
120 let hard_coded_expected_hash = [
121 13, 250, 206, 124, 182, 159, 160, 87, 57, 23, 80, 155, 25, 43, 40, 136, 228, 255,
122 201, 1, 22, 168, 211, 220, 176, 187, 23, 176, 46, 198, 140, 211,
123 ];
124
125 let result = create_hash_chain_from_slice(&inputs).unwrap();
126
127 assert_eq!(
128 result, expected_hash,
129 "Functional test with manual hash comparison failed."
130 );
131 assert_eq!(result, hard_coded_expected_hash);
132 }
133
134 {
136 let inputs = [[4u8; 32], [5u8; 32], [6u8; 32]];
137
138 let expected_hash = Poseidon::hashv(&[&inputs[0], &inputs[1]]).unwrap();
139 let expected_hash = Poseidon::hashv(&[&expected_hash, &inputs[2]]).unwrap();
140 let hard_coded_expected_hash = [
141 12, 74, 32, 81, 132, 82, 10, 115, 75, 248, 169, 125, 228, 230, 140, 167, 149, 181,
142 244, 194, 63, 201, 26, 150, 142, 4, 60, 16, 77, 145, 194, 152,
143 ];
144
145 let result = create_hash_chain_from_slice(&inputs).unwrap();
146
147 assert_eq!(
148 result, expected_hash,
149 "Functional test with manual hash comparison failed."
150 );
151 assert_eq!(result, hard_coded_expected_hash);
152 }
153
154 {
156 let inputs: [[u8; 32]; 2] = [[6u8; 32], [7u8; 32]];
158
159 let first_hash = create_hash_chain_from_slice(&inputs).unwrap();
161
162 let second_hash = create_hash_chain_from_slice(&inputs).unwrap();
164
165 assert_eq!(
167 first_hash, second_hash,
168 "Determinism test failed: Hashes do not match."
169 );
170 }
171
172 {
174 let inputs: [[u8; 32]; 0] = [];
175 let result = create_hash_chain_from_slice(&inputs).unwrap();
176 assert_eq!(result, [0u8; 32], "Empty input should return zero hash");
177 }
178
179 {
181 let modulus: BigUint = ark_bn254::Fr::MODULUS.into();
182 let modulus_bytes: [u8; 32] = bigint_to_be_bytes_array(&modulus).unwrap();
183 let huge_input = vec![modulus_bytes, modulus_bytes];
184 let result = create_hash_chain_from_slice(&huge_input);
185 assert!(
186 matches!(result, Err(HasherError::Poseidon(error)) if error == PoseidonError::InputLargerThanModulus),
187 );
188 }
189 }
190
191 #[test]
198 fn test_create_two_inputs_hash_chain() {
199 {
201 let hashes_first: &[[u8; 32]] = &[];
202 let hashes_second: &[[u8; 32]] = &[];
203 let result = create_two_inputs_hash_chain(hashes_first, hashes_second).unwrap();
204 assert_eq!(result, [0u8; 32], "Empty input should return zero hash");
205 }
206
207 {
209 let hashes_first: &[[u8; 32]] = &[[1u8; 32]];
210 let hashes_second: &[[u8; 32]] = &[[2u8; 32]];
211 let expected_hash = Poseidon::hashv(&[&hashes_first[0], &hashes_second[0]]).unwrap();
212 let result = create_two_inputs_hash_chain(hashes_first, hashes_second).unwrap();
213 assert_eq!(result, expected_hash, "Single input each test failed");
214 }
215
216 {
218 let hashes_first: &[[u8; 32]] = &[[1u8; 32], [2u8; 32]];
219 let hashes_second: &[[u8; 32]] = &[[3u8; 32], [4u8; 32]];
220 let intermediate_hash =
221 Poseidon::hashv(&[&hashes_first[0], &hashes_second[0]]).unwrap();
222 let expected_hash =
223 Poseidon::hashv(&[&intermediate_hash, &hashes_first[1], &hashes_second[1]])
224 .unwrap();
225 let result = create_two_inputs_hash_chain(hashes_first, hashes_second).unwrap();
226 assert_eq!(result, expected_hash, "Two inputs each test failed");
227 }
228
229 {
231 let hashes_first: &[[u8; 32]] = &[[1u8; 32]];
232 let hashes_second: &[[u8; 32]] = &[[2u8; 32], [3u8; 32]];
233 let result = create_two_inputs_hash_chain(hashes_first, hashes_second);
234 assert!(
235 matches!(result, Err(HasherError::InvalidInputLength(1, 2))),
236 "Invalid input length for hashes_first test failed"
237 );
238 }
239
240 {
242 let hashes_first: &[[u8; 32]] = &[[1u8; 32], [2u8; 32]];
243 let hashes_second: &[[u8; 32]] = &[[3u8; 32]];
244 let result = create_two_inputs_hash_chain(hashes_first, hashes_second);
245 assert!(
246 matches!(result, Err(HasherError::InvalidInputLength(2, 1))),
247 "Invalid input length for hashes_second test failed"
248 );
249 }
250 }
251}