diff --git a/poseidon377/Cargo.toml b/poseidon377/Cargo.toml index 9cc1d7a..b7e82ac 100644 --- a/poseidon377/Cargo.toml +++ b/poseidon377/Cargo.toml @@ -16,9 +16,6 @@ ark-snark = { version = "0.3", default-features = false, optional=true } decaf377 = { version="0.3", default-features = false, features = ["r1cs"] } num-bigint = { version = "0.4.3", default-features = false } once_cell = { version = "1.8", default-features = false } - -ark-sponge = { git = "https://github.com/penumbra-zone/sponge", default-features = false, branch = "r1cs" } - poseidon-parameters = { path = "../poseidon-parameters", default-features = false } poseidon-permutation = { path = "../poseidon-permutation", default-features = false } @@ -44,7 +41,6 @@ r1cs = [ "ark-relations", "ark-snark", "ark-r1cs-std", - "ark-sponge/r1cs", "decaf377/r1cs", ] parallel = [ diff --git a/poseidon377/src/r1cs.rs b/poseidon377/src/r1cs.rs index 4dee651..ed846e6 100644 --- a/poseidon377/src/r1cs.rs +++ b/poseidon377/src/r1cs.rs @@ -1,13 +1,16 @@ use ark_relations::r1cs::{ConstraintSystemRef, SynthesisError}; -use ark_sponge::{ - constraints::CryptographicSpongeVar, - poseidon::{constraints::PoseidonSpongeVar, PoseidonParameters as ArkPoseidonParameters}, -}; use decaf377::r1cs::FqVar; use poseidon_parameters::{Alpha, PoseidonParameters}; use crate::Fq; +mod vendor; + +use vendor::sponge::{ + constraints::CryptographicSpongeVar, + poseidon::{constraints::PoseidonSpongeVar, PoseidonParameters as ArkPoseidonParameters}, +}; + fn convert_to_ark_sponge_parameters(params: PoseidonParameters) -> ArkPoseidonParameters { let alpha = match params.alpha { Alpha::Exponent(exp) => exp as u64, diff --git a/poseidon377/src/r1cs/vendor.rs b/poseidon377/src/r1cs/vendor.rs new file mode 100644 index 0000000..333ca5b --- /dev/null +++ b/poseidon377/src/r1cs/vendor.rs @@ -0,0 +1,3 @@ +// This is the vendored repository from: https://github.com/penumbra-zone/sponge +// TODO: Replace this +pub mod sponge; diff --git a/poseidon377/src/r1cs/vendor/absorb.rs b/poseidon377/src/r1cs/vendor/absorb.rs new file mode 100644 index 0000000..62c70ca --- /dev/null +++ b/poseidon377/src/r1cs/vendor/absorb.rs @@ -0,0 +1,394 @@ +use ark_ec::models::short_weierstrass_jacobian::GroupAffine as SWAffine; +use ark_ec::models::twisted_edwards_extended::GroupAffine as TEAffine; +use ark_ec::models::{SWModelParameters, TEModelParameters}; +use ark_ff::models::{ + Fp256, Fp256Parameters, Fp320, Fp320Parameters, Fp384, Fp384Parameters, Fp768, Fp768Parameters, + Fp832, Fp832Parameters, +}; +use ark_ff::{PrimeField, ToConstraintField}; +use ark_serialize::CanonicalSerialize; +use ark_std::vec::Vec; +/// An interface for objects that can be absorbed by a `CryptographicSponge`. +pub trait Absorb { + /// Converts the object into a list of bytes that can be absorbed by a `CryptographicSponge`. + /// Append the list to `dest`. + fn to_sponge_bytes(&self, dest: &mut Vec); + + /// Converts the object into a list of bytes that can be absorbed by a `CryptographicSponge`. + /// Return the list as `Vec`. + fn to_sponge_bytes_as_vec(&self) -> Vec { + let mut result = Vec::new(); + self.to_sponge_bytes(&mut result); + result + } + + /// Converts the object into field elements that can be absorbed by a `CryptographicSponge`. + /// Append the list to `dest` + fn to_sponge_field_elements(&self, dest: &mut Vec); + + /// Converts the object into field elements that can be absorbed by a `CryptographicSponge`. + /// Return the list as `Vec` + fn to_sponge_field_elements_as_vec(&self) -> Vec { + let mut result = Vec::new(); + self.to_sponge_field_elements(&mut result); + result + } + + /// Specifies the conversion into a list of bytes for a batch. Append the list to `dest`. + fn batch_to_sponge_bytes(batch: &[Self], dest: &mut Vec) + where + Self: Sized, + { + for absorbable in batch { + absorbable.to_sponge_bytes(dest) + } + } + + /// Specifies the conversion into a list of bytes for a batch. Return the list as `Vec`. + fn batch_to_sponge_bytes_as_vec(batch: &[Self]) -> Vec + where + Self: Sized, + { + let mut result = Vec::new(); + Self::batch_to_sponge_bytes(batch, &mut result); + result + } + + /// Specifies the conversion into a list of field elements for a batch. Append the list to `dest`. + fn batch_to_sponge_field_elements(batch: &[Self], dest: &mut Vec) + where + Self: Sized, + { + for absorbable in batch { + absorbable.to_sponge_field_elements(dest) + } + } + + /// Specifies the conversion into a list of field elements for a batch. Append the list to `dest`. + fn batch_to_sponge_field_elements_as_vec(batch: &[Self]) -> Vec + where + Self: Sized, + { + let mut result = Vec::new(); + for absorbable in batch { + absorbable.to_sponge_field_elements(&mut result) + } + result + } +} + +/// An extension to `Absorb` that is specific to items with variable length, such as a list. +pub trait AbsorbWithLength: Absorb { + /// The length of the `self` being absorbed. + fn absorb_length(&self) -> usize; + + /// Converts the object into a list of bytes along with its length information + /// that can be absorbed by a `CryptographicSponge`. + /// Append the list to `dest`. + fn to_sponge_bytes_with_length(&self, dest: &mut Vec) { + self.absorb_length().to_sponge_bytes(dest); + self.to_sponge_bytes(dest) + } + + /// Converts the object into field elements along with its length information + /// that can be absorbed by a `CryptographicSponge`. + /// Append the list to `dest` + fn to_sponge_field_elements_with_length(&self, dest: &mut Vec) { + self.absorb_length().to_sponge_field_elements(dest); + ::to_sponge_field_elements(&self, dest) + } +} + +/// If `F1` and `F2` have the same prime modulus, this method returns `Some(input)` +/// but cast to `F2`, and returns `None` otherwise. +pub(crate) fn field_cast(input: F1) -> Option { + if F1::characteristic() != F2::characteristic() { + // Trying to absorb non-native field elements. + None + } else { + let mut buf = Vec::new(); + input.serialize(&mut buf).unwrap(); + Some(F2::from_le_bytes_mod_order(&buf)) + } +} + +/// If `F1` equals to `F2`, add all elements of `x` as `F2` to `dest` and returns `dest` pointer. +/// +/// This function will return None and no-op if `F1` is not equal to `F2`. +pub(crate) fn batch_field_cast<'a, F1: PrimeField, F2: PrimeField>( + x: &[F1], + dest: &'a mut Vec, +) -> Option<&'a mut Vec> { + if F1::characteristic() != F2::characteristic() { + // "Trying to absorb non-native field elements." + None + } else { + x.iter().for_each(|item| { + let mut buf = Vec::new(); + item.serialize(&mut buf).unwrap(); + dest.push(F2::from_le_bytes_mod_order(&buf)) + }); + Some(dest) + } +} + +impl Absorb for u8 { + fn to_sponge_bytes(&self, dest: &mut Vec) { + dest.push(*self) + } + + fn to_sponge_field_elements(&self, dest: &mut Vec) { + dest.push(F::from(*self)) + } + + fn batch_to_sponge_bytes(batch: &[Self], dest: &mut Vec) { + dest.extend_from_slice(batch) + } + + fn batch_to_sponge_field_elements(batch: &[Self], dest: &mut Vec) { + let mut bytes = (batch.len() as u64).to_le_bytes().to_vec(); + bytes.extend_from_slice(batch); + dest.extend_from_slice(&bytes.to_field_elements().unwrap()[..]) + } +} + +impl Absorb for bool { + fn to_sponge_bytes(&self, dest: &mut Vec) { + dest.push(*self as u8) + } + + fn to_sponge_field_elements(&self, dest: &mut Vec) { + dest.push(F::from(*self)) + } +} + +macro_rules! impl_absorbable_field { + ($field:ident, $params:ident) => { + impl Absorb for $field

{ + fn to_sponge_bytes(&self, dest: &mut Vec) { + self.serialize(dest).unwrap() + } + + fn to_sponge_field_elements(&self, dest: &mut Vec) { + dest.push(field_cast(*self).unwrap()) + } + + fn batch_to_sponge_field_elements(batch: &[Self], dest: &mut Vec) + where + Self: Sized, + { + batch_field_cast(batch, dest).unwrap(); + } + } + }; +} + +impl_absorbable_field!(Fp256, Fp256Parameters); +impl_absorbable_field!(Fp320, Fp320Parameters); +impl_absorbable_field!(Fp384, Fp384Parameters); +impl_absorbable_field!(Fp768, Fp768Parameters); +impl_absorbable_field!(Fp832, Fp832Parameters); + +macro_rules! impl_absorbable_unsigned { + ($t:ident) => { + impl Absorb for $t { + fn to_sponge_bytes(&self, dest: &mut Vec) { + dest.extend_from_slice(&self.to_le_bytes()[..]) + } + + fn to_sponge_field_elements(&self, dest: &mut Vec) { + dest.push(F::from(*self)) + } + } + }; +} +// +impl_absorbable_unsigned!(u16); +impl_absorbable_unsigned!(u32); +impl_absorbable_unsigned!(u64); +impl_absorbable_unsigned!(u128); + +macro_rules! impl_absorbable_signed { + ($signed:ident, $unsigned:ident) => { + impl Absorb for $signed { + fn to_sponge_bytes(&self, dest: &mut Vec) { + dest.extend_from_slice(&self.to_le_bytes()[..]) + } + + fn to_sponge_field_elements(&self, dest: &mut Vec) { + let mut elem = F::from(self.abs() as $unsigned); + if *self < 0 { + elem = -elem; + } + dest.push(elem) + } + } + }; +} + +impl_absorbable_signed!(i8, u8); +impl_absorbable_signed!(i16, u16); +impl_absorbable_signed!(i32, u32); +impl_absorbable_signed!(i64, u64); +impl_absorbable_signed!(i128, u128); + +impl Absorb for usize { + fn to_sponge_bytes(&self, dest: &mut Vec) { + dest.extend_from_slice(&((*self as u64).to_le_bytes())[..]) + } + + fn to_sponge_field_elements(&self, dest: &mut Vec) { + (*self as u64).to_sponge_field_elements(dest) + } +} + +impl Absorb for isize { + fn to_sponge_bytes(&self, dest: &mut Vec) { + dest.extend_from_slice(&self.to_le_bytes()[..]) + } + + fn to_sponge_field_elements(&self, dest: &mut Vec) { + (*self as i64).to_sponge_field_elements(dest) + } +} + +impl> Absorb for TEAffine

{ + fn to_sponge_bytes(&self, dest: &mut Vec) { + self.to_field_elements().unwrap().serialize(dest).unwrap() + } + + fn to_sponge_field_elements(&self, dest: &mut Vec) { + batch_field_cast::(&self.to_field_elements().unwrap(), dest).unwrap(); + } +} + +impl> Absorb for SWAffine

{ + fn to_sponge_bytes(&self, dest: &mut Vec) { + self.to_field_elements().unwrap().serialize(dest).unwrap() + } + + fn to_sponge_field_elements(&self, dest: &mut Vec) { + batch_field_cast::(&self.to_field_elements().unwrap(), dest).unwrap(); + } +} + +impl Absorb for &[A] { + fn to_sponge_bytes(&self, dest: &mut Vec) { + A::batch_to_sponge_bytes(self, dest) + } + + fn to_sponge_field_elements(&self, dest: &mut Vec) { + A::batch_to_sponge_field_elements(self, dest) + } +} + +impl AbsorbWithLength for &[A] { + fn absorb_length(&self) -> usize { + self.len() + } +} + +impl Absorb for Vec { + fn to_sponge_bytes(&self, dest: &mut Vec) { + self.as_slice().to_sponge_bytes(dest) + } + + fn to_sponge_field_elements(&self, dest: &mut Vec) { + self.as_slice().to_sponge_field_elements(dest) + } +} + +impl AbsorbWithLength for Vec { + fn absorb_length(&self) -> usize { + self.as_slice().len() + } +} + +impl Absorb for Option { + fn to_sponge_bytes(&self, dest: &mut Vec) { + self.is_some().to_sponge_bytes(dest); + if let Some(item) = self { + item.to_sponge_bytes(dest) + } + } + + fn to_sponge_field_elements(&self, dest: &mut Vec) { + self.is_some().to_sponge_field_elements(dest); + if let Some(item) = self { + item.to_sponge_field_elements(dest) + } + } +} + +// TODO: add more for common data structures, treemap? + +impl Absorb for &A { + fn to_sponge_bytes(&self, dest: &mut Vec) { + (*self).to_sponge_bytes(dest) + } + + fn to_sponge_field_elements(&self, dest: &mut Vec) { + (*self).to_sponge_field_elements(dest) + } +} + +/// Individually absorbs each element in a comma-separated list of absorbables into a sponge. +/// Format is `absorb!(s, a_0, a_1, ..., a_n)`, where `s` is a mutable reference to a sponge +/// and each `a_i` implements `Absorb`. +#[macro_export] +macro_rules! absorb { + ($sponge:expr, $($absorbable:expr),+ ) => { + $( + CryptographicSponge::absorb($sponge, &$absorbable); + )+ + }; +} + +/// Quickly convert a list of different [`Absorb`]s into sponge bytes. +#[macro_export] +macro_rules! collect_sponge_bytes { + ($head:expr $(, $tail:expr)* ) => { + { + let mut output = Absorb::to_sponge_bytes_as_vec(&$head); + $( + Absorb::to_sponge_bytes(&$tail, &mut output); + )* + output + } + }; +} + +/// Quickly convert a list of different [`Absorb`]s into sponge field elements. +#[macro_export] +macro_rules! collect_sponge_field_elements { + ($head:expr $(, $tail:expr)* ) => { + { + let mut output = Absorb::to_sponge_field_elements_as_vec(&$head); + $( + Absorb::to_sponge_field_elements(&$tail, &mut output); + )* + output + } + }; +} + +#[cfg(test)] +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] + fn test_cast() { + let mut rng = test_rng(); + let expected = Fr::rand(&mut rng); + let actual = field_cast::<_, Fr>(expected).unwrap(); + assert_eq!(actual, expected); + let expected: Vec<_> = (0..10).map(|_| Fr::rand(&mut rng)).collect(); + let mut actual = Vec::new(); + batch_field_cast::<_, Fr>(&expected, &mut actual).unwrap(); + assert_eq!(actual, expected); + } +} diff --git a/poseidon377/src/r1cs/vendor/sponge.rs b/poseidon377/src/r1cs/vendor/sponge.rs new file mode 100644 index 0000000..cfc0065 --- /dev/null +++ b/poseidon377/src/r1cs/vendor/sponge.rs @@ -0,0 +1,213 @@ +#![cfg_attr(not(feature = "std"), no_std)] +//! A crate for the cryptographic sponge trait. +#![deny( + const_err, + future_incompatible, + missing_docs, + non_shorthand_field_patterns, + renamed_and_removed_lints, + rust_2018_idioms, + stable_features, + trivial_casts, + trivial_numeric_casts, + unused, + variant_size_differences +)] +#![forbid(unsafe_code)] + +use ark_ff::{FpParameters, PrimeField}; +use ark_std::vec; +use ark_std::vec::Vec; + +/// Infrastructure for the constraints counterparts. +#[cfg(feature = "r1cs")] +pub mod constraints; + +mod absorb; +pub use absorb::*; + +/// The sponge for Poseidon +/// +/// This implementation of Poseidon is entirely from Fractal's implementation in [COS20][cos] +/// with small syntax changes. +/// +/// [cos]: https://eprint.iacr.org/2019/1076 +pub mod poseidon; + +/// An enum for specifying the output field element size. +#[derive(Clone, Copy, Eq, PartialEq)] +pub enum FieldElementSize { + /// Sample field elements from the entire field. + Full, + + /// Sample field elements from a subset of the field, specified by the maximum number of bits. + Truncated(usize), +} + +impl FieldElementSize { + pub(crate) fn num_bits(&self) -> usize { + if let FieldElementSize::Truncated(num_bits) = self { + if *num_bits > (F::Params::MODULUS_BITS as usize) { + panic!("num_bits is greater than the capacity of the field.") + } + }; + F::Params::CAPACITY as usize + } + + /// Calculate the sum of field element sizes in `elements`. + pub fn sum(elements: &[Self]) -> usize { + elements.iter().map(|item| item.num_bits::()).sum() + } +} + +/// Default implementation of `CryptographicSponge::squeeze_field_elements_with_sizes` +pub(crate) fn squeeze_field_elements_with_sizes_default_impl( + sponge: &mut impl CryptographicSponge, + sizes: &[FieldElementSize], +) -> Vec { + if sizes.len() == 0 { + return Vec::new(); + } + + let mut total_bits = 0usize; + for size in sizes { + total_bits += size.num_bits::(); + } + + let bits = sponge.squeeze_bits(total_bits); + let mut bits_window = bits.as_slice(); + + let mut output = Vec::with_capacity(sizes.len()); + for size in sizes { + let num_bits = size.num_bits::(); + let nonnative_bits_le: Vec = bits_window[..num_bits].to_vec(); + bits_window = &bits_window[num_bits..]; + + let nonnative_bytes = nonnative_bits_le + .chunks(8) + .map(|bits| { + let mut byte = 0u8; + for (i, &bit) in bits.into_iter().enumerate() { + if bit { + byte += 1 << i; + } + } + byte + }) + .collect::>(); + + output.push(F::from_le_bytes_mod_order(nonnative_bytes.as_slice())); + } + + output +} + +/// The interface for a cryptographic sponge. +/// A sponge can `absorb` or take in inputs and later `squeeze` or output bytes or field elements. +/// The outputs are dependent on previous `absorb` and `squeeze` calls. +pub trait CryptographicSponge: Clone { + /// Parameters used by the sponge. + type Parameters; + + /// Initialize a new instance of the sponge. + fn new(params: &Self::Parameters) -> Self; + + /// Absorb an input into the sponge. + fn absorb(&mut self, input: &impl Absorb); + + /// Squeeze `num_bytes` bytes from the sponge. + fn squeeze_bytes(&mut self, num_bytes: usize) -> Vec; + + /// Squeeze `num_bits` bits from the sponge. + fn squeeze_bits(&mut self, num_bits: usize) -> Vec; + + /// Squeeze `sizes.len()` field elements from the sponge, where the `i`-th element of + /// the output has size `sizes[i]`. + /// + /// If the implementation is field-based, to squeeze native field elements, + /// call `self.squeeze_native_field_elements` instead. + /// + /// TODO: Support general Field. + /// + /// Note that when `FieldElementSize` is `FULL`, the output is not strictly uniform. Output + /// space is uniform in \[0, 2^{F::MODULUS_BITS - 1}\] + fn squeeze_field_elements_with_sizes( + &mut self, + sizes: &[FieldElementSize], + ) -> Vec { + squeeze_field_elements_with_sizes_default_impl(self, sizes) + } + + /// Squeeze `num_elements` nonnative field elements from the sponge. + /// + /// Because of rust limitation, for field-based implementation, using this method to squeeze + /// native field elements will have runtime casting cost. For better efficiency, use `squeeze_native_field_elements`. + fn squeeze_field_elements(&mut self, num_elements: usize) -> Vec { + self.squeeze_field_elements_with_sizes::( + vec![FieldElementSize::Full; num_elements].as_slice(), + ) + } + + /// Creates a new sponge with applied domain separation. + fn fork(&self, domain: &[u8]) -> Self { + let mut new_sponge = self.clone(); + + let mut input = Absorb::to_sponge_bytes_as_vec(&domain.len()); + input.extend_from_slice(domain); + new_sponge.absorb(&input); + + new_sponge + } +} + +/// The interface for field-based cryptographic sponge. +/// `CF` is the native field used by the cryptographic sponge implementation. +pub trait FieldBasedCryptographicSponge: CryptographicSponge { + /// Squeeze `num_elements` field elements from the sponge. + fn squeeze_native_field_elements(&mut self, num_elements: usize) -> Vec; + + /// Squeeze `sizes.len()` field elements from the sponge, where the `i`-th element of + /// the output has size `sizes[i]`. + fn squeeze_native_field_elements_with_sizes(&mut self, sizes: &[FieldElementSize]) -> Vec { + let mut all_full_sizes = true; + for size in sizes { + if *size != FieldElementSize::Full { + all_full_sizes = false; + break; + } + } + + if all_full_sizes { + self.squeeze_native_field_elements(sizes.len()) + } else { + squeeze_field_elements_with_sizes_default_impl(self, sizes) + } + } +} + +/// An extension for the interface of a cryptographic sponge. +/// In addition to operations defined in `CryptographicSponge`, `SpongeExt` can convert itself to +/// a state, and instantiate itself from state. +pub trait SpongeExt: CryptographicSponge { + /// The full state of the cryptographic sponge. + type State: Clone; + /// Returns a sponge that uses `state`. + fn from_state(state: Self::State, params: &Self::Parameters) -> Self; + /// 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/poseidon377/src/r1cs/vendor/sponge/constraints/absorb.rs b/poseidon377/src/r1cs/vendor/sponge/constraints/absorb.rs new file mode 100644 index 0000000..09fe2a6 --- /dev/null +++ b/poseidon377/src/r1cs/vendor/sponge/constraints/absorb.rs @@ -0,0 +1,263 @@ +use ark_ec::{ModelParameters, SWModelParameters, TEModelParameters}; +use ark_ff::{Field, PrimeField}; +use ark_r1cs_std::bits::boolean::Boolean; +use ark_r1cs_std::bits::uint8::UInt8; +use ark_r1cs_std::fields::fp::FpVar; +use ark_r1cs_std::fields::{FieldOpsBounds, FieldVar}; +use ark_r1cs_std::groups::curves::short_weierstrass::{ + AffineVar as SWAffineVar, ProjectiveVar as SWProjectiveVar, +}; +use ark_r1cs_std::groups::curves::twisted_edwards::AffineVar as TEAffineVar; +use ark_r1cs_std::{ToBytesGadget, ToConstraintFieldGadget}; +use ark_relations::r1cs::SynthesisError; +use ark_std::vec; +use ark_std::vec::Vec; +/// An interface for objects that can be absorbed by a `CryptographicSpongeVar` whose constraint field +/// is `CF`. +pub trait AbsorbGadget { + /// Converts the object into a list of bytes that can be absorbed by a `CryptographicSpongeVar`. + /// return the list. + fn to_sponge_bytes(&self) -> Result>, SynthesisError>; + + /// Specifies the conversion into a list of bytes for a batch. + fn batch_to_sponge_bytes(batch: &[Self]) -> Result>, SynthesisError> + where + Self: Sized, + { + let mut result = Vec::new(); + for item in batch { + result.append(&mut (item.to_sponge_bytes()?)) + } + Ok(result) + } + + /// Converts the object into field elements that can be absorbed by a `CryptographicSpongeVar`. + fn to_sponge_field_elements(&self) -> Result>, SynthesisError>; + + /// Specifies the conversion into a list of field elements for a batch. + fn batch_to_sponge_field_elements(batch: &[Self]) -> Result>, SynthesisError> + where + Self: Sized, + { + let mut output = Vec::new(); + for absorbable in batch { + output.append(&mut absorbable.to_sponge_field_elements()?); + } + + Ok(output) + } +} + +impl AbsorbGadget for UInt8 { + fn to_sponge_bytes(&self) -> Result>, SynthesisError> { + Ok(vec![self.clone()]) + } + + fn to_sponge_field_elements(&self) -> Result>, SynthesisError> { + vec![self.clone()].to_constraint_field() + } + + fn batch_to_sponge_field_elements(batch: &[Self]) -> Result>, SynthesisError> { + // It's okay to allocate as constant because at circuit-generation time, + // the length must be statically known (it cannot vary with the variable assignments). + let mut bytes = UInt8::constant_vec((batch.len() as u64).to_le_bytes().as_ref()); + bytes.extend_from_slice(batch); + bytes.to_constraint_field() + } +} + +impl AbsorbGadget for Boolean { + fn to_sponge_bytes(&self) -> Result>, SynthesisError> { + self.to_bytes() + } + + fn to_sponge_field_elements(&self) -> Result>, SynthesisError> { + Ok(vec![FpVar::from(self.clone())]) + } +} + +impl AbsorbGadget for FpVar { + fn to_sponge_bytes(&self) -> Result>, SynthesisError> { + self.to_bytes() + } + + fn to_sponge_field_elements(&self) -> Result>, SynthesisError> { + Ok(vec![self.clone()]) + } + + fn batch_to_sponge_field_elements(batch: &[Self]) -> Result>, SynthesisError> { + Ok(batch.to_vec()) + } +} + +macro_rules! impl_absorbable_group { + ($group:ident, $params:ident) => { + impl AbsorbGadget<::BasePrimeField> for $group + where + P: $params, + F: FieldVar::BasePrimeField>, + for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>, + F: ToConstraintFieldGadget<::BasePrimeField>, + { + fn to_sponge_bytes( + &self, + ) -> Result::BasePrimeField>>, SynthesisError> { + self.to_constraint_field()?.to_sponge_bytes() + } + + fn to_sponge_field_elements( + &self, + ) -> Result::BasePrimeField>>, SynthesisError> { + self.to_constraint_field() + } + } + }; +} + +impl_absorbable_group!(TEAffineVar, TEModelParameters); +impl_absorbable_group!(SWAffineVar, SWModelParameters); + +impl AbsorbGadget<::BasePrimeField> for SWProjectiveVar +where + P: SWModelParameters, + F: FieldVar::BasePrimeField>, + for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>, + F: ToConstraintFieldGadget<::BasePrimeField>, +{ + fn to_sponge_bytes( + &self, + ) -> Result< + Vec::BaseField as Field>::BasePrimeField>>, + SynthesisError, + > { + self.to_bytes() + } + + fn to_sponge_field_elements( + &self, + ) -> Result< + Vec::BaseField as Field>::BasePrimeField>>, + SynthesisError, + > { + self.to_affine()?.to_sponge_field_elements() + } +} + +impl> AbsorbGadget for &[A] { + fn to_sponge_bytes(&self) -> Result>, SynthesisError> { + A::batch_to_sponge_bytes(self) + } + + fn to_sponge_field_elements(&self) -> Result>, SynthesisError> { + A::batch_to_sponge_field_elements(self) + } +} + +impl> AbsorbGadget for Vec { + fn to_sponge_bytes(&self) -> Result>, SynthesisError> { + self.as_slice().to_sponge_bytes() + } + + fn to_sponge_field_elements(&self) -> Result>, SynthesisError> { + self.as_slice().to_sponge_field_elements() + } +} + +impl> AbsorbGadget for Option { + fn to_sponge_bytes(&self) -> Result>, SynthesisError> { + let mut output = Vec::new(); + output.append(&mut (Boolean::Constant(self.is_some()).to_sponge_bytes()?)); + if let Some(item) = self { + output.append(&mut (item.to_sponge_bytes()?)) + } + Ok(output) + } + + fn to_sponge_field_elements(&self) -> Result>, SynthesisError> { + let mut output = vec![FpVar::from(Boolean::constant(self.is_some()))]; + if let Some(absorbable) = self.as_ref() { + output.append(&mut absorbable.to_sponge_field_elements()?); + } + Ok(output) + } +} + +impl> AbsorbGadget for &A { + fn to_sponge_bytes(&self) -> Result>, SynthesisError> { + (*self).to_sponge_bytes() + } + + fn to_sponge_field_elements(&self) -> Result>, SynthesisError> { + (*self).to_sponge_field_elements() + } +} + +/// Individually absorbs each element in a comma-separated list of [`Absorbable`]s into a sponge. +/// Format is `absorb!(s, a_0, a_1, ..., a_n)`, where `s` is a mutable reference to a sponge +/// and each `a_i` implements `AbsorbableVar`. +#[macro_export] +macro_rules! absorb_gadget { + ($sponge:expr, $($absorbable:expr),+ ) => { + $( + CryptographicSpongeVar::absorb($sponge, &$absorbable)?; + )+ + }; +} + +/// Quickly convert a list of different [`Absorbable`]s into sponge field elements. +#[macro_export] +macro_rules! collect_sponge_field_elements_gadget { + ($head:expr $(, $tail:expr)* ) => { + { + let mut output = AbsorbGadget::to_sponge_field_elements(&$head)?; + $( + output.append(&mut AbsorbGadget::to_sponge_field_elements(&$tail)?); + )* + + Ok(output) + } + }; +} + +#[cfg(test)] +mod tests { + use crate::constraints::AbsorbGadget; + use crate::Absorb; + use ark_r1cs_std::alloc::AllocVar; + use ark_r1cs_std::fields::fp::FpVar; + use ark_r1cs_std::uint8::UInt8; + use ark_r1cs_std::R1CSVar; + use ark_relations::r1cs::ConstraintSystem; + use ark_relations::*; + use ark_std::{test_rng, UniformRand}; + use ark_test_curves::bls12_381::Fr; + + #[test] + fn consistency_check() { + // test constraint is consistent with native + let cs = ConstraintSystem::::new_ref(); + let mut rng = test_rng(); + // uint8 + let data = vec![0u8, 1u8, 2u8, 3u8, 4u8, 5u8]; + let data_var = UInt8::new_input_vec(ns!(cs, "u8data"), &data).unwrap(); + + let native_bytes = data.to_sponge_bytes_as_vec(); + let constraint_bytes = data_var.to_sponge_bytes().unwrap(); + + assert_eq!(constraint_bytes.value().unwrap(), native_bytes); + + // field + + let data: Vec<_> = (0..10).map(|_| Fr::rand(&mut rng)).collect(); + let data_var: Vec<_> = data + .iter() + .map(|item| FpVar::new_input(ns!(cs, "fpdata"), || Ok(*item)).unwrap()) + .collect(); + + let native_bytes = data.to_sponge_bytes_as_vec(); + let constraint_bytes = data_var.to_sponge_bytes().unwrap(); + assert_eq!(constraint_bytes.value().unwrap(), native_bytes); + + assert!(cs.is_satisfied().unwrap()) + } +} diff --git a/poseidon377/src/r1cs/vendor/sponge/constraints/mod.rs b/poseidon377/src/r1cs/vendor/sponge/constraints/mod.rs new file mode 100644 index 0000000..997d5b4 --- /dev/null +++ b/poseidon377/src/r1cs/vendor/sponge/constraints/mod.rs @@ -0,0 +1,188 @@ +use crate::{Absorb, CryptographicSponge, FieldElementSize}; +use ark_ff::PrimeField; +use ark_nonnative_field::params::{get_params, OptimizationType}; +use ark_nonnative_field::{AllocatedNonNativeFieldVar, NonNativeFieldVar}; +use ark_r1cs_std::alloc::AllocVar; +use ark_r1cs_std::bits::boolean::Boolean; +use ark_r1cs_std::bits::uint8::UInt8; +use ark_r1cs_std::fields::fp::{AllocatedFp, FpVar}; +use ark_r1cs_std::R1CSVar; +use ark_relations::lc; +use ark_relations::r1cs::{ConstraintSystemRef, LinearCombination, SynthesisError}; +use ark_std::vec; +use ark_std::vec::Vec; + +mod absorb; +pub use absorb::*; + +/// Converts little-endian bits to a list of nonnative elements. +pub fn bits_le_to_nonnative<'a, F: PrimeField, CF: PrimeField>( + cs: ConstraintSystemRef, + all_nonnative_bits_le: impl IntoIterator>>, +) -> Result>, SynthesisError> { + let all_nonnative_bits_le = all_nonnative_bits_le.into_iter().collect::>(); + if all_nonnative_bits_le.is_empty() { + return Ok(Vec::new()); + } + + let mut max_nonnative_bits = 0usize; + for bits in &all_nonnative_bits_le { + max_nonnative_bits = max_nonnative_bits.max(bits.len()); + } + + let mut lookup_table = Vec::>::new(); + let mut cur = F::one(); + for _ in 0..max_nonnative_bits { + let repr = AllocatedNonNativeFieldVar::::get_limbs_representations( + &cur, + OptimizationType::Constraints, + )?; + lookup_table.push(repr); + cur.double_in_place(); + } + + let params = get_params( + F::size_in_bits(), + CF::size_in_bits(), + OptimizationType::Constraints, + ); + + let mut output = Vec::with_capacity(all_nonnative_bits_le.len()); + for nonnative_bits_le in all_nonnative_bits_le { + let mut val = vec![CF::zero(); params.num_limbs]; + let mut lc = vec![LinearCombination::::zero(); params.num_limbs]; + + for (j, bit) in nonnative_bits_le.iter().enumerate() { + if bit.value().unwrap_or_default() { + for (k, val) in val.iter_mut().enumerate().take(params.num_limbs) { + *val += &lookup_table[j][k]; + } + } + + #[allow(clippy::needless_range_loop)] + for k in 0..params.num_limbs { + lc[k] = &lc[k] + bit.lc() * lookup_table[j][k]; + } + } + + let mut limbs = Vec::new(); + for k in 0..params.num_limbs { + let gadget = + AllocatedFp::new_witness(ark_relations::ns!(cs, "alloc"), || Ok(val[k])).unwrap(); + lc[k] = lc[k].clone() - (CF::one(), gadget.variable); + cs.enforce_constraint(lc!(), lc!(), lc[k].clone()).unwrap(); + limbs.push(FpVar::::from(gadget)); + } + + output.push(NonNativeFieldVar::::Var( + AllocatedNonNativeFieldVar:: { + cs: cs.clone(), + limbs, + num_of_additions_over_normal_form: CF::zero(), + is_in_the_normal_form: true, + target_phantom: Default::default(), + }, + )); + } + + Ok(output) +} + +/// Enables simple access to the "gadget" version of the sponge. +/// Simplifies trait bounds in downstream generic code. +pub trait SpongeWithGadget: CryptographicSponge { + /// The gadget version of `Self`. + type Var: CryptographicSpongeVar; +} + +/// The interface for a cryptographic sponge constraints on field `CF`. +/// A sponge can `absorb` or take in inputs and later `squeeze` or output bytes or field elements. +/// The outputs are dependent on previous `absorb` and `squeeze` calls. +pub trait CryptographicSpongeVar: Clone { + /// Parameters used by the sponge. + type Parameters; + + /// Initialize a new instance of the sponge. + fn new(cs: ConstraintSystemRef, params: &Self::Parameters) -> Self; + + /// Returns a ref to the underlying constraint system the sponge is operating in. + fn cs(&self) -> ConstraintSystemRef; + + /// Absorb an input into the sponge. + fn absorb(&mut self, input: &impl AbsorbGadget) -> Result<(), SynthesisError>; + + /// Squeeze `num_bytes` bytes from the sponge. + fn squeeze_bytes(&mut self, num_bytes: usize) -> Result>, SynthesisError>; + + /// Squeeze `num_bit` bits from the sponge. + fn squeeze_bits(&mut self, num_bits: usize) -> Result>, SynthesisError>; + + /// Squeeze `sizes.len()` nonnative field elements from the sponge, where the `i`-th element of + /// the output has size `sizes[i]`. + fn squeeze_nonnative_field_elements_with_sizes( + &mut self, + sizes: &[FieldElementSize], + ) -> Result<(Vec>, Vec>>), SynthesisError> { + if sizes.len() == 0 { + return Ok((Vec::new(), Vec::new())); + } + + let cs = self.cs(); + + let mut total_bits = 0usize; + for size in sizes { + total_bits += size.num_bits::(); + } + + let bits = self.squeeze_bits(total_bits)?; + + let mut dest_bits = Vec::>>::with_capacity(sizes.len()); + + let mut bits_window = bits.as_slice(); + for size in sizes { + let num_bits = size.num_bits::(); + let nonnative_bits_le = bits_window[..num_bits].to_vec(); + bits_window = &bits_window[num_bits..]; + + dest_bits.push(nonnative_bits_le); + } + + let dest_gadgets = bits_le_to_nonnative(cs, dest_bits.iter())?; + + Ok((dest_gadgets, dest_bits)) + } + + /// Squeeze `num_elements` nonnative field elements from the sponge. + fn squeeze_nonnative_field_elements( + &mut self, + num_elements: usize, + ) -> Result<(Vec>, Vec>>), SynthesisError> { + self.squeeze_nonnative_field_elements_with_sizes::( + vec![FieldElementSize::Full; num_elements].as_slice(), + ) + } + + /// Creates a new sponge with applied domain separation. + fn fork(&self, domain: &[u8]) -> Result { + let mut new_sponge = self.clone(); + + let mut input = Absorb::to_sponge_bytes_as_vec(&domain.len()); + input.extend_from_slice(domain); + + let elems: Vec = input.to_sponge_field_elements_as_vec(); + let elem_vars = elems + .into_iter() + .map(|elem| FpVar::Constant(elem)) + .collect::>(); + + new_sponge.absorb(&elem_vars)?; + + Ok(new_sponge) + } + + /// Squeeze `num_elements` field elements from the sponge. + fn squeeze_field_elements( + &mut self, + num_elements: usize, + ) -> Result>, SynthesisError>; +} diff --git a/poseidon377/src/r1cs/vendor/sponge/poseidon/constraints.rs b/poseidon377/src/r1cs/vendor/sponge/poseidon/constraints.rs new file mode 100644 index 0000000..86a0931 --- /dev/null +++ b/poseidon377/src/r1cs/vendor/sponge/poseidon/constraints.rs @@ -0,0 +1,350 @@ +use crate::constraints::AbsorbGadget; +use crate::constraints::{CryptographicSpongeVar, SpongeWithGadget}; +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::*; +use ark_relations::r1cs::{ConstraintSystemRef, SynthesisError}; +use ark_std::vec; +use ark_std::vec::Vec; + +#[derive(Clone)] +/// the gadget for Poseidon sponge +/// +/// This implementation of Poseidon is entirely from Fractal's implementation in [COS20][cos] +/// with small syntax changes. +/// +/// [cos]: https://eprint.iacr.org/2019/1076 +pub struct PoseidonSpongeVar { + /// Constraint system + pub cs: ConstraintSystemRef, + + /// Sponge Parameters + pub parameters: PoseidonParameters, + + // Sponge State + /// The sponge's state + pub state: Vec>, + /// The mode + pub mode: DuplexSpongeMode, +} + +impl SpongeWithGadget for PoseidonSponge { + type Var = PoseidonSpongeVar; +} + +impl PoseidonSpongeVar { + #[tracing::instrument(target = "r1cs", skip(self))] + fn apply_s_box( + &self, + state: &mut [FpVar], + is_full_round: bool, + ) -> Result<(), SynthesisError> { + // 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.parameters.alpha])?; + } + } + // Partial rounds apply the S Box (x^alpha) to just the first element of state + else { + state[0] = state[0].pow_by_constant(&[self.parameters.alpha])?; + } + + Ok(()) + } + + #[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.parameters.ark[round_number][i]; + } + Ok(()) + } + + #[tracing::instrument(target = "r1cs", skip(self))] + fn apply_mds(&self, state: &mut [FpVar]) -> Result<(), SynthesisError> { + let mut new_state = Vec::new(); + let zero = FpVar::::zero(); + for i in 0..state.len() { + let mut cur = zero.clone(); + for (j, state_elem) in state.iter().enumerate() { + let term = state_elem * self.parameters.mds[i][j]; + cur += &term; + } + new_state.push(cur); + } + state.clone_from_slice(&new_state[..state.len()]); + Ok(()) + } + + #[tracing::instrument(target = "r1cs", skip(self))] + fn permute(&mut self) -> Result<(), SynthesisError> { + 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)?; + self.apply_s_box(&mut state, true)?; + self.apply_mds(&mut state)?; + } + 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.parameters.partial_rounds) + ..(self.parameters.partial_rounds + self.parameters.full_rounds) + { + self.apply_ark(&mut state, i)?; + self.apply_s_box(&mut state, true)?; + self.apply_mds(&mut state)?; + } + + self.state = state; + Ok(()) + } + + #[tracing::instrument(target = "r1cs", skip(self))] + fn absorb_internal( + &mut self, + mut rate_start_index: usize, + elements: &[FpVar], + ) -> Result<(), SynthesisError> { + let mut remaining_elements = elements; + loop { + // if we can finish in this call + if rate_start_index + remaining_elements.len() + <= self.parameters.rate + self.parameters.capacity + { + for (i, element) in remaining_elements.iter().enumerate() { + self.state[i + rate_start_index] += element; + } + 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.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.permute()?; + // the input elements got truncated by num elements absorbed + remaining_elements = &remaining_elements[num_elements_absorbed..]; + rate_start_index = 0; + } + } + + // Squeeze |output| many elements. This does not end in a squeeze + #[tracing::instrument(target = "r1cs", skip(self))] + fn squeeze_internal( + &mut self, + mut rate_start_index: usize, + output: &mut [FpVar], + ) -> Result<(), SynthesisError> { + let mut remaining_output = output; + loop { + // if we can finish in this call + if rate_start_index + remaining_output.len() <= self.parameters.rate { + remaining_output.clone_from_slice( + &self.state[self.parameters.capacity + rate_start_index + ..(self.parameters.capacity + remaining_output.len() + rate_start_index)], + ); + 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.parameters.rate - rate_start_index; + remaining_output[..num_elements_squeezed].clone_from_slice( + &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.parameters.rate { + self.permute()?; + } + // Repeat with updated output slices and rate start index + remaining_output = &mut remaining_output[num_elements_squeezed..]; + rate_start_index = 0; + } + } +} + +impl CryptographicSpongeVar> for PoseidonSpongeVar { + type Parameters = PoseidonParameters; + + #[tracing::instrument(target = "r1cs", skip(cs))] + fn new(cs: ConstraintSystemRef, parameters: &PoseidonParameters) -> Self { + let zero = FpVar::::zero(); + let state = vec![zero; parameters.rate + parameters.capacity]; + let mode = DuplexSpongeMode::Absorbing { + next_absorb_index: 0, + }; + + Self { + cs, + parameters: parameters.clone(), + state, + mode, + } + } + + #[tracing::instrument(target = "r1cs", skip(self))] + fn cs(&self) -> ConstraintSystemRef { + self.cs.clone() + } + + #[tracing::instrument(target = "r1cs", skip(self, input))] + fn absorb(&mut self, input: &impl AbsorbGadget) -> Result<(), SynthesisError> { + let input = input.to_sponge_field_elements()?; + if input.is_empty() { + return Ok(()); + } + + match self.mode { + DuplexSpongeMode::Absorbing { next_absorb_index } => { + let mut absorb_index = next_absorb_index; + if absorb_index == self.parameters.rate { + self.permute()?; + absorb_index = 0; + } + self.absorb_internal(absorb_index, input.as_slice())?; + } + DuplexSpongeMode::Squeezing { + next_squeeze_index: _, + } => { + self.permute()?; + self.absorb_internal(0, input.as_slice())?; + } + }; + + Ok(()) + } + + #[tracing::instrument(target = "r1cs", skip(self))] + fn squeeze_bytes(&mut self, num_bytes: usize) -> Result>, SynthesisError> { + let usable_bytes = (F::Params::CAPACITY / 8) as usize; + + let num_elements = (num_bytes + usable_bytes - 1) / usable_bytes; + let src_elements = self.squeeze_field_elements(num_elements)?; + + let mut bytes: Vec> = Vec::with_capacity(usable_bytes * num_elements); + for elem in &src_elements { + bytes.extend_from_slice(&elem.to_bytes()?[..usable_bytes]); + } + + bytes.truncate(num_bytes); + Ok(bytes) + } + + #[tracing::instrument(target = "r1cs", skip(self))] + fn squeeze_bits(&mut self, num_bits: usize) -> Result>, SynthesisError> { + let usable_bits = F::Params::CAPACITY as usize; + + let num_elements = (num_bits + usable_bits - 1) / usable_bits; + let src_elements = self.squeeze_field_elements(num_elements)?; + + let mut bits: Vec> = Vec::with_capacity(usable_bits * num_elements); + for elem in &src_elements { + bits.extend_from_slice(&elem.to_bits_le()?[..usable_bits]); + } + + bits.truncate(num_bits); + Ok(bits) + } + + #[tracing::instrument(target = "r1cs", skip(self))] + fn squeeze_field_elements( + &mut self, + num_elements: usize, + ) -> Result>, SynthesisError> { + let zero = FpVar::zero(); + let mut squeezed_elems = vec![zero; num_elements]; + match self.mode { + DuplexSpongeMode::Absorbing { + next_absorb_index: _, + } => { + self.permute()?; + self.squeeze_internal(0, &mut squeezed_elems)?; + } + DuplexSpongeMode::Squeezing { next_squeeze_index } => { + let mut squeeze_index = next_squeeze_index; + if squeeze_index == self.parameters.rate { + self.permute()?; + squeeze_index = 0; + } + self.squeeze_internal(squeeze_index, &mut squeezed_elems)?; + } + }; + + Ok(squeezed_elems) + } +} + +#[cfg(test)] +mod tests { + use crate::constraints::CryptographicSpongeVar; + use crate::poseidon::constraints::PoseidonSpongeVar; + use crate::poseidon::tests::poseidon_parameters_for_test; + use crate::poseidon::PoseidonSponge; + use crate::{CryptographicSponge, FieldBasedCryptographicSponge}; + use ark_ff::UniformRand; + use ark_r1cs_std::fields::fp::FpVar; + use ark_r1cs_std::prelude::*; + use ark_relations::r1cs::ConstraintSystem; + use ark_relations::*; + use ark_std::test_rng; + use ark_test_curves::bls12_381::Fr; + + #[test] + fn absorb_test() { + let mut rng = test_rng(); + let cs = ConstraintSystem::new_ref(); + + let absorb1: Vec<_> = (0..256).map(|_| Fr::rand(&mut rng)).collect(); + let absorb1_var: Vec<_> = absorb1 + .iter() + .map(|v| FpVar::new_input(ns!(cs, "absorb1"), || Ok(*v)).unwrap()) + .collect(); + + let absorb2: Vec<_> = (0..8).map(|i| vec![i, i + 1, i + 2]).collect(); + let absorb2_var: Vec<_> = absorb2 + .iter() + .map(|v| UInt8::new_input_vec(ns!(cs, "absorb2"), v).unwrap()) + .collect(); + + let sponge_params = poseidon_parameters_for_test(); + + let mut native_sponge = PoseidonSponge::::new(&sponge_params); + let mut constraint_sponge = PoseidonSpongeVar::::new(cs.clone(), &sponge_params); + + native_sponge.absorb(&absorb1); + constraint_sponge.absorb(&absorb1_var).unwrap(); + + let squeeze1 = native_sponge.squeeze_native_field_elements(1); + let squeeze2 = constraint_sponge.squeeze_field_elements(1).unwrap(); + + assert_eq!(squeeze2.value().unwrap(), squeeze1); + assert!(cs.is_satisfied().unwrap()); + + native_sponge.absorb(&absorb2); + constraint_sponge.absorb(&absorb2_var).unwrap(); + + let squeeze1 = native_sponge.squeeze_native_field_elements(1); + let squeeze2 = constraint_sponge.squeeze_field_elements(1).unwrap(); + + assert_eq!(squeeze2.value().unwrap(), squeeze1); + assert!(cs.is_satisfied().unwrap()); + } +} diff --git a/poseidon377/src/r1cs/vendor/sponge/poseidon/grain_lfsr.rs b/poseidon377/src/r1cs/vendor/sponge/poseidon/grain_lfsr.rs new file mode 100644 index 0000000..3ecc661 --- /dev/null +++ b/poseidon377/src/r1cs/vendor/sponge/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/poseidon377/src/r1cs/vendor/sponge/poseidon/mod.rs b/poseidon377/src/r1cs/vendor/sponge/poseidon/mod.rs new file mode 100644 index 0000000..56086ee --- /dev/null +++ b/poseidon377/src/r1cs/vendor/sponge/poseidon/mod.rs @@ -0,0 +1,462 @@ +use crate::{ + batch_field_cast, squeeze_field_elements_with_sizes_default_impl, Absorb, CryptographicSponge, + DuplexSpongeMode, FieldBasedCryptographicSponge, FieldElementSize, SpongeExt, +}; +use ark_ff::{BigInteger, FpParameters, PrimeField}; +use ark_std::any::TypeId; +use ark_std::vec; +use ark_std::vec::Vec; + +/// constraints for Poseidon +#[cfg(feature = "r1cs")] +pub mod constraints; +#[cfg(test)] +mod tests; + +/// 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)] +/// A duplex sponge based using the Poseidon permutation. +/// +/// This implementation of Poseidon is entirely from Fractal's implementation in [COS20][cos] +/// with small syntax changes. +/// +/// [cos]: https://eprint.iacr.org/2019/1076 +pub struct PoseidonSponge { + /// Sponge Parameters + pub parameters: PoseidonParameters, + + // Sponge State + /// 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 { + fn apply_s_box(&self, state: &mut [F], is_full_round: bool) { + // 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.parameters.alpha]); + } + } + // Partial rounds apply the S Box (x^alpha) to just the first element of state + else { + 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.parameters.ark[round_number][i]); + } + } + + fn apply_mds(&self, state: &mut [F]) { + let mut new_state = Vec::new(); + 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.parameters.mds[i][j]); + cur.add_assign(&term); + } + new_state.push(cur); + } + state.clone_from_slice(&new_state[..state.len()]) + } + + fn permute(&mut self) { + 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); + self.apply_s_box(&mut state, true); + self.apply_mds(&mut state); + } + + 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.parameters.partial_rounds) + ..(self.parameters.partial_rounds + self.parameters.full_rounds) + { + self.apply_ark(&mut state, i); + self.apply_s_box(&mut state, true); + self.apply_mds(&mut state); + } + self.state = state; + } + + // Absorbs everything in elements, this does not end in an absorbtion. + fn absorb_internal(&mut self, mut rate_start_index: usize, elements: &[F]) { + let mut remaining_elements = elements; + + loop { + // if we can finish in this call + // The internal state is rate + capacity elements. + if rate_start_index + remaining_elements.len() + <= self.parameters.rate + self.parameters.capacity + { + for (i, element) in remaining_elements.iter().enumerate() { + self.state[i + rate_start_index] += element; + } + 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.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.permute(); + // the input elements got truncated by num elements absorbed + remaining_elements = &remaining_elements[num_elements_absorbed..]; + rate_start_index = 0; + } + } + + // Squeeze |output| many elements. This does not end in a squeeze + fn squeeze_internal(&mut self, mut rate_start_index: usize, output: &mut [F]) { + let mut output_remaining = output; + loop { + // if we can finish in this call + if rate_start_index + output_remaining.len() <= self.parameters.rate { + output_remaining.clone_from_slice( + &self.state[self.parameters.capacity + rate_start_index + ..(self.parameters.capacity + output_remaining.len() + rate_start_index)], + ); + 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.parameters.rate - rate_start_index; + output_remaining[..num_elements_squeezed].clone_from_slice( + &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.parameters.rate { + self.permute(); + } + // Repeat with updated output slices + output_remaining = &mut output_remaining[num_elements_squeezed..]; + rate_start_index = 0; + } + } +} + +impl PoseidonParameters { + /// Initialize the parameter for Poseidon Sponge. + pub fn new( + full_rounds: usize, + partial_rounds: usize, + alpha: u64, + mds: Vec>, + ark: Vec>, + rate: usize, + capacity: usize, + ) -> Self { + assert_eq!(ark.len(), full_rounds + partial_rounds); + for item in &ark { + 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, + partial_rounds, + alpha, + mds, + ark, + rate, + capacity, + } + } +} + +impl CryptographicSponge for PoseidonSponge { + type Parameters = PoseidonParameters; + + fn new(parameters: &Self::Parameters) -> Self { + let state = vec![F::zero(); parameters.rate + parameters.capacity]; + let mode = DuplexSpongeMode::Absorbing { + next_absorb_index: 0, + }; + + Self { + parameters: parameters.clone(), + state, + mode, + } + } + + fn absorb(&mut self, input: &impl Absorb) { + let elems = input.to_sponge_field_elements_as_vec::(); + if elems.is_empty() { + return; + } + + match self.mode { + DuplexSpongeMode::Absorbing { next_absorb_index } => { + let mut absorb_index = next_absorb_index; + if absorb_index == self.parameters.rate { + self.permute(); + absorb_index = 0; + } + self.absorb_internal(absorb_index, elems.as_slice()); + } + DuplexSpongeMode::Squeezing { + next_squeeze_index: _, + } => { + self.permute(); + self.absorb_internal(0, elems.as_slice()); + } + }; + } + + fn squeeze_bytes(&mut self, num_bytes: usize) -> Vec { + let usable_bytes = (F::Params::CAPACITY / 8) as usize; + + let num_elements = (num_bytes + usable_bytes - 1) / usable_bytes; + let src_elements = self.squeeze_native_field_elements(num_elements); + + let mut bytes: Vec = Vec::with_capacity(usable_bytes * num_elements); + for elem in &src_elements { + let elem_bytes = elem.into_repr().to_bytes_le(); + bytes.extend_from_slice(&elem_bytes[..usable_bytes]); + } + + bytes.truncate(num_bytes); + bytes + } + + fn squeeze_bits(&mut self, num_bits: usize) -> Vec { + let usable_bits = F::Params::CAPACITY as usize; + + let num_elements = (num_bits + usable_bits - 1) / usable_bits; + let src_elements = self.squeeze_native_field_elements(num_elements); + + let mut bits: Vec = Vec::with_capacity(usable_bits * num_elements); + for elem in &src_elements { + let elem_bits = elem.into_repr().to_bits_le(); + bits.extend_from_slice(&elem_bits[..usable_bits]); + } + + bits.truncate(num_bits); + bits + } + + fn squeeze_field_elements_with_sizes( + &mut self, + sizes: &[FieldElementSize], + ) -> Vec { + if F::characteristic() == F2::characteristic() { + // native case + let mut buf = Vec::with_capacity(sizes.len()); + batch_field_cast( + &self.squeeze_native_field_elements_with_sizes(sizes), + &mut buf, + ) + .unwrap(); + buf + } else { + squeeze_field_elements_with_sizes_default_impl(self, sizes) + } + } + + fn squeeze_field_elements(&mut self, num_elements: usize) -> Vec { + if TypeId::of::() == TypeId::of::() { + let result = self.squeeze_native_field_elements(num_elements); + let mut cast = Vec::with_capacity(result.len()); + batch_field_cast(&result, &mut cast).unwrap(); + cast + } else { + self.squeeze_field_elements_with_sizes::( + vec![FieldElementSize::Full; num_elements].as_slice(), + ) + } + } +} + +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 { + DuplexSpongeMode::Absorbing { + next_absorb_index: _, + } => { + self.permute(); + self.squeeze_internal(0, &mut squeezed_elems); + } + DuplexSpongeMode::Squeezing { next_squeeze_index } => { + let mut squeeze_index = next_squeeze_index; + if squeeze_index == self.parameters.rate { + self.permute(); + squeeze_index = 0; + } + self.squeeze_internal(squeeze_index, &mut squeezed_elems); + } + }; + + squeezed_elems + } +} + +#[derive(Clone)] +/// Stores the state of a Poseidon Sponge. Does not store any parameter. +pub struct PoseidonSpongeState { + state: Vec, + mode: DuplexSpongeMode, +} + +impl SpongeExt for PoseidonSponge { + type State = PoseidonSpongeState; + + fn from_state(state: Self::State, params: &Self::Parameters) -> Self { + let mut sponge = Self::new(params); + sponge.mode = state.mode; + sponge.state = state.state; + sponge + } + + fn into_state(self) -> Self::State { + Self::State { + state: self.state, + mode: self.mode, + } + } +} + +#[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/poseidon377/src/r1cs/vendor/sponge/poseidon/tests.rs b/poseidon377/src/r1cs/vendor/sponge/poseidon/tests.rs new file mode 100644 index 0000000..62429e9 --- /dev/null +++ b/poseidon377/src/r1cs/vendor/sponge/poseidon/tests.rs @@ -0,0 +1,819 @@ +use crate::poseidon::{PoseidonParameters, PoseidonSponge}; +use crate::{absorb, collect_sponge_bytes, collect_sponge_field_elements}; +use crate::{Absorb, AbsorbWithLength, CryptographicSponge, FieldBasedCryptographicSponge}; +use ark_ff::{One, PrimeField, UniformRand}; +use ark_std::test_rng; +use ark_test_curves::bls12_381::Fr; +fn assert_different_encodings(a: &A, b: &A) { + let bytes1 = a.to_sponge_bytes_as_vec(); + let bytes2 = b.to_sponge_bytes_as_vec(); + assert_ne!(bytes1, bytes2); + + let sponge_param = poseidon_parameters_for_test(); + let mut sponge1 = PoseidonSponge::::new(&sponge_param); + let mut sponge2 = PoseidonSponge::::new(&sponge_param); + + sponge1.absorb(&a); + sponge2.absorb(&b); + + assert_ne!( + sponge1.squeeze_native_field_elements(3), + sponge2.squeeze_native_field_elements(3) + ); +} + +#[test] +fn single_field_element() { + let mut rng = test_rng(); + let elem1 = Fr::rand(&mut rng); + let elem2 = elem1 + Fr::one(); + + assert_different_encodings::(&elem1, &elem2) +} + +#[test] +fn list_with_constant_size_element() { + let mut rng = test_rng(); + let lst1: Vec<_> = (0..1024 * 8).map(|_| Fr::rand(&mut rng)).collect(); + let mut lst2 = lst1.to_vec(); + lst2[3] += Fr::one(); + + assert_different_encodings::(&lst1, &lst2) +} + +struct VariableSizeList(Vec); + +impl Absorb for VariableSizeList { + fn to_sponge_bytes(&self, dest: &mut Vec) { + self.0.to_sponge_bytes_with_length(dest) + } + + fn to_sponge_field_elements(&self, dest: &mut Vec) { + self.0.to_sponge_field_elements_with_length(dest) + } +} + +#[test] +fn list_with_nonconstant_size_element() { + let lst1 = vec![ + VariableSizeList(vec![1u8, 2, 3, 4]), + VariableSizeList(vec![5, 6]), + ]; + let lst2 = vec![ + VariableSizeList(vec![1u8, 2]), + VariableSizeList(vec![3, 4, 5, 6]), + ]; + + assert_different_encodings::(&lst1, &lst2); +} + +#[test] +fn test_squeeze_cast_native() { + let mut rng = test_rng(); + let sponge_param = poseidon_parameters_for_test(); + let elem = Fr::rand(&mut rng); + let mut sponge1 = PoseidonSponge::::new(&sponge_param); + sponge1.absorb(&elem); + let mut sponge2 = sponge1.clone(); + + // those two should return same result + let squeezed1 = sponge1.squeeze_native_field_elements(5); + let squeezed2 = sponge2.squeeze_field_elements::(5); + + assert_eq!(squeezed1, squeezed2); +} + +#[test] +fn test_macros() { + let sponge_param = poseidon_parameters_for_test(); + let mut sponge1 = PoseidonSponge::::new(&sponge_param); + sponge1.absorb(&vec![1, 2, 3, 4, 5, 6]); + sponge1.absorb(&Fr::from(114514u128)); + + let mut sponge2 = PoseidonSponge::::new(&sponge_param); + absorb!(&mut sponge2, vec![1, 2, 3, 4, 5, 6], Fr::from(114514u128)); + + let expected = sponge1.squeeze_native_field_elements(3); + let actual = sponge2.squeeze_native_field_elements(3); + + assert_eq!(actual, expected); + + let mut expected = Vec::new(); + vec![6, 5, 4, 3, 2, 1].to_sponge_bytes(&mut expected); + Fr::from(42u8).to_sponge_bytes(&mut expected); + + let actual = collect_sponge_bytes!(vec![6, 5, 4, 3, 2, 1], Fr::from(42u8)); + + assert_eq!(actual, expected); + + let mut expected: Vec = Vec::new(); + vec![6, 5, 4, 3, 2, 1].to_sponge_field_elements(&mut expected); + Fr::from(42u8).to_sponge_field_elements(&mut expected); + + let actual: Vec = collect_sponge_field_elements!(vec![6, 5, 4, 3, 2, 1], Fr::from(42u8)); + + assert_eq!(actual, expected); +} + +/// Generate default parameters (bls381-fr-only) for alpha = 17, state-size = 8 +pub(crate) fn poseidon_parameters_for_test() -> PoseidonParameters { + let alpha = 17; + let mds = vec![ + vec![ + F::from_str( + "43228725308391137369947362226390319299014033584574058394339561338097152657858", + ) + .map_err(|_| ()) + .unwrap(), + F::from_str( + "20729134655727743386784826341366384914431326428651109729494295849276339718592", + ) + .map_err(|_| ()) + .unwrap(), + F::from_str( + "14275792724825301816674509766636153429127896752891673527373812580216824074377", + ) + .map_err(|_| ()) + .unwrap(), + ], + vec![ + F::from_str( + "3039440043015681380498693766234886011876841428799441709991632635031851609481", + ) + .map_err(|_| ()) + .unwrap(), + F::from_str( + "6678863357926068615342013496680930722082156498064457711885464611323928471101", + ) + .map_err(|_| ()) + .unwrap(), + F::from_str( + "37355038393562575053091209735467454314247378274125943833499651442997254948957", + ) + .map_err(|_| ()) + .unwrap(), + ], + vec![ + F::from_str( + "26481612700543967643159862864328231943993263806649000633819754663276818191580", + ) + .map_err(|_| ()) + .unwrap(), + F::from_str( + "30103264397473155564098369644643015994024192377175707604277831692111219371047", + ) + .map_err(|_| ()) + .unwrap(), + F::from_str( + "5712721806190262694719203887224391960978962995663881615739647362444059585747", + ) + .map_err(|_| ()) + .unwrap(), + ], + ]; + let ark = vec![ + vec![ + F::from_str( + "44595993092652566245296379427906271087754779418564084732265552598173323099784", + ) + .map_err(|_| ()) + .unwrap(), + F::from_str( + "23298463296221002559050231199021122673158929708101049474262017406235785365706", + ) + .map_err(|_| ()) + .unwrap(), + F::from_str( + "34212491019164671611180318500074499609633402631511849759183986060951187784466", + ) + .map_err(|_| ()) + .unwrap(), + ], + vec![ + F::from_str( + "19098051134080182375553680073525644187968170656591203562523489333616681350367", + ) + .map_err(|_| ()) + .unwrap(), + F::from_str( + "7027675418691353855077049716619550622043312043660992344940177187528247727783", + ) + .map_err(|_| ()) + .unwrap(), + F::from_str( + "47642753235356257928619065424282314733361764347085604019867862722762702755609", + ) + .map_err(|_| ()) + .unwrap(), + ], + vec![ + F::from_str( + "24281836129477728386327945482863886685457469794572168729834072693507088619997", + ) + .map_err(|_| ()) + .unwrap(), + F::from_str( + "12624893078331920791384400430193929292743809612452779381349824703573823883410", + ) + .map_err(|_| ()) + .unwrap(), + F::from_str( + "22654862987689323504199204643771547606936339944127455903448909090318619188561", + ) + .map_err(|_| ()) + .unwrap(), + ], + vec![ + F::from_str( + "27229172992560143399715985732065737093562061782414043625359531774550940662372", + ) + .map_err(|_| ()) + .unwrap(), + F::from_str( + "13224952063922250960936823741448973692264041750100990569445192064567307041002", + ) + .map_err(|_| ()) + .unwrap(), + F::from_str( + "40380869235216625717296601204704413215735530626882135230693823362552484855508", + ) + .map_err(|_| ()) + .unwrap(), + ], + vec![ + F::from_str( + "4245751157938905689397184705633683893932492370323323780371834663438472308145", + ) + .map_err(|_| ()) + .unwrap(), + F::from_str( + "8252156875535418429533049587170755750275631534314711502253775796882240991261", + ) + .map_err(|_| ()) + .unwrap(), + F::from_str( + "32910829712934971129644416249914075073083903821282503505466324428991624789936", + ) + .map_err(|_| ()) + .unwrap(), + ], + vec![ + F::from_str( + "49412601297460128335642438246716127241669915737656789613664349252868389975962", + ) + .map_err(|_| ()) + .unwrap(), + F::from_str( + "841661305510340459373323516098909074520942972558284146843779636353111592117", + ) + .map_err(|_| ()) + .unwrap(), + F::from_str( + "37926489020263024391336570420006226544461516787280929232555625742588667303947", + ) + .map_err(|_| ()) + .unwrap(), + ], + vec![ + F::from_str( + "18433043696013996573551852847056868761017170818820490351056924728720017242180", + ) + .map_err(|_| ()) + .unwrap(), + F::from_str( + "45376910275288438312773930242803223482318753992595269901397542214841496212310", + ) + .map_err(|_| ()) + .unwrap(), + F::from_str( + "47854349410014339708332226068958253098964727682486278458389508597930796651514", + ) + .map_err(|_| ()) + .unwrap(), + ], + vec![ + F::from_str( + "32638426693771251366613055506166587312642876874690861030672730491779486904360", + ) + .map_err(|_| ()) + .unwrap(), + F::from_str( + "19105439281696418043426755774110765432959446684037017837894045255490581318047", + ) + .map_err(|_| ()) + .unwrap(), + F::from_str( + "13484299981373196201166722380389594773562113262309564134825386266765751213853", + ) + .map_err(|_| ()) + .unwrap(), + ], + vec![ + F::from_str( + "63360321133852659797114062808297090090814531427710842859827725871241144161", + ) + .map_err(|_| ()) + .unwrap(), + F::from_str( + "42427543035537409467993338717379268954936885184662765745740070438835506287271", + ) + .map_err(|_| ()) + .unwrap(), + F::from_str( + "149101987103211771991327927827692640556911620408176100290586418839323044234", + ) + .map_err(|_| ()) + .unwrap(), + ], + vec![ + F::from_str( + "8341764062226826803887898710015561861526081583071950015446833446251359696930", + ) + .map_err(|_| ()) + .unwrap(), + F::from_str( + "45635980415044299013530304465786867101223925975971912073759959440335364441441", + ) + .map_err(|_| ()) + .unwrap(), + F::from_str( + "49833261156201520743834327917353893365097424877680239796845398698940689734850", + ) + .map_err(|_| ()) + .unwrap(), + ], + vec![ + F::from_str( + "26764715016591436228000634284249890185894507497739511725029482580508707525029", + ) + .map_err(|_| ()) + .unwrap(), + F::from_str( + "25054530812095491217523557726611612265064441619646263299990388543372685322499", + ) + .map_err(|_| ()) + .unwrap(), + F::from_str( + "47654590955096246997622155031169641628093104787883934397920286718814889326452", + ) + .map_err(|_| ()) + .unwrap(), + ], + vec![ + F::from_str( + "16463825890556752307085325855351334996898686633642574805918056141310194135796", + ) + .map_err(|_| ()) + .unwrap(), + F::from_str( + "17473961341633494489168064889016732306117097771640351649096482400214968053040", + ) + .map_err(|_| ()) + .unwrap(), + F::from_str( + "49914603434867854893558366922996753035832008639512305549839666311012232077468", + ) + .map_err(|_| ()) + .unwrap(), + ], + vec![ + F::from_str( + "17122578514152308432111470949473865420090463026624297565504381163777697818362", + ) + .map_err(|_| ()) + .unwrap(), + F::from_str( + "34870689836420861427379101859113225049736283485335674111421609473028315711541", + ) + .map_err(|_| ()) + .unwrap(), + F::from_str( + "4622082908476410083286670201138165773322781640914243047922441301693321472984", + ) + .map_err(|_| ()) + .unwrap(), + ], + vec![ + F::from_str( + "6079244375752010013798561155333454682564824861645642293573415833483620500976", + ) + .map_err(|_| ()) + .unwrap(), + F::from_str( + "2635090520059500019661864086615522409798872905401305311748231832709078452746", + ) + .map_err(|_| ()) + .unwrap(), + F::from_str( + "19070766579582338321241892986615538320421651429118757507174186491084617237586", + ) + .map_err(|_| ()) + .unwrap(), + ], + vec![ + F::from_str( + "12622420533971517050761060317049369208980632120901481436392835424625664738526", + ) + .map_err(|_| ()) + .unwrap(), + F::from_str( + "8965101225657199137904506150282256568170501907667138404080397024857524386266", + ) + .map_err(|_| ()) + .unwrap(), + F::from_str( + "27085091008069524593196374148553176565775450537072498305327481366756159319838", + ) + .map_err(|_| ()) + .unwrap(), + ], + vec![ + F::from_str( + "45929056591150668409624595495643698205830429971690813312608217341940499221218", + ) + .map_err(|_| ()) + .unwrap(), + F::from_str( + "50361689160518167880500080025023064746137161030119436080957023803101861300846", + ) + .map_err(|_| ()) + .unwrap(), + F::from_str( + "6722586346537620732668048024627882970582133613352245923413730968378696371065", + ) + .map_err(|_| ()) + .unwrap(), + ], + vec![ + F::from_str( + "7340485916200743279276570085958556798507770452421357119145466906520506506342", + ) + .map_err(|_| ()) + .unwrap(), + F::from_str( + "25946733168219652706630789514519162148860502996914241011500280690204368174083", + ) + .map_err(|_| ()) + .unwrap(), + F::from_str( + "9962367658743163006517635070396368828381757404628822422306438427554934645464", + ) + .map_err(|_| ()) + .unwrap(), + ], + vec![ + F::from_str( + "7221669722700687417346373353960536661883467014204005276831020252277657076044", + ) + .map_err(|_| ()) + .unwrap(), + F::from_str( + "21487980358388383563030903293359140836304488103090321183948009095669344637431", + ) + .map_err(|_| ()) + .unwrap(), + F::from_str( + "44389482047246878765773958430749333249729101516826571588063797358040130313157", + ) + .map_err(|_| ()) + .unwrap(), + ], + vec![ + F::from_str( + "32887270862917330820874162842519225370447850172085449103568878409533683733185", + ) + .map_err(|_| ()) + .unwrap(), + F::from_str( + "15453393396765207016379045014101989306173462885430532298601655955681532648226", + ) + .map_err(|_| ()) + .unwrap(), + F::from_str( + "5478929644476681096437469958231489102974161353940993351588559414552523375472", + ) + .map_err(|_| ()) + .unwrap(), + ], + vec![ + F::from_str( + "41981370411247590312677561209178363054744730805951096631186178388981705304138", + ) + .map_err(|_| ()) + .unwrap(), + F::from_str( + "3474136981645476955784428843999869229067282976757744542648188369810577298585", + ) + .map_err(|_| ()) + .unwrap(), + F::from_str( + "26251477770740399889956219915654371915771248171098220204692699710414817081869", + ) + .map_err(|_| ()) + .unwrap(), + ], + vec![ + F::from_str( + "51916561889718854106125837319509539220778634838409949714061033196765117231752", + ) + .map_err(|_| ()) + .unwrap(), + F::from_str( + "25355145802812435959748831835587713214179184608408449220418373832038339021974", + ) + .map_err(|_| ()) + .unwrap(), + F::from_str( + "31950684570730625275416731570246297947385359051792335826965013637877068017530", + ) + .map_err(|_| ()) + .unwrap(), + ], + vec![ + F::from_str( + "40966378914980473680181850710703295982197782082391794594149984057481543436879", + ) + .map_err(|_| ()) + .unwrap(), + F::from_str( + "1141315130963422417761731263662398620858625339733452795772225916965481730059", + ) + .map_err(|_| ()) + .unwrap(), + F::from_str( + "9812100862165422922235757591915383485338044715409891361026651619010947646011", + ) + .map_err(|_| ()) + .unwrap(), + ], + vec![ + F::from_str( + "25276091996614379065765602410190790163396484122487585763380676888280427744737", + ) + .map_err(|_| ()) + .unwrap(), + F::from_str( + "18512694312063606403196469408971540495273694846641903978723927656359350642619", + ) + .map_err(|_| ()) + .unwrap(), + F::from_str( + "5791584766415439694303685437881192048262049244830616851865505314899699012588", + ) + .map_err(|_| ()) + .unwrap(), + ], + vec![ + F::from_str( + "34501536331706470927069149344450300773777486993504673779438188495686129846168", + ) + .map_err(|_| ()) + .unwrap(), + F::from_str( + "10797737565565774079718466476236831116206064650762676383469703413649447678207", + ) + .map_err(|_| ()) + .unwrap(), + F::from_str( + "42599392747310354323136214835734307933597896695637215127297036595538235868368", + ) + .map_err(|_| ()) + .unwrap(), + ], + vec![ + F::from_str( + "1336670998775417133322626564820911986969949054454812685145275612519924150700", + ) + .map_err(|_| ()) + .unwrap(), + F::from_str( + "2630141283339761901081411552890260088516693208402906795133548756078952896770", + ) + .map_err(|_| ()) + .unwrap(), + F::from_str( + "5206688943117414740600380377278238268309952400341418217132724749372435975215", + ) + .map_err(|_| ()) + .unwrap(), + ], + vec![ + F::from_str( + "10739264253827005683370721104077252560524362323422172665530191908848354339715", + ) + .map_err(|_| ()) + .unwrap(), + F::from_str( + "48010640624945719826344492755710886355389194986527731603685956726907395779674", + ) + .map_err(|_| ()) + .unwrap(), + F::from_str( + "47880724693177306044229143357252697148359033158394459365791331000715957339701", + ) + .map_err(|_| ()) + .unwrap(), + ], + vec![ + F::from_str( + "51658938856669444737833983076793759752280196674149218924101718974926964118996", + ) + .map_err(|_| ()) + .unwrap(), + F::from_str( + "27558055650076329657496888512074319504342606463881203707330358472954748913263", + ) + .map_err(|_| ()) + .unwrap(), + F::from_str( + "38886981777859313701520424626728402175860609948757992393598285291689196608037", + ) + .map_err(|_| ()) + .unwrap(), + ], + vec![ + F::from_str( + "17152756165118461969542990684402410297675979513690903033350206658079448802479", + ) + .map_err(|_| ()) + .unwrap(), + F::from_str( + "43766946932033687220387514221943418338304186408056458476301583041390483707207", + ) + .map_err(|_| ()) + .unwrap(), + F::from_str( + "24324495647041812436929170644873622904287038078113808264580396461953421400343", + ) + .map_err(|_| ()) + .unwrap(), + ], + vec![ + F::from_str( + "6935839211798937659784055008131602708847374430164859822530563797964932598700", + ) + .map_err(|_| ()) + .unwrap(), + F::from_str( + "42126767398190942911395299419182514513368023621144776598842282267908712110039", + ) + .map_err(|_| ()) + .unwrap(), + F::from_str( + "5702364486091252903915715761606014714345316580946072019346660327857498603375", + ) + .map_err(|_| ()) + .unwrap(), + ], + vec![ + F::from_str( + "28184981699552917714085740963279595942132561155181044254318202220270242523053", + ) + .map_err(|_| ()) + .unwrap(), + F::from_str( + "27078204494010940048327822707224393686245007379331357330801926151074766130790", + ) + .map_err(|_| ()) + .unwrap(), + F::from_str( + "5004172841233947987988267535285080365124079140142987718231874743202918551203", + ) + .map_err(|_| ()) + .unwrap(), + ], + vec![ + F::from_str( + "7974360962120296064882769128577382489451060235999590492215336103105134345602", + ) + .map_err(|_| ()) + .unwrap(), + F::from_str( + "48062035869818179910046292951628308709251170031813126950740044942870578526376", + ) + .map_err(|_| ()) + .unwrap(), + F::from_str( + "26361151154829600651603985995297072258262605598910254660032612019129606811983", + ) + .map_err(|_| ()) + .unwrap(), + ], + vec![ + F::from_str( + "46973867849986280770641828877435510444176572688208439836496241838832695841519", + ) + .map_err(|_| ()) + .unwrap(), + F::from_str( + "1219439673853113792340300173186247996249367102884530407862469123523013083971", + ) + .map_err(|_| ()) + .unwrap(), + F::from_str( + "8063356002935671186275773257019749639571745240775941450161086349727882957042", + ) + .map_err(|_| ()) + .unwrap(), + ], + vec![ + F::from_str( + "8815571992701260640209942886673939234666734294275300852283020522390608544536", + ) + .map_err(|_| ()) + .unwrap(), + F::from_str( + "36384568984671043678320545346945893232044626942887414733675890845013312931948", + ) + .map_err(|_| ()) + .unwrap(), + F::from_str( + "7493936589040764830842760521372106574503511314427857201860148571929278344956", + ) + .map_err(|_| ()) + .unwrap(), + ], + vec![ + F::from_str( + "26516538878265871822073279450474977673130300973488209984756372331392531193948", + ) + .map_err(|_| ()) + .unwrap(), + F::from_str( + "3872858659373466814413243601289105962248870842202907364656526273784217311104", + ) + .map_err(|_| ()) + .unwrap(), + F::from_str( + "8291822807524000248589997648893671538524566700364221355689839490238724479848", + ) + .map_err(|_| ()) + .unwrap(), + ], + vec![ + F::from_str( + "32842548776827046388198955038089826231531188946525483251252938248379132381248", + ) + .map_err(|_| ()) + .unwrap(), + F::from_str( + "10749428410907700061565796335489079278748501945557710351216806276547834974736", + ) + .map_err(|_| ()) + .unwrap(), + F::from_str( + "43342287917341177925402357903832370099402579088513884654598017447701677948416", + ) + .map_err(|_| ()) + .unwrap(), + ], + vec![ + F::from_str( + "29658571352070370791360499299098360881857072189358092237807807261478461425147", + ) + .map_err(|_| ()) + .unwrap(), + F::from_str( + "7805182565862454238315452208989152534554369855020544477885853141626690738363", + ) + .map_err(|_| ()) + .unwrap(), + F::from_str( + "30699555847500141715826240743138908521140760599479365867708690318477369178275", + ) + .map_err(|_| ()) + .unwrap(), + ], + vec![ + F::from_str( + "1231951350103545216624376889222508148537733140742167414518514908719103925687", + ) + .map_err(|_| ()) + .unwrap(), + F::from_str( + "24784260089125933876714702247471508077514206350883487938806451152907502751770", + ) + .map_err(|_| ()) + .unwrap(), + F::from_str( + "36563542611079418454711392295126742705798573252480028863133394504154697924536", + ) + .map_err(|_| ()) + .unwrap(), + ], + ]; + 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/poseidon377/src/r1cs/vendor/sponge/poseidon/traits.rs b/poseidon377/src/r1cs/vendor/sponge/poseidon/traits.rs new file mode 100644 index 0000000..d288be9 --- /dev/null +++ b/poseidon377/src/r1cs/vendor/sponge/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" + ) + ); + } +}