trident/package/
poseidon2.rs1const P: u64 = crate::field::goldilocks::MODULUS;
11
12const T: usize = 8;
14const RATE: usize = 4;
16const R_F: usize = 8;
18const R_P: usize = 22;
20#[cfg(test)]
22const ALPHA: u64 = 7;
23
24const DIAG: [u64; T] = [2, 3, 5, 9, 17, 33, 65, 129];
26
27#[derive(Clone, Copy, Debug, PartialEq, Eq)]
33pub struct GoldilocksField(pub u64);
34
35impl GoldilocksField {
36 pub const ZERO: Self = Self(0);
37 pub const ONE: Self = Self(1);
38
39 #[inline]
41 pub fn new(v: u64) -> Self {
42 Self(v % P)
43 }
44
45 #[inline]
47 fn reduce128(x: u128) -> Self {
48 let lo = x as u64;
51 let hi = (x >> 64) as u64;
52 let hi_shifted = (hi as u128) * ((1u128 << 32) - 1);
53 let sum = lo as u128 + hi_shifted;
54 let lo2 = sum as u64;
56 let hi2 = (sum >> 64) as u64;
57 if hi2 == 0 {
58 Self(if lo2 >= P { lo2 - P } else { lo2 })
59 } else {
60 let r = lo2 as u128 + (hi2 as u128) * ((1u128 << 32) - 1);
62 let lo3 = r as u64;
64 let hi3 = (r >> 64) as u64;
65 if hi3 == 0 {
66 Self(if lo3 >= P { lo3 - P } else { lo3 })
67 } else {
68 let v = lo3.wrapping_add(hi3.wrapping_mul(u32::MAX as u64));
70 Self(if v >= P { v - P } else { v })
71 }
72 }
73 }
74
75 #[inline]
76 pub fn add(self, rhs: Self) -> Self {
77 let (sum, carry) = self.0.overflowing_add(rhs.0);
78 if carry {
79 let r = sum + (u32::MAX as u64);
80 Self(if r >= P { r - P } else { r })
81 } else {
82 Self(if sum >= P { sum - P } else { sum })
83 }
84 }
85
86 #[inline]
87 pub fn sub(self, rhs: Self) -> Self {
88 if self.0 >= rhs.0 {
89 Self(self.0 - rhs.0)
90 } else {
91 Self(P - rhs.0 + self.0)
92 }
93 }
94
95 #[inline]
96 pub fn mul(self, rhs: Self) -> Self {
97 Self::reduce128((self.0 as u128) * (rhs.0 as u128))
98 }
99
100 pub fn pow(self, mut exp: u64) -> Self {
102 let mut base = self;
103 let mut acc = Self::ONE;
104 while exp > 0 {
105 if exp & 1 == 1 {
106 acc = acc.mul(base);
107 }
108 base = base.mul(base);
109 exp >>= 1;
110 }
111 acc
112 }
113
114 #[inline]
116 pub fn sbox(self) -> Self {
117 let x2 = self.mul(self);
118 let x3 = x2.mul(self);
119 let x6 = x3.mul(x3);
120 x6.mul(self)
121 }
122}
123
124const TOTAL_ROUNDS: usize = R_F + R_P;
129
130fn round_constant(round: usize, element: usize) -> GoldilocksField {
132 let tag = format!("Poseidon2-Goldilocks-t8-RF8-RP22-{round}-{element}");
133 let digest = blake3::hash(tag.as_bytes());
134 let bytes: [u8; 8] = digest.as_bytes()[..8].try_into().unwrap_or([0u8; 8]);
135 GoldilocksField::new(u64::from_le_bytes(bytes))
136}
137
138fn generate_all_constants() -> Vec<GoldilocksField> {
140 let mut constants = Vec::new();
141 for r in 0..TOTAL_ROUNDS {
142 let is_full = r < R_F / 2 || r >= R_F / 2 + R_P;
143 if is_full {
144 for e in 0..T {
145 constants.push(round_constant(r, e));
146 }
147 } else {
148 constants.push(round_constant(r, 0));
149 }
150 }
151 constants
152}
153
154fn cached_round_constants() -> &'static [GoldilocksField] {
156 static CONSTANTS: std::sync::OnceLock<Vec<GoldilocksField>> = std::sync::OnceLock::new();
157 CONSTANTS.get_or_init(generate_all_constants)
158}
159
160pub struct Poseidon2Sponge {
166 pub state: [GoldilocksField; T],
167}
168
169impl Poseidon2Sponge {
170 pub fn new() -> Self {
171 Self {
172 state: [GoldilocksField::ZERO; T],
173 }
174 }
175
176 #[inline]
178 fn full_sbox(&mut self) {
179 for s in self.state.iter_mut() {
180 *s = s.sbox();
181 }
182 }
183
184 #[inline]
186 fn partial_sbox(&mut self) {
187 self.state[0] = self.state[0].sbox();
188 }
189
190 fn external_linear(&mut self) {
193 let sum = self
194 .state
195 .iter()
196 .fold(GoldilocksField::ZERO, |a, &b| a.add(b));
197 for s in self.state.iter_mut() {
198 *s = s.add(sum); }
200 }
201
202 fn internal_linear(&mut self) {
205 let sum = self
206 .state
207 .iter()
208 .fold(GoldilocksField::ZERO, |a, &b| a.add(b));
209 for (i, s) in self.state.iter_mut().enumerate() {
210 *s = GoldilocksField(DIAG[i]).mul(*s).add(sum);
211 }
212 }
213
214 pub fn permutation(&mut self) {
216 let constants = cached_round_constants();
217 let mut ci = 0;
218
219 for _ in 0..R_F / 2 {
221 for s in self.state.iter_mut() {
222 *s = s.add(constants[ci]);
223 ci += 1;
224 }
225 self.full_sbox();
226 self.external_linear();
227 }
228
229 for _ in 0..R_P {
231 self.state[0] = self.state[0].add(constants[ci]);
232 ci += 1;
233 self.partial_sbox();
234 self.internal_linear();
235 }
236
237 for _ in 0..R_F / 2 {
239 for s in self.state.iter_mut() {
240 *s = s.add(constants[ci]);
241 ci += 1;
242 }
243 self.full_sbox();
244 self.external_linear();
245 }
246
247 debug_assert_eq!(ci, constants.len());
248 }
249}
250
251pub struct Poseidon2Hasher {
257 state: Poseidon2Sponge,
258 absorbed: usize,
259}
260
261impl Poseidon2Hasher {
262 pub fn new() -> Self {
263 Self {
264 state: Poseidon2Sponge::new(),
265 absorbed: 0,
266 }
267 }
268
269 pub fn absorb(&mut self, elements: &[GoldilocksField]) {
271 for &elem in elements {
272 if self.absorbed == RATE {
273 self.state.permutation();
274 self.absorbed = 0;
275 }
276 self.state.state[self.absorbed] = self.state.state[self.absorbed].add(elem);
277 self.absorbed += 1;
278 }
279 }
280
281 pub fn absorb_bytes(&mut self, data: &[u8]) {
283 const BYTES_PER_ELEM: usize = 7;
284 let mut elements = Vec::with_capacity(data.len() / BYTES_PER_ELEM + 2);
285 for chunk in data.chunks(BYTES_PER_ELEM) {
286 let mut buf = [0u8; 8];
287 buf[..chunk.len()].copy_from_slice(chunk);
288 elements.push(GoldilocksField::new(u64::from_le_bytes(buf)));
289 }
290 elements.push(GoldilocksField::new(data.len() as u64));
292 self.absorb(&elements);
293 }
294
295 pub fn squeeze(&mut self, count: usize) -> Vec<GoldilocksField> {
297 let mut out = Vec::with_capacity(count);
298 self.state.permutation();
299 self.absorbed = 0;
300 let mut squeezed = 0;
301 loop {
302 for &elem in self.state.state[..RATE].iter() {
303 out.push(elem);
304 squeezed += 1;
305 if squeezed == count {
306 return out;
307 }
308 }
309 self.state.permutation();
310 }
311 }
312
313 pub fn finalize(mut self) -> GoldilocksField {
315 self.squeeze(1)[0]
316 }
317
318 pub fn finalize_4(mut self) -> [GoldilocksField; 4] {
320 let v = self.squeeze(4);
321 [v[0], v[1], v[2], v[3]]
322 }
323}
324
325pub fn hash_bytes(data: &[u8]) -> [u8; 32] {
331 let mut hasher = Poseidon2Hasher::new();
332 hasher.absorb_bytes(data);
333 let result = hasher.finalize_4();
334 let mut out = [0u8; 32];
335 for (i, elem) in result.iter().enumerate() {
336 out[i * 8..i * 8 + 8].copy_from_slice(&elem.0.to_le_bytes());
337 }
338 out
339}
340
341pub fn hash_fields(elements: &[GoldilocksField]) -> [GoldilocksField; 4] {
343 let mut hasher = Poseidon2Hasher::new();
344 hasher.absorb(elements);
345 hasher.finalize_4()
346}
347
348#[cfg(test)]
353mod tests {
354 use super::*;
355
356 #[test]
357 fn test_goldilocks_arithmetic() {
358 let a = GoldilocksField::new(P - 1);
359 let b = GoldilocksField::ONE;
360 assert_eq!(a.add(b), GoldilocksField::ZERO);
362 assert_eq!(GoldilocksField::ZERO.sub(b), a);
364 let x = GoldilocksField::new(123456789);
366 assert_eq!(x.mul(GoldilocksField::ONE), x);
367 assert_eq!(x.mul(GoldilocksField::ZERO), GoldilocksField::ZERO);
368 let y = GoldilocksField::new(987654321);
370 assert_eq!(x.mul(y), y.mul(x));
371 assert_eq!(x.pow(0), GoldilocksField::ONE);
373 assert_eq!(x.pow(1), x);
374 assert_eq!(x.pow(3), x.mul(x).mul(x));
375 assert_eq!(a.mul(a), GoldilocksField::ONE);
377 }
378
379 #[test]
380 fn test_sbox() {
381 let x = GoldilocksField::new(42);
382 assert_eq!(x.sbox(), x.pow(ALPHA));
383 assert_eq!(GoldilocksField::ZERO.sbox(), GoldilocksField::ZERO);
384 assert_eq!(GoldilocksField::ONE.sbox(), GoldilocksField::ONE);
385 let z = GoldilocksField::new(1000);
386 assert_ne!(z.sbox(), z);
387 assert_eq!(z.sbox(), z.pow(7));
388 }
389
390 #[test]
391 fn test_permutation_deterministic() {
392 let input: [GoldilocksField; T] =
393 core::array::from_fn(|i| GoldilocksField::new(i as u64 + 1));
394 let mut s1 = Poseidon2Sponge { state: input };
395 let mut s2 = Poseidon2Sponge { state: input };
396 s1.permutation();
397 s2.permutation();
398 assert_eq!(s1.state, s2.state);
399 }
400
401 #[test]
402 fn test_permutation_diffusion() {
403 let base: [GoldilocksField; T] =
404 core::array::from_fn(|i| GoldilocksField::new(i as u64 + 100));
405 let mut s_base = Poseidon2Sponge { state: base };
406 s_base.permutation();
407
408 let mut tweaked = base;
409 tweaked[0] = tweaked[0].add(GoldilocksField::ONE);
410 let mut s_tweak = Poseidon2Sponge { state: tweaked };
411 s_tweak.permutation();
412
413 for i in 0..T {
414 assert_ne!(
415 s_base.state[i], s_tweak.state[i],
416 "Element {i} unchanged after input tweak"
417 );
418 }
419 }
420
421 #[test]
422 fn test_hash_bytes_deterministic() {
423 assert_eq!(hash_bytes(b"hello world"), hash_bytes(b"hello world"));
424 }
425
426 #[test]
427 fn test_hash_bytes_different_inputs() {
428 assert_ne!(hash_bytes(b"hello"), hash_bytes(b"world"));
429 }
430
431 #[test]
432 fn test_absorb_squeeze() {
433 let elems: Vec<GoldilocksField> =
434 (0..10).map(|i| GoldilocksField::new(i * 7 + 3)).collect();
435
436 let mut h1 = Poseidon2Hasher::new();
437 h1.absorb(&elems);
438 let out1 = h1.squeeze(4);
439
440 let mut h2 = Poseidon2Hasher::new();
441 h2.absorb(&elems);
442 let out2 = h2.squeeze(4);
443
444 assert_eq!(out1, out2);
445 assert!(out1.iter().any(|e| *e != GoldilocksField::ZERO));
446 }
447
448 #[test]
449 fn test_hash_fields() {
450 let elems: Vec<GoldilocksField> = (1..=5).map(GoldilocksField::new).collect();
451 assert_eq!(hash_fields(&elems), hash_fields(&elems));
452 }
453
454 #[test]
455 fn test_empty_hash() {
456 let h = hash_bytes(b"");
457 assert_eq!(h, hash_bytes(b""));
458 assert_ne!(h, [0u8; 32]);
459 }
460
461 #[test]
462 fn test_collision_resistance() {
463 let hashes: Vec<[u8; 32]> = (0u64..20).map(|i| hash_bytes(&i.to_le_bytes())).collect();
464 for i in 0..hashes.len() {
465 for j in i + 1..hashes.len() {
466 assert_ne!(hashes[i], hashes[j], "Collision between inputs {i} and {j}");
467 }
468 }
469 }
470}