From 5dd59ec28232b1599bef6d6887eddfdf109c07a3 Mon Sep 17 00:00:00 2001 From: redshiftzero Date: Tue, 25 Apr 2023 13:22:21 -0400 Subject: [PATCH] vendor: add code of `ark-sponge` fork to `poseidon377` This is the `r1cs` branch from our git fork of `ark-sponge`: https://github.com/penumbra-zone/sponge It is unmodified in this commit, changes will be made in the subsequent commits for clarity of the git history. The upstream `ark-sponge` repository is archived because they moved the poseidon implementation to a different repository. In addition, we need to make a lot of changes to get on the upstream impl. This was attempted back in November and at that time we decided to continue using our fork only for the R1CS feature. However, in light of the upcoming migration to Poseidon2, any work resolving the divergence between our fork and the upstream repo would be wasted, as we'll be soon removing Poseidon 1 from `poseidon377`. As such, for stability, we are vendoring our fork of `ark-sponge` on a temporary basis such that we can release `poseidon377` unchanged to crates, then pin all penumbra repositories to that release. Afterwards, we will proceed with migrating to Arkworks 0.4.x release series [0] and migrating to Poseidon2 [1]. [0] https://github.com/penumbra-zone/penumbra/issues/2004 [1] https://github.com/penumbra-zone/poseidon377/issues/40 --- poseidon377/Cargo.toml | 4 - poseidon377/src/r1cs.rs | 11 +- poseidon377/src/r1cs/vendor.rs | 3 + poseidon377/src/r1cs/vendor/absorb.rs | 394 +++++++++ poseidon377/src/r1cs/vendor/sponge.rs | 213 +++++ .../r1cs/vendor/sponge/constraints/absorb.rs | 263 ++++++ .../src/r1cs/vendor/sponge/constraints/mod.rs | 188 ++++ .../vendor/sponge/poseidon/constraints.rs | 350 ++++++++ .../r1cs/vendor/sponge/poseidon/grain_lfsr.rs | 216 +++++ .../src/r1cs/vendor/sponge/poseidon/mod.rs | 462 ++++++++++ .../src/r1cs/vendor/sponge/poseidon/tests.rs | 819 ++++++++++++++++++ .../src/r1cs/vendor/sponge/poseidon/traits.rs | 454 ++++++++++ 12 files changed, 3369 insertions(+), 8 deletions(-) create mode 100644 poseidon377/src/r1cs/vendor.rs create mode 100644 poseidon377/src/r1cs/vendor/absorb.rs create mode 100644 poseidon377/src/r1cs/vendor/sponge.rs create mode 100644 poseidon377/src/r1cs/vendor/sponge/constraints/absorb.rs create mode 100644 poseidon377/src/r1cs/vendor/sponge/constraints/mod.rs create mode 100644 poseidon377/src/r1cs/vendor/sponge/poseidon/constraints.rs create mode 100644 poseidon377/src/r1cs/vendor/sponge/poseidon/grain_lfsr.rs create mode 100644 poseidon377/src/r1cs/vendor/sponge/poseidon/mod.rs create mode 100644 poseidon377/src/r1cs/vendor/sponge/poseidon/tests.rs create mode 100644 poseidon377/src/r1cs/vendor/sponge/poseidon/traits.rs 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" + ) + ); + } +}