1#![no_std]
2
3extern crate alloc;
4
5#[cfg(feature = "p3")]
6pub use qp_poseidon_constants as constants;
7
8pub mod serialization;
9
10use crate::serialization::{digest_felts_to_bytes, injective_bytes_to_felts};
11use alloc::vec::Vec;
12use p3_field::PrimeCharacteristicRing;
13use p3_goldilocks::{Goldilocks, Poseidon2Goldilocks};
14use p3_symmetric::Permutation;
15use rand_chacha::{rand_core::SeedableRng, ChaCha20Rng};
16
17pub const FIELD_ELEMENT_PREIMAGE_PADDING_LEN: usize = 189;
19
20const WIDTH: usize = 12;
23const RATE: usize = 4;
24const OUTPUT: usize = 4;
25
26#[cfg(feature = "p2")]
28pub use serialization::p2_backend::GF as P2GF;
29#[cfg(feature = "p3")]
30pub use serialization::p3_backend::GF as P3GF;
31
32pub struct Poseidon2State {
34 poseidon2: Poseidon2Goldilocks<WIDTH>,
35 state: [Goldilocks; WIDTH],
36 buf: [Goldilocks; RATE],
37 buf_len: usize,
38}
39
40impl Poseidon2State {
41 fn new() -> Self {
42 Self {
43 poseidon2: constants::create_poseidon(),
44 state: [Goldilocks::ZERO; WIDTH],
45 buf: [Goldilocks::ZERO; RATE],
46 buf_len: 0,
47 }
48 }
49
50 #[inline]
51 fn push_to_buf(&mut self, x: Goldilocks) {
52 self.buf[self.buf_len] = x;
53 self.buf_len += 1;
54 if self.buf_len == RATE {
55 self.absorb_full_block();
56 }
57 }
58
59 #[inline]
60 fn absorb_full_block(&mut self) {
61 for i in 0..RATE {
63 self.state[i] += self.buf[i];
64 }
65 self.poseidon2.permute_mut(&mut self.state);
66 self.buf = [Goldilocks::ZERO; RATE];
67 self.buf_len = 0;
68 }
69
70 fn append(&mut self, blocks: &[Goldilocks]) {
71 for &b in blocks {
72 self.push_to_buf(b);
73 }
74 }
75
76 fn append_bytes(&mut self, bytes: &[u8]) {
77 let felts = injective_bytes_to_felts(bytes);
78 self.append(&felts);
79 }
80
81 fn finalize_state(mut self) -> [Goldilocks; WIDTH] {
83 self.push_to_buf(Goldilocks::ONE);
85 while self.buf_len != 0 {
87 self.push_to_buf(Goldilocks::ZERO);
88 }
89 self.state
90 }
91
92 fn finalize(self) -> [u8; 32] {
94 let state = self.finalize_state();
95 digest_felts_to_bytes(&state[..OUTPUT].try_into().expect("OUTPUT <= WIDTH"))
96 }
97
98 fn finalize_twice(mut self) -> [u8; 64] {
100 self.push_to_buf(Goldilocks::ONE);
102 while self.buf_len != 0 {
104 self.push_to_buf(Goldilocks::ZERO);
105 }
106
107 let h1: [u8; 32] =
108 digest_felts_to_bytes(&self.state[..RATE].try_into().expect("RATE <= WIDTH"));
109 self.poseidon2.permute_mut(&mut self.state);
111 let h2: [u8; 32] =
112 digest_felts_to_bytes(&self.state[..RATE].try_into().expect("RATE <= WIDTH"));
113
114 [h1, h2].concat().try_into().expect("64 bytes")
115 }
116}
117
118pub fn poseidon2_from_seed(seed: u64) -> Poseidon2State {
119 let mut rng = ChaCha20Rng::seed_from_u64(seed);
120 let poseidon2 = Poseidon2Goldilocks::<WIDTH>::new_from_rng_128(&mut rng);
121 Poseidon2State {
122 poseidon2,
123 state: [Goldilocks::ZERO; WIDTH],
124 buf: [Goldilocks::ZERO; RATE],
125 buf_len: 0,
126 }
127}
128
129fn hash_circuit_padding_felts<const C: usize>(mut x: Vec<Goldilocks>) -> [u8; 32] {
134 let len = x.len();
139 if len < C {
140 x.resize(C, Goldilocks::ZERO);
141 }
142
143 hash_variable_length(x)
144}
145
146pub fn hash_padded_bytes<const C: usize>(x: &[u8]) -> [u8; 32] {
149 hash_circuit_padding_felts::<C>(injective_bytes_to_felts(x))
150}
151
152pub fn hash_variable_length(x: Vec<Goldilocks>) -> [u8; 32] {
154 let mut st = Poseidon2State::new();
155 st.append(&x);
156 st.finalize()
157}
158
159pub fn double_hash_variable_length(x: Vec<Goldilocks>) -> [u8; 32] {
161 let mut st = Poseidon2State::new();
162 st.append(&x);
163 let state_0 = st.finalize_state();
165 let output_0 = &state_0[..OUTPUT];
166 st = Poseidon2State::new();
168 st.append(output_0);
169 st.finalize()
170}
171
172pub fn hash_variable_length_bytes(x: &[u8]) -> [u8; 32] {
175 let mut st = Poseidon2State::new();
176 st.append_bytes(x);
177 st.finalize()
178}
179
180pub fn hash_squeeze_twice(x: &[u8]) -> [u8; 64] {
182 let mut st = Poseidon2State::new();
183 st.append_bytes(x);
184 st.finalize_twice()
185}
186
187#[cfg(test)]
188mod tests {
189 use crate::serialization::unsafe_digest_bytes_to_felts;
190
191 use super::*;
192 use alloc::vec;
193 use hex;
194 use p3_field::PrimeField64;
195
196 const C: usize = FIELD_ELEMENT_PREIMAGE_PADDING_LEN;
197
198 #[test]
199 fn test_empty_input() {
200 let result = hash_padded_bytes::<C>(&[]);
201 assert_eq!(result.len(), 32);
202 }
203
204 #[test]
205 fn test_single_byte() {
206 let input = vec![42u8];
207 let result = hash_padded_bytes::<C>(&input);
208 assert_eq!(result.len(), 32);
209 }
210
211 #[test]
212 fn test_exactly_32_bytes() {
213 let input = [1u8; 32];
214 let result = hash_padded_bytes::<C>(&input);
215 assert_eq!(result.len(), 32);
216 }
217
218 #[test]
219 fn test_multiple_chunks() {
220 let input = [2u8; 64]; let result = hash_padded_bytes::<C>(&input);
222 assert_eq!(result.len(), 32);
223 }
224
225 #[test]
226 fn test_partial_chunk() {
227 let input = [3u8; 40]; let result = hash_padded_bytes::<C>(&input);
229 assert_eq!(result.len(), 32);
230 }
231
232 #[test]
233 fn test_consistency() {
234 let input = [4u8; 50];
235 let iterations = 10;
236 let current_hash = hash_padded_bytes::<C>(&input);
237
238 for _ in 0..iterations {
239 let hash1 = hash_padded_bytes::<C>(¤t_hash);
240 let hash2 = hash_padded_bytes::<C>(¤t_hash);
241 assert_eq!(hash1, hash2, "Hash function should be deterministic");
242 }
243 }
244
245 #[test]
246 fn test_different_inputs() {
247 let input1 = [5u8; 32];
248 let input2 = [6u8; 32];
249 let hash1 = hash_padded_bytes::<C>(&input1);
250 let hash2 = hash_padded_bytes::<C>(&input2);
251 assert_ne!(hash1, hash2, "Different inputs should produce different hashes");
252 }
253
254 #[test]
255 fn test_poseidon2_hash_input_sizes() {
256 for size in 1..=128 {
258 let input: Vec<u8> = (0..size).map(|i| (i * i % 256) as u8).collect();
260 let hash = hash_padded_bytes::<C>(&input);
261
262 assert_eq!(hash.len(), 32, "Input size {} should produce 32-byte hash", size);
264 }
265 }
266
267 #[test]
268 fn test_big_preimage() {
269 for overflow in 1..=10 {
270 let preimage =
271 (<p3_goldilocks::Goldilocks as PrimeField64>::ORDER_U64 + overflow).to_le_bytes();
272 let _hash = hash_padded_bytes::<C>(&preimage);
273 }
274 }
275
276 #[test]
277 fn test_circuit_preimage() {
278 let preimage =
279 hex::decode("afd8e7530b95ee5ebab950c9a0c62fae1e80463687b3982233028e914f8ec7cc");
280 let hash = hash_padded_bytes::<C>(&preimage.unwrap());
281 let _hash = hash_padded_bytes::<C>(&hash);
282 }
283
284 #[test]
285 fn test_random_inputs() {
286 let hex_strings = [
287 "a3f8",
288 "1b7e9d",
289 "4c2a6f81",
290 "e5d30b9a",
291 "1a4f7c2e9b0d8356",
292 "3e8d2a7f5c1b09e4d6f7a2c8",
293 "7b3e9a1f4c8d2e6b0a5f9d3c",
294 "1a4f7c2e9b0d83561a4f7c2e9b0d83561a4f7c2e9b0d83561a4f7c2e9b0d8356",
295 "e5d30b9a4c2a6f81e5d30b9a4c2a6f81e5d30b9a4c2a6f81e5d30b9a4c2a6f81",
296 ];
297
298 for hex_string in hex_strings.iter() {
299 let preimage = hex::decode(hex_string).unwrap();
300 let hash = hash_padded_bytes::<C>(&preimage);
301 let _hash2 = hash_padded_bytes::<C>(&hash);
302 }
303 }
304
305 #[test]
306 fn test_known_value_hashes() {
307 let vectors = [
308 (
309 vec![],
310 "89d1c547f1b828c8659fe0600c90d58e95b435d91d04439b67c83b88a679380a",
311 "4d8d22af81f6c27a005a07028590ef4ee480f6c4b93f813daf9de47a07c8ae86",
312 ),
313 (
314 vec![0u8],
315 "dbb29ba5d3bf3246356a8918dc2808ea5130a9ae02afefe360703afc848d3769",
316 "8f5b42e350ff5a12788210c86c2bcd49243b8f9350de818b3b0c56839a42ebad",
317 ),
318 (
319 vec![0u8, 0u8],
320 "23b58c9f2aa60a1677e9bb360be87db2f48f52e8bd2702948f7f11b36cb1d607",
321 "3e6ee24fb61a22f4d825b72fc8ebd359e3b3b9566e246c71c3e450ebe3262f9c",
322 ),
323 (
324 vec![0u8, 0u8, 0u8],
325 "1799097faca4e7faa34fa7e17c2e16ae281a655cd502f6ef9f1c993d74f161d6",
326 "34f4338a6f1b671062a3ac00b37ca05a47b43e16e589ccaa5b063416ba42356b",
327 ),
328 (
329 vec![0u8, 0u8, 0u8, 0u8],
330 "5d1e9b2cdf43cce05de115f156dcf2062e3102341303613eeb1547886ebba4cc",
331 "7bac8c6bc49b0b750f2ce0912b815a2cb4ae20c75ac430850257882d9d321afa",
332 ),
333 (
334 vec![0u8, 0u8, 0u8, 0u8, 0u8],
335 "d941bb3132ac34a919add937354f09cf302c5e972411c1854f2e5ebf5b054fc4",
336 "c95cfddb573adf4070b3d7c8d2dfbbee48b4b973d80cbda2b458abe7bb6f0def",
337 ),
338 (
339 vec![0u8, 0u8, 0u8, 0u8, 0u8, 0u8],
340 "8d2fdb09cd31ab6fd59f0b429d50684a6425a7f21bc5e32e38ab19ced4fc5492",
341 "a4dc08d0a8c5ea44007462fe1fd8e45962d4ea85c420eab4140fbb30b5b5e111",
342 ),
343 (
344 vec![0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8],
345 "490936de1357c80889dd9fee7f0ee58e7d7fe4c11e66bda55fa860bb6b94cddd",
346 "b01975012df91d9f9f040c34655f23f3ec1f6d1738d85679e9848143756637c9",
347 ),
348 (
349 vec![0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8],
350 "eaa5c78853eac1ee6240512dd85077776ec909186fe46ec463e167790d768a40",
351 "eacd9e48d2e968131e48c8e69f2a211cc06c7778db6c5467348b45418fc7f585",
352 ),
353 (
354 vec![0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8],
355 "2d2822c6cc2fd816ceb90cd9561bb1f5eb1638c2574b838a16b426e01d929928",
356 "00df670e8ec0751d3fb9b5f0281d0af9a7a82f62ad35a21247a9d6117daec151",
357 ),
358 (
359 vec![0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8],
360 "9cd82ca4f742f54f62d577b3254d5138e4f5c9eea3f4173a6c1733c08cff79f2",
361 "6488c3c47c17114e3998bf90d6c50dc323a82e6e91768dca6977cfe152415ad5",
362 ),
363 (
364 vec![0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8],
365 "9794607d182df1504c1a5af25d51105332b4520c06e9c669669a4060e704b15c",
366 "a5ba11e5959cdb59e6b2b0d25d470d656caf59aae961b52c159ffd6e0f04baff",
367 ),
368 (
369 vec![0u8, 0u8, 0u8, 1u8],
370 "779f5f6d4ae11964fc2efd012bb691899ccc317ed9e186f9efdab73a2bf3af9e",
371 "6ffff0c97262139567c426e916c1fd70c924010153c366bb2a8957ea89902942",
372 ),
373 (
374 vec![1u8, 2, 3, 4, 5, 6, 7, 8],
375 "ecdf30787278c049402e704b298c30c7787116d75e4dbcd8ce6b5757ed8833e5",
376 "131020b2e74819343f8568258ae2e9717e9b2253d57baabab78a518bc7499a8b",
377 ),
378 (
379 vec![255u8; 32],
380 "fac64f5ed32acfa79a37cd5d1c4e48c67c500ae48043a61a95e51a2e181527ec",
381 "05a90ac8e3c4b7635fa3735c3a9c4fef620479fa68a9e4ae1421c39aa6939125",
382 ),
383 (
384 b"hello world".to_vec(),
385 "95d6a29c17bfd2149cda69c8becbc8cc33c527f39b3a2f7d12865272fd7f5677",
386 "fd1f5d7d4701c25bbdd5dd6e3be6abb474fffbaa402f814dce95f8283abbf3e7",
387 ),
388 (
389 (0u8..32).collect::<Vec<u8>>(),
390 "66f2c7df65a0f456314999fcf95899e27a5a5436cb4f04d79f11f12f8f86f0e0",
391 "2e3e4a00be0d8520cddaf3000d98c1f1d73c19bfe9fe181694bfa9afdfce7687",
392 ),
393 ];
394 for (input, expected_hex1, expected_hex2) in vectors.iter() {
395 let hash = hash_padded_bytes::<C>(input);
396 assert_eq!(
397 hex::encode(hash.as_slice()),
398 *expected_hex1,
399 "input: 0x{}",
400 hex::encode(input)
401 );
402
403 let hash2 = hash_variable_length_bytes(input);
404 assert_eq!(
405 hex::encode(hash2.as_slice()),
406 *expected_hex2,
407 "input: 0x{}",
408 hex::encode(input)
409 );
410 }
411 }
412
413 #[test]
414 fn test_hash_variable_length() {
415 let input = b"test";
416 let padded_hash = hash_padded_bytes::<C>(input);
417 let no_pad_hash = hash_variable_length_bytes(input);
418
419 assert_ne!(padded_hash, no_pad_hash);
421 assert_eq!(padded_hash.len(), 32);
422 assert_eq!(no_pad_hash.len(), 32);
423 }
424
425 #[test]
426 fn test_hash_squeeze_twice() {
427 let input = b"test 512-bit hash";
428 let hash512 = hash_squeeze_twice(input);
429
430 assert_eq!(hash512.len(), 64);
432
433 let expected_first = hash_variable_length_bytes(input);
435 assert_eq!(&hash512[0..32], &expected_first);
436
437 let hash512_2 = hash_squeeze_twice(input);
439 assert_eq!(hash512, hash512_2);
440
441 let different_hash = hash_squeeze_twice(b"different input");
443 assert_ne!(hash512, different_hash);
444 }
445
446 #[test]
447 fn test_double_hash_variable_length() {
448 let preimage = b"double hash test";
449 let first_hash = hash_variable_length_bytes(preimage);
450 let double_hash = double_hash_variable_length(injective_bytes_to_felts(preimage));
451
452 assert_ne!(first_hash, double_hash);
454
455 let recomputed_double_hash =
457 hash_variable_length(unsafe_digest_bytes_to_felts(&first_hash).to_vec());
458 assert_eq!(double_hash, recomputed_double_hash);
459 }
460}