From 55401d517cbf94b72bde7205739484dac88a689b Mon Sep 17 00:00:00 2001 From: Weikeng Chen Date: Wed, 14 Jul 2021 23:05:15 -0700 Subject: [PATCH] Traits for Default Poseidon Parameters + Poseidon cleanup (#22) * half way adding Rescue * add the grain lfsr * add Grain LFSR * update changelog * minor * minor * This PR masks away 21 * remove dependency on bigint * no-std * make param fields public * Update src/poseidon/grain_lfsr.rs Co-authored-by: Pratyush Mishra * Update src/poseidon/grain_lfsr.rs Co-authored-by: Pratyush Mishra * Update src/poseidon/mod.rs Co-authored-by: Pratyush Mishra * Update src/poseidon/mod.rs Co-authored-by: Pratyush Mishra * local changes * align with the reference impl * Update src/poseidon/mod.rs Co-authored-by: Pratyush Mishra * edit * rename the structure * make a few fields public * doc Co-authored-by: Pratyush Mishra --- CHANGELOG.md | 4 +- src/absorb.rs | 1 + src/lib.rs | 15 ++ src/poseidon/constraints.rs | 114 ++++----- src/poseidon/grain_lfsr.rs | 216 +++++++++++++++++ src/poseidon/mod.rs | 282 +++++++++++++--------- src/poseidon/tests.rs | 4 + src/poseidon/traits.rs | 454 ++++++++++++++++++++++++++++++++++++ 8 files changed, 909 insertions(+), 181 deletions(-) create mode 100644 src/poseidon/grain_lfsr.rs create mode 100644 src/poseidon/traits.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index bacd0f3..41c0b54 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,10 +4,12 @@ ### Breaking changes -- [\#21](https://github.com/arkworks-rs/sponge/pull/21) Fix Poseidon `random_ark` being too short. +- [\#22](https://github.com/arkworks-rs/sponge/pull/22) Clean up the Poseidon parameter and sponge structures. ### Features +- [\#22](https://github.com/arkworks-rs/sponge/pull/22) Add traits and derivations for default Poseidon parameters. + ### Improvements ### Bug fixes diff --git a/src/absorb.rs b/src/absorb.rs index 78dd34d..62c70ca 100644 --- a/src/absorb.rs +++ b/src/absorb.rs @@ -377,6 +377,7 @@ mod tests { use crate::{batch_field_cast, field_cast}; use ark_ff::UniformRand; use ark_std::test_rng; + use ark_std::vec::Vec; use ark_test_curves::bls12_381::Fr; #[test] diff --git a/src/lib.rs b/src/lib.rs index 6e6aaef..0548771 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -197,3 +197,18 @@ pub trait SpongeExt: CryptographicSponge { /// Consumes `self` and returns the state. fn into_state(self) -> Self::State; } + +/// The mode structure for duplex sponges +#[derive(Clone, Debug)] +pub enum DuplexSpongeMode { + /// The sponge is currently absorbing data. + Absorbing { + /// next position of the state to be XOR-ed when absorbing. + next_absorb_index: usize, + }, + /// The sponge is currently squeezing data out. + Squeezing { + /// next position of the state to be outputted when squeezing. + next_squeeze_index: usize, + }, +} diff --git a/src/poseidon/constraints.rs b/src/poseidon/constraints.rs index f5d56c9..b7f6417 100644 --- a/src/poseidon/constraints.rs +++ b/src/poseidon/constraints.rs @@ -1,6 +1,7 @@ use crate::constraints::AbsorbGadget; use crate::constraints::CryptographicSpongeVar; -use crate::poseidon::{PoseidonParameters, PoseidonSponge, PoseidonSpongeMode}; +use crate::poseidon::{PoseidonParameters, PoseidonSponge}; +use crate::DuplexSpongeMode; use ark_ff::{FpParameters, PrimeField}; use ark_r1cs_std::fields::fp::FpVar; use ark_r1cs_std::prelude::*; @@ -16,31 +17,17 @@ use ark_std::vec::Vec; /// /// [cos]: https://eprint.iacr.org/2019/1076 pub struct PoseidonSpongeVar { - /// constraint system + /// Constraint system pub cs: ConstraintSystemRef, - // Sponge Parameters - /// number of rounds in a full-round operation - pub full_rounds: u32, - /// number of rounds in a partial-round operation - pub partial_rounds: u32, - /// Exponent used in S-boxes - pub alpha: u64, - /// Additive Round keys. These are added before each MDS matrix application to make it an affine shift. - /// They are indexed by `ark[round_num][state_element_index]` - pub ark: Vec>, - /// Maximally Distance Separating Matrix. - pub mds: Vec>, - /// the rate - pub rate: usize, - /// the capacity - pub capacity: usize, + /// Sponge Parameters + pub parameters: PoseidonParameters, // Sponge State - /// the sponge's state + /// The sponge's state pub state: Vec>, - /// the mode - mode: PoseidonSpongeMode, + /// The mode + pub mode: DuplexSpongeMode, } impl PoseidonSpongeVar { @@ -53,12 +40,12 @@ impl PoseidonSpongeVar { // Full rounds apply the S Box (x^alpha) to every element of state if is_full_round { for state_item in state.iter_mut() { - *state_item = state_item.pow_by_constant(&[self.alpha])?; + *state_item = state_item.pow_by_constant(&[self.parameters.alpha])?; } } - // Partial rounds apply the S Box (x^alpha) to just the final element of state + // Partial rounds apply the S Box (x^alpha) to just the first element of state else { - state[state.len() - 1] = state[state.len() - 1].pow_by_constant(&[self.alpha])?; + state[0] = state[0].pow_by_constant(&[self.parameters.alpha])?; } Ok(()) @@ -67,7 +54,7 @@ impl PoseidonSpongeVar { #[tracing::instrument(target = "r1cs", skip(self))] fn apply_ark(&self, state: &mut [FpVar], round_number: usize) -> Result<(), SynthesisError> { for (i, state_elem) in state.iter_mut().enumerate() { - *state_elem += self.ark[round_number][i]; + *state_elem += self.parameters.ark[round_number][i]; } Ok(()) } @@ -79,7 +66,7 @@ impl PoseidonSpongeVar { for i in 0..state.len() { let mut cur = zero.clone(); for (j, state_elem) in state.iter().enumerate() { - let term = state_elem * self.mds[i][j]; + let term = state_elem * self.parameters.mds[i][j]; cur += &term; } new_state.push(cur); @@ -90,23 +77,23 @@ impl PoseidonSpongeVar { #[tracing::instrument(target = "r1cs", skip(self))] fn permute(&mut self) -> Result<(), SynthesisError> { - let full_rounds_over_2 = self.full_rounds / 2; + let full_rounds_over_2 = self.parameters.full_rounds / 2; let mut state = self.state.clone(); for i in 0..full_rounds_over_2 { - self.apply_ark(&mut state, i as usize)?; + self.apply_ark(&mut state, i)?; self.apply_s_box(&mut state, true)?; self.apply_mds(&mut state)?; } - for i in full_rounds_over_2..(full_rounds_over_2 + self.partial_rounds) { - self.apply_ark(&mut state, i as usize)?; + for i in full_rounds_over_2..(full_rounds_over_2 + self.parameters.partial_rounds) { + self.apply_ark(&mut state, i)?; self.apply_s_box(&mut state, false)?; self.apply_mds(&mut state)?; } - for i in - (full_rounds_over_2 + self.partial_rounds)..(self.partial_rounds + self.full_rounds) + for i in (full_rounds_over_2 + self.parameters.partial_rounds) + ..(self.parameters.partial_rounds + self.parameters.full_rounds) { - self.apply_ark(&mut state, i as usize)?; + self.apply_ark(&mut state, i)?; self.apply_s_box(&mut state, true)?; self.apply_mds(&mut state)?; } @@ -124,24 +111,24 @@ impl PoseidonSpongeVar { let mut remaining_elements = elements; loop { // if we can finish in this call - if rate_start_index + remaining_elements.len() <= self.rate { + if rate_start_index + remaining_elements.len() <= self.parameters.rate { for (i, element) in remaining_elements.iter().enumerate() { - self.state[i + rate_start_index] += element; + self.state[self.parameters.capacity + i + rate_start_index] += element; } - self.mode = PoseidonSpongeMode::Absorbing { + self.mode = DuplexSpongeMode::Absorbing { next_absorb_index: rate_start_index + remaining_elements.len(), }; return Ok(()); } // otherwise absorb (rate - rate_start_index) elements - let num_elements_absorbed = self.rate - rate_start_index; + let num_elements_absorbed = self.parameters.rate - rate_start_index; for (i, element) in remaining_elements .iter() .enumerate() .take(num_elements_absorbed) { - self.state[i + rate_start_index] += element; + self.state[self.parameters.capacity + i + rate_start_index] += element; } self.permute()?; // the input elements got truncated by num elements absorbed @@ -160,23 +147,25 @@ impl PoseidonSpongeVar { let mut remaining_output = output; loop { // if we can finish in this call - if rate_start_index + remaining_output.len() <= self.rate { + if rate_start_index + remaining_output.len() <= self.parameters.rate { remaining_output.clone_from_slice( - &self.state[rate_start_index..(remaining_output.len() + rate_start_index)], + &self.state[self.parameters.capacity + rate_start_index + ..(self.parameters.capacity + remaining_output.len() + rate_start_index)], ); - self.mode = PoseidonSpongeMode::Squeezing { + self.mode = DuplexSpongeMode::Squeezing { next_squeeze_index: rate_start_index + remaining_output.len(), }; return Ok(()); } // otherwise squeeze (rate - rate_start_index) elements - let num_elements_squeezed = self.rate - rate_start_index; + let num_elements_squeezed = self.parameters.rate - rate_start_index; remaining_output[..num_elements_squeezed].clone_from_slice( - &self.state[rate_start_index..(num_elements_squeezed + rate_start_index)], + &self.state[self.parameters.capacity + rate_start_index + ..(self.parameters.capacity + num_elements_squeezed + rate_start_index)], ); // Unless we are done with squeezing in this call, permute. - if remaining_output.len() != self.rate { + if remaining_output.len() != self.parameters.rate { self.permute()?; } // Repeat with updated output slices and rate start index @@ -190,34 +179,17 @@ impl CryptographicSpongeVar> for PoseidonSpo type Parameters = PoseidonParameters; #[tracing::instrument(target = "r1cs", skip(cs))] - fn new(cs: ConstraintSystemRef, params: &PoseidonParameters) -> Self { - let full_rounds = params.full_rounds; - let partial_rounds = params.partial_rounds; - let alpha = params.alpha; - - let mds = params.mds.to_vec(); - - let ark = params.ark.to_vec(); - - let rate = 2; - let capacity = 1; + fn new(cs: ConstraintSystemRef, parameters: &PoseidonParameters) -> Self { let zero = FpVar::::zero(); - let state = vec![zero; rate + capacity]; - let mode = PoseidonSpongeMode::Absorbing { + let state = vec![zero; parameters.rate + parameters.capacity]; + let mode = DuplexSpongeMode::Absorbing { next_absorb_index: 0, }; Self { cs, - full_rounds, - partial_rounds, - alpha, - ark, - mds, - + parameters: parameters.clone(), state, - rate, - capacity, mode, } } @@ -235,15 +207,15 @@ impl CryptographicSpongeVar> for PoseidonSpo } match self.mode { - PoseidonSpongeMode::Absorbing { next_absorb_index } => { + DuplexSpongeMode::Absorbing { next_absorb_index } => { let mut absorb_index = next_absorb_index; - if absorb_index == self.rate { + if absorb_index == self.parameters.rate { self.permute()?; absorb_index = 0; } self.absorb_internal(absorb_index, input.as_slice())?; } - PoseidonSpongeMode::Squeezing { + DuplexSpongeMode::Squeezing { next_squeeze_index: _, } => { self.permute()?; @@ -294,15 +266,15 @@ impl CryptographicSpongeVar> for PoseidonSpo let zero = FpVar::zero(); let mut squeezed_elems = vec![zero; num_elements]; match self.mode { - PoseidonSpongeMode::Absorbing { + DuplexSpongeMode::Absorbing { next_absorb_index: _, } => { self.permute()?; self.squeeze_internal(0, &mut squeezed_elems)?; } - PoseidonSpongeMode::Squeezing { next_squeeze_index } => { + DuplexSpongeMode::Squeezing { next_squeeze_index } => { let mut squeeze_index = next_squeeze_index; - if squeeze_index == self.rate { + if squeeze_index == self.parameters.rate { self.permute()?; squeeze_index = 0; } diff --git a/src/poseidon/grain_lfsr.rs b/src/poseidon/grain_lfsr.rs new file mode 100644 index 0000000..3ecc661 --- /dev/null +++ b/src/poseidon/grain_lfsr.rs @@ -0,0 +1,216 @@ +#![allow(dead_code)] + +use ark_ff::{BigInteger, FpParameters, PrimeField}; +use ark_std::vec::Vec; + +pub struct PoseidonGrainLFSR { + pub prime_num_bits: u64, + + pub state: [bool; 80], + pub head: usize, +} + +#[allow(unused_variables)] +impl PoseidonGrainLFSR { + pub fn new( + is_sbox_an_inverse: bool, + prime_num_bits: u64, + state_len: u64, + num_full_rounds: u64, + num_partial_rounds: u64, + ) -> Self { + let mut state = [false; 80]; + + // b0, b1 describes the field + state[1] = true; + + // b2, ..., b5 describes the S-BOX + if is_sbox_an_inverse { + state[5] = true; + } else { + state[5] = false; + } + + // b6, ..., b17 are the binary representation of n (prime_num_bits) + { + let mut cur = prime_num_bits; + for i in (6..=17).rev() { + state[i] = cur & 1 == 1; + cur >>= 1; + } + } + + // b18, ..., b29 are the binary representation of t (state_len, rate + capacity) + { + let mut cur = state_len; + for i in (18..=29).rev() { + state[i] = cur & 1 == 1; + cur >>= 1; + } + } + + // b30, ..., b39 are the binary representation of R_F (the number of full rounds) + { + let mut cur = num_full_rounds; + for i in (30..=39).rev() { + state[i] = cur & 1 == 1; + cur >>= 1; + } + } + + // b40, ..., b49 are the binary representation of R_P (the number of partial rounds) + { + let mut cur = num_partial_rounds; + for i in (40..=49).rev() { + state[i] = cur & 1 == 1; + cur >>= 1; + } + } + + // b50, ..., b79 are set to 1 + for i in 50..=79 { + state[i] = true; + } + + let head = 0; + + let mut res = Self { + prime_num_bits, + state, + head, + }; + res.init(); + res + } + + pub fn get_bits(&mut self, num_bits: usize) -> Vec { + let mut res = Vec::new(); + + for _ in 0..num_bits { + // Obtain the first bit + let mut new_bit = self.update(); + + // Loop until the first bit is true + while new_bit == false { + // Discard the second bit + let _ = self.update(); + // Obtain another first bit + new_bit = self.update(); + } + + // Obtain the second bit + res.push(self.update()); + } + + res + } + + pub fn get_field_elements_rejection_sampling( + &mut self, + num_elems: usize, + ) -> Vec { + assert_eq!(F::Params::MODULUS_BITS as u64, self.prime_num_bits); + + let mut res = Vec::new(); + for _ in 0..num_elems { + // Perform rejection sampling + loop { + // Obtain n bits and make it most-significant-bit first + let mut bits = self.get_bits(self.prime_num_bits as usize); + bits.reverse(); + + // Construct the number + let bigint = F::BigInt::from_bits_le(&bits); + + if let Some(f) = F::from_repr(bigint) { + res.push(f); + break; + } + } + } + + res + } + + pub fn get_field_elements_mod_p(&mut self, num_elems: usize) -> Vec { + assert_eq!(F::Params::MODULUS_BITS as u64, self.prime_num_bits); + + let mut res = Vec::new(); + for _ in 0..num_elems { + // Obtain n bits and make it most-significant-bit first + let mut bits = self.get_bits(self.prime_num_bits as usize); + bits.reverse(); + + let bytes = bits + .chunks(8) + .map(|chunk| { + let mut result = 0u8; + for (i, bit) in chunk.iter().enumerate() { + result |= u8::from(*bit) << i + } + result + }) + .collect::>(); + + res.push(F::from_le_bytes_mod_order(&bytes)); + } + + res + } + + #[inline] + fn update(&mut self) -> bool { + let new_bit = self.state[(self.head + 62) % 80] + ^ self.state[(self.head + 51) % 80] + ^ self.state[(self.head + 38) % 80] + ^ self.state[(self.head + 23) % 80] + ^ self.state[(self.head + 13) % 80] + ^ self.state[self.head]; + self.state[self.head] = new_bit; + self.head += 1; + self.head %= 80; + + new_bit + } + + fn init(&mut self) { + for _ in 0..160 { + let new_bit = self.state[(self.head + 62) % 80] + ^ self.state[(self.head + 51) % 80] + ^ self.state[(self.head + 38) % 80] + ^ self.state[(self.head + 23) % 80] + ^ self.state[(self.head + 13) % 80] + ^ self.state[self.head]; + self.state[self.head] = new_bit; + self.head += 1; + self.head %= 80; + } + } +} + +#[cfg(test)] +mod test { + use crate::poseidon::grain_lfsr::PoseidonGrainLFSR; + use ark_ff::field_new; + use ark_test_curves::bls12_381::Fr; + + #[test] + fn test_grain_lfsr_consistency() { + let mut lfsr = PoseidonGrainLFSR::new(false, 255, 3, 8, 31); + + assert_eq!( + lfsr.get_field_elements_rejection_sampling::(1)[0], + field_new!( + Fr, + "27117311055620256798560880810000042840428971800021819916023577129547249660720" + ) + ); + assert_eq!( + lfsr.get_field_elements_rejection_sampling::(1)[0], + field_new!( + Fr, + "51641662388546346858987925410984003801092143452466182801674685248597955169158" + ) + ); + } +} diff --git a/src/poseidon/mod.rs b/src/poseidon/mod.rs index e7ef9e7..506109b 100644 --- a/src/poseidon/mod.rs +++ b/src/poseidon/mod.rs @@ -1,10 +1,9 @@ use crate::{ batch_field_cast, squeeze_field_elements_with_sizes_default_impl, Absorb, CryptographicSponge, - FieldBasedCryptographicSponge, FieldElementSize, SpongeExt, + DuplexSpongeMode, FieldBasedCryptographicSponge, FieldElementSize, SpongeExt, }; use ark_ff::{BigInteger, FpParameters, PrimeField}; use ark_std::any::TypeId; -use ark_std::rand::Rng; use ark_std::vec; use ark_std::vec::Vec; @@ -14,10 +13,32 @@ pub mod constraints; #[cfg(test)] mod tests; -#[derive(Clone)] -enum PoseidonSpongeMode { - Absorbing { next_absorb_index: usize }, - Squeezing { next_squeeze_index: usize }, +/// default parameters traits for Poseidon +pub mod traits; +pub use traits::*; + +mod grain_lfsr; + +/// Parameters and RNG used +#[derive(Clone, Debug)] +pub struct PoseidonParameters { + /// Number of rounds in a full-round operation. + pub full_rounds: usize, + /// Number of rounds in a partial-round operation. + pub partial_rounds: usize, + /// Exponent used in S-boxes. + pub alpha: u64, + /// Additive Round keys. These are added before each MDS matrix application to make it an affine shift. + /// They are indexed by `ark[round_num][state_element_index]` + pub ark: Vec>, + /// Maximally Distance Separating (MDS) Matrix. + pub mds: Vec>, + /// The rate (in terms of number of field elements). + /// See [On the Indifferentiability of the Sponge Construction](https://iacr.org/archive/eurocrypt2008/49650180/49650180.pdf) + /// for more details on the rate and capacity of a sponge. + pub rate: usize, + /// The capacity (in terms of number of field elements). + pub capacity: usize, } #[derive(Clone)] @@ -28,28 +49,14 @@ enum PoseidonSpongeMode { /// /// [cos]: https://eprint.iacr.org/2019/1076 pub struct PoseidonSponge { - // Sponge Parameters - /// number of rounds in a full-round operation - full_rounds: u32, - /// number of rounds in a partial-round operation - partial_rounds: u32, - /// Exponent used in S-boxes - alpha: u64, - /// Additive Round keys. These are added before each MDS matrix application to make it an affine shift. - /// They are indexed by `ark[round_num][state_element_index]` - ark: Vec>, - /// Maximally Distance Separating Matrix. - mds: Vec>, - /// the rate (in terms of number of field elements) - rate: usize, - /// the capacity (in terms of number of field elements) - capacity: usize, + /// Sponge Parameters + pub parameters: PoseidonParameters, // Sponge State - /// current sponge's state (current elements in the permutation block) - state: Vec, - /// current mode (whether its absorbing or squeezing) - mode: PoseidonSpongeMode, + /// Current sponge's state (current elements in the permutation block) + pub state: Vec, + /// Current mode (whether its absorbing or squeezing) + pub mode: DuplexSpongeMode, } impl PoseidonSponge { @@ -57,18 +64,18 @@ impl PoseidonSponge { // Full rounds apply the S Box (x^alpha) to every element of state if is_full_round { for elem in state { - *elem = elem.pow(&[self.alpha]); + *elem = elem.pow(&[self.parameters.alpha]); } } - // Partial rounds apply the S Box (x^alpha) to just the final element of state + // Partial rounds apply the S Box (x^alpha) to just the first element of state else { - state[state.len() - 1] = state[state.len() - 1].pow(&[self.alpha]); + state[0] = state[0].pow(&[self.parameters.alpha]); } } fn apply_ark(&self, state: &mut [F], round_number: usize) { for (i, state_elem) in state.iter_mut().enumerate() { - state_elem.add_assign(&self.ark[round_number][i]); + state_elem.add_assign(&self.parameters.ark[round_number][i]); } } @@ -77,7 +84,7 @@ impl PoseidonSponge { for i in 0..state.len() { let mut cur = F::zero(); for (j, state_elem) in state.iter().enumerate() { - let term = state_elem.mul(&self.mds[i][j]); + let term = state_elem.mul(&self.parameters.mds[i][j]); cur.add_assign(&term); } new_state.push(cur); @@ -86,24 +93,24 @@ impl PoseidonSponge { } fn permute(&mut self) { - let full_rounds_over_2 = self.full_rounds / 2; + let full_rounds_over_2 = self.parameters.full_rounds / 2; let mut state = self.state.clone(); for i in 0..full_rounds_over_2 { - self.apply_ark(&mut state, i as usize); + self.apply_ark(&mut state, i); self.apply_s_box(&mut state, true); self.apply_mds(&mut state); } - for i in full_rounds_over_2..(full_rounds_over_2 + self.partial_rounds) { - self.apply_ark(&mut state, i as usize); + for i in full_rounds_over_2..(full_rounds_over_2 + self.parameters.partial_rounds) { + self.apply_ark(&mut state, i); self.apply_s_box(&mut state, false); self.apply_mds(&mut state); } - for i in - (full_rounds_over_2 + self.partial_rounds)..(self.partial_rounds + self.full_rounds) + for i in (full_rounds_over_2 + self.parameters.partial_rounds) + ..(self.parameters.partial_rounds + self.parameters.full_rounds) { - self.apply_ark(&mut state, i as usize); + self.apply_ark(&mut state, i); self.apply_s_box(&mut state, true); self.apply_mds(&mut state); } @@ -116,24 +123,24 @@ impl PoseidonSponge { loop { // if we can finish in this call - if rate_start_index + remaining_elements.len() <= self.rate { + if rate_start_index + remaining_elements.len() <= self.parameters.rate { for (i, element) in remaining_elements.iter().enumerate() { - self.state[i + rate_start_index] += element; + self.state[self.parameters.capacity + i + rate_start_index] += element; } - self.mode = PoseidonSpongeMode::Absorbing { + self.mode = DuplexSpongeMode::Absorbing { next_absorb_index: rate_start_index + remaining_elements.len(), }; return; } // otherwise absorb (rate - rate_start_index) elements - let num_elements_absorbed = self.rate - rate_start_index; + let num_elements_absorbed = self.parameters.rate - rate_start_index; for (i, element) in remaining_elements .iter() .enumerate() .take(num_elements_absorbed) { - self.state[i + rate_start_index] += element; + self.state[self.parameters.capacity + i + rate_start_index] += element; } self.permute(); // the input elements got truncated by num elements absorbed @@ -147,23 +154,25 @@ impl PoseidonSponge { let mut output_remaining = output; loop { // if we can finish in this call - if rate_start_index + output_remaining.len() <= self.rate { + if rate_start_index + output_remaining.len() <= self.parameters.rate { output_remaining.clone_from_slice( - &self.state[rate_start_index..(output_remaining.len() + rate_start_index)], + &self.state[self.parameters.capacity + rate_start_index + ..(self.parameters.capacity + output_remaining.len() + rate_start_index)], ); - self.mode = PoseidonSpongeMode::Squeezing { + self.mode = DuplexSpongeMode::Squeezing { next_squeeze_index: rate_start_index + output_remaining.len(), }; return; } // otherwise squeeze (rate - rate_start_index) elements - let num_elements_squeezed = self.rate - rate_start_index; + let num_elements_squeezed = self.parameters.rate - rate_start_index; output_remaining[..num_elements_squeezed].clone_from_slice( - &self.state[rate_start_index..(num_elements_squeezed + rate_start_index)], + &self.state[self.parameters.capacity + rate_start_index + ..(self.parameters.capacity + num_elements_squeezed + rate_start_index)], ); // Unless we are done with squeezing in this call, permute. - if output_remaining.len() != self.rate { + if output_remaining.len() != self.parameters.rate { self.permute(); } // Repeat with updated output slices @@ -173,29 +182,24 @@ impl PoseidonSponge { } } -/// Parameters and RNG used -#[derive(Clone, Debug)] -pub struct PoseidonParameters { - full_rounds: u32, - partial_rounds: u32, - alpha: u64, - mds: Vec>, - ark: Vec>, -} - impl PoseidonParameters { /// Initialize the parameter for Poseidon Sponge. pub fn new( - full_rounds: u32, - partial_rounds: u32, + full_rounds: usize, + partial_rounds: usize, alpha: u64, mds: Vec>, ark: Vec>, + rate: usize, + capacity: usize, ) -> Self { - // shape check - assert_eq!(ark.len() as u32, full_rounds + partial_rounds); + assert_eq!(ark.len(), full_rounds + partial_rounds); for item in &ark { - assert_eq!(item.len(), 3); + assert_eq!(item.len(), rate + capacity); + } + assert_eq!(mds.len(), rate + capacity); + for item in &mds { + assert_eq!(item.len(), rate + capacity); } Self { full_rounds, @@ -203,56 +207,24 @@ impl PoseidonParameters { alpha, mds, ark, + rate, + capacity, } } - - /// Return a random round constant. - pub fn random_ark(full_rounds: u32, partial_rounds: u32, rng: &mut R) -> Vec> { - let mut ark = Vec::new(); - - for _ in 0..full_rounds + partial_rounds { - let mut res = Vec::new(); - - for _ in 0..3 { - res.push(F::rand(rng)); - } - ark.push(res); - } - - ark - } } impl CryptographicSponge for PoseidonSponge { type Parameters = PoseidonParameters; - fn new(params: &Self::Parameters) -> Self { - // Requires F to be Alt_Bn128Fr - let full_rounds = params.full_rounds; - let partial_rounds = params.partial_rounds; - let alpha = params.alpha; - - let mds = params.mds.clone(); - - let ark = params.ark.to_vec(); - - let rate = 2; - let capacity = 1; - let state = vec![F::zero(); rate + capacity]; - let mode = PoseidonSpongeMode::Absorbing { + fn new(parameters: &Self::Parameters) -> Self { + let state = vec![F::zero(); parameters.rate + parameters.capacity]; + let mode = DuplexSpongeMode::Absorbing { next_absorb_index: 0, }; Self { - full_rounds, - partial_rounds, - alpha, - ark, - mds, - + parameters: parameters.clone(), state, - rate, - capacity, mode, } } @@ -264,15 +236,15 @@ impl CryptographicSponge for PoseidonSponge { } match self.mode { - PoseidonSpongeMode::Absorbing { next_absorb_index } => { + DuplexSpongeMode::Absorbing { next_absorb_index } => { let mut absorb_index = next_absorb_index; - if absorb_index == self.rate { + if absorb_index == self.parameters.rate { self.permute(); absorb_index = 0; } self.absorb_internal(absorb_index, elems.as_slice()); } - PoseidonSpongeMode::Squeezing { + DuplexSpongeMode::Squeezing { next_squeeze_index: _, } => { self.permute(); @@ -349,15 +321,15 @@ impl FieldBasedCryptographicSponge for PoseidonSponge { fn squeeze_native_field_elements(&mut self, num_elements: usize) -> Vec { let mut squeezed_elems = vec![F::zero(); num_elements]; match self.mode { - PoseidonSpongeMode::Absorbing { + DuplexSpongeMode::Absorbing { next_absorb_index: _, } => { self.permute(); self.squeeze_internal(0, &mut squeezed_elems); } - PoseidonSpongeMode::Squeezing { next_squeeze_index } => { + DuplexSpongeMode::Squeezing { next_squeeze_index } => { let mut squeeze_index = next_squeeze_index; - if squeeze_index == self.rate { + if squeeze_index == self.parameters.rate { self.permute(); squeeze_index = 0; } @@ -373,7 +345,7 @@ impl FieldBasedCryptographicSponge for PoseidonSponge { /// Stores the state of a Poseidon Sponge. Does not store any parameter. pub struct PoseidonSpongeState { state: Vec, - mode: PoseidonSpongeMode, + mode: DuplexSpongeMode, } impl SpongeExt for PoseidonSponge { @@ -393,3 +365,95 @@ impl SpongeExt for PoseidonSponge { } } } + +#[cfg(test)] +mod test { + use crate::poseidon::{ + PoseidonDefaultParameters, PoseidonDefaultParametersEntry, PoseidonDefaultParametersField, + }; + use crate::{poseidon::PoseidonSponge, CryptographicSponge, FieldBasedCryptographicSponge}; + use ark_ff::{field_new, BigInteger256, FftParameters, Fp256, Fp256Parameters, FpParameters}; + use ark_test_curves::bls12_381::FrParameters; + + pub struct TestFrParameters; + + impl Fp256Parameters for TestFrParameters {} + impl FftParameters for TestFrParameters { + type BigInt = ::BigInt; + const TWO_ADICITY: u32 = FrParameters::TWO_ADICITY; + const TWO_ADIC_ROOT_OF_UNITY: Self::BigInt = FrParameters::TWO_ADIC_ROOT_OF_UNITY; + } + + // This TestFrParameters is the same as the BLS12-381's Fr. + // MODULUS = 52435875175126190479447740508185965837690552500527637822603658699938581184513 + impl FpParameters for TestFrParameters { + const MODULUS: BigInteger256 = FrParameters::MODULUS; + const MODULUS_BITS: u32 = FrParameters::MODULUS_BITS; + const CAPACITY: u32 = FrParameters::CAPACITY; + const REPR_SHAVE_BITS: u32 = FrParameters::REPR_SHAVE_BITS; + const R: BigInteger256 = FrParameters::R; + const R2: BigInteger256 = FrParameters::R2; + const INV: u64 = FrParameters::INV; + const GENERATOR: BigInteger256 = FrParameters::GENERATOR; + const MODULUS_MINUS_ONE_DIV_TWO: BigInteger256 = FrParameters::MODULUS_MINUS_ONE_DIV_TWO; + const T: BigInteger256 = FrParameters::T; + const T_MINUS_ONE_DIV_TWO: BigInteger256 = FrParameters::T_MINUS_ONE_DIV_TWO; + } + + impl PoseidonDefaultParameters for TestFrParameters { + const PARAMS_OPT_FOR_CONSTRAINTS: [PoseidonDefaultParametersEntry; 7] = [ + PoseidonDefaultParametersEntry::new(2, 17, 8, 31, 0), + PoseidonDefaultParametersEntry::new(3, 5, 8, 56, 0), + PoseidonDefaultParametersEntry::new(4, 5, 8, 56, 0), + PoseidonDefaultParametersEntry::new(5, 5, 8, 57, 0), + PoseidonDefaultParametersEntry::new(6, 5, 8, 57, 0), + PoseidonDefaultParametersEntry::new(7, 5, 8, 57, 0), + PoseidonDefaultParametersEntry::new(8, 5, 8, 57, 0), + ]; + const PARAMS_OPT_FOR_WEIGHTS: [PoseidonDefaultParametersEntry; 7] = [ + PoseidonDefaultParametersEntry::new(2, 257, 8, 13, 0), + PoseidonDefaultParametersEntry::new(3, 257, 8, 13, 0), + PoseidonDefaultParametersEntry::new(4, 257, 8, 13, 0), + PoseidonDefaultParametersEntry::new(5, 257, 8, 13, 0), + PoseidonDefaultParametersEntry::new(6, 257, 8, 13, 0), + PoseidonDefaultParametersEntry::new(7, 257, 8, 13, 0), + PoseidonDefaultParametersEntry::new(8, 257, 8, 13, 0), + ]; + } + + pub type TestFr = Fp256; + + #[test] + fn test_poseidon_sponge_consistency() { + let sponge_param = TestFr::get_default_poseidon_parameters(2, false).unwrap(); + + let mut sponge = PoseidonSponge::::new(&sponge_param); + sponge.absorb(&vec![ + TestFr::from(0u8), + TestFr::from(1u8), + TestFr::from(2u8), + ]); + let res = sponge.squeeze_native_field_elements(3); + assert_eq!( + res[0], + field_new!( + TestFr, + "40442793463571304028337753002242186710310163897048962278675457993207843616876" + ) + ); + assert_eq!( + res[1], + field_new!( + TestFr, + "2664374461699898000291153145224099287711224021716202960480903840045233645301" + ) + ); + assert_eq!( + res[2], + field_new!( + TestFr, + "50191078828066923662070228256530692951801504043422844038937334196346054068797" + ) + ); + } +} diff --git a/src/poseidon/tests.rs b/src/poseidon/tests.rs index 1c0f570..62429e9 100644 --- a/src/poseidon/tests.rs +++ b/src/poseidon/tests.rs @@ -805,11 +805,15 @@ pub(crate) fn poseidon_parameters_for_test() -> PoseidonParameter let full_rounds = 8; let total_rounds = 37; let partial_rounds = total_rounds - full_rounds; + let capacity = 1; + let rate = 2; PoseidonParameters { full_rounds, partial_rounds, alpha, ark, mds, + rate, + capacity, } } diff --git a/src/poseidon/traits.rs b/src/poseidon/traits.rs new file mode 100644 index 0000000..d288be9 --- /dev/null +++ b/src/poseidon/traits.rs @@ -0,0 +1,454 @@ +use crate::poseidon::grain_lfsr::PoseidonGrainLFSR; +use crate::poseidon::PoseidonParameters; +use ark_ff::{fields::models::*, FpParameters, PrimeField}; +use ark_std::{vec, vec::Vec}; + +/// An entry in the default Poseidon parameters +pub struct PoseidonDefaultParametersEntry { + /// The rate (in terms of number of field elements). + pub rate: usize, + /// Exponent used in S-boxes. + pub alpha: usize, + /// Number of rounds in a full-round operation. + pub full_rounds: usize, + /// Number of rounds in a partial-round operation. + pub partial_rounds: usize, + /// Number of matrices to skip when generating parameters using the Grain LFSR. + /// + /// The matrices being skipped are those that do not satisfy all the desired properties. + /// See the [reference implementation](https://extgit.iaik.tugraz.at/krypto/hadeshash/-/blob/master/code/generate_parameters_grain.sage) for more detail. + pub skip_matrices: usize, +} + +impl PoseidonDefaultParametersEntry { + /// Create an entry in PoseidonDefaultParameters. + pub const fn new( + rate: usize, + alpha: usize, + full_rounds: usize, + partial_rounds: usize, + skip_matrices: usize, + ) -> Self { + Self { + rate, + alpha, + full_rounds, + partial_rounds, + skip_matrices, + } + } +} + +/// A trait for default Poseidon parameters associated with a prime field +pub trait PoseidonDefaultParameters: FpParameters { + /// An array of the parameters optimized for constraints + /// (rate, alpha, full_rounds, partial_rounds, skip_matrices) + /// for rate = 2, 3, 4, 5, 6, 7, 8 + /// + /// Here, `skip_matrices` denote how many matrices to skip before + /// finding one that satisfy all the requirements. + const PARAMS_OPT_FOR_CONSTRAINTS: [PoseidonDefaultParametersEntry; 7]; + + /// An array of the parameters optimized for weights + /// (rate, alpha, full_rounds, partial_rounds, skip_matrices) + /// for rate = 2, 3, 4, 5, 6, 7, 8 + const PARAMS_OPT_FOR_WEIGHTS: [PoseidonDefaultParametersEntry; 7]; +} + +/// A field with Poseidon parameters associated +pub trait PoseidonDefaultParametersField: PrimeField { + /// Obtain the default Poseidon parameters for this rate and for this prime field, + /// with a specific optimization goal. + fn get_default_poseidon_parameters( + rate: usize, + optimized_for_weights: bool, + ) -> Option>; +} + +/// Internal function that uses the `PoseidonDefaultParameters` to compute the Poseidon parameters. +pub fn get_default_poseidon_parameters_internal( + rate: usize, + optimized_for_weights: bool, +) -> Option> { + let params_set = if !optimized_for_weights { + P::PARAMS_OPT_FOR_CONSTRAINTS + } else { + P::PARAMS_OPT_FOR_WEIGHTS + }; + + for param in params_set.iter() { + if param.rate == rate { + let (ark, mds) = find_poseidon_ark_and_mds::( + P::MODULUS_BITS as u64, + rate, + param.full_rounds as u64, + param.partial_rounds as u64, + param.skip_matrices as u64, + ); + + return Some(PoseidonParameters { + full_rounds: param.full_rounds, + partial_rounds: param.partial_rounds, + alpha: param.alpha as u64, + ark, + mds, + rate: param.rate, + capacity: 1, + }); + } + } + + None +} + +/// Internal function that computes the ark and mds from the Poseidon Grain LFSR. +pub fn find_poseidon_ark_and_mds( + prime_bits: u64, + rate: usize, + full_rounds: u64, + partial_rounds: u64, + skip_matrices: u64, +) -> (Vec>, Vec>) { + let mut lfsr = PoseidonGrainLFSR::new( + false, + prime_bits, + (rate + 1) as u64, + full_rounds, + partial_rounds, + ); + + let mut ark = Vec::>::new(); + for _ in 0..(full_rounds + partial_rounds) { + ark.push(lfsr.get_field_elements_rejection_sampling(rate + 1)); + } + + let mut mds = Vec::>::new(); + mds.resize(rate + 1, vec![F::zero(); rate + 1]); + for _ in 0..skip_matrices { + let _ = lfsr.get_field_elements_mod_p::(2 * (rate + 1)); + } + + // a qualifying matrix must satisfy the following requirements + // - there is no duplication among the elements in x or y + // - there is no i and j such that x[i] + y[j] = p + // - the resultant MDS passes all the three tests + + let xs = lfsr.get_field_elements_mod_p::(rate + 1); + let ys = lfsr.get_field_elements_mod_p::(rate + 1); + + for i in 0..(rate + 1) { + for j in 0..(rate + 1) { + mds[i][j] = (xs[i] + &ys[j]).inverse().unwrap(); + } + } + + (ark, mds) +} + +macro_rules! impl_poseidon_default_parameters_field { + ($field: ident, $params: ident) => { + impl PoseidonDefaultParametersField for $field

{ + fn get_default_poseidon_parameters( + rate: usize, + optimized_for_weights: bool, + ) -> Option> { + get_default_poseidon_parameters_internal::(rate, optimized_for_weights) + } + } + }; +} + +impl_poseidon_default_parameters_field!(Fp64, Fp64Parameters); +impl_poseidon_default_parameters_field!(Fp256, Fp256Parameters); +impl_poseidon_default_parameters_field!(Fp320, Fp320Parameters); +impl_poseidon_default_parameters_field!(Fp384, Fp384Parameters); +impl_poseidon_default_parameters_field!(Fp448, Fp448Parameters); +impl_poseidon_default_parameters_field!(Fp768, Fp768Parameters); +impl_poseidon_default_parameters_field!(Fp832, Fp832Parameters); + +#[cfg(test)] +mod test { + use crate::poseidon::{ + PoseidonDefaultParameters, PoseidonDefaultParametersEntry, PoseidonDefaultParametersField, + }; + use ark_ff::{field_new, fields::Fp256}; + use ark_ff::{BigInteger256, FftParameters, Fp256Parameters, FpParameters}; + use ark_test_curves::bls12_381::FrParameters; + + pub struct TestFrParameters; + + impl Fp256Parameters for TestFrParameters {} + impl FftParameters for TestFrParameters { + type BigInt = ::BigInt; + const TWO_ADICITY: u32 = FrParameters::TWO_ADICITY; + const TWO_ADIC_ROOT_OF_UNITY: Self::BigInt = FrParameters::TWO_ADIC_ROOT_OF_UNITY; + } + + // This TestFrParameters is the same as the BLS12-381's Fr. + // MODULUS = 52435875175126190479447740508185965837690552500527637822603658699938581184513 + impl FpParameters for TestFrParameters { + const MODULUS: BigInteger256 = FrParameters::MODULUS; + const MODULUS_BITS: u32 = FrParameters::MODULUS_BITS; + const CAPACITY: u32 = FrParameters::CAPACITY; + const REPR_SHAVE_BITS: u32 = FrParameters::REPR_SHAVE_BITS; + const R: BigInteger256 = FrParameters::R; + const R2: BigInteger256 = FrParameters::R2; + const INV: u64 = FrParameters::INV; + const GENERATOR: BigInteger256 = FrParameters::GENERATOR; + const MODULUS_MINUS_ONE_DIV_TWO: BigInteger256 = FrParameters::MODULUS_MINUS_ONE_DIV_TWO; + const T: BigInteger256 = FrParameters::T; + const T_MINUS_ONE_DIV_TWO: BigInteger256 = FrParameters::T_MINUS_ONE_DIV_TWO; + } + + impl PoseidonDefaultParameters for TestFrParameters { + const PARAMS_OPT_FOR_CONSTRAINTS: [PoseidonDefaultParametersEntry; 7] = [ + PoseidonDefaultParametersEntry::new(2, 17, 8, 31, 0), + PoseidonDefaultParametersEntry::new(3, 5, 8, 56, 0), + PoseidonDefaultParametersEntry::new(4, 5, 8, 56, 0), + PoseidonDefaultParametersEntry::new(5, 5, 8, 57, 0), + PoseidonDefaultParametersEntry::new(6, 5, 8, 57, 0), + PoseidonDefaultParametersEntry::new(7, 5, 8, 57, 0), + PoseidonDefaultParametersEntry::new(8, 5, 8, 57, 0), + ]; + const PARAMS_OPT_FOR_WEIGHTS: [PoseidonDefaultParametersEntry; 7] = [ + PoseidonDefaultParametersEntry::new(2, 257, 8, 13, 0), + PoseidonDefaultParametersEntry::new(3, 257, 8, 13, 0), + PoseidonDefaultParametersEntry::new(4, 257, 8, 13, 0), + PoseidonDefaultParametersEntry::new(5, 257, 8, 13, 0), + PoseidonDefaultParametersEntry::new(6, 257, 8, 13, 0), + PoseidonDefaultParametersEntry::new(7, 257, 8, 13, 0), + PoseidonDefaultParametersEntry::new(8, 257, 8, 13, 0), + ]; + } + + pub type TestFr = Fp256; + + #[test] + fn bls12_381_fr_poseidon_default_parameters_test() { + // constraints + let constraints_rate_2 = TestFr::get_default_poseidon_parameters(2, false).unwrap(); + assert_eq!( + constraints_rate_2.ark[0][0], + field_new!( + TestFr, + "27117311055620256798560880810000042840428971800021819916023577129547249660720" + ) + ); + assert_eq!( + constraints_rate_2.mds[0][0], + field_new!( + TestFr, + "26017457457808754696901916760153646963713419596921330311675236858336250747575" + ) + ); + + let constraints_rate_3 = TestFr::get_default_poseidon_parameters(3, false).unwrap(); + assert_eq!( + constraints_rate_3.ark[0][0], + field_new!( + TestFr, + "11865901593870436687704696210307853465124332568266803587887584059192277437537" + ) + ); + assert_eq!( + constraints_rate_3.mds[0][0], + field_new!( + TestFr, + "18791275321793747281053101601584820964683215017313972132092847596434094368732" + ) + ); + + let constraints_rate_4 = TestFr::get_default_poseidon_parameters(4, false).unwrap(); + assert_eq!( + constraints_rate_4.ark[0][0], + field_new!( + TestFr, + "41775194144383840477168997387904574072980173775424253289429546852163474914621" + ) + ); + assert_eq!( + constraints_rate_4.mds[0][0], + field_new!( + TestFr, + "42906651709148432559075674119637355642263148226238482628104108168707874713729" + ) + ); + + let constraints_rate_5 = TestFr::get_default_poseidon_parameters(5, false).unwrap(); + assert_eq!( + constraints_rate_5.ark[0][0], + field_new!( + TestFr, + "24877380261526996562448766783081897666376381975344509826094208368479247894723" + ) + ); + assert_eq!( + constraints_rate_5.mds[0][0], + field_new!( + TestFr, + "30022080821787948421423927053079656488514459012053372877891553084525866347732" + ) + ); + + let constraints_rate_6 = TestFr::get_default_poseidon_parameters(6, false).unwrap(); + assert_eq!( + constraints_rate_6.ark[0][0], + field_new!( + TestFr, + "37928506567864057383105673253383925733025682403141583234734361541053005808936" + ) + ); + assert_eq!( + constraints_rate_6.mds[0][0], + field_new!( + TestFr, + "49124738641420159156404016903087065194698370461819821829905285681776084204443" + ) + ); + + let constraints_rate_7 = TestFr::get_default_poseidon_parameters(7, false).unwrap(); + assert_eq!( + constraints_rate_7.ark[0][0], + field_new!( + TestFr, + "37848764121158464546907147011864524711588624175161409526679215525602690343051" + ) + ); + assert_eq!( + constraints_rate_7.mds[0][0], + field_new!( + TestFr, + "28113878661515342855868752866874334649815072505130059513989633785080391114646" + ) + ); + + let constraints_rate_8 = TestFr::get_default_poseidon_parameters(8, false).unwrap(); + assert_eq!( + constraints_rate_8.ark[0][0], + field_new!( + TestFr, + "51456871630395278065627483917901523970718884366549119139144234240744684354360" + ) + ); + assert_eq!( + constraints_rate_8.mds[0][0], + field_new!( + TestFr, + "12929023787467701044434927689422385731071756681420195282613396560814280256210" + ) + ); + + // weights + let weights_rate_2 = TestFr::get_default_poseidon_parameters(2, true).unwrap(); + assert_eq!( + weights_rate_2.ark[0][0], + field_new!( + TestFr, + "25126470399169474618535500283750950727260324358529540538588217772729895991183" + ) + ); + assert_eq!( + weights_rate_2.mds[0][0], + field_new!( + TestFr, + "46350838805835525240431215868760423854112287760212339623795708191499274188615" + ) + ); + + let weights_rate_3 = TestFr::get_default_poseidon_parameters(3, true).unwrap(); + assert_eq!( + weights_rate_3.ark[0][0], + field_new!( + TestFr, + "16345358380711600255519479157621098002794924491287389755192263320486827897573" + ) + ); + assert_eq!( + weights_rate_3.mds[0][0], + field_new!( + TestFr, + "37432344439659887296708509941462699942272362339508052702346957525719991245918" + ) + ); + + let weights_rate_4 = TestFr::get_default_poseidon_parameters(4, true).unwrap(); + assert_eq!( + weights_rate_4.ark[0][0], + field_new!( + TestFr, + "2997721997773001075802235431463112417440167809433966871891875582435098138600" + ) + ); + assert_eq!( + weights_rate_4.mds[0][0], + field_new!( + TestFr, + "43959024692079347032841256941012668338943730711936867712802582656046301966186" + ) + ); + + let weights_rate_5 = TestFr::get_default_poseidon_parameters(5, true).unwrap(); + assert_eq!( + weights_rate_5.ark[0][0], + field_new!( + TestFr, + "28142027771717376151411984909531650866105717069245696861966432993496676054077" + ) + ); + assert_eq!( + weights_rate_5.mds[0][0], + field_new!( + TestFr, + "13157425078305676755394500322568002504776463228389342308130514165393397413991" + ) + ); + + let weights_rate_6 = TestFr::get_default_poseidon_parameters(6, true).unwrap(); + assert_eq!( + weights_rate_6.ark[0][0], + field_new!( + TestFr, + "7417004907071346600696060525974582183666365156576759507353305331252133694222" + ) + ); + assert_eq!( + weights_rate_6.mds[0][0], + field_new!( + TestFr, + "51393878771453405560681338747290999206747890655420330824736778052231938173954" + ) + ); + + let weights_rate_7 = TestFr::get_default_poseidon_parameters(7, true).unwrap(); + assert_eq!( + weights_rate_7.ark[0][0], + field_new!( + TestFr, + "47093173418416013663709314805327945458844779999893881721688570889452680883650" + ) + ); + assert_eq!( + weights_rate_7.mds[0][0], + field_new!( + TestFr, + "51455917624412053400160569105425532358410121118308957353565646758865245830775" + ) + ); + + let weights_rate_8 = TestFr::get_default_poseidon_parameters(8, true).unwrap(); + assert_eq!( + weights_rate_8.ark[0][0], + field_new!( + TestFr, + "16478680729975035007348178961232525927424769683353433314299437589237598655079" + ) + ); + assert_eq!( + weights_rate_8.mds[0][0], + field_new!( + TestFr, + "39160448583049384229582837387246752222769278402304070376350288593586064961857" + ) + ); + } +}