snarkvm_algorithms/crypto_hash/
poseidon.rs1use crate::{AlgebraicSponge, DuplexSpongeMode, nonnative_params::*};
17use snarkvm_fields::{FieldParameters, PoseidonParameters, PrimeField, ToConstraintField};
18use snarkvm_utilities::{BigInteger, FromBits, ToBits};
19
20use smallvec::SmallVec;
21use std::{
22 iter::Peekable,
23 ops::{Index, IndexMut},
24 sync::Arc,
25};
26
27#[derive(Copy, Clone, Debug)]
28pub struct State<F: PrimeField, const RATE: usize, const CAPACITY: usize> {
29 capacity_state: [F; CAPACITY],
30 rate_state: [F; RATE],
31}
32
33impl<F: PrimeField, const RATE: usize, const CAPACITY: usize> Default for State<F, RATE, CAPACITY> {
34 fn default() -> Self {
35 Self { capacity_state: [F::zero(); CAPACITY], rate_state: [F::zero(); RATE] }
36 }
37}
38
39impl<F: PrimeField, const RATE: usize, const CAPACITY: usize> State<F, RATE, CAPACITY> {
40 pub fn iter(&self) -> impl Iterator<Item = &F> + Clone {
42 self.capacity_state.iter().chain(self.rate_state.iter())
43 }
44
45 pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut F> {
47 self.capacity_state.iter_mut().chain(self.rate_state.iter_mut())
48 }
49}
50
51impl<F: PrimeField, const RATE: usize, const CAPACITY: usize> Index<usize> for State<F, RATE, CAPACITY> {
52 type Output = F;
53
54 fn index(&self, index: usize) -> &Self::Output {
55 assert!(index < RATE + CAPACITY, "Index out of bounds: index is {} but length is {}", index, RATE + CAPACITY);
56 if index < CAPACITY { &self.capacity_state[index] } else { &self.rate_state[index - CAPACITY] }
57 }
58}
59
60impl<F: PrimeField, const RATE: usize, const CAPACITY: usize> IndexMut<usize> for State<F, RATE, CAPACITY> {
61 fn index_mut(&mut self, index: usize) -> &mut Self::Output {
62 assert!(index < RATE + CAPACITY, "Index out of bounds: index is {} but length is {}", index, RATE + CAPACITY);
63 if index < CAPACITY { &mut self.capacity_state[index] } else { &mut self.rate_state[index - CAPACITY] }
64 }
65}
66
67#[derive(Clone, Debug, PartialEq, Eq)]
68pub struct Poseidon<F: PrimeField, const RATE: usize> {
69 parameters: Arc<PoseidonParameters<F, RATE, 1>>,
70}
71
72impl<F: PrimeField, const RATE: usize> Poseidon<F, RATE> {
73 pub fn setup() -> Self {
75 Self { parameters: Arc::new(F::default_poseidon_parameters::<RATE>().unwrap()) }
76 }
77
78 pub fn evaluate(&self, input: &[F]) -> F {
81 self.evaluate_many(input, 1)[0]
82 }
83
84 pub fn evaluate_many(&self, input: &[F], num_outputs: usize) -> Vec<F> {
88 let mut sponge = PoseidonSponge::<F, RATE, 1>::new_with_parameters(&self.parameters);
89 sponge.absorb_native_field_elements(input);
90 sponge.squeeze_native_field_elements(num_outputs).to_vec()
91 }
92
93 pub fn evaluate_with_len(&self, input: &[F]) -> F {
96 self.evaluate(&[vec![F::from(input.len() as u128)], input.to_vec()].concat())
97 }
98
99 pub fn parameters(&self) -> &Arc<PoseidonParameters<F, RATE, 1>> {
100 &self.parameters
101 }
102}
103
104#[derive(Clone, Debug)]
111pub struct PoseidonSponge<F: PrimeField, const RATE: usize, const CAPACITY: usize> {
112 parameters: Arc<PoseidonParameters<F, RATE, CAPACITY>>,
114 state: State<F, RATE, CAPACITY>,
116 pub mode: DuplexSpongeMode,
118 adjustment_factor_lookup_table: Arc<[F]>,
120}
121
122impl<F: PrimeField, const RATE: usize> AlgebraicSponge<F, RATE> for PoseidonSponge<F, RATE, 1> {
123 type Parameters = Arc<PoseidonParameters<F, RATE, 1>>;
124
125 fn sample_parameters() -> Self::Parameters {
126 Arc::new(F::default_poseidon_parameters::<RATE>().unwrap())
127 }
128
129 fn new_with_parameters(parameters: &Self::Parameters) -> Self {
130 Self {
131 parameters: parameters.clone(),
132 state: State::default(),
133 mode: DuplexSpongeMode::Absorbing { next_absorb_index: 0 },
134 adjustment_factor_lookup_table: {
135 let capacity = F::size_in_bits() - 1;
136 let mut table = Vec::<F>::with_capacity(capacity);
137
138 let mut cur = F::one();
139 for _ in 0..capacity {
140 table.push(cur);
141 cur.double_in_place();
142 }
143
144 table.into()
145 },
146 }
147 }
148
149 fn absorb_native_field_elements<T: ToConstraintField<F>>(&mut self, elements: &[T]) {
151 let input = elements.iter().flat_map(|e| e.to_field_elements().unwrap()).collect::<Vec<_>>();
152 if !input.is_empty() {
153 match self.mode {
154 DuplexSpongeMode::Absorbing { mut next_absorb_index } => {
155 if next_absorb_index == RATE {
156 self.permute();
157 next_absorb_index = 0;
158 }
159 self.absorb_internal(next_absorb_index, &input);
160 }
161 DuplexSpongeMode::Squeezing { next_squeeze_index: _ } => {
162 self.permute();
163 self.absorb_internal(0, &input);
164 }
165 }
166 }
167 }
168
169 fn absorb_nonnative_field_elements<Target: PrimeField>(&mut self, elements: impl IntoIterator<Item = Target>) {
171 Self::push_elements_to_sponge(self, elements, OptimizationType::Weight);
172 }
173
174 fn squeeze_nonnative_field_elements<Target: PrimeField>(&mut self, num: usize) -> SmallVec<[Target; 10]> {
175 self.get_fe(num, false)
176 }
177
178 fn squeeze_native_field_elements(&mut self, num_elements: usize) -> SmallVec<[F; 10]> {
179 if num_elements == 0 {
180 return SmallVec::<[F; 10]>::new();
181 }
182 let mut output = if num_elements <= 10 {
183 smallvec::smallvec_inline![F::zero(); 10]
184 } else {
185 smallvec::smallvec![F::zero(); num_elements]
186 };
187
188 match self.mode {
189 DuplexSpongeMode::Absorbing { next_absorb_index: _ } => {
190 self.permute();
191 self.squeeze_internal(0, &mut output[..num_elements]);
192 }
193 DuplexSpongeMode::Squeezing { mut next_squeeze_index } => {
194 if next_squeeze_index == RATE {
195 self.permute();
196 next_squeeze_index = 0;
197 }
198 self.squeeze_internal(next_squeeze_index, &mut output[..num_elements]);
199 }
200 }
201
202 output.truncate(num_elements);
203 output
204 }
205
206 fn squeeze_short_nonnative_field_elements<Target: PrimeField>(&mut self, num: usize) -> SmallVec<[Target; 10]> {
208 self.get_fe(num, true)
209 }
210}
211
212impl<F: PrimeField, const RATE: usize> PoseidonSponge<F, RATE, 1> {
213 #[inline]
214 fn apply_ark(&mut self, round_number: usize) {
215 for (state_elem, ark_elem) in self.state.iter_mut().zip(&self.parameters.ark[round_number]) {
216 *state_elem += ark_elem;
217 }
218 }
219
220 #[inline]
221 fn apply_s_box(&mut self, is_full_round: bool) {
222 let pow_alpha_in_place: fn(&mut F) = match self.parameters.alpha {
223 3 => pow_3_in_place,
224 5 => pow_5_in_place,
225 17 => pow_17_in_place,
226 alpha => panic!("No optimized S-box for alpha = {alpha}"),
227 };
228
229 if is_full_round {
230 self.state.iter_mut().for_each(pow_alpha_in_place);
231 } else {
232 pow_alpha_in_place(&mut self.state[0]);
233 }
234 }
235
236 #[inline]
237 fn apply_mds(&mut self) {
238 let mut new_state = State::default();
239 let curr_state: Vec<F> = self.state.iter().copied().collect::<Vec<_>>();
240 new_state.iter_mut().zip(&self.parameters.mds).for_each(|(new_elem, mds_row)| {
241 *new_elem = F::sum_of_products(&curr_state, mds_row);
242 });
243 self.state = new_state;
244 }
245
246 #[inline]
247 fn permute(&mut self) {
248 let partial_rounds = self.parameters.partial_rounds;
250 let full_rounds = self.parameters.full_rounds;
251 let full_rounds_over_2 = full_rounds / 2;
252 let partial_round_range = full_rounds_over_2..(full_rounds_over_2 + partial_rounds);
253
254 for i in 0..(partial_rounds + full_rounds) {
256 let is_full_round = !partial_round_range.contains(&i);
257 self.apply_ark(i);
258 self.apply_s_box(is_full_round);
259 self.apply_mds();
260 }
261 }
262
263 #[inline]
265 fn absorb_internal(&mut self, mut rate_start: usize, input: &[F]) {
266 if !input.is_empty() {
267 let first_chunk_size = std::cmp::min(RATE - rate_start, input.len());
268 let num_elements_remaining = input.len() - first_chunk_size;
269 let (first_chunk, rest_chunk) = input.split_at(first_chunk_size);
270 let rest_chunks = rest_chunk.chunks(RATE);
271 let total_num_chunks = 1 + (num_elements_remaining / RATE) +
276 usize::from((num_elements_remaining % RATE) != 0);
279
280 for (i, chunk) in std::iter::once(first_chunk).chain(rest_chunks).enumerate() {
283 for (element, state_elem) in chunk.iter().zip(&mut self.state.rate_state[rate_start..]) {
284 *state_elem += element;
285 }
286 if i == total_num_chunks - 1 {
289 self.mode = DuplexSpongeMode::Absorbing { next_absorb_index: rate_start + chunk.len() };
290 return;
291 } else {
292 self.permute();
293 }
294 rate_start = 0;
295 }
296 }
297 }
298
299 #[inline]
301 fn squeeze_internal(&mut self, mut rate_start: usize, output: &mut [F]) {
302 let output_size = output.len();
303 if output_size != 0 {
304 let first_chunk_size = std::cmp::min(RATE - rate_start, output.len());
305 let num_output_remaining = output.len() - first_chunk_size;
306 let (first_chunk, rest_chunk) = output.split_at_mut(first_chunk_size);
307 assert_eq!(rest_chunk.len(), num_output_remaining);
308 let rest_chunks = rest_chunk.chunks_mut(RATE);
309 let total_num_chunks = 1 + (num_output_remaining / RATE) +
314 usize::from((num_output_remaining % RATE) != 0);
317
318 for (i, chunk) in std::iter::once(first_chunk).chain(rest_chunks).enumerate() {
321 let range = rate_start..(rate_start + chunk.len());
322 debug_assert_eq!(
323 chunk.len(),
324 self.state.rate_state[range.clone()].len(),
325 "failed with squeeze {output_size} at rate {RATE} and rate_start {rate_start}"
326 );
327 chunk.copy_from_slice(&self.state.rate_state[range]);
328 if i == total_num_chunks - 1 {
331 self.mode = DuplexSpongeMode::Squeezing { next_squeeze_index: (rate_start + chunk.len()) };
332 return;
333 } else {
334 self.permute();
335 }
336 rate_start = 0;
337 }
338 }
339 }
340
341 pub fn compress_elements<TargetField: PrimeField, I: Iterator<Item = (F, F)>>(
344 &self,
345 mut src_limbs: Peekable<I>,
346 ty: OptimizationType,
347 ) -> Vec<F> {
348 let capacity = F::size_in_bits() - 1;
349 let mut dest_limbs = Vec::<F>::new();
350
351 let params = get_params(TargetField::size_in_bits(), F::size_in_bits(), ty);
352
353 let mut num_bits = Vec::new();
355
356 while let Some(first) = src_limbs.next() {
357 let second = src_limbs.peek();
358
359 let first_max_bits_per_limb = params.bits_per_limb + crate::overhead!(first.1 + F::one(), &mut num_bits);
360 let second_max_bits_per_limb = if let Some(second) = second {
361 params.bits_per_limb + crate::overhead!(second.1 + F::one(), &mut num_bits)
362 } else {
363 0
364 };
365
366 if let Some(second) = second {
367 if first_max_bits_per_limb + second_max_bits_per_limb <= capacity {
368 let adjustment_factor = &self.adjustment_factor_lookup_table[second_max_bits_per_limb];
369
370 dest_limbs.push(first.0 * adjustment_factor + second.0);
371 src_limbs.next();
372 } else {
373 dest_limbs.push(first.0);
374 }
375 } else {
376 dest_limbs.push(first.0);
377 }
378 }
379
380 dest_limbs
381 }
382
383 pub fn get_limbs_representations<TargetField: PrimeField>(
387 elem: &TargetField,
388 optimization_type: OptimizationType,
389 ) -> SmallVec<[F; 10]> {
390 Self::get_limbs_representations_from_big_integer::<TargetField>(&elem.to_bigint(), optimization_type)
391 }
392
393 pub fn get_limbs_representations_from_big_integer<TargetField: PrimeField>(
395 elem: &<TargetField as PrimeField>::BigInteger,
396 optimization_type: OptimizationType,
397 ) -> SmallVec<[F; 10]> {
398 let params = get_params(TargetField::size_in_bits(), F::size_in_bits(), optimization_type);
399
400 let mut cur_bits = Vec::new();
402 let mut limbs: SmallVec<[F; 10]> = SmallVec::new();
404 let mut cur = *elem;
405 for _ in 0..params.num_limbs {
406 cur.write_bits_be(&mut cur_bits); let cur_mod_r =
408 <F as PrimeField>::BigInteger::from_bits_be(&cur_bits[cur_bits.len() - params.bits_per_limb..])
409 .unwrap(); limbs.push(F::from_bigint(cur_mod_r).unwrap());
411 cur.divn(params.bits_per_limb as u32);
412 cur_bits.clear();
414 }
415
416 limbs.reverse();
418
419 limbs
420 }
421
422 pub fn push_elements_to_sponge<TargetField: PrimeField>(
425 &mut self,
426 src: impl IntoIterator<Item = TargetField>,
427 ty: OptimizationType,
428 ) {
429 let src_limbs = src
430 .into_iter()
431 .flat_map(|elem| {
432 let limbs = Self::get_limbs_representations(&elem, ty);
433 limbs.into_iter().map(|limb| (limb, F::one()))
434 })
438 .peekable();
439
440 let dest_limbs = self.compress_elements::<TargetField, _>(src_limbs, ty);
441 self.absorb_native_field_elements(&dest_limbs);
442 }
443
444 pub fn get_bits(&mut self, num_bits: usize) -> Vec<bool> {
448 let bits_per_element = F::size_in_bits() - 1;
449 let num_elements = num_bits.div_ceil(bits_per_element);
450
451 let src_elements = self.squeeze_native_field_elements(num_elements);
452 let mut dest_bits = Vec::<bool>::with_capacity(num_elements * bits_per_element);
453
454 let skip = (F::Parameters::REPR_SHAVE_BITS + 1) as usize;
455 for elem in src_elements.iter() {
456 let elem_bits = elem.to_bigint().to_bits_be();
458 dest_bits.extend_from_slice(&elem_bits[skip..]);
459 }
460 dest_bits.truncate(num_bits);
461
462 dest_bits
463 }
464
465 pub fn get_fe<TargetField: PrimeField>(
469 &mut self,
470 num_elements: usize,
471 outputs_short_elements: bool,
472 ) -> SmallVec<[TargetField; 10]> {
473 let num_bits_per_nonnative = if outputs_short_elements {
474 168
475 } else {
476 TargetField::size_in_bits() - 1 };
478 let bits = self.get_bits(num_bits_per_nonnative * num_elements);
479
480 let mut lookup_table = Vec::<TargetField>::with_capacity(num_bits_per_nonnative);
481 let mut cur = TargetField::one();
482 for _ in 0..num_bits_per_nonnative {
483 lookup_table.push(cur);
484 cur.double_in_place();
485 }
486
487 let dest_elements = bits
488 .chunks_exact(num_bits_per_nonnative)
489 .map(|per_nonnative_bits| {
490 let mut res = TargetField::zero();
493
494 for (i, bit) in per_nonnative_bits.iter().rev().enumerate() {
495 if *bit {
496 res += &lookup_table[i];
497 }
498 }
499 res
500 })
501 .collect::<SmallVec<_>>();
502 debug_assert_eq!(dest_elements.len(), num_elements);
503
504 dest_elements
505 }
506}
507
508#[inline]
512fn pow_3_in_place<F: PrimeField>(val: &mut F) {
513 let val_copy = *val;
514 val.square_in_place();
515 val.mul_assign(val_copy);
516}
517
518#[inline]
519fn pow_5_in_place<F: PrimeField>(val: &mut F) {
520 let val_copy = *val;
521 val.square_in_place();
522 val.square_in_place();
523 val.mul_assign(val_copy);
524}
525
526#[inline]
527fn pow_17_in_place<F: PrimeField>(val: &mut F) {
528 let val_copy = *val;
529 val.square_in_place();
530 val.square_in_place();
531 val.square_in_place();
532 val.square_in_place();
533 val.mul_assign(val_copy);
534}