From 2974aad07968a2e6d4b20051304c8421f69d61b0 Mon Sep 17 00:00:00 2001 From: Alisander Qoshqosh Date: Thu, 9 Jan 2025 19:27:42 +0400 Subject: [PATCH 01/61] ++ --- Cargo.lock | 1 - Cargo.toml | 3 - lib/crypto/Cargo.toml | 2 - lib/crypto/src/bigint.rs | 253 +++++++++++++++++++++++++++----- lib/crypto/src/const_helpers.rs | 97 ++++++++++++ lib/crypto/src/field/fp.rs | 127 ++++++---------- lib/crypto/src/lib.rs | 1 + 7 files changed, 359 insertions(+), 125 deletions(-) create mode 100644 lib/crypto/src/const_helpers.rs diff --git a/Cargo.lock b/Cargo.lock index e669b1633..5d4167b49 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2734,7 +2734,6 @@ dependencies = [ name = "openzeppelin-crypto" version = "0.2.0-alpha.2" dependencies = [ - "crypto-bigint", "educe", "hex-literal", "num-traits", diff --git a/Cargo.toml b/Cargo.toml index 610d339e7..d92466585 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -107,9 +107,6 @@ tiny-keccak = { version = "2.0.2", features = ["keccak"] } tokio = { version = "1.12.0", features = ["full"] } futures = "0.3.30" dashmap = "6.1.0" -crypto-bigint = { version = "0.5.5", default-features = false, features = [ - "zeroize", -] } num-traits = "0.2.14" zeroize = { version = "1.8.1", features = ["derive"] } proptest = "1" diff --git a/lib/crypto/Cargo.toml b/lib/crypto/Cargo.toml index 16e3eab03..4c6922e50 100644 --- a/lib/crypto/Cargo.toml +++ b/lib/crypto/Cargo.toml @@ -12,12 +12,10 @@ version.workspace = true tiny-keccak.workspace = true num-traits.workspace = true zeroize.workspace = true -crypto-bigint.workspace = true educe.workspace = true hex-literal.workspace = true [dev-dependencies] -crypto-bigint = { workspace = true, default-features = false, features = ["rand"] } rand.workspace = true proptest.workspace = true diff --git a/lib/crypto/src/bigint.rs b/lib/crypto/src/bigint.rs index 5701739b9..01fc90314 100644 --- a/lib/crypto/src/bigint.rs +++ b/lib/crypto/src/bigint.rs @@ -9,13 +9,196 @@ use core::{ }, }; -#[allow(clippy::module_name_repetitions)] -pub use crypto_bigint; -use crypto_bigint::{Encoding, Integer, Limb, Uint, Word, Zero}; use num_traits::ConstZero; use zeroize::Zeroize; -use crate::bits::BitIteratorBE; +use crate::{adc, bits::BitIteratorBE, const_for, const_modulo, sbb}; + +pub type Word = u64; +// TODO#q: Have Fp as residue integers +// TODO#q: Uint - normal big integers +// TODO#q: Limbs - wrapper type for [Limb; N] array +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Zeroize)] +pub struct BigInt(pub [Word; N]); + +impl Default for BigInt { + fn default() -> Self { + Self([0u64; N]) + } +} + +impl BigInt { + pub const fn new(value: [u64; N]) -> Self { + Self(value) + } + + pub const fn zero() -> Self { + Self([0u64; N]) + } + + pub const fn one() -> Self { + let mut one = Self::zero(); + one.0[0] = 1; + one + } + + #[doc(hidden)] + pub const fn const_is_even(&self) -> bool { + self.0[0] % 2 == 0 + } + + #[doc(hidden)] + pub const fn const_is_odd(&self) -> bool { + self.0[0] % 2 == 1 + } + + #[doc(hidden)] + pub const fn mod_4(&self) -> u8 { + // To compute n % 4, we need to simply look at the + // 2 least significant bits of n, and check their value mod 4. + (((self.0[0] << 62) >> 62) % 4) as u8 + } + + /// Compute a right shift of `self` + /// This is equivalent to a (saturating) division by 2. + #[doc(hidden)] + pub const fn const_shr(&self) -> Self { + let mut result = *self; + let mut t = 0; + crate::const_for!((i in 0..N) { + let a = result.0[N - i - 1]; + let t2 = a << 63; + result.0[N - i - 1] >>= 1; + result.0[N - i - 1] |= t; + t = t2; + }); + result + } + + const fn const_geq(&self, other: &Self) -> bool { + const_for!((i in 0..N) { + let a = self.0[N - i - 1]; + let b = other.0[N - i - 1]; + if a < b { + return false; + } else if a > b { + return true; + } + }); + true + } + + /// Compute the largest integer `s` such that `self = 2**s * t + 1` for odd + /// `t`. + #[doc(hidden)] + pub const fn two_adic_valuation(mut self) -> u32 { + assert!(self.const_is_odd()); + let mut two_adicity = 0; + // Since `self` is odd, we can always subtract one + // without a borrow + self.0[0] -= 1; + while self.const_is_even() { + self = self.const_shr(); + two_adicity += 1; + } + two_adicity + } + + /// Compute the smallest odd integer `t` such that `self = 2**s * t + 1` for + /// some integer `s = self.two_adic_valuation()`. + #[doc(hidden)] + pub const fn two_adic_coefficient(mut self) -> Self { + assert!(self.const_is_odd()); + // Since `self` is odd, we can always subtract one + // without a borrow + self.0[0] -= 1; + while self.const_is_even() { + self = self.const_shr(); + } + assert!(self.const_is_odd()); + self + } + + /// Divide `self` by 2, rounding down if necessary. + /// That is, if `self.is_odd()`, compute `(self - 1)/2`. + /// Else, compute `self/2`. + #[doc(hidden)] + pub const fn divide_by_2_round_down(mut self) -> Self { + if self.const_is_odd() { + self.0[0] -= 1; + } + self.const_shr() + } + + /// Find the number of bits in the binary decomposition of `self`. + #[doc(hidden)] + pub const fn const_num_bits(self) -> u32 { + ((N - 1) * 64) as u32 + (64 - self.0[N - 1].leading_zeros()) + } + + #[inline] + pub(crate) const fn const_sub_with_borrow( + mut self, + other: &Self, + ) -> (Self, bool) { + let mut borrow = 0; + + const_for!((i in 0..N) { + self.0[i] = sbb!(self.0[i], other.0[i], &mut borrow); + }); + + (self, borrow != 0) + } + + #[inline] + pub(crate) const fn const_add_with_carry( + mut self, + other: &Self, + ) -> (Self, bool) { + let mut carry = 0; + + crate::const_for!((i in 0..N) { + self.0[i] = adc!(self.0[i], other.0[i], &mut carry); + }); + + (self, carry != 0) + } + + const fn const_mul2_with_carry(mut self) -> (Self, bool) { + let mut last = 0; + crate::const_for!((i in 0..N) { + let a = self.0[i]; + let tmp = a >> 63; + self.0[i] <<= 1; + self.0[i] |= last; + last = tmp; + }); + (self, last != 0) + } + + pub(crate) const fn const_is_zero(&self) -> bool { + let mut is_zero = true; + crate::const_for!((i in 0..N) { + is_zero &= self.0[i] == 0; + }); + is_zero + } + + /// Computes the Montgomery R constant modulo `self`. + #[doc(hidden)] + pub const fn montgomery_r(&self) -> Self { + let two_pow_n_times_64 = crate::const_helpers::RBuffer([0u64; N], 1); + const_modulo!(two_pow_n_times_64, self) + } + + /// Computes the Montgomery R2 constant modulo `self`. + #[doc(hidden)] + pub const fn montgomery_r2(&self) -> Self { + let two_pow_n_times_64_square = + crate::const_helpers::R2Buffer([0u64; N], [0u64; N], 1); + const_modulo!(two_pow_n_times_64_square, self) + } +} /// Defines a big integer with a constant length. pub trait BigInteger: @@ -132,39 +315,39 @@ pub trait BigInteger: fn into_bytes_le(self) -> alloc::vec::Vec; } -impl BigInteger for Uint { +impl BigInteger for BigInt { const NUM_LIMBS: usize = N; fn is_odd(&self) -> bool { - as Integer>::is_odd(self).into() + unimplemented!() } fn is_even(&self) -> bool { - as Integer>::is_even(self).into() + unimplemented!() } fn is_zero(&self) -> bool { - as Zero>::is_zero(self).into() + unimplemented!() } fn num_bits(&self) -> usize { - self.bits() + unimplemented!() } fn get_bit(&self, i: usize) -> bool { - self.bit(i).into() + unimplemented!() } fn from_bytes_le(bytes: &[u8]) -> Self { - Self::from_le_slice(bytes) + unimplemented!() } fn into_bytes_le(self) -> alloc::vec::Vec { - self.to_limbs().into_iter().flat_map(|l| l.to_le_bytes()).collect() + unimplemented!() } } -impl BitIteratorBE for Uint { +impl BitIteratorBE for BigInt { fn bit_be_iter(&self) -> impl Iterator { self.as_words().iter().rev().flat_map(Word::bit_be_iter) } @@ -180,7 +363,7 @@ impl BitIteratorBE for Uint { pub const fn from_str_radix( s: &str, radix: u32, -) -> Uint { +) -> BigInt { let bytes = s.as_bytes(); assert!(!bytes.is_empty(), "empty string"); @@ -188,12 +371,12 @@ pub const fn from_str_radix( // Begin parsing from the last index of the string. let mut index = bytes.len() - 1; - let mut uint = Uint::from_u32(0); - let mut order = Uint::from_u32(1); - let uint_radix = Uint::from_u32(radix); + let mut uint = BigInt::from_u32(0); + let mut order = BigInt::from_u32(1); + let uint_radix = BigInt::from_u32(radix); loop { - let digit = Uint::from_u32(parse_digit(bytes[index], radix)); + let digit = BigInt::from_u32(parse_digit(bytes[index], radix)); // Add a digit multiplied by order. uint = add(&uint, &mul(&digit, &order)); @@ -219,7 +402,7 @@ pub const fn from_str_radix( /// If the string number is shorter, then [`Uint`] can store. /// Returns a [`Uint`] with leading zeroes. #[must_use] -pub const fn from_str_hex(s: &str) -> Uint { +pub const fn from_str_hex(s: &str) -> BigInt { let bytes = s.as_bytes(); assert!(!bytes.is_empty(), "empty string"); @@ -234,7 +417,7 @@ pub const fn from_str_hex(s: &str) -> Uint { let digit_radix = 16; let digit_size = 4; // Size of a hex digit in bits (2^4 = 16). - let digits_in_limb = Limb::BITS / digit_size; + let digits_in_limb = Word::BITS / digit_size; loop { let digit = parse_digit(bytes[index], digit_radix) as Word; @@ -246,7 +429,7 @@ pub const fn from_str_hex(s: &str) -> Uint { // If we reached the beginning of the string, return the number. if index == 0 { - return Uint::from_words(num); + return BigInt::new(num); } // Move to the next digit. @@ -258,9 +441,9 @@ pub const fn from_str_hex(s: &str) -> Uint { /// Multiply two numbers and panic on overflow. #[must_use] const fn mul( - a: &Uint, - b: &Uint, -) -> Uint { + a: &BigInt, + b: &BigInt, +) -> BigInt { let (low, high) = a.mul_wide(b); assert!(high.bits() == 0, "overflow on multiplication"); low @@ -269,10 +452,10 @@ const fn mul( /// Add two numbers and panic on overflow. #[must_use] const fn add( - a: &Uint, - b: &Uint, -) -> Uint { - let (low, carry) = a.adc(b, Limb::ZERO); + a: &BigInt, + b: &BigInt, +) -> BigInt { + let (low, carry) = a.adc(b, Word::ZERO); assert!(carry.0 == 0, "overflow on addition"); low } @@ -320,12 +503,12 @@ mod test { #[test] fn convert_from_str_radix() { - let uint_from_base10: Uint<4> = from_str_radix( + let uint_from_base10: BigInt<4> = from_str_radix( "28948022309329048855892746252171976963363056481941647379679742748393362948097", 10 ); #[allow(clippy::unreadable_literal)] - let expected = Uint::<4>::from_words([ + let expected = BigInt::<4>::new([ 10108024940646105089u64, 2469829653919213789u64, 0u64, @@ -333,9 +516,9 @@ mod test { ]); assert_eq!(uint_from_base10, expected); - let uint_from_base10: Uint<1> = + let uint_from_base10: BigInt<1> = from_str_radix("18446744069414584321", 10); - let uint_from_binary: Uint<1> = from_str_radix( + let uint_from_binary: BigInt<1> = from_str_radix( "1111111111111111111111111111111100000000000000000000000000000001", 2, ); @@ -346,8 +529,8 @@ mod test { fn convert_from_str_hex() { // Test different implementations of hex parsing on random hex inputs. proptest!(|(s in "[0-9a-fA-F]{1,64}")| { - let uint_from_hex: Uint<4> = from_str_hex(&s); - let expected: Uint<4> = from_str_radix(&s, 16); + let uint_from_hex: BigInt<4> = from_str_hex(&s); + let expected: BigInt<4> = from_str_radix(&s, 16); assert_eq!(uint_from_hex, expected); }); } @@ -355,7 +538,7 @@ mod test { #[test] fn uint_bit_iterator_be() { let words: [Word; 4] = [0b1100, 0, 0, 0]; - let num = Uint::<4>::from_words(words); + let num = BigInt::<4>::new(words); let bits: Vec = num.bit_be_trimmed_iter().collect(); assert_eq!(bits.len(), 4); diff --git a/lib/crypto/src/const_helpers.rs b/lib/crypto/src/const_helpers.rs new file mode 100644 index 000000000..09c036f0c --- /dev/null +++ b/lib/crypto/src/const_helpers.rs @@ -0,0 +1,97 @@ +#[macro_export] +macro_rules! const_for { + (($i:ident in $start:tt..$end:tt) $code:expr ) => {{ + let mut $i = $start; + while $i < $end { + $code + $i += 1; + } + }}; +} + +#[macro_export] +macro_rules! sbb { + ($a:expr, $b:expr, &mut $borrow:expr $(,)?) => {{ + let tmp = + (1u128 << 64) + ($a as u128) - ($b as u128) - ($borrow as u128); + $borrow = if tmp >> 64 == 0 { 1 } else { 0 }; + tmp as u64 + }}; +} + +#[macro_export] +macro_rules! adc { + ($a:expr, $b:expr, &mut $carry:expr $(,)?) => {{ + let tmp = ($a as u128) + ($b as u128) + ($carry as u128); + $carry = (tmp >> 64) as u64; + tmp as u64 + }}; +} + +// TODO#q: implement as a function +#[macro_export] +macro_rules! const_modulo { + ($a:expr, $divisor:expr) => {{ + // Stupid slow base-2 long division taken from + // https://en.wikipedia.org/wiki/Division_algorithm + assert!(!$divisor.const_is_zero()); + let mut remainder = Self::new([0u64; N]); + let mut i = ($a.num_bits() - 1) as isize; + let mut carry; + while i >= 0 { + (remainder, carry) = remainder.const_mul2_with_carry(); + remainder.0[0] |= $a.get_bit(i as usize) as u64; + if remainder.const_geq($divisor) || carry { + let (r, borrow) = remainder.const_sub_with_borrow($divisor); + remainder = r; + assert!(borrow == carry); + } + i -= 1; + } + remainder + }}; +} + +pub(super) struct RBuffer(pub [u64; N], pub u64); + +impl RBuffer { + /// Find the number of bits in the binary decomposition of `self`. + pub(super) const fn num_bits(&self) -> u32 { + (N * 64) as u32 + (64 - self.1.leading_zeros()) + } + + /// Returns the `i`-th bit where bit 0 is the least significant one. + /// In other words, the bit with weight `2^i`. + pub(super) const fn get_bit(&self, i: usize) -> bool { + let d = i / 64; + let b = i % 64; + if d == N { + (self.1 >> b) & 1 == 1 + } else { + (self.0[d] >> b) & 1 == 1 + } + } +} + +pub(super) struct R2Buffer(pub [u64; N], pub [u64; N], pub u64); + +impl R2Buffer { + /// Find the number of bits in the binary decomposition of `self`. + pub(super) const fn num_bits(&self) -> u32 { + ((2 * N) * 64) as u32 + (64 - self.2.leading_zeros()) + } + + /// Returns the `i`-th bit where bit 0 is the least significant one. + /// In other words, the bit with weight `2^i`. + pub(super) const fn get_bit(&self, i: usize) -> bool { + let d = i / 64; + let b = i % 64; + if d == 2 * N { + (self.2 >> b) & 1 == 1 + } else if d >= N { + (self.1[d - N] >> b) & 1 == 1 + } else { + (self.0[d] >> b) & 1 == 1 + } + } +} diff --git a/lib/crypto/src/field/fp.rs b/lib/crypto/src/field/fp.rs index d5ee9052f..b0a481e1b 100644 --- a/lib/crypto/src/field/fp.rs +++ b/lib/crypto/src/field/fp.rs @@ -21,23 +21,19 @@ use core::{ marker::PhantomData, }; -use crypto_bigint::{ - modular::{ - constant_mod::{Residue, ResidueParams}, - montgomery_reduction, - }, - Limb, Uint, Word, -}; use educe::Educe; use num_traits::{One, Zero}; -use crate::field::{group::AdditiveGroup, prime::PrimeField, Field}; +use crate::{ + bigint::{BigInt, Word}, + field::{group::AdditiveGroup, prime::PrimeField, Field}, +}; /// A trait that specifies the configuration of a prime field. /// Also specifies how to perform arithmetic on field elements. pub trait FpParams: Send + Sync + 'static + Sized { /// The modulus of the field. - const MODULUS: Uint; + const MODULUS: BigInt; /// A multiplicative generator of the field. /// [`Self::GENERATOR`] is an element having multiplicative order @@ -46,55 +42,52 @@ pub trait FpParams: Send + Sync + 'static + Sized { /// Set `a += b`. fn add_assign(a: &mut Fp, b: &Fp) { - a.residue += b.residue; + unimplemented!() } /// Set `a -= b`. fn sub_assign(a: &mut Fp, b: &Fp) { - a.residue -= b.residue; + unimplemented!() } /// Set `a = a + a`. fn double_in_place(a: &mut Fp) { - a.residue += a.residue; + unimplemented!() } /// Set `a = -a`; fn neg_in_place(a: &mut Fp) { - a.residue = a.residue.neg(); + unimplemented!() } /// Set `a *= b`. fn mul_assign(a: &mut Fp, b: &Fp) { - a.residue *= b.residue; + unimplemented!() } /// Set `a *= a`. fn square_in_place(a: &mut Fp) { - a.residue = a.residue.square(); + unimplemented!() } /// Compute `a^{-1}` if `a` is not zero. #[must_use] fn inverse(a: &Fp) -> Option> { - let (residue, choice) = a.residue.invert(); - let is_inverse: bool = choice.into(); - - is_inverse.then_some(Fp { residue }) + unimplemented!() } /// Construct a field element from an integer. /// /// By the end element will be converted to a montgomery form and reduced. #[must_use] - fn from_bigint(r: Uint) -> Fp { - Fp::new(r) + fn from_bigint(r: BigInt) -> Fp { + unimplemented!() } /// Convert a field element to an integer less than [`Self::MODULUS`]. #[must_use] - fn into_bigint(a: Fp) -> Uint { - a.residue.retrieve() + fn into_bigint(a: Fp) -> BigInt { + unimplemented!() } } @@ -104,12 +97,12 @@ pub trait FpParams: Send + Sync + 'static + Sized { /// for 64-bit systems and N * 32 bits for 32-bit systems. #[derive(Educe)] #[educe(Default, Clone, Copy, PartialEq, Eq)] -pub struct Fp, const LIMBS: usize> { +pub struct Fp, const LIMBS: usize>( /// Contains the element in Montgomery form for efficient multiplication. /// To convert an element to a [`BigInt`], use [`FpParams::into_bigint`] /// or `into`. - residue: Residue, LIMBS>, -} + BigInt, +); /// Declare [`Fp`] types for different bit sizes. macro_rules! declare_fp { @@ -119,14 +112,14 @@ macro_rules! declare_fp { #[doc = "bits size element."] pub type $fp

= crate::field::fp::Fp< P, - { usize::div_ceil($bits, ::crypto_bigint::Word::BITS as usize) }, + { usize::div_ceil($bits, ::crate::bigint::Word::BITS as usize) }, >; #[doc = "Number of limbs in the field with"] #[doc = stringify!($bits)] #[doc = "bits size element."] pub const $limbs: usize = - usize::div_ceil($bits, ::crypto_bigint::Word::BITS as usize); + usize::div_ceil($bits, ::crate::bigint::Word::BITS as usize); }; } @@ -144,34 +137,6 @@ declare_fp!(Fp704, LIMBS_704, 704); declare_fp!(Fp768, LIMBS_768, 768); declare_fp!(Fp832, LIMBS_832, 832); -#[derive(Educe)] -#[educe(Clone, Copy, Debug, Default, Eq, PartialEq)] -struct ResidueParam, const LIMBS: usize>(PhantomData

); - -impl, const LIMBS: usize> ResidueParams - for ResidueParam -{ - const LIMBS: usize = LIMBS; - const MODULUS: Uint = { - let modulus = P::MODULUS; - // Uint represents integer in low-endian form. - assert!(modulus.as_limbs()[0].0 & 1 == 1, "modulus must be odd"); - modulus - }; - const MOD_NEG_INV: Limb = Limb(Word::MIN.wrapping_sub( - P::MODULUS.inv_mod2k_vartime(Word::BITS as usize).as_limbs()[0].0, - )); - const R: Uint = - Uint::MAX.const_rem(&P::MODULUS).0.wrapping_add(&Uint::ONE); - const R2: Uint = - Uint::const_rem_wide(Self::R.square_wide(), &P::MODULUS).0; - const R3: Uint = montgomery_reduction( - &Self::R2.square_wide(), - &P::MODULUS, - Self::MOD_NEG_INV, - ); -} - impl, const LIMBS: usize> Fp { /// A multiplicative generator of the field. /// [`Self::GENERATOR`] is an element having multiplicative order @@ -184,20 +149,20 @@ impl, const LIMBS: usize> Fp { pub const ONE: Fp = Fp::new_unchecked(Self::R); /// Let `M` be the power of 2^64 nearest to [`Self::MODULUS_BITS`]. Then /// `R = M % MODULUS`. - const R: Uint = ResidueParam::::R; + const R: BigInt = unimplemented!(); /// `R2 = R^2 % MODULUS` #[allow(dead_code)] - const R2: Uint = ResidueParam::::R2; + const R2: BigInt = unimplemented!(); /// Additive identity of the field, i.e., the element `e` /// such that, for all elements `f` of the field, `e + f = f`. - pub const ZERO: Fp = Fp::new_unchecked(Uint::ZERO); + pub const ZERO: Fp = unimplemented!(); /// Construct a new field element from [`Uint`] and convert it in /// Montgomery form. #[inline] #[must_use] - pub const fn new(element: Uint) -> Self { - Fp { residue: Residue::, LIMBS>::new(&element) } + pub const fn new(element: BigInt) -> Self { + unimplemented!() } /// Construct a new field element from [`Uint`]. @@ -207,18 +172,14 @@ impl, const LIMBS: usize> Fp { /// integer that has already been put in Montgomery form. #[inline] #[must_use] - pub const fn new_unchecked(element: Uint) -> Self { - Fp { - residue: Residue::, LIMBS>::from_montgomery( - element, - ), - } + pub const fn new_unchecked(element: BigInt) -> Self { + unimplemented!() } } impl, const LIMBS: usize> Hash for Fp { fn hash(&self, state: &mut H) { - self.residue.as_montgomery().hash(state); + unimplemented!() } } @@ -308,17 +269,17 @@ impl, const LIMBS: usize> Field for Fp { } impl, const LIMBS: usize> PrimeField for Fp { - type BigInt = Uint; + type BigInt = BigInt; const MODULUS: Self::BigInt = P::MODULUS; - const MODULUS_BIT_SIZE: usize = P::MODULUS.bits(); + const MODULUS_BIT_SIZE: usize = unimplemented!(); #[inline] fn from_bigint(repr: Self::BigInt) -> Self { P::from_bigint(repr) } - fn into_bigint(self) -> Uint { + fn into_bigint(self) -> BigInt { P::into_bigint(self) } } @@ -343,7 +304,7 @@ macro_rules! impl_fp_from_unsigned_int { for Fp { fn from(other: $int) -> Self { - Fp::from_bigint(Uint::from(other)) + Fp::from_bigint(BigInt::from(other)) } } }; @@ -414,15 +375,14 @@ impl_int_from_fp!(i32); impl_int_from_fp!(i16); impl_int_from_fp!(i8); -#[cfg(test)] -impl, const LIMBS: usize> crypto_bigint::Random - for Fp -{ +// TODO#q: implement random for Fp +/*#[cfg(test)] +impl, const LIMBS: usize> Random for Fp { #[inline] - fn random(rng: &mut impl crypto_bigint::rand_core::CryptoRngCore) -> Self { + fn random(rng: &mut impl rand_core::CryptoRngCore) -> Self { Fp { residue: Residue::, LIMBS>::random(rng) } } -} +}*/ /// Outputs a string containing the value of `self`, /// represented as a decimal without leading zeroes. @@ -824,12 +784,12 @@ impl, const LIMBS: usize> zeroize::Zeroize for Fp { // The phantom data does not contain element-specific data // and thus does not need to be zeroized. fn zeroize(&mut self) { - self.residue.zeroize(); + self.0.zeroize(); } } impl, const LIMBS: usize> From> - for Uint + for BigInt { #[inline] fn from(fp: Fp) -> Self { @@ -837,12 +797,12 @@ impl, const LIMBS: usize> From> } } -impl, const LIMBS: usize> From> +impl, const LIMBS: usize> From> for Fp { /// Converts `Self::BigInteger` into `Self` #[inline] - fn from(int: Uint) -> Self { + fn from(int: BigInt) -> Self { Self::from_bigint(int) } } @@ -869,7 +829,6 @@ mod tests { use super::*; use crate::{ - bigint::crypto_bigint::U64, field::{ fp::{Fp64, FpParams, LIMBS_64}, group::AdditiveGroup, @@ -881,7 +840,7 @@ mod tests { struct Fp64Param; impl FpParams for Fp64Param { const GENERATOR: Fp64 = fp_from_num!("3"); - const MODULUS: U64 = from_num!("1000003"); // Prime number + const MODULUS: BigInt = from_num!("1000003"); // Prime number } const MODULUS: i128 = 1000003; // Prime number diff --git a/lib/crypto/src/lib.rs b/lib/crypto/src/lib.rs index 76080443a..21ddd1f22 100644 --- a/lib/crypto/src/lib.rs +++ b/lib/crypto/src/lib.rs @@ -27,6 +27,7 @@ pub mod bigint; pub mod bits; #[macro_use] pub mod field; +mod const_helpers; pub mod hash; pub mod keccak; pub mod merkle; From 7b8a8bac5e5991646807ac511dee05ee7ab062e4 Mon Sep 17 00:00:00 2001 From: Alisander Qoshqosh Date: Thu, 9 Jan 2025 22:44:14 +0400 Subject: [PATCH 02/61] ++ --- examples/poseidon/src/lib.rs | 2 +- .../src/{bigint.rs => arithmetic/mod.rs} | 246 +++++++- lib/crypto/src/const_helpers.rs | 56 ++ lib/crypto/src/field/fp.rs | 543 ++++++++++++------ lib/crypto/src/field/mod.rs | 2 +- lib/crypto/src/field/prime.rs | 2 +- lib/crypto/src/lib.rs | 2 +- 7 files changed, 670 insertions(+), 183 deletions(-) rename lib/crypto/src/{bigint.rs => arithmetic/mod.rs} (68%) diff --git a/examples/poseidon/src/lib.rs b/examples/poseidon/src/lib.rs index de688ac1c..1db5ab680 100644 --- a/examples/poseidon/src/lib.rs +++ b/examples/poseidon/src/lib.rs @@ -5,7 +5,7 @@ use alloc::vec::Vec; use alloy_primitives::U256; use openzeppelin_crypto::{ - bigint::{crypto_bigint::Uint, BigInteger}, + arithmetic::{crypto_bigint::Uint, BigInteger}, field::{instance::FpBN256, prime::PrimeField}, poseidon2::{instance::bn256::BN256Params, Poseidon2}, }; diff --git a/lib/crypto/src/bigint.rs b/lib/crypto/src/arithmetic/mod.rs similarity index 68% rename from lib/crypto/src/bigint.rs rename to lib/crypto/src/arithmetic/mod.rs index 01fc90314..aa4f600c1 100644 --- a/lib/crypto/src/bigint.rs +++ b/lib/crypto/src/arithmetic/mod.rs @@ -10,6 +10,10 @@ use core::{ }; use num_traits::ConstZero; +use rand::{ + distributions::{Distribution, Standard}, + Rng, +}; use zeroize::Zeroize; use crate::{adc, bits::BitIteratorBE, const_for, const_modulo, sbb}; @@ -150,6 +154,20 @@ impl BigInt { (self, borrow != 0) } + #[inline] + #[allow(unused)] + pub(crate) fn mul2(&mut self) -> bool { + let mut last = 0; + for i in 0..N { + let a = &mut self.0[i]; + let tmp = *a >> 63; + *a <<= 1; + *a |= last; + last = tmp; + } + last != 0 + } + #[inline] pub(crate) const fn const_add_with_carry( mut self, @@ -198,6 +216,220 @@ impl BigInt { crate::const_helpers::R2Buffer([0u64; N], [0u64; N], 1); const_modulo!(two_pow_n_times_64_square, self) } + + pub(crate) fn sub_with_borrow(&mut self, other: &Self) -> bool { + let mut borrow = 0; + + for i in 0..N { + borrow = + sbb_for_sub_with_borrow(&mut self.0[i], other.0[i], borrow); + } + + borrow != 0 + } + + pub fn div2(&mut self) { + let mut t = 0; + for a in self.0.iter_mut().rev() { + let t2 = *a << 63; + *a >>= 1; + *a |= t; + t = t2; + } + } + + pub(crate) fn add_with_carry(&mut self, other: &Self) -> bool { + let mut carry = 0; + + for i in 0..N { + carry = adc_for_add_with_carry(&mut self.0[i], other.0[i], carry); + } + + carry != 0 + } +} + +/// Calculate a + b * c, returning the lower 64 bits of the result and setting +/// `carry` to the upper 64 bits. +#[inline(always)] +#[doc(hidden)] +pub fn mac(a: u64, b: u64, c: u64, carry: &mut u64) -> u64 { + let tmp = (a as u128) + widening_mul(b, c); + *carry = (tmp >> 64) as u64; + tmp as u64 +} + +#[inline(always)] +#[doc(hidden)] +pub const fn widening_mul(a: u64, b: u64) -> u128 { + // TODO#q: check out this optimization + #[cfg(not(target_family = "wasm"))] + { + a as u128 * b as u128 + } + #[cfg(target_family = "wasm")] + { + let a_lo = a as u32 as u64; + let a_hi = a >> 32; + let b_lo = b as u32 as u64; + let b_hi = b >> 32; + + let lolo = (a_lo * b_lo) as u128; + let lohi = ((a_lo * b_hi) as u128) << 32; + let hilo = ((a_hi * b_lo) as u128) << 32; + let hihi = ((a_hi * b_hi) as u128) << 64; + (lolo | hihi) + (lohi + hilo) + } +} + +/// Calculate a + b * c, discarding the lower 64 bits of the result and setting +/// `carry` to the upper 64 bits. +#[inline(always)] +#[doc(hidden)] +pub fn mac_discard(a: u64, b: u64, c: u64, carry: &mut u64) { + let tmp = (a as u128) + widening_mul(b, c); + *carry = (tmp >> 64) as u64; +} + +/// Calculate a + (b * c) + carry, returning the least significant digit +/// and setting carry to the most significant digit. +#[inline(always)] +#[doc(hidden)] +pub fn mac_with_carry(a: u64, b: u64, c: u64, carry: &mut u64) -> u64 { + let tmp = (a as u128) + widening_mul(b, c) + (*carry as u128); + *carry = (tmp >> 64) as u64; + tmp as u64 +} + +/// Sets a = a - b - borrow, and returns the borrow. +#[inline(always)] +#[allow(unused_mut)] +#[doc(hidden)] +pub fn sbb_for_sub_with_borrow(a: &mut u64, b: u64, borrow: u8) -> u8 { + #[cfg(all(target_arch = "x86_64", feature = "asm"))] + #[allow(unsafe_code)] + unsafe { + use core::arch::x86_64::_subborrow_u64; + _subborrow_u64(borrow, *a, b, a) + } + #[cfg(not(all(target_arch = "x86_64", feature = "asm")))] + { + let tmp = (1u128 << 64) + (*a as u128) - (b as u128) - (borrow as u128); + *a = tmp as u64; + u8::from(tmp >> 64 == 0) + } +} + +// TODO#q: adc can be unified with adc_for_add_with_carry +/// Sets a = a + b + carry, and returns the new carry. +#[inline(always)] +#[allow(unused_mut)] +#[doc(hidden)] +pub fn adc(a: &mut u64, b: u64, carry: u64) -> u64 { + let tmp = *a as u128 + b as u128 + carry as u128; + *a = tmp as u64; + (tmp >> 64) as u64 +} + +/// Sets a = a + b + carry, and returns the new carry. +#[inline(always)] +#[allow(unused_mut)] +#[doc(hidden)] +pub fn adc_for_add_with_carry(a: &mut u64, b: u64, carry: u8) -> u8 { + let tmp = *a as u128 + b as u128 + carry as u128; + *a = tmp as u64; + (tmp >> 64) as u8 +} + +// ----------- Traits Impls ----------- + +impl Ord for BigInt { + #[inline] + fn cmp(&self, other: &Self) -> core::cmp::Ordering { + use core::cmp::Ordering; + // TODO#q: check in benchmark for wasm + #[cfg(target_arch = "x86_64")] + for i in 0..N { + let a = &self.0[N - i - 1]; + let b = &other.0[N - i - 1]; + match a.cmp(b) { + Ordering::Equal => {} + order => return order, + }; + } + + #[cfg(not(target_arch = "x86_64"))] + for (a, b) in self.0.iter().rev().zip(other.0.iter().rev()) { + if let order @ (Ordering::Less | Ordering::Greater) = a.cmp(b) { + return order; + } + } + Ordering::Equal + } +} + +impl PartialOrd for BigInt { + #[inline] + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +// TODO#q: Implement rand Distribution +/*impl Distribution> for Standard { + fn sample(&self, rng: &mut R) -> BigInt { + BigInt([(); N].map(|_| rng.gen())) + } +}*/ + +impl AsMut<[u64]> for BigInt { + #[inline] + fn as_mut(&mut self) -> &mut [u64] { + &mut self.0 + } +} + +impl AsRef<[u64]> for BigInt { + #[inline] + fn as_ref(&self) -> &[u64] { + &self.0 + } +} + +impl From for BigInt { + #[inline] + fn from(val: u64) -> BigInt { + let mut repr = Self::default(); + repr.0[0] = val; + repr + } +} + +impl From for BigInt { + #[inline] + fn from(val: u32) -> BigInt { + let mut repr = Self::default(); + repr.0[0] = val.into(); + repr + } +} + +impl From for BigInt { + #[inline] + fn from(val: u16) -> BigInt { + let mut repr = Self::default(); + repr.0[0] = val.into(); + repr + } +} + +impl From for BigInt { + #[inline] + fn from(val: u8) -> BigInt { + let mut repr = Self::default(); + repr.0[0] = val.into(); + repr + } } /// Defines a big integer with a constant length. @@ -245,7 +477,7 @@ pub trait BigInteger: /// # Example /// /// ``` - /// use openzeppelin_crypto::bigint::{BigInteger, crypto_bigint::U64}; + /// use openzeppelin_crypto::arithmetic::{BigInteger, crypto_bigint::U64}; /// /// let mut one = U64::from(1u64); /// assert!(one.is_odd()); @@ -257,7 +489,7 @@ pub trait BigInteger: /// # Example /// /// ``` - /// use openzeppelin_crypto::bigint::{BigInteger, crypto_bigint::U64}; + /// use openzeppelin_crypto::arithmetic::{BigInteger, crypto_bigint::U64}; /// /// let mut two = U64::from(2u64); /// assert!(two.is_even()); @@ -269,7 +501,7 @@ pub trait BigInteger: /// # Example /// /// ``` - /// use openzeppelin_crypto::bigint::{BigInteger, crypto_bigint::U64}; + /// use openzeppelin_crypto::arithmetic::{BigInteger, crypto_bigint::U64}; /// /// let mut zero = U64::from(0u64); /// assert!(zero.is_zero()); @@ -279,7 +511,7 @@ pub trait BigInteger: /// Compute the minimum number of bits needed to encode this number. /// # Example /// ``` - /// use openzeppelin_crypto::bigint::{BigInteger, crypto_bigint::U64}; + /// use openzeppelin_crypto::arithmetic::{BigInteger, crypto_bigint::U64}; /// /// let zero = U64::from(0u64); /// assert_eq!(zero.num_bits(), 0); @@ -296,7 +528,7 @@ pub trait BigInteger: /// # Example /// /// ``` - /// use openzeppelin_crypto::bigint::{BigInteger, crypto_bigint::U64}; + /// use openzeppelin_crypto::arithmetic::{BigInteger, crypto_bigint::U64}; /// /// let mut one = U64::from(1u64); /// assert!(one.get_bit(0)); @@ -483,7 +715,7 @@ pub(crate) const fn parse_utf8_byte(byte: u8) -> char { #[macro_export] macro_rules! from_num { ($num:literal) => { - $crate::bigint::from_str_radix($num, 10) + $crate::arithmetic::from_str_radix($num, 10) }; } @@ -491,7 +723,7 @@ macro_rules! from_num { #[macro_export] macro_rules! from_hex { ($num:literal) => { - $crate::bigint::from_str_hex($num) + $crate::arithmetic::from_str_hex($num) }; } diff --git a/lib/crypto/src/const_helpers.rs b/lib/crypto/src/const_helpers.rs index 09c036f0c..31f43dcb8 100644 --- a/lib/crypto/src/const_helpers.rs +++ b/lib/crypto/src/const_helpers.rs @@ -1,3 +1,5 @@ +use core::ops::{Index, IndexMut}; + #[macro_export] macro_rules! const_for { (($i:ident in $start:tt..$end:tt) $code:expr ) => {{ @@ -95,3 +97,57 @@ impl R2Buffer { } } } + +/// A buffer to hold values of size 2 * N. This is mostly +/// a hack that's necessary until `generic_const_exprs` is stable. +#[derive(Copy, Clone)] +#[repr(C, align(8))] +pub(super) struct MulBuffer { + pub(super) b0: [u64; N], + pub(super) b1: [u64; N], +} + +impl MulBuffer { + const fn new(b0: [u64; N], b1: [u64; N]) -> Self { + Self { b0, b1 } + } + + pub(super) const fn zeroed() -> Self { + let b = [0u64; N]; + Self::new(b, b) + } + + #[inline(always)] + pub(super) const fn get(&self, index: usize) -> &u64 { + if index < N { + &self.b0[index] + } else { + &self.b1[index - N] + } + } + + #[inline(always)] + pub(super) fn get_mut(&mut self, index: usize) -> &mut u64 { + if index < N { + &mut self.b0[index] + } else { + &mut self.b1[index - N] + } + } +} + +impl Index for MulBuffer { + type Output = u64; + + #[inline(always)] + fn index(&self, index: usize) -> &Self::Output { + self.get(index) + } +} + +impl IndexMut for MulBuffer { + #[inline(always)] + fn index_mut(&mut self, index: usize) -> &mut Self::Output { + self.get_mut(index) + } +} diff --git a/lib/crypto/src/field/fp.rs b/lib/crypto/src/field/fp.rs index b0a481e1b..52fed8f90 100644 --- a/lib/crypto/src/field/fp.rs +++ b/lib/crypto/src/field/fp.rs @@ -25,83 +25,340 @@ use educe::Educe; use num_traits::{One, Zero}; use crate::{ - bigint::{BigInt, Word}, + arithmetic, + arithmetic::{BigInt, BigInteger, Word}, field::{group::AdditiveGroup, prime::PrimeField, Field}, }; /// A trait that specifies the configuration of a prime field. /// Also specifies how to perform arithmetic on field elements. -pub trait FpParams: Send + Sync + 'static + Sized { +// TODO#q: rename N -> L +// TODO#q: rename FpParams -> Params +pub trait FpParams: Send + Sync + 'static + Sized { /// The modulus of the field. - const MODULUS: BigInt; + const MODULUS: BigInt; /// A multiplicative generator of the field. /// [`Self::GENERATOR`] is an element having multiplicative order /// `MODULUS - 1`. - const GENERATOR: Fp; + const GENERATOR: Fp; + + const CAN_USE_NO_CARRY_MUL_OPT: bool = unimplemented!(); + + const MODULUS_HAS_SPARE_BIT: bool = unimplemented!(); + + /// INV = -MODULUS^{-1} mod 2^64 + const INV: u64 = inv::(); + + /// Let `M` be the power of 2^64 nearest to [`Self::MODULUS_BITS`]. Then + /// `R = M % MODULUS`. + const R: BigInt = unimplemented!(); + + /// `R2 = R^2 % MODULUS` + #[allow(dead_code)] + const R2: BigInt = unimplemented!(); /// Set `a += b`. - fn add_assign(a: &mut Fp, b: &Fp) { - unimplemented!() + fn add_assign(a: &mut Fp, b: &Fp) { + // This cannot exceed the backing capacity. + let c = a.0.add_with_carry(&b.0); + // However, it may need to be reduced + if Self::MODULUS_HAS_SPARE_BIT { + a.subtract_modulus() + } else { + a.subtract_modulus_with_carry(c) + } } /// Set `a -= b`. - fn sub_assign(a: &mut Fp, b: &Fp) { - unimplemented!() + fn sub_assign(a: &mut Fp, b: &Fp) { + // If `other` is larger than `self`, add the modulus to self first. + if b.0 > a.0 { + a.0.add_with_carry(&Self::MODULUS); + } + a.0.sub_with_borrow(&b.0); } /// Set `a = a + a`. - fn double_in_place(a: &mut Fp) { - unimplemented!() + fn double_in_place(a: &mut Fp) { + // This cannot exceed the backing capacity. + let c = a.0.mul2(); + // However, it may need to be reduced. + if Self::MODULUS_HAS_SPARE_BIT { + a.subtract_modulus() + } else { + a.subtract_modulus_with_carry(c) + } } /// Set `a = -a`; - fn neg_in_place(a: &mut Fp) { - unimplemented!() + fn neg_in_place(a: &mut Fp) { + if !a.is_zero() { + let mut tmp = Self::MODULUS; + tmp.sub_with_borrow(&a.0); + a.0 = tmp; + } } - /// Set `a *= b`. - fn mul_assign(a: &mut Fp, b: &Fp) { - unimplemented!() + /// This modular multiplication algorithm uses Montgomery + /// reduction for efficient implementation. It also additionally + /// uses the "no-carry optimization" outlined + /// [here](https://hackmd.io/@gnark/modular_multiplication) if + /// `Self::MODULUS` has (a) a non-zero MSB, and (b) at least one + /// zero bit in the rest of the modulus. + fn mul_assign(a: &mut Fp, b: &Fp) { + // No-carry optimisation applied to CIOS + if Self::CAN_USE_NO_CARRY_MUL_OPT { + let mut r = [0u64; N]; + + for i in 0..N { + let mut carry1 = 0u64; + r[0] = + arithmetic::mac(r[0], (a.0).0[0], (b.0).0[i], &mut carry1); + + let k = r[0].wrapping_mul(Self::INV); + + let mut carry2 = 0u64; + arithmetic::mac_discard( + r[0], + k, + Self::MODULUS.0[0], + &mut carry2, + ); + + for j in 1..N { + r[j] = arithmetic::mac_with_carry( + r[j], + (a.0).0[j], + (b.0).0[i], + &mut carry1, + ); + r[j - 1] = arithmetic::mac_with_carry( + r[j], + k, + Self::MODULUS.0[j], + &mut carry2, + ); + } + r[N - 1] = carry1 + carry2; + } + (a.0).0.copy_from_slice(&r); + a.subtract_modulus(); + } else { + // Alternative implementation + // Implements CIOS. + let (carry, res) = a.mul_without_cond_subtract(b); + *a = res; + + if Self::MODULUS_HAS_SPARE_BIT { + a.subtract_modulus_with_carry(carry); + } else { + a.subtract_modulus(); + } + } } /// Set `a *= a`. - fn square_in_place(a: &mut Fp) { - unimplemented!() + fn square_in_place(a: &mut Fp) { + if N == 1 { + // We default to multiplying with `a` using the `Mul` impl + // for the N == 1 case + *a *= *a; + return; + } + + let mut r = crate::const_helpers::MulBuffer::::zeroed(); + + let mut carry = 0; + for i in 0..(N - 1) { + for j in (i + 1)..N { + r[i + j] = arithmetic::mac_with_carry( + r[i + j], + (a.0).0[i], + (a.0).0[j], + &mut carry, + ); + } + r.b1[i] = carry; + carry = 0; + } + + r.b1[N - 1] = r.b1[N - 2] >> 63; + for i in 2..(2 * N - 1) { + r[2 * N - i] = (r[2 * N - i] << 1) | (r[2 * N - (i + 1)] >> 63); + } + r.b0[1] <<= 1; + + for i in 0..N { + r[2 * i] = arithmetic::mac_with_carry( + r[2 * i], + (a.0).0[i], + (a.0).0[i], + &mut carry, + ); + carry = arithmetic::adc(&mut r[2 * i + 1], 0, carry); + } + // Montgomery reduction + let mut carry2 = 0; + for i in 0..N { + let k = r[i].wrapping_mul(Self::INV); + carry = 0; + arithmetic::mac_discard(r[i], k, Self::MODULUS.0[0], &mut carry); + for j in 1..N { + r[j + i] = arithmetic::mac_with_carry( + r[j + i], + k, + Self::MODULUS.0[j], + &mut carry, + ); + } + carry2 = arithmetic::adc(&mut r.b1[i], carry, carry2); + } + (a.0).0.copy_from_slice(&r.b1); + if Self::MODULUS_HAS_SPARE_BIT { + a.subtract_modulus(); + } else { + a.subtract_modulus_with_carry(carry2 != 0); + } } /// Compute `a^{-1}` if `a` is not zero. #[must_use] - fn inverse(a: &Fp) -> Option> { - unimplemented!() + fn inverse(a: &Fp) -> Option> { + if a.is_zero() { + return None; + } + // Guajardo Kumar Paar Pelzl + // Efficient Software-Implementation of Finite Fields with Applications + // to Cryptography + // Algorithm 16 (BEA for Inversion in Fp) + + let one = BigInt::from(1u64); + + let mut u = a.0; + let mut v = Self::MODULUS; + let mut b = Fp::new_unchecked(Self::R2); // Avoids unnecessary reduction step. + let mut c = Fp::zero(); + + while u != one && v != one { + while u.is_even() { + u.div2(); + + if b.0.is_even() { + b.0.div2(); + } else { + let carry = b.0.add_with_carry(&Self::MODULUS); + b.0.div2(); + if !Self::MODULUS_HAS_SPARE_BIT && carry { + (b.0).0[N - 1] |= 1 << 63; + } + } + } + + while v.is_even() { + v.div2(); + + if c.0.is_even() { + c.0.div2(); + } else { + let carry = c.0.add_with_carry(&Self::MODULUS); + c.0.div2(); + if !Self::MODULUS_HAS_SPARE_BIT && carry { + (c.0).0[N - 1] |= 1 << 63; + } + } + } + + if v < u { + u.sub_with_borrow(&v); + b -= &c; + } else { + v.sub_with_borrow(&u); + c -= &b; + } + } + + if u == one { + Some(b) + } else { + Some(c) + } } /// Construct a field element from an integer. /// /// By the end element will be converted to a montgomery form and reduced. #[must_use] - fn from_bigint(r: BigInt) -> Fp { - unimplemented!() + fn from_bigint(r: BigInt) -> Option> { + let mut r = Fp::new_unchecked(r); + if r.is_zero() { + Some(r) + } else if r.is_geq_modulus() { + None + } else { + r *= &Fp::new_unchecked(Self::R2); + Some(r) + } } /// Convert a field element to an integer less than [`Self::MODULUS`]. #[must_use] - fn into_bigint(a: Fp) -> BigInt { - unimplemented!() + fn into_bigint(a: Fp) -> BigInt { + let mut r = (a.0).0; + // Montgomery Reduction + for i in 0..N { + let k = r[i].wrapping_mul(Self::INV); + let mut carry = 0; + + arithmetic::mac_with_carry(r[i], k, Self::MODULUS.0[0], &mut carry); + for j in 1..N { + r[(j + i) % N] = arithmetic::mac_with_carry( + r[(j + i) % N], + k, + Self::MODULUS.0[j], + &mut carry, + ); + } + r[i % N] = carry; + } + + BigInt::new(r) } } +/// Compute -M^{-1} mod 2^64. +pub const fn inv, const N: usize>() -> u64 { + // We compute this as follows. + // First, MODULUS mod 2^64 is just the lower 64 bits of MODULUS. + // Hence MODULUS mod 2^64 = MODULUS.0[0] mod 2^64. + // + // Next, computing the inverse mod 2^64 involves exponentiating by + // the multiplicative group order, which is euler_totient(2^64) - 1. + // Now, euler_totient(2^64) = 1 << 63, and so + // euler_totient(2^64) - 1 = (1 << 63) - 1 = 1111111... (63 digits). + // We compute this powering via standard square and multiply. + let mut inv = 1u64; + crate::const_for!((_i in 0..63) { + // Square + inv = inv.wrapping_mul(inv); + // Multiply + inv = inv.wrapping_mul(T::MODULUS.0[0]); + }); + inv.wrapping_neg() +} + /// Represents an element of the prime field `F_p`, where `p == P::MODULUS`. /// /// This type can represent elements in any field of size at most N * 64 bits /// for 64-bit systems and N * 32 bits for 32-bit systems. #[derive(Educe)] #[educe(Default, Clone, Copy, PartialEq, Eq)] -pub struct Fp, const LIMBS: usize>( +pub struct Fp, const N: usize>( /// Contains the element in Montgomery form for efficient multiplication. /// To convert an element to a [`BigInt`], use [`FpParams::into_bigint`] /// or `into`. - BigInt, + BigInt, + #[doc(hidden)] pub PhantomData

, ); /// Declare [`Fp`] types for different bit sizes. @@ -137,31 +394,25 @@ declare_fp!(Fp704, LIMBS_704, 704); declare_fp!(Fp768, LIMBS_768, 768); declare_fp!(Fp832, LIMBS_832, 832); -impl, const LIMBS: usize> Fp { +impl, const N: usize> Fp { /// A multiplicative generator of the field. /// [`Self::GENERATOR`] is an element having multiplicative order /// `MODULUS - 1`. /// /// Every element of the field should be represented as `GENERATOR^i` - pub const GENERATOR: Fp = P::GENERATOR; + pub const GENERATOR: Fp = P::GENERATOR; /// Multiplicative identity of the field, i.e., the element `e` /// such that, for all elements `f` of the field, `e * f = f`. - pub const ONE: Fp = Fp::new_unchecked(Self::R); - /// Let `M` be the power of 2^64 nearest to [`Self::MODULUS_BITS`]. Then - /// `R = M % MODULUS`. - const R: BigInt = unimplemented!(); - /// `R2 = R^2 % MODULUS` - #[allow(dead_code)] - const R2: BigInt = unimplemented!(); + pub const ONE: Fp = Fp::new_unchecked(P::R); /// Additive identity of the field, i.e., the element `e` /// such that, for all elements `f` of the field, `e + f = f`. - pub const ZERO: Fp = unimplemented!(); + pub const ZERO: Fp = unimplemented!(); /// Construct a new field element from [`Uint`] and convert it in /// Montgomery form. #[inline] #[must_use] - pub const fn new(element: BigInt) -> Self { + pub const fn new(element: BigInt) -> Self { unimplemented!() } @@ -172,24 +423,31 @@ impl, const LIMBS: usize> Fp { /// integer that has already been put in Montgomery form. #[inline] #[must_use] - pub const fn new_unchecked(element: BigInt) -> Self { + pub const fn new_unchecked(element: BigInt) -> Self { unimplemented!() } + + #[inline] + fn subtract_modulus(&mut self) { + if self.is_geq_modulus() { + self.0.sub_with_borrow(&Self::MODULUS); + } + } } -impl, const LIMBS: usize> Hash for Fp { +impl, const N: usize> Hash for Fp { fn hash(&self, state: &mut H) { unimplemented!() } } -impl, const LIMBS: usize> Debug for Fp { +impl, const N: usize> Debug for Fp { fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { Debug::fmt(&self.into_bigint(), f) } } -impl, const LIMBS: usize> Zero for Fp { +impl, const N: usize> Zero for Fp { #[inline] fn zero() -> Self { Self::ZERO @@ -201,7 +459,7 @@ impl, const LIMBS: usize> Zero for Fp { } } -impl, const LIMBS: usize> One for Fp { +impl, const N: usize> One for Fp { #[inline] fn one() -> Self { Self::ONE @@ -213,7 +471,7 @@ impl, const LIMBS: usize> One for Fp { } } -impl, const LIMBS: usize> AdditiveGroup for Fp { +impl, const N: usize> AdditiveGroup for Fp { type Scalar = Self; const ZERO: Self = Self::ZERO; @@ -238,8 +496,8 @@ impl, const LIMBS: usize> AdditiveGroup for Fp { } } -impl, const LIMBS: usize> Field for Fp { - const ONE: Self = Fp::new_unchecked(Self::R); +impl, const N: usize> Field for Fp { + const ONE: Self = Fp::new_unchecked(P::R); #[inline] fn square(&self) -> Self { @@ -268,29 +526,30 @@ impl, const LIMBS: usize> Field for Fp { } } -impl, const LIMBS: usize> PrimeField for Fp { - type BigInt = BigInt; +impl, const N: usize> PrimeField for Fp { + type BigInt = BigInt; const MODULUS: Self::BigInt = P::MODULUS; const MODULUS_BIT_SIZE: usize = unimplemented!(); #[inline] fn from_bigint(repr: Self::BigInt) -> Self { - P::from_bigint(repr) + // TODO#q: convert it from bigint of any size without `unwrap` + P::from_bigint(repr).unwrap() } - fn into_bigint(self) -> BigInt { + fn into_bigint(self) -> BigInt { P::into_bigint(self) } } -impl, const LIMBS: usize> Ord for Fp { +impl, const N: usize> Ord for Fp { fn cmp(&self, other: &Self) -> Ordering { self.into_bigint().cmp(&other.into_bigint()) } } -impl, const LIMBS: usize> PartialOrd for Fp { +impl, const N: usize> PartialOrd for Fp { #[inline] fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) @@ -300,9 +559,7 @@ impl, const LIMBS: usize> PartialOrd for Fp { /// Auto implements conversion from unsigned integer of type `$int` to [`Fp`]. macro_rules! impl_fp_from_unsigned_int { ($int:ty) => { - impl, const LIMBS: usize> From<$int> - for Fp - { + impl, const N: usize> From<$int> for Fp { fn from(other: $int) -> Self { Fp::from_bigint(BigInt::from(other)) } @@ -313,9 +570,7 @@ macro_rules! impl_fp_from_unsigned_int { /// Auto implements conversion from signed integer of type `$int` to [`Fp`]. macro_rules! impl_fp_from_signed_int { ($int:ty) => { - impl, const LIMBS: usize> From<$int> - for Fp - { + impl, const N: usize> From<$int> for Fp { fn from(other: $int) -> Self { let abs = other.unsigned_abs().into(); if other.is_positive() { @@ -340,7 +595,7 @@ impl_fp_from_signed_int!(i32); impl_fp_from_signed_int!(i16); impl_fp_from_signed_int!(i8); -impl, const LIMBS: usize> From for Fp { +impl, const N: usize> From for Fp { fn from(other: bool) -> Self { u8::from(other).into() } @@ -349,7 +604,7 @@ impl, const LIMBS: usize> From for Fp { /// Auto implements conversion from [`Fp`] to integer of type `$int`. /// /// Conversion is available only for a single limb field elements, -/// i.e. `LIMBS = 1`. +/// i.e. `N = 1`. macro_rules! impl_int_from_fp { ($int:ty) => { impl> From> for $int { @@ -377,16 +632,16 @@ impl_int_from_fp!(i8); // TODO#q: implement random for Fp /*#[cfg(test)] -impl, const LIMBS: usize> Random for Fp { +impl, const N: usize> Random for Fp { #[inline] fn random(rng: &mut impl rand_core::CryptoRngCore) -> Self { - Fp { residue: Residue::, LIMBS>::random(rng) } + Fp { residue: Residue::, N>::random(rng) } } }*/ /// Outputs a string containing the value of `self`, /// represented as a decimal without leading zeroes. -impl, const LIMBS: usize> Display for Fp { +impl, const N: usize> Display for Fp { #[inline] fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { let str = self.into_bigint().to_string(); @@ -394,7 +649,7 @@ impl, const LIMBS: usize> Display for Fp { } } -impl, const LIMBS: usize> core::ops::Neg for Fp { +impl, const N: usize> core::ops::Neg for Fp { type Output = Self; #[inline] @@ -404,9 +659,7 @@ impl, const LIMBS: usize> core::ops::Neg for Fp { } } -impl, const LIMBS: usize> core::ops::Add<&Fp> - for Fp -{ +impl, const N: usize> core::ops::Add<&Fp> for Fp { type Output = Self; #[inline] @@ -417,9 +670,7 @@ impl, const LIMBS: usize> core::ops::Add<&Fp> } } -impl, const LIMBS: usize> core::ops::Sub<&Fp> - for Fp -{ +impl, const N: usize> core::ops::Sub<&Fp> for Fp { type Output = Self; #[inline] @@ -430,9 +681,7 @@ impl, const LIMBS: usize> core::ops::Sub<&Fp> } } -impl, const LIMBS: usize> core::ops::Mul<&Fp> - for Fp -{ +impl, const N: usize> core::ops::Mul<&Fp> for Fp { type Output = Self; #[inline] @@ -443,9 +692,7 @@ impl, const LIMBS: usize> core::ops::Mul<&Fp> } } -impl, const LIMBS: usize> core::ops::Div<&Fp> - for Fp -{ +impl, const N: usize> core::ops::Div<&Fp> for Fp { type Output = Self; /// Returns `self * other.inverse()` if `other.inverse()` is `Some`, and @@ -458,13 +705,11 @@ impl, const LIMBS: usize> core::ops::Div<&Fp> } } -impl, const LIMBS: usize> core::ops::Add<&Fp> - for &Fp -{ - type Output = Fp; +impl, const N: usize> core::ops::Add<&Fp> for &Fp { + type Output = Fp; #[inline] - fn add(self, other: &Fp) -> Fp { + fn add(self, other: &Fp) -> Fp { use core::ops::AddAssign; let mut result = *self; result.add_assign(other); @@ -472,13 +717,11 @@ impl, const LIMBS: usize> core::ops::Add<&Fp> } } -impl, const LIMBS: usize> core::ops::Sub<&Fp> - for &Fp -{ - type Output = Fp; +impl, const N: usize> core::ops::Sub<&Fp> for &Fp { + type Output = Fp; #[inline] - fn sub(self, other: &Fp) -> Fp { + fn sub(self, other: &Fp) -> Fp { use core::ops::SubAssign; let mut result = *self; result.sub_assign(other); @@ -486,13 +729,11 @@ impl, const LIMBS: usize> core::ops::Sub<&Fp> } } -impl, const LIMBS: usize> core::ops::Mul<&Fp> - for &Fp -{ - type Output = Fp; +impl, const N: usize> core::ops::Mul<&Fp> for &Fp { + type Output = Fp; #[inline] - fn mul(self, other: &Fp) -> Fp { + fn mul(self, other: &Fp) -> Fp { use core::ops::MulAssign; let mut result = *self; result.mul_assign(other); @@ -500,13 +741,11 @@ impl, const LIMBS: usize> core::ops::Mul<&Fp> } } -impl, const LIMBS: usize> core::ops::Div<&Fp> - for &Fp -{ - type Output = Fp; +impl, const N: usize> core::ops::Div<&Fp> for &Fp { + type Output = Fp; #[inline] - fn div(self, other: &Fp) -> Fp { + fn div(self, other: &Fp) -> Fp { use core::ops::DivAssign; let mut result = *self; result.div_assign(other); @@ -514,18 +753,14 @@ impl, const LIMBS: usize> core::ops::Div<&Fp> } } -impl, const LIMBS: usize> core::ops::AddAssign<&Self> - for Fp -{ +impl, const N: usize> core::ops::AddAssign<&Self> for Fp { #[inline] fn add_assign(&mut self, other: &Self) { P::add_assign(self, other); } } -impl, const LIMBS: usize> core::ops::SubAssign<&Self> - for Fp -{ +impl, const N: usize> core::ops::SubAssign<&Self> for Fp { #[inline] fn sub_assign(&mut self, other: &Self) { P::sub_assign(self, other); @@ -533,9 +768,7 @@ impl, const LIMBS: usize> core::ops::SubAssign<&Self> } #[allow(unused_qualifications)] -impl, const LIMBS: usize> core::ops::Add - for Fp -{ +impl, const N: usize> core::ops::Add for Fp { type Output = Self; #[inline] @@ -547,9 +780,7 @@ impl, const LIMBS: usize> core::ops::Add } #[allow(unused_qualifications)] -impl, const LIMBS: usize> core::ops::Add<&mut Self> - for Fp -{ +impl, const N: usize> core::ops::Add<&mut Self> for Fp { type Output = Self; #[inline] @@ -561,9 +792,7 @@ impl, const LIMBS: usize> core::ops::Add<&mut Self> } #[allow(unused_qualifications)] -impl, const LIMBS: usize> core::ops::Sub - for Fp -{ +impl, const N: usize> core::ops::Sub for Fp { type Output = Self; #[inline] @@ -575,9 +804,7 @@ impl, const LIMBS: usize> core::ops::Sub } #[allow(unused_qualifications)] -impl, const LIMBS: usize> core::ops::Sub<&mut Self> - for Fp -{ +impl, const N: usize> core::ops::Sub<&mut Self> for Fp { type Output = Self; #[inline] @@ -589,17 +816,15 @@ impl, const LIMBS: usize> core::ops::Sub<&mut Self> } #[allow(unused_qualifications)] -impl, const LIMBS: usize> core::iter::Sum - for Fp -{ +impl, const N: usize> core::iter::Sum for Fp { fn sum>(iter: I) -> Self { iter.fold(Self::zero(), core::ops::Add::add) } } #[allow(unused_qualifications)] -impl<'a, P: FpParams, const LIMBS: usize> core::iter::Sum<&'a Self> - for Fp +impl<'a, P: FpParams, const N: usize> core::iter::Sum<&'a Self> + for Fp { fn sum>(iter: I) -> Self { iter.fold(Self::zero(), core::ops::Add::add) @@ -607,9 +832,7 @@ impl<'a, P: FpParams, const LIMBS: usize> core::iter::Sum<&'a Self> } #[allow(unused_qualifications)] -impl, const LIMBS: usize> core::ops::AddAssign - for Fp -{ +impl, const N: usize> core::ops::AddAssign for Fp { #[inline] fn add_assign(&mut self, other: Self) { self.add_assign(&other); @@ -617,9 +840,7 @@ impl, const LIMBS: usize> core::ops::AddAssign } #[allow(unused_qualifications)] -impl, const LIMBS: usize> core::ops::SubAssign - for Fp -{ +impl, const N: usize> core::ops::SubAssign for Fp { #[inline] fn sub_assign(&mut self, other: Self) { self.sub_assign(&other); @@ -627,8 +848,8 @@ impl, const LIMBS: usize> core::ops::SubAssign } #[allow(unused_qualifications)] -impl, const LIMBS: usize> core::ops::AddAssign<&mut Self> - for Fp +impl, const N: usize> core::ops::AddAssign<&mut Self> + for Fp { #[inline] fn add_assign(&mut self, other: &mut Self) { @@ -637,8 +858,8 @@ impl, const LIMBS: usize> core::ops::AddAssign<&mut Self> } #[allow(unused_qualifications)] -impl, const LIMBS: usize> core::ops::SubAssign<&mut Self> - for Fp +impl, const N: usize> core::ops::SubAssign<&mut Self> + for Fp { #[inline] fn sub_assign(&mut self, other: &mut Self) { @@ -646,9 +867,7 @@ impl, const LIMBS: usize> core::ops::SubAssign<&mut Self> } } -impl, const LIMBS: usize> core::ops::MulAssign<&Self> - for Fp -{ +impl, const N: usize> core::ops::MulAssign<&Self> for Fp { fn mul_assign(&mut self, other: &Self) { P::mul_assign(self, other); } @@ -656,9 +875,7 @@ impl, const LIMBS: usize> core::ops::MulAssign<&Self> /// Computes `self *= other.inverse()` if `other.inverse()` is `Some`, and /// panics otherwise. -impl, const LIMBS: usize> core::ops::DivAssign<&Self> - for Fp -{ +impl, const N: usize> core::ops::DivAssign<&Self> for Fp { #[inline] fn div_assign(&mut self, other: &Self) { use core::ops::MulAssign; @@ -667,9 +884,7 @@ impl, const LIMBS: usize> core::ops::DivAssign<&Self> } #[allow(unused_qualifications)] -impl, const LIMBS: usize> core::ops::Mul - for Fp -{ +impl, const N: usize> core::ops::Mul for Fp { type Output = Self; #[inline] @@ -681,9 +896,7 @@ impl, const LIMBS: usize> core::ops::Mul } #[allow(unused_qualifications)] -impl, const LIMBS: usize> core::ops::Div - for Fp -{ +impl, const N: usize> core::ops::Div for Fp { type Output = Self; #[inline] @@ -695,9 +908,7 @@ impl, const LIMBS: usize> core::ops::Div } #[allow(unused_qualifications)] -impl, const LIMBS: usize> core::ops::Mul<&mut Self> - for Fp -{ +impl, const N: usize> core::ops::Mul<&mut Self> for Fp { type Output = Self; #[inline] @@ -709,9 +920,7 @@ impl, const LIMBS: usize> core::ops::Mul<&mut Self> } #[allow(unused_qualifications)] -impl, const LIMBS: usize> core::ops::Div<&mut Self> - for Fp -{ +impl, const N: usize> core::ops::Div<&mut Self> for Fp { type Output = Self; #[inline] @@ -723,17 +932,15 @@ impl, const LIMBS: usize> core::ops::Div<&mut Self> } #[allow(unused_qualifications)] -impl, const LIMBS: usize> core::iter::Product - for Fp -{ +impl, const N: usize> core::iter::Product for Fp { fn product>(iter: I) -> Self { iter.fold(Self::one(), core::ops::Mul::mul) } } #[allow(unused_qualifications)] -impl<'a, P: FpParams, const LIMBS: usize> core::iter::Product<&'a Self> - for Fp +impl<'a, P: FpParams, const N: usize> core::iter::Product<&'a Self> + for Fp { fn product>(iter: I) -> Self { iter.fold(Self::one(), core::ops::Mul::mul) @@ -741,9 +948,7 @@ impl<'a, P: FpParams, const LIMBS: usize> core::iter::Product<&'a Self> } #[allow(unused_qualifications)] -impl, const LIMBS: usize> core::ops::MulAssign - for Fp -{ +impl, const N: usize> core::ops::MulAssign for Fp { #[inline] fn mul_assign(&mut self, other: Self) { self.mul_assign(&other); @@ -751,8 +956,8 @@ impl, const LIMBS: usize> core::ops::MulAssign } #[allow(unused_qualifications)] -impl, const LIMBS: usize> core::ops::DivAssign<&mut Self> - for Fp +impl, const N: usize> core::ops::DivAssign<&mut Self> + for Fp { #[inline] fn div_assign(&mut self, other: &mut Self) { @@ -761,8 +966,8 @@ impl, const LIMBS: usize> core::ops::DivAssign<&mut Self> } #[allow(unused_qualifications)] -impl, const LIMBS: usize> core::ops::MulAssign<&mut Self> - for Fp +impl, const N: usize> core::ops::MulAssign<&mut Self> + for Fp { #[inline] fn mul_assign(&mut self, other: &mut Self) { @@ -771,16 +976,14 @@ impl, const LIMBS: usize> core::ops::MulAssign<&mut Self> } #[allow(unused_qualifications)] -impl, const LIMBS: usize> core::ops::DivAssign - for Fp -{ +impl, const N: usize> core::ops::DivAssign for Fp { #[inline] fn div_assign(&mut self, other: Self) { self.div_assign(&other); } } -impl, const LIMBS: usize> zeroize::Zeroize for Fp { +impl, const N: usize> zeroize::Zeroize for Fp { // The phantom data does not contain element-specific data // and thus does not need to be zeroized. fn zeroize(&mut self) { @@ -788,21 +991,17 @@ impl, const LIMBS: usize> zeroize::Zeroize for Fp { } } -impl, const LIMBS: usize> From> - for BigInt -{ +impl, const N: usize> From> for BigInt { #[inline] - fn from(fp: Fp) -> Self { + fn from(fp: Fp) -> Self { fp.into_bigint() } } -impl, const LIMBS: usize> From> - for Fp -{ +impl, const N: usize> From> for Fp { /// Converts `Self::BigInteger` into `Self` #[inline] - fn from(int: BigInt) -> Self { + fn from(int: BigInt) -> Self { Self::from_bigint(int) } } @@ -811,7 +1010,7 @@ impl, const LIMBS: usize> From> #[macro_export] macro_rules! fp_from_num { ($num:literal) => { - $crate::field::fp::Fp::new($crate::bigint::from_str_radix($num, 10)) + $crate::field::fp::Fp::new($crate::arithmetic::from_str_radix($num, 10)) }; } @@ -819,7 +1018,7 @@ macro_rules! fp_from_num { #[macro_export] macro_rules! fp_from_hex { ($num:literal) => {{ - $crate::field::fp::Fp::new($crate::bigint::from_str_hex($num)) + $crate::field::fp::Fp::new($crate::arithmetic::from_str_hex($num)) }}; } @@ -840,7 +1039,7 @@ mod tests { struct Fp64Param; impl FpParams for Fp64Param { const GENERATOR: Fp64 = fp_from_num!("3"); - const MODULUS: BigInt = from_num!("1000003"); // Prime number + const MODULUS: BigInt = from_num!("1000003"); // Prime number } const MODULUS: i128 = 1000003; // Prime number diff --git a/lib/crypto/src/field/mod.rs b/lib/crypto/src/field/mod.rs index a5ce76d3b..5fdf11618 100644 --- a/lib/crypto/src/field/mod.rs +++ b/lib/crypto/src/field/mod.rs @@ -8,7 +8,7 @@ //! ## Example //! ```rust //! use openzeppelin_crypto::{ -//! bigint::crypto_bigint::U64, +//! arithmetic::crypto_bigint::U64, //! field::{ //! fp::{Fp64, FpParams, LIMBS_64}, //! group::AdditiveGroup, diff --git a/lib/crypto/src/field/prime.rs b/lib/crypto/src/field/prime.rs index 379b6aa66..d920386e1 100644 --- a/lib/crypto/src/field/prime.rs +++ b/lib/crypto/src/field/prime.rs @@ -1,6 +1,6 @@ //! This module provides a generic interface for finite prime fields. -use crate::{bigint::BigInteger, field::Field}; +use crate::{arithmetic::BigInteger, field::Field}; /// Defines an abstract prime field. /// I.e., the field of integers of prime module [`Self::MODULUS`]. diff --git a/lib/crypto/src/lib.rs b/lib/crypto/src/lib.rs index 21ddd1f22..a05222898 100644 --- a/lib/crypto/src/lib.rs +++ b/lib/crypto/src/lib.rs @@ -23,7 +23,7 @@ Common cryptographic procedures for a blockchain environment. extern crate alloc; extern crate core; -pub mod bigint; +pub mod arithmetic; pub mod bits; #[macro_use] pub mod field; From 9cc5f494f86c5c054a380951c2abb3dd2746f520 Mon Sep 17 00:00:00 2001 From: Alisander Qoshqosh Date: Thu, 9 Jan 2025 23:24:43 +0400 Subject: [PATCH 03/61] ++ --- lib/crypto/src/arithmetic/mod.rs | 8 +++++--- lib/crypto/src/field/fp.rs | 27 +++++++++++++++++++++++---- 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/lib/crypto/src/arithmetic/mod.rs b/lib/crypto/src/arithmetic/mod.rs index aa4f600c1..77585179c 100644 --- a/lib/crypto/src/arithmetic/mod.rs +++ b/lib/crypto/src/arithmetic/mod.rs @@ -19,9 +19,11 @@ use zeroize::Zeroize; use crate::{adc, bits::BitIteratorBE, const_for, const_modulo, sbb}; pub type Word = u64; -// TODO#q: Have Fp as residue integers -// TODO#q: Uint - normal big integers -// TODO#q: Limbs - wrapper type for [Limb; N] array +// TODO#q: Refactor types to: +// Fp(Limbs) - residue classes modulo prime numbers +// Uint(Limbs) - normal big integers. Make sense to implement only +// constant operations necessary for hex parsing +// Wrapper type for limbs: Limbs([Limb;N]) #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Zeroize)] pub struct BigInt(pub [Word; N]); diff --git a/lib/crypto/src/field/fp.rs b/lib/crypto/src/field/fp.rs index 52fed8f90..a4c45396e 100644 --- a/lib/crypto/src/field/fp.rs +++ b/lib/crypto/src/field/fp.rs @@ -43,20 +43,21 @@ pub trait FpParams: Send + Sync + 'static + Sized { /// `MODULUS - 1`. const GENERATOR: Fp; - const CAN_USE_NO_CARRY_MUL_OPT: bool = unimplemented!(); + const CAN_USE_NO_CARRY_MUL_OPT: bool = + can_use_no_carry_mul_optimization::(); - const MODULUS_HAS_SPARE_BIT: bool = unimplemented!(); + const MODULUS_HAS_SPARE_BIT: bool = modulus_has_spare_bit::(); /// INV = -MODULUS^{-1} mod 2^64 const INV: u64 = inv::(); /// Let `M` be the power of 2^64 nearest to [`Self::MODULUS_BITS`]. Then /// `R = M % MODULUS`. - const R: BigInt = unimplemented!(); + const R: BigInt = Self::MODULUS.montgomery_r(); /// `R2 = R^2 % MODULUS` #[allow(dead_code)] - const R2: BigInt = unimplemented!(); + const R2: BigInt = Self::MODULUS.montgomery_r2(); /// Set `a += b`. fn add_assign(a: &mut Fp, b: &Fp) { @@ -347,6 +348,24 @@ pub const fn inv, const N: usize>() -> u64 { inv.wrapping_neg() } +#[inline] +pub const fn can_use_no_carry_mul_optimization< + T: FpParams, + const N: usize, +>() -> bool { + // Checking the modulus at compile time + let mut all_remaining_bits_are_one = T::MODULUS.0[N - 1] == u64::MAX >> 1; + crate::const_for!((i in 1..N) { + all_remaining_bits_are_one &= T::MODULUS.0[N - i - 1] == u64::MAX; + }); + modulus_has_spare_bit::() && !all_remaining_bits_are_one +} + +#[inline] +pub const fn modulus_has_spare_bit, const N: usize>() -> bool { + T::MODULUS.0[N - 1] >> 63 == 0 +} + /// Represents an element of the prime field `F_p`, where `p == P::MODULUS`. /// /// This type can represent elements in any field of size at most N * 64 bits From 69d753df9f37cfd9183be627f1c8f90dbc82710f Mon Sep 17 00:00:00 2001 From: Alisander Qoshqosh Date: Thu, 9 Jan 2025 23:24:57 +0400 Subject: [PATCH 04/61] ++ --- lib/crypto/src/const_helpers.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/crypto/src/const_helpers.rs b/lib/crypto/src/const_helpers.rs index 31f43dcb8..505b90095 100644 --- a/lib/crypto/src/const_helpers.rs +++ b/lib/crypto/src/const_helpers.rs @@ -30,7 +30,7 @@ macro_rules! adc { }}; } -// TODO#q: implement as a function +// TODO#q: implement const_module as a function #[macro_export] macro_rules! const_modulo { ($a:expr, $divisor:expr) => {{ From 63011f93cf103ed089172ea59f8f2143f8320e58 Mon Sep 17 00:00:00 2001 From: Alisander Qoshqosh Date: Thu, 9 Jan 2025 23:48:45 +0400 Subject: [PATCH 05/61] ++ --- lib/crypto/src/arithmetic/mod.rs | 35 ++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/lib/crypto/src/arithmetic/mod.rs b/lib/crypto/src/arithmetic/mod.rs index 77585179c..418f0b601 100644 --- a/lib/crypto/src/arithmetic/mod.rs +++ b/lib/crypto/src/arithmetic/mod.rs @@ -9,7 +9,7 @@ use core::{ }, }; -use num_traits::ConstZero; +use num_traits::{ConstZero, Zero}; use rand::{ distributions::{Distribution, Standard}, Rng, @@ -22,8 +22,9 @@ pub type Word = u64; // TODO#q: Refactor types to: // Fp(Limbs) - residue classes modulo prime numbers // Uint(Limbs) - normal big integers. Make sense to implement only -// constant operations necessary for hex parsing -// Wrapper type for limbs: Limbs([Limb;N]) +// constant operations necessary for hex parsing (uint.rs) +// Limbs([Limb;N]) - Wrapper type for limbs. (limbs.rs) +// Odd> - Odd numbers. (odd.rs) #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Zeroize)] pub struct BigInt(pub [Word; N]); @@ -204,6 +205,7 @@ impl BigInt { is_zero } + // TODO#q: Montgomery constant computation from rust-crypto /// Computes the Montgomery R constant modulo `self`. #[doc(hidden)] pub const fn montgomery_r(&self) -> Self { @@ -553,23 +555,38 @@ impl BigInteger for BigInt { const NUM_LIMBS: usize = N; fn is_odd(&self) -> bool { - unimplemented!() + self.0[0] & 1 == 1 } fn is_even(&self) -> bool { - unimplemented!() + !self.is_odd() } fn is_zero(&self) -> bool { - unimplemented!() + self.0.iter().all(Zero::is_zero) } fn num_bits(&self) -> usize { - unimplemented!() + let mut ret = N as u32 * 64; + for i in self.0.iter().rev() { + let leading = i.leading_zeros(); + ret -= leading; + if leading != 64 { + break; + } + } + + ret as usize } fn get_bit(&self, i: usize) -> bool { - unimplemented!() + if i >= 64 * N { + false + } else { + let limb = i / 64; + let bit = i - (64 * limb); + (self.0[limb] & (1 << bit)) != 0 + } } fn from_bytes_le(bytes: &[u8]) -> Self { @@ -577,7 +594,7 @@ impl BigInteger for BigInt { } fn into_bytes_le(self) -> alloc::vec::Vec { - unimplemented!() + self.0.iter().flat_map(|&limb| limb.to_le_bytes()).collect() } } From 9b82dd5e06d5b2fb921f6a7e04e54a01c12d40cf Mon Sep 17 00:00:00 2001 From: Alisander Qoshqosh Date: Thu, 9 Jan 2025 23:56:20 +0400 Subject: [PATCH 06/61] ++ --- lib/crypto/src/field/instance.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/crypto/src/field/instance.rs b/lib/crypto/src/field/instance.rs index afada1460..71ea2031c 100644 --- a/lib/crypto/src/field/instance.rs +++ b/lib/crypto/src/field/instance.rs @@ -1,9 +1,9 @@ //! This module contains the field instances for some popular curves. #![allow(missing_docs)] -use crypto_bigint::{U256, U64}; use crate::{ + arithmetic::BigInt, field::fp::{Fp256, Fp64, FpParams, LIMBS_256, LIMBS_64}, fp_from_num, from_num, }; @@ -12,40 +12,40 @@ pub type FpVesta = Fp256; pub struct VestaParam; impl FpParams for VestaParam { const GENERATOR: Fp256 = fp_from_num!("5"); - const MODULUS: U256 = from_num!("28948022309329048855892746252171976963363056481941647379679742748393362948097"); + const MODULUS: BigInt = from_num!("28948022309329048855892746252171976963363056481941647379679742748393362948097"); } pub type FpBabyBear = Fp64; pub struct BabyBearParam; impl FpParams for BabyBearParam { const GENERATOR: Fp64 = fp_from_num!("31"); - const MODULUS: U64 = from_num!("2013265921"); + const MODULUS: BigInt = from_num!("2013265921"); } pub type FpBLS12 = Fp256; pub struct BLS12Param; impl FpParams for BLS12Param { const GENERATOR: Fp256 = fp_from_num!("7"); - const MODULUS: U256 = from_num!("52435875175126190479447740508185965837690552500527637822603658699938581184513"); + const MODULUS: BigInt = from_num!("52435875175126190479447740508185965837690552500527637822603658699938581184513"); } pub type FpBN256 = Fp256; pub struct BN256Param; impl FpParams for BN256Param { const GENERATOR: Fp256 = fp_from_num!("7"); - const MODULUS: U256 = from_num!("21888242871839275222246405745257275088548364400416034343698204186575808495617"); + const MODULUS: BigInt = from_num!("21888242871839275222246405745257275088548364400416034343698204186575808495617"); } pub type FpGoldiLocks = Fp64; pub struct GoldiLocksParam; impl FpParams for GoldiLocksParam { const GENERATOR: Fp64 = fp_from_num!("7"); - const MODULUS: U64 = from_num!("18446744069414584321"); + const MODULUS: BigInt = from_num!("18446744069414584321"); } pub type FpPallas = Fp256; pub struct PallasParam; impl FpParams for PallasParam { const GENERATOR: Fp256 = fp_from_num!("5"); - const MODULUS: U256 = from_num!("28948022309329048855892746252171976963363056481941560715954676764349967630337"); + const MODULUS: BigInt = from_num!("28948022309329048855892746252171976963363056481941560715954676764349967630337"); } From 8df14c04e5b2d9188e3af8d55f581b2d7a1019bc Mon Sep 17 00:00:00 2001 From: Alisander Qoshqosh Date: Fri, 10 Jan 2025 00:25:21 +0400 Subject: [PATCH 07/61] ++ --- Cargo.lock | 1 + Cargo.toml | 1 + lib/crypto/Cargo.toml | 1 + lib/crypto/src/arithmetic/mod.rs | 212 +++++++++++++++++++++++++++++-- lib/crypto/src/field/fp.rs | 15 ++- 5 files changed, 219 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5d4167b49..8c14cf7d9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2736,6 +2736,7 @@ version = "0.2.0-alpha.2" dependencies = [ "educe", "hex-literal", + "num-bigint", "num-traits", "proptest", "rand", diff --git a/Cargo.toml b/Cargo.toml index d92466585..734204ff8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -112,6 +112,7 @@ zeroize = { version = "1.8.1", features = ["derive"] } proptest = "1" educe = "0.6.0" hex-literal = "0.4.1" +num-bigint = { version = "0.4", default-features = false } # procedural macros syn = { version = "2.0.58", features = ["full"] } diff --git a/lib/crypto/Cargo.toml b/lib/crypto/Cargo.toml index 4c6922e50..9163c0f06 100644 --- a/lib/crypto/Cargo.toml +++ b/lib/crypto/Cargo.toml @@ -14,6 +14,7 @@ num-traits.workspace = true zeroize.workspace = true educe.workspace = true hex-literal.workspace = true +num-bigint.workspace = true [dev-dependencies] rand.workspace = true diff --git a/lib/crypto/src/arithmetic/mod.rs b/lib/crypto/src/arithmetic/mod.rs index 418f0b601..25c166ea3 100644 --- a/lib/crypto/src/arithmetic/mod.rs +++ b/lib/crypto/src/arithmetic/mod.rs @@ -2,13 +2,16 @@ //! functions for big integers. use core::{ - fmt::{Debug, Display}, + borrow::Borrow, + fmt::{Debug, Display, UpperHex}, ops::{ BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Shl, ShlAssign, Shr, ShrAssign, }, }; +use std::ops::Not; +use num_bigint::BigUint; use num_traits::{ConstZero, Zero}; use rand::{ distributions::{Distribution, Standard}, @@ -18,15 +21,15 @@ use zeroize::Zeroize; use crate::{adc, bits::BitIteratorBE, const_for, const_modulo, sbb}; -pub type Word = u64; +pub type Limb = u64; // TODO#q: Refactor types to: // Fp(Limbs) - residue classes modulo prime numbers // Uint(Limbs) - normal big integers. Make sense to implement only // constant operations necessary for hex parsing (uint.rs) // Limbs([Limb;N]) - Wrapper type for limbs. (limbs.rs) // Odd> - Odd numbers. (odd.rs) -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Zeroize)] -pub struct BigInt(pub [Word; N]); +#[derive(Copy, Clone, PartialEq, Eq, Hash, Zeroize)] +pub struct BigInt(pub [Limb; N]); impl Default for BigInt { fn default() -> Self { @@ -347,6 +350,24 @@ pub fn adc_for_add_with_carry(a: &mut u64, b: u64, carry: u8) -> u8 { // ----------- Traits Impls ----------- +impl UpperHex for BigInt { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:016X}", BigUint::from(*self)) + } +} + +impl Debug for BigInt { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:?}", BigUint::from(*self)) + } +} + +impl Display for BigInt { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{}", BigUint::from(*self)) + } +} + impl Ord for BigInt { #[inline] fn cmp(&self, other: &Self) -> core::cmp::Ordering { @@ -436,6 +457,177 @@ impl From for BigInt { } } +impl From> for BigUint { + #[inline] + fn from(val: BigInt) -> num_bigint::BigUint { + BigUint::from_bytes_le(&val.into_bytes_le()) + } +} + +impl From> for num_bigint::BigInt { + #[inline] + fn from(val: BigInt) -> num_bigint::BigInt { + use num_bigint::Sign; + let sign = if val.is_zero() { Sign::NoSign } else { Sign::Plus }; + num_bigint::BigInt::from_bytes_le(sign, &val.into_bytes_le()) + } +} + +impl, const N: usize> BitXorAssign for BigInt { + fn bitxor_assign(&mut self, rhs: B) { + (0..N).for_each(|i| self.0[i] ^= rhs.borrow().0[i]) + } +} + +impl, const N: usize> BitXor for BigInt { + type Output = Self; + + fn bitxor(mut self, rhs: B) -> Self::Output { + self ^= rhs; + self + } +} + +impl, const N: usize> BitAndAssign for BigInt { + fn bitand_assign(&mut self, rhs: B) { + (0..N).for_each(|i| self.0[i] &= rhs.borrow().0[i]) + } +} + +impl, const N: usize> BitAnd for BigInt { + type Output = Self; + + fn bitand(mut self, rhs: B) -> Self::Output { + self &= rhs; + self + } +} + +impl, const N: usize> BitOrAssign for BigInt { + fn bitor_assign(&mut self, rhs: B) { + (0..N).for_each(|i| self.0[i] |= rhs.borrow().0[i]) + } +} + +impl, const N: usize> BitOr for BigInt { + type Output = Self; + + fn bitor(mut self, rhs: B) -> Self::Output { + self |= rhs; + self + } +} + +impl ShrAssign for BigInt { + /// Computes the bitwise shift right operation in place. + /// + /// Differently from the built-in numeric types (u8, u32, u64, etc.) this + /// operation does *not* return an underflow error if the number of bits + /// shifted is larger than N * 64. Instead the result will be saturated to + /// zero. + fn shr_assign(&mut self, mut rhs: u32) { + if rhs >= (64 * N) as u32 { + *self = Self::from(0u64); + return; + } + + while rhs >= 64 { + let mut t = 0; + for limb in self.0.iter_mut().rev() { + core::mem::swap(&mut t, limb); + } + rhs -= 64; + } + + if rhs > 0 { + let mut t = 0; + for a in self.0.iter_mut().rev() { + let t2 = *a << (64 - rhs); + *a >>= rhs; + *a |= t; + t = t2; + } + } + } +} + +impl Shr for BigInt { + type Output = Self; + + /// Computes bitwise shift right operation. + /// + /// Differently from the built-in numeric types (u8, u32, u64, etc.) this + /// operation does *not* return an underflow error if the number of bits + /// shifted is larger than N * 64. Instead the result will be saturated to + /// zero. + fn shr(mut self, rhs: u32) -> Self::Output { + self >>= rhs; + self + } +} + +impl ShlAssign for BigInt { + /// Computes the bitwise shift left operation in place. + /// + /// Differently from the built-in numeric types (u8, u32, u64, etc.) this + /// operation does *not* return an overflow error if the number of bits + /// shifted is larger than N * 64. Instead, the overflow will be chopped + /// off. + fn shl_assign(&mut self, mut rhs: u32) { + if rhs >= (64 * N) as u32 { + *self = Self::from(0u64); + return; + } + + while rhs >= 64 { + let mut t = 0; + for i in 0..N { + core::mem::swap(&mut t, &mut self.0[i]); + } + rhs -= 64; + } + + if rhs > 0 { + let mut t = 0; + #[allow(unused)] + for i in 0..N { + let a = &mut self.0[i]; + let t2 = *a >> (64 - rhs); + *a <<= rhs; + *a |= t; + t = t2; + } + } + } +} + +impl Shl for BigInt { + type Output = Self; + + /// Computes the bitwise shift left operation in place. + /// + /// Differently from the built-in numeric types (u8, u32, u64, etc.) this + /// operation does *not* return an overflow error if the number of bits + /// shifted is larger than N * 64. Instead, the overflow will be chopped + /// off. + fn shl(mut self, rhs: u32) -> Self::Output { + self <<= rhs; + self + } +} + +impl Not for BigInt { + type Output = Self; + + fn not(self) -> Self::Output { + let mut result = Self::zero(); + for i in 0..N { + result.0[i] = !self.0[i]; + } + result + } +} + /// Defines a big integer with a constant length. pub trait BigInteger: 'static @@ -600,7 +792,7 @@ impl BigInteger for BigInt { impl BitIteratorBE for BigInt { fn bit_be_iter(&self) -> impl Iterator { - self.as_words().iter().rev().flat_map(Word::bit_be_iter) + self.as_words().iter().rev().flat_map(Limb::bit_be_iter) } } @@ -663,15 +855,15 @@ pub const fn from_str_hex(s: &str) -> BigInt { // The lowest order limb is at the beginning of the `num` array. // Begin indexing from `0`. - let mut num = [Word::ZERO; LIMBS]; + let mut num = [Limb::ZERO; LIMBS]; let mut num_index = 0; let digit_radix = 16; let digit_size = 4; // Size of a hex digit in bits (2^4 = 16). - let digits_in_limb = Word::BITS / digit_size; + let digits_in_limb = Limb::BITS / digit_size; loop { - let digit = parse_digit(bytes[index], digit_radix) as Word; + let digit = parse_digit(bytes[index], digit_radix) as Limb; // Since a base-16 digit can be represented with the same bits, we can // copy these bits. @@ -706,7 +898,7 @@ const fn add( a: &BigInt, b: &BigInt, ) -> BigInt { - let (low, carry) = a.adc(b, Word::ZERO); + let (low, carry) = a.adc(b, Limb::ZERO); assert!(carry.0 == 0, "overflow on addition"); low } @@ -788,7 +980,7 @@ mod test { #[test] fn uint_bit_iterator_be() { - let words: [Word; 4] = [0b1100, 0, 0, 0]; + let words: [Limb; 4] = [0b1100, 0, 0, 0]; let num = BigInt::<4>::new(words); let bits: Vec = num.bit_be_trimmed_iter().collect(); diff --git a/lib/crypto/src/field/fp.rs b/lib/crypto/src/field/fp.rs index a4c45396e..66ac9ad27 100644 --- a/lib/crypto/src/field/fp.rs +++ b/lib/crypto/src/field/fp.rs @@ -26,7 +26,7 @@ use num_traits::{One, Zero}; use crate::{ arithmetic, - arithmetic::{BigInt, BigInteger, Word}, + arithmetic::{BigInt, BigInteger, Limb}, field::{group::AdditiveGroup, prime::PrimeField, Field}, }; @@ -446,12 +446,25 @@ impl, const N: usize> Fp { unimplemented!() } + #[doc(hidden)] + #[inline] + pub fn is_geq_modulus(&self) -> bool { + self.0 >= P::MODULUS + } + #[inline] fn subtract_modulus(&mut self) { if self.is_geq_modulus() { self.0.sub_with_borrow(&Self::MODULUS); } } + + #[inline] + fn subtract_modulus_with_carry(&mut self, carry: bool) { + if carry || self.is_geq_modulus() { + self.0.sub_with_borrow(&Self::MODULUS); + } + } } impl, const N: usize> Hash for Fp { From b5dff65a15110ac01e09700d1b2949ba254316c0 Mon Sep 17 00:00:00 2001 From: Alisander Qoshqosh Date: Fri, 10 Jan 2025 01:11:17 +0400 Subject: [PATCH 08/61] ++ --- lib/crypto/src/arithmetic/mod.rs | 7 ++++- lib/crypto/src/const_helpers.rs | 20 ++++++++++++ lib/crypto/src/field/fp.rs | 54 +++++++++++++++++++++++++++----- lib/crypto/src/field/mod.rs | 4 +-- 4 files changed, 75 insertions(+), 10 deletions(-) diff --git a/lib/crypto/src/arithmetic/mod.rs b/lib/crypto/src/arithmetic/mod.rs index 25c166ea3..0faf27139 100644 --- a/lib/crypto/src/arithmetic/mod.rs +++ b/lib/crypto/src/arithmetic/mod.rs @@ -42,6 +42,10 @@ impl BigInt { Self(value) } + pub const fn as_limbs(&self) -> &[Limb; N] { + &self.0 + } + pub const fn zero() -> Self { Self([0u64; N]) } @@ -457,6 +461,7 @@ impl From for BigInt { } } +// TODO#q: remove num_bigint::BigUint conversion impl From> for BigUint { #[inline] fn from(val: BigInt) -> num_bigint::BigUint { @@ -792,7 +797,7 @@ impl BigInteger for BigInt { impl BitIteratorBE for BigInt { fn bit_be_iter(&self) -> impl Iterator { - self.as_words().iter().rev().flat_map(Limb::bit_be_iter) + self.as_limbs().iter().rev().flat_map(Limb::bit_be_iter) } } diff --git a/lib/crypto/src/const_helpers.rs b/lib/crypto/src/const_helpers.rs index 505b90095..49a8adb2e 100644 --- a/lib/crypto/src/const_helpers.rs +++ b/lib/crypto/src/const_helpers.rs @@ -54,6 +54,26 @@ macro_rules! const_modulo { }}; } +#[macro_export] +macro_rules! mac_with_carry { + ($a:expr, $b:expr, $c:expr, &mut $carry:expr $(,)?) => {{ + let tmp = ($a as u128) + + $crate::arithmetic::widening_mul($b, $c) + + ($carry as u128); + $carry = (tmp >> 64) as u64; + tmp as u64 + }}; +} + +#[macro_export] +macro_rules! mac { + ($a:expr, $b:expr, $c:expr, &mut $carry:expr $(,)?) => {{ + let tmp = ($a as u128) + $crate::arithmetic::widening_mul($b, $c); + $carry = (tmp >> 64) as u64; + tmp as u64 + }}; +} + pub(super) struct RBuffer(pub [u64; N], pub u64); impl RBuffer { diff --git a/lib/crypto/src/field/fp.rs b/lib/crypto/src/field/fp.rs index 66ac9ad27..68174b93b 100644 --- a/lib/crypto/src/field/fp.rs +++ b/lib/crypto/src/field/fp.rs @@ -25,9 +25,10 @@ use educe::Educe; use num_traits::{One, Zero}; use crate::{ - arithmetic, + adc, arithmetic, arithmetic::{BigInt, BigInteger, Limb}, field::{group::AdditiveGroup, prime::PrimeField, Field}, + mac, mac_with_carry, }; /// A trait that specifies the configuration of a prime field. @@ -386,16 +387,16 @@ macro_rules! declare_fp { #[doc = "Finite field with max"] #[doc = stringify!($bits)] #[doc = "bits size element."] - pub type $fp

= crate::field::fp::Fp< + pub type $fp

= $crate::field::fp::Fp< P, - { usize::div_ceil($bits, ::crate::bigint::Word::BITS as usize) }, + { usize::div_ceil($bits, $crate::arithmetic::Limb::BITS as usize) }, >; #[doc = "Number of limbs in the field with"] #[doc = stringify!($bits)] #[doc = "bits size element."] pub const $limbs: usize = - usize::div_ceil($bits, ::crate::bigint::Word::BITS as usize); + usize::div_ceil($bits, $crate::arithmetic::Limb::BITS as usize); }; } @@ -465,6 +466,43 @@ impl, const N: usize> Fp { self.0.sub_with_borrow(&Self::MODULUS); } } + + const fn mul_without_cond_subtract(mut self, other: &Self) -> (bool, Self) { + let (mut lo, mut hi) = ([0u64; N], [0u64; N]); + crate::const_for!((i in 0..N) { + let mut carry = 0; + crate::const_for!((j in 0..N) { + let k = i + j; + if k >= N { + hi[k - N] = mac_with_carry!(hi[k - N], (self.0).0[i], (other.0).0[j], &mut carry); + } else { + lo[k] = mac_with_carry!(lo[k], (self.0).0[i], (other.0).0[j], &mut carry); + } + }); + hi[i] = carry; + }); + // Montgomery reduction + let mut carry2 = 0; + crate::const_for!((i in 0..N) { + let tmp = lo[i].wrapping_mul(P::INV); + let mut carry; + mac!(lo[i], tmp, P::MODULUS.0[0], &mut carry); + crate::const_for!((j in 1..N) { + let k = i + j; + if k >= N { + hi[k - N] = mac_with_carry!(hi[k - N], tmp, P::MODULUS.0[j], &mut carry); + } else { + lo[k] = mac_with_carry!(lo[k], tmp, P::MODULUS.0[j], &mut carry); + } + }); + hi[i] = adc!(hi[i], carry, &mut carry2); + }); + + crate::const_for!((i in 0..N) { + (self.0).0[i] = hi[i]; + }); + (carry2 != 0, self) + } } impl, const N: usize> Hash for Fp { @@ -615,13 +653,15 @@ macro_rules! impl_fp_from_signed_int { }; } -impl_fp_from_unsigned_int!(u128); +// TODO#q: add u128 conversion + +// impl_fp_from_unsigned_int!(u128); impl_fp_from_unsigned_int!(u64); impl_fp_from_unsigned_int!(u32); impl_fp_from_unsigned_int!(u16); impl_fp_from_unsigned_int!(u8); -impl_fp_from_signed_int!(i128); +// impl_fp_from_signed_int!(i128); impl_fp_from_signed_int!(i64); impl_fp_from_signed_int!(i32); impl_fp_from_signed_int!(i16); @@ -642,7 +682,7 @@ macro_rules! impl_int_from_fp { impl> From> for $int { fn from(other: Fp) -> Self { let uint = other.into_bigint(); - let words = uint.as_words(); + let words = uint.as_limbs(); <$int>::try_from(words[0]).unwrap_or_else(|_| { panic!("should convert to {}", stringify!($int)) }) diff --git a/lib/crypto/src/field/mod.rs b/lib/crypto/src/field/mod.rs index 5fdf11618..2deb07ce4 100644 --- a/lib/crypto/src/field/mod.rs +++ b/lib/crypto/src/field/mod.rs @@ -84,12 +84,12 @@ pub trait Field: + for<'a> Div<&'a mut Self, Output = Self> + for<'a> DivAssign<&'a mut Self> + for<'a> Product<&'a Self> - + From + // + From // TODO#q: add u128 conversion + From + From + From + From - + From + // + From + From + From + From From 5c988ca12495269079d63011f495f2eb95c6afcb Mon Sep 17 00:00:00 2001 From: Alisander Qoshqosh Date: Fri, 10 Jan 2025 01:37:28 +0400 Subject: [PATCH 09/61] ++ --- lib/crypto/src/arithmetic/mod.rs | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/lib/crypto/src/arithmetic/mod.rs b/lib/crypto/src/arithmetic/mod.rs index 0faf27139..ba9b42a2a 100644 --- a/lib/crypto/src/arithmetic/mod.rs +++ b/lib/crypto/src/arithmetic/mod.rs @@ -56,6 +56,13 @@ impl BigInt { one } + // TODO#q: add another conversions from u8, u16 and so on + pub const fn from_u32(val: u32) -> Self { + let mut repr = Self::zero(); + repr.0[0] = val as u64; + repr + } + #[doc(hidden)] pub const fn const_is_even(&self) -> bool { self.0[0] % 2 == 0 @@ -663,16 +670,16 @@ pub trait BigInteger: + for<'a> BitOrAssign<&'a Self> + BitOr + for<'a> BitOr<&'a Self, Output = Self> - + Shr - + ShrAssign - + Shl - + ShlAssign + + Shr + + ShrAssign + + Shl + + ShlAssign { /// Number of `usize` limbs representing `Self`. const NUM_LIMBS: usize; /// Number of bytes in the integer. - const BYTES: usize = Self::NUM_LIMBS * Limb::BYTES; + const BYTES: usize = Self::NUM_LIMBS * Limb::BITS as usize / 8; /// Returns true if this number is odd. /// # Example @@ -873,7 +880,7 @@ pub const fn from_str_hex(s: &str) -> BigInt { // Since a base-16 digit can be represented with the same bits, we can // copy these bits. let digit_mask = digit << ((num_index % digits_in_limb) * digit_size); - num[num_index / digits_in_limb] |= digit_mask; + num[(num_index / digits_in_limb) as usize] |= digit_mask; // If we reached the beginning of the string, return the number. if index == 0 { @@ -886,6 +893,8 @@ pub const fn from_str_hex(s: &str) -> BigInt { } } +// TODO#q: move mul / add operations to BigInt impl + /// Multiply two numbers and panic on overflow. #[must_use] const fn mul( From f68e8e151ed6f95eb2f0fb1c23309c5ba80cc202 Mon Sep 17 00:00:00 2001 From: Alisander Qoshqosh Date: Fri, 10 Jan 2025 02:03:16 +0400 Subject: [PATCH 10/61] ++ --- lib/crypto/src/arithmetic/mod.rs | 2 +- lib/crypto/src/field/fp.rs | 14 +++++++++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/lib/crypto/src/arithmetic/mod.rs b/lib/crypto/src/arithmetic/mod.rs index ba9b42a2a..44474f8c1 100644 --- a/lib/crypto/src/arithmetic/mod.rs +++ b/lib/crypto/src/arithmetic/mod.rs @@ -670,7 +670,7 @@ pub trait BigInteger: + for<'a> BitOrAssign<&'a Self> + BitOr + for<'a> BitOr<&'a Self, Output = Self> - + Shr + + Shr // TODO#q: use usize instead of u32 + ShrAssign + Shl + ShlAssign diff --git a/lib/crypto/src/field/fp.rs b/lib/crypto/src/field/fp.rs index 68174b93b..32c74f590 100644 --- a/lib/crypto/src/field/fp.rs +++ b/lib/crypto/src/field/fp.rs @@ -426,14 +426,22 @@ impl, const N: usize> Fp { pub const ONE: Fp = Fp::new_unchecked(P::R); /// Additive identity of the field, i.e., the element `e` /// such that, for all elements `f` of the field, `e + f = f`. - pub const ZERO: Fp = unimplemented!(); + pub const ZERO: Fp = Fp::new_unchecked(BigInt([0; N])); /// Construct a new field element from [`Uint`] and convert it in /// Montgomery form. #[inline] #[must_use] pub const fn new(element: BigInt) -> Self { - unimplemented!() + let mut r = Fp::new_unchecked(element); + if r.is_zero() { + r + } else if r.is_geq_modulus() { + panic!("element is not in the field"); + } else { + r *= &Fp::new_unchecked(P::R2); + r + } } /// Construct a new field element from [`Uint`]. @@ -444,7 +452,7 @@ impl, const N: usize> Fp { #[inline] #[must_use] pub const fn new_unchecked(element: BigInt) -> Self { - unimplemented!() + Self(element, PhantomData) } #[doc(hidden)] From 0d9e3be243a34300de89378b7af038c923594989 Mon Sep 17 00:00:00 2001 From: Alisander Qoshqosh Date: Fri, 10 Jan 2025 03:04:41 +0400 Subject: [PATCH 11/61] ++ --- lib/crypto/src/arithmetic/mod.rs | 116 ++++++++++++++++--------------- lib/crypto/src/field/fp.rs | 112 ++++++++++++++++++++++------- 2 files changed, 149 insertions(+), 79 deletions(-) diff --git a/lib/crypto/src/arithmetic/mod.rs b/lib/crypto/src/arithmetic/mod.rs index 44474f8c1..80dc0e235 100644 --- a/lib/crypto/src/arithmetic/mod.rs +++ b/lib/crypto/src/arithmetic/mod.rs @@ -324,18 +324,9 @@ pub fn mac_with_carry(a: u64, b: u64, c: u64, carry: &mut u64) -> u64 { #[allow(unused_mut)] #[doc(hidden)] pub fn sbb_for_sub_with_borrow(a: &mut u64, b: u64, borrow: u8) -> u8 { - #[cfg(all(target_arch = "x86_64", feature = "asm"))] - #[allow(unsafe_code)] - unsafe { - use core::arch::x86_64::_subborrow_u64; - _subborrow_u64(borrow, *a, b, a) - } - #[cfg(not(all(target_arch = "x86_64", feature = "asm")))] - { - let tmp = (1u128 << 64) + (*a as u128) - (b as u128) - (borrow as u128); - *a = tmp as u64; - u8::from(tmp >> 64 == 0) - } + let tmp = (1u128 << 64) + (*a as u128) - (b as u128) - (borrow as u128); + *a = tmp as u64; + u8::from(tmp >> 64 == 0) } // TODO#q: adc can be unified with adc_for_add_with_carry @@ -642,38 +633,38 @@ impl Not for BigInt { /// Defines a big integer with a constant length. pub trait BigInteger: - 'static - + Copy - + Clone - + Debug - + Default - + Display - + Eq - + Ord - + Send - + Sized - + Sync - + Zeroize - + From - + From - + From - + From - + BitXorAssign - + for<'a> BitXorAssign<&'a Self> - + BitXor - + for<'a> BitXor<&'a Self, Output = Self> - + BitAndAssign - + for<'a> BitAndAssign<&'a Self> - + BitAnd - + for<'a> BitAnd<&'a Self, Output = Self> - + BitOrAssign - + for<'a> BitOrAssign<&'a Self> - + BitOr - + for<'a> BitOr<&'a Self, Output = Self> - + Shr // TODO#q: use usize instead of u32 - + ShrAssign - + Shl - + ShlAssign +'static ++ Copy ++ Clone ++ Debug ++ Default ++ Display ++ Eq ++ Ord ++ Send ++ Sized ++ Sync ++ Zeroize ++ From ++ From ++ From ++ From ++ BitXorAssign ++ for<'a> BitXorAssign<&'a Self> ++ BitXor ++ for<'a> BitXor<&'a Self, Output=Self> ++ BitAndAssign ++ for<'a> BitAndAssign<&'a Self> ++ BitAnd ++ for<'a> BitAnd<&'a Self, Output=Self> ++ BitOrAssign ++ for<'a> BitOrAssign<&'a Self> ++ BitOr ++ for<'a> BitOr<&'a Self, Output=Self> ++ Shr // TODO#q: use usize instead of u32 ++ ShrAssign ++ Shl ++ ShlAssign { /// Number of `usize` limbs representing `Self`. const NUM_LIMBS: usize; @@ -834,7 +825,7 @@ pub const fn from_str_radix( let digit = BigInt::from_u32(parse_digit(bytes[index], radix)); // Add a digit multiplied by order. - uint = add(&uint, &mul(&digit, &order)); + uint = ct_add(&uint, &ct_mul(&digit, &order)); // If we reached the beginning of the string, return the number. if index == 0 { @@ -842,7 +833,7 @@ pub const fn from_str_radix( } // Increase the order of magnitude. - order = mul(&uint_radix, &order); + order = ct_mul(&uint_radix, &order); // Move to the next digit. index -= 1; @@ -897,10 +888,7 @@ pub const fn from_str_hex(s: &str) -> BigInt { /// Multiply two numbers and panic on overflow. #[must_use] -const fn mul( - a: &BigInt, - b: &BigInt, -) -> BigInt { +pub const fn ct_mul(a: &BigInt, b: &BigInt) -> BigInt { let (low, high) = a.mul_wide(b); assert!(high.bits() == 0, "overflow on multiplication"); low @@ -908,15 +896,33 @@ const fn mul( /// Add two numbers and panic on overflow. #[must_use] -const fn add( - a: &BigInt, - b: &BigInt, -) -> BigInt { +pub const fn ct_add(a: &BigInt, b: &BigInt) -> BigInt { let (low, carry) = a.adc(b, Limb::ZERO); assert!(carry.0 == 0, "overflow on addition"); low } +pub const fn ct_ge(a: &BigInt, b: &BigInt) -> bool { + const_for!((i in 0..N) { + if a.0[i] < b.0[i] { + return false; + } else if a.0[i] > b.0[i] { + return true; + } + }); + true +} + +// TODO#q: compare with const_is_zero +pub const fn ct_eq(a: &BigInt, b: &BigInt) -> bool { + const_for!((i in 0..N) { + if a.0[i] != b.0[i] { + return false; + } + }); + true +} + // Try to parse a digit from utf-8 byte. const fn parse_digit(utf8_digit: u8, digit_radix: u32) -> u32 { let ch = parse_utf8_byte(utf8_digit); @@ -962,7 +968,7 @@ mod test { fn convert_from_str_radix() { let uint_from_base10: BigInt<4> = from_str_radix( "28948022309329048855892746252171976963363056481941647379679742748393362948097", - 10 + 10, ); #[allow(clippy::unreadable_literal)] let expected = BigInt::<4>::new([ diff --git a/lib/crypto/src/field/fp.rs b/lib/crypto/src/field/fp.rs index 32c74f590..6da78da4a 100644 --- a/lib/crypto/src/field/fp.rs +++ b/lib/crypto/src/field/fp.rs @@ -20,13 +20,15 @@ use core::{ hash::{Hash, Hasher}, marker::PhantomData, }; +use std::ops::Mul; use educe::Educe; use num_traits::{One, Zero}; use crate::{ adc, arithmetic, - arithmetic::{BigInt, BigInteger, Limb}, + arithmetic::{ct_eq, ct_ge, BigInt, BigInteger, Limb}, + const_for, field::{group::AdditiveGroup, prime::PrimeField, Field}, mac, mac_with_carry, }; @@ -340,7 +342,7 @@ pub const fn inv, const N: usize>() -> u64 { // euler_totient(2^64) - 1 = (1 << 63) - 1 = 1111111... (63 digits). // We compute this powering via standard square and multiply. let mut inv = 1u64; - crate::const_for!((_i in 0..63) { + const_for!((_i in 0..63) { // Square inv = inv.wrapping_mul(inv); // Multiply @@ -356,7 +358,7 @@ pub const fn can_use_no_carry_mul_optimization< >() -> bool { // Checking the modulus at compile time let mut all_remaining_bits_are_one = T::MODULUS.0[N - 1] == u64::MAX >> 1; - crate::const_for!((i in 1..N) { + const_for!((i in 1..N) { all_remaining_bits_are_one &= T::MODULUS.0[N - i - 1] == u64::MAX; }); modulus_has_spare_bit::() && !all_remaining_bits_are_one @@ -428,22 +430,6 @@ impl, const N: usize> Fp { /// such that, for all elements `f` of the field, `e + f = f`. pub const ZERO: Fp = Fp::new_unchecked(BigInt([0; N])); - /// Construct a new field element from [`Uint`] and convert it in - /// Montgomery form. - #[inline] - #[must_use] - pub const fn new(element: BigInt) -> Self { - let mut r = Fp::new_unchecked(element); - if r.is_zero() { - r - } else if r.is_geq_modulus() { - panic!("element is not in the field"); - } else { - r *= &Fp::new_unchecked(P::R2); - r - } - } - /// Construct a new field element from [`Uint`]. /// /// Unlike [`Self::new`], this method does not perform Montgomery reduction. @@ -468,6 +454,53 @@ impl, const N: usize> Fp { } } + // TODO#q: think about using it + /*/// Interpret a set of limbs (along with a sign) as a field element. + /// For *internal* use only; please use the `ark_ff::MontFp` macro instead + /// of this method + #[doc(hidden)] + pub const fn from_sign_and_limbs(is_positive: bool, limbs: &[u64]) -> Self { + let mut repr = BigInt([0; N]); + assert!(limbs.len() <= N); + crate::const_for!((i in 0..(limbs.len())) { + repr.0[i] = limbs[i]; + }); + let res = Self::new(repr); + if is_positive { + res + } else { + res.const_neg() + } + }*/ + + // TODO#q: rename all const_* methods to ct_* + + /// Construct a new field element from its underlying + /// [`struct@BigInt`] data type. + #[inline] + pub const fn new(element: BigInt) -> Self { + let mut r = Self(element, PhantomData); + if r.const_is_zero() { + r + } else { + r = r.const_mul(&Fp(P::R2, PhantomData)); + r + } + } + + const fn const_mul(self, other: &Self) -> Self { + let (carry, res) = self.mul_without_cond_subtract(other); + if P::MODULUS_HAS_SPARE_BIT { + res.const_subtract_modulus() + } else { + res.const_subtract_modulus_with_carry(carry) + } + } + + const fn const_is_zero(&self) -> bool { + self.0.const_is_zero() + } + #[inline] fn subtract_modulus_with_carry(&mut self, carry: bool) { if carry || self.is_geq_modulus() { @@ -477,9 +510,9 @@ impl, const N: usize> Fp { const fn mul_without_cond_subtract(mut self, other: &Self) -> (bool, Self) { let (mut lo, mut hi) = ([0u64; N], [0u64; N]); - crate::const_for!((i in 0..N) { + const_for!((i in 0..N) { let mut carry = 0; - crate::const_for!((j in 0..N) { + const_for!((j in 0..N) { let k = i + j; if k >= N { hi[k - N] = mac_with_carry!(hi[k - N], (self.0).0[i], (other.0).0[j], &mut carry); @@ -491,11 +524,11 @@ impl, const N: usize> Fp { }); // Montgomery reduction let mut carry2 = 0; - crate::const_for!((i in 0..N) { + const_for!((i in 0..N) { let tmp = lo[i].wrapping_mul(P::INV); let mut carry; mac!(lo[i], tmp, P::MODULUS.0[0], &mut carry); - crate::const_for!((j in 1..N) { + const_for!((j in 1..N) { let k = i + j; if k >= N { hi[k - N] = mac_with_carry!(hi[k - N], tmp, P::MODULUS.0[j], &mut carry); @@ -506,11 +539,42 @@ impl, const N: usize> Fp { hi[i] = adc!(hi[i], carry, &mut carry2); }); - crate::const_for!((i in 0..N) { + const_for!((i in 0..N) { (self.0).0[i] = hi[i]; }); (carry2 != 0, self) } + + const fn const_is_valid(&self) -> bool { + const_for!((i in 0..N) { + if (self.0).0[N - i - 1] < P::MODULUS.0[N - i - 1] { + return true + } else if (self.0).0[N - i - 1] > P::MODULUS.0[N - i - 1] { + return false + } + }); + false + } + + #[inline] + const fn const_subtract_modulus(mut self) -> Self { + if !self.const_is_valid() { + self.0 = Self::sub_with_borrow(&self.0, &P::MODULUS); + } + self + } + + #[inline] + const fn const_subtract_modulus_with_carry(mut self, carry: bool) -> Self { + if carry || !self.const_is_valid() { + self.0 = Self::sub_with_borrow(&self.0, &P::MODULUS); + } + self + } + + const fn sub_with_borrow(a: &BigInt, b: &BigInt) -> BigInt { + a.const_sub_with_borrow(b).0 + } } impl, const N: usize> Hash for Fp { From 286b3c2ecf2102a77222c5c0bd70357a5cd04f7a Mon Sep 17 00:00:00 2001 From: Alisander Qoshqosh Date: Fri, 10 Jan 2025 14:22:38 +0400 Subject: [PATCH 12/61] ++ --- lib/crypto/src/arithmetic/mod.rs | 42 ++++++++++++++++++++++++++------ lib/crypto/src/field/fp.rs | 2 +- 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/lib/crypto/src/arithmetic/mod.rs b/lib/crypto/src/arithmetic/mod.rs index 80dc0e235..cc8ca625b 100644 --- a/lib/crypto/src/arithmetic/mod.rs +++ b/lib/crypto/src/arithmetic/mod.rs @@ -22,6 +22,8 @@ use zeroize::Zeroize; use crate::{adc, bits::BitIteratorBE, const_for, const_modulo, sbb}; pub type Limb = u64; +pub type WideLimb = u128; + // TODO#q: Refactor types to: // Fp(Limbs) - residue classes modulo prime numbers // Uint(Limbs) - normal big integers. Make sense to implement only @@ -256,12 +258,13 @@ impl BigInt { } } - pub(crate) fn add_with_carry(&mut self, other: &Self) -> bool { + // TODO#q: rename with prefix ct_* + pub(crate) const fn add_with_carry(&mut self, other: &Self) -> bool { let mut carry = 0; - for i in 0..N { + const_for!((i in 0..N) { carry = adc_for_add_with_carry(&mut self.0[i], other.0[i], carry); - } + }); carry != 0 } @@ -344,7 +347,7 @@ pub fn adc(a: &mut u64, b: u64, carry: u64) -> u64 { #[inline(always)] #[allow(unused_mut)] #[doc(hidden)] -pub fn adc_for_add_with_carry(a: &mut u64, b: u64, carry: u8) -> u8 { +pub const fn adc_for_add_with_carry(a: &mut u64, b: u64, carry: u8) -> u8 { let tmp = *a as u128 + b as u128 + carry as u128; *a = tmp as u64; (tmp >> 64) as u8 @@ -897,9 +900,34 @@ pub const fn ct_mul(a: &BigInt, b: &BigInt) -> BigInt { /// Add two numbers and panic on overflow. #[must_use] pub const fn ct_add(a: &BigInt, b: &BigInt) -> BigInt { - let (low, carry) = a.adc(b, Limb::ZERO); - assert!(carry.0 == 0, "overflow on addition"); - low + let a = *a; + let carry = a.add_with_carry(b); + assert!(carry, "overflow on addition"); + a +} + +/// Computes `lhs * rhs`, returning the low and the high limbs of the result. +#[inline(always)] +pub const fn ct_mul_wide(lhs: Limb, rhs: Limb) -> (Limb, Limb) { + let a = lhs as WideLimb; + let b = rhs as WideLimb; + let ret = a * b; + (ret as Limb, (ret >> Limb::BITS) as Limb) +} + +// TODO#q: merge with adc function +/// Computes `lhs + rhs + carry`, returning the result along with the new carry +/// (0, 1, or 2). +#[inline(always)] +pub const fn ct_adc(lhs: Limb, rhs: Limb, carry: Limb) -> (Limb, Limb) { + // We could use `Word::overflowing_add()` here analogous to + // `overflowing_add()`, but this version seems to produce a slightly + // better assembly. + let a = lhs as WideLimb; + let b = rhs as WideLimb; + let carry = carry as WideLimb; + let ret = a + b + carry; + (ret as Limb, (ret >> Limb::BITS) as Limb) } pub const fn ct_ge(a: &BigInt, b: &BigInt) -> bool { diff --git a/lib/crypto/src/field/fp.rs b/lib/crypto/src/field/fp.rs index 6da78da4a..77329c2b1 100644 --- a/lib/crypto/src/field/fp.rs +++ b/lib/crypto/src/field/fp.rs @@ -27,7 +27,7 @@ use num_traits::{One, Zero}; use crate::{ adc, arithmetic, - arithmetic::{ct_eq, ct_ge, BigInt, BigInteger, Limb}, + arithmetic::{BigInt, BigInteger, Limb}, const_for, field::{group::AdditiveGroup, prime::PrimeField, Field}, mac, mac_with_carry, From 474d82de1a8e4cf40a97626ba13970a74575729d Mon Sep 17 00:00:00 2001 From: Alisander Qoshqosh Date: Fri, 10 Jan 2025 14:57:01 +0400 Subject: [PATCH 13/61] ++ --- lib/crypto/src/arithmetic/mod.rs | 33 ++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/lib/crypto/src/arithmetic/mod.rs b/lib/crypto/src/arithmetic/mod.rs index cc8ca625b..a0944e37f 100644 --- a/lib/crypto/src/arithmetic/mod.rs +++ b/lib/crypto/src/arithmetic/mod.rs @@ -258,16 +258,29 @@ impl BigInt { } } - // TODO#q: rename with prefix ct_* - pub(crate) const fn add_with_carry(&mut self, other: &Self) -> bool { + // TODO#q: rename to checked_add? + pub(crate) fn add_with_carry(&mut self, other: &Self) -> bool { let mut carry = 0; - const_for!((i in 0..N) { + for i in 0..N { carry = adc_for_add_with_carry(&mut self.0[i], other.0[i], carry); - }); + } carry != 0 } + + pub(crate) const fn ct_add_with_carry( + mut self, + other: &Self, + ) -> (Self, bool) { + let mut carry = 0; + + const_for!((i in 0..N) { + (self.0[i], carry) = ct_adc_for_add_with_carry(self.0[i], other.0[i], carry); + }); + + (self, carry != 0) + } } /// Calculate a + b * c, returning the lower 64 bits of the result and setting @@ -347,12 +360,20 @@ pub fn adc(a: &mut u64, b: u64, carry: u64) -> u64 { #[inline(always)] #[allow(unused_mut)] #[doc(hidden)] -pub const fn adc_for_add_with_carry(a: &mut u64, b: u64, carry: u8) -> u8 { +pub fn adc_for_add_with_carry(a: &mut u64, b: u64, carry: u8) -> u8 { let tmp = *a as u128 + b as u128 + carry as u128; *a = tmp as u64; (tmp >> 64) as u8 } +#[inline(always)] +#[allow(unused_mut)] +#[doc(hidden)] +pub const fn ct_adc_for_add_with_carry(a: u64, b: u64, carry: u8) -> (u64, u8) { + let tmp = a as u128 + b as u128 + carry as u128; + (tmp as u64, (tmp >> 64) as u8) +} + // ----------- Traits Impls ----------- impl UpperHex for BigInt { @@ -901,7 +922,7 @@ pub const fn ct_mul(a: &BigInt, b: &BigInt) -> BigInt { #[must_use] pub const fn ct_add(a: &BigInt, b: &BigInt) -> BigInt { let a = *a; - let carry = a.add_with_carry(b); + let (a, carry) = a.ct_add_with_carry(b); assert!(carry, "overflow on addition"); a } From 306025e87b58c9fba5b53409257e5b45dd09b73d Mon Sep 17 00:00:00 2001 From: Alisander Qoshqosh Date: Fri, 10 Jan 2025 16:12:07 +0400 Subject: [PATCH 14/61] ++ --- lib/crypto/src/arithmetic/mod.rs | 72 +++++++++++++++++++++++++++++++- 1 file changed, 70 insertions(+), 2 deletions(-) diff --git a/lib/crypto/src/arithmetic/mod.rs b/lib/crypto/src/arithmetic/mod.rs index a0944e37f..5b7939b77 100644 --- a/lib/crypto/src/arithmetic/mod.rs +++ b/lib/crypto/src/arithmetic/mod.rs @@ -40,6 +40,9 @@ impl Default for BigInt { } impl BigInt { + pub const ONE: Self = Self::one(); + pub const ZERO: Self = Self::zero(); + pub const fn new(value: [u64; N]) -> Self { Self(value) } @@ -48,6 +51,8 @@ impl BigInt { &self.0 } + // TODO#q: remove zero() and one() in favour of const ONE and const ZERO + pub const fn zero() -> Self { Self([0u64; N]) } @@ -281,6 +286,60 @@ impl BigInt { (self, carry != 0) } + + /// Compute "wide" multiplication, with a product twice the size of the + /// input. + /// + /// Returns a tuple containing the `(lo, hi)` components of the product. + /// + /// # Ordering note + /// + /// Releases of `crypto-bigint` prior to v0.3 used `(hi, lo)` ordering + /// instead. This has been changed for better consistency with the rest of + /// the APIs in this crate. + /// + /// For more info see: + pub const fn ct_mul_wide( + &self, + rhs: &BigInt, + ) -> (Self, BigInt) { + let mut i = 0; + let mut lo = Self::ZERO; + let mut hi = BigInt::::ZERO; + + // Schoolbook multiplication. + // TODO(tarcieri): use Karatsuba for better performance? + while i < N { + let mut j = 0; + let mut carry = Limb::ZERO; + + while j < HN { + let k = i + j; + + if k >= N { + let (n, c) = + ct_mac(hi.0[k - N], self.0[i], rhs.0[j], carry); + hi.0[k - N] = n; + carry = c; + } else { + let (n, c) = ct_mac(lo.0[k], self.0[i], rhs.0[j], carry); + lo.0[k] = n; + carry = c; + } + + j += 1; + } + + if i + j >= N { + hi.0[i + j - N] = carry; + } else { + lo.0[i + j] = carry; + } + i += 1; + } + + (lo, hi) + } } /// Calculate a + b * c, returning the lower 64 bits of the result and setting @@ -293,6 +352,15 @@ pub fn mac(a: u64, b: u64, c: u64, carry: &mut u64) -> u64 { tmp as u64 } +pub const fn ct_mac(a: Limb, b: Limb, c: Limb, carry: Limb) -> (Limb, Limb) { + let a = a as WideLimb; + let b = b as WideLimb; + let c = c as WideLimb; + let carry = carry as WideLimb; + let ret = a + (b * c) + carry; + (ret as Limb, (ret >> Limb::BITS) as Limb) +} + #[inline(always)] #[doc(hidden)] pub const fn widening_mul(a: u64, b: u64) -> u128 { @@ -913,8 +981,8 @@ pub const fn from_str_hex(s: &str) -> BigInt { /// Multiply two numbers and panic on overflow. #[must_use] pub const fn ct_mul(a: &BigInt, b: &BigInt) -> BigInt { - let (low, high) = a.mul_wide(b); - assert!(high.bits() == 0, "overflow on multiplication"); + let (low, high) = a.ct_mul_wide(b); + assert!(!ct_eq(&high, &BigInt::::ZERO), "overflow on multiplication"); low } From 26af3ac76ba6c009f320700524d4ffd5c75bd246 Mon Sep 17 00:00:00 2001 From: Alisander Qoshqosh Date: Fri, 10 Jan 2025 16:42:08 +0400 Subject: [PATCH 15/61] ++ --- lib/crypto/src/arithmetic/mod.rs | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/lib/crypto/src/arithmetic/mod.rs b/lib/crypto/src/arithmetic/mod.rs index 5b7939b77..0304a533c 100644 --- a/lib/crypto/src/arithmetic/mod.rs +++ b/lib/crypto/src/arithmetic/mod.rs @@ -317,12 +317,17 @@ impl BigInt { let k = i + j; if k >= N { - let (n, c) = - ct_mac(hi.0[k - N], self.0[i], rhs.0[j], carry); + let (n, c) = ct_mac_with_carry( + hi.0[k - N], + self.0[i], + rhs.0[j], + carry, + ); hi.0[k - N] = n; carry = c; } else { - let (n, c) = ct_mac(lo.0[k], self.0[i], rhs.0[j], carry); + let (n, c) = + ct_mac_with_carry(lo.0[k], self.0[i], rhs.0[j], carry); lo.0[k] = n; carry = c; } @@ -352,7 +357,12 @@ pub fn mac(a: u64, b: u64, c: u64, carry: &mut u64) -> u64 { tmp as u64 } -pub const fn ct_mac(a: Limb, b: Limb, c: Limb, carry: Limb) -> (Limb, Limb) { +pub const fn ct_mac_with_carry( + a: Limb, + b: Limb, + c: Limb, + carry: Limb, +) -> (Limb, Limb) { let a = a as WideLimb; let b = b as WideLimb; let c = c as WideLimb; From 3f86a87c009a8f96cd92b8c40ca76aef21d9efa7 Mon Sep 17 00:00:00 2001 From: Alisander Qoshqosh Date: Fri, 10 Jan 2025 16:59:14 +0400 Subject: [PATCH 16/61] ++ --- lib/crypto/src/arithmetic/mod.rs | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/lib/crypto/src/arithmetic/mod.rs b/lib/crypto/src/arithmetic/mod.rs index 0304a533c..6c26c693b 100644 --- a/lib/crypto/src/arithmetic/mod.rs +++ b/lib/crypto/src/arithmetic/mod.rs @@ -299,6 +299,7 @@ impl BigInt { /// the APIs in this crate. /// /// For more info see: + // NOTE#q: crypto_bigint pub const fn ct_mul_wide( &self, rhs: &BigInt, @@ -345,6 +346,27 @@ impl BigInt { (lo, hi) } + + #[inline(always)] + /// Computes `a + b + carry`, returning the result along with the new carry. + // NOTE#q: crypto_bigint + pub const fn ct_adc( + &self, + rhs: &BigInt, + mut carry: Limb, + ) -> (Self, Limb) { + let mut limbs = [Limb::ZERO; N]; + let mut i = 0; + + while i < N { + let (w, c) = ct_adc(self.0[i], rhs.0[i], carry); + limbs[i] = w; + carry = c; + i += 1; + } + + (Self(limbs), carry) + } } /// Calculate a + b * c, returning the lower 64 bits of the result and setting @@ -999,10 +1021,9 @@ pub const fn ct_mul(a: &BigInt, b: &BigInt) -> BigInt { /// Add two numbers and panic on overflow. #[must_use] pub const fn ct_add(a: &BigInt, b: &BigInt) -> BigInt { - let a = *a; - let (a, carry) = a.ct_add_with_carry(b); - assert!(carry, "overflow on addition"); - a + let (low, carry) = a.ct_adc(b, Limb::ZERO); + assert!(carry == 0, "overflow on addition"); + low } /// Computes `lhs * rhs`, returning the low and the high limbs of the result. @@ -1017,6 +1038,7 @@ pub const fn ct_mul_wide(lhs: Limb, rhs: Limb) -> (Limb, Limb) { // TODO#q: merge with adc function /// Computes `lhs + rhs + carry`, returning the result along with the new carry /// (0, 1, or 2). +// NOTE#q: crypto_bigint #[inline(always)] pub const fn ct_adc(lhs: Limb, rhs: Limb, carry: Limb) -> (Limb, Limb) { // We could use `Word::overflowing_add()` here analogous to From d44383f798f8ed6d206ebae2d2183d4d1b2372f4 Mon Sep 17 00:00:00 2001 From: Alisander Qoshqosh Date: Fri, 10 Jan 2025 18:23:56 +0400 Subject: [PATCH 17/61] poseidon bn256 works --- examples/poseidon/src/lib.rs | 4 +-- lib/crypto/src/arithmetic/mod.rs | 45 +++++++++++++++++++++++++------- lib/crypto/src/field/fp.rs | 2 +- 3 files changed, 38 insertions(+), 13 deletions(-) diff --git a/examples/poseidon/src/lib.rs b/examples/poseidon/src/lib.rs index 1db5ab680..c14bb3a05 100644 --- a/examples/poseidon/src/lib.rs +++ b/examples/poseidon/src/lib.rs @@ -5,7 +5,7 @@ use alloc::vec::Vec; use alloy_primitives::U256; use openzeppelin_crypto::{ - arithmetic::{crypto_bigint::Uint, BigInteger}, + arithmetic::{BigInt, BigInteger}, field::{instance::FpBN256, prime::PrimeField}, poseidon2::{instance::bn256::BN256Params, Poseidon2}, }; @@ -21,7 +21,7 @@ impl PoseidonExample { let mut hasher = Poseidon2::::new(); for input in inputs.iter() { - let fp = FpBN256::from_bigint(Uint::from_bytes_le( + let fp = FpBN256::from_bigint(BigInt::from_bytes_le( &input.to_le_bytes_vec(), )); hasher.absorb(&fp); diff --git a/lib/crypto/src/arithmetic/mod.rs b/lib/crypto/src/arithmetic/mod.rs index 6c26c693b..5980f6503 100644 --- a/lib/crypto/src/arithmetic/mod.rs +++ b/lib/crypto/src/arithmetic/mod.rs @@ -5,18 +5,17 @@ use core::{ borrow::Borrow, fmt::{Debug, Display, UpperHex}, ops::{ - BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Shl, - ShlAssign, Shr, ShrAssign, + BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Not, + Shl, ShlAssign, Shr, ShrAssign, }, }; -use std::ops::Not; use num_bigint::BigUint; use num_traits::{ConstZero, Zero}; -use rand::{ - distributions::{Distribution, Standard}, - Rng, -}; +// use rand::{ +// distributions::{Distribution, Standard}, +// Rng, +// }; use zeroize::Zeroize; use crate::{adc, bits::BitIteratorBE, const_for, const_modulo, sbb}; @@ -367,6 +366,32 @@ impl BigInt { (Self(limbs), carry) } + + /// Create a new [`Uint`] from the provided little endian bytes. + // NOTE#q: crypto_bigint + pub const fn ct_from_le_slice(bytes: &[u8]) -> Self { + const LIMB_BYTES: usize = Limb::BITS as usize / 8; + assert!( + bytes.len() == LIMB_BYTES * N, + "bytes are not the expected size" + ); + + let mut res = [Limb::ZERO; N]; + let mut buf = [0u8; LIMB_BYTES]; + let mut i = 0; + + while i < N { + let mut j = 0; + while j < LIMB_BYTES { + buf[j] = bytes[i * LIMB_BYTES + j]; + j += 1; + } + res[i] = Limb::from_le_bytes(buf); + i += 1; + } + + Self::new(res) + } } /// Calculate a + b * c, returning the lower 64 bits of the result and setting @@ -909,7 +934,7 @@ impl BigInteger for BigInt { } fn from_bytes_le(bytes: &[u8]) -> Self { - unimplemented!() + Self::ct_from_le_slice(bytes) } fn into_bytes_le(self) -> alloc::vec::Vec { @@ -1014,7 +1039,7 @@ pub const fn from_str_hex(s: &str) -> BigInt { #[must_use] pub const fn ct_mul(a: &BigInt, b: &BigInt) -> BigInt { let (low, high) = a.ct_mul_wide(b); - assert!(!ct_eq(&high, &BigInt::::ZERO), "overflow on multiplication"); + // assert!(!ct_eq(&high, &BigInt::::ZERO), "overflow on multiplication"); low } @@ -1022,7 +1047,7 @@ pub const fn ct_mul(a: &BigInt, b: &BigInt) -> BigInt { #[must_use] pub const fn ct_add(a: &BigInt, b: &BigInt) -> BigInt { let (low, carry) = a.ct_adc(b, Limb::ZERO); - assert!(carry == 0, "overflow on addition"); + // assert!(carry != 0, "overflow on addition"); low } diff --git a/lib/crypto/src/field/fp.rs b/lib/crypto/src/field/fp.rs index 77329c2b1..464b6cda9 100644 --- a/lib/crypto/src/field/fp.rs +++ b/lib/crypto/src/field/fp.rs @@ -19,8 +19,8 @@ use core::{ fmt::{Debug, Display, Formatter}, hash::{Hash, Hasher}, marker::PhantomData, + ops::Mul, }; -use std::ops::Mul; use educe::Educe; use num_traits::{One, Zero}; From 8d292633c5d6de9b692d54e777427de265a31060 Mon Sep 17 00:00:00 2001 From: Alisander Qoshqosh Date: Fri, 10 Jan 2025 18:30:42 +0400 Subject: [PATCH 18/61] ++ --- benches/src/main.rs | 14 +++++++------- lib/crypto/src/arithmetic/mod.rs | 2 ++ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/benches/src/main.rs b/benches/src/main.rs index 9d801f50b..1c34c75dd 100644 --- a/benches/src/main.rs +++ b/benches/src/main.rs @@ -8,13 +8,13 @@ use itertools::Itertools; #[tokio::main] async fn main() -> eyre::Result<()> { let benchmarks = [ - access_control::bench().boxed(), - erc20::bench().boxed(), - erc721::bench().boxed(), - merkle_proofs::bench().boxed(), - ownable::bench().boxed(), - erc1155::bench().boxed(), - erc1155_metadata_uri::bench().boxed(), + // access_control::bench().boxed(), + // erc20::bench().boxed(), + // erc721::bench().boxed(), + // merkle_proofs::bench().boxed(), + // ownable::bench().boxed(), + // erc1155::bench().boxed(), + // erc1155_metadata_uri::bench().boxed(), poseidon::bench().boxed(), poseidon_sol::bench().boxed(), ]; diff --git a/lib/crypto/src/arithmetic/mod.rs b/lib/crypto/src/arithmetic/mod.rs index 5980f6503..43179e3e7 100644 --- a/lib/crypto/src/arithmetic/mod.rs +++ b/lib/crypto/src/arithmetic/mod.rs @@ -1039,6 +1039,7 @@ pub const fn from_str_hex(s: &str) -> BigInt { #[must_use] pub const fn ct_mul(a: &BigInt, b: &BigInt) -> BigInt { let (low, high) = a.ct_mul_wide(b); + // TODO#q: uncomment this assertion // assert!(!ct_eq(&high, &BigInt::::ZERO), "overflow on multiplication"); low } @@ -1047,6 +1048,7 @@ pub const fn ct_mul(a: &BigInt, b: &BigInt) -> BigInt { #[must_use] pub const fn ct_add(a: &BigInt, b: &BigInt) -> BigInt { let (low, carry) = a.ct_adc(b, Limb::ZERO); + // TODO#q: uncomment this assertion // assert!(carry != 0, "overflow on addition"); low } From f8ca3165a279e5ac12ecc03a340acef3cc421d83 Mon Sep 17 00:00:00 2001 From: Alisander Qoshqosh Date: Fri, 10 Jan 2025 20:54:45 +0400 Subject: [PATCH 19/61] add loops unrolls --- Cargo.lock | 14 +++++++++++++ lib/crypto/Cargo.toml | 1 + lib/crypto/src/arithmetic/mod.rs | 34 +++++++++++++++++++------------- lib/crypto/src/field/fp.rs | 5 +++++ 4 files changed, 40 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8c14cf7d9..c11566428 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -770,6 +770,19 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "ark-ff-macros" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09be120733ee33f7693ceaa202ca41accd5653b779563608f1234f78ae07c4b3" +dependencies = [ + "num-bigint", + "num-traits", + "proc-macro2", + "quote", + "syn 2.0.89", +] + [[package]] name = "ark-serialize" version = "0.3.0" @@ -2734,6 +2747,7 @@ dependencies = [ name = "openzeppelin-crypto" version = "0.2.0-alpha.2" dependencies = [ + "ark-ff-macros 0.5.0", "educe", "hex-literal", "num-bigint", diff --git a/lib/crypto/Cargo.toml b/lib/crypto/Cargo.toml index 9163c0f06..682cc2612 100644 --- a/lib/crypto/Cargo.toml +++ b/lib/crypto/Cargo.toml @@ -15,6 +15,7 @@ zeroize.workspace = true educe.workspace = true hex-literal.workspace = true num-bigint.workspace = true +ark-ff-macros = "0.5.0" [dev-dependencies] rand.workspace = true diff --git a/lib/crypto/src/arithmetic/mod.rs b/lib/crypto/src/arithmetic/mod.rs index 43179e3e7..cb65579fd 100644 --- a/lib/crypto/src/arithmetic/mod.rs +++ b/lib/crypto/src/arithmetic/mod.rs @@ -241,17 +241,6 @@ impl BigInt { const_modulo!(two_pow_n_times_64_square, self) } - pub(crate) fn sub_with_borrow(&mut self, other: &Self) -> bool { - let mut borrow = 0; - - for i in 0..N { - borrow = - sbb_for_sub_with_borrow(&mut self.0[i], other.0[i], borrow); - } - - borrow != 0 - } - pub fn div2(&mut self) { let mut t = 0; for a in self.0.iter_mut().rev() { @@ -263,6 +252,7 @@ impl BigInt { } // TODO#q: rename to checked_add? + #[ark_ff_macros::unroll_for_loops(6)] pub(crate) fn add_with_carry(&mut self, other: &Self) -> bool { let mut carry = 0; @@ -273,6 +263,18 @@ impl BigInt { carry != 0 } + #[ark_ff_macros::unroll_for_loops(6)] + pub(crate) fn sub_with_borrow(&mut self, other: &Self) -> bool { + let mut borrow = 0; + + for i in 0..N { + borrow = + sbb_for_sub_with_borrow(&mut self.0[i], other.0[i], borrow); + } + + borrow != 0 + } + pub(crate) const fn ct_add_with_carry( mut self, other: &Self, @@ -521,10 +523,14 @@ impl Display for BigInt { impl Ord for BigInt { #[inline] + // TODO#q: loop unroll actually faster here, and not bloat wasm + #[cfg_attr( + any(target_arch = "x86_64", target_family = "wasm"), + ark_ff_macros::unroll_for_loops(12) + )] fn cmp(&self, other: &Self) -> core::cmp::Ordering { use core::cmp::Ordering; - // TODO#q: check in benchmark for wasm - #[cfg(target_arch = "x86_64")] + #[cfg(any(target_arch = "x86_64", target_family = "wasm"))] for i in 0..N { let a = &self.0[N - i - 1]; let b = &other.0[N - i - 1]; @@ -534,7 +540,7 @@ impl Ord for BigInt { }; } - #[cfg(not(target_arch = "x86_64"))] + #[cfg(not(any(target_arch = "x86_64", target_family = "wasm")))] for (a, b) in self.0.iter().rev().zip(other.0.iter().rev()) { if let order @ (Ordering::Less | Ordering::Greater) = a.cmp(b) { return order; diff --git a/lib/crypto/src/field/fp.rs b/lib/crypto/src/field/fp.rs index 464b6cda9..a2cb16bd5 100644 --- a/lib/crypto/src/field/fp.rs +++ b/lib/crypto/src/field/fp.rs @@ -110,6 +110,7 @@ pub trait FpParams: Send + Sync + 'static + Sized { /// [here](https://hackmd.io/@gnark/modular_multiplication) if /// `Self::MODULUS` has (a) a non-zero MSB, and (b) at least one /// zero bit in the rest of the modulus. + #[ark_ff_macros::unroll_for_loops(6)] fn mul_assign(a: &mut Fp, b: &Fp) { // No-carry optimisation applied to CIOS if Self::CAN_USE_NO_CARRY_MUL_OPT { @@ -163,6 +164,7 @@ pub trait FpParams: Send + Sync + 'static + Sized { } /// Set `a *= a`. + #[ark_ff_macros::unroll_for_loops(6)] fn square_in_place(a: &mut Fp) { if N == 1 { // We default to multiplying with `a` using the `Mul` impl @@ -307,6 +309,9 @@ pub trait FpParams: Send + Sync + 'static + Sized { /// Convert a field element to an integer less than [`Self::MODULUS`]. #[must_use] + // TODO#q: almost zero advantage of this loop unroll, and it bloats binary + // size by 0.3kb + #[ark_ff_macros::unroll_for_loops(6)] fn into_bigint(a: Fp) -> BigInt { let mut r = (a.0).0; // Montgomery Reduction From 815553b098c54e1182c20146158a5ab662f629b2 Mon Sep 17 00:00:00 2001 From: Alisander Qoshqosh Date: Fri, 10 Jan 2025 22:18:01 +0400 Subject: [PATCH 20/61] ++ --- lib/crypto/src/const_helpers.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/crypto/src/const_helpers.rs b/lib/crypto/src/const_helpers.rs index 49a8adb2e..d8ad05965 100644 --- a/lib/crypto/src/const_helpers.rs +++ b/lib/crypto/src/const_helpers.rs @@ -30,7 +30,7 @@ macro_rules! adc { }}; } -// TODO#q: implement const_module as a function +// TODO#q: implement const_modulo as a function #[macro_export] macro_rules! const_modulo { ($a:expr, $divisor:expr) => {{ From e5885bfab11bbc3776de2255d75ae4a88a6272bf Mon Sep 17 00:00:00 2001 From: Alisander Qoshqosh Date: Fri, 10 Jan 2025 23:10:38 +0400 Subject: [PATCH 21/61] ++ --- lib/crypto/src/arithmetic/mod.rs | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/lib/crypto/src/arithmetic/mod.rs b/lib/crypto/src/arithmetic/mod.rs index cb65579fd..82f410607 100644 --- a/lib/crypto/src/arithmetic/mod.rs +++ b/lib/crypto/src/arithmetic/mod.rs @@ -24,11 +24,13 @@ pub type Limb = u64; pub type WideLimb = u128; // TODO#q: Refactor types to: -// Fp(Limbs) - residue classes modulo prime numbers -// Uint(Limbs) - normal big integers. Make sense to implement only -// constant operations necessary for hex parsing (uint.rs) -// Limbs([Limb;N]) - Wrapper type for limbs. (limbs.rs) -// Odd> - Odd numbers. (odd.rs) +// - Fp(Limbs) - residue classes modulo prime numbers +// - Uint(Limbs) - normal big integers. Make sense to implement only +// constant operations necessary for hex parsing (uint.rs) +// - Limbs([Limb;N]) - Wrapper type for limbs. (limbs.rs) +// - Odd> - Odd numbers. (odd.rs) +// - Rename u64 and u128 to Limb and WideLimb + #[derive(Copy, Clone, PartialEq, Eq, Hash, Zeroize)] pub struct BigInt(pub [Limb; N]); @@ -467,9 +469,10 @@ pub fn mac_with_carry(a: u64, b: u64, c: u64, carry: &mut u64) -> u64 { #[allow(unused_mut)] #[doc(hidden)] pub fn sbb_for_sub_with_borrow(a: &mut u64, b: u64, borrow: u8) -> u8 { - let tmp = (1u128 << 64) + (*a as u128) - (b as u128) - (borrow as u128); - *a = tmp as u64; - u8::from(tmp >> 64 == 0) + let (sub, borrow1) = a.overflowing_sub(b); + let (sub, borrow2) = sub.overflowing_sub(borrow as u64); + *a = sub; + (borrow1 | borrow2) as u8 } // TODO#q: adc can be unified with adc_for_add_with_carry @@ -488,9 +491,10 @@ pub fn adc(a: &mut u64, b: u64, carry: u64) -> u64 { #[allow(unused_mut)] #[doc(hidden)] pub fn adc_for_add_with_carry(a: &mut u64, b: u64, carry: u8) -> u8 { - let tmp = *a as u128 + b as u128 + carry as u128; - *a = tmp as u64; - (tmp >> 64) as u8 + let (sum, carry1) = a.overflowing_add(b); + let (sum, carry2) = sum.overflowing_add(carry as u64); + *a = sum; + (carry1 | carry2) as u8 } #[inline(always)] From c64a2451899f845392c9b6fea5ac5376cdd048ae Mon Sep 17 00:00:00 2001 From: Alisander Qoshqosh Date: Fri, 10 Jan 2025 23:22:21 +0400 Subject: [PATCH 22/61] ++ --- lib/crypto/src/arithmetic/mod.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/crypto/src/arithmetic/mod.rs b/lib/crypto/src/arithmetic/mod.rs index 82f410607..af9c5defe 100644 --- a/lib/crypto/src/arithmetic/mod.rs +++ b/lib/crypto/src/arithmetic/mod.rs @@ -527,7 +527,6 @@ impl Display for BigInt { impl Ord for BigInt { #[inline] - // TODO#q: loop unroll actually faster here, and not bloat wasm #[cfg_attr( any(target_arch = "x86_64", target_family = "wasm"), ark_ff_macros::unroll_for_loops(12) @@ -544,6 +543,8 @@ impl Ord for BigInt { }; } + // TODO#q: loop unroll actually faster here, and not bloat wasm + // remove it #[cfg(not(any(target_arch = "x86_64", target_family = "wasm")))] for (a, b) in self.0.iter().rev().zip(other.0.iter().rev()) { if let order @ (Ordering::Less | Ordering::Greater) = a.cmp(b) { From d95cbdda3c319791787bd3fc0a3a2e035c3fdae9 Mon Sep 17 00:00:00 2001 From: Alisander Qoshqosh Date: Fri, 10 Jan 2025 23:35:17 +0400 Subject: [PATCH 23/61] ++ --- lib/crypto/src/arithmetic/mod.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/crypto/src/arithmetic/mod.rs b/lib/crypto/src/arithmetic/mod.rs index af9c5defe..471a57ae5 100644 --- a/lib/crypto/src/arithmetic/mod.rs +++ b/lib/crypto/src/arithmetic/mod.rs @@ -256,25 +256,25 @@ impl BigInt { // TODO#q: rename to checked_add? #[ark_ff_macros::unroll_for_loops(6)] pub(crate) fn add_with_carry(&mut self, other: &Self) -> bool { - let mut carry = 0; + let mut carry = false; for i in 0..N { carry = adc_for_add_with_carry(&mut self.0[i], other.0[i], carry); } - carry != 0 + carry } #[ark_ff_macros::unroll_for_loops(6)] pub(crate) fn sub_with_borrow(&mut self, other: &Self) -> bool { - let mut borrow = 0; + let mut borrow = false; for i in 0..N { borrow = sbb_for_sub_with_borrow(&mut self.0[i], other.0[i], borrow); } - borrow != 0 + borrow } pub(crate) const fn ct_add_with_carry( @@ -468,11 +468,11 @@ pub fn mac_with_carry(a: u64, b: u64, c: u64, carry: &mut u64) -> u64 { #[inline(always)] #[allow(unused_mut)] #[doc(hidden)] -pub fn sbb_for_sub_with_borrow(a: &mut u64, b: u64, borrow: u8) -> u8 { +pub fn sbb_for_sub_with_borrow(a: &mut u64, b: u64, borrow: bool) -> bool { let (sub, borrow1) = a.overflowing_sub(b); let (sub, borrow2) = sub.overflowing_sub(borrow as u64); *a = sub; - (borrow1 | borrow2) as u8 + borrow1 | borrow2 } // TODO#q: adc can be unified with adc_for_add_with_carry @@ -490,11 +490,11 @@ pub fn adc(a: &mut u64, b: u64, carry: u64) -> u64 { #[inline(always)] #[allow(unused_mut)] #[doc(hidden)] -pub fn adc_for_add_with_carry(a: &mut u64, b: u64, carry: u8) -> u8 { +pub fn adc_for_add_with_carry(a: &mut u64, b: u64, carry: bool) -> bool { let (sum, carry1) = a.overflowing_add(b); let (sum, carry2) = sum.overflowing_add(carry as u64); *a = sum; - (carry1 | carry2) as u8 + carry1 | carry2 } #[inline(always)] From 9794a28822b7d6d75788d7743d822a638a493ef2 Mon Sep 17 00:00:00 2001 From: Alisander Qoshqosh Date: Fri, 10 Jan 2025 23:45:18 +0400 Subject: [PATCH 24/61] ++ --- lib/crypto/src/arithmetic/mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/crypto/src/arithmetic/mod.rs b/lib/crypto/src/arithmetic/mod.rs index 471a57ae5..005f6f13f 100644 --- a/lib/crypto/src/arithmetic/mod.rs +++ b/lib/crypto/src/arithmetic/mod.rs @@ -425,7 +425,6 @@ pub const fn ct_mac_with_carry( #[inline(always)] #[doc(hidden)] pub const fn widening_mul(a: u64, b: u64) -> u128 { - // TODO#q: check out this optimization #[cfg(not(target_family = "wasm"))] { a as u128 * b as u128 From 90cc32a702e0b2c35a2b519fea69562548a48f73 Mon Sep 17 00:00:00 2001 From: Alisander Qoshqosh Date: Sat, 11 Jan 2025 02:01:58 +0400 Subject: [PATCH 25/61] ++ --- lib/crypto/src/field/fp.rs | 67 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 65 insertions(+), 2 deletions(-) diff --git a/lib/crypto/src/field/fp.rs b/lib/crypto/src/field/fp.rs index a2cb16bd5..9d31d7ae8 100644 --- a/lib/crypto/src/field/fp.rs +++ b/lib/crypto/src/field/fp.rs @@ -113,6 +113,8 @@ pub trait FpParams: Send + Sync + 'static + Sized { #[ark_ff_macros::unroll_for_loops(6)] fn mul_assign(a: &mut Fp, b: &Fp) { // No-carry optimisation applied to CIOS + // NOTE#q: seems no carry optimization doesn't improve poseidon + // performance if Self::CAN_USE_NO_CARRY_MUL_OPT { let mut r = [0u64; N]; @@ -494,7 +496,7 @@ impl, const N: usize> Fp { } const fn const_mul(self, other: &Self) -> Self { - let (carry, res) = self.mul_without_cond_subtract(other); + let (carry, res) = self.ct_mul_without_cond_subtract(other); if P::MODULUS_HAS_SPARE_BIT { res.const_subtract_modulus() } else { @@ -513,7 +515,10 @@ impl, const N: usize> Fp { } } - const fn mul_without_cond_subtract(mut self, other: &Self) -> (bool, Self) { + const fn ct_mul_without_cond_subtract( + mut self, + other: &Self, + ) -> (bool, Self) { let (mut lo, mut hi) = ([0u64; N], [0u64; N]); const_for!((i in 0..N) { let mut carry = 0; @@ -550,6 +555,64 @@ impl, const N: usize> Fp { (carry2 != 0, self) } + #[ark_ff_macros::unroll_for_loops(6)] + fn mul_without_cond_subtract(mut self, other: &Self) -> (bool, Self) { + let (mut lo, mut hi) = ([0u64; N], [0u64; N]); + for i in 0..N { + let mut carry = 0; + for j in 0..N { + let k = i + j; + if k >= N { + hi[k - N] = mac_with_carry!( + hi[k - N], + (self.0).0[i], + (other.0).0[j], + &mut carry + ); + } else { + lo[k] = mac_with_carry!( + lo[k], + (self.0).0[i], + (other.0).0[j], + &mut carry + ); + } + } + hi[i] = carry; + } + // Montgomery reduction + let mut carry2 = 0; + for i in 0..N { + let tmp = lo[i].wrapping_mul(P::INV); + let mut carry; + mac!(lo[i], tmp, P::MODULUS.0[0], &mut carry); + for j in 1..N { + let k = i + j; + if k >= N { + hi[k - N] = mac_with_carry!( + hi[k - N], + tmp, + P::MODULUS.0[j], + &mut carry + ); + } else { + lo[k] = mac_with_carry!( + lo[k], + tmp, + P::MODULUS.0[j], + &mut carry + ); + } + } + hi[i] = adc!(hi[i], carry, &mut carry2); + } + + for i in 0..N { + (self.0).0[i] = hi[i]; + } + (carry2 != 0, self) + } + const fn const_is_valid(&self) -> bool { const_for!((i in 0..N) { if (self.0).0[N - i - 1] < P::MODULUS.0[N - i - 1] { From 1929652beca8d576b1f819b68736e27f18b6a5aa Mon Sep 17 00:00:00 2001 From: Alisander Qoshqosh Date: Sat, 11 Jan 2025 03:03:17 +0400 Subject: [PATCH 26/61] ++ --- lib/crypto/src/arithmetic/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/crypto/src/arithmetic/mod.rs b/lib/crypto/src/arithmetic/mod.rs index 005f6f13f..e0b53e62f 100644 --- a/lib/crypto/src/arithmetic/mod.rs +++ b/lib/crypto/src/arithmetic/mod.rs @@ -30,6 +30,7 @@ pub type WideLimb = u128; // - Limbs([Limb;N]) - Wrapper type for limbs. (limbs.rs) // - Odd> - Odd numbers. (odd.rs) // - Rename u64 and u128 to Limb and WideLimb +// - Rename functions *_with_carry to carrying_* #[derive(Copy, Clone, PartialEq, Eq, Hash, Zeroize)] pub struct BigInt(pub [Limb; N]); From bbaa1020d95ad52a1cf36b902197f94d90e0d559 Mon Sep 17 00:00:00 2001 From: Alisander Qoshqosh Date: Sat, 11 Jan 2025 03:57:26 +0400 Subject: [PATCH 27/61] biginteger ff multiplication benchmark --- Cargo.lock | 127 ++++++++++ Cargo.toml | 2 + benches/src/lib.rs | 1 + benches/src/main.rs | 4 +- benches/src/poseidon_renegades.rs | 53 ++++ examples/poseidon-renegades/Cargo.toml | 27 ++ examples/poseidon-renegades/src/lib.rs | 331 +++++++++++++++++++++++++ examples/poseidon/src/lib.rs | 29 ++- 8 files changed, 563 insertions(+), 11 deletions(-) create mode 100644 benches/src/poseidon_renegades.rs create mode 100644 examples/poseidon-renegades/Cargo.toml create mode 100644 examples/poseidon-renegades/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index c11566428..a3550a2fb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -687,6 +687,38 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" +[[package]] +name = "ark-bn254" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d69eab57e8d2663efa5c63135b2af4f396d66424f88954c21104125ab6b3e6bc" +dependencies = [ + "ark-ec", + "ark-ff 0.5.0", + "ark-std 0.5.0", +] + +[[package]] +name = "ark-ec" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43d68f2d516162846c1238e755a7c4d131b892b70cc70c471a8e3ca3ed818fce" +dependencies = [ + "ahash 0.8.11", + "ark-ff 0.5.0", + "ark-poly", + "ark-serialize 0.5.0", + "ark-std 0.5.0", + "educe", + "fnv", + "hashbrown 0.15.2", + "itertools 0.13.0", + "num-bigint", + "num-integer", + "num-traits", + "zeroize", +] + [[package]] name = "ark-ff" version = "0.3.0" @@ -725,6 +757,26 @@ dependencies = [ "zeroize", ] +[[package]] +name = "ark-ff" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a177aba0ed1e0fbb62aa9f6d0502e9b46dad8c2eab04c14258a1212d2557ea70" +dependencies = [ + "ark-ff-asm 0.5.0", + "ark-ff-macros 0.5.0", + "ark-serialize 0.5.0", + "ark-std 0.5.0", + "arrayvec", + "digest 0.10.7", + "educe", + "itertools 0.13.0", + "num-bigint", + "num-traits", + "paste", + "zeroize", +] + [[package]] name = "ark-ff-asm" version = "0.3.0" @@ -745,6 +797,16 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "ark-ff-asm" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62945a2f7e6de02a31fe400aa489f0e0f5b2502e69f95f853adb82a96c7a6b60" +dependencies = [ + "quote", + "syn 2.0.89", +] + [[package]] name = "ark-ff-macros" version = "0.3.0" @@ -783,6 +845,21 @@ dependencies = [ "syn 2.0.89", ] +[[package]] +name = "ark-poly" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "579305839da207f02b89cd1679e50e67b4331e2f9294a57693e5051b7703fe27" +dependencies = [ + "ahash 0.8.11", + "ark-ff 0.5.0", + "ark-serialize 0.5.0", + "ark-std 0.5.0", + "educe", + "fnv", + "hashbrown 0.15.2", +] + [[package]] name = "ark-serialize" version = "0.3.0" @@ -804,6 +881,30 @@ dependencies = [ "num-bigint", ] +[[package]] +name = "ark-serialize" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f4d068aaf107ebcd7dfb52bc748f8030e0fc930ac8e360146ca54c1203088f7" +dependencies = [ + "ark-serialize-derive", + "ark-std 0.5.0", + "arrayvec", + "digest 0.10.7", + "num-bigint", +] + +[[package]] +name = "ark-serialize-derive" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "213888f660fddcca0d257e88e54ac05bca01885f258ccdf695bafd77031bb69d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.89", +] + [[package]] name = "ark-std" version = "0.3.0" @@ -824,6 +925,16 @@ dependencies = [ "rand", ] +[[package]] +name = "ark-std" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "246a225cc6131e9ee4f24619af0f19d67761fff15d7ccc22e42b80846e69449a" +dependencies = [ + "num-traits", + "rand", +] + [[package]] name = "arrayvec" version = "0.7.4" @@ -2114,6 +2225,7 @@ version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" dependencies = [ + "allocator-api2", "foldhash", "serde", ] @@ -2958,6 +3070,21 @@ dependencies = [ "tokio", ] +[[package]] +name = "poseidon-renegades-example" +version = "0.2.0-alpha.2" +dependencies = [ + "alloy", + "alloy-primitives", + "ark-bn254", + "ark-ec", + "ark-ff 0.5.0", + "e2e", + "eyre", + "stylus-sdk", + "tokio", +] + [[package]] name = "ppv-lite86" version = "0.2.17" diff --git a/Cargo.toml b/Cargo.toml index 734204ff8..552d8af2b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,6 +25,7 @@ members = [ "examples/safe-erc20", "benches", "examples/poseidon", + "examples/poseidon-renegades" ] default-members = [ "contracts", @@ -49,6 +50,7 @@ default-members = [ "examples/basic/token", "examples/ecdsa", "examples/poseidon", + "examples/poseidon-renegades" ] # Explicitly set the resolver to version 2, which is the default for packages diff --git a/benches/src/lib.rs b/benches/src/lib.rs index c8fccc1d6..66cce2543 100644 --- a/benches/src/lib.rs +++ b/benches/src/lib.rs @@ -22,6 +22,7 @@ pub mod erc721; pub mod merkle_proofs; pub mod ownable; pub mod poseidon; +pub mod poseidon_renegades; pub mod poseidon_sol; pub mod report; pub mod vesting_wallet; diff --git a/benches/src/main.rs b/benches/src/main.rs index 1c34c75dd..a2da82d97 100644 --- a/benches/src/main.rs +++ b/benches/src/main.rs @@ -1,6 +1,7 @@ use benches::{ access_control, erc1155, erc1155_metadata_uri, erc20, erc721, - merkle_proofs, ownable, poseidon, poseidon_sol, report::BenchmarkReport, + merkle_proofs, ownable, poseidon, poseidon_renegades, poseidon_sol, + report::BenchmarkReport, }; use futures::FutureExt; use itertools::Itertools; @@ -17,6 +18,7 @@ async fn main() -> eyre::Result<()> { // erc1155_metadata_uri::bench().boxed(), poseidon::bench().boxed(), poseidon_sol::bench().boxed(), + poseidon_renegades::bench().boxed(), ]; // Run benchmarks max 3 at the same time. diff --git a/benches/src/poseidon_renegades.rs b/benches/src/poseidon_renegades.rs new file mode 100644 index 000000000..58ff1ed7f --- /dev/null +++ b/benches/src/poseidon_renegades.rs @@ -0,0 +1,53 @@ +use alloy::{ + network::{AnyNetwork, EthereumWallet}, + primitives::Address, + providers::ProviderBuilder, + sol, + sol_types::SolCall, +}; +use alloy_primitives::uint; +use e2e::{receipt, Account}; + +use crate::{ + report::{ContractReport, FunctionReport}, + Opt, +}; + +sol!( + #[sol(rpc)] + contract PoseidonExample { + #[derive(Debug)] + function hash(uint256[2] memory inputs) external view returns (uint256 hash); + } +); + +pub async fn bench() -> eyre::Result { + ContractReport::generate("renegades::Poseidon", run).await +} + +pub async fn run(cache_opt: Opt) -> eyre::Result> { + let alice = Account::new().await?; + let alice_wallet = ProviderBuilder::new() + .network::() + .with_recommended_fillers() + .wallet(EthereumWallet::from(alice.signer.clone())) + .on_http(alice.url().parse()?); + + let contract_addr = deploy(&alice, cache_opt).await?; + + let contract = PoseidonExample::new(contract_addr, &alice_wallet); + + #[rustfmt::skip] + let receipts = vec![ + (PoseidonExample::hashCall::SIGNATURE, receipt!(contract.hash([uint!(123_U256), uint!(123456_U256)]))?), + ]; + + receipts + .into_iter() + .map(FunctionReport::new) + .collect::>>() +} + +async fn deploy(account: &Account, cache_opt: Opt) -> eyre::Result

{ + crate::deploy(account, "poseidon-renegades", None, cache_opt).await +} diff --git a/examples/poseidon-renegades/Cargo.toml b/examples/poseidon-renegades/Cargo.toml new file mode 100644 index 000000000..90363d7a0 --- /dev/null +++ b/examples/poseidon-renegades/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "poseidon-renegades-example" +edition.workspace = true +license.workspace = true +repository.workspace = true +publish = false +version.workspace = true + +[dependencies] +alloy-primitives.workspace = true +stylus-sdk.workspace = true +#renegade-crypto = { git = "https://github.com/renegade-fi/renegade.git", package = "renegade-crypto", default-features = false } +ark-ff = { version = "0.5" } +ark-ec = "0.5" +ark-bn254 = "0.5.0" + +[dev-dependencies] +alloy.workspace = true +e2e.workspace = true +tokio.workspace = true +eyre.workspace = true + +[lib] +crate-type = ["lib", "cdylib"] + +[features] +e2e = [] diff --git a/examples/poseidon-renegades/src/lib.rs b/examples/poseidon-renegades/src/lib.rs new file mode 100644 index 000000000..b2d3431c3 --- /dev/null +++ b/examples/poseidon-renegades/src/lib.rs @@ -0,0 +1,331 @@ +#![cfg_attr(not(test), no_main)] +extern crate alloc; + +use alloc::vec::Vec; + +use alloy_primitives::U256; +use ark_ff::{ + AdditiveGroup, BigInteger, Field, Fp256, MontBackend, MontConfig, + PrimeField, +}; +use stylus_sdk::prelude::{entrypoint, public, storage}; + +pub struct FqConfig; + +const _: () = { + use ark_ff::{ + biginteger::arithmetic as fa, + fields::{Fp, *}, + BigInt, BigInteger, + }; + type B = BigInt<4usize>; + type F = Fp, 4usize>; + #[automatically_derived] + impl MontConfig<4usize> for FqConfig { + const GENERATOR: F = ark_ff::MontFp!("7"); + const MODULUS: B = BigInt([ + 4891460686036598785u64, + 2896914383306846353u64, + 13281191951274694749u64, + 3486998266802970665u64, + ]); + const TWO_ADIC_ROOT_OF_UNITY: F = ark_ff::MontFp!("1748695177688661943023146337482803886740723238769601073607632802312037301404" ); + + #[inline(always)] + fn mul_assign(a: &mut F, b: &F) { + { + if cfg!(all( + feature = "asm", + target_feature = "bmi2", + target_feature = "adx", + target_arch = "x86_64" + )) { + #[cfg(all( + feature = "asm", + target_feature = "bmi2", + target_feature = "adx", + target_arch = "x86_64" + ))] + #[allow(unsafe_code, unused_mut)] + ark_ff::x86_64_asm_mul!(4usize, (a.0).0, (b.0).0); + } else { + #[cfg(not(all( + feature = "asm", + target_feature = "bmi2", + target_feature = "adx", + target_arch = "x86_64" + )))] + { + let mut r = [0u64; 4usize]; + let mut carry1 = 0u64; + r[0] = fa::mac( + r[0], + (a.0).0[0], + (b.0).0[0usize], + &mut carry1, + ); + let k = r[0].wrapping_mul(Self::INV); + let mut carry2 = 0u64; + fa::mac_discard( + r[0], + k, + 4891460686036598785u64, + &mut carry2, + ); + r[1usize] = fa::mac_with_carry( + r[1usize], + (a.0).0[1usize], + (b.0).0[0usize], + &mut carry1, + ); + r[0usize] = fa::mac_with_carry( + r[1usize], + k, + 2896914383306846353u64, + &mut carry2, + ); + r[2usize] = fa::mac_with_carry( + r[2usize], + (a.0).0[2usize], + (b.0).0[0usize], + &mut carry1, + ); + r[1usize] = fa::mac_with_carry( + r[2usize], + k, + 13281191951274694749u64, + &mut carry2, + ); + r[3usize] = fa::mac_with_carry( + r[3usize], + (a.0).0[3usize], + (b.0).0[0usize], + &mut carry1, + ); + r[2usize] = fa::mac_with_carry( + r[3usize], + k, + 3486998266802970665u64, + &mut carry2, + ); + r[4usize - 1] = carry1 + carry2; + let mut carry1 = 0u64; + r[0] = fa::mac( + r[0], + (a.0).0[0], + (b.0).0[1usize], + &mut carry1, + ); + let k = r[0].wrapping_mul(Self::INV); + let mut carry2 = 0u64; + fa::mac_discard( + r[0], + k, + 4891460686036598785u64, + &mut carry2, + ); + r[1usize] = fa::mac_with_carry( + r[1usize], + (a.0).0[1usize], + (b.0).0[1usize], + &mut carry1, + ); + r[0usize] = fa::mac_with_carry( + r[1usize], + k, + 2896914383306846353u64, + &mut carry2, + ); + r[2usize] = fa::mac_with_carry( + r[2usize], + (a.0).0[2usize], + (b.0).0[1usize], + &mut carry1, + ); + r[1usize] = fa::mac_with_carry( + r[2usize], + k, + 13281191951274694749u64, + &mut carry2, + ); + r[3usize] = fa::mac_with_carry( + r[3usize], + (a.0).0[3usize], + (b.0).0[1usize], + &mut carry1, + ); + r[2usize] = fa::mac_with_carry( + r[3usize], + k, + 3486998266802970665u64, + &mut carry2, + ); + r[4usize - 1] = carry1 + carry2; + let mut carry1 = 0u64; + r[0] = fa::mac( + r[0], + (a.0).0[0], + (b.0).0[2usize], + &mut carry1, + ); + let k = r[0].wrapping_mul(Self::INV); + let mut carry2 = 0u64; + fa::mac_discard( + r[0], + k, + 4891460686036598785u64, + &mut carry2, + ); + r[1usize] = fa::mac_with_carry( + r[1usize], + (a.0).0[1usize], + (b.0).0[2usize], + &mut carry1, + ); + r[0usize] = fa::mac_with_carry( + r[1usize], + k, + 2896914383306846353u64, + &mut carry2, + ); + r[2usize] = fa::mac_with_carry( + r[2usize], + (a.0).0[2usize], + (b.0).0[2usize], + &mut carry1, + ); + r[1usize] = fa::mac_with_carry( + r[2usize], + k, + 13281191951274694749u64, + &mut carry2, + ); + r[3usize] = fa::mac_with_carry( + r[3usize], + (a.0).0[3usize], + (b.0).0[2usize], + &mut carry1, + ); + r[2usize] = fa::mac_with_carry( + r[3usize], + k, + 3486998266802970665u64, + &mut carry2, + ); + r[4usize - 1] = carry1 + carry2; + let mut carry1 = 0u64; + r[0] = fa::mac( + r[0], + (a.0).0[0], + (b.0).0[3usize], + &mut carry1, + ); + let k = r[0].wrapping_mul(Self::INV); + let mut carry2 = 0u64; + fa::mac_discard( + r[0], + k, + 4891460686036598785u64, + &mut carry2, + ); + r[1usize] = fa::mac_with_carry( + r[1usize], + (a.0).0[1usize], + (b.0).0[3usize], + &mut carry1, + ); + r[0usize] = fa::mac_with_carry( + r[1usize], + k, + 2896914383306846353u64, + &mut carry2, + ); + r[2usize] = fa::mac_with_carry( + r[2usize], + (a.0).0[2usize], + (b.0).0[3usize], + &mut carry1, + ); + r[1usize] = fa::mac_with_carry( + r[2usize], + k, + 13281191951274694749u64, + &mut carry2, + ); + r[3usize] = fa::mac_with_carry( + r[3usize], + (a.0).0[3usize], + (b.0).0[3usize], + &mut carry1, + ); + r[2usize] = fa::mac_with_carry( + r[3usize], + k, + 3486998266802970665u64, + &mut carry2, + ); + r[4usize - 1] = carry1 + carry2; + (a.0).0 = r; + } + } + } + __subtract_modulus(a); + } + } + + #[inline(always)] + fn __subtract_modulus(a: &mut F) { + if a.is_geq_modulus() { + __sub_with_borrow( + &mut a.0, + &BigInt([ + 4891460686036598785u64, + 2896914383306846353u64, + 13281191951274694749u64, + 3486998266802970665u64, + ]), + ); + } + } + + #[inline(always)] + fn __sub_with_borrow(a: &mut B, b: &B) -> bool { + use ark_ff::biginteger::arithmetic::sbb_for_sub_with_borrow as sbb; + let mut borrow = 0; + borrow = sbb(&mut a.0[0usize], b.0[0usize], borrow); + borrow = sbb(&mut a.0[1usize], b.0[1usize], borrow); + borrow = sbb(&mut a.0[2usize], b.0[2usize], borrow); + borrow = sbb(&mut a.0[3usize], b.0[3usize], borrow); + borrow != 0 + } +}; + +pub type FpBN256 = Fp256>; + +#[entrypoint] +#[storage] +struct PoseidonExample {} + +#[public] +impl PoseidonExample { + pub fn hash(&mut self, inputs: [U256; 2]) -> Result> { + let inputs: Vec<_> = inputs + .iter() + .map(|input| { + FpBN256::from_le_bytes_mod_order(&input.to_le_bytes_vec()) + }) + .collect(); + + let mut res = FpBN256::ONE; + for _ in 0..1000 { + for input in inputs.iter() { + res *= input; + res.square_in_place(); + } + } + + let res = res.into_bigint().to_bytes_le(); + + Ok(U256::from_le_slice(&res)) + } +} diff --git a/examples/poseidon/src/lib.rs b/examples/poseidon/src/lib.rs index c14bb3a05..80c78d3a3 100644 --- a/examples/poseidon/src/lib.rs +++ b/examples/poseidon/src/lib.rs @@ -6,7 +6,9 @@ use alloc::vec::Vec; use alloy_primitives::U256; use openzeppelin_crypto::{ arithmetic::{BigInt, BigInteger}, - field::{instance::FpBN256, prime::PrimeField}, + field::{ + group::AdditiveGroup, instance::FpBN256, prime::PrimeField, Field, + }, poseidon2::{instance::bn256::BN256Params, Poseidon2}, }; use stylus_sdk::prelude::{entrypoint, public, storage}; @@ -18,18 +20,25 @@ struct PoseidonExample {} #[public] impl PoseidonExample { pub fn hash(&mut self, inputs: [U256; 2]) -> Result> { - let mut hasher = Poseidon2::::new(); + let inputs: Vec<_> = inputs + .iter() + .map(|input| { + FpBN256::from_bigint(BigInt::from_bytes_le( + &input.to_le_bytes_vec(), + )) + }) + .collect(); - for input in inputs.iter() { - let fp = FpBN256::from_bigint(BigInt::from_bytes_le( - &input.to_le_bytes_vec(), - )); - hasher.absorb(&fp); + let mut res = FpBN256::ONE; + for _ in 0..1000 { + for input in inputs.iter() { + res *= input; + res.square_in_place(); + } } - let hash = hasher.squeeze(); - let hash = hash.into_bigint().into_bytes_le(); + let res = res.into_bigint().into_bytes_le(); - Ok(U256::from_le_slice(&hash)) + Ok(U256::from_le_slice(&res)) } } From 79cebd1191ec057ccc6fa1b79463f2da4bc5cff0 Mon Sep 17 00:00:00 2001 From: Alisander Qoshqosh Date: Sat, 11 Jan 2025 05:20:38 +0400 Subject: [PATCH 28/61] biginteger ff multiplication benchmark --- Cargo.lock | 56 +++++++++--------- Cargo.toml | 8 +-- benches/src/{poseidon.rs => ark_ff.rs} | 12 ++-- benches/src/lib.rs | 4 +- benches/src/main.rs | 9 ++- .../{poseidon_renegades.rs => oz_crypto.rs} | 12 ++-- .../{poseidon-renegades => ark-ff}/Cargo.toml | 3 +- .../{poseidon-renegades => ark-ff}/src/lib.rs | 7 +-- examples/{poseidon => oz-crypto}/Cargo.toml | 2 +- examples/{poseidon => oz-crypto}/src/lib.rs | 12 ++-- examples/poseidon/tests/abi/mod.rs | 10 ---- examples/poseidon/tests/poseidon.rs | 30 ---------- lib/crypto/src/field/fp.rs | 57 +++---------------- 13 files changed, 68 insertions(+), 154 deletions(-) rename benches/src/{poseidon.rs => ark_ff.rs} (70%) rename benches/src/{poseidon_renegades.rs => oz_crypto.rs} (70%) rename examples/{poseidon-renegades => ark-ff}/Cargo.toml (72%) rename examples/{poseidon-renegades => ark-ff}/src/lib.rs (98%) rename examples/{poseidon => oz-crypto}/Cargo.toml (93%) rename examples/{poseidon => oz-crypto}/src/lib.rs (71%) delete mode 100644 examples/poseidon/tests/abi/mod.rs delete mode 100644 examples/poseidon/tests/poseidon.rs diff --git a/Cargo.lock b/Cargo.lock index a3550a2fb..395e8db8f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -807,6 +807,21 @@ dependencies = [ "syn 2.0.89", ] +[[package]] +name = "ark-ff-example" +version = "0.2.0-alpha.2" +dependencies = [ + "alloy", + "alloy-primitives", + "ark-bn254", + "ark-ec", + "ark-ff 0.5.0", + "e2e", + "eyre", + "stylus-sdk", + "tokio", +] + [[package]] name = "ark-ff-macros" version = "0.3.0" @@ -2928,6 +2943,19 @@ version = "4.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "caff54706df99d2a78a5a4e3455ff45448d81ef1bb63c22cd14052ca0e993a3f" +[[package]] +name = "oz-crypto-example" +version = "0.2.0-alpha.2" +dependencies = [ + "alloy", + "alloy-primitives", + "e2e", + "eyre", + "openzeppelin-crypto", + "stylus-sdk", + "tokio", +] + [[package]] name = "parity-scale-codec" version = "3.6.12" @@ -3057,34 +3085,6 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" -[[package]] -name = "poseidon-example" -version = "0.2.0-alpha.2" -dependencies = [ - "alloy", - "alloy-primitives", - "e2e", - "eyre", - "openzeppelin-crypto", - "stylus-sdk", - "tokio", -] - -[[package]] -name = "poseidon-renegades-example" -version = "0.2.0-alpha.2" -dependencies = [ - "alloy", - "alloy-primitives", - "ark-bn254", - "ark-ec", - "ark-ff 0.5.0", - "e2e", - "eyre", - "stylus-sdk", - "tokio", -] - [[package]] name = "ppv-lite86" version = "0.2.17" diff --git a/Cargo.toml b/Cargo.toml index 552d8af2b..003ecc284 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,8 +24,8 @@ members = [ "examples/ownable-two-step", "examples/safe-erc20", "benches", - "examples/poseidon", - "examples/poseidon-renegades" + "examples/oz-crypto", + "examples/ark-ff" ] default-members = [ "contracts", @@ -49,8 +49,8 @@ default-members = [ "examples/access-control", "examples/basic/token", "examples/ecdsa", - "examples/poseidon", - "examples/poseidon-renegades" + "examples/oz-crypto", + "examples/ark-ff" ] # Explicitly set the resolver to version 2, which is the default for packages diff --git a/benches/src/poseidon.rs b/benches/src/ark_ff.rs similarity index 70% rename from benches/src/poseidon.rs rename to benches/src/ark_ff.rs index a596bccc0..364548e41 100644 --- a/benches/src/poseidon.rs +++ b/benches/src/ark_ff.rs @@ -15,14 +15,14 @@ use crate::{ sol!( #[sol(rpc)] - contract PoseidonExample { + contract MathExample { #[derive(Debug)] - function hash(uint256[2] memory inputs) external view returns (uint256 hash); + function compute(uint256[2] memory inputs) external view returns (uint256 hash); } ); pub async fn bench() -> eyre::Result { - ContractReport::generate("Poseidon", run).await + ContractReport::generate("ark_ff::MathExample", run).await } pub async fn run(cache_opt: Opt) -> eyre::Result> { @@ -35,11 +35,11 @@ pub async fn run(cache_opt: Opt) -> eyre::Result> { let contract_addr = deploy(&alice, cache_opt).await?; - let contract = PoseidonExample::new(contract_addr, &alice_wallet); + let contract = MathExample::new(contract_addr, &alice_wallet); #[rustfmt::skip] let receipts = vec![ - (PoseidonExample::hashCall::SIGNATURE, receipt!(contract.hash([uint!(123_U256), uint!(123456_U256)]))?), + (MathExample::computeCall::SIGNATURE, receipt!(contract.compute([uint!(123456_U256), uint!(123456789_U256)]))?), ]; receipts @@ -49,5 +49,5 @@ pub async fn run(cache_opt: Opt) -> eyre::Result> { } async fn deploy(account: &Account, cache_opt: Opt) -> eyre::Result
{ - crate::deploy(account, "poseidon", None, cache_opt).await + crate::deploy(account, "ark-ff", None, cache_opt).await } diff --git a/benches/src/lib.rs b/benches/src/lib.rs index 66cce2543..202ac3ec6 100644 --- a/benches/src/lib.rs +++ b/benches/src/lib.rs @@ -14,6 +14,7 @@ use koba::config::{Deploy, Generate, PrivateKey}; use serde::Deserialize; pub mod access_control; +pub mod ark_ff; pub mod erc1155; pub mod erc1155_metadata_uri; pub mod erc1155_supply; @@ -21,8 +22,7 @@ pub mod erc20; pub mod erc721; pub mod merkle_proofs; pub mod ownable; -pub mod poseidon; -pub mod poseidon_renegades; +pub mod oz_crypto; pub mod poseidon_sol; pub mod report; pub mod vesting_wallet; diff --git a/benches/src/main.rs b/benches/src/main.rs index a2da82d97..7bff2e7af 100644 --- a/benches/src/main.rs +++ b/benches/src/main.rs @@ -1,7 +1,6 @@ use benches::{ - access_control, erc1155, erc1155_metadata_uri, erc20, erc721, - merkle_proofs, ownable, poseidon, poseidon_renegades, poseidon_sol, - report::BenchmarkReport, + access_control, ark_ff, erc1155, erc1155_metadata_uri, erc20, erc721, + merkle_proofs, ownable, oz_crypto, poseidon_sol, report::BenchmarkReport, }; use futures::FutureExt; use itertools::Itertools; @@ -16,9 +15,9 @@ async fn main() -> eyre::Result<()> { // ownable::bench().boxed(), // erc1155::bench().boxed(), // erc1155_metadata_uri::bench().boxed(), - poseidon::bench().boxed(), poseidon_sol::bench().boxed(), - poseidon_renegades::bench().boxed(), + oz_crypto::bench().boxed(), + ark_ff::bench().boxed(), ]; // Run benchmarks max 3 at the same time. diff --git a/benches/src/poseidon_renegades.rs b/benches/src/oz_crypto.rs similarity index 70% rename from benches/src/poseidon_renegades.rs rename to benches/src/oz_crypto.rs index 58ff1ed7f..c3020a76d 100644 --- a/benches/src/poseidon_renegades.rs +++ b/benches/src/oz_crypto.rs @@ -15,14 +15,14 @@ use crate::{ sol!( #[sol(rpc)] - contract PoseidonExample { + contract MathExample { #[derive(Debug)] - function hash(uint256[2] memory inputs) external view returns (uint256 hash); + function compute(uint256[2] memory inputs) external view returns (uint256 hash); } ); pub async fn bench() -> eyre::Result { - ContractReport::generate("renegades::Poseidon", run).await + ContractReport::generate("oz_crypto::MathExample", run).await } pub async fn run(cache_opt: Opt) -> eyre::Result> { @@ -35,11 +35,11 @@ pub async fn run(cache_opt: Opt) -> eyre::Result> { let contract_addr = deploy(&alice, cache_opt).await?; - let contract = PoseidonExample::new(contract_addr, &alice_wallet); + let contract = MathExample::new(contract_addr, &alice_wallet); #[rustfmt::skip] let receipts = vec![ - (PoseidonExample::hashCall::SIGNATURE, receipt!(contract.hash([uint!(123_U256), uint!(123456_U256)]))?), + (MathExample::computeCall::SIGNATURE, receipt!(contract.compute([uint!(123456_U256), uint!(123456789_U256)]))?), ]; receipts @@ -49,5 +49,5 @@ pub async fn run(cache_opt: Opt) -> eyre::Result> { } async fn deploy(account: &Account, cache_opt: Opt) -> eyre::Result
{ - crate::deploy(account, "poseidon-renegades", None, cache_opt).await + crate::deploy(account, "oz-crypto", None, cache_opt).await } diff --git a/examples/poseidon-renegades/Cargo.toml b/examples/ark-ff/Cargo.toml similarity index 72% rename from examples/poseidon-renegades/Cargo.toml rename to examples/ark-ff/Cargo.toml index 90363d7a0..31fc8444a 100644 --- a/examples/poseidon-renegades/Cargo.toml +++ b/examples/ark-ff/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "poseidon-renegades-example" +name = "ark-ff-example" edition.workspace = true license.workspace = true repository.workspace = true @@ -9,7 +9,6 @@ version.workspace = true [dependencies] alloy-primitives.workspace = true stylus-sdk.workspace = true -#renegade-crypto = { git = "https://github.com/renegade-fi/renegade.git", package = "renegade-crypto", default-features = false } ark-ff = { version = "0.5" } ark-ec = "0.5" ark-bn254 = "0.5.0" diff --git a/examples/poseidon-renegades/src/lib.rs b/examples/ark-ff/src/lib.rs similarity index 98% rename from examples/poseidon-renegades/src/lib.rs rename to examples/ark-ff/src/lib.rs index b2d3431c3..7cfbc973d 100644 --- a/examples/poseidon-renegades/src/lib.rs +++ b/examples/ark-ff/src/lib.rs @@ -304,11 +304,11 @@ pub type FpBN256 = Fp256>; #[entrypoint] #[storage] -struct PoseidonExample {} +struct MathExample {} #[public] -impl PoseidonExample { - pub fn hash(&mut self, inputs: [U256; 2]) -> Result> { +impl MathExample { + pub fn compute(&mut self, inputs: [U256; 2]) -> Result> { let inputs: Vec<_> = inputs .iter() .map(|input| { @@ -320,7 +320,6 @@ impl PoseidonExample { for _ in 0..1000 { for input in inputs.iter() { res *= input; - res.square_in_place(); } } diff --git a/examples/poseidon/Cargo.toml b/examples/oz-crypto/Cargo.toml similarity index 93% rename from examples/poseidon/Cargo.toml rename to examples/oz-crypto/Cargo.toml index 337b30beb..b7ac6a407 100644 --- a/examples/poseidon/Cargo.toml +++ b/examples/oz-crypto/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "poseidon-example" +name = "oz-crypto-example" edition.workspace = true license.workspace = true repository.workspace = true diff --git a/examples/poseidon/src/lib.rs b/examples/oz-crypto/src/lib.rs similarity index 71% rename from examples/poseidon/src/lib.rs rename to examples/oz-crypto/src/lib.rs index 80c78d3a3..689e438a6 100644 --- a/examples/poseidon/src/lib.rs +++ b/examples/oz-crypto/src/lib.rs @@ -6,20 +6,17 @@ use alloc::vec::Vec; use alloy_primitives::U256; use openzeppelin_crypto::{ arithmetic::{BigInt, BigInteger}, - field::{ - group::AdditiveGroup, instance::FpBN256, prime::PrimeField, Field, - }, - poseidon2::{instance::bn256::BN256Params, Poseidon2}, + field::{instance::FpBN256, prime::PrimeField, Field}, }; use stylus_sdk::prelude::{entrypoint, public, storage}; #[entrypoint] #[storage] -struct PoseidonExample {} +struct MathExample {} #[public] -impl PoseidonExample { - pub fn hash(&mut self, inputs: [U256; 2]) -> Result> { +impl MathExample { + pub fn compute(&mut self, inputs: [U256; 2]) -> Result> { let inputs: Vec<_> = inputs .iter() .map(|input| { @@ -33,7 +30,6 @@ impl PoseidonExample { for _ in 0..1000 { for input in inputs.iter() { res *= input; - res.square_in_place(); } } diff --git a/examples/poseidon/tests/abi/mod.rs b/examples/poseidon/tests/abi/mod.rs deleted file mode 100644 index 0dbb9f1df..000000000 --- a/examples/poseidon/tests/abi/mod.rs +++ /dev/null @@ -1,10 +0,0 @@ -#![allow(dead_code)] -use alloy::sol; - -sol!( - #[sol(rpc)] - contract PoseidonExample { - #[derive(Debug)] - function hash(uint[2] memory inputs) external view returns (uint hash); - } -); diff --git a/examples/poseidon/tests/poseidon.rs b/examples/poseidon/tests/poseidon.rs deleted file mode 100644 index 763c37b3c..000000000 --- a/examples/poseidon/tests/poseidon.rs +++ /dev/null @@ -1,30 +0,0 @@ -#![cfg(feature = "e2e")] - -use alloy_primitives::{hex, uint, U256}; -use e2e::{Account, ReceiptExt}; -use eyre::Result; - -use crate::abi::PoseidonExample; - -mod abi; - -// ============================================================================ -// Integration Tests: Poseidon -// ============================================================================ - -#[e2e::test] -async fn poseidon_works(alice: Account) -> Result<()> { - let contract_addr = alice.as_deployer().deploy().await?.address()?; - let contract = PoseidonExample::new(contract_addr, &alice.wallet); - - let PoseidonExample::hashReturn { hash } = - contract.hash([uint!(123_U256), uint!(123456_U256)]).call().await?; - - let expected = U256::from_be_slice(&hex!( - "16f70722695a5829a59319fbf746df957a513fdf72b070a67bb72db08070e5de" - )); - - assert_eq!(hash, expected); - - Ok(()) -} diff --git a/lib/crypto/src/field/fp.rs b/lib/crypto/src/field/fp.rs index 9d31d7ae8..f094bd143 100644 --- a/lib/crypto/src/field/fp.rs +++ b/lib/crypto/src/field/fp.rs @@ -111,57 +111,17 @@ pub trait FpParams: Send + Sync + 'static + Sized { /// `Self::MODULUS` has (a) a non-zero MSB, and (b) at least one /// zero bit in the rest of the modulus. #[ark_ff_macros::unroll_for_loops(6)] + #[inline(always)] fn mul_assign(a: &mut Fp, b: &Fp) { - // No-carry optimisation applied to CIOS - // NOTE#q: seems no carry optimization doesn't improve poseidon - // performance - if Self::CAN_USE_NO_CARRY_MUL_OPT { - let mut r = [0u64; N]; - - for i in 0..N { - let mut carry1 = 0u64; - r[0] = - arithmetic::mac(r[0], (a.0).0[0], (b.0).0[i], &mut carry1); - - let k = r[0].wrapping_mul(Self::INV); - - let mut carry2 = 0u64; - arithmetic::mac_discard( - r[0], - k, - Self::MODULUS.0[0], - &mut carry2, - ); + // Alternative implementation + // Implements CIOS. + let (carry, res) = a.mul_without_cond_subtract(b); + *a = res; - for j in 1..N { - r[j] = arithmetic::mac_with_carry( - r[j], - (a.0).0[j], - (b.0).0[i], - &mut carry1, - ); - r[j - 1] = arithmetic::mac_with_carry( - r[j], - k, - Self::MODULUS.0[j], - &mut carry2, - ); - } - r[N - 1] = carry1 + carry2; - } - (a.0).0.copy_from_slice(&r); - a.subtract_modulus(); + if Self::MODULUS_HAS_SPARE_BIT { + a.subtract_modulus_with_carry(carry); } else { - // Alternative implementation - // Implements CIOS. - let (carry, res) = a.mul_without_cond_subtract(b); - *a = res; - - if Self::MODULUS_HAS_SPARE_BIT { - a.subtract_modulus_with_carry(carry); - } else { - a.subtract_modulus(); - } + a.subtract_modulus(); } } @@ -556,6 +516,7 @@ impl, const N: usize> Fp { } #[ark_ff_macros::unroll_for_loops(6)] + #[inline(always)] fn mul_without_cond_subtract(mut self, other: &Self) -> (bool, Self) { let (mut lo, mut hi) = ([0u64; N], [0u64; N]); for i in 0..N { From 3a65c4712d032b994e061af55e3ad8ce455ceea4 Mon Sep 17 00:00:00 2001 From: Alisander Qoshqosh Date: Sat, 11 Jan 2025 05:29:44 +0400 Subject: [PATCH 29/61] ++ --- Cargo.lock | 146 ++++++++++- Cargo.toml | 8 +- benches/src/lib.rs | 2 + benches/src/main.rs | 5 +- benches/src/poseidon.rs | 53 ++++ benches/src/poseidon_renegades.rs | 53 ++++ examples/poseidon-renegades/Cargo.toml | 27 ++ examples/poseidon-renegades/src/lib.rs | 327 +++++++++++++++++++++++++ examples/poseidon/Cargo.toml | 24 ++ examples/poseidon/src/lib.rs | 40 +++ examples/poseidon/tests/abi/mod.rs | 10 + examples/poseidon/tests/poseidon.rs | 30 +++ 12 files changed, 717 insertions(+), 8 deletions(-) create mode 100644 benches/src/poseidon.rs create mode 100644 benches/src/poseidon_renegades.rs create mode 100644 examples/poseidon-renegades/Cargo.toml create mode 100644 examples/poseidon-renegades/src/lib.rs create mode 100644 examples/poseidon/Cargo.toml create mode 100644 examples/poseidon/src/lib.rs create mode 100644 examples/poseidon/tests/abi/mod.rs create mode 100644 examples/poseidon/tests/poseidon.rs diff --git a/Cargo.lock b/Cargo.lock index 395e8db8f..e90ba0094 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -687,17 +687,45 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" +[[package]] +name = "ark-bn254" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a22f4561524cd949590d78d7d4c5df8f592430d221f7f3c9497bbafd8972120f" +dependencies = [ + "ark-ec 0.4.2", + "ark-ff 0.4.2", + "ark-std 0.4.0", +] + [[package]] name = "ark-bn254" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d69eab57e8d2663efa5c63135b2af4f396d66424f88954c21104125ab6b3e6bc" dependencies = [ - "ark-ec", + "ark-ec 0.5.0", "ark-ff 0.5.0", "ark-std 0.5.0", ] +[[package]] +name = "ark-ec" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "defd9a439d56ac24968cca0571f598a61bc8c55f71d50a89cda591cb750670ba" +dependencies = [ + "ark-ff 0.4.2", + "ark-poly 0.4.2", + "ark-serialize 0.4.2", + "ark-std 0.4.0", + "derivative", + "hashbrown 0.13.2", + "itertools 0.10.5", + "num-traits", + "zeroize", +] + [[package]] name = "ark-ec" version = "0.5.0" @@ -706,7 +734,7 @@ checksum = "43d68f2d516162846c1238e755a7c4d131b892b70cc70c471a8e3ca3ed818fce" dependencies = [ "ahash 0.8.11", "ark-ff 0.5.0", - "ark-poly", + "ark-poly 0.5.0", "ark-serialize 0.5.0", "ark-std 0.5.0", "educe", @@ -719,6 +747,18 @@ dependencies = [ "zeroize", ] +[[package]] +name = "ark-ed-on-bn254" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71892f265d01650e34988a546b37ea1d2ba1da162a639597a03d1550f26004d8" +dependencies = [ + "ark-bn254 0.4.0", + "ark-ec 0.4.2", + "ark-ff 0.4.2", + "ark-std 0.4.0", +] + [[package]] name = "ark-ff" version = "0.3.0" @@ -813,8 +853,8 @@ version = "0.2.0-alpha.2" dependencies = [ "alloy", "alloy-primitives", - "ark-bn254", - "ark-ec", + "ark-bn254 0.5.0", + "ark-ec 0.5.0", "ark-ff 0.5.0", "e2e", "eyre", @@ -860,6 +900,19 @@ dependencies = [ "syn 2.0.89", ] +[[package]] +name = "ark-poly" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d320bfc44ee185d899ccbadfa8bc31aab923ce1558716e1997a1e74057fe86bf" +dependencies = [ + "ark-ff 0.4.2", + "ark-serialize 0.4.2", + "ark-std 0.4.0", + "derivative", + "hashbrown 0.13.2", +] + [[package]] name = "ark-poly" version = "0.5.0" @@ -891,6 +944,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" dependencies = [ + "ark-serialize-derive 0.4.2", "ark-std 0.4.0", "digest 0.10.7", "num-bigint", @@ -902,13 +956,24 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f4d068aaf107ebcd7dfb52bc748f8030e0fc930ac8e360146ca54c1203088f7" dependencies = [ - "ark-serialize-derive", + "ark-serialize-derive 0.5.0", "ark-std 0.5.0", "arrayvec", "digest 0.10.7", "num-bigint", ] +[[package]] +name = "ark-serialize-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae3281bc6d0fd7e549af32b52511e1302185bd688fd3359fa36423346ff682ea" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "ark-serialize-derive" version = "0.5.0" @@ -1076,6 +1141,17 @@ dependencies = [ "tokio", ] +[[package]] +name = "bigdecimal" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6773ddc0eafc0e509fb60e48dff7f450f8e674a0686ae8605e8d9901bd5eefa" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", +] + [[package]] name = "bit-set" version = "0.5.3" @@ -1312,6 +1388,16 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" +[[package]] +name = "constants" +version = "0.1.0" +source = "git+https://github.com/renegade-fi/renegade.git#3ba7671b28369624b0439010d916a1a5a035b493" +dependencies = [ + "ark-bn254 0.4.0", + "ark-ec 0.4.2", + "ark-ed-on-bn254", +] + [[package]] name = "convert_case" version = "0.6.0" @@ -2223,6 +2309,9 @@ name = "hashbrown" version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash 0.8.11", +] [[package]] name = "hashbrown" @@ -2750,6 +2839,8 @@ checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" dependencies = [ "num-integer", "num-traits", + "rand", + "serde", ] [[package]] @@ -3085,6 +3176,35 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" +[[package]] +name = "poseidon-example" +version = "0.2.0-alpha.2" +dependencies = [ + "alloy", + "alloy-primitives", + "e2e", + "eyre", + "openzeppelin-crypto", + "stylus-sdk", + "tokio", +] + +[[package]] +name = "poseidon-renegades-example" +version = "0.2.0-alpha.2" +dependencies = [ + "alloy", + "alloy-primitives", + "ark-bn254 0.5.0", + "ark-ec 0.5.0", + "ark-ff 0.5.0", + "e2e", + "eyre", + "renegade-crypto", + "stylus-sdk", + "tokio", +] + [[package]] name = "ppv-lite86" version = "0.2.17" @@ -3369,6 +3489,22 @@ dependencies = [ "bytecheck", ] +[[package]] +name = "renegade-crypto" +version = "0.1.0" +source = "git+https://github.com/renegade-fi/renegade.git#3ba7671b28369624b0439010d916a1a5a035b493" +dependencies = [ + "ark-ec 0.4.2", + "ark-ff 0.4.2", + "bigdecimal", + "constants", + "itertools 0.10.5", + "lazy_static", + "num-bigint", + "serde", + "serde_json", +] + [[package]] name = "reqwest" version = "0.12.5" diff --git a/Cargo.toml b/Cargo.toml index 003ecc284..2974423b8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,7 +25,9 @@ members = [ "examples/safe-erc20", "benches", "examples/oz-crypto", - "examples/ark-ff" + "examples/ark-ff", + "examples/poseidon", + "examples/poseidon-renegades" ] default-members = [ "contracts", @@ -50,7 +52,9 @@ default-members = [ "examples/basic/token", "examples/ecdsa", "examples/oz-crypto", - "examples/ark-ff" + "examples/ark-ff", + "examples/poseidon", + "examples/poseidon-renegades" ] # Explicitly set the resolver to version 2, which is the default for packages diff --git a/benches/src/lib.rs b/benches/src/lib.rs index 202ac3ec6..19b06dff3 100644 --- a/benches/src/lib.rs +++ b/benches/src/lib.rs @@ -23,6 +23,8 @@ pub mod erc721; pub mod merkle_proofs; pub mod ownable; pub mod oz_crypto; +pub mod poseidon; +pub mod poseidon_renegades; pub mod poseidon_sol; pub mod report; pub mod vesting_wallet; diff --git a/benches/src/main.rs b/benches/src/main.rs index 7bff2e7af..5a4797edd 100644 --- a/benches/src/main.rs +++ b/benches/src/main.rs @@ -1,6 +1,7 @@ use benches::{ access_control, ark_ff, erc1155, erc1155_metadata_uri, erc20, erc721, - merkle_proofs, ownable, oz_crypto, poseidon_sol, report::BenchmarkReport, + merkle_proofs, ownable, oz_crypto, poseidon, poseidon_renegades, + poseidon_sol, report::BenchmarkReport, }; use futures::FutureExt; use itertools::Itertools; @@ -16,6 +17,8 @@ async fn main() -> eyre::Result<()> { // erc1155::bench().boxed(), // erc1155_metadata_uri::bench().boxed(), poseidon_sol::bench().boxed(), + poseidon::bench().boxed(), + poseidon_renegades::bench().boxed(), oz_crypto::bench().boxed(), ark_ff::bench().boxed(), ]; diff --git a/benches/src/poseidon.rs b/benches/src/poseidon.rs new file mode 100644 index 000000000..a596bccc0 --- /dev/null +++ b/benches/src/poseidon.rs @@ -0,0 +1,53 @@ +use alloy::{ + network::{AnyNetwork, EthereumWallet}, + primitives::Address, + providers::ProviderBuilder, + sol, + sol_types::SolCall, +}; +use alloy_primitives::uint; +use e2e::{receipt, Account}; + +use crate::{ + report::{ContractReport, FunctionReport}, + Opt, +}; + +sol!( + #[sol(rpc)] + contract PoseidonExample { + #[derive(Debug)] + function hash(uint256[2] memory inputs) external view returns (uint256 hash); + } +); + +pub async fn bench() -> eyre::Result { + ContractReport::generate("Poseidon", run).await +} + +pub async fn run(cache_opt: Opt) -> eyre::Result> { + let alice = Account::new().await?; + let alice_wallet = ProviderBuilder::new() + .network::() + .with_recommended_fillers() + .wallet(EthereumWallet::from(alice.signer.clone())) + .on_http(alice.url().parse()?); + + let contract_addr = deploy(&alice, cache_opt).await?; + + let contract = PoseidonExample::new(contract_addr, &alice_wallet); + + #[rustfmt::skip] + let receipts = vec![ + (PoseidonExample::hashCall::SIGNATURE, receipt!(contract.hash([uint!(123_U256), uint!(123456_U256)]))?), + ]; + + receipts + .into_iter() + .map(FunctionReport::new) + .collect::>>() +} + +async fn deploy(account: &Account, cache_opt: Opt) -> eyre::Result
{ + crate::deploy(account, "poseidon", None, cache_opt).await +} diff --git a/benches/src/poseidon_renegades.rs b/benches/src/poseidon_renegades.rs new file mode 100644 index 000000000..58ff1ed7f --- /dev/null +++ b/benches/src/poseidon_renegades.rs @@ -0,0 +1,53 @@ +use alloy::{ + network::{AnyNetwork, EthereumWallet}, + primitives::Address, + providers::ProviderBuilder, + sol, + sol_types::SolCall, +}; +use alloy_primitives::uint; +use e2e::{receipt, Account}; + +use crate::{ + report::{ContractReport, FunctionReport}, + Opt, +}; + +sol!( + #[sol(rpc)] + contract PoseidonExample { + #[derive(Debug)] + function hash(uint256[2] memory inputs) external view returns (uint256 hash); + } +); + +pub async fn bench() -> eyre::Result { + ContractReport::generate("renegades::Poseidon", run).await +} + +pub async fn run(cache_opt: Opt) -> eyre::Result> { + let alice = Account::new().await?; + let alice_wallet = ProviderBuilder::new() + .network::() + .with_recommended_fillers() + .wallet(EthereumWallet::from(alice.signer.clone())) + .on_http(alice.url().parse()?); + + let contract_addr = deploy(&alice, cache_opt).await?; + + let contract = PoseidonExample::new(contract_addr, &alice_wallet); + + #[rustfmt::skip] + let receipts = vec![ + (PoseidonExample::hashCall::SIGNATURE, receipt!(contract.hash([uint!(123_U256), uint!(123456_U256)]))?), + ]; + + receipts + .into_iter() + .map(FunctionReport::new) + .collect::>>() +} + +async fn deploy(account: &Account, cache_opt: Opt) -> eyre::Result
{ + crate::deploy(account, "poseidon-renegades", None, cache_opt).await +} diff --git a/examples/poseidon-renegades/Cargo.toml b/examples/poseidon-renegades/Cargo.toml new file mode 100644 index 000000000..801e7ee1d --- /dev/null +++ b/examples/poseidon-renegades/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "poseidon-renegades-example" +edition.workspace = true +license.workspace = true +repository.workspace = true +publish = false +version.workspace = true + +[dependencies] +alloy-primitives.workspace = true +stylus-sdk.workspace = true +renegade-crypto = { git = "https://github.com/renegade-fi/renegade.git", package = "renegade-crypto", default-features = false } +ark-ff = { version = "0.5" } +ark-ec = "0.5" +ark-bn254 = "0.5.0" + +[dev-dependencies] +alloy.workspace = true +e2e.workspace = true +tokio.workspace = true +eyre.workspace = true + +[lib] +crate-type = ["lib", "cdylib"] + +[features] +e2e = [] diff --git a/examples/poseidon-renegades/src/lib.rs b/examples/poseidon-renegades/src/lib.rs new file mode 100644 index 000000000..bf6da23e6 --- /dev/null +++ b/examples/poseidon-renegades/src/lib.rs @@ -0,0 +1,327 @@ +#![cfg_attr(not(test), no_main)] +extern crate alloc; + +use alloc::vec::Vec; + +use alloy_primitives::U256; +use ark_ff::{BigInteger, Field, Fp256, MontBackend, MontConfig, PrimeField}; +use stylus_sdk::prelude::{entrypoint, public, storage}; + +pub struct FqConfig; + +const _: () = { + use ark_ff::{ + biginteger::arithmetic as fa, + fields::{Fp, *}, + BigInt, BigInteger, + }; + type B = BigInt<4usize>; + type F = Fp, 4usize>; + #[automatically_derived] + impl MontConfig<4usize> for FqConfig { + const GENERATOR: F = ark_ff::MontFp!("7"); + const MODULUS: B = BigInt([ + 4891460686036598785u64, + 2896914383306846353u64, + 13281191951274694749u64, + 3486998266802970665u64, + ]); + const TWO_ADIC_ROOT_OF_UNITY: F = ark_ff::MontFp!("1748695177688661943023146337482803886740723238769601073607632802312037301404" ); + + #[inline(always)] + fn mul_assign(a: &mut F, b: &F) { + { + if cfg!(all( + feature = "asm", + target_feature = "bmi2", + target_feature = "adx", + target_arch = "x86_64" + )) { + #[cfg(all( + feature = "asm", + target_feature = "bmi2", + target_feature = "adx", + target_arch = "x86_64" + ))] + #[allow(unsafe_code, unused_mut)] + ark_ff::x86_64_asm_mul!(4usize, (a.0).0, (b.0).0); + } else { + #[cfg(not(all( + feature = "asm", + target_feature = "bmi2", + target_feature = "adx", + target_arch = "x86_64" + )))] + { + let mut r = [0u64; 4usize]; + let mut carry1 = 0u64; + r[0] = fa::mac( + r[0], + (a.0).0[0], + (b.0).0[0usize], + &mut carry1, + ); + let k = r[0].wrapping_mul(Self::INV); + let mut carry2 = 0u64; + fa::mac_discard( + r[0], + k, + 4891460686036598785u64, + &mut carry2, + ); + r[1usize] = fa::mac_with_carry( + r[1usize], + (a.0).0[1usize], + (b.0).0[0usize], + &mut carry1, + ); + r[0usize] = fa::mac_with_carry( + r[1usize], + k, + 2896914383306846353u64, + &mut carry2, + ); + r[2usize] = fa::mac_with_carry( + r[2usize], + (a.0).0[2usize], + (b.0).0[0usize], + &mut carry1, + ); + r[1usize] = fa::mac_with_carry( + r[2usize], + k, + 13281191951274694749u64, + &mut carry2, + ); + r[3usize] = fa::mac_with_carry( + r[3usize], + (a.0).0[3usize], + (b.0).0[0usize], + &mut carry1, + ); + r[2usize] = fa::mac_with_carry( + r[3usize], + k, + 3486998266802970665u64, + &mut carry2, + ); + r[4usize - 1] = carry1 + carry2; + let mut carry1 = 0u64; + r[0] = fa::mac( + r[0], + (a.0).0[0], + (b.0).0[1usize], + &mut carry1, + ); + let k = r[0].wrapping_mul(Self::INV); + let mut carry2 = 0u64; + fa::mac_discard( + r[0], + k, + 4891460686036598785u64, + &mut carry2, + ); + r[1usize] = fa::mac_with_carry( + r[1usize], + (a.0).0[1usize], + (b.0).0[1usize], + &mut carry1, + ); + r[0usize] = fa::mac_with_carry( + r[1usize], + k, + 2896914383306846353u64, + &mut carry2, + ); + r[2usize] = fa::mac_with_carry( + r[2usize], + (a.0).0[2usize], + (b.0).0[1usize], + &mut carry1, + ); + r[1usize] = fa::mac_with_carry( + r[2usize], + k, + 13281191951274694749u64, + &mut carry2, + ); + r[3usize] = fa::mac_with_carry( + r[3usize], + (a.0).0[3usize], + (b.0).0[1usize], + &mut carry1, + ); + r[2usize] = fa::mac_with_carry( + r[3usize], + k, + 3486998266802970665u64, + &mut carry2, + ); + r[4usize - 1] = carry1 + carry2; + let mut carry1 = 0u64; + r[0] = fa::mac( + r[0], + (a.0).0[0], + (b.0).0[2usize], + &mut carry1, + ); + let k = r[0].wrapping_mul(Self::INV); + let mut carry2 = 0u64; + fa::mac_discard( + r[0], + k, + 4891460686036598785u64, + &mut carry2, + ); + r[1usize] = fa::mac_with_carry( + r[1usize], + (a.0).0[1usize], + (b.0).0[2usize], + &mut carry1, + ); + r[0usize] = fa::mac_with_carry( + r[1usize], + k, + 2896914383306846353u64, + &mut carry2, + ); + r[2usize] = fa::mac_with_carry( + r[2usize], + (a.0).0[2usize], + (b.0).0[2usize], + &mut carry1, + ); + r[1usize] = fa::mac_with_carry( + r[2usize], + k, + 13281191951274694749u64, + &mut carry2, + ); + r[3usize] = fa::mac_with_carry( + r[3usize], + (a.0).0[3usize], + (b.0).0[2usize], + &mut carry1, + ); + r[2usize] = fa::mac_with_carry( + r[3usize], + k, + 3486998266802970665u64, + &mut carry2, + ); + r[4usize - 1] = carry1 + carry2; + let mut carry1 = 0u64; + r[0] = fa::mac( + r[0], + (a.0).0[0], + (b.0).0[3usize], + &mut carry1, + ); + let k = r[0].wrapping_mul(Self::INV); + let mut carry2 = 0u64; + fa::mac_discard( + r[0], + k, + 4891460686036598785u64, + &mut carry2, + ); + r[1usize] = fa::mac_with_carry( + r[1usize], + (a.0).0[1usize], + (b.0).0[3usize], + &mut carry1, + ); + r[0usize] = fa::mac_with_carry( + r[1usize], + k, + 2896914383306846353u64, + &mut carry2, + ); + r[2usize] = fa::mac_with_carry( + r[2usize], + (a.0).0[2usize], + (b.0).0[3usize], + &mut carry1, + ); + r[1usize] = fa::mac_with_carry( + r[2usize], + k, + 13281191951274694749u64, + &mut carry2, + ); + r[3usize] = fa::mac_with_carry( + r[3usize], + (a.0).0[3usize], + (b.0).0[3usize], + &mut carry1, + ); + r[2usize] = fa::mac_with_carry( + r[3usize], + k, + 3486998266802970665u64, + &mut carry2, + ); + r[4usize - 1] = carry1 + carry2; + (a.0).0 = r; + } + } + } + __subtract_modulus(a); + } + } + + #[inline(always)] + fn __subtract_modulus(a: &mut F) { + if a.is_geq_modulus() { + __sub_with_borrow( + &mut a.0, + &BigInt([ + 4891460686036598785u64, + 2896914383306846353u64, + 13281191951274694749u64, + 3486998266802970665u64, + ]), + ); + } + } + + #[inline(always)] + fn __sub_with_borrow(a: &mut B, b: &B) -> bool { + use ark_ff::biginteger::arithmetic::sbb_for_sub_with_borrow as sbb; + let mut borrow = 0; + borrow = sbb(&mut a.0[0usize], b.0[0usize], borrow); + borrow = sbb(&mut a.0[1usize], b.0[1usize], borrow); + borrow = sbb(&mut a.0[2usize], b.0[2usize], borrow); + borrow = sbb(&mut a.0[3usize], b.0[3usize], borrow); + borrow != 0 + } +}; + +pub type FpBN256 = Fp256>; + +#[entrypoint] +#[storage] +struct PoseidonExample {} + +#[public] +impl PoseidonExample { + pub fn hash(&mut self, inputs: [U256; 2]) -> Result> { + let inputs: Vec<_> = inputs + .iter() + .map(|input| { + FpBN256::from_le_bytes_mod_order(&input.to_le_bytes_vec()) + }) + .collect(); + + let mut res = FpBN256::ONE; + for _ in 0..1000 { + for input in inputs.iter() { + res *= input + } + } + + let res = res.into_bigint().to_bytes_le(); + + Ok(U256::from_le_slice(&res)) + } +} diff --git a/examples/poseidon/Cargo.toml b/examples/poseidon/Cargo.toml new file mode 100644 index 000000000..337b30beb --- /dev/null +++ b/examples/poseidon/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "poseidon-example" +edition.workspace = true +license.workspace = true +repository.workspace = true +publish = false +version.workspace = true + +[dependencies] +openzeppelin-crypto.workspace = true +alloy-primitives.workspace = true +stylus-sdk.workspace = true + +[dev-dependencies] +alloy.workspace = true +e2e.workspace = true +tokio.workspace = true +eyre.workspace = true + +[lib] +crate-type = ["lib", "cdylib"] + +[features] +e2e = [] diff --git a/examples/poseidon/src/lib.rs b/examples/poseidon/src/lib.rs new file mode 100644 index 000000000..289e7172d --- /dev/null +++ b/examples/poseidon/src/lib.rs @@ -0,0 +1,40 @@ +#![cfg_attr(not(test), no_main)] +extern crate alloc; + +use alloc::vec::Vec; + +use alloy_primitives::U256; +use openzeppelin_crypto::{ + arithmetic::{BigInt, BigInteger}, + field::{instance::FpBN256, prime::PrimeField}, +}; +use stylus_sdk::prelude::{entrypoint, public, storage}; + +#[entrypoint] +#[storage] +struct PoseidonExample {} + +#[public] +impl PoseidonExample { + pub fn hash(&mut self, inputs: [U256; 2]) -> Result> { + let inputs: Vec<_> = inputs + .iter() + .map(|input| { + FpBN256::from_bigint(BigInt::from_bytes_le( + &input.to_le_bytes_vec(), + )) + }) + .collect(); + + let mut res = FpBN256::ONE; + for _ in 0..1000 { + for input in inputs.iter() { + res *= input + } + } + + let res = res.into_bigint().into_bytes_le(); + + Ok(U256::from_le_slice(&res)) + } +} diff --git a/examples/poseidon/tests/abi/mod.rs b/examples/poseidon/tests/abi/mod.rs new file mode 100644 index 000000000..0dbb9f1df --- /dev/null +++ b/examples/poseidon/tests/abi/mod.rs @@ -0,0 +1,10 @@ +#![allow(dead_code)] +use alloy::sol; + +sol!( + #[sol(rpc)] + contract PoseidonExample { + #[derive(Debug)] + function hash(uint[2] memory inputs) external view returns (uint hash); + } +); diff --git a/examples/poseidon/tests/poseidon.rs b/examples/poseidon/tests/poseidon.rs new file mode 100644 index 000000000..763c37b3c --- /dev/null +++ b/examples/poseidon/tests/poseidon.rs @@ -0,0 +1,30 @@ +#![cfg(feature = "e2e")] + +use alloy_primitives::{hex, uint, U256}; +use e2e::{Account, ReceiptExt}; +use eyre::Result; + +use crate::abi::PoseidonExample; + +mod abi; + +// ============================================================================ +// Integration Tests: Poseidon +// ============================================================================ + +#[e2e::test] +async fn poseidon_works(alice: Account) -> Result<()> { + let contract_addr = alice.as_deployer().deploy().await?.address()?; + let contract = PoseidonExample::new(contract_addr, &alice.wallet); + + let PoseidonExample::hashReturn { hash } = + contract.hash([uint!(123_U256), uint!(123456_U256)]).call().await?; + + let expected = U256::from_be_slice(&hex!( + "16f70722695a5829a59319fbf746df957a513fdf72b070a67bb72db08070e5de" + )); + + assert_eq!(hash, expected); + + Ok(()) +} From 6f99e1b6f4bb7b12ae46676806c380066f9372e5 Mon Sep 17 00:00:00 2001 From: Alisander Qoshqosh Date: Sat, 11 Jan 2025 05:33:31 +0400 Subject: [PATCH 30/61] ++ --- Cargo.lock | 6 +- examples/poseidon-renegades/Cargo.toml | 2 +- examples/poseidon-renegades/src/lib.rs | 321 ++----------------------- 3 files changed, 19 insertions(+), 310 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e90ba0094..2ecb7d50d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1391,7 +1391,6 @@ checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" [[package]] name = "constants" version = "0.1.0" -source = "git+https://github.com/renegade-fi/renegade.git#3ba7671b28369624b0439010d916a1a5a035b493" dependencies = [ "ark-bn254 0.4.0", "ark-ec 0.4.2", @@ -3492,10 +3491,9 @@ dependencies = [ [[package]] name = "renegade-crypto" version = "0.1.0" -source = "git+https://github.com/renegade-fi/renegade.git#3ba7671b28369624b0439010d916a1a5a035b493" dependencies = [ - "ark-ec 0.4.2", - "ark-ff 0.4.2", + "ark-ec 0.5.0", + "ark-ff 0.5.0", "bigdecimal", "constants", "itertools 0.10.5", diff --git a/examples/poseidon-renegades/Cargo.toml b/examples/poseidon-renegades/Cargo.toml index 801e7ee1d..64dba337b 100644 --- a/examples/poseidon-renegades/Cargo.toml +++ b/examples/poseidon-renegades/Cargo.toml @@ -9,7 +9,7 @@ version.workspace = true [dependencies] alloy-primitives.workspace = true stylus-sdk.workspace = true -renegade-crypto = { git = "https://github.com/renegade-fi/renegade.git", package = "renegade-crypto", default-features = false } +renegade-crypto = { path = "../../../renegade/renegade-crypto", package = "renegade-crypto", default-features = false } ark-ff = { version = "0.5" } ark-ec = "0.5" ark-bn254 = "0.5.0" diff --git a/examples/poseidon-renegades/src/lib.rs b/examples/poseidon-renegades/src/lib.rs index bf6da23e6..e7ed90682 100644 --- a/examples/poseidon-renegades/src/lib.rs +++ b/examples/poseidon-renegades/src/lib.rs @@ -4,300 +4,16 @@ extern crate alloc; use alloc::vec::Vec; use alloy_primitives::U256; -use ark_ff::{BigInteger, Field, Fp256, MontBackend, MontConfig, PrimeField}; +use ark_ff::{BigInteger, PrimeField}; +use renegade_crypto::hash::{Poseidon2Sponge, ScalarField}; use stylus_sdk::prelude::{entrypoint, public, storage}; -pub struct FqConfig; - -const _: () = { - use ark_ff::{ - biginteger::arithmetic as fa, - fields::{Fp, *}, - BigInt, BigInteger, - }; - type B = BigInt<4usize>; - type F = Fp, 4usize>; - #[automatically_derived] - impl MontConfig<4usize> for FqConfig { - const GENERATOR: F = ark_ff::MontFp!("7"); - const MODULUS: B = BigInt([ - 4891460686036598785u64, - 2896914383306846353u64, - 13281191951274694749u64, - 3486998266802970665u64, - ]); - const TWO_ADIC_ROOT_OF_UNITY: F = ark_ff::MontFp!("1748695177688661943023146337482803886740723238769601073607632802312037301404" ); - - #[inline(always)] - fn mul_assign(a: &mut F, b: &F) { - { - if cfg!(all( - feature = "asm", - target_feature = "bmi2", - target_feature = "adx", - target_arch = "x86_64" - )) { - #[cfg(all( - feature = "asm", - target_feature = "bmi2", - target_feature = "adx", - target_arch = "x86_64" - ))] - #[allow(unsafe_code, unused_mut)] - ark_ff::x86_64_asm_mul!(4usize, (a.0).0, (b.0).0); - } else { - #[cfg(not(all( - feature = "asm", - target_feature = "bmi2", - target_feature = "adx", - target_arch = "x86_64" - )))] - { - let mut r = [0u64; 4usize]; - let mut carry1 = 0u64; - r[0] = fa::mac( - r[0], - (a.0).0[0], - (b.0).0[0usize], - &mut carry1, - ); - let k = r[0].wrapping_mul(Self::INV); - let mut carry2 = 0u64; - fa::mac_discard( - r[0], - k, - 4891460686036598785u64, - &mut carry2, - ); - r[1usize] = fa::mac_with_carry( - r[1usize], - (a.0).0[1usize], - (b.0).0[0usize], - &mut carry1, - ); - r[0usize] = fa::mac_with_carry( - r[1usize], - k, - 2896914383306846353u64, - &mut carry2, - ); - r[2usize] = fa::mac_with_carry( - r[2usize], - (a.0).0[2usize], - (b.0).0[0usize], - &mut carry1, - ); - r[1usize] = fa::mac_with_carry( - r[2usize], - k, - 13281191951274694749u64, - &mut carry2, - ); - r[3usize] = fa::mac_with_carry( - r[3usize], - (a.0).0[3usize], - (b.0).0[0usize], - &mut carry1, - ); - r[2usize] = fa::mac_with_carry( - r[3usize], - k, - 3486998266802970665u64, - &mut carry2, - ); - r[4usize - 1] = carry1 + carry2; - let mut carry1 = 0u64; - r[0] = fa::mac( - r[0], - (a.0).0[0], - (b.0).0[1usize], - &mut carry1, - ); - let k = r[0].wrapping_mul(Self::INV); - let mut carry2 = 0u64; - fa::mac_discard( - r[0], - k, - 4891460686036598785u64, - &mut carry2, - ); - r[1usize] = fa::mac_with_carry( - r[1usize], - (a.0).0[1usize], - (b.0).0[1usize], - &mut carry1, - ); - r[0usize] = fa::mac_with_carry( - r[1usize], - k, - 2896914383306846353u64, - &mut carry2, - ); - r[2usize] = fa::mac_with_carry( - r[2usize], - (a.0).0[2usize], - (b.0).0[1usize], - &mut carry1, - ); - r[1usize] = fa::mac_with_carry( - r[2usize], - k, - 13281191951274694749u64, - &mut carry2, - ); - r[3usize] = fa::mac_with_carry( - r[3usize], - (a.0).0[3usize], - (b.0).0[1usize], - &mut carry1, - ); - r[2usize] = fa::mac_with_carry( - r[3usize], - k, - 3486998266802970665u64, - &mut carry2, - ); - r[4usize - 1] = carry1 + carry2; - let mut carry1 = 0u64; - r[0] = fa::mac( - r[0], - (a.0).0[0], - (b.0).0[2usize], - &mut carry1, - ); - let k = r[0].wrapping_mul(Self::INV); - let mut carry2 = 0u64; - fa::mac_discard( - r[0], - k, - 4891460686036598785u64, - &mut carry2, - ); - r[1usize] = fa::mac_with_carry( - r[1usize], - (a.0).0[1usize], - (b.0).0[2usize], - &mut carry1, - ); - r[0usize] = fa::mac_with_carry( - r[1usize], - k, - 2896914383306846353u64, - &mut carry2, - ); - r[2usize] = fa::mac_with_carry( - r[2usize], - (a.0).0[2usize], - (b.0).0[2usize], - &mut carry1, - ); - r[1usize] = fa::mac_with_carry( - r[2usize], - k, - 13281191951274694749u64, - &mut carry2, - ); - r[3usize] = fa::mac_with_carry( - r[3usize], - (a.0).0[3usize], - (b.0).0[2usize], - &mut carry1, - ); - r[2usize] = fa::mac_with_carry( - r[3usize], - k, - 3486998266802970665u64, - &mut carry2, - ); - r[4usize - 1] = carry1 + carry2; - let mut carry1 = 0u64; - r[0] = fa::mac( - r[0], - (a.0).0[0], - (b.0).0[3usize], - &mut carry1, - ); - let k = r[0].wrapping_mul(Self::INV); - let mut carry2 = 0u64; - fa::mac_discard( - r[0], - k, - 4891460686036598785u64, - &mut carry2, - ); - r[1usize] = fa::mac_with_carry( - r[1usize], - (a.0).0[1usize], - (b.0).0[3usize], - &mut carry1, - ); - r[0usize] = fa::mac_with_carry( - r[1usize], - k, - 2896914383306846353u64, - &mut carry2, - ); - r[2usize] = fa::mac_with_carry( - r[2usize], - (a.0).0[2usize], - (b.0).0[3usize], - &mut carry1, - ); - r[1usize] = fa::mac_with_carry( - r[2usize], - k, - 13281191951274694749u64, - &mut carry2, - ); - r[3usize] = fa::mac_with_carry( - r[3usize], - (a.0).0[3usize], - (b.0).0[3usize], - &mut carry1, - ); - r[2usize] = fa::mac_with_carry( - r[3usize], - k, - 3486998266802970665u64, - &mut carry2, - ); - r[4usize - 1] = carry1 + carry2; - (a.0).0 = r; - } - } - } - __subtract_modulus(a); - } - } - - #[inline(always)] - fn __subtract_modulus(a: &mut F) { - if a.is_geq_modulus() { - __sub_with_borrow( - &mut a.0, - &BigInt([ - 4891460686036598785u64, - 2896914383306846353u64, - 13281191951274694749u64, - 3486998266802970665u64, - ]), - ); - } - } - - #[inline(always)] - fn __sub_with_borrow(a: &mut B, b: &B) -> bool { - use ark_ff::biginteger::arithmetic::sbb_for_sub_with_borrow as sbb; - let mut borrow = 0; - borrow = sbb(&mut a.0[0usize], b.0[0usize], borrow); - borrow = sbb(&mut a.0[1usize], b.0[1usize], borrow); - borrow = sbb(&mut a.0[2usize], b.0[2usize], borrow); - borrow = sbb(&mut a.0[3usize], b.0[3usize], borrow); - borrow != 0 - } -}; - -pub type FpBN256 = Fp256>; +// #[derive(MontConfig)] +// #[modulus = +// "21888242871839275222246405745257275088548364400416034343698204186575808495617" +// ] #[generator = "7"] +// pub struct FqConfig; +// pub type FpBN256 = Fp256>; #[entrypoint] #[storage] @@ -306,22 +22,17 @@ struct PoseidonExample {} #[public] impl PoseidonExample { pub fn hash(&mut self, inputs: [U256; 2]) -> Result> { - let inputs: Vec<_> = inputs - .iter() - .map(|input| { - FpBN256::from_le_bytes_mod_order(&input.to_le_bytes_vec()) - }) - .collect(); + let mut hasher = Poseidon2Sponge::new(); - let mut res = FpBN256::ONE; - for _ in 0..1000 { - for input in inputs.iter() { - res *= input - } + for input in inputs.iter() { + let fp = + ScalarField::from_le_bytes_mod_order(&input.to_le_bytes_vec()); + hasher.absorb(&fp); } - let res = res.into_bigint().to_bytes_le(); + let hash = hasher.squeeze(); + let hash = hash.into_bigint().to_bytes_le(); - Ok(U256::from_le_slice(&res)) + Ok(U256::from_le_slice(&hash)) } } From 0981130e93ee407043bffa3c152de0b75dbc31c5 Mon Sep 17 00:00:00 2001 From: Alisander Qoshqosh Date: Sat, 11 Jan 2025 05:35:37 +0400 Subject: [PATCH 31/61] ++ --- examples/poseidon/src/lib.rs | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/examples/poseidon/src/lib.rs b/examples/poseidon/src/lib.rs index 289e7172d..c14bb3a05 100644 --- a/examples/poseidon/src/lib.rs +++ b/examples/poseidon/src/lib.rs @@ -7,6 +7,7 @@ use alloy_primitives::U256; use openzeppelin_crypto::{ arithmetic::{BigInt, BigInteger}, field::{instance::FpBN256, prime::PrimeField}, + poseidon2::{instance::bn256::BN256Params, Poseidon2}, }; use stylus_sdk::prelude::{entrypoint, public, storage}; @@ -17,24 +18,18 @@ struct PoseidonExample {} #[public] impl PoseidonExample { pub fn hash(&mut self, inputs: [U256; 2]) -> Result> { - let inputs: Vec<_> = inputs - .iter() - .map(|input| { - FpBN256::from_bigint(BigInt::from_bytes_le( - &input.to_le_bytes_vec(), - )) - }) - .collect(); + let mut hasher = Poseidon2::::new(); - let mut res = FpBN256::ONE; - for _ in 0..1000 { - for input in inputs.iter() { - res *= input - } + for input in inputs.iter() { + let fp = FpBN256::from_bigint(BigInt::from_bytes_le( + &input.to_le_bytes_vec(), + )); + hasher.absorb(&fp); } - let res = res.into_bigint().into_bytes_le(); + let hash = hasher.squeeze(); + let hash = hash.into_bigint().into_bytes_le(); - Ok(U256::from_le_slice(&res)) + Ok(U256::from_le_slice(&hash)) } } From 9e3165e987f96adcc2e59ba9fab662085df691f3 Mon Sep 17 00:00:00 2001 From: Alisander Qoshqosh Date: Sat, 11 Jan 2025 05:55:58 +0400 Subject: [PATCH 32/61] ++ --- examples/ark-ff/src/lib.rs | 279 ++-------------------------------- examples/oz-crypto/src/lib.rs | 7 +- 2 files changed, 16 insertions(+), 270 deletions(-) diff --git a/examples/ark-ff/src/lib.rs b/examples/ark-ff/src/lib.rs index 7cfbc973d..812e3f9b9 100644 --- a/examples/ark-ff/src/lib.rs +++ b/examples/ark-ff/src/lib.rs @@ -10,6 +10,14 @@ use ark_ff::{ }; use stylus_sdk::prelude::{entrypoint, public, storage}; +#[derive(MontConfig)] +#[modulus = "21888242871839275222246405745257275088548364400416034343698204186575808495617"] +#[generator = "7"] +pub struct FqConfig; +/// Bn245 field +pub type ScalarField = Fp256>; + +/* pub struct FqConfig; const _: () = { @@ -30,275 +38,9 @@ const _: () = { 3486998266802970665u64, ]); const TWO_ADIC_ROOT_OF_UNITY: F = ark_ff::MontFp!("1748695177688661943023146337482803886740723238769601073607632802312037301404" ); - - #[inline(always)] - fn mul_assign(a: &mut F, b: &F) { - { - if cfg!(all( - feature = "asm", - target_feature = "bmi2", - target_feature = "adx", - target_arch = "x86_64" - )) { - #[cfg(all( - feature = "asm", - target_feature = "bmi2", - target_feature = "adx", - target_arch = "x86_64" - ))] - #[allow(unsafe_code, unused_mut)] - ark_ff::x86_64_asm_mul!(4usize, (a.0).0, (b.0).0); - } else { - #[cfg(not(all( - feature = "asm", - target_feature = "bmi2", - target_feature = "adx", - target_arch = "x86_64" - )))] - { - let mut r = [0u64; 4usize]; - let mut carry1 = 0u64; - r[0] = fa::mac( - r[0], - (a.0).0[0], - (b.0).0[0usize], - &mut carry1, - ); - let k = r[0].wrapping_mul(Self::INV); - let mut carry2 = 0u64; - fa::mac_discard( - r[0], - k, - 4891460686036598785u64, - &mut carry2, - ); - r[1usize] = fa::mac_with_carry( - r[1usize], - (a.0).0[1usize], - (b.0).0[0usize], - &mut carry1, - ); - r[0usize] = fa::mac_with_carry( - r[1usize], - k, - 2896914383306846353u64, - &mut carry2, - ); - r[2usize] = fa::mac_with_carry( - r[2usize], - (a.0).0[2usize], - (b.0).0[0usize], - &mut carry1, - ); - r[1usize] = fa::mac_with_carry( - r[2usize], - k, - 13281191951274694749u64, - &mut carry2, - ); - r[3usize] = fa::mac_with_carry( - r[3usize], - (a.0).0[3usize], - (b.0).0[0usize], - &mut carry1, - ); - r[2usize] = fa::mac_with_carry( - r[3usize], - k, - 3486998266802970665u64, - &mut carry2, - ); - r[4usize - 1] = carry1 + carry2; - let mut carry1 = 0u64; - r[0] = fa::mac( - r[0], - (a.0).0[0], - (b.0).0[1usize], - &mut carry1, - ); - let k = r[0].wrapping_mul(Self::INV); - let mut carry2 = 0u64; - fa::mac_discard( - r[0], - k, - 4891460686036598785u64, - &mut carry2, - ); - r[1usize] = fa::mac_with_carry( - r[1usize], - (a.0).0[1usize], - (b.0).0[1usize], - &mut carry1, - ); - r[0usize] = fa::mac_with_carry( - r[1usize], - k, - 2896914383306846353u64, - &mut carry2, - ); - r[2usize] = fa::mac_with_carry( - r[2usize], - (a.0).0[2usize], - (b.0).0[1usize], - &mut carry1, - ); - r[1usize] = fa::mac_with_carry( - r[2usize], - k, - 13281191951274694749u64, - &mut carry2, - ); - r[3usize] = fa::mac_with_carry( - r[3usize], - (a.0).0[3usize], - (b.0).0[1usize], - &mut carry1, - ); - r[2usize] = fa::mac_with_carry( - r[3usize], - k, - 3486998266802970665u64, - &mut carry2, - ); - r[4usize - 1] = carry1 + carry2; - let mut carry1 = 0u64; - r[0] = fa::mac( - r[0], - (a.0).0[0], - (b.0).0[2usize], - &mut carry1, - ); - let k = r[0].wrapping_mul(Self::INV); - let mut carry2 = 0u64; - fa::mac_discard( - r[0], - k, - 4891460686036598785u64, - &mut carry2, - ); - r[1usize] = fa::mac_with_carry( - r[1usize], - (a.0).0[1usize], - (b.0).0[2usize], - &mut carry1, - ); - r[0usize] = fa::mac_with_carry( - r[1usize], - k, - 2896914383306846353u64, - &mut carry2, - ); - r[2usize] = fa::mac_with_carry( - r[2usize], - (a.0).0[2usize], - (b.0).0[2usize], - &mut carry1, - ); - r[1usize] = fa::mac_with_carry( - r[2usize], - k, - 13281191951274694749u64, - &mut carry2, - ); - r[3usize] = fa::mac_with_carry( - r[3usize], - (a.0).0[3usize], - (b.0).0[2usize], - &mut carry1, - ); - r[2usize] = fa::mac_with_carry( - r[3usize], - k, - 3486998266802970665u64, - &mut carry2, - ); - r[4usize - 1] = carry1 + carry2; - let mut carry1 = 0u64; - r[0] = fa::mac( - r[0], - (a.0).0[0], - (b.0).0[3usize], - &mut carry1, - ); - let k = r[0].wrapping_mul(Self::INV); - let mut carry2 = 0u64; - fa::mac_discard( - r[0], - k, - 4891460686036598785u64, - &mut carry2, - ); - r[1usize] = fa::mac_with_carry( - r[1usize], - (a.0).0[1usize], - (b.0).0[3usize], - &mut carry1, - ); - r[0usize] = fa::mac_with_carry( - r[1usize], - k, - 2896914383306846353u64, - &mut carry2, - ); - r[2usize] = fa::mac_with_carry( - r[2usize], - (a.0).0[2usize], - (b.0).0[3usize], - &mut carry1, - ); - r[1usize] = fa::mac_with_carry( - r[2usize], - k, - 13281191951274694749u64, - &mut carry2, - ); - r[3usize] = fa::mac_with_carry( - r[3usize], - (a.0).0[3usize], - (b.0).0[3usize], - &mut carry1, - ); - r[2usize] = fa::mac_with_carry( - r[3usize], - k, - 3486998266802970665u64, - &mut carry2, - ); - r[4usize - 1] = carry1 + carry2; - (a.0).0 = r; - } - } - } - __subtract_modulus(a); - } - } - - #[inline(always)] - fn __subtract_modulus(a: &mut F) { - if a.is_geq_modulus() { - __sub_with_borrow( - &mut a.0, - &BigInt([ - 4891460686036598785u64, - 2896914383306846353u64, - 13281191951274694749u64, - 3486998266802970665u64, - ]), - ); - } - } - - #[inline(always)] - fn __sub_with_borrow(a: &mut B, b: &B) -> bool { - use ark_ff::biginteger::arithmetic::sbb_for_sub_with_borrow as sbb; - let mut borrow = 0; - borrow = sbb(&mut a.0[0usize], b.0[0usize], borrow); - borrow = sbb(&mut a.0[1usize], b.0[1usize], borrow); - borrow = sbb(&mut a.0[2usize], b.0[2usize], borrow); - borrow = sbb(&mut a.0[3usize], b.0[3usize], borrow); - borrow != 0 } }; +*/ pub type FpBN256 = Fp256>; @@ -319,7 +61,8 @@ impl MathExample { let mut res = FpBN256::ONE; for _ in 0..1000 { for input in inputs.iter() { - res *= input; + res += input; + res.square_in_place(); } } diff --git a/examples/oz-crypto/src/lib.rs b/examples/oz-crypto/src/lib.rs index 689e438a6..cf7233818 100644 --- a/examples/oz-crypto/src/lib.rs +++ b/examples/oz-crypto/src/lib.rs @@ -6,7 +6,9 @@ use alloc::vec::Vec; use alloy_primitives::U256; use openzeppelin_crypto::{ arithmetic::{BigInt, BigInteger}, - field::{instance::FpBN256, prime::PrimeField, Field}, + field::{ + group::AdditiveGroup, instance::FpBN256, prime::PrimeField, Field, + }, }; use stylus_sdk::prelude::{entrypoint, public, storage}; @@ -29,7 +31,8 @@ impl MathExample { let mut res = FpBN256::ONE; for _ in 0..1000 { for input in inputs.iter() { - res *= input; + res += input; + res.square_in_place(); } } From a1e689f6ac19a6f54126d65ff1adb9ffe04b7b83 Mon Sep 17 00:00:00 2001 From: Alisander Qoshqosh Date: Sat, 11 Jan 2025 06:29:21 +0400 Subject: [PATCH 33/61] ++ --- lib/crypto/src/field/fp.rs | 65 +++----------------------------------- 1 file changed, 4 insertions(+), 61 deletions(-) diff --git a/lib/crypto/src/field/fp.rs b/lib/crypto/src/field/fp.rs index f094bd143..5228af2bc 100644 --- a/lib/crypto/src/field/fp.rs +++ b/lib/crypto/src/field/fp.rs @@ -46,6 +46,7 @@ pub trait FpParams: Send + Sync + 'static + Sized { /// `MODULUS - 1`. const GENERATOR: Fp; + // TODO#q: remove CAN_USE_NO_CARRY_MUL_OPT const CAN_USE_NO_CARRY_MUL_OPT: bool = can_use_no_carry_mul_optimization::(); @@ -126,68 +127,9 @@ pub trait FpParams: Send + Sync + 'static + Sized { } /// Set `a *= a`. - #[ark_ff_macros::unroll_for_loops(6)] + #[inline(always)] fn square_in_place(a: &mut Fp) { - if N == 1 { - // We default to multiplying with `a` using the `Mul` impl - // for the N == 1 case - *a *= *a; - return; - } - - let mut r = crate::const_helpers::MulBuffer::::zeroed(); - - let mut carry = 0; - for i in 0..(N - 1) { - for j in (i + 1)..N { - r[i + j] = arithmetic::mac_with_carry( - r[i + j], - (a.0).0[i], - (a.0).0[j], - &mut carry, - ); - } - r.b1[i] = carry; - carry = 0; - } - - r.b1[N - 1] = r.b1[N - 2] >> 63; - for i in 2..(2 * N - 1) { - r[2 * N - i] = (r[2 * N - i] << 1) | (r[2 * N - (i + 1)] >> 63); - } - r.b0[1] <<= 1; - - for i in 0..N { - r[2 * i] = arithmetic::mac_with_carry( - r[2 * i], - (a.0).0[i], - (a.0).0[i], - &mut carry, - ); - carry = arithmetic::adc(&mut r[2 * i + 1], 0, carry); - } - // Montgomery reduction - let mut carry2 = 0; - for i in 0..N { - let k = r[i].wrapping_mul(Self::INV); - carry = 0; - arithmetic::mac_discard(r[i], k, Self::MODULUS.0[0], &mut carry); - for j in 1..N { - r[j + i] = arithmetic::mac_with_carry( - r[j + i], - k, - Self::MODULUS.0[j], - &mut carry, - ); - } - carry2 = arithmetic::adc(&mut r.b1[i], carry, carry2); - } - (a.0).0.copy_from_slice(&r.b1); - if Self::MODULUS_HAS_SPARE_BIT { - a.subtract_modulus(); - } else { - a.subtract_modulus_with_carry(carry2 != 0); - } + Self::mul_assign(a, &a.clone()); } /// Compute `a^{-1}` if `a` is not zero. @@ -677,6 +619,7 @@ impl, const N: usize> Field for Fp { temp } + #[inline] fn square_in_place(&mut self) -> &mut Self { P::square_in_place(self); self From fa075db6924150d6228252dba326047f413425c1 Mon Sep 17 00:00:00 2001 From: Alisander Qoshqosh Date: Sat, 11 Jan 2025 07:18:13 +0400 Subject: [PATCH 34/61] ++ --- examples/ark-ff/src/lib.rs | 8 +++++++- examples/oz-crypto/src/lib.rs | 8 +++++++- lib/crypto/src/arithmetic/mod.rs | 2 ++ lib/crypto/src/field/fp.rs | 19 ++++++++++++++----- 4 files changed, 30 insertions(+), 7 deletions(-) diff --git a/examples/ark-ff/src/lib.rs b/examples/ark-ff/src/lib.rs index 812e3f9b9..483e47185 100644 --- a/examples/ark-ff/src/lib.rs +++ b/examples/ark-ff/src/lib.rs @@ -62,7 +62,13 @@ impl MathExample { for _ in 0..1000 { for input in inputs.iter() { res += input; - res.square_in_place(); + // res.inverse(); + // res += input; + // res.inverse(); + // res.double_in_place(); + // res.inverse(); + // res.double_in_place(); + // res.inverse(); } } diff --git a/examples/oz-crypto/src/lib.rs b/examples/oz-crypto/src/lib.rs index cf7233818..74ac551bc 100644 --- a/examples/oz-crypto/src/lib.rs +++ b/examples/oz-crypto/src/lib.rs @@ -32,7 +32,13 @@ impl MathExample { for _ in 0..1000 { for input in inputs.iter() { res += input; - res.square_in_place(); + // res.inverse(); + // res += input; + // res.inverse(); + // res.double_in_place(); + // res.inverse(); + // res.double_in_place(); + // res.inverse(); } } diff --git a/lib/crypto/src/arithmetic/mod.rs b/lib/crypto/src/arithmetic/mod.rs index e0b53e62f..eaf527e40 100644 --- a/lib/crypto/src/arithmetic/mod.rs +++ b/lib/crypto/src/arithmetic/mod.rs @@ -255,6 +255,7 @@ impl BigInt { } // TODO#q: rename to checked_add? + #[inline(always)] #[ark_ff_macros::unroll_for_loops(6)] pub(crate) fn add_with_carry(&mut self, other: &Self) -> bool { let mut carry = false; @@ -266,6 +267,7 @@ impl BigInt { carry } + #[inline(always)] #[ark_ff_macros::unroll_for_loops(6)] pub(crate) fn sub_with_borrow(&mut self, other: &Self) -> bool { let mut borrow = false; diff --git a/lib/crypto/src/field/fp.rs b/lib/crypto/src/field/fp.rs index 5228af2bc..59ba666b1 100644 --- a/lib/crypto/src/field/fp.rs +++ b/lib/crypto/src/field/fp.rs @@ -64,6 +64,7 @@ pub trait FpParams: Send + Sync + 'static + Sized { const R2: BigInt = Self::MODULUS.montgomery_r2(); /// Set `a += b`. + #[inline(always)] fn add_assign(a: &mut Fp, b: &Fp) { // This cannot exceed the backing capacity. let c = a.0.add_with_carry(&b.0); @@ -76,6 +77,7 @@ pub trait FpParams: Send + Sync + 'static + Sized { } /// Set `a -= b`. + #[inline(always)] fn sub_assign(a: &mut Fp, b: &Fp) { // If `other` is larger than `self`, add the modulus to self first. if b.0 > a.0 { @@ -85,6 +87,7 @@ pub trait FpParams: Send + Sync + 'static + Sized { } /// Set `a = a + a`. + #[inline(always)] fn double_in_place(a: &mut Fp) { // This cannot exceed the backing capacity. let c = a.0.mul2(); @@ -97,6 +100,7 @@ pub trait FpParams: Send + Sync + 'static + Sized { } /// Set `a = -a`; + #[inline(always)] fn neg_in_place(a: &mut Fp) { if !a.is_zero() { let mut tmp = Self::MODULUS; @@ -134,6 +138,7 @@ pub trait FpParams: Send + Sync + 'static + Sized { /// Compute `a^{-1}` if `a` is not zero. #[must_use] + #[inline(always)] fn inverse(a: &Fp) -> Option> { if a.is_zero() { return None; @@ -143,7 +148,7 @@ pub trait FpParams: Send + Sync + 'static + Sized { // to Cryptography // Algorithm 16 (BEA for Inversion in Fp) - let one = BigInt::from(1u64); + let one = BigInt::ONE; let mut u = a.0; let mut v = Self::MODULUS; @@ -151,6 +156,8 @@ pub trait FpParams: Send + Sync + 'static + Sized { let mut c = Fp::zero(); while u != one && v != one { + // TODO#q: why code here duplicated? + // TODO#q: Inverse consumes incredible ammount of gas while u.is_even() { u.div2(); @@ -216,6 +223,7 @@ pub trait FpParams: Send + Sync + 'static + Sized { // TODO#q: almost zero advantage of this loop unroll, and it bloats binary // size by 0.3kb #[ark_ff_macros::unroll_for_loops(6)] + #[inline(always)] fn into_bigint(a: Fp) -> BigInt { let mut r = (a.0).0; // Montgomery Reduction @@ -344,19 +352,19 @@ impl, const N: usize> Fp { /// Unlike [`Self::new`], this method does not perform Montgomery reduction. /// This method should be used only when constructing an element from an /// integer that has already been put in Montgomery form. - #[inline] #[must_use] + #[inline(always)] pub const fn new_unchecked(element: BigInt) -> Self { Self(element, PhantomData) } #[doc(hidden)] - #[inline] + #[inline(always)] pub fn is_geq_modulus(&self) -> bool { self.0 >= P::MODULUS } - #[inline] + #[inline(always)] fn subtract_modulus(&mut self) { if self.is_geq_modulus() { self.0.sub_with_borrow(&Self::MODULUS); @@ -410,7 +418,7 @@ impl, const N: usize> Fp { self.0.const_is_zero() } - #[inline] + #[inline(always)] fn subtract_modulus_with_carry(&mut self, carry: bool) { if carry || self.is_geq_modulus() { self.0.sub_with_borrow(&Self::MODULUS); @@ -652,6 +660,7 @@ impl, const N: usize> PrimeField for Fp { P::from_bigint(repr).unwrap() } + #[inline] fn into_bigint(self) -> BigInt { P::into_bigint(self) } From 7a5f7241dffc54957a0fc7b7d8f22a68b5cf0b7b Mon Sep 17 00:00:00 2001 From: Alisander Qoshqosh Date: Sat, 11 Jan 2025 08:20:32 +0400 Subject: [PATCH 35/61] remove ark-ff-macros --- Cargo.lock | 1 - lib/crypto/Cargo.toml | 1 - lib/crypto/src/arithmetic/mod.rs | 36 +++++++++++------------ lib/crypto/src/const_helpers.rs | 49 ++++++++++++++++++++++++++++++++ lib/crypto/src/field/fp.rs | 29 +++++++++---------- 5 files changed, 78 insertions(+), 38 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2ecb7d50d..70ba453e6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2964,7 +2964,6 @@ dependencies = [ name = "openzeppelin-crypto" version = "0.2.0-alpha.2" dependencies = [ - "ark-ff-macros 0.5.0", "educe", "hex-literal", "num-bigint", diff --git a/lib/crypto/Cargo.toml b/lib/crypto/Cargo.toml index 682cc2612..9163c0f06 100644 --- a/lib/crypto/Cargo.toml +++ b/lib/crypto/Cargo.toml @@ -15,7 +15,6 @@ zeroize.workspace = true educe.workspace = true hex-literal.workspace = true num-bigint.workspace = true -ark-ff-macros = "0.5.0" [dev-dependencies] rand.workspace = true diff --git a/lib/crypto/src/arithmetic/mod.rs b/lib/crypto/src/arithmetic/mod.rs index eaf527e40..7de6b2074 100644 --- a/lib/crypto/src/arithmetic/mod.rs +++ b/lib/crypto/src/arithmetic/mod.rs @@ -18,7 +18,9 @@ use num_traits::{ConstZero, Zero}; // }; use zeroize::Zeroize; -use crate::{adc, bits::BitIteratorBE, const_for, const_modulo, sbb}; +use crate::{ + adc, bits::BitIteratorBE, const_for, const_modulo, sbb, unroll6_for, +}; pub type Limb = u64; pub type WideLimb = u128; @@ -256,26 +258,24 @@ impl BigInt { // TODO#q: rename to checked_add? #[inline(always)] - #[ark_ff_macros::unroll_for_loops(6)] pub(crate) fn add_with_carry(&mut self, other: &Self) -> bool { let mut carry = false; - for i in 0..N { + unroll6_for!((i in 0..N) { carry = adc_for_add_with_carry(&mut self.0[i], other.0[i], carry); - } + }); carry } #[inline(always)] - #[ark_ff_macros::unroll_for_loops(6)] pub(crate) fn sub_with_borrow(&mut self, other: &Self) -> bool { let mut borrow = false; - for i in 0..N { + unroll6_for!((i in 0..N) { borrow = sbb_for_sub_with_borrow(&mut self.0[i], other.0[i], borrow); - } + }); borrow } @@ -529,30 +529,26 @@ impl Display for BigInt { impl Ord for BigInt { #[inline] - #[cfg_attr( - any(target_arch = "x86_64", target_family = "wasm"), - ark_ff_macros::unroll_for_loops(12) - )] fn cmp(&self, other: &Self) -> core::cmp::Ordering { use core::cmp::Ordering; - #[cfg(any(target_arch = "x86_64", target_family = "wasm"))] - for i in 0..N { + // #[cfg(any(target_arch = "x86_64", target_family = "wasm"))] + unroll6_for!((i in 0..N) { let a = &self.0[N - i - 1]; let b = &other.0[N - i - 1]; match a.cmp(b) { Ordering::Equal => {} order => return order, }; - } + }); // TODO#q: loop unroll actually faster here, and not bloat wasm // remove it - #[cfg(not(any(target_arch = "x86_64", target_family = "wasm")))] - for (a, b) in self.0.iter().rev().zip(other.0.iter().rev()) { - if let order @ (Ordering::Less | Ordering::Greater) = a.cmp(b) { - return order; - } - } + // #[cfg(not(any(target_arch = "x86_64", target_family = "wasm")))] + // for (a, b) in self.0.iter().rev().zip(other.0.iter().rev()) { + // if let order @ (Ordering::Less | Ordering::Greater) = a.cmp(b) { + // return order; + // } + // } Ordering::Equal } } diff --git a/lib/crypto/src/const_helpers.rs b/lib/crypto/src/const_helpers.rs index d8ad05965..6e3bb0480 100644 --- a/lib/crypto/src/const_helpers.rs +++ b/lib/crypto/src/const_helpers.rs @@ -10,6 +10,55 @@ macro_rules! const_for { } }}; } +#[macro_export] +macro_rules! unroll6_for { + (($i:ident in $start:tt.. $end:tt) $code:expr) => {{ + let mut $i = $start; + loop { + if $i < $end { + $code + } else { + break; + } + $i += 1; + + if $i < $end { + $code + } else { + break; + } + $i += 1; + + if $i < $end { + $code + } else { + break; + } + $i += 1; + + if $i < $end { + $code + } else { + break; + } + $i += 1; + + if $i < $end { + $code + } else { + break; + } + $i += 1; + + if $i < $end { + $code + } else { + break; + } + $i += 1; + } + }}; +} #[macro_export] macro_rules! sbb { diff --git a/lib/crypto/src/field/fp.rs b/lib/crypto/src/field/fp.rs index 59ba666b1..a417daa32 100644 --- a/lib/crypto/src/field/fp.rs +++ b/lib/crypto/src/field/fp.rs @@ -30,7 +30,7 @@ use crate::{ arithmetic::{BigInt, BigInteger, Limb}, const_for, field::{group::AdditiveGroup, prime::PrimeField, Field}, - mac, mac_with_carry, + mac, mac_with_carry, unroll6_for, }; /// A trait that specifies the configuration of a prime field. @@ -115,7 +115,6 @@ pub trait FpParams: Send + Sync + 'static + Sized { /// [here](https://hackmd.io/@gnark/modular_multiplication) if /// `Self::MODULUS` has (a) a non-zero MSB, and (b) at least one /// zero bit in the rest of the modulus. - #[ark_ff_macros::unroll_for_loops(6)] #[inline(always)] fn mul_assign(a: &mut Fp, b: &Fp) { // Alternative implementation @@ -222,7 +221,6 @@ pub trait FpParams: Send + Sync + 'static + Sized { #[must_use] // TODO#q: almost zero advantage of this loop unroll, and it bloats binary // size by 0.3kb - #[ark_ff_macros::unroll_for_loops(6)] #[inline(always)] fn into_bigint(a: Fp) -> BigInt { let mut r = (a.0).0; @@ -232,14 +230,14 @@ pub trait FpParams: Send + Sync + 'static + Sized { let mut carry = 0; arithmetic::mac_with_carry(r[i], k, Self::MODULUS.0[0], &mut carry); - for j in 1..N { + unroll6_for!((j in 1..N) { r[(j + i) % N] = arithmetic::mac_with_carry( r[(j + i) % N], k, Self::MODULUS.0[j], &mut carry, ); - } + }); r[i % N] = carry; } @@ -465,13 +463,12 @@ impl, const N: usize> Fp { (carry2 != 0, self) } - #[ark_ff_macros::unroll_for_loops(6)] #[inline(always)] fn mul_without_cond_subtract(mut self, other: &Self) -> (bool, Self) { let (mut lo, mut hi) = ([0u64; N], [0u64; N]); - for i in 0..N { + unroll6_for!((i in 0..N) { let mut carry = 0; - for j in 0..N { + unroll6_for!((j in 0..N) { let k = i + j; if k >= N { hi[k - N] = mac_with_carry!( @@ -488,16 +485,16 @@ impl, const N: usize> Fp { &mut carry ); } - } + }); hi[i] = carry; - } + }); // Montgomery reduction let mut carry2 = 0; - for i in 0..N { + unroll6_for!((i in 0..N) { let tmp = lo[i].wrapping_mul(P::INV); let mut carry; mac!(lo[i], tmp, P::MODULUS.0[0], &mut carry); - for j in 1..N { + unroll6_for!((j in 1..N) { let k = i + j; if k >= N { hi[k - N] = mac_with_carry!( @@ -514,13 +511,13 @@ impl, const N: usize> Fp { &mut carry ); } - } + }); hi[i] = adc!(hi[i], carry, &mut carry2); - } + }); - for i in 0..N { + unroll6_for!((i in 0..N) { (self.0).0[i] = hi[i]; - } + }); (carry2 != 0, self) } From bf2b2aeb609cdee4a315b100e61f254fd095e4d9 Mon Sep 17 00:00:00 2001 From: Alisander Qoshqosh Date: Sat, 11 Jan 2025 08:30:40 +0400 Subject: [PATCH 36/61] ++ --- lib/crypto/src/arithmetic/mod.rs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/lib/crypto/src/arithmetic/mod.rs b/lib/crypto/src/arithmetic/mod.rs index 7de6b2074..51373783b 100644 --- a/lib/crypto/src/arithmetic/mod.rs +++ b/lib/crypto/src/arithmetic/mod.rs @@ -531,7 +531,6 @@ impl Ord for BigInt { #[inline] fn cmp(&self, other: &Self) -> core::cmp::Ordering { use core::cmp::Ordering; - // #[cfg(any(target_arch = "x86_64", target_family = "wasm"))] unroll6_for!((i in 0..N) { let a = &self.0[N - i - 1]; let b = &other.0[N - i - 1]; @@ -541,14 +540,6 @@ impl Ord for BigInt { }; }); - // TODO#q: loop unroll actually faster here, and not bloat wasm - // remove it - // #[cfg(not(any(target_arch = "x86_64", target_family = "wasm")))] - // for (a, b) in self.0.iter().rev().zip(other.0.iter().rev()) { - // if let order @ (Ordering::Less | Ordering::Greater) = a.cmp(b) { - // return order; - // } - // } Ordering::Equal } } From 6672a06c52ee665d51058fb3964bb8164d365b8b Mon Sep 17 00:00:00 2001 From: Alisander Qoshqosh Date: Sat, 11 Jan 2025 08:44:55 +0400 Subject: [PATCH 37/61] ++ --- lib/crypto/src/const_helpers.rs | 60 +++++++++++---------------------- 1 file changed, 19 insertions(+), 41 deletions(-) diff --git a/lib/crypto/src/const_helpers.rs b/lib/crypto/src/const_helpers.rs index 6e3bb0480..318b20540 100644 --- a/lib/crypto/src/const_helpers.rs +++ b/lib/crypto/src/const_helpers.rs @@ -2,7 +2,7 @@ use core::ops::{Index, IndexMut}; #[macro_export] macro_rules! const_for { - (($i:ident in $start:tt..$end:tt) $code:expr ) => {{ + (($i:ident in $start:tt..$end:tt) $code:expr ) => {{ let mut $i = $start; while $i < $end { $code @@ -10,53 +10,31 @@ macro_rules! const_for { } }}; } + #[macro_export] macro_rules! unroll6_for { (($i:ident in $start:tt.. $end:tt) $code:expr) => {{ let mut $i = $start; loop { - if $i < $end { - $code - } else { - break; - } - $i += 1; - - if $i < $end { - $code - } else { - break; - } - $i += 1; - - if $i < $end { - $code - } else { - break; - } - $i += 1; - - if $i < $end { - $code - } else { - break; - } - $i += 1; - - if $i < $end { - $code - } else { - break; - } - $i += 1; + $crate::cycle!($i, $end, $code); + $crate::cycle!($i, $end, $code); + $crate::cycle!($i, $end, $code); + $crate::cycle!($i, $end, $code); + $crate::cycle!($i, $end, $code); + $crate::cycle!($i, $end, $code); + } + }}; +} - if $i < $end { - $code - } else { - break; - } - $i += 1; +#[macro_export] +macro_rules! cycle { + ($i:ident, $end:tt, $code:expr) => {{ + if $i < $end { + $code + } else { + break; } + $i += 1; }}; } From cd1261bce7a40d069c18151b708842030c22879d Mon Sep 17 00:00:00 2001 From: Alisander Qoshqosh Date: Sat, 11 Jan 2025 08:52:39 +0400 Subject: [PATCH 38/61] ++ --- lib/crypto/src/const_helpers.rs | 48 ++++++++++++++++++++++++++++++--- 1 file changed, 44 insertions(+), 4 deletions(-) diff --git a/lib/crypto/src/const_helpers.rs b/lib/crypto/src/const_helpers.rs index 318b20540..0afe04f0f 100644 --- a/lib/crypto/src/const_helpers.rs +++ b/lib/crypto/src/const_helpers.rs @@ -2,11 +2,34 @@ use core::ops::{Index, IndexMut}; #[macro_export] macro_rules! const_for { - (($i:ident in $start:tt..$end:tt) $code:expr ) => {{ + (($i:ident in $start:tt.. $end:tt) $code:expr) => {{ let mut $i = $start; - while $i < $end { - $code - $i += 1; + loop { + $crate::cycle!($i, $end, $code); + } + }}; +} + +#[macro_export] +macro_rules! unroll2_for { + (($i:ident in $start:tt.. $end:tt) $code:expr) => {{ + let mut $i = $start; + loop { + $crate::cycle!($i, $end, $code); + $crate::cycle!($i, $end, $code); + } + }}; +} + +#[macro_export] +macro_rules! unroll4_for { + (($i:ident in $start:tt.. $end:tt) $code:expr) => {{ + let mut $i = $start; + loop { + $crate::cycle!($i, $end, $code); + $crate::cycle!($i, $end, $code); + $crate::cycle!($i, $end, $code); + $crate::cycle!($i, $end, $code); } }}; } @@ -26,6 +49,23 @@ macro_rules! unroll6_for { }}; } +#[macro_export] +macro_rules! unroll8_for { + (($i:ident in $start:tt.. $end:tt) $code:expr) => {{ + let mut $i = $start; + loop { + $crate::cycle!($i, $end, $code); + $crate::cycle!($i, $end, $code); + $crate::cycle!($i, $end, $code); + $crate::cycle!($i, $end, $code); + $crate::cycle!($i, $end, $code); + $crate::cycle!($i, $end, $code); + $crate::cycle!($i, $end, $code); + $crate::cycle!($i, $end, $code); + } + }}; +} + #[macro_export] macro_rules! cycle { ($i:ident, $end:tt, $code:expr) => {{ From 9f1cf09078b58843f1839360b746d2789ab145ea Mon Sep 17 00:00:00 2001 From: Alisander Qoshqosh Date: Sat, 11 Jan 2025 09:17:29 +0400 Subject: [PATCH 39/61] ++ --- lib/crypto/src/poseidon2/mod.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/lib/crypto/src/poseidon2/mod.rs b/lib/crypto/src/poseidon2/mod.rs index ade136014..3f1a6be88 100644 --- a/lib/crypto/src/poseidon2/mod.rs +++ b/lib/crypto/src/poseidon2/mod.rs @@ -42,6 +42,7 @@ impl, F: PrimeField> Default for Poseidon2 { impl, F: PrimeField> Poseidon2 { /// Create a new Poseidon sponge. #[must_use] + #[inline] pub fn new() -> Self { Self { phantom: core::marker::PhantomData, @@ -81,6 +82,7 @@ impl, F: PrimeField> Poseidon2 { /// # Panics /// /// May panic if absorbing while squeezing. + #[inline] pub fn absorb(&mut self, elem: &F) { if let Mode::Squeezing = self.mode { panic!("cannot absorb while squeezing"); @@ -96,6 +98,7 @@ impl, F: PrimeField> Poseidon2 { } /// Absorb batch of elements into the sponge. + #[inline] pub fn absorb_batch(&mut self, elems: &[F]) { for elem in elems { self.absorb(elem); @@ -103,6 +106,7 @@ impl, F: PrimeField> Poseidon2 { } /// Permute elements in the sponge. + #[inline] pub fn permute(&mut self) { // Linear layer at the beginning. self.matmul_external(); @@ -124,6 +128,7 @@ impl, F: PrimeField> Poseidon2 { } /// Apply external round to the state. + #[inline] fn external_round(&mut self, round: usize) { self.add_rc_external(round); self.apply_sbox_external(); @@ -131,6 +136,7 @@ impl, F: PrimeField> Poseidon2 { } /// Apply internal round to the state. + #[inline] fn internal_round(&mut self, round: usize) { self.add_rc_internal(round); self.apply_sbox_internal(); @@ -138,6 +144,7 @@ impl, F: PrimeField> Poseidon2 { } /// Squeeze a single element from the sponge. + #[inline] pub fn squeeze(&mut self) -> F { if self.mode == Mode::Absorbing || self.index == Self::state_size() { self.permute(); @@ -151,11 +158,13 @@ impl, F: PrimeField> Poseidon2 { } /// Squeeze a batch of elements from the sponge. + #[inline] pub fn squeeze_batch(&mut self, n: usize) -> Vec { (0..n).map(|_| self.squeeze()).collect() } /// Apply sbox to the entire state in the external round. + #[inline] fn apply_sbox_external(&mut self) { for elem in &mut self.state { *elem = elem.pow(P::D); @@ -163,12 +172,14 @@ impl, F: PrimeField> Poseidon2 { } /// Apply sbox to the first element in the internal round. + #[inline] fn apply_sbox_internal(&mut self) { self.state[0] = self.state[0].pow(P::D); } /// Apply the external MDS matrix `M_E` to the state. #[allow(clippy::needless_range_loop)] + #[inline(always)] fn matmul_external(&mut self) { let t = Self::state_size(); match t { @@ -211,6 +222,7 @@ impl, F: PrimeField> Poseidon2 { } /// Apply the cheap 4x4 MDS matrix to each 4-element part of the state. + #[inline(always)] fn matmul_m4(&mut self) { let state = &mut self.state; let t = Self::state_size(); @@ -247,6 +259,7 @@ impl, F: PrimeField> Poseidon2 { } /// Apply the internal MDS matrix `M_I` to the state. + #[inline(always)] fn matmul_internal(&mut self) { let t = Self::state_size(); @@ -285,6 +298,7 @@ impl, F: PrimeField> Poseidon2 { } /// Add a round constant to the entire state in external round. + #[inline] fn add_rc_external(&mut self, round: usize) { for (a, b) in self.state.iter_mut().zip(P::ROUND_CONSTANTS[round].iter()) @@ -294,6 +308,7 @@ impl, F: PrimeField> Poseidon2 { } // Add a round constant to the first state element in internal round. + #[inline] fn add_rc_internal(&mut self, round: usize) { self.state[0] += P::ROUND_CONSTANTS[round][0]; } From d23eaf9bc85a11ae09e93df40abb4be71df2da18 Mon Sep 17 00:00:00 2001 From: Alisander Qoshqosh Date: Sat, 11 Jan 2025 09:30:13 +0400 Subject: [PATCH 40/61] ++ --- lib/crypto/src/field/fp.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/crypto/src/field/fp.rs b/lib/crypto/src/field/fp.rs index a417daa32..a479160e1 100644 --- a/lib/crypto/src/field/fp.rs +++ b/lib/crypto/src/field/fp.rs @@ -205,6 +205,7 @@ pub trait FpParams: Send + Sync + 'static + Sized { /// /// By the end element will be converted to a montgomery form and reduced. #[must_use] + #[inline] fn from_bigint(r: BigInt) -> Option> { let mut r = Fp::new_unchecked(r); if r.is_zero() { From 466ae78053360caf20161dbf1ba0e1b8297e7d4a Mon Sep 17 00:00:00 2001 From: Alisander Qoshqosh Date: Sat, 11 Jan 2025 09:32:33 +0400 Subject: [PATCH 41/61] ++ --- lib/crypto/src/field/fp.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/crypto/src/field/fp.rs b/lib/crypto/src/field/fp.rs index a479160e1..b43dca036 100644 --- a/lib/crypto/src/field/fp.rs +++ b/lib/crypto/src/field/fp.rs @@ -666,6 +666,7 @@ impl, const N: usize> PrimeField for Fp { impl, const N: usize> Ord for Fp { fn cmp(&self, other: &Self) -> Ordering { + // TODO#q: is it optimal to convert to bigint? self.into_bigint().cmp(&other.into_bigint()) } } From 24a87a0b360561f2feaac51b44fe86c3cd0f3118 Mon Sep 17 00:00:00 2001 From: Alisander Qoshqosh Date: Sat, 11 Jan 2025 09:59:07 +0400 Subject: [PATCH 42/61] ++ --- lib/crypto/src/const_helpers.rs | 56 --------------------------------- 1 file changed, 56 deletions(-) diff --git a/lib/crypto/src/const_helpers.rs b/lib/crypto/src/const_helpers.rs index 0afe04f0f..b236e2c00 100644 --- a/lib/crypto/src/const_helpers.rs +++ b/lib/crypto/src/const_helpers.rs @@ -1,5 +1,3 @@ -use core::ops::{Index, IndexMut}; - #[macro_export] macro_rules! const_for { (($i:ident in $start:tt.. $end:tt) $code:expr) => {{ @@ -184,57 +182,3 @@ impl R2Buffer { } } } - -/// A buffer to hold values of size 2 * N. This is mostly -/// a hack that's necessary until `generic_const_exprs` is stable. -#[derive(Copy, Clone)] -#[repr(C, align(8))] -pub(super) struct MulBuffer { - pub(super) b0: [u64; N], - pub(super) b1: [u64; N], -} - -impl MulBuffer { - const fn new(b0: [u64; N], b1: [u64; N]) -> Self { - Self { b0, b1 } - } - - pub(super) const fn zeroed() -> Self { - let b = [0u64; N]; - Self::new(b, b) - } - - #[inline(always)] - pub(super) const fn get(&self, index: usize) -> &u64 { - if index < N { - &self.b0[index] - } else { - &self.b1[index - N] - } - } - - #[inline(always)] - pub(super) fn get_mut(&mut self, index: usize) -> &mut u64 { - if index < N { - &mut self.b0[index] - } else { - &mut self.b1[index - N] - } - } -} - -impl Index for MulBuffer { - type Output = u64; - - #[inline(always)] - fn index(&self, index: usize) -> &Self::Output { - self.get(index) - } -} - -impl IndexMut for MulBuffer { - #[inline(always)] - fn index_mut(&mut self, index: usize) -> &mut Self::Output { - self.get_mut(index) - } -} From 08aed23a984fcd2bd894e1cb01ceeb9ea736c12a Mon Sep 17 00:00:00 2001 From: Alisander Qoshqosh Date: Mon, 13 Jan 2025 21:39:49 +0400 Subject: [PATCH 43/61] ++ --- lib/crypto/src/arithmetic/mod.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/crypto/src/arithmetic/mod.rs b/lib/crypto/src/arithmetic/mod.rs index 51373783b..52f4dbad5 100644 --- a/lib/crypto/src/arithmetic/mod.rs +++ b/lib/crypto/src/arithmetic/mod.rs @@ -1039,8 +1039,7 @@ pub const fn from_str_hex(s: &str) -> BigInt { #[must_use] pub const fn ct_mul(a: &BigInt, b: &BigInt) -> BigInt { let (low, high) = a.ct_mul_wide(b); - // TODO#q: uncomment this assertion - // assert!(!ct_eq(&high, &BigInt::::ZERO), "overflow on multiplication"); + assert!(ct_eq(&high, &BigInt::::ZERO), "overflow on multiplication"); low } @@ -1048,8 +1047,7 @@ pub const fn ct_mul(a: &BigInt, b: &BigInt) -> BigInt { #[must_use] pub const fn ct_add(a: &BigInt, b: &BigInt) -> BigInt { let (low, carry) = a.ct_adc(b, Limb::ZERO); - // TODO#q: uncomment this assertion - // assert!(carry != 0, "overflow on addition"); + assert!(carry == 0, "overflow on addition"); low } From 113b25fae373c0c374e2ecc114fc10faf24d8adc Mon Sep 17 00:00:00 2001 From: Alisander Qoshqosh Date: Mon, 13 Jan 2025 22:13:23 +0400 Subject: [PATCH 44/61] fix mul_assign bug --- lib/crypto/src/field/fp.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/crypto/src/field/fp.rs b/lib/crypto/src/field/fp.rs index b43dca036..1f1d2219e 100644 --- a/lib/crypto/src/field/fp.rs +++ b/lib/crypto/src/field/fp.rs @@ -123,9 +123,9 @@ pub trait FpParams: Send + Sync + 'static + Sized { *a = res; if Self::MODULUS_HAS_SPARE_BIT { - a.subtract_modulus_with_carry(carry); - } else { a.subtract_modulus(); + } else { + a.subtract_modulus_with_carry(carry); } } From 47c4bc9fa694f8fb6ed3f001fed23d9904c71af2 Mon Sep 17 00:00:00 2001 From: Alisander Qoshqosh Date: Mon, 13 Jan 2025 22:47:57 +0400 Subject: [PATCH 45/61] ++ --- lib/crypto/src/field/fp.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/lib/crypto/src/field/fp.rs b/lib/crypto/src/field/fp.rs index 1f1d2219e..47f0adf8c 100644 --- a/lib/crypto/src/field/fp.rs +++ b/lib/crypto/src/field/fp.rs @@ -220,25 +220,23 @@ pub trait FpParams: Send + Sync + 'static + Sized { /// Convert a field element to an integer less than [`Self::MODULUS`]. #[must_use] - // TODO#q: almost zero advantage of this loop unroll, and it bloats binary - // size by 0.3kb #[inline(always)] fn into_bigint(a: Fp) -> BigInt { let mut r = (a.0).0; // Montgomery Reduction - for i in 0..N { + for i in 1..N { let k = r[i].wrapping_mul(Self::INV); let mut carry = 0; arithmetic::mac_with_carry(r[i], k, Self::MODULUS.0[0], &mut carry); - unroll6_for!((j in 1..N) { + for j in 1..N { r[(j + i) % N] = arithmetic::mac_with_carry( r[(j + i) % N], k, Self::MODULUS.0[j], &mut carry, ); - }); + } r[i % N] = carry; } From bedd7d8f2514ff9ffb18899485bd0ec3b5433484 Mon Sep 17 00:00:00 2001 From: Alisander Qoshqosh Date: Tue, 14 Jan 2025 00:55:37 +0400 Subject: [PATCH 46/61] ++ --- lib/crypto/src/field/fp.rs | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/lib/crypto/src/field/fp.rs b/lib/crypto/src/field/fp.rs index 47f0adf8c..94e4c8cae 100644 --- a/lib/crypto/src/field/fp.rs +++ b/lib/crypto/src/field/fp.rs @@ -46,10 +46,6 @@ pub trait FpParams: Send + Sync + 'static + Sized { /// `MODULUS - 1`. const GENERATOR: Fp; - // TODO#q: remove CAN_USE_NO_CARRY_MUL_OPT - const CAN_USE_NO_CARRY_MUL_OPT: bool = - can_use_no_carry_mul_optimization::(); - const MODULUS_HAS_SPARE_BIT: bool = modulus_has_spare_bit::(); /// INV = -MODULUS^{-1} mod 2^64 @@ -265,19 +261,6 @@ pub const fn inv, const N: usize>() -> u64 { inv.wrapping_neg() } -#[inline] -pub const fn can_use_no_carry_mul_optimization< - T: FpParams, - const N: usize, ->() -> bool { - // Checking the modulus at compile time - let mut all_remaining_bits_are_one = T::MODULUS.0[N - 1] == u64::MAX >> 1; - const_for!((i in 1..N) { - all_remaining_bits_are_one &= T::MODULUS.0[N - i - 1] == u64::MAX; - }); - modulus_has_spare_bit::() && !all_remaining_bits_are_one -} - #[inline] pub const fn modulus_has_spare_bit, const N: usize>() -> bool { T::MODULUS.0[N - 1] >> 63 == 0 From 7b80194e4e31df5959fb9c591933f21d4e9145bf Mon Sep 17 00:00:00 2001 From: Alisander Qoshqosh Date: Tue, 14 Jan 2025 15:29:19 +0400 Subject: [PATCH 47/61] ++ --- lib/crypto/src/arithmetic/mod.rs | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/lib/crypto/src/arithmetic/mod.rs b/lib/crypto/src/arithmetic/mod.rs index 52f4dbad5..4536b83d3 100644 --- a/lib/crypto/src/arithmetic/mod.rs +++ b/lib/crypto/src/arithmetic/mod.rs @@ -9,6 +9,7 @@ use core::{ Shl, ShlAssign, Shr, ShrAssign, }, }; +use std::num::TryFromIntError; use num_bigint::BigUint; use num_traits::{ConstZero, Zero}; @@ -44,6 +45,7 @@ impl Default for BigInt { } impl BigInt { + pub const BITS: u32 = (N as u32) * Limb::BITS; pub const ONE: Self = Self::one(); pub const ZERO: Self = Self::zero(); @@ -572,6 +574,31 @@ impl AsRef<[u64]> for BigInt { } } +// TODO#q: implement conversions in as similar way to +// impl_try_from_upper_bounded!(u128 => u8, u16, u32, u64); as in std +/* +impl From for BigInt { + fn from(value: u128) -> Self { + let result = Limb::try_from(value); + if u128::BITS > BigInt::BITS { + panic!("u128 is too large to fit in BigInt"); + } + } +} + +impl TryFrom for BigInt { + type Error = TryFromIntError; + + fn try_from(value: u128) -> Result { + if u128::BITS > BigInt::BITS { + Limb::try_from(value).map(|limb| limb.into()) + } else { + unimplemented!() + } + } +} +*/ + impl From for BigInt { #[inline] fn from(val: u64) -> BigInt { From 267962718575a2f8bd3b93efdfe92219f12b3feb Mon Sep 17 00:00:00 2001 From: Alisander Qoshqosh Date: Tue, 14 Jan 2025 16:25:59 +0400 Subject: [PATCH 48/61] ++ --- lib/crypto/src/field/fp.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/lib/crypto/src/field/fp.rs b/lib/crypto/src/field/fp.rs index 94e4c8cae..d529fc290 100644 --- a/lib/crypto/src/field/fp.rs +++ b/lib/crypto/src/field/fp.rs @@ -23,7 +23,7 @@ use core::{ }; use educe::Educe; -use num_traits::{One, Zero}; +use num_traits::{ConstZero, One, Zero}; use crate::{ adc, arithmetic, @@ -1254,4 +1254,15 @@ mod tests { prop_assert_eq!(res, a.rem_euclid(MODULUS)); } } + + #[test] + fn check_mul() { + let a: i64 = 1; + let b: i64 = 1; + let res = Field64::from(a) * Field64::from(b); + let res: i128 = res.into(); + let a = i128::from(a); + let b = i128::from(b); + assert_eq!(res, (a * b).rem_euclid(MODULUS)); + } } From 2267f63328c4a078a1c676c7d844e4c44c9fd7e5 Mon Sep 17 00:00:00 2001 From: Alisander Qoshqosh Date: Tue, 14 Jan 2025 16:31:26 +0400 Subject: [PATCH 49/61] ++ --- lib/crypto/src/field/fp.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/crypto/src/field/fp.rs b/lib/crypto/src/field/fp.rs index d529fc290..1191df957 100644 --- a/lib/crypto/src/field/fp.rs +++ b/lib/crypto/src/field/fp.rs @@ -320,9 +320,13 @@ impl, const N: usize> Fp { /// /// Every element of the field should be represented as `GENERATOR^i` pub const GENERATOR: Fp = P::GENERATOR; + // TODO#q: remove + pub const INV: u64 = P::INV; /// Multiplicative identity of the field, i.e., the element `e` /// such that, for all elements `f` of the field, `e * f = f`. pub const ONE: Fp = Fp::new_unchecked(P::R); + // TODO#q: remove + pub const R: BigInt = P::R; /// Additive identity of the field, i.e., the element `e` /// such that, for all elements `f` of the field, `e + f = f`. pub const ZERO: Fp = Fp::new_unchecked(BigInt([0; N])); @@ -1257,6 +1261,8 @@ mod tests { #[test] fn check_mul() { + dbg!(Field64::R); + dbg!(Field64::INV); let a: i64 = 1; let b: i64 = 1; let res = Field64::from(a) * Field64::from(b); From fed6ef5adbb474f7a75229a6b4fce96be636c20b Mon Sep 17 00:00:00 2001 From: Alisander Qoshqosh Date: Tue, 14 Jan 2025 22:39:16 +0400 Subject: [PATCH 50/61] fix into_bigint conversion bug --- lib/crypto/src/field/fp.rs | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/lib/crypto/src/field/fp.rs b/lib/crypto/src/field/fp.rs index 1191df957..50e8e865b 100644 --- a/lib/crypto/src/field/fp.rs +++ b/lib/crypto/src/field/fp.rs @@ -206,8 +206,6 @@ pub trait FpParams: Send + Sync + 'static + Sized { let mut r = Fp::new_unchecked(r); if r.is_zero() { Some(r) - } else if r.is_geq_modulus() { - None } else { r *= &Fp::new_unchecked(Self::R2); Some(r) @@ -220,7 +218,7 @@ pub trait FpParams: Send + Sync + 'static + Sized { fn into_bigint(a: Fp) -> BigInt { let mut r = (a.0).0; // Montgomery Reduction - for i in 1..N { + for i in 0..N { let k = r[i].wrapping_mul(Self::INV); let mut carry = 0; @@ -541,6 +539,7 @@ impl, const N: usize> Fp { impl, const N: usize> Hash for Fp { fn hash(&self, state: &mut H) { + // TODO#q: implement hash for Fp unimplemented!() } } @@ -1258,17 +1257,4 @@ mod tests { prop_assert_eq!(res, a.rem_euclid(MODULUS)); } } - - #[test] - fn check_mul() { - dbg!(Field64::R); - dbg!(Field64::INV); - let a: i64 = 1; - let b: i64 = 1; - let res = Field64::from(a) * Field64::from(b); - let res: i128 = res.into(); - let a = i128::from(a); - let b = i128::from(b); - assert_eq!(res, (a * b).rem_euclid(MODULUS)); - } } From 26a465308ced66a8440c5bc4c027466bcc61109e Mon Sep 17 00:00:00 2001 From: Alisander Qoshqosh Date: Tue, 14 Jan 2025 22:40:28 +0400 Subject: [PATCH 51/61] ++ --- lib/crypto/src/arithmetic/mod.rs | 64 ++++++++++++++++---------------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/lib/crypto/src/arithmetic/mod.rs b/lib/crypto/src/arithmetic/mod.rs index 4536b83d3..098ef17cd 100644 --- a/lib/crypto/src/arithmetic/mod.rs +++ b/lib/crypto/src/arithmetic/mod.rs @@ -809,38 +809,38 @@ impl Not for BigInt { /// Defines a big integer with a constant length. pub trait BigInteger: -'static -+ Copy -+ Clone -+ Debug -+ Default -+ Display -+ Eq -+ Ord -+ Send -+ Sized -+ Sync -+ Zeroize -+ From -+ From -+ From -+ From -+ BitXorAssign -+ for<'a> BitXorAssign<&'a Self> -+ BitXor -+ for<'a> BitXor<&'a Self, Output=Self> -+ BitAndAssign -+ for<'a> BitAndAssign<&'a Self> -+ BitAnd -+ for<'a> BitAnd<&'a Self, Output=Self> -+ BitOrAssign -+ for<'a> BitOrAssign<&'a Self> -+ BitOr -+ for<'a> BitOr<&'a Self, Output=Self> -+ Shr // TODO#q: use usize instead of u32 -+ ShrAssign -+ Shl -+ ShlAssign + 'static + + Copy + + Clone + + Debug + + Default + + Display + + Eq + + Ord + + Send + + Sized + + Sync + + Zeroize + + From + + From + + From + + From + + BitXorAssign + + for<'a> BitXorAssign<&'a Self> + + BitXor + + for<'a> BitXor<&'a Self, Output=Self> + + BitAndAssign + + for<'a> BitAndAssign<&'a Self> + + BitAnd + + for<'a> BitAnd<&'a Self, Output=Self> + + BitOrAssign + + for<'a> BitOrAssign<&'a Self> + + BitOr + + for<'a> BitOr<&'a Self, Output=Self> + + Shr // TODO#q: use usize instead of u32 + + ShrAssign + + Shl + + ShlAssign { /// Number of `usize` limbs representing `Self`. const NUM_LIMBS: usize; From e4d92d3a8a37c09e9afdab46d9cf6876e6aae2a8 Mon Sep 17 00:00:00 2001 From: Alisander Qoshqosh Date: Tue, 14 Jan 2025 22:41:19 +0400 Subject: [PATCH 52/61] ++ --- lib/crypto/src/arithmetic/mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/crypto/src/arithmetic/mod.rs b/lib/crypto/src/arithmetic/mod.rs index 098ef17cd..9635a4437 100644 --- a/lib/crypto/src/arithmetic/mod.rs +++ b/lib/crypto/src/arithmetic/mod.rs @@ -9,7 +9,6 @@ use core::{ Shl, ShlAssign, Shr, ShrAssign, }, }; -use std::num::TryFromIntError; use num_bigint::BigUint; use num_traits::{ConstZero, Zero}; From 4b86b4f8b803259d450225898452570b353eca3e Mon Sep 17 00:00:00 2001 From: Alisander Qoshqosh Date: Wed, 15 Jan 2025 01:54:41 +0400 Subject: [PATCH 53/61] ++ --- lib/crypto/src/arithmetic/mod.rs | 62 +++++++++++++------------- lib/crypto/src/const_helpers.rs | 11 ----- lib/crypto/src/field/fp.rs | 74 ++++++++------------------------ 3 files changed, 49 insertions(+), 98 deletions(-) diff --git a/lib/crypto/src/arithmetic/mod.rs b/lib/crypto/src/arithmetic/mod.rs index 9635a4437..6d3c40ba6 100644 --- a/lib/crypto/src/arithmetic/mod.rs +++ b/lib/crypto/src/arithmetic/mod.rs @@ -402,30 +402,6 @@ impl BigInt { } } -/// Calculate a + b * c, returning the lower 64 bits of the result and setting -/// `carry` to the upper 64 bits. -#[inline(always)] -#[doc(hidden)] -pub fn mac(a: u64, b: u64, c: u64, carry: &mut u64) -> u64 { - let tmp = (a as u128) + widening_mul(b, c); - *carry = (tmp >> 64) as u64; - tmp as u64 -} - -pub const fn ct_mac_with_carry( - a: Limb, - b: Limb, - c: Limb, - carry: Limb, -) -> (Limb, Limb) { - let a = a as WideLimb; - let b = b as WideLimb; - let c = c as WideLimb; - let carry = carry as WideLimb; - let ret = a + (b * c) + carry; - (ret as Limb, (ret >> Limb::BITS) as Limb) -} - #[inline(always)] #[doc(hidden)] pub const fn widening_mul(a: u64, b: u64) -> u128 { @@ -448,23 +424,49 @@ pub const fn widening_mul(a: u64, b: u64) -> u128 { } } -/// Calculate a + b * c, discarding the lower 64 bits of the result and setting +// TODO#q: we need carrying_mac and mac + +/// Calculate a + b * c, returning the lower 64 bits of the result and setting /// `carry` to the upper 64 bits. #[inline(always)] #[doc(hidden)] -pub fn mac_discard(a: u64, b: u64, c: u64, carry: &mut u64) { +pub const fn mac(a: u64, b: u64, c: u64) -> (u64, u64) { let tmp = (a as u128) + widening_mul(b, c); - *carry = (tmp >> 64) as u64; + let carry = (tmp >> 64) as u64; + (tmp as u64, carry) } /// Calculate a + (b * c) + carry, returning the least significant digit /// and setting carry to the most significant digit. #[inline(always)] #[doc(hidden)] -pub fn mac_with_carry(a: u64, b: u64, c: u64, carry: &mut u64) -> u64 { - let tmp = (a as u128) + widening_mul(b, c) + (*carry as u128); +pub const fn mac_with_carry(a: u64, b: u64, c: u64, carry: u64) -> (u64, u64) { + let tmp = (a as u128) + widening_mul(b, c) + (carry as u128); + let carry = (tmp >> 64) as u64; + (tmp as u64, carry) +} + +pub const fn ct_mac_with_carry( + a: Limb, + b: Limb, + c: Limb, + carry: Limb, +) -> (Limb, Limb) { + let a = a as WideLimb; + let b = b as WideLimb; + let c = c as WideLimb; + let carry = carry as WideLimb; + let ret = a + (b * c) + carry; + (ret as Limb, (ret >> Limb::BITS) as Limb) +} + +/// Calculate a + b * c, discarding the lower 64 bits of the result and setting +/// `carry` to the upper 64 bits. +#[inline(always)] +#[doc(hidden)] +pub fn mac_discard(a: u64, b: u64, c: u64, carry: &mut u64) { + let tmp = (a as u128) + widening_mul(b, c); *carry = (tmp >> 64) as u64; - tmp as u64 } /// Sets a = a - b - borrow, and returns the borrow. diff --git a/lib/crypto/src/const_helpers.rs b/lib/crypto/src/const_helpers.rs index b236e2c00..00470af93 100644 --- a/lib/crypto/src/const_helpers.rs +++ b/lib/crypto/src/const_helpers.rs @@ -119,17 +119,6 @@ macro_rules! const_modulo { }}; } -#[macro_export] -macro_rules! mac_with_carry { - ($a:expr, $b:expr, $c:expr, &mut $carry:expr $(,)?) => {{ - let tmp = ($a as u128) - + $crate::arithmetic::widening_mul($b, $c) - + ($carry as u128); - $carry = (tmp >> 64) as u64; - tmp as u64 - }}; -} - #[macro_export] macro_rules! mac { ($a:expr, $b:expr, $c:expr, &mut $carry:expr $(,)?) => {{ diff --git a/lib/crypto/src/field/fp.rs b/lib/crypto/src/field/fp.rs index 50e8e865b..5aa207d18 100644 --- a/lib/crypto/src/field/fp.rs +++ b/lib/crypto/src/field/fp.rs @@ -30,7 +30,7 @@ use crate::{ arithmetic::{BigInt, BigInteger, Limb}, const_for, field::{group::AdditiveGroup, prime::PrimeField, Field}, - mac, mac_with_carry, unroll6_for, + mac, unroll6_for, }; /// A trait that specifies the configuration of a prime field. @@ -220,15 +220,15 @@ pub trait FpParams: Send + Sync + 'static + Sized { // Montgomery Reduction for i in 0..N { let k = r[i].wrapping_mul(Self::INV); - let mut carry = 0; + // let mut carry = 0; - arithmetic::mac_with_carry(r[i], k, Self::MODULUS.0[0], &mut carry); + let (_, mut carry) = arithmetic::mac(r[i], k, Self::MODULUS.0[0]); for j in 1..N { - r[(j + i) % N] = arithmetic::mac_with_carry( + (r[(j + i) % N], carry) = arithmetic::mac_with_carry( r[(j + i) % N], k, Self::MODULUS.0[j], - &mut carry, + carry, ); } r[i % N] = carry; @@ -388,7 +388,7 @@ impl, const N: usize> Fp { } const fn const_mul(self, other: &Self) -> Self { - let (carry, res) = self.ct_mul_without_cond_subtract(other); + let (carry, res) = self.mul_without_cond_subtract(other); if P::MODULUS_HAS_SPARE_BIT { res.const_subtract_modulus() } else { @@ -407,66 +407,26 @@ impl, const N: usize> Fp { } } - const fn ct_mul_without_cond_subtract( - mut self, - other: &Self, - ) -> (bool, Self) { - let (mut lo, mut hi) = ([0u64; N], [0u64; N]); - const_for!((i in 0..N) { - let mut carry = 0; - const_for!((j in 0..N) { - let k = i + j; - if k >= N { - hi[k - N] = mac_with_carry!(hi[k - N], (self.0).0[i], (other.0).0[j], &mut carry); - } else { - lo[k] = mac_with_carry!(lo[k], (self.0).0[i], (other.0).0[j], &mut carry); - } - }); - hi[i] = carry; - }); - // Montgomery reduction - let mut carry2 = 0; - const_for!((i in 0..N) { - let tmp = lo[i].wrapping_mul(P::INV); - let mut carry; - mac!(lo[i], tmp, P::MODULUS.0[0], &mut carry); - const_for!((j in 1..N) { - let k = i + j; - if k >= N { - hi[k - N] = mac_with_carry!(hi[k - N], tmp, P::MODULUS.0[j], &mut carry); - } else { - lo[k] = mac_with_carry!(lo[k], tmp, P::MODULUS.0[j], &mut carry); - } - }); - hi[i] = adc!(hi[i], carry, &mut carry2); - }); - - const_for!((i in 0..N) { - (self.0).0[i] = hi[i]; - }); - (carry2 != 0, self) - } - #[inline(always)] - fn mul_without_cond_subtract(mut self, other: &Self) -> (bool, Self) { + const fn mul_without_cond_subtract(mut self, other: &Self) -> (bool, Self) { let (mut lo, mut hi) = ([0u64; N], [0u64; N]); unroll6_for!((i in 0..N) { let mut carry = 0; unroll6_for!((j in 0..N) { let k = i + j; if k >= N { - hi[k - N] = mac_with_carry!( + (hi[k - N], carry) = arithmetic::mac_with_carry( hi[k - N], (self.0).0[i], (other.0).0[j], - &mut carry + carry ); } else { - lo[k] = mac_with_carry!( + (lo[k], carry) = arithmetic::mac_with_carry( lo[k], (self.0).0[i], (other.0).0[j], - &mut carry + carry ); } }); @@ -477,26 +437,26 @@ impl, const N: usize> Fp { unroll6_for!((i in 0..N) { let tmp = lo[i].wrapping_mul(P::INV); let mut carry; - mac!(lo[i], tmp, P::MODULUS.0[0], &mut carry); + mac!(lo[i], tmp, P::MODULUS.0[0], &mut carry); // TODO#q: remove this mac unroll6_for!((j in 1..N) { let k = i + j; if k >= N { - hi[k - N] = mac_with_carry!( + (hi[k - N], carry) = arithmetic::mac_with_carry( hi[k - N], tmp, P::MODULUS.0[j], - &mut carry + carry ); } else { - lo[k] = mac_with_carry!( + (lo[k], carry) = arithmetic::mac_with_carry( lo[k], tmp, P::MODULUS.0[j], - &mut carry + carry ); } }); - hi[i] = adc!(hi[i], carry, &mut carry2); + hi[i] = adc!(hi[i], carry, &mut carry2); // TODO#q: remove this adc }); unroll6_for!((i in 0..N) { From cbf29a9738fd8b6087a9e8c3e1561fdcc43d991f Mon Sep 17 00:00:00 2001 From: Alisander Qoshqosh Date: Wed, 15 Jan 2025 01:55:42 +0400 Subject: [PATCH 54/61] ++ --- lib/crypto/src/arithmetic/mod.rs | 2 +- lib/crypto/src/field/fp.rs | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/crypto/src/arithmetic/mod.rs b/lib/crypto/src/arithmetic/mod.rs index 6d3c40ba6..6ab117507 100644 --- a/lib/crypto/src/arithmetic/mod.rs +++ b/lib/crypto/src/arithmetic/mod.rs @@ -440,7 +440,7 @@ pub const fn mac(a: u64, b: u64, c: u64) -> (u64, u64) { /// and setting carry to the most significant digit. #[inline(always)] #[doc(hidden)] -pub const fn mac_with_carry(a: u64, b: u64, c: u64, carry: u64) -> (u64, u64) { +pub const fn carrying_mac(a: u64, b: u64, c: u64, carry: u64) -> (u64, u64) { let tmp = (a as u128) + widening_mul(b, c) + (carry as u128); let carry = (tmp >> 64) as u64; (tmp as u64, carry) diff --git a/lib/crypto/src/field/fp.rs b/lib/crypto/src/field/fp.rs index 5aa207d18..f32a6060b 100644 --- a/lib/crypto/src/field/fp.rs +++ b/lib/crypto/src/field/fp.rs @@ -224,7 +224,7 @@ pub trait FpParams: Send + Sync + 'static + Sized { let (_, mut carry) = arithmetic::mac(r[i], k, Self::MODULUS.0[0]); for j in 1..N { - (r[(j + i) % N], carry) = arithmetic::mac_with_carry( + (r[(j + i) % N], carry) = arithmetic::carrying_mac( r[(j + i) % N], k, Self::MODULUS.0[j], @@ -415,14 +415,14 @@ impl, const N: usize> Fp { unroll6_for!((j in 0..N) { let k = i + j; if k >= N { - (hi[k - N], carry) = arithmetic::mac_with_carry( + (hi[k - N], carry) = arithmetic::carrying_mac( hi[k - N], (self.0).0[i], (other.0).0[j], carry ); } else { - (lo[k], carry) = arithmetic::mac_with_carry( + (lo[k], carry) = arithmetic::carrying_mac( lo[k], (self.0).0[i], (other.0).0[j], @@ -441,14 +441,14 @@ impl, const N: usize> Fp { unroll6_for!((j in 1..N) { let k = i + j; if k >= N { - (hi[k - N], carry) = arithmetic::mac_with_carry( + (hi[k - N], carry) = arithmetic::carrying_mac( hi[k - N], tmp, P::MODULUS.0[j], carry ); } else { - (lo[k], carry) = arithmetic::mac_with_carry( + (lo[k], carry) = arithmetic::carrying_mac( lo[k], tmp, P::MODULUS.0[j], From fb209c2bc9abdf71700380036124688a0b154c7a Mon Sep 17 00:00:00 2001 From: Alisander Qoshqosh Date: Wed, 15 Jan 2025 02:06:14 +0400 Subject: [PATCH 55/61] ++ --- lib/crypto/src/arithmetic/mod.rs | 30 +++++++++++++++--------------- lib/crypto/src/field/fp.rs | 7 ++++--- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/lib/crypto/src/arithmetic/mod.rs b/lib/crypto/src/arithmetic/mod.rs index 6ab117507..6fdd00908 100644 --- a/lib/crypto/src/arithmetic/mod.rs +++ b/lib/crypto/src/arithmetic/mod.rs @@ -469,26 +469,15 @@ pub fn mac_discard(a: u64, b: u64, c: u64, carry: &mut u64) { *carry = (tmp >> 64) as u64; } -/// Sets a = a - b - borrow, and returns the borrow. -#[inline(always)] -#[allow(unused_mut)] -#[doc(hidden)] -pub fn sbb_for_sub_with_borrow(a: &mut u64, b: u64, borrow: bool) -> bool { - let (sub, borrow1) = a.overflowing_sub(b); - let (sub, borrow2) = sub.overflowing_sub(borrow as u64); - *a = sub; - borrow1 | borrow2 -} - // TODO#q: adc can be unified with adc_for_add_with_carry /// Sets a = a + b + carry, and returns the new carry. #[inline(always)] #[allow(unused_mut)] #[doc(hidden)] -pub fn adc(a: &mut u64, b: u64, carry: u64) -> u64 { - let tmp = *a as u128 + b as u128 + carry as u128; - *a = tmp as u64; - (tmp >> 64) as u64 +pub const fn adc(a: u64, b: u64, carry: u64) -> (u64, u64) { + let tmp = a as u128 + b as u128 + carry as u128; + let carry = (tmp >> 64) as u64; + (tmp as u64, carry) } /// Sets a = a + b + carry, and returns the new carry. @@ -510,6 +499,17 @@ pub const fn ct_adc_for_add_with_carry(a: u64, b: u64, carry: u8) -> (u64, u8) { (tmp as u64, (tmp >> 64) as u8) } +/// Sets a = a - b - borrow, and returns the borrow. +#[inline(always)] +#[allow(unused_mut)] +#[doc(hidden)] +pub fn sbb_for_sub_with_borrow(a: &mut u64, b: u64, borrow: bool) -> bool { + let (sub, borrow1) = a.overflowing_sub(b); + let (sub, borrow2) = sub.overflowing_sub(borrow as u64); + *a = sub; + borrow1 | borrow2 +} + // ----------- Traits Impls ----------- impl UpperHex for BigInt { diff --git a/lib/crypto/src/field/fp.rs b/lib/crypto/src/field/fp.rs index f32a6060b..df9e06a24 100644 --- a/lib/crypto/src/field/fp.rs +++ b/lib/crypto/src/field/fp.rs @@ -436,8 +436,9 @@ impl, const N: usize> Fp { let mut carry2 = 0; unroll6_for!((i in 0..N) { let tmp = lo[i].wrapping_mul(P::INV); - let mut carry; - mac!(lo[i], tmp, P::MODULUS.0[0], &mut carry); // TODO#q: remove this mac + + let (_, mut carry) = arithmetic::mac(lo[i], tmp, P::MODULUS.0[0]); + unroll6_for!((j in 1..N) { let k = i + j; if k >= N { @@ -456,7 +457,7 @@ impl, const N: usize> Fp { ); } }); - hi[i] = adc!(hi[i], carry, &mut carry2); // TODO#q: remove this adc + (hi[i], carry2) = arithmetic::adc(hi[i], carry, carry2); }); unroll6_for!((i in 0..N) { From 98ec8765c8d8e2acd859579fcb6d869c950ed4e9 Mon Sep 17 00:00:00 2001 From: Alisander Qoshqosh Date: Wed, 15 Jan 2025 02:26:39 +0400 Subject: [PATCH 56/61] ++ --- lib/crypto/src/arithmetic/mod.rs | 35 +++++++++----------------------- lib/crypto/src/const_helpers.rs | 28 ------------------------- lib/crypto/src/field/fp.rs | 6 +++--- 3 files changed, 13 insertions(+), 56 deletions(-) diff --git a/lib/crypto/src/arithmetic/mod.rs b/lib/crypto/src/arithmetic/mod.rs index 6fdd00908..11a4726a3 100644 --- a/lib/crypto/src/arithmetic/mod.rs +++ b/lib/crypto/src/arithmetic/mod.rs @@ -18,9 +18,7 @@ use num_traits::{ConstZero, Zero}; // }; use zeroize::Zeroize; -use crate::{ - adc, bits::BitIteratorBE, const_for, const_modulo, sbb, unroll6_for, -}; +use crate::{bits::BitIteratorBE, const_for, const_modulo, unroll6_for}; pub type Limb = u64; pub type WideLimb = u128; @@ -177,7 +175,7 @@ impl BigInt { let mut borrow = 0; const_for!((i in 0..N) { - self.0[i] = sbb!(self.0[i], other.0[i], &mut borrow); + (self.0[i], borrow) = sbb(self.0[i], other.0[i], borrow); }); (self, borrow != 0) @@ -205,7 +203,7 @@ impl BigInt { let mut carry = 0; crate::const_for!((i in 0..N) { - self.0[i] = adc!(self.0[i], other.0[i], &mut carry); + (self.0[i], carry) = adc(self.0[i], other.0[i], carry); }); (self, carry != 0) @@ -281,19 +279,6 @@ impl BigInt { borrow } - pub(crate) const fn ct_add_with_carry( - mut self, - other: &Self, - ) -> (Self, bool) { - let mut carry = 0; - - const_for!((i in 0..N) { - (self.0[i], carry) = ct_adc_for_add_with_carry(self.0[i], other.0[i], carry); - }); - - (self, carry != 0) - } - /// Compute "wide" multiplication, with a product twice the size of the /// input. /// @@ -470,7 +455,7 @@ pub fn mac_discard(a: u64, b: u64, c: u64, carry: &mut u64) { } // TODO#q: adc can be unified with adc_for_add_with_carry -/// Sets a = a + b + carry, and returns the new carry. +/// Calculate `a = a + b + carry` and return the result and carry. #[inline(always)] #[allow(unused_mut)] #[doc(hidden)] @@ -491,12 +476,12 @@ pub fn adc_for_add_with_carry(a: &mut u64, b: u64, carry: bool) -> bool { carry1 | carry2 } -#[inline(always)] -#[allow(unused_mut)] -#[doc(hidden)] -pub const fn ct_adc_for_add_with_carry(a: u64, b: u64, carry: u8) -> (u64, u8) { - let tmp = a as u128 + b as u128 + carry as u128; - (tmp as u64, (tmp >> 64) as u8) +// TODO#q: sbb can be unified with sbb_for_sub_with_borrow +/// Calculate `a = a - b - borrow` and return the result and borrow. +pub const fn sbb(a: u64, b: u64, borrow: u64) -> (u64, u64) { + let tmp = (1u128 << 64) + (a as u128) - (b as u128) - (borrow as u128); + let borrow = if tmp >> 64 == 0 { 1 } else { 0 }; + (tmp as u64, borrow) } /// Sets a = a - b - borrow, and returns the borrow. diff --git a/lib/crypto/src/const_helpers.rs b/lib/crypto/src/const_helpers.rs index 00470af93..8bf7552d8 100644 --- a/lib/crypto/src/const_helpers.rs +++ b/lib/crypto/src/const_helpers.rs @@ -76,25 +76,6 @@ macro_rules! cycle { }}; } -#[macro_export] -macro_rules! sbb { - ($a:expr, $b:expr, &mut $borrow:expr $(,)?) => {{ - let tmp = - (1u128 << 64) + ($a as u128) - ($b as u128) - ($borrow as u128); - $borrow = if tmp >> 64 == 0 { 1 } else { 0 }; - tmp as u64 - }}; -} - -#[macro_export] -macro_rules! adc { - ($a:expr, $b:expr, &mut $carry:expr $(,)?) => {{ - let tmp = ($a as u128) + ($b as u128) + ($carry as u128); - $carry = (tmp >> 64) as u64; - tmp as u64 - }}; -} - // TODO#q: implement const_modulo as a function #[macro_export] macro_rules! const_modulo { @@ -119,15 +100,6 @@ macro_rules! const_modulo { }}; } -#[macro_export] -macro_rules! mac { - ($a:expr, $b:expr, $c:expr, &mut $carry:expr $(,)?) => {{ - let tmp = ($a as u128) + $crate::arithmetic::widening_mul($b, $c); - $carry = (tmp >> 64) as u64; - tmp as u64 - }}; -} - pub(super) struct RBuffer(pub [u64; N], pub u64); impl RBuffer { diff --git a/lib/crypto/src/field/fp.rs b/lib/crypto/src/field/fp.rs index df9e06a24..3651a0f09 100644 --- a/lib/crypto/src/field/fp.rs +++ b/lib/crypto/src/field/fp.rs @@ -26,11 +26,11 @@ use educe::Educe; use num_traits::{ConstZero, One, Zero}; use crate::{ - adc, arithmetic, - arithmetic::{BigInt, BigInteger, Limb}, + arithmetic, + arithmetic::{BigInt, BigInteger}, const_for, field::{group::AdditiveGroup, prime::PrimeField, Field}, - mac, unroll6_for, + unroll6_for, }; /// A trait that specifies the configuration of a prime field. From c12418f207134339c26411b8c694617813f62e3e Mon Sep 17 00:00:00 2001 From: Alisander Qoshqosh Date: Wed, 15 Jan 2025 13:38:53 +0400 Subject: [PATCH 57/61] ++ --- examples/oz-crypto/src/lib.rs | 4 +- examples/poseidon/src/lib.rs | 4 +- lib/crypto/src/arithmetic/mod.rs | 121 +++++++++++++++---------------- lib/crypto/src/field/fp.rs | 46 ++++++------ lib/crypto/src/field/instance.rs | 14 ++-- 5 files changed, 93 insertions(+), 96 deletions(-) diff --git a/examples/oz-crypto/src/lib.rs b/examples/oz-crypto/src/lib.rs index 74ac551bc..4aa094f8a 100644 --- a/examples/oz-crypto/src/lib.rs +++ b/examples/oz-crypto/src/lib.rs @@ -5,7 +5,7 @@ use alloc::vec::Vec; use alloy_primitives::U256; use openzeppelin_crypto::{ - arithmetic::{BigInt, BigInteger}, + arithmetic::{BigInteger, Uint}, field::{ group::AdditiveGroup, instance::FpBN256, prime::PrimeField, Field, }, @@ -22,7 +22,7 @@ impl MathExample { let inputs: Vec<_> = inputs .iter() .map(|input| { - FpBN256::from_bigint(BigInt::from_bytes_le( + FpBN256::from_bigint(Uint::from_bytes_le( &input.to_le_bytes_vec(), )) }) diff --git a/examples/poseidon/src/lib.rs b/examples/poseidon/src/lib.rs index c14bb3a05..62ff7abfc 100644 --- a/examples/poseidon/src/lib.rs +++ b/examples/poseidon/src/lib.rs @@ -5,7 +5,7 @@ use alloc::vec::Vec; use alloy_primitives::U256; use openzeppelin_crypto::{ - arithmetic::{BigInt, BigInteger}, + arithmetic::{BigInteger, Uint}, field::{instance::FpBN256, prime::PrimeField}, poseidon2::{instance::bn256::BN256Params, Poseidon2}, }; @@ -21,7 +21,7 @@ impl PoseidonExample { let mut hasher = Poseidon2::::new(); for input in inputs.iter() { - let fp = FpBN256::from_bigint(BigInt::from_bytes_le( + let fp = FpBN256::from_bigint(Uint::from_bytes_le( &input.to_le_bytes_vec(), )); hasher.absorb(&fp); diff --git a/lib/crypto/src/arithmetic/mod.rs b/lib/crypto/src/arithmetic/mod.rs index 11a4726a3..02404a48c 100644 --- a/lib/crypto/src/arithmetic/mod.rs +++ b/lib/crypto/src/arithmetic/mod.rs @@ -21,6 +21,7 @@ use zeroize::Zeroize; use crate::{bits::BitIteratorBE, const_for, const_modulo, unroll6_for}; pub type Limb = u64; +pub type Limbs = [Limb; N]; pub type WideLimb = u128; // TODO#q: Refactor types to: @@ -33,15 +34,15 @@ pub type WideLimb = u128; // - Rename functions *_with_carry to carrying_* #[derive(Copy, Clone, PartialEq, Eq, Hash, Zeroize)] -pub struct BigInt(pub [Limb; N]); +pub struct Uint(pub Limbs); -impl Default for BigInt { +impl Default for Uint { fn default() -> Self { Self([0u64; N]) } } -impl BigInt { +impl Uint { pub const BITS: u32 = (N as u32) * Limb::BITS; pub const ONE: Self = Self::one(); pub const ZERO: Self = Self::zero(); @@ -294,11 +295,11 @@ impl BigInt { // NOTE#q: crypto_bigint pub const fn ct_mul_wide( &self, - rhs: &BigInt, - ) -> (Self, BigInt) { + rhs: &Uint, + ) -> (Self, Uint) { let mut i = 0; let mut lo = Self::ZERO; - let mut hi = BigInt::::ZERO; + let mut hi = Uint::::ZERO; // Schoolbook multiplication. // TODO(tarcieri): use Karatsuba for better performance? @@ -342,11 +343,7 @@ impl BigInt { #[inline(always)] /// Computes `a + b + carry`, returning the result along with the new carry. // NOTE#q: crypto_bigint - pub const fn ct_adc( - &self, - rhs: &BigInt, - mut carry: Limb, - ) -> (Self, Limb) { + pub const fn ct_adc(&self, rhs: &Uint, mut carry: Limb) -> (Self, Limb) { let mut limbs = [Limb::ZERO; N]; let mut i = 0; @@ -497,25 +494,25 @@ pub fn sbb_for_sub_with_borrow(a: &mut u64, b: u64, borrow: bool) -> bool { // ----------- Traits Impls ----------- -impl UpperHex for BigInt { +impl UpperHex for Uint { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { write!(f, "{:016X}", BigUint::from(*self)) } } -impl Debug for BigInt { +impl Debug for Uint { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { write!(f, "{:?}", BigUint::from(*self)) } } -impl Display for BigInt { +impl Display for Uint { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { write!(f, "{}", BigUint::from(*self)) } } -impl Ord for BigInt { +impl Ord for Uint { #[inline] fn cmp(&self, other: &Self) -> core::cmp::Ordering { use core::cmp::Ordering; @@ -532,7 +529,7 @@ impl Ord for BigInt { } } -impl PartialOrd for BigInt { +impl PartialOrd for Uint { #[inline] fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) @@ -546,14 +543,14 @@ impl PartialOrd for BigInt { } }*/ -impl AsMut<[u64]> for BigInt { +impl AsMut<[u64]> for Uint { #[inline] fn as_mut(&mut self) -> &mut [u64] { &mut self.0 } } -impl AsRef<[u64]> for BigInt { +impl AsRef<[u64]> for Uint { #[inline] fn as_ref(&self) -> &[u64] { &self.0 @@ -585,36 +582,36 @@ impl TryFrom for BigInt { } */ -impl From for BigInt { +impl From for Uint { #[inline] - fn from(val: u64) -> BigInt { + fn from(val: u64) -> Uint { let mut repr = Self::default(); repr.0[0] = val; repr } } -impl From for BigInt { +impl From for Uint { #[inline] - fn from(val: u32) -> BigInt { + fn from(val: u32) -> Uint { let mut repr = Self::default(); repr.0[0] = val.into(); repr } } -impl From for BigInt { +impl From for Uint { #[inline] - fn from(val: u16) -> BigInt { + fn from(val: u16) -> Uint { let mut repr = Self::default(); repr.0[0] = val.into(); repr } } -impl From for BigInt { +impl From for Uint { #[inline] - fn from(val: u8) -> BigInt { + fn from(val: u8) -> Uint { let mut repr = Self::default(); repr.0[0] = val.into(); repr @@ -622,29 +619,29 @@ impl From for BigInt { } // TODO#q: remove num_bigint::BigUint conversion -impl From> for BigUint { +impl From> for BigUint { #[inline] - fn from(val: BigInt) -> num_bigint::BigUint { + fn from(val: Uint) -> num_bigint::BigUint { BigUint::from_bytes_le(&val.into_bytes_le()) } } -impl From> for num_bigint::BigInt { +impl From> for num_bigint::BigInt { #[inline] - fn from(val: BigInt) -> num_bigint::BigInt { + fn from(val: Uint) -> num_bigint::BigInt { use num_bigint::Sign; let sign = if val.is_zero() { Sign::NoSign } else { Sign::Plus }; num_bigint::BigInt::from_bytes_le(sign, &val.into_bytes_le()) } } -impl, const N: usize> BitXorAssign for BigInt { +impl, const N: usize> BitXorAssign for Uint { fn bitxor_assign(&mut self, rhs: B) { (0..N).for_each(|i| self.0[i] ^= rhs.borrow().0[i]) } } -impl, const N: usize> BitXor for BigInt { +impl, const N: usize> BitXor for Uint { type Output = Self; fn bitxor(mut self, rhs: B) -> Self::Output { @@ -653,13 +650,13 @@ impl, const N: usize> BitXor for BigInt { } } -impl, const N: usize> BitAndAssign for BigInt { +impl, const N: usize> BitAndAssign for Uint { fn bitand_assign(&mut self, rhs: B) { (0..N).for_each(|i| self.0[i] &= rhs.borrow().0[i]) } } -impl, const N: usize> BitAnd for BigInt { +impl, const N: usize> BitAnd for Uint { type Output = Self; fn bitand(mut self, rhs: B) -> Self::Output { @@ -668,13 +665,13 @@ impl, const N: usize> BitAnd for BigInt { } } -impl, const N: usize> BitOrAssign for BigInt { +impl, const N: usize> BitOrAssign for Uint { fn bitor_assign(&mut self, rhs: B) { (0..N).for_each(|i| self.0[i] |= rhs.borrow().0[i]) } } -impl, const N: usize> BitOr for BigInt { +impl, const N: usize> BitOr for Uint { type Output = Self; fn bitor(mut self, rhs: B) -> Self::Output { @@ -683,7 +680,7 @@ impl, const N: usize> BitOr for BigInt { } } -impl ShrAssign for BigInt { +impl ShrAssign for Uint { /// Computes the bitwise shift right operation in place. /// /// Differently from the built-in numeric types (u8, u32, u64, etc.) this @@ -716,7 +713,7 @@ impl ShrAssign for BigInt { } } -impl Shr for BigInt { +impl Shr for Uint { type Output = Self; /// Computes bitwise shift right operation. @@ -731,7 +728,7 @@ impl Shr for BigInt { } } -impl ShlAssign for BigInt { +impl ShlAssign for Uint { /// Computes the bitwise shift left operation in place. /// /// Differently from the built-in numeric types (u8, u32, u64, etc.) this @@ -766,7 +763,7 @@ impl ShlAssign for BigInt { } } -impl Shl for BigInt { +impl Shl for Uint { type Output = Self; /// Computes the bitwise shift left operation in place. @@ -781,7 +778,7 @@ impl Shl for BigInt { } } -impl Not for BigInt { +impl Not for Uint { type Output = Self; fn not(self) -> Self::Output { @@ -908,7 +905,7 @@ pub trait BigInteger: fn into_bytes_le(self) -> alloc::vec::Vec; } -impl BigInteger for BigInt { +impl BigInteger for Uint { const NUM_LIMBS: usize = N; fn is_odd(&self) -> bool { @@ -955,7 +952,7 @@ impl BigInteger for BigInt { } } -impl BitIteratorBE for BigInt { +impl BitIteratorBE for Uint { fn bit_be_iter(&self) -> impl Iterator { self.as_limbs().iter().rev().flat_map(Limb::bit_be_iter) } @@ -971,7 +968,7 @@ impl BitIteratorBE for BigInt { pub const fn from_str_radix( s: &str, radix: u32, -) -> BigInt { +) -> Uint { let bytes = s.as_bytes(); assert!(!bytes.is_empty(), "empty string"); @@ -979,12 +976,12 @@ pub const fn from_str_radix( // Begin parsing from the last index of the string. let mut index = bytes.len() - 1; - let mut uint = BigInt::from_u32(0); - let mut order = BigInt::from_u32(1); - let uint_radix = BigInt::from_u32(radix); + let mut uint = Uint::from_u32(0); + let mut order = Uint::from_u32(1); + let uint_radix = Uint::from_u32(radix); loop { - let digit = BigInt::from_u32(parse_digit(bytes[index], radix)); + let digit = Uint::from_u32(parse_digit(bytes[index], radix)); // Add a digit multiplied by order. uint = ct_add(&uint, &ct_mul(&digit, &order)); @@ -1010,7 +1007,7 @@ pub const fn from_str_radix( /// If the string number is shorter, then [`Uint`] can store. /// Returns a [`Uint`] with leading zeroes. #[must_use] -pub const fn from_str_hex(s: &str) -> BigInt { +pub const fn from_str_hex(s: &str) -> Uint { let bytes = s.as_bytes(); assert!(!bytes.is_empty(), "empty string"); @@ -1037,7 +1034,7 @@ pub const fn from_str_hex(s: &str) -> BigInt { // If we reached the beginning of the string, return the number. if index == 0 { - return BigInt::new(num); + return Uint::new(num); } // Move to the next digit. @@ -1050,15 +1047,15 @@ pub const fn from_str_hex(s: &str) -> BigInt { /// Multiply two numbers and panic on overflow. #[must_use] -pub const fn ct_mul(a: &BigInt, b: &BigInt) -> BigInt { +pub const fn ct_mul(a: &Uint, b: &Uint) -> Uint { let (low, high) = a.ct_mul_wide(b); - assert!(ct_eq(&high, &BigInt::::ZERO), "overflow on multiplication"); + assert!(ct_eq(&high, &Uint::::ZERO), "overflow on multiplication"); low } /// Add two numbers and panic on overflow. #[must_use] -pub const fn ct_add(a: &BigInt, b: &BigInt) -> BigInt { +pub const fn ct_add(a: &Uint, b: &Uint) -> Uint { let (low, carry) = a.ct_adc(b, Limb::ZERO); assert!(carry == 0, "overflow on addition"); low @@ -1089,7 +1086,7 @@ pub const fn ct_adc(lhs: Limb, rhs: Limb, carry: Limb) -> (Limb, Limb) { (ret as Limb, (ret >> Limb::BITS) as Limb) } -pub const fn ct_ge(a: &BigInt, b: &BigInt) -> bool { +pub const fn ct_ge(a: &Uint, b: &Uint) -> bool { const_for!((i in 0..N) { if a.0[i] < b.0[i] { return false; @@ -1101,7 +1098,7 @@ pub const fn ct_ge(a: &BigInt, b: &BigInt) -> bool { } // TODO#q: compare with const_is_zero -pub const fn ct_eq(a: &BigInt, b: &BigInt) -> bool { +pub const fn ct_eq(a: &Uint, b: &Uint) -> bool { const_for!((i in 0..N) { if a.0[i] != b.0[i] { return false; @@ -1153,12 +1150,12 @@ mod test { #[test] fn convert_from_str_radix() { - let uint_from_base10: BigInt<4> = from_str_radix( + let uint_from_base10: Uint<4> = from_str_radix( "28948022309329048855892746252171976963363056481941647379679742748393362948097", 10, ); #[allow(clippy::unreadable_literal)] - let expected = BigInt::<4>::new([ + let expected = Uint::<4>::new([ 10108024940646105089u64, 2469829653919213789u64, 0u64, @@ -1166,9 +1163,9 @@ mod test { ]); assert_eq!(uint_from_base10, expected); - let uint_from_base10: BigInt<1> = + let uint_from_base10: Uint<1> = from_str_radix("18446744069414584321", 10); - let uint_from_binary: BigInt<1> = from_str_radix( + let uint_from_binary: Uint<1> = from_str_radix( "1111111111111111111111111111111100000000000000000000000000000001", 2, ); @@ -1179,8 +1176,8 @@ mod test { fn convert_from_str_hex() { // Test different implementations of hex parsing on random hex inputs. proptest!(|(s in "[0-9a-fA-F]{1,64}")| { - let uint_from_hex: BigInt<4> = from_str_hex(&s); - let expected: BigInt<4> = from_str_radix(&s, 16); + let uint_from_hex: Uint<4> = from_str_hex(&s); + let expected: Uint<4> = from_str_radix(&s, 16); assert_eq!(uint_from_hex, expected); }); } @@ -1188,7 +1185,7 @@ mod test { #[test] fn uint_bit_iterator_be() { let words: [Limb; 4] = [0b1100, 0, 0, 0]; - let num = BigInt::<4>::new(words); + let num = Uint::<4>::new(words); let bits: Vec = num.bit_be_trimmed_iter().collect(); assert_eq!(bits.len(), 4); diff --git a/lib/crypto/src/field/fp.rs b/lib/crypto/src/field/fp.rs index 3651a0f09..6709bcdd8 100644 --- a/lib/crypto/src/field/fp.rs +++ b/lib/crypto/src/field/fp.rs @@ -27,7 +27,7 @@ use num_traits::{ConstZero, One, Zero}; use crate::{ arithmetic, - arithmetic::{BigInt, BigInteger}, + arithmetic::{BigInteger, Uint}, const_for, field::{group::AdditiveGroup, prime::PrimeField, Field}, unroll6_for, @@ -39,7 +39,7 @@ use crate::{ // TODO#q: rename FpParams -> Params pub trait FpParams: Send + Sync + 'static + Sized { /// The modulus of the field. - const MODULUS: BigInt; + const MODULUS: Uint; /// A multiplicative generator of the field. /// [`Self::GENERATOR`] is an element having multiplicative order @@ -53,11 +53,11 @@ pub trait FpParams: Send + Sync + 'static + Sized { /// Let `M` be the power of 2^64 nearest to [`Self::MODULUS_BITS`]. Then /// `R = M % MODULUS`. - const R: BigInt = Self::MODULUS.montgomery_r(); + const R: Uint = Self::MODULUS.montgomery_r(); /// `R2 = R^2 % MODULUS` #[allow(dead_code)] - const R2: BigInt = Self::MODULUS.montgomery_r2(); + const R2: Uint = Self::MODULUS.montgomery_r2(); /// Set `a += b`. #[inline(always)] @@ -143,7 +143,7 @@ pub trait FpParams: Send + Sync + 'static + Sized { // to Cryptography // Algorithm 16 (BEA for Inversion in Fp) - let one = BigInt::ONE; + let one = Uint::ONE; let mut u = a.0; let mut v = Self::MODULUS; @@ -202,7 +202,7 @@ pub trait FpParams: Send + Sync + 'static + Sized { /// By the end element will be converted to a montgomery form and reduced. #[must_use] #[inline] - fn from_bigint(r: BigInt) -> Option> { + fn from_bigint(r: Uint) -> Option> { let mut r = Fp::new_unchecked(r); if r.is_zero() { Some(r) @@ -215,7 +215,7 @@ pub trait FpParams: Send + Sync + 'static + Sized { /// Convert a field element to an integer less than [`Self::MODULUS`]. #[must_use] #[inline(always)] - fn into_bigint(a: Fp) -> BigInt { + fn into_bigint(a: Fp) -> Uint { let mut r = (a.0).0; // Montgomery Reduction for i in 0..N { @@ -234,7 +234,7 @@ pub trait FpParams: Send + Sync + 'static + Sized { r[i % N] = carry; } - BigInt::new(r) + Uint::new(r) } } @@ -272,9 +272,9 @@ pub const fn modulus_has_spare_bit, const N: usize>() -> bool { #[educe(Default, Clone, Copy, PartialEq, Eq)] pub struct Fp, const N: usize>( /// Contains the element in Montgomery form for efficient multiplication. - /// To convert an element to a [`BigInt`], use [`FpParams::into_bigint`] + /// To convert an element to a [`Uint`], use [`FpParams::into_bigint`] /// or `into`. - BigInt, + Uint, #[doc(hidden)] pub PhantomData

, ); @@ -324,10 +324,10 @@ impl, const N: usize> Fp { /// such that, for all elements `f` of the field, `e * f = f`. pub const ONE: Fp = Fp::new_unchecked(P::R); // TODO#q: remove - pub const R: BigInt = P::R; + pub const R: Uint = P::R; /// Additive identity of the field, i.e., the element `e` /// such that, for all elements `f` of the field, `e + f = f`. - pub const ZERO: Fp = Fp::new_unchecked(BigInt([0; N])); + pub const ZERO: Fp = Fp::new_unchecked(Uint([0; N])); /// Construct a new field element from [`Uint`]. /// @@ -336,7 +336,7 @@ impl, const N: usize> Fp { /// integer that has already been put in Montgomery form. #[must_use] #[inline(always)] - pub const fn new_unchecked(element: BigInt) -> Self { + pub const fn new_unchecked(element: Uint) -> Self { Self(element, PhantomData) } @@ -375,9 +375,9 @@ impl, const N: usize> Fp { // TODO#q: rename all const_* methods to ct_* /// Construct a new field element from its underlying - /// [`struct@BigInt`] data type. + /// [`struct@Uint`] data type. #[inline] - pub const fn new(element: BigInt) -> Self { + pub const fn new(element: Uint) -> Self { let mut r = Self(element, PhantomData); if r.const_is_zero() { r @@ -493,7 +493,7 @@ impl, const N: usize> Fp { self } - const fn sub_with_borrow(a: &BigInt, b: &BigInt) -> BigInt { + const fn sub_with_borrow(a: &Uint, b: &Uint) -> Uint { a.const_sub_with_borrow(b).0 } } @@ -592,7 +592,7 @@ impl, const N: usize> Field for Fp { } impl, const N: usize> PrimeField for Fp { - type BigInt = BigInt; + type BigInt = Uint; const MODULUS: Self::BigInt = P::MODULUS; const MODULUS_BIT_SIZE: usize = unimplemented!(); @@ -604,7 +604,7 @@ impl, const N: usize> PrimeField for Fp { } #[inline] - fn into_bigint(self) -> BigInt { + fn into_bigint(self) -> Uint { P::into_bigint(self) } } @@ -628,7 +628,7 @@ macro_rules! impl_fp_from_unsigned_int { ($int:ty) => { impl, const N: usize> From<$int> for Fp { fn from(other: $int) -> Self { - Fp::from_bigint(BigInt::from(other)) + Fp::from_bigint(Uint::from(other)) } } }; @@ -1060,17 +1060,17 @@ impl, const N: usize> zeroize::Zeroize for Fp { } } -impl, const N: usize> From> for BigInt { +impl, const N: usize> From> for Uint { #[inline] fn from(fp: Fp) -> Self { fp.into_bigint() } } -impl, const N: usize> From> for Fp { +impl, const N: usize> From> for Fp { /// Converts `Self::BigInteger` into `Self` #[inline] - fn from(int: BigInt) -> Self { + fn from(int: Uint) -> Self { Self::from_bigint(int) } } @@ -1108,7 +1108,7 @@ mod tests { struct Fp64Param; impl FpParams for Fp64Param { const GENERATOR: Fp64 = fp_from_num!("3"); - const MODULUS: BigInt = from_num!("1000003"); // Prime number + const MODULUS: Uint = from_num!("1000003"); // Prime number } const MODULUS: i128 = 1000003; // Prime number diff --git a/lib/crypto/src/field/instance.rs b/lib/crypto/src/field/instance.rs index 71ea2031c..6db6c59f6 100644 --- a/lib/crypto/src/field/instance.rs +++ b/lib/crypto/src/field/instance.rs @@ -3,7 +3,7 @@ #![allow(missing_docs)] use crate::{ - arithmetic::BigInt, + arithmetic::Uint, field::fp::{Fp256, Fp64, FpParams, LIMBS_256, LIMBS_64}, fp_from_num, from_num, }; @@ -12,40 +12,40 @@ pub type FpVesta = Fp256; pub struct VestaParam; impl FpParams for VestaParam { const GENERATOR: Fp256 = fp_from_num!("5"); - const MODULUS: BigInt = from_num!("28948022309329048855892746252171976963363056481941647379679742748393362948097"); + const MODULUS: Uint = from_num!("28948022309329048855892746252171976963363056481941647379679742748393362948097"); } pub type FpBabyBear = Fp64; pub struct BabyBearParam; impl FpParams for BabyBearParam { const GENERATOR: Fp64 = fp_from_num!("31"); - const MODULUS: BigInt = from_num!("2013265921"); + const MODULUS: Uint = from_num!("2013265921"); } pub type FpBLS12 = Fp256; pub struct BLS12Param; impl FpParams for BLS12Param { const GENERATOR: Fp256 = fp_from_num!("7"); - const MODULUS: BigInt = from_num!("52435875175126190479447740508185965837690552500527637822603658699938581184513"); + const MODULUS: Uint = from_num!("52435875175126190479447740508185965837690552500527637822603658699938581184513"); } pub type FpBN256 = Fp256; pub struct BN256Param; impl FpParams for BN256Param { const GENERATOR: Fp256 = fp_from_num!("7"); - const MODULUS: BigInt = from_num!("21888242871839275222246405745257275088548364400416034343698204186575808495617"); + const MODULUS: Uint = from_num!("21888242871839275222246405745257275088548364400416034343698204186575808495617"); } pub type FpGoldiLocks = Fp64; pub struct GoldiLocksParam; impl FpParams for GoldiLocksParam { const GENERATOR: Fp64 = fp_from_num!("7"); - const MODULUS: BigInt = from_num!("18446744069414584321"); + const MODULUS: Uint = from_num!("18446744069414584321"); } pub type FpPallas = Fp256; pub struct PallasParam; impl FpParams for PallasParam { const GENERATOR: Fp256 = fp_from_num!("5"); - const MODULUS: BigInt = from_num!("28948022309329048855892746252171976963363056481941560715954676764349967630337"); + const MODULUS: Uint = from_num!("28948022309329048855892746252171976963363056481941560715954676764349967630337"); } From a9ece5d32fe39d4d7ee1994bdd53739ce53339b5 Mon Sep 17 00:00:00 2001 From: Alisander Qoshqosh Date: Wed, 15 Jan 2025 14:17:20 +0400 Subject: [PATCH 58/61] ++ --- lib/crypto/src/arithmetic/mod.rs | 173 ++++++++++++++++++------------- lib/crypto/src/const_helpers.rs | 2 +- lib/crypto/src/field/fp.rs | 108 ++++++++++--------- lib/crypto/src/field/mod.rs | 2 +- 4 files changed, 160 insertions(+), 125 deletions(-) diff --git a/lib/crypto/src/arithmetic/mod.rs b/lib/crypto/src/arithmetic/mod.rs index 02404a48c..5698ac863 100644 --- a/lib/crypto/src/arithmetic/mod.rs +++ b/lib/crypto/src/arithmetic/mod.rs @@ -25,20 +25,43 @@ pub type Limbs = [Limb; N]; pub type WideLimb = u128; // TODO#q: Refactor types to: -// - Fp(Limbs) - residue classes modulo prime numbers -// - Uint(Limbs) - normal big integers. Make sense to implement only -// constant operations necessary for hex parsing (uint.rs) -// - Limbs([Limb;N]) - Wrapper type for limbs. (limbs.rs) // - Odd> - Odd numbers. (odd.rs) // - Rename u64 and u128 to Limb and WideLimb // - Rename functions *_with_carry to carrying_* #[derive(Copy, Clone, PartialEq, Eq, Hash, Zeroize)] -pub struct Uint(pub Limbs); +pub struct Uint { + pub limbs: Limbs, +} + +/// Declare [`Uint`] types for different bit sizes. +macro_rules! declare_num { + ($num:ident, $bits:expr) => { + #[doc = "Unsigned integer with "] + #[doc = stringify!($bits)] + #[doc = "bits size."] + pub type $num = $crate::arithmetic::Uint< + { usize::div_ceil($bits, $crate::arithmetic::Limb::BITS as usize) }, + >; + }; +} + +declare_num!(U64, 64); +declare_num!(U128, 128); +declare_num!(U192, 192); +declare_num!(U256, 256); +declare_num!(U384, 384); +declare_num!(U448, 448); +declare_num!(U512, 512); +declare_num!(U576, 576); +declare_num!(U640, 640); +declare_num!(U704, 704); +declare_num!(U768, 768); +declare_num!(U832, 832); impl Default for Uint { fn default() -> Self { - Self([0u64; N]) + Self { limbs: [0u64; N] } } } @@ -48,47 +71,47 @@ impl Uint { pub const ZERO: Self = Self::zero(); pub const fn new(value: [u64; N]) -> Self { - Self(value) + Self { limbs: value } } pub const fn as_limbs(&self) -> &[Limb; N] { - &self.0 + &self.limbs } // TODO#q: remove zero() and one() in favour of const ONE and const ZERO pub const fn zero() -> Self { - Self([0u64; N]) + Self { limbs: [0u64; N] } } pub const fn one() -> Self { let mut one = Self::zero(); - one.0[0] = 1; + one.limbs[0] = 1; one } // TODO#q: add another conversions from u8, u16 and so on pub const fn from_u32(val: u32) -> Self { let mut repr = Self::zero(); - repr.0[0] = val as u64; + repr.limbs[0] = val as u64; repr } #[doc(hidden)] pub const fn const_is_even(&self) -> bool { - self.0[0] % 2 == 0 + self.limbs[0] % 2 == 0 } #[doc(hidden)] pub const fn const_is_odd(&self) -> bool { - self.0[0] % 2 == 1 + self.limbs[0] % 2 == 1 } #[doc(hidden)] pub const fn mod_4(&self) -> u8 { // To compute n % 4, we need to simply look at the // 2 least significant bits of n, and check their value mod 4. - (((self.0[0] << 62) >> 62) % 4) as u8 + (((self.limbs[0] << 62) >> 62) % 4) as u8 } /// Compute a right shift of `self` @@ -98,10 +121,10 @@ impl Uint { let mut result = *self; let mut t = 0; crate::const_for!((i in 0..N) { - let a = result.0[N - i - 1]; + let a = result.limbs[N - i - 1]; let t2 = a << 63; - result.0[N - i - 1] >>= 1; - result.0[N - i - 1] |= t; + result.limbs[N - i - 1] >>= 1; + result.limbs[N - i - 1] |= t; t = t2; }); result @@ -109,8 +132,8 @@ impl Uint { const fn const_geq(&self, other: &Self) -> bool { const_for!((i in 0..N) { - let a = self.0[N - i - 1]; - let b = other.0[N - i - 1]; + let a = self.limbs[N - i - 1]; + let b = other.limbs[N - i - 1]; if a < b { return false; } else if a > b { @@ -128,7 +151,7 @@ impl Uint { let mut two_adicity = 0; // Since `self` is odd, we can always subtract one // without a borrow - self.0[0] -= 1; + self.limbs[0] -= 1; while self.const_is_even() { self = self.const_shr(); two_adicity += 1; @@ -143,7 +166,7 @@ impl Uint { assert!(self.const_is_odd()); // Since `self` is odd, we can always subtract one // without a borrow - self.0[0] -= 1; + self.limbs[0] -= 1; while self.const_is_even() { self = self.const_shr(); } @@ -157,7 +180,7 @@ impl Uint { #[doc(hidden)] pub const fn divide_by_2_round_down(mut self) -> Self { if self.const_is_odd() { - self.0[0] -= 1; + self.limbs[0] -= 1; } self.const_shr() } @@ -165,7 +188,7 @@ impl Uint { /// Find the number of bits in the binary decomposition of `self`. #[doc(hidden)] pub const fn const_num_bits(self) -> u32 { - ((N - 1) * 64) as u32 + (64 - self.0[N - 1].leading_zeros()) + ((N - 1) * 64) as u32 + (64 - self.limbs[N - 1].leading_zeros()) } #[inline] @@ -176,7 +199,7 @@ impl Uint { let mut borrow = 0; const_for!((i in 0..N) { - (self.0[i], borrow) = sbb(self.0[i], other.0[i], borrow); + (self.limbs[i], borrow) = sbb(self.limbs[i], other.limbs[i], borrow); }); (self, borrow != 0) @@ -187,7 +210,7 @@ impl Uint { pub(crate) fn mul2(&mut self) -> bool { let mut last = 0; for i in 0..N { - let a = &mut self.0[i]; + let a = &mut self.limbs[i]; let tmp = *a >> 63; *a <<= 1; *a |= last; @@ -204,7 +227,7 @@ impl Uint { let mut carry = 0; crate::const_for!((i in 0..N) { - (self.0[i], carry) = adc(self.0[i], other.0[i], carry); + (self.limbs[i], carry) = adc(self.limbs[i], other.limbs[i], carry); }); (self, carry != 0) @@ -213,10 +236,10 @@ impl Uint { const fn const_mul2_with_carry(mut self) -> (Self, bool) { let mut last = 0; crate::const_for!((i in 0..N) { - let a = self.0[i]; + let a = self.limbs[i]; let tmp = a >> 63; - self.0[i] <<= 1; - self.0[i] |= last; + self.limbs[i] <<= 1; + self.limbs[i] |= last; last = tmp; }); (self, last != 0) @@ -225,7 +248,7 @@ impl Uint { pub(crate) const fn const_is_zero(&self) -> bool { let mut is_zero = true; crate::const_for!((i in 0..N) { - is_zero &= self.0[i] == 0; + is_zero &= self.limbs[i] == 0; }); is_zero } @@ -248,7 +271,7 @@ impl Uint { pub fn div2(&mut self) { let mut t = 0; - for a in self.0.iter_mut().rev() { + for a in self.limbs.iter_mut().rev() { let t2 = *a << 63; *a >>= 1; *a |= t; @@ -262,7 +285,7 @@ impl Uint { let mut carry = false; unroll6_for!((i in 0..N) { - carry = adc_for_add_with_carry(&mut self.0[i], other.0[i], carry); + carry = adc_for_add_with_carry(&mut self.limbs[i], other.limbs[i], carry); }); carry @@ -274,7 +297,7 @@ impl Uint { unroll6_for!((i in 0..N) { borrow = - sbb_for_sub_with_borrow(&mut self.0[i], other.0[i], borrow); + sbb_for_sub_with_borrow(&mut self.limbs[i], other.limbs[i], borrow); }); borrow @@ -312,17 +335,21 @@ impl Uint { if k >= N { let (n, c) = ct_mac_with_carry( - hi.0[k - N], - self.0[i], - rhs.0[j], + hi.limbs[k - N], + self.limbs[i], + rhs.limbs[j], carry, ); - hi.0[k - N] = n; + hi.limbs[k - N] = n; carry = c; } else { - let (n, c) = - ct_mac_with_carry(lo.0[k], self.0[i], rhs.0[j], carry); - lo.0[k] = n; + let (n, c) = ct_mac_with_carry( + lo.limbs[k], + self.limbs[i], + rhs.limbs[j], + carry, + ); + lo.limbs[k] = n; carry = c; } @@ -330,9 +357,9 @@ impl Uint { } if i + j >= N { - hi.0[i + j - N] = carry; + hi.limbs[i + j - N] = carry; } else { - lo.0[i + j] = carry; + lo.limbs[i + j] = carry; } i += 1; } @@ -348,13 +375,13 @@ impl Uint { let mut i = 0; while i < N { - let (w, c) = ct_adc(self.0[i], rhs.0[i], carry); + let (w, c) = ct_adc(self.limbs[i], rhs.limbs[i], carry); limbs[i] = w; carry = c; i += 1; } - (Self(limbs), carry) + (Self { limbs }, carry) } /// Create a new [`Uint`] from the provided little endian bytes. @@ -517,8 +544,8 @@ impl Ord for Uint { fn cmp(&self, other: &Self) -> core::cmp::Ordering { use core::cmp::Ordering; unroll6_for!((i in 0..N) { - let a = &self.0[N - i - 1]; - let b = &other.0[N - i - 1]; + let a = &self.limbs[N - i - 1]; + let b = &other.limbs[N - i - 1]; match a.cmp(b) { Ordering::Equal => {} order => return order, @@ -546,14 +573,14 @@ impl PartialOrd for Uint { impl AsMut<[u64]> for Uint { #[inline] fn as_mut(&mut self) -> &mut [u64] { - &mut self.0 + &mut self.limbs } } impl AsRef<[u64]> for Uint { #[inline] fn as_ref(&self) -> &[u64] { - &self.0 + &self.limbs } } @@ -586,7 +613,7 @@ impl From for Uint { #[inline] fn from(val: u64) -> Uint { let mut repr = Self::default(); - repr.0[0] = val; + repr.limbs[0] = val; repr } } @@ -595,7 +622,7 @@ impl From for Uint { #[inline] fn from(val: u32) -> Uint { let mut repr = Self::default(); - repr.0[0] = val.into(); + repr.limbs[0] = val.into(); repr } } @@ -604,7 +631,7 @@ impl From for Uint { #[inline] fn from(val: u16) -> Uint { let mut repr = Self::default(); - repr.0[0] = val.into(); + repr.limbs[0] = val.into(); repr } } @@ -613,7 +640,7 @@ impl From for Uint { #[inline] fn from(val: u8) -> Uint { let mut repr = Self::default(); - repr.0[0] = val.into(); + repr.limbs[0] = val.into(); repr } } @@ -637,7 +664,7 @@ impl From> for num_bigint::BigInt { impl, const N: usize> BitXorAssign for Uint { fn bitxor_assign(&mut self, rhs: B) { - (0..N).for_each(|i| self.0[i] ^= rhs.borrow().0[i]) + (0..N).for_each(|i| self.limbs[i] ^= rhs.borrow().limbs[i]) } } @@ -652,7 +679,7 @@ impl, const N: usize> BitXor for Uint { impl, const N: usize> BitAndAssign for Uint { fn bitand_assign(&mut self, rhs: B) { - (0..N).for_each(|i| self.0[i] &= rhs.borrow().0[i]) + (0..N).for_each(|i| self.limbs[i] &= rhs.borrow().limbs[i]) } } @@ -667,7 +694,7 @@ impl, const N: usize> BitAnd for Uint { impl, const N: usize> BitOrAssign for Uint { fn bitor_assign(&mut self, rhs: B) { - (0..N).for_each(|i| self.0[i] |= rhs.borrow().0[i]) + (0..N).for_each(|i| self.limbs[i] |= rhs.borrow().limbs[i]) } } @@ -695,7 +722,7 @@ impl ShrAssign for Uint { while rhs >= 64 { let mut t = 0; - for limb in self.0.iter_mut().rev() { + for limb in self.limbs.iter_mut().rev() { core::mem::swap(&mut t, limb); } rhs -= 64; @@ -703,7 +730,7 @@ impl ShrAssign for Uint { if rhs > 0 { let mut t = 0; - for a in self.0.iter_mut().rev() { + for a in self.limbs.iter_mut().rev() { let t2 = *a << (64 - rhs); *a >>= rhs; *a |= t; @@ -744,7 +771,7 @@ impl ShlAssign for Uint { while rhs >= 64 { let mut t = 0; for i in 0..N { - core::mem::swap(&mut t, &mut self.0[i]); + core::mem::swap(&mut t, &mut self.limbs[i]); } rhs -= 64; } @@ -753,7 +780,7 @@ impl ShlAssign for Uint { let mut t = 0; #[allow(unused)] for i in 0..N { - let a = &mut self.0[i]; + let a = &mut self.limbs[i]; let t2 = *a >> (64 - rhs); *a <<= rhs; *a |= t; @@ -784,7 +811,7 @@ impl Not for Uint { fn not(self) -> Self::Output { let mut result = Self::zero(); for i in 0..N { - result.0[i] = !self.0[i]; + result.limbs[i] = !self.limbs[i]; } result } @@ -835,7 +862,7 @@ pub trait BigInteger: /// # Example /// /// ``` - /// use openzeppelin_crypto::arithmetic::{BigInteger, crypto_bigint::U64}; + /// use openzeppelin_crypto::arithmetic::{BigInteger, U64}; /// /// let mut one = U64::from(1u64); /// assert!(one.is_odd()); @@ -847,7 +874,7 @@ pub trait BigInteger: /// # Example /// /// ``` - /// use openzeppelin_crypto::arithmetic::{BigInteger, crypto_bigint::U64}; + /// use openzeppelin_crypto::arithmetic::{BigInteger, U64}; /// /// let mut two = U64::from(2u64); /// assert!(two.is_even()); @@ -859,7 +886,7 @@ pub trait BigInteger: /// # Example /// /// ``` - /// use openzeppelin_crypto::arithmetic::{BigInteger, crypto_bigint::U64}; + /// use openzeppelin_crypto::arithmetic::{BigInteger, U64}; /// /// let mut zero = U64::from(0u64); /// assert!(zero.is_zero()); @@ -869,7 +896,7 @@ pub trait BigInteger: /// Compute the minimum number of bits needed to encode this number. /// # Example /// ``` - /// use openzeppelin_crypto::arithmetic::{BigInteger, crypto_bigint::U64}; + /// use openzeppelin_crypto::arithmetic::{BigInteger, U64}; /// /// let zero = U64::from(0u64); /// assert_eq!(zero.num_bits(), 0); @@ -886,7 +913,7 @@ pub trait BigInteger: /// # Example /// /// ``` - /// use openzeppelin_crypto::arithmetic::{BigInteger, crypto_bigint::U64}; + /// use openzeppelin_crypto::arithmetic::{BigInteger, U64}; /// /// let mut one = U64::from(1u64); /// assert!(one.get_bit(0)); @@ -909,7 +936,7 @@ impl BigInteger for Uint { const NUM_LIMBS: usize = N; fn is_odd(&self) -> bool { - self.0[0] & 1 == 1 + self.limbs[0] & 1 == 1 } fn is_even(&self) -> bool { @@ -917,12 +944,12 @@ impl BigInteger for Uint { } fn is_zero(&self) -> bool { - self.0.iter().all(Zero::is_zero) + self.limbs.iter().all(Zero::is_zero) } fn num_bits(&self) -> usize { let mut ret = N as u32 * 64; - for i in self.0.iter().rev() { + for i in self.limbs.iter().rev() { let leading = i.leading_zeros(); ret -= leading; if leading != 64 { @@ -939,7 +966,7 @@ impl BigInteger for Uint { } else { let limb = i / 64; let bit = i - (64 * limb); - (self.0[limb] & (1 << bit)) != 0 + (self.limbs[limb] & (1 << bit)) != 0 } } @@ -948,7 +975,7 @@ impl BigInteger for Uint { } fn into_bytes_le(self) -> alloc::vec::Vec { - self.0.iter().flat_map(|&limb| limb.to_le_bytes()).collect() + self.limbs.iter().flat_map(|&limb| limb.to_le_bytes()).collect() } } @@ -1088,9 +1115,9 @@ pub const fn ct_adc(lhs: Limb, rhs: Limb, carry: Limb) -> (Limb, Limb) { pub const fn ct_ge(a: &Uint, b: &Uint) -> bool { const_for!((i in 0..N) { - if a.0[i] < b.0[i] { + if a.limbs[i] < b.limbs[i] { return false; - } else if a.0[i] > b.0[i] { + } else if a.limbs[i] > b.limbs[i] { return true; } }); @@ -1100,7 +1127,7 @@ pub const fn ct_ge(a: &Uint, b: &Uint) -> bool { // TODO#q: compare with const_is_zero pub const fn ct_eq(a: &Uint, b: &Uint) -> bool { const_for!((i in 0..N) { - if a.0[i] != b.0[i] { + if a.limbs[i] != b.limbs[i] { return false; } }); diff --git a/lib/crypto/src/const_helpers.rs b/lib/crypto/src/const_helpers.rs index 8bf7552d8..9d56080c2 100644 --- a/lib/crypto/src/const_helpers.rs +++ b/lib/crypto/src/const_helpers.rs @@ -88,7 +88,7 @@ macro_rules! const_modulo { let mut carry; while i >= 0 { (remainder, carry) = remainder.const_mul2_with_carry(); - remainder.0[0] |= $a.get_bit(i as usize) as u64; + remainder.limbs[0] |= $a.get_bit(i as usize) as u64; if remainder.const_geq($divisor) || carry { let (r, borrow) = remainder.const_sub_with_borrow($divisor); remainder = r; diff --git a/lib/crypto/src/field/fp.rs b/lib/crypto/src/field/fp.rs index 6709bcdd8..281d61e0d 100644 --- a/lib/crypto/src/field/fp.rs +++ b/lib/crypto/src/field/fp.rs @@ -63,7 +63,7 @@ pub trait FpParams: Send + Sync + 'static + Sized { #[inline(always)] fn add_assign(a: &mut Fp, b: &Fp) { // This cannot exceed the backing capacity. - let c = a.0.add_with_carry(&b.0); + let c = a.montgomery_form.add_with_carry(&b.montgomery_form); // However, it may need to be reduced if Self::MODULUS_HAS_SPARE_BIT { a.subtract_modulus() @@ -76,17 +76,17 @@ pub trait FpParams: Send + Sync + 'static + Sized { #[inline(always)] fn sub_assign(a: &mut Fp, b: &Fp) { // If `other` is larger than `self`, add the modulus to self first. - if b.0 > a.0 { - a.0.add_with_carry(&Self::MODULUS); + if b.montgomery_form > a.montgomery_form { + a.montgomery_form.add_with_carry(&Self::MODULUS); } - a.0.sub_with_borrow(&b.0); + a.montgomery_form.sub_with_borrow(&b.montgomery_form); } /// Set `a = a + a`. #[inline(always)] fn double_in_place(a: &mut Fp) { // This cannot exceed the backing capacity. - let c = a.0.mul2(); + let c = a.montgomery_form.mul2(); // However, it may need to be reduced. if Self::MODULUS_HAS_SPARE_BIT { a.subtract_modulus() @@ -100,8 +100,8 @@ pub trait FpParams: Send + Sync + 'static + Sized { fn neg_in_place(a: &mut Fp) { if !a.is_zero() { let mut tmp = Self::MODULUS; - tmp.sub_with_borrow(&a.0); - a.0 = tmp; + tmp.sub_with_borrow(&a.montgomery_form); + a.montgomery_form = tmp; } } @@ -145,24 +145,24 @@ pub trait FpParams: Send + Sync + 'static + Sized { let one = Uint::ONE; - let mut u = a.0; + let mut u = a.montgomery_form; let mut v = Self::MODULUS; let mut b = Fp::new_unchecked(Self::R2); // Avoids unnecessary reduction step. let mut c = Fp::zero(); while u != one && v != one { - // TODO#q: why code here duplicated? - // TODO#q: Inverse consumes incredible ammount of gas + // TODO#q: Inverse consumes incredible amount of gas while u.is_even() { u.div2(); - if b.0.is_even() { - b.0.div2(); + if b.montgomery_form.is_even() { + b.montgomery_form.div2(); } else { - let carry = b.0.add_with_carry(&Self::MODULUS); - b.0.div2(); + let carry = + b.montgomery_form.add_with_carry(&Self::MODULUS); + b.montgomery_form.div2(); if !Self::MODULUS_HAS_SPARE_BIT && carry { - (b.0).0[N - 1] |= 1 << 63; + b.montgomery_form.limbs[N - 1] |= 1 << 63; } } } @@ -170,13 +170,14 @@ pub trait FpParams: Send + Sync + 'static + Sized { while v.is_even() { v.div2(); - if c.0.is_even() { - c.0.div2(); + if c.montgomery_form.is_even() { + c.montgomery_form.div2(); } else { - let carry = c.0.add_with_carry(&Self::MODULUS); - c.0.div2(); + let carry = + c.montgomery_form.add_with_carry(&Self::MODULUS); + c.montgomery_form.div2(); if !Self::MODULUS_HAS_SPARE_BIT && carry { - (c.0).0[N - 1] |= 1 << 63; + c.montgomery_form.limbs[N - 1] |= 1 << 63; } } } @@ -216,18 +217,19 @@ pub trait FpParams: Send + Sync + 'static + Sized { #[must_use] #[inline(always)] fn into_bigint(a: Fp) -> Uint { - let mut r = (a.0).0; + let mut r = a.montgomery_form.limbs; // Montgomery Reduction for i in 0..N { let k = r[i].wrapping_mul(Self::INV); // let mut carry = 0; - let (_, mut carry) = arithmetic::mac(r[i], k, Self::MODULUS.0[0]); + let (_, mut carry) = + arithmetic::mac(r[i], k, Self::MODULUS.limbs[0]); for j in 1..N { (r[(j + i) % N], carry) = arithmetic::carrying_mac( r[(j + i) % N], k, - Self::MODULUS.0[j], + Self::MODULUS.limbs[j], carry, ); } @@ -254,14 +256,14 @@ pub const fn inv, const N: usize>() -> u64 { // Square inv = inv.wrapping_mul(inv); // Multiply - inv = inv.wrapping_mul(T::MODULUS.0[0]); + inv = inv.wrapping_mul(T::MODULUS.limbs[0]); }); inv.wrapping_neg() } #[inline] pub const fn modulus_has_spare_bit, const N: usize>() -> bool { - T::MODULUS.0[N - 1] >> 63 == 0 + T::MODULUS.limbs[N - 1] >> 63 == 0 } /// Represents an element of the prime field `F_p`, where `p == P::MODULUS`. @@ -270,13 +272,14 @@ pub const fn modulus_has_spare_bit, const N: usize>() -> bool { /// for 64-bit systems and N * 32 bits for 32-bit systems. #[derive(Educe)] #[educe(Default, Clone, Copy, PartialEq, Eq)] -pub struct Fp, const N: usize>( +pub struct Fp, const N: usize> { /// Contains the element in Montgomery form for efficient multiplication. /// To convert an element to a [`Uint`], use [`FpParams::into_bigint`] /// or `into`. - Uint, - #[doc(hidden)] pub PhantomData

, -); + montgomery_form: Uint, + #[doc(hidden)] + phantom: PhantomData

, +} /// Declare [`Fp`] types for different bit sizes. macro_rules! declare_fp { @@ -327,7 +330,7 @@ impl, const N: usize> Fp { pub const R: Uint = P::R; /// Additive identity of the field, i.e., the element `e` /// such that, for all elements `f` of the field, `e + f = f`. - pub const ZERO: Fp = Fp::new_unchecked(Uint([0; N])); + pub const ZERO: Fp = Fp::new_unchecked(Uint { limbs: [0; N] }); /// Construct a new field element from [`Uint`]. /// @@ -337,19 +340,19 @@ impl, const N: usize> Fp { #[must_use] #[inline(always)] pub const fn new_unchecked(element: Uint) -> Self { - Self(element, PhantomData) + Self { montgomery_form: element, phantom: PhantomData } } #[doc(hidden)] #[inline(always)] pub fn is_geq_modulus(&self) -> bool { - self.0 >= P::MODULUS + self.montgomery_form >= P::MODULUS } #[inline(always)] fn subtract_modulus(&mut self) { if self.is_geq_modulus() { - self.0.sub_with_borrow(&Self::MODULUS); + self.montgomery_form.sub_with_borrow(&Self::MODULUS); } } @@ -378,11 +381,14 @@ impl, const N: usize> Fp { /// [`struct@Uint`] data type. #[inline] pub const fn new(element: Uint) -> Self { - let mut r = Self(element, PhantomData); + let mut r = Self { montgomery_form: element, phantom: PhantomData }; if r.const_is_zero() { r } else { - r = r.const_mul(&Fp(P::R2, PhantomData)); + r = r.const_mul(&Fp { + montgomery_form: P::R2, + phantom: PhantomData, + }); r } } @@ -397,13 +403,13 @@ impl, const N: usize> Fp { } const fn const_is_zero(&self) -> bool { - self.0.const_is_zero() + self.montgomery_form.const_is_zero() } #[inline(always)] fn subtract_modulus_with_carry(&mut self, carry: bool) { if carry || self.is_geq_modulus() { - self.0.sub_with_borrow(&Self::MODULUS); + self.montgomery_form.sub_with_borrow(&Self::MODULUS); } } @@ -417,15 +423,15 @@ impl, const N: usize> Fp { if k >= N { (hi[k - N], carry) = arithmetic::carrying_mac( hi[k - N], - (self.0).0[i], - (other.0).0[j], + self.montgomery_form.limbs[i], + other.montgomery_form.limbs[j], carry ); } else { (lo[k], carry) = arithmetic::carrying_mac( lo[k], - (self.0).0[i], - (other.0).0[j], + self.montgomery_form.limbs[i], + other.montgomery_form.limbs[j], carry ); } @@ -437,7 +443,7 @@ impl, const N: usize> Fp { unroll6_for!((i in 0..N) { let tmp = lo[i].wrapping_mul(P::INV); - let (_, mut carry) = arithmetic::mac(lo[i], tmp, P::MODULUS.0[0]); + let (_, mut carry) = arithmetic::mac(lo[i], tmp, P::MODULUS.limbs[0]); unroll6_for!((j in 1..N) { let k = i + j; @@ -445,14 +451,14 @@ impl, const N: usize> Fp { (hi[k - N], carry) = arithmetic::carrying_mac( hi[k - N], tmp, - P::MODULUS.0[j], + P::MODULUS.limbs[j], carry ); } else { (lo[k], carry) = arithmetic::carrying_mac( lo[k], tmp, - P::MODULUS.0[j], + P::MODULUS.limbs[j], carry ); } @@ -461,16 +467,16 @@ impl, const N: usize> Fp { }); unroll6_for!((i in 0..N) { - (self.0).0[i] = hi[i]; + self.montgomery_form.limbs[i] = hi[i]; }); (carry2 != 0, self) } const fn const_is_valid(&self) -> bool { const_for!((i in 0..N) { - if (self.0).0[N - i - 1] < P::MODULUS.0[N - i - 1] { + if self.montgomery_form.limbs[N - i - 1] < P::MODULUS.limbs[N - i - 1] { return true - } else if (self.0).0[N - i - 1] > P::MODULUS.0[N - i - 1] { + } else if self.montgomery_form.limbs[N - i - 1] > P::MODULUS.limbs[N - i - 1] { return false } }); @@ -480,7 +486,8 @@ impl, const N: usize> Fp { #[inline] const fn const_subtract_modulus(mut self) -> Self { if !self.const_is_valid() { - self.0 = Self::sub_with_borrow(&self.0, &P::MODULUS); + self.montgomery_form = + Self::sub_with_borrow(&self.montgomery_form, &P::MODULUS); } self } @@ -488,7 +495,8 @@ impl, const N: usize> Fp { #[inline] const fn const_subtract_modulus_with_carry(mut self, carry: bool) -> Self { if carry || !self.const_is_valid() { - self.0 = Self::sub_with_borrow(&self.0, &P::MODULUS); + self.montgomery_form = + Self::sub_with_borrow(&self.montgomery_form, &P::MODULUS); } self } @@ -1056,7 +1064,7 @@ impl, const N: usize> zeroize::Zeroize for Fp { // The phantom data does not contain element-specific data // and thus does not need to be zeroized. fn zeroize(&mut self) { - self.0.zeroize(); + self.montgomery_form.zeroize(); } } diff --git a/lib/crypto/src/field/mod.rs b/lib/crypto/src/field/mod.rs index 2deb07ce4..82f8750b0 100644 --- a/lib/crypto/src/field/mod.rs +++ b/lib/crypto/src/field/mod.rs @@ -8,7 +8,7 @@ //! ## Example //! ```rust //! use openzeppelin_crypto::{ -//! arithmetic::crypto_bigint::U64, +//! arithmetic::U64, //! field::{ //! fp::{Fp64, FpParams, LIMBS_64}, //! group::AdditiveGroup, From 7f2fe1c68902a9d93ac40b81fad57252ec385e70 Mon Sep 17 00:00:00 2001 From: Alisander Qoshqosh Date: Wed, 15 Jan 2025 14:36:06 +0400 Subject: [PATCH 59/61] use renegades poseidon from repo --- Cargo.lock | 12 +++++++----- examples/poseidon-renegades/Cargo.toml | 8 ++++---- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1d5fc91e8..79194aed3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1392,6 +1392,7 @@ checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" [[package]] name = "constants" version = "0.1.0" +source = "git+https://github.com/renegade-fi/renegade.git#c4816feb849b0c75b8b6e6f9ac981a3d2df520bb" dependencies = [ "ark-bn254 0.4.0", "ark-ec 0.4.2", @@ -3196,9 +3197,9 @@ version = "0.2.0-alpha.2" dependencies = [ "alloy", "alloy-primitives", - "ark-bn254 0.5.0", - "ark-ec 0.5.0", - "ark-ff 0.5.0", + "ark-bn254 0.4.0", + "ark-ec 0.4.2", + "ark-ff 0.4.2", "e2e", "eyre", "renegade-crypto", @@ -3493,9 +3494,10 @@ dependencies = [ [[package]] name = "renegade-crypto" version = "0.1.0" +source = "git+https://github.com/renegade-fi/renegade.git#c4816feb849b0c75b8b6e6f9ac981a3d2df520bb" dependencies = [ - "ark-ec 0.5.0", - "ark-ff 0.5.0", + "ark-ec 0.4.2", + "ark-ff 0.4.2", "bigdecimal", "constants", "itertools 0.10.5", diff --git a/examples/poseidon-renegades/Cargo.toml b/examples/poseidon-renegades/Cargo.toml index 64dba337b..43874cc54 100644 --- a/examples/poseidon-renegades/Cargo.toml +++ b/examples/poseidon-renegades/Cargo.toml @@ -9,10 +9,10 @@ version.workspace = true [dependencies] alloy-primitives.workspace = true stylus-sdk.workspace = true -renegade-crypto = { path = "../../../renegade/renegade-crypto", package = "renegade-crypto", default-features = false } -ark-ff = { version = "0.5" } -ark-ec = "0.5" -ark-bn254 = "0.5.0" +renegade-crypto = { git = "https://github.com/renegade-fi/renegade.git", package = "renegade-crypto", default-features = false } +ark-ff = { version = "0.4" } +ark-ec = "0.4" +ark-bn254 = "0.4.0" [dev-dependencies] alloy.workspace = true From 4efeb3e342cbd9597ee7a81fe5cbd465fe050cf4 Mon Sep 17 00:00:00 2001 From: Alisander Qoshqosh Date: Wed, 15 Jan 2025 14:49:57 +0400 Subject: [PATCH 60/61] add sol asm into comparison --- benches/src/PoseidonT3_asm.sol | 391 +++++++++++++++++++++++++++ benches/src/lib.rs | 1 + benches/src/main.rs | 5 +- benches/src/poseidon_asm_sol.rs | 451 ++++++++++++++++++++++++++++++++ 4 files changed, 846 insertions(+), 2 deletions(-) create mode 100644 benches/src/PoseidonT3_asm.sol create mode 100644 benches/src/poseidon_asm_sol.rs diff --git a/benches/src/PoseidonT3_asm.sol b/benches/src/PoseidonT3_asm.sol new file mode 100644 index 000000000..db712f8d7 --- /dev/null +++ b/benches/src/PoseidonT3_asm.sol @@ -0,0 +1,391 @@ +/// SPDX-License-Identifier: MIT +pragma solidity >=0.7.0; + +library PoseidonT3 { + uint constant M00 = 0x109b7f411ba0e4c9b2b70caf5c36a7b194be7c11ad24378bfedb68592ba8118b; + uint constant M01 = 0x2969f27eed31a480b9c36c764379dbca2cc8fdd1415c3dded62940bcde0bd771; + uint constant M02 = 0x143021ec686a3f330d5f9e654638065ce6cd79e28c5b3753326244ee65a1b1a7; + uint constant M10 = 0x16ed41e13bb9c0c66ae119424fddbcbc9314dc9fdbdeea55d6c64543dc4903e0; + uint constant M11 = 0x2e2419f9ec02ec394c9871c832963dc1b89d743c8c7b964029b2311687b1fe23; + uint constant M12 = 0x176cc029695ad02582a70eff08a6fd99d057e12e58e7d7b6b16cdfabc8ee2911; + + // See here for a simplified implementation: https://github.com/vimwitch/poseidon-solidity/blob/e57becdabb65d99fdc586fe1e1e09e7108202d53/contracts/Poseidon.sol#L40 + // Inspired by: https://github.com/iden3/circomlibjs/blob/v0.0.8/src/poseidon_slow.js + function hash(uint[2] memory) public pure returns (uint) { + assembly { + let F := 21888242871839275222246405745257275088548364400416034343698204186575808495617 + let M20 := 0x2b90bba00fca0589f617e7dcbfe82e0df706ab640ceb247b791a93b74e36736d + let M21 := 0x101071f0032379b697315876690f053d148d4e109f5fb065c8aacc55a0f89bfa + let M22 := 0x19a3fc0a56702bf417ba7fee3802593fa644470307043f7773279cd71d25d5e0 + + // load the inputs from memory + let state1 := add(mod(mload(0x80), F), 0x00f1445235f2148c5986587169fc1bcd887b08d4d00868df5696fff40956e864) + let state2 := add(mod(mload(0xa0), F), 0x08dff3487e8ac99e1f29a058d0fa80b930c728730b7ab36ce879f3890ecf73f5) + let scratch0 := mulmod(state1, state1, F) + state1 := mulmod(mulmod(scratch0, scratch0, F), state1, F) + scratch0 := mulmod(state2, state2, F) + state2 := mulmod(mulmod(scratch0, scratch0, F), state2, F) + scratch0 := add( + 0x2f27be690fdaee46c3ce28f7532b13c856c35342c84bda6e20966310fadc01d0, + add(add(15452833169820924772166449970675545095234312153403844297388521437673434406763, mulmod(state1, M10, F)), mulmod(state2, M20, F)) + ) + let scratch1 := add( + 0x2b2ae1acf68b7b8d2416bebf3d4f6234b763fe04b8043ee48b8327bebca16cf2, + add(add(18674271267752038776579386132900109523609358935013267566297499497165104279117, mulmod(state1, M11, F)), mulmod(state2, M21, F)) + ) + let scratch2 := add( + 0x0319d062072bef7ecca5eac06f97d4d55952c175ab6b03eae64b44c7dbf11cfa, + add(add(14817777843080276494683266178512808687156649753153012854386334860566696099579, mulmod(state1, M12, F)), mulmod(state2, M22, F)) + ) + let state0 := mulmod(scratch0, scratch0, F) + scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F) + state0 := mulmod(scratch1, scratch1, F) + scratch1 := mulmod(mulmod(state0, state0, F), scratch1, F) + state0 := mulmod(scratch2, scratch2, F) + scratch2 := mulmod(mulmod(state0, state0, F), scratch2, F) + state0 := add(0x28813dcaebaeaa828a376df87af4a63bc8b7bf27ad49c6298ef7b387bf28526d, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F))) + state1 := add(0x2727673b2ccbc903f181bf38e1c1d40d2033865200c352bc150928adddf9cb78, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F))) + state2 := add(0x234ec45ca27727c2e74abd2b2a1494cd6efbd43e340587d6b8fb9e31e65cc632, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F))) + scratch0 := mulmod(state0, state0, F) + state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F) + scratch0 := mulmod(state1, state1, F) + state1 := mulmod(mulmod(scratch0, scratch0, F), state1, F) + scratch0 := mulmod(state2, state2, F) + state2 := mulmod(mulmod(scratch0, scratch0, F), state2, F) + scratch0 := add(0x15b52534031ae18f7f862cb2cf7cf760ab10a8150a337b1ccd99ff6e8797d428, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F))) + scratch1 := add(0x0dc8fad6d9e4b35f5ed9a3d186b79ce38e0e8a8d1b58b132d701d4eecf68d1f6, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F))) + scratch2 := add(0x1bcd95ffc211fbca600f705fad3fb567ea4eb378f62e1fec97805518a47e4d9c, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F))) + state0 := mulmod(scratch0, scratch0, F) + scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F) + state0 := mulmod(scratch1, scratch1, F) + scratch1 := mulmod(mulmod(state0, state0, F), scratch1, F) + state0 := mulmod(scratch2, scratch2, F) + scratch2 := mulmod(mulmod(state0, state0, F), scratch2, F) + state0 := add(0x10520b0ab721cadfe9eff81b016fc34dc76da36c2578937817cb978d069de559, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F))) + state1 := add(0x1f6d48149b8e7f7d9b257d8ed5fbbaf42932498075fed0ace88a9eb81f5627f6, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F))) + state2 := add(0x1d9655f652309014d29e00ef35a2089bfff8dc1c816f0dc9ca34bdb5460c8705, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F))) + scratch0 := mulmod(state0, state0, F) + state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F) + scratch0 := add(0x04df5a56ff95bcafb051f7b1cd43a99ba731ff67e47032058fe3d4185697cc7d, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F))) + scratch1 := add(0x0672d995f8fff640151b3d290cedaf148690a10a8c8424a7f6ec282b6e4be828, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F))) + scratch2 := add(0x099952b414884454b21200d7ffafdd5f0c9a9dcc06f2708e9fc1d8209b5c75b9, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F))) + state0 := mulmod(scratch0, scratch0, F) + scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F) + state0 := add(0x052cba2255dfd00c7c483143ba8d469448e43586a9b4cd9183fd0e843a6b9fa6, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F))) + state1 := add(0x0b8badee690adb8eb0bd74712b7999af82de55707251ad7716077cb93c464ddc, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F))) + state2 := add(0x119b1590f13307af5a1ee651020c07c749c15d60683a8050b963d0a8e4b2bdd1, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F))) + scratch0 := mulmod(state0, state0, F) + state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F) + scratch0 := add(0x03150b7cd6d5d17b2529d36be0f67b832c4acfc884ef4ee5ce15be0bfb4a8d09, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F))) + scratch1 := add(0x2cc6182c5e14546e3cf1951f173912355374efb83d80898abe69cb317c9ea565, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F))) + scratch2 := add(0x005032551e6378c450cfe129a404b3764218cadedac14e2b92d2cd73111bf0f9, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F))) + state0 := mulmod(scratch0, scratch0, F) + scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F) + state0 := add(0x233237e3289baa34bb147e972ebcb9516469c399fcc069fb88f9da2cc28276b5, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F))) + state1 := add(0x05c8f4f4ebd4a6e3c980d31674bfbe6323037f21b34ae5a4e80c2d4c24d60280, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F))) + state2 := add(0x0a7b1db13042d396ba05d818a319f25252bcf35ef3aeed91ee1f09b2590fc65b, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F))) + scratch0 := mulmod(state0, state0, F) + state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F) + scratch0 := add(0x2a73b71f9b210cf5b14296572c9d32dbf156e2b086ff47dc5df542365a404ec0, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F))) + scratch1 := add(0x1ac9b0417abcc9a1935107e9ffc91dc3ec18f2c4dbe7f22976a760bb5c50c460, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F))) + scratch2 := add(0x12c0339ae08374823fabb076707ef479269f3e4d6cb104349015ee046dc93fc0, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F))) + state0 := mulmod(scratch0, scratch0, F) + scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F) + state0 := add(0x0b7475b102a165ad7f5b18db4e1e704f52900aa3253baac68246682e56e9a28e, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F))) + state1 := add(0x037c2849e191ca3edb1c5e49f6e8b8917c843e379366f2ea32ab3aa88d7f8448, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F))) + state2 := add(0x05a6811f8556f014e92674661e217e9bd5206c5c93a07dc145fdb176a716346f, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F))) + scratch0 := mulmod(state0, state0, F) + state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F) + scratch0 := add(0x29a795e7d98028946e947b75d54e9f044076e87a7b2883b47b675ef5f38bd66e, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F))) + scratch1 := add(0x20439a0c84b322eb45a3857afc18f5826e8c7382c8a1585c507be199981fd22f, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F))) + scratch2 := add(0x2e0ba8d94d9ecf4a94ec2050c7371ff1bb50f27799a84b6d4a2a6f2a0982c887, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F))) + state0 := mulmod(scratch0, scratch0, F) + scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F) + state0 := add(0x143fd115ce08fb27ca38eb7cce822b4517822cd2109048d2e6d0ddcca17d71c8, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F))) + state1 := add(0x0c64cbecb1c734b857968dbbdcf813cdf8611659323dbcbfc84323623be9caf1, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F))) + state2 := add(0x028a305847c683f646fca925c163ff5ae74f348d62c2b670f1426cef9403da53, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F))) + scratch0 := mulmod(state0, state0, F) + state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F) + scratch0 := add(0x2e4ef510ff0b6fda5fa940ab4c4380f26a6bcb64d89427b824d6755b5db9e30c, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F))) + scratch1 := add(0x0081c95bc43384e663d79270c956ce3b8925b4f6d033b078b96384f50579400e, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F))) + scratch2 := add(0x2ed5f0c91cbd9749187e2fade687e05ee2491b349c039a0bba8a9f4023a0bb38, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F))) + state0 := mulmod(scratch0, scratch0, F) + scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F) + state0 := add(0x30509991f88da3504bbf374ed5aae2f03448a22c76234c8c990f01f33a735206, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F))) + state1 := add(0x1c3f20fd55409a53221b7c4d49a356b9f0a1119fb2067b41a7529094424ec6ad, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F))) + state2 := add(0x10b4e7f3ab5df003049514459b6e18eec46bb2213e8e131e170887b47ddcb96c, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F))) + scratch0 := mulmod(state0, state0, F) + state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F) + scratch0 := add(0x2a1982979c3ff7f43ddd543d891c2abddd80f804c077d775039aa3502e43adef, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F))) + scratch1 := add(0x1c74ee64f15e1db6feddbead56d6d55dba431ebc396c9af95cad0f1315bd5c91, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F))) + scratch2 := add(0x07533ec850ba7f98eab9303cace01b4b9e4f2e8b82708cfa9c2fe45a0ae146a0, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F))) + state0 := mulmod(scratch0, scratch0, F) + scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F) + state0 := add(0x21576b438e500449a151e4eeaf17b154285c68f42d42c1808a11abf3764c0750, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F))) + state1 := add(0x2f17c0559b8fe79608ad5ca193d62f10bce8384c815f0906743d6930836d4a9e, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F))) + state2 := add(0x2d477e3862d07708a79e8aae946170bc9775a4201318474ae665b0b1b7e2730e, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F))) + scratch0 := mulmod(state0, state0, F) + state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F) + scratch0 := add(0x162f5243967064c390e095577984f291afba2266c38f5abcd89be0f5b2747eab, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F))) + scratch1 := add(0x2b4cb233ede9ba48264ecd2c8ae50d1ad7a8596a87f29f8a7777a70092393311, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F))) + scratch2 := add(0x2c8fbcb2dd8573dc1dbaf8f4622854776db2eece6d85c4cf4254e7c35e03b07a, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F))) + state0 := mulmod(scratch0, scratch0, F) + scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F) + state0 := add(0x1d6f347725e4816af2ff453f0cd56b199e1b61e9f601e9ade5e88db870949da9, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F))) + state1 := add(0x204b0c397f4ebe71ebc2d8b3df5b913df9e6ac02b68d31324cd49af5c4565529, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F))) + state2 := add(0x0c4cb9dc3c4fd8174f1149b3c63c3c2f9ecb827cd7dc25534ff8fb75bc79c502, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F))) + scratch0 := mulmod(state0, state0, F) + state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F) + scratch0 := add(0x174ad61a1448c899a25416474f4930301e5c49475279e0639a616ddc45bc7b54, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F))) + scratch1 := add(0x1a96177bcf4d8d89f759df4ec2f3cde2eaaa28c177cc0fa13a9816d49a38d2ef, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F))) + scratch2 := add(0x066d04b24331d71cd0ef8054bc60c4ff05202c126a233c1a8242ace360b8a30a, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F))) + state0 := mulmod(scratch0, scratch0, F) + scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F) + state0 := add(0x2a4c4fc6ec0b0cf52195782871c6dd3b381cc65f72e02ad527037a62aa1bd804, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F))) + state1 := add(0x13ab2d136ccf37d447e9f2e14a7cedc95e727f8446f6d9d7e55afc01219fd649, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F))) + state2 := add(0x1121552fca26061619d24d843dc82769c1b04fcec26f55194c2e3e869acc6a9a, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F))) + scratch0 := mulmod(state0, state0, F) + state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F) + scratch0 := add(0x00ef653322b13d6c889bc81715c37d77a6cd267d595c4a8909a5546c7c97cff1, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F))) + scratch1 := add(0x0e25483e45a665208b261d8ba74051e6400c776d652595d9845aca35d8a397d3, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F))) + scratch2 := add(0x29f536dcb9dd7682245264659e15d88e395ac3d4dde92d8c46448db979eeba89, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F))) + state0 := mulmod(scratch0, scratch0, F) + scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F) + state0 := add(0x2a56ef9f2c53febadfda33575dbdbd885a124e2780bbea170e456baace0fa5be, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F))) + state1 := add(0x1c8361c78eb5cf5decfb7a2d17b5c409f2ae2999a46762e8ee416240a8cb9af1, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F))) + state2 := add(0x151aff5f38b20a0fc0473089aaf0206b83e8e68a764507bfd3d0ab4be74319c5, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F))) + scratch0 := mulmod(state0, state0, F) + state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F) + scratch0 := add(0x04c6187e41ed881dc1b239c88f7f9d43a9f52fc8c8b6cdd1e76e47615b51f100, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F))) + scratch1 := add(0x13b37bd80f4d27fb10d84331f6fb6d534b81c61ed15776449e801b7ddc9c2967, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F))) + scratch2 := add(0x01a5c536273c2d9df578bfbd32c17b7a2ce3664c2a52032c9321ceb1c4e8a8e4, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F))) + state0 := mulmod(scratch0, scratch0, F) + scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F) + state0 := add(0x2ab3561834ca73835ad05f5d7acb950b4a9a2c666b9726da832239065b7c3b02, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F))) + state1 := add(0x1d4d8ec291e720db200fe6d686c0d613acaf6af4e95d3bf69f7ed516a597b646, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F))) + state2 := add(0x041294d2cc484d228f5784fe7919fd2bb925351240a04b711514c9c80b65af1d, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F))) + scratch0 := mulmod(state0, state0, F) + state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F) + scratch0 := add(0x154ac98e01708c611c4fa715991f004898f57939d126e392042971dd90e81fc6, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F))) + scratch1 := add(0x0b339d8acca7d4f83eedd84093aef51050b3684c88f8b0b04524563bc6ea4da4, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F))) + scratch2 := add(0x0955e49e6610c94254a4f84cfbab344598f0e71eaff4a7dd81ed95b50839c82e, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F))) + state0 := mulmod(scratch0, scratch0, F) + scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F) + state0 := add(0x06746a6156eba54426b9e22206f15abca9a6f41e6f535c6f3525401ea0654626, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F))) + state1 := add(0x0f18f5a0ecd1423c496f3820c549c27838e5790e2bd0a196ac917c7ff32077fb, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F))) + state2 := add(0x04f6eeca1751f7308ac59eff5beb261e4bb563583ede7bc92a738223d6f76e13, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F))) + scratch0 := mulmod(state0, state0, F) + state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F) + scratch0 := add(0x2b56973364c4c4f5c1a3ec4da3cdce038811eb116fb3e45bc1768d26fc0b3758, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F))) + scratch1 := add(0x123769dd49d5b054dcd76b89804b1bcb8e1392b385716a5d83feb65d437f29ef, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F))) + scratch2 := add(0x2147b424fc48c80a88ee52b91169aacea989f6446471150994257b2fb01c63e9, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F))) + state0 := mulmod(scratch0, scratch0, F) + scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F) + state0 := add(0x0fdc1f58548b85701a6c5505ea332a29647e6f34ad4243c2ea54ad897cebe54d, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F))) + state1 := add(0x12373a8251fea004df68abcf0f7786d4bceff28c5dbbe0c3944f685cc0a0b1f2, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F))) + state2 := add(0x21e4f4ea5f35f85bad7ea52ff742c9e8a642756b6af44203dd8a1f35c1a90035, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F))) + scratch0 := mulmod(state0, state0, F) + state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F) + scratch0 := add(0x16243916d69d2ca3dfb4722224d4c462b57366492f45e90d8a81934f1bc3b147, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F))) + scratch1 := add(0x1efbe46dd7a578b4f66f9adbc88b4378abc21566e1a0453ca13a4159cac04ac2, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F))) + scratch2 := add(0x07ea5e8537cf5dd08886020e23a7f387d468d5525be66f853b672cc96a88969a, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F))) + state0 := mulmod(scratch0, scratch0, F) + scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F) + state0 := add(0x05a8c4f9968b8aa3b7b478a30f9a5b63650f19a75e7ce11ca9fe16c0b76c00bc, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F))) + state1 := add(0x20f057712cc21654fbfe59bd345e8dac3f7818c701b9c7882d9d57b72a32e83f, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F))) + state2 := add(0x04a12ededa9dfd689672f8c67fee31636dcd8e88d01d49019bd90b33eb33db69, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F))) + scratch0 := mulmod(state0, state0, F) + state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F) + scratch0 := add(0x27e88d8c15f37dcee44f1e5425a51decbd136ce5091a6767e49ec9544ccd101a, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F))) + scratch1 := add(0x2feed17b84285ed9b8a5c8c5e95a41f66e096619a7703223176c41ee433de4d1, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F))) + scratch2 := add(0x1ed7cc76edf45c7c404241420f729cf394e5942911312a0d6972b8bd53aff2b8, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F))) + state0 := mulmod(scratch0, scratch0, F) + scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F) + state0 := add(0x15742e99b9bfa323157ff8c586f5660eac6783476144cdcadf2874be45466b1a, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F))) + state1 := add(0x1aac285387f65e82c895fc6887ddf40577107454c6ec0317284f033f27d0c785, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F))) + state2 := add(0x25851c3c845d4790f9ddadbdb6057357832e2e7a49775f71ec75a96554d67c77, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F))) + scratch0 := mulmod(state0, state0, F) + state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F) + scratch0 := add(0x15a5821565cc2ec2ce78457db197edf353b7ebba2c5523370ddccc3d9f146a67, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F))) + scratch1 := add(0x2411d57a4813b9980efa7e31a1db5966dcf64f36044277502f15485f28c71727, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F))) + scratch2 := add(0x002e6f8d6520cd4713e335b8c0b6d2e647e9a98e12f4cd2558828b5ef6cb4c9b, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F))) + state0 := mulmod(scratch0, scratch0, F) + scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F) + state0 := add(0x2ff7bc8f4380cde997da00b616b0fcd1af8f0e91e2fe1ed7398834609e0315d2, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F))) + state1 := add(0x00b9831b948525595ee02724471bcd182e9521f6b7bb68f1e93be4febb0d3cbe, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F))) + state2 := add(0x0a2f53768b8ebf6a86913b0e57c04e011ca408648a4743a87d77adbf0c9c3512, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F))) + scratch0 := mulmod(state0, state0, F) + state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F) + scratch0 := add(0x00248156142fd0373a479f91ff239e960f599ff7e94be69b7f2a290305e1198d, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F))) + scratch1 := add(0x171d5620b87bfb1328cf8c02ab3f0c9a397196aa6a542c2350eb512a2b2bcda9, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F))) + scratch2 := add(0x170a4f55536f7dc970087c7c10d6fad760c952172dd54dd99d1045e4ec34a808, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F))) + state0 := mulmod(scratch0, scratch0, F) + scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F) + state0 := add(0x29aba33f799fe66c2ef3134aea04336ecc37e38c1cd211ba482eca17e2dbfae1, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F))) + state1 := add(0x1e9bc179a4fdd758fdd1bb1945088d47e70d114a03f6a0e8b5ba650369e64973, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F))) + state2 := add(0x1dd269799b660fad58f7f4892dfb0b5afeaad869a9c4b44f9c9e1c43bdaf8f09, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F))) + scratch0 := mulmod(state0, state0, F) + state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F) + scratch0 := add(0x22cdbc8b70117ad1401181d02e15459e7ccd426fe869c7c95d1dd2cb0f24af38, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F))) + scratch1 := add(0x0ef042e454771c533a9f57a55c503fcefd3150f52ed94a7cd5ba93b9c7dacefd, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F))) + scratch2 := add(0x11609e06ad6c8fe2f287f3036037e8851318e8b08a0359a03b304ffca62e8284, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F))) + state0 := mulmod(scratch0, scratch0, F) + scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F) + state0 := add(0x1166d9e554616dba9e753eea427c17b7fecd58c076dfe42708b08f5b783aa9af, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F))) + state1 := add(0x2de52989431a859593413026354413db177fbf4cd2ac0b56f855a888357ee466, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F))) + state2 := add(0x3006eb4ffc7a85819a6da492f3a8ac1df51aee5b17b8e89d74bf01cf5f71e9ad, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F))) + scratch0 := mulmod(state0, state0, F) + state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F) + scratch0 := add(0x2af41fbb61ba8a80fdcf6fff9e3f6f422993fe8f0a4639f962344c8225145086, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F))) + scratch1 := add(0x119e684de476155fe5a6b41a8ebc85db8718ab27889e85e781b214bace4827c3, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F))) + scratch2 := add(0x1835b786e2e8925e188bea59ae363537b51248c23828f047cff784b97b3fd800, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F))) + state0 := mulmod(scratch0, scratch0, F) + scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F) + state0 := add(0x28201a34c594dfa34d794996c6433a20d152bac2a7905c926c40e285ab32eeb6, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F))) + state1 := add(0x083efd7a27d1751094e80fefaf78b000864c82eb571187724a761f88c22cc4e7, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F))) + state2 := add(0x0b6f88a3577199526158e61ceea27be811c16df7774dd8519e079564f61fd13b, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F))) + scratch0 := mulmod(state0, state0, F) + state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F) + scratch0 := add(0x0ec868e6d15e51d9644f66e1d6471a94589511ca00d29e1014390e6ee4254f5b, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F))) + scratch1 := add(0x2af33e3f866771271ac0c9b3ed2e1142ecd3e74b939cd40d00d937ab84c98591, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F))) + scratch2 := add(0x0b520211f904b5e7d09b5d961c6ace7734568c547dd6858b364ce5e47951f178, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F))) + state0 := mulmod(scratch0, scratch0, F) + scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F) + state0 := add(0x0b2d722d0919a1aad8db58f10062a92ea0c56ac4270e822cca228620188a1d40, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F))) + state1 := add(0x1f790d4d7f8cf094d980ceb37c2453e957b54a9991ca38bbe0061d1ed6e562d4, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F))) + state2 := add(0x0171eb95dfbf7d1eaea97cd385f780150885c16235a2a6a8da92ceb01e504233, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F))) + scratch0 := mulmod(state0, state0, F) + state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F) + scratch0 := add(0x0c2d0e3b5fd57549329bf6885da66b9b790b40defd2c8650762305381b168873, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F))) + scratch1 := add(0x1162fb28689c27154e5a8228b4e72b377cbcafa589e283c35d3803054407a18d, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F))) + scratch2 := add(0x2f1459b65dee441b64ad386a91e8310f282c5a92a89e19921623ef8249711bc0, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F))) + state0 := mulmod(scratch0, scratch0, F) + scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F) + state0 := add(0x1e6ff3216b688c3d996d74367d5cd4c1bc489d46754eb712c243f70d1b53cfbb, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F))) + state1 := add(0x01ca8be73832b8d0681487d27d157802d741a6f36cdc2a0576881f9326478875, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F))) + state2 := add(0x1f7735706ffe9fc586f976d5bdf223dc680286080b10cea00b9b5de315f9650e, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F))) + scratch0 := mulmod(state0, state0, F) + state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F) + scratch0 := add(0x2522b60f4ea3307640a0c2dce041fba921ac10a3d5f096ef4745ca838285f019, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F))) + scratch1 := add(0x23f0bee001b1029d5255075ddc957f833418cad4f52b6c3f8ce16c235572575b, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F))) + scratch2 := add(0x2bc1ae8b8ddbb81fcaac2d44555ed5685d142633e9df905f66d9401093082d59, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F))) + state0 := mulmod(scratch0, scratch0, F) + scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F) + state0 := add(0x0f9406b8296564a37304507b8dba3ed162371273a07b1fc98011fcd6ad72205f, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F))) + state1 := add(0x2360a8eb0cc7defa67b72998de90714e17e75b174a52ee4acb126c8cd995f0a8, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F))) + state2 := add(0x15871a5cddead976804c803cbaef255eb4815a5e96df8b006dcbbc2767f88948, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F))) + scratch0 := mulmod(state0, state0, F) + state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F) + scratch0 := add(0x193a56766998ee9e0a8652dd2f3b1da0362f4f54f72379544f957ccdeefb420f, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F))) + scratch1 := add(0x2a394a43934f86982f9be56ff4fab1703b2e63c8ad334834e4309805e777ae0f, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F))) + scratch2 := add(0x1859954cfeb8695f3e8b635dcb345192892cd11223443ba7b4166e8876c0d142, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F))) + state0 := mulmod(scratch0, scratch0, F) + scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F) + state0 := add(0x04e1181763050e58013444dbcb99f1902b11bc25d90bbdca408d3819f4fed32b, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F))) + state1 := add(0x0fdb253dee83869d40c335ea64de8c5bb10eb82db08b5e8b1f5e5552bfd05f23, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F))) + state2 := add(0x058cbe8a9a5027bdaa4efb623adead6275f08686f1c08984a9d7c5bae9b4f1c0, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F))) + scratch0 := mulmod(state0, state0, F) + state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F) + scratch0 := add(0x1382edce9971e186497eadb1aeb1f52b23b4b83bef023ab0d15228b4cceca59a, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F))) + scratch1 := add(0x03464990f045c6ee0819ca51fd11b0be7f61b8eb99f14b77e1e6634601d9e8b5, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F))) + scratch2 := add(0x23f7bfc8720dc296fff33b41f98ff83c6fcab4605db2eb5aaa5bc137aeb70a58, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F))) + state0 := mulmod(scratch0, scratch0, F) + scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F) + state0 := add(0x0a59a158e3eec2117e6e94e7f0e9decf18c3ffd5e1531a9219636158bbaf62f2, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F))) + state1 := add(0x06ec54c80381c052b58bf23b312ffd3ce2c4eba065420af8f4c23ed0075fd07b, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F))) + state2 := add(0x118872dc832e0eb5476b56648e867ec8b09340f7a7bcb1b4962f0ff9ed1f9d01, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F))) + scratch0 := mulmod(state0, state0, F) + state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F) + scratch0 := add(0x13d69fa127d834165ad5c7cba7ad59ed52e0b0f0e42d7fea95e1906b520921b1, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F))) + scratch1 := add(0x169a177f63ea681270b1c6877a73d21bde143942fb71dc55fd8a49f19f10c77b, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F))) + scratch2 := add(0x04ef51591c6ead97ef42f287adce40d93abeb032b922f66ffb7e9a5a7450544d, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F))) + state0 := mulmod(scratch0, scratch0, F) + scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F) + state0 := add(0x256e175a1dc079390ecd7ca703fb2e3b19ec61805d4f03ced5f45ee6dd0f69ec, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F))) + state1 := add(0x30102d28636abd5fe5f2af412ff6004f75cc360d3205dd2da002813d3e2ceeb2, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F))) + state2 := add(0x10998e42dfcd3bbf1c0714bc73eb1bf40443a3fa99bef4a31fd31be182fcc792, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F))) + scratch0 := mulmod(state0, state0, F) + state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F) + scratch0 := add(0x193edd8e9fcf3d7625fa7d24b598a1d89f3362eaf4d582efecad76f879e36860, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F))) + scratch1 := add(0x18168afd34f2d915d0368ce80b7b3347d1c7a561ce611425f2664d7aa51f0b5d, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F))) + scratch2 := add(0x29383c01ebd3b6ab0c017656ebe658b6a328ec77bc33626e29e2e95b33ea6111, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F))) + state0 := mulmod(scratch0, scratch0, F) + scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F) + state0 := add(0x10646d2f2603de39a1f4ae5e7771a64a702db6e86fb76ab600bf573f9010c711, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F))) + state1 := add(0x0beb5e07d1b27145f575f1395a55bf132f90c25b40da7b3864d0242dcb1117fb, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F))) + state2 := add(0x16d685252078c133dc0d3ecad62b5c8830f95bb2e54b59abdffbf018d96fa336, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F))) + scratch0 := mulmod(state0, state0, F) + state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F) + scratch0 := add(0x0a6abd1d833938f33c74154e0404b4b40a555bbbec21ddfafd672dd62047f01a, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F))) + scratch1 := add(0x1a679f5d36eb7b5c8ea12a4c2dedc8feb12dffeec450317270a6f19b34cf1860, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F))) + scratch2 := add(0x0980fb233bd456c23974d50e0ebfde4726a423eada4e8f6ffbc7592e3f1b93d6, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F))) + state0 := mulmod(scratch0, scratch0, F) + scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F) + state0 := add(0x161b42232e61b84cbf1810af93a38fc0cece3d5628c9282003ebacb5c312c72b, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F))) + state1 := add(0x0ada10a90c7f0520950f7d47a60d5e6a493f09787f1564e5d09203db47de1a0b, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F))) + state2 := add(0x1a730d372310ba82320345a29ac4238ed3f07a8a2b4e121bb50ddb9af407f451, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F))) + scratch0 := mulmod(state0, state0, F) + state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F) + scratch0 := add(0x2c8120f268ef054f817064c369dda7ea908377feaba5c4dffbda10ef58e8c556, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F))) + scratch1 := add(0x1c7c8824f758753fa57c00789c684217b930e95313bcb73e6e7b8649a4968f70, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F))) + scratch2 := add(0x2cd9ed31f5f8691c8e39e4077a74faa0f400ad8b491eb3f7b47b27fa3fd1cf77, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F))) + state0 := mulmod(scratch0, scratch0, F) + scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F) + state0 := add(0x23ff4f9d46813457cf60d92f57618399a5e022ac321ca550854ae23918a22eea, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F))) + state1 := add(0x09945a5d147a4f66ceece6405dddd9d0af5a2c5103529407dff1ea58f180426d, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F))) + state2 := add(0x188d9c528025d4c2b67660c6b771b90f7c7da6eaa29d3f268a6dd223ec6fc630, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F))) + scratch0 := mulmod(state0, state0, F) + state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F) + scratch0 := add(0x3050e37996596b7f81f68311431d8734dba7d926d3633595e0c0d8ddf4f0f47f, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F))) + scratch1 := add(0x15af1169396830a91600ca8102c35c426ceae5461e3f95d89d829518d30afd78, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F))) + scratch2 := add(0x1da6d09885432ea9a06d9f37f873d985dae933e351466b2904284da3320d8acc, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F))) + state0 := mulmod(scratch0, scratch0, F) + scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F) + state0 := add(0x2796ea90d269af29f5f8acf33921124e4e4fad3dbe658945e546ee411ddaa9cb, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F))) + state1 := add(0x202d7dd1da0f6b4b0325c8b3307742f01e15612ec8e9304a7cb0319e01d32d60, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F))) + state2 := add(0x096d6790d05bb759156a952ba263d672a2d7f9c788f4c831a29dace4c0f8be5f, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F))) + scratch0 := mulmod(state0, state0, F) + state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F) + scratch0 := add(0x054efa1f65b0fce283808965275d877b438da23ce5b13e1963798cb1447d25a4, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F))) + scratch1 := add(0x1b162f83d917e93edb3308c29802deb9d8aa690113b2e14864ccf6e18e4165f1, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F))) + scratch2 := add(0x21e5241e12564dd6fd9f1cdd2a0de39eedfefc1466cc568ec5ceb745a0506edc, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F))) + state0 := mulmod(scratch0, scratch0, F) + scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F) + state0 := mulmod(scratch1, scratch1, F) + scratch1 := mulmod(mulmod(state0, state0, F), scratch1, F) + state0 := mulmod(scratch2, scratch2, F) + scratch2 := mulmod(mulmod(state0, state0, F), scratch2, F) + state0 := add(0x1cfb5662e8cf5ac9226a80ee17b36abecb73ab5f87e161927b4349e10e4bdf08, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F))) + state1 := add(0x0f21177e302a771bbae6d8d1ecb373b62c99af346220ac0129c53f666eb24100, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F))) + state2 := add(0x1671522374606992affb0dd7f71b12bec4236aede6290546bcef7e1f515c2320, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F))) + scratch0 := mulmod(state0, state0, F) + state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F) + scratch0 := mulmod(state1, state1, F) + state1 := mulmod(mulmod(scratch0, scratch0, F), state1, F) + scratch0 := mulmod(state2, state2, F) + state2 := mulmod(mulmod(scratch0, scratch0, F), state2, F) + scratch0 := add(0x0fa3ec5b9488259c2eb4cf24501bfad9be2ec9e42c5cc8ccd419d2a692cad870, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F))) + scratch1 := add(0x193c0e04e0bd298357cb266c1506080ed36edce85c648cc085e8c57b1ab54bba, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F))) + scratch2 := add(0x102adf8ef74735a27e9128306dcbc3c99f6f7291cd406578ce14ea2adaba68f8, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F))) + state0 := mulmod(scratch0, scratch0, F) + scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F) + state0 := mulmod(scratch1, scratch1, F) + scratch1 := mulmod(mulmod(state0, state0, F), scratch1, F) + state0 := mulmod(scratch2, scratch2, F) + scratch2 := mulmod(mulmod(state0, state0, F), scratch2, F) + state0 := add(0x0fe0af7858e49859e2a54d6f1ad945b1316aa24bfbdd23ae40a6d0cb70c3eab1, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F))) + state1 := add(0x216f6717bbc7dedb08536a2220843f4e2da5f1daa9ebdefde8a5ea7344798d22, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F))) + state2 := add(0x1da55cc900f0d21f4a3e694391918a1b3c23b2ac773c6b3ef88e2e4228325161, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F))) + scratch0 := mulmod(state0, state0, F) + state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F) + scratch0 := mulmod(state1, state1, F) + state1 := mulmod(mulmod(scratch0, scratch0, F), state1, F) + scratch0 := mulmod(state2, state2, F) + state2 := mulmod(mulmod(scratch0, scratch0, F), state2, F) + + mstore(0x0, mod(add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)), F)) + + return (0, 0x20) + } + } +} diff --git a/benches/src/lib.rs b/benches/src/lib.rs index 19b06dff3..a1fbabcb8 100644 --- a/benches/src/lib.rs +++ b/benches/src/lib.rs @@ -24,6 +24,7 @@ pub mod merkle_proofs; pub mod ownable; pub mod oz_crypto; pub mod poseidon; +pub mod poseidon_asm_sol; pub mod poseidon_renegades; pub mod poseidon_sol; pub mod report; diff --git a/benches/src/main.rs b/benches/src/main.rs index 5a4797edd..26535c304 100644 --- a/benches/src/main.rs +++ b/benches/src/main.rs @@ -1,7 +1,7 @@ use benches::{ access_control, ark_ff, erc1155, erc1155_metadata_uri, erc20, erc721, - merkle_proofs, ownable, oz_crypto, poseidon, poseidon_renegades, - poseidon_sol, report::BenchmarkReport, + merkle_proofs, ownable, oz_crypto, poseidon, poseidon_asm_sol, + poseidon_renegades, poseidon_sol, report::BenchmarkReport, }; use futures::FutureExt; use itertools::Itertools; @@ -17,6 +17,7 @@ async fn main() -> eyre::Result<()> { // erc1155::bench().boxed(), // erc1155_metadata_uri::bench().boxed(), poseidon_sol::bench().boxed(), + poseidon_asm_sol::bench().boxed(), poseidon::bench().boxed(), poseidon_renegades::bench().boxed(), oz_crypto::bench().boxed(), diff --git a/benches/src/poseidon_asm_sol.rs b/benches/src/poseidon_asm_sol.rs new file mode 100644 index 000000000..9578f0cb3 --- /dev/null +++ b/benches/src/poseidon_asm_sol.rs @@ -0,0 +1,451 @@ +use alloy::{ + network::{AnyNetwork, EthereumWallet}, + primitives::Address, + providers::ProviderBuilder, + sol, + sol_types::SolCall, +}; +use alloy_primitives::uint; +use e2e::{receipt, Account}; + +use crate::report::{ContractReport, FunctionReport}; + +sol!( + #[sol(rpc)] + contract PoseidonExample { + #[derive(Debug)] + function hash(uint256[2] memory inputs) external view returns (uint256 hash); + } +); + +pub async fn bench() -> eyre::Result { + let reports = run().await?; + let report = reports.into_iter().try_fold( + ContractReport::new("Sol:Asm:Poseidon"), + ContractReport::add, + )?; + + Ok(report) +} + +pub async fn run() -> eyre::Result> { + let alice = Account::new().await?; + let alice_wallet = ProviderBuilder::new() + .network::() + .with_recommended_fillers() + .wallet(EthereumWallet::from(alice.signer.clone())) + .on_http(alice.url().parse()?); + + let contract_addr = deploy(&alice).await?; + + let contract = PoseidonExample::new(contract_addr, &alice_wallet); + + #[rustfmt::skip] + let receipts = vec![ + (PoseidonExample::hashCall::SIGNATURE, receipt!(contract.hash([uint!(123_U256), uint!(123456_U256)]))?), + ]; + + receipts + .into_iter() + .map(FunctionReport::new) + .collect::>>() +} + +pub async fn deploy(account: &Account) -> eyre::Result

{ + let contract = PoseidonT3::deploy(&account.wallet).await?; + Ok(*contract.address()) +} + +sol! { + #[allow(missing_docs)] + // Built with Remix IDE; solc 0.8.24+commit.e11b9ed9 + #[sol(rpc, bytecode="615db161004d600b8282823980515f1a6073146041577f4e487b71000000000000000000000000000000000000000000000000000000005f525f60045260245ffd5b305f52607381538281f3fe7300000000000000000000000000000000000000003014608060405260043610610034575f3560e01c8063561558fe14610038575b5f80fd5b610052600480360381019061004d9190615d28565b610068565b60405161005f9190615d62565b60405180910390f35b5f7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f2b90bba00fca0589f617e7dcbfe82e0df706ab640ceb247b791a93b74e36736d7f101071f0032379b697315876690f053d148d4e109f5fb065c8aacc55a0f89bfa7f19a3fc0a56702bf417ba7fee3802593fa644470307043f7773279cd71d25d5e07ef1445235f2148c5986587169fc1bcd887b08d4d00868df5696fff40956e8648460805106017f08dff3487e8ac99e1f29a058d0fa80b930c728730b7ab36ce879f3890ecf73f58560a05106018582830986838883840909925086828309905086828883840909915086868309877f16ed41e13bb9c0c66ae119424fddbcbc9314dc9fdbdeea55d6c64543dc4903e085097f2229fe5e63f56eef4bfba02c26292de10ac2b2b045e6184acff16e4660c05f6b01017f2f27be690fdaee46c3ce28f7532b13c856c35342c84bda6e20966310fadc01d001905086858309877f2e2419f9ec02ec394c9871c832963dc1b89d743c8c7b964029b2311687b1fe2385097f2949435275a29cdbffe3e4101a45669873f9408a5d11e21b4ec6edf8501eee4d01017f2b2ae1acf68b7b8d2416bebf3d4f6234b763fe04b8043ee48b8327bebca16cf20187858409887f176cc029695ad02582a70eff08a6fd99d057e12e58e7d7b6b16cdfabc8ee291186097f20c290a7269657965092ef5700a447f5bc2c41dfca932f527cb2600ac9bcfefb01017f0319d062072bef7ecca5eac06f97d4d55952c175ab6b03eae64b44c7dbf11cfa018883840989848b83840909935089838409905089838b83840909925089828309905089828b838409099150898983098a7f16ed41e13bb9c0c66ae119424fddbcbc9314dc9fdbdeea55d6c64543dc4903e085098b7f109b7f411ba0e4c9b2b70caf5c36a7b194be7c11ad24378bfedb68592ba8118b870901017f28813dcaebaeaa828a376df87af4a63bc8b7bf27ad49c6298ef7b387bf28526d019050898883098a7f2e2419f9ec02ec394c9871c832963dc1b89d743c8c7b964029b2311687b1fe2385098b7f2969f27eed31a480b9c36c764379dbca2cc8fdd1415c3dded62940bcde0bd771870901017f2727673b2ccbc903f181bf38e1c1d40d2033865200c352bc150928adddf9cb78019550898783098a7f176cc029695ad02582a70eff08a6fd99d057e12e58e7d7b6b16cdfabc8ee291185098b7f143021ec686a3f330d5f9e654638065ce6cd79e28c5b3753326244ee65a1b1a7870901017f234ec45ca27727c2e74abd2b2a1494cd6efbd43e340587d6b8fb9e31e65cc63201945089818209935089818b86870909905089868709935089868b86870909955089858609935089858b868709099450898986098a7f16ed41e13bb9c0c66ae119424fddbcbc9314dc9fdbdeea55d6c64543dc4903e088098b7f109b7f411ba0e4c9b2b70caf5c36a7b194be7c11ad24378bfedb68592ba8118b840901017f15b52534031ae18f7f862cb2cf7cf760ab10a8150a337b1ccd99ff6e8797d428019350898886098a7f2e2419f9ec02ec394c9871c832963dc1b89d743c8c7b964029b2311687b1fe2388098b7f2969f27eed31a480b9c36c764379dbca2cc8fdd1415c3dded62940bcde0bd771840901017f0dc8fad6d9e4b35f5ed9a3d186b79ce38e0e8a8d1b58b132d701d4eecf68d1f6019250898786098a7f176cc029695ad02582a70eff08a6fd99d057e12e58e7d7b6b16cdfabc8ee291188098b7f143021ec686a3f330d5f9e654638065ce6cd79e28c5b3753326244ee65a1b1a7840901017f1bcd95ffc211fbca600f705fad3fb567ea4eb378f62e1fec97805518a47e4d9c01915089848509905089848b83840909935089838409905089838b83840909925089828309905089828b838409099150898983098a7f16ed41e13bb9c0c66ae119424fddbcbc9314dc9fdbdeea55d6c64543dc4903e085098b7f109b7f411ba0e4c9b2b70caf5c36a7b194be7c11ad24378bfedb68592ba8118b870901017f10520b0ab721cadfe9eff81b016fc34dc76da36c2578937817cb978d069de559019050898883098a7f2e2419f9ec02ec394c9871c832963dc1b89d743c8c7b964029b2311687b1fe2385098b7f2969f27eed31a480b9c36c764379dbca2cc8fdd1415c3dded62940bcde0bd771870901017f1f6d48149b8e7f7d9b257d8ed5fbbaf42932498075fed0ace88a9eb81f5627f6019550898783098a7f176cc029695ad02582a70eff08a6fd99d057e12e58e7d7b6b16cdfabc8ee291185098b7f143021ec686a3f330d5f9e654638065ce6cd79e28c5b3753326244ee65a1b1a7870901017f1d9655f652309014d29e00ef35a2089bfff8dc1c816f0dc9ca34bdb5460c870501945089818209935089818b868709099050898986098a7f16ed41e13bb9c0c66ae119424fddbcbc9314dc9fdbdeea55d6c64543dc4903e088098b7f109b7f411ba0e4c9b2b70caf5c36a7b194be7c11ad24378bfedb68592ba8118b840901017f04df5a56ff95bcafb051f7b1cd43a99ba731ff67e47032058fe3d4185697cc7d019350898886098a7f2e2419f9ec02ec394c9871c832963dc1b89d743c8c7b964029b2311687b1fe2388098b7f2969f27eed31a480b9c36c764379dbca2cc8fdd1415c3dded62940bcde0bd771840901017f0672d995f8fff640151b3d290cedaf148690a10a8c8424a7f6ec282b6e4be828019250898786098a7f176cc029695ad02582a70eff08a6fd99d057e12e58e7d7b6b16cdfabc8ee291188098b7f143021ec686a3f330d5f9e654638065ce6cd79e28c5b3753326244ee65a1b1a7840901017f099952b414884454b21200d7ffafdd5f0c9a9dcc06f2708e9fc1d8209b5c75b901915089848509905089848b838409099350898983098a7f16ed41e13bb9c0c66ae119424fddbcbc9314dc9fdbdeea55d6c64543dc4903e085098b7f109b7f411ba0e4c9b2b70caf5c36a7b194be7c11ad24378bfedb68592ba8118b870901017f052cba2255dfd00c7c483143ba8d469448e43586a9b4cd9183fd0e843a6b9fa6019050898883098a7f2e2419f9ec02ec394c9871c832963dc1b89d743c8c7b964029b2311687b1fe2385098b7f2969f27eed31a480b9c36c764379dbca2cc8fdd1415c3dded62940bcde0bd771870901017f0b8badee690adb8eb0bd74712b7999af82de55707251ad7716077cb93c464ddc019550898783098a7f176cc029695ad02582a70eff08a6fd99d057e12e58e7d7b6b16cdfabc8ee291185098b7f143021ec686a3f330d5f9e654638065ce6cd79e28c5b3753326244ee65a1b1a7870901017f119b1590f13307af5a1ee651020c07c749c15d60683a8050b963d0a8e4b2bdd101945089818209935089818b868709099050898986098a7f16ed41e13bb9c0c66ae119424fddbcbc9314dc9fdbdeea55d6c64543dc4903e088098b7f109b7f411ba0e4c9b2b70caf5c36a7b194be7c11ad24378bfedb68592ba8118b840901017f03150b7cd6d5d17b2529d36be0f67b832c4acfc884ef4ee5ce15be0bfb4a8d09019350898886098a7f2e2419f9ec02ec394c9871c832963dc1b89d743c8c7b964029b2311687b1fe2388098b7f2969f27eed31a480b9c36c764379dbca2cc8fdd1415c3dded62940bcde0bd771840901017f2cc6182c5e14546e3cf1951f173912355374efb83d80898abe69cb317c9ea565019250898786098a7f176cc029695ad02582a70eff08a6fd99d057e12e58e7d7b6b16cdfabc8ee291188098b7f143021ec686a3f330d5f9e654638065ce6cd79e28c5b3753326244ee65a1b1a7840901017e5032551e6378c450cfe129a404b3764218cadedac14e2b92d2cd73111bf0f901915089848509905089848b838409099350898983098a7f16ed41e13bb9c0c66ae119424fddbcbc9314dc9fdbdeea55d6c64543dc4903e085098b7f109b7f411ba0e4c9b2b70caf5c36a7b194be7c11ad24378bfedb68592ba8118b870901017f233237e3289baa34bb147e972ebcb9516469c399fcc069fb88f9da2cc28276b5019050898883098a7f2e2419f9ec02ec394c9871c832963dc1b89d743c8c7b964029b2311687b1fe2385098b7f2969f27eed31a480b9c36c764379dbca2cc8fdd1415c3dded62940bcde0bd771870901017f05c8f4f4ebd4a6e3c980d31674bfbe6323037f21b34ae5a4e80c2d4c24d60280019550898783098a7f176cc029695ad02582a70eff08a6fd99d057e12e58e7d7b6b16cdfabc8ee291185098b7f143021ec686a3f330d5f9e654638065ce6cd79e28c5b3753326244ee65a1b1a7870901017f0a7b1db13042d396ba05d818a319f25252bcf35ef3aeed91ee1f09b2590fc65b01945089818209935089818b868709099050898986098a7f16ed41e13bb9c0c66ae119424fddbcbc9314dc9fdbdeea55d6c64543dc4903e088098b7f109b7f411ba0e4c9b2b70caf5c36a7b194be7c11ad24378bfedb68592ba8118b840901017f2a73b71f9b210cf5b14296572c9d32dbf156e2b086ff47dc5df542365a404ec0019350898886098a7f2e2419f9ec02ec394c9871c832963dc1b89d743c8c7b964029b2311687b1fe2388098b7f2969f27eed31a480b9c36c764379dbca2cc8fdd1415c3dded62940bcde0bd771840901017f1ac9b0417abcc9a1935107e9ffc91dc3ec18f2c4dbe7f22976a760bb5c50c460019250898786098a7f176cc029695ad02582a70eff08a6fd99d057e12e58e7d7b6b16cdfabc8ee291188098b7f143021ec686a3f330d5f9e654638065ce6cd79e28c5b3753326244ee65a1b1a7840901017f12c0339ae08374823fabb076707ef479269f3e4d6cb104349015ee046dc93fc001915089848509905089848b838409099350898983098a7f16ed41e13bb9c0c66ae119424fddbcbc9314dc9fdbdeea55d6c64543dc4903e085098b7f109b7f411ba0e4c9b2b70caf5c36a7b194be7c11ad24378bfedb68592ba8118b870901017f0b7475b102a165ad7f5b18db4e1e704f52900aa3253baac68246682e56e9a28e019050898883098a7f2e2419f9ec02ec394c9871c832963dc1b89d743c8c7b964029b2311687b1fe2385098b7f2969f27eed31a480b9c36c764379dbca2cc8fdd1415c3dded62940bcde0bd771870901017f037c2849e191ca3edb1c5e49f6e8b8917c843e379366f2ea32ab3aa88d7f8448019550898783098a7f176cc029695ad02582a70eff08a6fd99d057e12e58e7d7b6b16cdfabc8ee291185098b7f143021ec686a3f330d5f9e654638065ce6cd79e28c5b3753326244ee65a1b1a7870901017f05a6811f8556f014e92674661e217e9bd5206c5c93a07dc145fdb176a716346f01945089818209935089818b868709099050898986098a7f16ed41e13bb9c0c66ae119424fddbcbc9314dc9fdbdeea55d6c64543dc4903e088098b7f109b7f411ba0e4c9b2b70caf5c36a7b194be7c11ad24378bfedb68592ba8118b840901017f29a795e7d98028946e947b75d54e9f044076e87a7b2883b47b675ef5f38bd66e019350898886098a7f2e2419f9ec02ec394c9871c832963dc1b89d743c8c7b964029b2311687b1fe2388098b7f2969f27eed31a480b9c36c764379dbca2cc8fdd1415c3dded62940bcde0bd771840901017f20439a0c84b322eb45a3857afc18f5826e8c7382c8a1585c507be199981fd22f019250898786098a7f176cc029695ad02582a70eff08a6fd99d057e12e58e7d7b6b16cdfabc8ee291188098b7f143021ec686a3f330d5f9e654638065ce6cd79e28c5b3753326244ee65a1b1a7840901017f2e0ba8d94d9ecf4a94ec2050c7371ff1bb50f27799a84b6d4a2a6f2a0982c88701915089848509905089848b838409099350898983098a7f16ed41e13bb9c0c66ae119424fddbcbc9314dc9fdbdeea55d6c64543dc4903e085098b7f109b7f411ba0e4c9b2b70caf5c36a7b194be7c11ad24378bfedb68592ba8118b870901017f143fd115ce08fb27ca38eb7cce822b4517822cd2109048d2e6d0ddcca17d71c8019050898883098a7f2e2419f9ec02ec394c9871c832963dc1b89d743c8c7b964029b2311687b1fe2385098b7f2969f27eed31a480b9c36c764379dbca2cc8fdd1415c3dded62940bcde0bd771870901017f0c64cbecb1c734b857968dbbdcf813cdf8611659323dbcbfc84323623be9caf1019550898783098a7f176cc029695ad02582a70eff08a6fd99d057e12e58e7d7b6b16cdfabc8ee291185098b7f143021ec686a3f330d5f9e654638065ce6cd79e28c5b3753326244ee65a1b1a7870901017f028a305847c683f646fca925c163ff5ae74f348d62c2b670f1426cef9403da5301945089818209935089818b868709099050898986098a7f16ed41e13bb9c0c66ae119424fddbcbc9314dc9fdbdeea55d6c64543dc4903e088098b7f109b7f411ba0e4c9b2b70caf5c36a7b194be7c11ad24378bfedb68592ba8118b840901017f2e4ef510ff0b6fda5fa940ab4c4380f26a6bcb64d89427b824d6755b5db9e30c019350898886098a7f2e2419f9ec02ec394c9871c832963dc1b89d743c8c7b964029b2311687b1fe2388098b7f2969f27eed31a480b9c36c764379dbca2cc8fdd1415c3dded62940bcde0bd771840901017e81c95bc43384e663d79270c956ce3b8925b4f6d033b078b96384f50579400e019250898786098a7f176cc029695ad02582a70eff08a6fd99d057e12e58e7d7b6b16cdfabc8ee291188098b7f143021ec686a3f330d5f9e654638065ce6cd79e28c5b3753326244ee65a1b1a7840901017f2ed5f0c91cbd9749187e2fade687e05ee2491b349c039a0bba8a9f4023a0bb3801915089848509905089848b838409099350898983098a7f16ed41e13bb9c0c66ae119424fddbcbc9314dc9fdbdeea55d6c64543dc4903e085098b7f109b7f411ba0e4c9b2b70caf5c36a7b194be7c11ad24378bfedb68592ba8118b870901017f30509991f88da3504bbf374ed5aae2f03448a22c76234c8c990f01f33a735206019050898883098a7f2e2419f9ec02ec394c9871c832963dc1b89d743c8c7b964029b2311687b1fe2385098b7f2969f27eed31a480b9c36c764379dbca2cc8fdd1415c3dded62940bcde0bd771870901017f1c3f20fd55409a53221b7c4d49a356b9f0a1119fb2067b41a7529094424ec6ad019550898783098a7f176cc029695ad02582a70eff08a6fd99d057e12e58e7d7b6b16cdfabc8ee291185098b7f143021ec686a3f330d5f9e654638065ce6cd79e28c5b3753326244ee65a1b1a7870901017f10b4e7f3ab5df003049514459b6e18eec46bb2213e8e131e170887b47ddcb96c01945089818209935089818b868709099050898986098a7f16ed41e13bb9c0c66ae119424fddbcbc9314dc9fdbdeea55d6c64543dc4903e088098b7f109b7f411ba0e4c9b2b70caf5c36a7b194be7c11ad24378bfedb68592ba8118b840901017f2a1982979c3ff7f43ddd543d891c2abddd80f804c077d775039aa3502e43adef019350898886098a7f2e2419f9ec02ec394c9871c832963dc1b89d743c8c7b964029b2311687b1fe2388098b7f2969f27eed31a480b9c36c764379dbca2cc8fdd1415c3dded62940bcde0bd771840901017f1c74ee64f15e1db6feddbead56d6d55dba431ebc396c9af95cad0f1315bd5c91019250898786098a7f176cc029695ad02582a70eff08a6fd99d057e12e58e7d7b6b16cdfabc8ee291188098b7f143021ec686a3f330d5f9e654638065ce6cd79e28c5b3753326244ee65a1b1a7840901017f07533ec850ba7f98eab9303cace01b4b9e4f2e8b82708cfa9c2fe45a0ae146a001915089848509905089848b838409099350898983098a7f16ed41e13bb9c0c66ae119424fddbcbc9314dc9fdbdeea55d6c64543dc4903e085098b7f109b7f411ba0e4c9b2b70caf5c36a7b194be7c11ad24378bfedb68592ba8118b870901017f21576b438e500449a151e4eeaf17b154285c68f42d42c1808a11abf3764c0750019050898883098a7f2e2419f9ec02ec394c9871c832963dc1b89d743c8c7b964029b2311687b1fe2385098b7f2969f27eed31a480b9c36c764379dbca2cc8fdd1415c3dded62940bcde0bd771870901017f2f17c0559b8fe79608ad5ca193d62f10bce8384c815f0906743d6930836d4a9e019550898783098a7f176cc029695ad02582a70eff08a6fd99d057e12e58e7d7b6b16cdfabc8ee291185098b7f143021ec686a3f330d5f9e654638065ce6cd79e28c5b3753326244ee65a1b1a7870901017f2d477e3862d07708a79e8aae946170bc9775a4201318474ae665b0b1b7e2730e01945089818209935089818b868709099050898986098a7f16ed41e13bb9c0c66ae119424fddbcbc9314dc9fdbdeea55d6c64543dc4903e088098b7f109b7f411ba0e4c9b2b70caf5c36a7b194be7c11ad24378bfedb68592ba8118b840901017f162f5243967064c390e095577984f291afba2266c38f5abcd89be0f5b2747eab019350898886098a7f2e2419f9ec02ec394c9871c832963dc1b89d743c8c7b964029b2311687b1fe2388098b7f2969f27eed31a480b9c36c764379dbca2cc8fdd1415c3dded62940bcde0bd771840901017f2b4cb233ede9ba48264ecd2c8ae50d1ad7a8596a87f29f8a7777a70092393311019250898786098a7f176cc029695ad02582a70eff08a6fd99d057e12e58e7d7b6b16cdfabc8ee291188098b7f143021ec686a3f330d5f9e654638065ce6cd79e28c5b3753326244ee65a1b1a7840901017f2c8fbcb2dd8573dc1dbaf8f4622854776db2eece6d85c4cf4254e7c35e03b07a01915089848509905089848b838409099350898983098a7f16ed41e13bb9c0c66ae119424fddbcbc9314dc9fdbdeea55d6c64543dc4903e085098b7f109b7f411ba0e4c9b2b70caf5c36a7b194be7c11ad24378bfedb68592ba8118b870901017f1d6f347725e4816af2ff453f0cd56b199e1b61e9f601e9ade5e88db870949da9019050898883098a7f2e2419f9ec02ec394c9871c832963dc1b89d743c8c7b964029b2311687b1fe2385098b7f2969f27eed31a480b9c36c764379dbca2cc8fdd1415c3dded62940bcde0bd771870901017f204b0c397f4ebe71ebc2d8b3df5b913df9e6ac02b68d31324cd49af5c4565529019550898783098a7f176cc029695ad02582a70eff08a6fd99d057e12e58e7d7b6b16cdfabc8ee291185098b7f143021ec686a3f330d5f9e654638065ce6cd79e28c5b3753326244ee65a1b1a7870901017f0c4cb9dc3c4fd8174f1149b3c63c3c2f9ecb827cd7dc25534ff8fb75bc79c50201945089818209935089818b868709099050898986098a7f16ed41e13bb9c0c66ae119424fddbcbc9314dc9fdbdeea55d6c64543dc4903e088098b7f109b7f411ba0e4c9b2b70caf5c36a7b194be7c11ad24378bfedb68592ba8118b840901017f174ad61a1448c899a25416474f4930301e5c49475279e0639a616ddc45bc7b54019350898886098a7f2e2419f9ec02ec394c9871c832963dc1b89d743c8c7b964029b2311687b1fe2388098b7f2969f27eed31a480b9c36c764379dbca2cc8fdd1415c3dded62940bcde0bd771840901017f1a96177bcf4d8d89f759df4ec2f3cde2eaaa28c177cc0fa13a9816d49a38d2ef019250898786098a7f176cc029695ad02582a70eff08a6fd99d057e12e58e7d7b6b16cdfabc8ee291188098b7f143021ec686a3f330d5f9e654638065ce6cd79e28c5b3753326244ee65a1b1a7840901017f066d04b24331d71cd0ef8054bc60c4ff05202c126a233c1a8242ace360b8a30a01915089848509905089848b838409099350898983098a7f16ed41e13bb9c0c66ae119424fddbcbc9314dc9fdbdeea55d6c64543dc4903e085098b7f109b7f411ba0e4c9b2b70caf5c36a7b194be7c11ad24378bfedb68592ba8118b870901017f2a4c4fc6ec0b0cf52195782871c6dd3b381cc65f72e02ad527037a62aa1bd804019050898883098a7f2e2419f9ec02ec394c9871c832963dc1b89d743c8c7b964029b2311687b1fe2385098b7f2969f27eed31a480b9c36c764379dbca2cc8fdd1415c3dded62940bcde0bd771870901017f13ab2d136ccf37d447e9f2e14a7cedc95e727f8446f6d9d7e55afc01219fd649019550898783098a7f176cc029695ad02582a70eff08a6fd99d057e12e58e7d7b6b16cdfabc8ee291185098b7f143021ec686a3f330d5f9e654638065ce6cd79e28c5b3753326244ee65a1b1a7870901017f1121552fca26061619d24d843dc82769c1b04fcec26f55194c2e3e869acc6a9a01945089818209935089818b868709099050898986098a7f16ed41e13bb9c0c66ae119424fddbcbc9314dc9fdbdeea55d6c64543dc4903e088098b7f109b7f411ba0e4c9b2b70caf5c36a7b194be7c11ad24378bfedb68592ba8118b840901017eef653322b13d6c889bc81715c37d77a6cd267d595c4a8909a5546c7c97cff1019350898886098a7f2e2419f9ec02ec394c9871c832963dc1b89d743c8c7b964029b2311687b1fe2388098b7f2969f27eed31a480b9c36c764379dbca2cc8fdd1415c3dded62940bcde0bd771840901017f0e25483e45a665208b261d8ba74051e6400c776d652595d9845aca35d8a397d3019250898786098a7f176cc029695ad02582a70eff08a6fd99d057e12e58e7d7b6b16cdfabc8ee291188098b7f143021ec686a3f330d5f9e654638065ce6cd79e28c5b3753326244ee65a1b1a7840901017f29f536dcb9dd7682245264659e15d88e395ac3d4dde92d8c46448db979eeba8901915089848509905089848b838409099350898983098a7f16ed41e13bb9c0c66ae119424fddbcbc9314dc9fdbdeea55d6c64543dc4903e085098b7f109b7f411ba0e4c9b2b70caf5c36a7b194be7c11ad24378bfedb68592ba8118b870901017f2a56ef9f2c53febadfda33575dbdbd885a124e2780bbea170e456baace0fa5be019050898883098a7f2e2419f9ec02ec394c9871c832963dc1b89d743c8c7b964029b2311687b1fe2385098b7f2969f27eed31a480b9c36c764379dbca2cc8fdd1415c3dded62940bcde0bd771870901017f1c8361c78eb5cf5decfb7a2d17b5c409f2ae2999a46762e8ee416240a8cb9af1019550898783098a7f176cc029695ad02582a70eff08a6fd99d057e12e58e7d7b6b16cdfabc8ee291185098b7f143021ec686a3f330d5f9e654638065ce6cd79e28c5b3753326244ee65a1b1a7870901017f151aff5f38b20a0fc0473089aaf0206b83e8e68a764507bfd3d0ab4be74319c501945089818209935089818b868709099050898986098a7f16ed41e13bb9c0c66ae119424fddbcbc9314dc9fdbdeea55d6c64543dc4903e088098b7f109b7f411ba0e4c9b2b70caf5c36a7b194be7c11ad24378bfedb68592ba8118b840901017f04c6187e41ed881dc1b239c88f7f9d43a9f52fc8c8b6cdd1e76e47615b51f100019350898886098a7f2e2419f9ec02ec394c9871c832963dc1b89d743c8c7b964029b2311687b1fe2388098b7f2969f27eed31a480b9c36c764379dbca2cc8fdd1415c3dded62940bcde0bd771840901017f13b37bd80f4d27fb10d84331f6fb6d534b81c61ed15776449e801b7ddc9c2967019250898786098a7f176cc029695ad02582a70eff08a6fd99d057e12e58e7d7b6b16cdfabc8ee291188098b7f143021ec686a3f330d5f9e654638065ce6cd79e28c5b3753326244ee65a1b1a7840901017f01a5c536273c2d9df578bfbd32c17b7a2ce3664c2a52032c9321ceb1c4e8a8e401915089848509905089848b838409099350898983098a7f16ed41e13bb9c0c66ae119424fddbcbc9314dc9fdbdeea55d6c64543dc4903e085098b7f109b7f411ba0e4c9b2b70caf5c36a7b194be7c11ad24378bfedb68592ba8118b870901017f2ab3561834ca73835ad05f5d7acb950b4a9a2c666b9726da832239065b7c3b02019050898883098a7f2e2419f9ec02ec394c9871c832963dc1b89d743c8c7b964029b2311687b1fe2385098b7f2969f27eed31a480b9c36c764379dbca2cc8fdd1415c3dded62940bcde0bd771870901017f1d4d8ec291e720db200fe6d686c0d613acaf6af4e95d3bf69f7ed516a597b646019550898783098a7f176cc029695ad02582a70eff08a6fd99d057e12e58e7d7b6b16cdfabc8ee291185098b7f143021ec686a3f330d5f9e654638065ce6cd79e28c5b3753326244ee65a1b1a7870901017f041294d2cc484d228f5784fe7919fd2bb925351240a04b711514c9c80b65af1d01945089818209935089818b868709099050898986098a7f16ed41e13bb9c0c66ae119424fddbcbc9314dc9fdbdeea55d6c64543dc4903e088098b7f109b7f411ba0e4c9b2b70caf5c36a7b194be7c11ad24378bfedb68592ba8118b840901017f154ac98e01708c611c4fa715991f004898f57939d126e392042971dd90e81fc6019350898886098a7f2e2419f9ec02ec394c9871c832963dc1b89d743c8c7b964029b2311687b1fe2388098b7f2969f27eed31a480b9c36c764379dbca2cc8fdd1415c3dded62940bcde0bd771840901017f0b339d8acca7d4f83eedd84093aef51050b3684c88f8b0b04524563bc6ea4da4019250898786098a7f176cc029695ad02582a70eff08a6fd99d057e12e58e7d7b6b16cdfabc8ee291188098b7f143021ec686a3f330d5f9e654638065ce6cd79e28c5b3753326244ee65a1b1a7840901017f0955e49e6610c94254a4f84cfbab344598f0e71eaff4a7dd81ed95b50839c82e01915089848509905089848b838409099350898983098a7f16ed41e13bb9c0c66ae119424fddbcbc9314dc9fdbdeea55d6c64543dc4903e085098b7f109b7f411ba0e4c9b2b70caf5c36a7b194be7c11ad24378bfedb68592ba8118b870901017f06746a6156eba54426b9e22206f15abca9a6f41e6f535c6f3525401ea0654626019050898883098a7f2e2419f9ec02ec394c9871c832963dc1b89d743c8c7b964029b2311687b1fe2385098b7f2969f27eed31a480b9c36c764379dbca2cc8fdd1415c3dded62940bcde0bd771870901017f0f18f5a0ecd1423c496f3820c549c27838e5790e2bd0a196ac917c7ff32077fb019550898783098a7f176cc029695ad02582a70eff08a6fd99d057e12e58e7d7b6b16cdfabc8ee291185098b7f143021ec686a3f330d5f9e654638065ce6cd79e28c5b3753326244ee65a1b1a7870901017f04f6eeca1751f7308ac59eff5beb261e4bb563583ede7bc92a738223d6f76e1301945089818209935089818b868709099050898986098a7f16ed41e13bb9c0c66ae119424fddbcbc9314dc9fdbdeea55d6c64543dc4903e088098b7f109b7f411ba0e4c9b2b70caf5c36a7b194be7c11ad24378bfedb68592ba8118b840901017f2b56973364c4c4f5c1a3ec4da3cdce038811eb116fb3e45bc1768d26fc0b3758019350898886098a7f2e2419f9ec02ec394c9871c832963dc1b89d743c8c7b964029b2311687b1fe2388098b7f2969f27eed31a480b9c36c764379dbca2cc8fdd1415c3dded62940bcde0bd771840901017f123769dd49d5b054dcd76b89804b1bcb8e1392b385716a5d83feb65d437f29ef019250898786098a7f176cc029695ad02582a70eff08a6fd99d057e12e58e7d7b6b16cdfabc8ee291188098b7f143021ec686a3f330d5f9e654638065ce6cd79e28c5b3753326244ee65a1b1a7840901017f2147b424fc48c80a88ee52b91169aacea989f6446471150994257b2fb01c63e901915089848509905089848b838409099350898983098a7f16ed41e13bb9c0c66ae119424fddbcbc9314dc9fdbdeea55d6c64543dc4903e085098b7f109b7f411ba0e4c9b2b70caf5c36a7b194be7c11ad24378bfedb68592ba8118b870901017f0fdc1f58548b85701a6c5505ea332a29647e6f34ad4243c2ea54ad897cebe54d019050898883098a7f2e2419f9ec02ec394c9871c832963dc1b89d743c8c7b964029b2311687b1fe2385098b7f2969f27eed31a480b9c36c764379dbca2cc8fdd1415c3dded62940bcde0bd771870901017f12373a8251fea004df68abcf0f7786d4bceff28c5dbbe0c3944f685cc0a0b1f2019550898783098a7f176cc029695ad02582a70eff08a6fd99d057e12e58e7d7b6b16cdfabc8ee291185098b7f143021ec686a3f330d5f9e654638065ce6cd79e28c5b3753326244ee65a1b1a7870901017f21e4f4ea5f35f85bad7ea52ff742c9e8a642756b6af44203dd8a1f35c1a9003501945089818209935089818b868709099050898986098a7f16ed41e13bb9c0c66ae119424fddbcbc9314dc9fdbdeea55d6c64543dc4903e088098b7f109b7f411ba0e4c9b2b70caf5c36a7b194be7c11ad24378bfedb68592ba8118b840901017f16243916d69d2ca3dfb4722224d4c462b57366492f45e90d8a81934f1bc3b147019350898886098a7f2e2419f9ec02ec394c9871c832963dc1b89d743c8c7b964029b2311687b1fe2388098b7f2969f27eed31a480b9c36c764379dbca2cc8fdd1415c3dded62940bcde0bd771840901017f1efbe46dd7a578b4f66f9adbc88b4378abc21566e1a0453ca13a4159cac04ac2019250898786098a7f176cc029695ad02582a70eff08a6fd99d057e12e58e7d7b6b16cdfabc8ee291188098b7f143021ec686a3f330d5f9e654638065ce6cd79e28c5b3753326244ee65a1b1a7840901017f07ea5e8537cf5dd08886020e23a7f387d468d5525be66f853b672cc96a88969a01915089848509905089848b838409099350898983098a7f16ed41e13bb9c0c66ae119424fddbcbc9314dc9fdbdeea55d6c64543dc4903e085098b7f109b7f411ba0e4c9b2b70caf5c36a7b194be7c11ad24378bfedb68592ba8118b870901017f05a8c4f9968b8aa3b7b478a30f9a5b63650f19a75e7ce11ca9fe16c0b76c00bc019050898883098a7f2e2419f9ec02ec394c9871c832963dc1b89d743c8c7b964029b2311687b1fe2385098b7f2969f27eed31a480b9c36c764379dbca2cc8fdd1415c3dded62940bcde0bd771870901017f20f057712cc21654fbfe59bd345e8dac3f7818c701b9c7882d9d57b72a32e83f019550898783098a7f176cc029695ad02582a70eff08a6fd99d057e12e58e7d7b6b16cdfabc8ee291185098b7f143021ec686a3f330d5f9e654638065ce6cd79e28c5b3753326244ee65a1b1a7870901017f04a12ededa9dfd689672f8c67fee31636dcd8e88d01d49019bd90b33eb33db6901945089818209935089818b868709099050898986098a7f16ed41e13bb9c0c66ae119424fddbcbc9314dc9fdbdeea55d6c64543dc4903e088098b7f109b7f411ba0e4c9b2b70caf5c36a7b194be7c11ad24378bfedb68592ba8118b840901017f27e88d8c15f37dcee44f1e5425a51decbd136ce5091a6767e49ec9544ccd101a019350898886098a7f2e2419f9ec02ec394c9871c832963dc1b89d743c8c7b964029b2311687b1fe2388098b7f2969f27eed31a480b9c36c764379dbca2cc8fdd1415c3dded62940bcde0bd771840901017f2feed17b84285ed9b8a5c8c5e95a41f66e096619a7703223176c41ee433de4d1019250898786098a7f176cc029695ad02582a70eff08a6fd99d057e12e58e7d7b6b16cdfabc8ee291188098b7f143021ec686a3f330d5f9e654638065ce6cd79e28c5b3753326244ee65a1b1a7840901017f1ed7cc76edf45c7c404241420f729cf394e5942911312a0d6972b8bd53aff2b801915089848509905089848b838409099350898983098a7f16ed41e13bb9c0c66ae119424fddbcbc9314dc9fdbdeea55d6c64543dc4903e085098b7f109b7f411ba0e4c9b2b70caf5c36a7b194be7c11ad24378bfedb68592ba8118b870901017f15742e99b9bfa323157ff8c586f5660eac6783476144cdcadf2874be45466b1a019050898883098a7f2e2419f9ec02ec394c9871c832963dc1b89d743c8c7b964029b2311687b1fe2385098b7f2969f27eed31a480b9c36c764379dbca2cc8fdd1415c3dded62940bcde0bd771870901017f1aac285387f65e82c895fc6887ddf40577107454c6ec0317284f033f27d0c785019550898783098a7f176cc029695ad02582a70eff08a6fd99d057e12e58e7d7b6b16cdfabc8ee291185098b7f143021ec686a3f330d5f9e654638065ce6cd79e28c5b3753326244ee65a1b1a7870901017f25851c3c845d4790f9ddadbdb6057357832e2e7a49775f71ec75a96554d67c7701945089818209935089818b868709099050898986098a7f16ed41e13bb9c0c66ae119424fddbcbc9314dc9fdbdeea55d6c64543dc4903e088098b7f109b7f411ba0e4c9b2b70caf5c36a7b194be7c11ad24378bfedb68592ba8118b840901017f15a5821565cc2ec2ce78457db197edf353b7ebba2c5523370ddccc3d9f146a67019350898886098a7f2e2419f9ec02ec394c9871c832963dc1b89d743c8c7b964029b2311687b1fe2388098b7f2969f27eed31a480b9c36c764379dbca2cc8fdd1415c3dded62940bcde0bd771840901017f2411d57a4813b9980efa7e31a1db5966dcf64f36044277502f15485f28c71727019250898786098a7f176cc029695ad02582a70eff08a6fd99d057e12e58e7d7b6b16cdfabc8ee291188098b7f143021ec686a3f330d5f9e654638065ce6cd79e28c5b3753326244ee65a1b1a7840901017e2e6f8d6520cd4713e335b8c0b6d2e647e9a98e12f4cd2558828b5ef6cb4c9b01915089848509905089848b838409099350898983098a7f16ed41e13bb9c0c66ae119424fddbcbc9314dc9fdbdeea55d6c64543dc4903e085098b7f109b7f411ba0e4c9b2b70caf5c36a7b194be7c11ad24378bfedb68592ba8118b870901017f2ff7bc8f4380cde997da00b616b0fcd1af8f0e91e2fe1ed7398834609e0315d2019050898883098a7f2e2419f9ec02ec394c9871c832963dc1b89d743c8c7b964029b2311687b1fe2385098b7f2969f27eed31a480b9c36c764379dbca2cc8fdd1415c3dded62940bcde0bd771870901017eb9831b948525595ee02724471bcd182e9521f6b7bb68f1e93be4febb0d3cbe019550898783098a7f176cc029695ad02582a70eff08a6fd99d057e12e58e7d7b6b16cdfabc8ee291185098b7f143021ec686a3f330d5f9e654638065ce6cd79e28c5b3753326244ee65a1b1a7870901017f0a2f53768b8ebf6a86913b0e57c04e011ca408648a4743a87d77adbf0c9c351201945089818209935089818b868709099050898986098a7f16ed41e13bb9c0c66ae119424fddbcbc9314dc9fdbdeea55d6c64543dc4903e088098b7f109b7f411ba0e4c9b2b70caf5c36a7b194be7c11ad24378bfedb68592ba8118b840901017e248156142fd0373a479f91ff239e960f599ff7e94be69b7f2a290305e1198d019350898886098a7f2e2419f9ec02ec394c9871c832963dc1b89d743c8c7b964029b2311687b1fe2388098b7f2969f27eed31a480b9c36c764379dbca2cc8fdd1415c3dded62940bcde0bd771840901017f171d5620b87bfb1328cf8c02ab3f0c9a397196aa6a542c2350eb512a2b2bcda9019250898786098a7f176cc029695ad02582a70eff08a6fd99d057e12e58e7d7b6b16cdfabc8ee291188098b7f143021ec686a3f330d5f9e654638065ce6cd79e28c5b3753326244ee65a1b1a7840901017f170a4f55536f7dc970087c7c10d6fad760c952172dd54dd99d1045e4ec34a80801915089848509905089848b838409099350898983098a7f16ed41e13bb9c0c66ae119424fddbcbc9314dc9fdbdeea55d6c64543dc4903e085098b7f109b7f411ba0e4c9b2b70caf5c36a7b194be7c11ad24378bfedb68592ba8118b870901017f29aba33f799fe66c2ef3134aea04336ecc37e38c1cd211ba482eca17e2dbfae1019050898883098a7f2e2419f9ec02ec394c9871c832963dc1b89d743c8c7b964029b2311687b1fe2385098b7f2969f27eed31a480b9c36c764379dbca2cc8fdd1415c3dded62940bcde0bd771870901017f1e9bc179a4fdd758fdd1bb1945088d47e70d114a03f6a0e8b5ba650369e64973019550898783098a7f176cc029695ad02582a70eff08a6fd99d057e12e58e7d7b6b16cdfabc8ee291185098b7f143021ec686a3f330d5f9e654638065ce6cd79e28c5b3753326244ee65a1b1a7870901017f1dd269799b660fad58f7f4892dfb0b5afeaad869a9c4b44f9c9e1c43bdaf8f0901945089818209935089818b868709099050898986098a7f16ed41e13bb9c0c66ae119424fddbcbc9314dc9fdbdeea55d6c64543dc4903e088098b7f109b7f411ba0e4c9b2b70caf5c36a7b194be7c11ad24378bfedb68592ba8118b840901017f22cdbc8b70117ad1401181d02e15459e7ccd426fe869c7c95d1dd2cb0f24af38019350898886098a7f2e2419f9ec02ec394c9871c832963dc1b89d743c8c7b964029b2311687b1fe2388098b7f2969f27eed31a480b9c36c764379dbca2cc8fdd1415c3dded62940bcde0bd771840901017f0ef042e454771c533a9f57a55c503fcefd3150f52ed94a7cd5ba93b9c7dacefd019250898786098a7f176cc029695ad02582a70eff08a6fd99d057e12e58e7d7b6b16cdfabc8ee291188098b7f143021ec686a3f330d5f9e654638065ce6cd79e28c5b3753326244ee65a1b1a7840901017f11609e06ad6c8fe2f287f3036037e8851318e8b08a0359a03b304ffca62e828401915089848509905089848b838409099350898983098a7f16ed41e13bb9c0c66ae119424fddbcbc9314dc9fdbdeea55d6c64543dc4903e085098b7f109b7f411ba0e4c9b2b70caf5c36a7b194be7c11ad24378bfedb68592ba8118b870901017f1166d9e554616dba9e753eea427c17b7fecd58c076dfe42708b08f5b783aa9af019050898883098a7f2e2419f9ec02ec394c9871c832963dc1b89d743c8c7b964029b2311687b1fe2385098b7f2969f27eed31a480b9c36c764379dbca2cc8fdd1415c3dded62940bcde0bd771870901017f2de52989431a859593413026354413db177fbf4cd2ac0b56f855a888357ee466019550898783098a7f176cc029695ad02582a70eff08a6fd99d057e12e58e7d7b6b16cdfabc8ee291185098b7f143021ec686a3f330d5f9e654638065ce6cd79e28c5b3753326244ee65a1b1a7870901017f3006eb4ffc7a85819a6da492f3a8ac1df51aee5b17b8e89d74bf01cf5f71e9ad01945089818209935089818b868709099050898986098a7f16ed41e13bb9c0c66ae119424fddbcbc9314dc9fdbdeea55d6c64543dc4903e088098b7f109b7f411ba0e4c9b2b70caf5c36a7b194be7c11ad24378bfedb68592ba8118b840901017f2af41fbb61ba8a80fdcf6fff9e3f6f422993fe8f0a4639f962344c8225145086019350898886098a7f2e2419f9ec02ec394c9871c832963dc1b89d743c8c7b964029b2311687b1fe2388098b7f2969f27eed31a480b9c36c764379dbca2cc8fdd1415c3dded62940bcde0bd771840901017f119e684de476155fe5a6b41a8ebc85db8718ab27889e85e781b214bace4827c3019250898786098a7f176cc029695ad02582a70eff08a6fd99d057e12e58e7d7b6b16cdfabc8ee291188098b7f143021ec686a3f330d5f9e654638065ce6cd79e28c5b3753326244ee65a1b1a7840901017f1835b786e2e8925e188bea59ae363537b51248c23828f047cff784b97b3fd80001915089848509905089848b838409099350898983098a7f16ed41e13bb9c0c66ae119424fddbcbc9314dc9fdbdeea55d6c64543dc4903e085098b7f109b7f411ba0e4c9b2b70caf5c36a7b194be7c11ad24378bfedb68592ba8118b870901017f28201a34c594dfa34d794996c6433a20d152bac2a7905c926c40e285ab32eeb6019050898883098a7f2e2419f9ec02ec394c9871c832963dc1b89d743c8c7b964029b2311687b1fe2385098b7f2969f27eed31a480b9c36c764379dbca2cc8fdd1415c3dded62940bcde0bd771870901017f083efd7a27d1751094e80fefaf78b000864c82eb571187724a761f88c22cc4e7019550898783098a7f176cc029695ad02582a70eff08a6fd99d057e12e58e7d7b6b16cdfabc8ee291185098b7f143021ec686a3f330d5f9e654638065ce6cd79e28c5b3753326244ee65a1b1a7870901017f0b6f88a3577199526158e61ceea27be811c16df7774dd8519e079564f61fd13b01945089818209935089818b868709099050898986098a7f16ed41e13bb9c0c66ae119424fddbcbc9314dc9fdbdeea55d6c64543dc4903e088098b7f109b7f411ba0e4c9b2b70caf5c36a7b194be7c11ad24378bfedb68592ba8118b840901017f0ec868e6d15e51d9644f66e1d6471a94589511ca00d29e1014390e6ee4254f5b019350898886098a7f2e2419f9ec02ec394c9871c832963dc1b89d743c8c7b964029b2311687b1fe2388098b7f2969f27eed31a480b9c36c764379dbca2cc8fdd1415c3dded62940bcde0bd771840901017f2af33e3f866771271ac0c9b3ed2e1142ecd3e74b939cd40d00d937ab84c98591019250898786098a7f176cc029695ad02582a70eff08a6fd99d057e12e58e7d7b6b16cdfabc8ee291188098b7f143021ec686a3f330d5f9e654638065ce6cd79e28c5b3753326244ee65a1b1a7840901017f0b520211f904b5e7d09b5d961c6ace7734568c547dd6858b364ce5e47951f17801915089848509905089848b838409099350898983098a7f16ed41e13bb9c0c66ae119424fddbcbc9314dc9fdbdeea55d6c64543dc4903e085098b7f109b7f411ba0e4c9b2b70caf5c36a7b194be7c11ad24378bfedb68592ba8118b870901017f0b2d722d0919a1aad8db58f10062a92ea0c56ac4270e822cca228620188a1d40019050898883098a7f2e2419f9ec02ec394c9871c832963dc1b89d743c8c7b964029b2311687b1fe2385098b7f2969f27eed31a480b9c36c764379dbca2cc8fdd1415c3dded62940bcde0bd771870901017f1f790d4d7f8cf094d980ceb37c2453e957b54a9991ca38bbe0061d1ed6e562d4019550898783098a7f176cc029695ad02582a70eff08a6fd99d057e12e58e7d7b6b16cdfabc8ee291185098b7f143021ec686a3f330d5f9e654638065ce6cd79e28c5b3753326244ee65a1b1a7870901017f0171eb95dfbf7d1eaea97cd385f780150885c16235a2a6a8da92ceb01e50423301945089818209935089818b868709099050898986098a7f16ed41e13bb9c0c66ae119424fddbcbc9314dc9fdbdeea55d6c64543dc4903e088098b7f109b7f411ba0e4c9b2b70caf5c36a7b194be7c11ad24378bfedb68592ba8118b840901017f0c2d0e3b5fd57549329bf6885da66b9b790b40defd2c8650762305381b168873019350898886098a7f2e2419f9ec02ec394c9871c832963dc1b89d743c8c7b964029b2311687b1fe2388098b7f2969f27eed31a480b9c36c764379dbca2cc8fdd1415c3dded62940bcde0bd771840901017f1162fb28689c27154e5a8228b4e72b377cbcafa589e283c35d3803054407a18d019250898786098a7f176cc029695ad02582a70eff08a6fd99d057e12e58e7d7b6b16cdfabc8ee291188098b7f143021ec686a3f330d5f9e654638065ce6cd79e28c5b3753326244ee65a1b1a7840901017f2f1459b65dee441b64ad386a91e8310f282c5a92a89e19921623ef8249711bc001915089848509905089848b838409099350898983098a7f16ed41e13bb9c0c66ae119424fddbcbc9314dc9fdbdeea55d6c64543dc4903e085098b7f109b7f411ba0e4c9b2b70caf5c36a7b194be7c11ad24378bfedb68592ba8118b870901017f1e6ff3216b688c3d996d74367d5cd4c1bc489d46754eb712c243f70d1b53cfbb019050898883098a7f2e2419f9ec02ec394c9871c832963dc1b89d743c8c7b964029b2311687b1fe2385098b7f2969f27eed31a480b9c36c764379dbca2cc8fdd1415c3dded62940bcde0bd771870901017f01ca8be73832b8d0681487d27d157802d741a6f36cdc2a0576881f9326478875019550898783098a7f176cc029695ad02582a70eff08a6fd99d057e12e58e7d7b6b16cdfabc8ee291185098b7f143021ec686a3f330d5f9e654638065ce6cd79e28c5b3753326244ee65a1b1a7870901017f1f7735706ffe9fc586f976d5bdf223dc680286080b10cea00b9b5de315f9650e01945089818209935089818b868709099050898986098a7f16ed41e13bb9c0c66ae119424fddbcbc9314dc9fdbdeea55d6c64543dc4903e088098b7f109b7f411ba0e4c9b2b70caf5c36a7b194be7c11ad24378bfedb68592ba8118b840901017f2522b60f4ea3307640a0c2dce041fba921ac10a3d5f096ef4745ca838285f019019350898886098a7f2e2419f9ec02ec394c9871c832963dc1b89d743c8c7b964029b2311687b1fe2388098b7f2969f27eed31a480b9c36c764379dbca2cc8fdd1415c3dded62940bcde0bd771840901017f23f0bee001b1029d5255075ddc957f833418cad4f52b6c3f8ce16c235572575b019250898786098a7f176cc029695ad02582a70eff08a6fd99d057e12e58e7d7b6b16cdfabc8ee291188098b7f143021ec686a3f330d5f9e654638065ce6cd79e28c5b3753326244ee65a1b1a7840901017f2bc1ae8b8ddbb81fcaac2d44555ed5685d142633e9df905f66d9401093082d5901915089848509905089848b838409099350898983098a7f16ed41e13bb9c0c66ae119424fddbcbc9314dc9fdbdeea55d6c64543dc4903e085098b7f109b7f411ba0e4c9b2b70caf5c36a7b194be7c11ad24378bfedb68592ba8118b870901017f0f9406b8296564a37304507b8dba3ed162371273a07b1fc98011fcd6ad72205f019050898883098a7f2e2419f9ec02ec394c9871c832963dc1b89d743c8c7b964029b2311687b1fe2385098b7f2969f27eed31a480b9c36c764379dbca2cc8fdd1415c3dded62940bcde0bd771870901017f2360a8eb0cc7defa67b72998de90714e17e75b174a52ee4acb126c8cd995f0a8019550898783098a7f176cc029695ad02582a70eff08a6fd99d057e12e58e7d7b6b16cdfabc8ee291185098b7f143021ec686a3f330d5f9e654638065ce6cd79e28c5b3753326244ee65a1b1a7870901017f15871a5cddead976804c803cbaef255eb4815a5e96df8b006dcbbc2767f8894801945089818209935089818b868709099050898986098a7f16ed41e13bb9c0c66ae119424fddbcbc9314dc9fdbdeea55d6c64543dc4903e088098b7f109b7f411ba0e4c9b2b70caf5c36a7b194be7c11ad24378bfedb68592ba8118b840901017f193a56766998ee9e0a8652dd2f3b1da0362f4f54f72379544f957ccdeefb420f019350898886098a7f2e2419f9ec02ec394c9871c832963dc1b89d743c8c7b964029b2311687b1fe2388098b7f2969f27eed31a480b9c36c764379dbca2cc8fdd1415c3dded62940bcde0bd771840901017f2a394a43934f86982f9be56ff4fab1703b2e63c8ad334834e4309805e777ae0f019250898786098a7f176cc029695ad02582a70eff08a6fd99d057e12e58e7d7b6b16cdfabc8ee291188098b7f143021ec686a3f330d5f9e654638065ce6cd79e28c5b3753326244ee65a1b1a7840901017f1859954cfeb8695f3e8b635dcb345192892cd11223443ba7b4166e8876c0d14201915089848509905089848b838409099350898983098a7f16ed41e13bb9c0c66ae119424fddbcbc9314dc9fdbdeea55d6c64543dc4903e085098b7f109b7f411ba0e4c9b2b70caf5c36a7b194be7c11ad24378bfedb68592ba8118b870901017f04e1181763050e58013444dbcb99f1902b11bc25d90bbdca408d3819f4fed32b019050898883098a7f2e2419f9ec02ec394c9871c832963dc1b89d743c8c7b964029b2311687b1fe2385098b7f2969f27eed31a480b9c36c764379dbca2cc8fdd1415c3dded62940bcde0bd771870901017f0fdb253dee83869d40c335ea64de8c5bb10eb82db08b5e8b1f5e5552bfd05f23019550898783098a7f176cc029695ad02582a70eff08a6fd99d057e12e58e7d7b6b16cdfabc8ee291185098b7f143021ec686a3f330d5f9e654638065ce6cd79e28c5b3753326244ee65a1b1a7870901017f058cbe8a9a5027bdaa4efb623adead6275f08686f1c08984a9d7c5bae9b4f1c001945089818209935089818b868709099050898986098a7f16ed41e13bb9c0c66ae119424fddbcbc9314dc9fdbdeea55d6c64543dc4903e088098b7f109b7f411ba0e4c9b2b70caf5c36a7b194be7c11ad24378bfedb68592ba8118b840901017f1382edce9971e186497eadb1aeb1f52b23b4b83bef023ab0d15228b4cceca59a019350898886098a7f2e2419f9ec02ec394c9871c832963dc1b89d743c8c7b964029b2311687b1fe2388098b7f2969f27eed31a480b9c36c764379dbca2cc8fdd1415c3dded62940bcde0bd771840901017f03464990f045c6ee0819ca51fd11b0be7f61b8eb99f14b77e1e6634601d9e8b5019250898786098a7f176cc029695ad02582a70eff08a6fd99d057e12e58e7d7b6b16cdfabc8ee291188098b7f143021ec686a3f330d5f9e654638065ce6cd79e28c5b3753326244ee65a1b1a7840901017f23f7bfc8720dc296fff33b41f98ff83c6fcab4605db2eb5aaa5bc137aeb70a5801915089848509905089848b838409099350898983098a7f16ed41e13bb9c0c66ae119424fddbcbc9314dc9fdbdeea55d6c64543dc4903e085098b7f109b7f411ba0e4c9b2b70caf5c36a7b194be7c11ad24378bfedb68592ba8118b870901017f0a59a158e3eec2117e6e94e7f0e9decf18c3ffd5e1531a9219636158bbaf62f2019050898883098a7f2e2419f9ec02ec394c9871c832963dc1b89d743c8c7b964029b2311687b1fe2385098b7f2969f27eed31a480b9c36c764379dbca2cc8fdd1415c3dded62940bcde0bd771870901017f06ec54c80381c052b58bf23b312ffd3ce2c4eba065420af8f4c23ed0075fd07b019550898783098a7f176cc029695ad02582a70eff08a6fd99d057e12e58e7d7b6b16cdfabc8ee291185098b7f143021ec686a3f330d5f9e654638065ce6cd79e28c5b3753326244ee65a1b1a7870901017f118872dc832e0eb5476b56648e867ec8b09340f7a7bcb1b4962f0ff9ed1f9d0101945089818209935089818b868709099050898986098a7f16ed41e13bb9c0c66ae119424fddbcbc9314dc9fdbdeea55d6c64543dc4903e088098b7f109b7f411ba0e4c9b2b70caf5c36a7b194be7c11ad24378bfedb68592ba8118b840901017f13d69fa127d834165ad5c7cba7ad59ed52e0b0f0e42d7fea95e1906b520921b1019350898886098a7f2e2419f9ec02ec394c9871c832963dc1b89d743c8c7b964029b2311687b1fe2388098b7f2969f27eed31a480b9c36c764379dbca2cc8fdd1415c3dded62940bcde0bd771840901017f169a177f63ea681270b1c6877a73d21bde143942fb71dc55fd8a49f19f10c77b019250898786098a7f176cc029695ad02582a70eff08a6fd99d057e12e58e7d7b6b16cdfabc8ee291188098b7f143021ec686a3f330d5f9e654638065ce6cd79e28c5b3753326244ee65a1b1a7840901017f04ef51591c6ead97ef42f287adce40d93abeb032b922f66ffb7e9a5a7450544d01915089848509905089848b838409099350898983098a7f16ed41e13bb9c0c66ae119424fddbcbc9314dc9fdbdeea55d6c64543dc4903e085098b7f109b7f411ba0e4c9b2b70caf5c36a7b194be7c11ad24378bfedb68592ba8118b870901017f256e175a1dc079390ecd7ca703fb2e3b19ec61805d4f03ced5f45ee6dd0f69ec019050898883098a7f2e2419f9ec02ec394c9871c832963dc1b89d743c8c7b964029b2311687b1fe2385098b7f2969f27eed31a480b9c36c764379dbca2cc8fdd1415c3dded62940bcde0bd771870901017f30102d28636abd5fe5f2af412ff6004f75cc360d3205dd2da002813d3e2ceeb2019550898783098a7f176cc029695ad02582a70eff08a6fd99d057e12e58e7d7b6b16cdfabc8ee291185098b7f143021ec686a3f330d5f9e654638065ce6cd79e28c5b3753326244ee65a1b1a7870901017f10998e42dfcd3bbf1c0714bc73eb1bf40443a3fa99bef4a31fd31be182fcc79201945089818209935089818b868709099050898986098a7f16ed41e13bb9c0c66ae119424fddbcbc9314dc9fdbdeea55d6c64543dc4903e088098b7f109b7f411ba0e4c9b2b70caf5c36a7b194be7c11ad24378bfedb68592ba8118b840901017f193edd8e9fcf3d7625fa7d24b598a1d89f3362eaf4d582efecad76f879e36860019350898886098a7f2e2419f9ec02ec394c9871c832963dc1b89d743c8c7b964029b2311687b1fe2388098b7f2969f27eed31a480b9c36c764379dbca2cc8fdd1415c3dded62940bcde0bd771840901017f18168afd34f2d915d0368ce80b7b3347d1c7a561ce611425f2664d7aa51f0b5d019250898786098a7f176cc029695ad02582a70eff08a6fd99d057e12e58e7d7b6b16cdfabc8ee291188098b7f143021ec686a3f330d5f9e654638065ce6cd79e28c5b3753326244ee65a1b1a7840901017f29383c01ebd3b6ab0c017656ebe658b6a328ec77bc33626e29e2e95b33ea611101915089848509905089848b838409099350898983098a7f16ed41e13bb9c0c66ae119424fddbcbc9314dc9fdbdeea55d6c64543dc4903e085098b7f109b7f411ba0e4c9b2b70caf5c36a7b194be7c11ad24378bfedb68592ba8118b870901017f10646d2f2603de39a1f4ae5e7771a64a702db6e86fb76ab600bf573f9010c711019050898883098a7f2e2419f9ec02ec394c9871c832963dc1b89d743c8c7b964029b2311687b1fe2385098b7f2969f27eed31a480b9c36c764379dbca2cc8fdd1415c3dded62940bcde0bd771870901017f0beb5e07d1b27145f575f1395a55bf132f90c25b40da7b3864d0242dcb1117fb019550898783098a7f176cc029695ad02582a70eff08a6fd99d057e12e58e7d7b6b16cdfabc8ee291185098b7f143021ec686a3f330d5f9e654638065ce6cd79e28c5b3753326244ee65a1b1a7870901017f16d685252078c133dc0d3ecad62b5c8830f95bb2e54b59abdffbf018d96fa33601945089818209935089818b868709099050898986098a7f16ed41e13bb9c0c66ae119424fddbcbc9314dc9fdbdeea55d6c64543dc4903e088098b7f109b7f411ba0e4c9b2b70caf5c36a7b194be7c11ad24378bfedb68592ba8118b840901017f0a6abd1d833938f33c74154e0404b4b40a555bbbec21ddfafd672dd62047f01a019350898886098a7f2e2419f9ec02ec394c9871c832963dc1b89d743c8c7b964029b2311687b1fe2388098b7f2969f27eed31a480b9c36c764379dbca2cc8fdd1415c3dded62940bcde0bd771840901017f1a679f5d36eb7b5c8ea12a4c2dedc8feb12dffeec450317270a6f19b34cf1860019250898786098a7f176cc029695ad02582a70eff08a6fd99d057e12e58e7d7b6b16cdfabc8ee291188098b7f143021ec686a3f330d5f9e654638065ce6cd79e28c5b3753326244ee65a1b1a7840901017f0980fb233bd456c23974d50e0ebfde4726a423eada4e8f6ffbc7592e3f1b93d601915089848509905089848b838409099350898983098a7f16ed41e13bb9c0c66ae119424fddbcbc9314dc9fdbdeea55d6c64543dc4903e085098b7f109b7f411ba0e4c9b2b70caf5c36a7b194be7c11ad24378bfedb68592ba8118b870901017f161b42232e61b84cbf1810af93a38fc0cece3d5628c9282003ebacb5c312c72b019050898883098a7f2e2419f9ec02ec394c9871c832963dc1b89d743c8c7b964029b2311687b1fe2385098b7f2969f27eed31a480b9c36c764379dbca2cc8fdd1415c3dded62940bcde0bd771870901017f0ada10a90c7f0520950f7d47a60d5e6a493f09787f1564e5d09203db47de1a0b019550898783098a7f176cc029695ad02582a70eff08a6fd99d057e12e58e7d7b6b16cdfabc8ee291185098b7f143021ec686a3f330d5f9e654638065ce6cd79e28c5b3753326244ee65a1b1a7870901017f1a730d372310ba82320345a29ac4238ed3f07a8a2b4e121bb50ddb9af407f45101945089818209935089818b868709099050898986098a7f16ed41e13bb9c0c66ae119424fddbcbc9314dc9fdbdeea55d6c64543dc4903e088098b7f109b7f411ba0e4c9b2b70caf5c36a7b194be7c11ad24378bfedb68592ba8118b840901017f2c8120f268ef054f817064c369dda7ea908377feaba5c4dffbda10ef58e8c556019350898886098a7f2e2419f9ec02ec394c9871c832963dc1b89d743c8c7b964029b2311687b1fe2388098b7f2969f27eed31a480b9c36c764379dbca2cc8fdd1415c3dded62940bcde0bd771840901017f1c7c8824f758753fa57c00789c684217b930e95313bcb73e6e7b8649a4968f70019250898786098a7f176cc029695ad02582a70eff08a6fd99d057e12e58e7d7b6b16cdfabc8ee291188098b7f143021ec686a3f330d5f9e654638065ce6cd79e28c5b3753326244ee65a1b1a7840901017f2cd9ed31f5f8691c8e39e4077a74faa0f400ad8b491eb3f7b47b27fa3fd1cf7701915089848509905089848b838409099350898983098a7f16ed41e13bb9c0c66ae119424fddbcbc9314dc9fdbdeea55d6c64543dc4903e085098b7f109b7f411ba0e4c9b2b70caf5c36a7b194be7c11ad24378bfedb68592ba8118b870901017f23ff4f9d46813457cf60d92f57618399a5e022ac321ca550854ae23918a22eea019050898883098a7f2e2419f9ec02ec394c9871c832963dc1b89d743c8c7b964029b2311687b1fe2385098b7f2969f27eed31a480b9c36c764379dbca2cc8fdd1415c3dded62940bcde0bd771870901017f09945a5d147a4f66ceece6405dddd9d0af5a2c5103529407dff1ea58f180426d019550898783098a7f176cc029695ad02582a70eff08a6fd99d057e12e58e7d7b6b16cdfabc8ee291185098b7f143021ec686a3f330d5f9e654638065ce6cd79e28c5b3753326244ee65a1b1a7870901017f188d9c528025d4c2b67660c6b771b90f7c7da6eaa29d3f268a6dd223ec6fc63001945089818209935089818b868709099050898986098a7f16ed41e13bb9c0c66ae119424fddbcbc9314dc9fdbdeea55d6c64543dc4903e088098b7f109b7f411ba0e4c9b2b70caf5c36a7b194be7c11ad24378bfedb68592ba8118b840901017f3050e37996596b7f81f68311431d8734dba7d926d3633595e0c0d8ddf4f0f47f019350898886098a7f2e2419f9ec02ec394c9871c832963dc1b89d743c8c7b964029b2311687b1fe2388098b7f2969f27eed31a480b9c36c764379dbca2cc8fdd1415c3dded62940bcde0bd771840901017f15af1169396830a91600ca8102c35c426ceae5461e3f95d89d829518d30afd78019250898786098a7f176cc029695ad02582a70eff08a6fd99d057e12e58e7d7b6b16cdfabc8ee291188098b7f143021ec686a3f330d5f9e654638065ce6cd79e28c5b3753326244ee65a1b1a7840901017f1da6d09885432ea9a06d9f37f873d985dae933e351466b2904284da3320d8acc01915089848509905089848b838409099350898983098a7f16ed41e13bb9c0c66ae119424fddbcbc9314dc9fdbdeea55d6c64543dc4903e085098b7f109b7f411ba0e4c9b2b70caf5c36a7b194be7c11ad24378bfedb68592ba8118b870901017f2796ea90d269af29f5f8acf33921124e4e4fad3dbe658945e546ee411ddaa9cb019050898883098a7f2e2419f9ec02ec394c9871c832963dc1b89d743c8c7b964029b2311687b1fe2385098b7f2969f27eed31a480b9c36c764379dbca2cc8fdd1415c3dded62940bcde0bd771870901017f202d7dd1da0f6b4b0325c8b3307742f01e15612ec8e9304a7cb0319e01d32d60019550898783098a7f176cc029695ad02582a70eff08a6fd99d057e12e58e7d7b6b16cdfabc8ee291185098b7f143021ec686a3f330d5f9e654638065ce6cd79e28c5b3753326244ee65a1b1a7870901017f096d6790d05bb759156a952ba263d672a2d7f9c788f4c831a29dace4c0f8be5f01945089818209935089818b868709099050898986098a7f16ed41e13bb9c0c66ae119424fddbcbc9314dc9fdbdeea55d6c64543dc4903e088098b7f109b7f411ba0e4c9b2b70caf5c36a7b194be7c11ad24378bfedb68592ba8118b840901017f054efa1f65b0fce283808965275d877b438da23ce5b13e1963798cb1447d25a4019350898886098a7f2e2419f9ec02ec394c9871c832963dc1b89d743c8c7b964029b2311687b1fe2388098b7f2969f27eed31a480b9c36c764379dbca2cc8fdd1415c3dded62940bcde0bd771840901017f1b162f83d917e93edb3308c29802deb9d8aa690113b2e14864ccf6e18e4165f1019250898786098a7f176cc029695ad02582a70eff08a6fd99d057e12e58e7d7b6b16cdfabc8ee291188098b7f143021ec686a3f330d5f9e654638065ce6cd79e28c5b3753326244ee65a1b1a7840901017f21e5241e12564dd6fd9f1cdd2a0de39eedfefc1466cc568ec5ceb745a0506edc01915089848509905089848b83840909935089838409905089838b83840909925089828309905089828b838409099150898983098a7f16ed41e13bb9c0c66ae119424fddbcbc9314dc9fdbdeea55d6c64543dc4903e085098b7f109b7f411ba0e4c9b2b70caf5c36a7b194be7c11ad24378bfedb68592ba8118b870901017f1cfb5662e8cf5ac9226a80ee17b36abecb73ab5f87e161927b4349e10e4bdf08019050898883098a7f2e2419f9ec02ec394c9871c832963dc1b89d743c8c7b964029b2311687b1fe2385098b7f2969f27eed31a480b9c36c764379dbca2cc8fdd1415c3dded62940bcde0bd771870901017f0f21177e302a771bbae6d8d1ecb373b62c99af346220ac0129c53f666eb24100019550898783098a7f176cc029695ad02582a70eff08a6fd99d057e12e58e7d7b6b16cdfabc8ee291185098b7f143021ec686a3f330d5f9e654638065ce6cd79e28c5b3753326244ee65a1b1a7870901017f1671522374606992affb0dd7f71b12bec4236aede6290546bcef7e1f515c232001945089818209935089818b86870909905089868709935089868b86870909955089858609935089858b868709099450898986098a7f16ed41e13bb9c0c66ae119424fddbcbc9314dc9fdbdeea55d6c64543dc4903e088098b7f109b7f411ba0e4c9b2b70caf5c36a7b194be7c11ad24378bfedb68592ba8118b840901017f0fa3ec5b9488259c2eb4cf24501bfad9be2ec9e42c5cc8ccd419d2a692cad870019350898886098a7f2e2419f9ec02ec394c9871c832963dc1b89d743c8c7b964029b2311687b1fe2388098b7f2969f27eed31a480b9c36c764379dbca2cc8fdd1415c3dded62940bcde0bd771840901017f193c0e04e0bd298357cb266c1506080ed36edce85c648cc085e8c57b1ab54bba019250898786098a7f176cc029695ad02582a70eff08a6fd99d057e12e58e7d7b6b16cdfabc8ee291188098b7f143021ec686a3f330d5f9e654638065ce6cd79e28c5b3753326244ee65a1b1a7840901017f102adf8ef74735a27e9128306dcbc3c99f6f7291cd406578ce14ea2adaba68f801915089848509905089848b83840909935089838409905089838b83840909925089828309905089828b838409099150898983098a7f16ed41e13bb9c0c66ae119424fddbcbc9314dc9fdbdeea55d6c64543dc4903e085098b7f109b7f411ba0e4c9b2b70caf5c36a7b194be7c11ad24378bfedb68592ba8118b870901017f0fe0af7858e49859e2a54d6f1ad945b1316aa24bfbdd23ae40a6d0cb70c3eab1019050898883098a7f2e2419f9ec02ec394c9871c832963dc1b89d743c8c7b964029b2311687b1fe2385098b7f2969f27eed31a480b9c36c764379dbca2cc8fdd1415c3dded62940bcde0bd771870901017f216f6717bbc7dedb08536a2220843f4e2da5f1daa9ebdefde8a5ea7344798d22019550898783098a7f176cc029695ad02582a70eff08a6fd99d057e12e58e7d7b6b16cdfabc8ee291185098b7f143021ec686a3f330d5f9e654638065ce6cd79e28c5b3753326244ee65a1b1a7870901017f1da55cc900f0d21f4a3e694391918a1b3c23b2ac773c6b3ef88e2e422832516101945089818209935089818b86870909905089868709935089868b86870909955089858609935089858b868709099450898a8a87098b7f16ed41e13bb9c0c66ae119424fddbcbc9314dc9fdbdeea55d6c64543dc4903e089098c7f109b7f411ba0e4c9b2b70caf5c36a7b194be7c11ad24378bfedb68592ba8118b85090101065f5260205ff35b5f604051905090565b5f80fd5b5f80fd5b5f601f19601f8301169050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b615c0182615bbb565b810181811067ffffffffffffffff82111715615c2057615c1f615bcb565b5b80604052505050565b5f615c32615baa565b9050615c3e8282615bf8565b919050565b5f67ffffffffffffffff821115615c5d57615c5c615bcb565b5b602082029050919050565b5f80fd5b5f819050919050565b615c7e81615c6c565b8114615c88575f80fd5b50565b5f81359050615c9981615c75565b92915050565b5f615cb1615cac84615c43565b615c29565b90508060208402830185811115615ccb57615cca615c68565b5b835b81811015615cf45780615ce08882615c8b565b845260208401935050602081019050615ccd565b5050509392505050565b5f82601f830112615d1257615d11615bb7565b5b6002615d1f848285615c9f565b91505092915050565b5f60408284031215615d3d57615d3c615bb3565b5b5f615d4a84828501615cfe565b91505092915050565b615d5c81615c6c565b82525050565b5f602082019050615d755f830184615d53565b9291505056fea26469706673582212205804906cb58c598b5f3037e4c176401bf08eb520cb7f07159ad241d501f2495c64736f6c634300081a0033")] + library PoseidonT3 { + uint constant M00 = 0x109b7f411ba0e4c9b2b70caf5c36a7b194be7c11ad24378bfedb68592ba8118b; + uint constant M01 = 0x2969f27eed31a480b9c36c764379dbca2cc8fdd1415c3dded62940bcde0bd771; + uint constant M02 = 0x143021ec686a3f330d5f9e654638065ce6cd79e28c5b3753326244ee65a1b1a7; + uint constant M10 = 0x16ed41e13bb9c0c66ae119424fddbcbc9314dc9fdbdeea55d6c64543dc4903e0; + uint constant M11 = 0x2e2419f9ec02ec394c9871c832963dc1b89d743c8c7b964029b2311687b1fe23; + uint constant M12 = 0x176cc029695ad02582a70eff08a6fd99d057e12e58e7d7b6b16cdfabc8ee2911; + + // See here for a simplified implementation: https://github.com/vimwitch/poseidon-solidity/blob/e57becdabb65d99fdc586fe1e1e09e7108202d53/contracts/Poseidon.sol#L40 + // Inspired by: https://github.com/iden3/circomlibjs/blob/v0.0.8/src/poseidon_slow.js + function hash(uint[2] memory) public pure returns (uint) { + assembly { + let F := 21888242871839275222246405745257275088548364400416034343698204186575808495617 + let M20 := 0x2b90bba00fca0589f617e7dcbfe82e0df706ab640ceb247b791a93b74e36736d + let M21 := 0x101071f0032379b697315876690f053d148d4e109f5fb065c8aacc55a0f89bfa + let M22 := 0x19a3fc0a56702bf417ba7fee3802593fa644470307043f7773279cd71d25d5e0 + + // load the inputs from memory + let state1 := add(mod(mload(0x80), F), 0x00f1445235f2148c5986587169fc1bcd887b08d4d00868df5696fff40956e864) + let state2 := add(mod(mload(0xa0), F), 0x08dff3487e8ac99e1f29a058d0fa80b930c728730b7ab36ce879f3890ecf73f5) + let scratch0 := mulmod(state1, state1, F) + state1 := mulmod(mulmod(scratch0, scratch0, F), state1, F) + scratch0 := mulmod(state2, state2, F) + state2 := mulmod(mulmod(scratch0, scratch0, F), state2, F) + scratch0 := add( + 0x2f27be690fdaee46c3ce28f7532b13c856c35342c84bda6e20966310fadc01d0, + add(add(15452833169820924772166449970675545095234312153403844297388521437673434406763, mulmod(state1, M10, F)), mulmod(state2, M20, F)) + ) + let scratch1 := add( + 0x2b2ae1acf68b7b8d2416bebf3d4f6234b763fe04b8043ee48b8327bebca16cf2, + add(add(18674271267752038776579386132900109523609358935013267566297499497165104279117, mulmod(state1, M11, F)), mulmod(state2, M21, F)) + ) + let scratch2 := add( + 0x0319d062072bef7ecca5eac06f97d4d55952c175ab6b03eae64b44c7dbf11cfa, + add(add(14817777843080276494683266178512808687156649753153012854386334860566696099579, mulmod(state1, M12, F)), mulmod(state2, M22, F)) + ) + let state0 := mulmod(scratch0, scratch0, F) + scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F) + state0 := mulmod(scratch1, scratch1, F) + scratch1 := mulmod(mulmod(state0, state0, F), scratch1, F) + state0 := mulmod(scratch2, scratch2, F) + scratch2 := mulmod(mulmod(state0, state0, F), scratch2, F) + state0 := add(0x28813dcaebaeaa828a376df87af4a63bc8b7bf27ad49c6298ef7b387bf28526d, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F))) + state1 := add(0x2727673b2ccbc903f181bf38e1c1d40d2033865200c352bc150928adddf9cb78, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F))) + state2 := add(0x234ec45ca27727c2e74abd2b2a1494cd6efbd43e340587d6b8fb9e31e65cc632, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F))) + scratch0 := mulmod(state0, state0, F) + state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F) + scratch0 := mulmod(state1, state1, F) + state1 := mulmod(mulmod(scratch0, scratch0, F), state1, F) + scratch0 := mulmod(state2, state2, F) + state2 := mulmod(mulmod(scratch0, scratch0, F), state2, F) + scratch0 := add(0x15b52534031ae18f7f862cb2cf7cf760ab10a8150a337b1ccd99ff6e8797d428, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F))) + scratch1 := add(0x0dc8fad6d9e4b35f5ed9a3d186b79ce38e0e8a8d1b58b132d701d4eecf68d1f6, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F))) + scratch2 := add(0x1bcd95ffc211fbca600f705fad3fb567ea4eb378f62e1fec97805518a47e4d9c, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F))) + state0 := mulmod(scratch0, scratch0, F) + scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F) + state0 := mulmod(scratch1, scratch1, F) + scratch1 := mulmod(mulmod(state0, state0, F), scratch1, F) + state0 := mulmod(scratch2, scratch2, F) + scratch2 := mulmod(mulmod(state0, state0, F), scratch2, F) + state0 := add(0x10520b0ab721cadfe9eff81b016fc34dc76da36c2578937817cb978d069de559, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F))) + state1 := add(0x1f6d48149b8e7f7d9b257d8ed5fbbaf42932498075fed0ace88a9eb81f5627f6, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F))) + state2 := add(0x1d9655f652309014d29e00ef35a2089bfff8dc1c816f0dc9ca34bdb5460c8705, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F))) + scratch0 := mulmod(state0, state0, F) + state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F) + scratch0 := add(0x04df5a56ff95bcafb051f7b1cd43a99ba731ff67e47032058fe3d4185697cc7d, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F))) + scratch1 := add(0x0672d995f8fff640151b3d290cedaf148690a10a8c8424a7f6ec282b6e4be828, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F))) + scratch2 := add(0x099952b414884454b21200d7ffafdd5f0c9a9dcc06f2708e9fc1d8209b5c75b9, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F))) + state0 := mulmod(scratch0, scratch0, F) + scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F) + state0 := add(0x052cba2255dfd00c7c483143ba8d469448e43586a9b4cd9183fd0e843a6b9fa6, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F))) + state1 := add(0x0b8badee690adb8eb0bd74712b7999af82de55707251ad7716077cb93c464ddc, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F))) + state2 := add(0x119b1590f13307af5a1ee651020c07c749c15d60683a8050b963d0a8e4b2bdd1, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F))) + scratch0 := mulmod(state0, state0, F) + state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F) + scratch0 := add(0x03150b7cd6d5d17b2529d36be0f67b832c4acfc884ef4ee5ce15be0bfb4a8d09, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F))) + scratch1 := add(0x2cc6182c5e14546e3cf1951f173912355374efb83d80898abe69cb317c9ea565, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F))) + scratch2 := add(0x005032551e6378c450cfe129a404b3764218cadedac14e2b92d2cd73111bf0f9, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F))) + state0 := mulmod(scratch0, scratch0, F) + scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F) + state0 := add(0x233237e3289baa34bb147e972ebcb9516469c399fcc069fb88f9da2cc28276b5, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F))) + state1 := add(0x05c8f4f4ebd4a6e3c980d31674bfbe6323037f21b34ae5a4e80c2d4c24d60280, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F))) + state2 := add(0x0a7b1db13042d396ba05d818a319f25252bcf35ef3aeed91ee1f09b2590fc65b, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F))) + scratch0 := mulmod(state0, state0, F) + state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F) + scratch0 := add(0x2a73b71f9b210cf5b14296572c9d32dbf156e2b086ff47dc5df542365a404ec0, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F))) + scratch1 := add(0x1ac9b0417abcc9a1935107e9ffc91dc3ec18f2c4dbe7f22976a760bb5c50c460, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F))) + scratch2 := add(0x12c0339ae08374823fabb076707ef479269f3e4d6cb104349015ee046dc93fc0, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F))) + state0 := mulmod(scratch0, scratch0, F) + scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F) + state0 := add(0x0b7475b102a165ad7f5b18db4e1e704f52900aa3253baac68246682e56e9a28e, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F))) + state1 := add(0x037c2849e191ca3edb1c5e49f6e8b8917c843e379366f2ea32ab3aa88d7f8448, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F))) + state2 := add(0x05a6811f8556f014e92674661e217e9bd5206c5c93a07dc145fdb176a716346f, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F))) + scratch0 := mulmod(state0, state0, F) + state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F) + scratch0 := add(0x29a795e7d98028946e947b75d54e9f044076e87a7b2883b47b675ef5f38bd66e, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F))) + scratch1 := add(0x20439a0c84b322eb45a3857afc18f5826e8c7382c8a1585c507be199981fd22f, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F))) + scratch2 := add(0x2e0ba8d94d9ecf4a94ec2050c7371ff1bb50f27799a84b6d4a2a6f2a0982c887, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F))) + state0 := mulmod(scratch0, scratch0, F) + scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F) + state0 := add(0x143fd115ce08fb27ca38eb7cce822b4517822cd2109048d2e6d0ddcca17d71c8, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F))) + state1 := add(0x0c64cbecb1c734b857968dbbdcf813cdf8611659323dbcbfc84323623be9caf1, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F))) + state2 := add(0x028a305847c683f646fca925c163ff5ae74f348d62c2b670f1426cef9403da53, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F))) + scratch0 := mulmod(state0, state0, F) + state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F) + scratch0 := add(0x2e4ef510ff0b6fda5fa940ab4c4380f26a6bcb64d89427b824d6755b5db9e30c, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F))) + scratch1 := add(0x0081c95bc43384e663d79270c956ce3b8925b4f6d033b078b96384f50579400e, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F))) + scratch2 := add(0x2ed5f0c91cbd9749187e2fade687e05ee2491b349c039a0bba8a9f4023a0bb38, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F))) + state0 := mulmod(scratch0, scratch0, F) + scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F) + state0 := add(0x30509991f88da3504bbf374ed5aae2f03448a22c76234c8c990f01f33a735206, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F))) + state1 := add(0x1c3f20fd55409a53221b7c4d49a356b9f0a1119fb2067b41a7529094424ec6ad, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F))) + state2 := add(0x10b4e7f3ab5df003049514459b6e18eec46bb2213e8e131e170887b47ddcb96c, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F))) + scratch0 := mulmod(state0, state0, F) + state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F) + scratch0 := add(0x2a1982979c3ff7f43ddd543d891c2abddd80f804c077d775039aa3502e43adef, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F))) + scratch1 := add(0x1c74ee64f15e1db6feddbead56d6d55dba431ebc396c9af95cad0f1315bd5c91, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F))) + scratch2 := add(0x07533ec850ba7f98eab9303cace01b4b9e4f2e8b82708cfa9c2fe45a0ae146a0, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F))) + state0 := mulmod(scratch0, scratch0, F) + scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F) + state0 := add(0x21576b438e500449a151e4eeaf17b154285c68f42d42c1808a11abf3764c0750, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F))) + state1 := add(0x2f17c0559b8fe79608ad5ca193d62f10bce8384c815f0906743d6930836d4a9e, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F))) + state2 := add(0x2d477e3862d07708a79e8aae946170bc9775a4201318474ae665b0b1b7e2730e, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F))) + scratch0 := mulmod(state0, state0, F) + state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F) + scratch0 := add(0x162f5243967064c390e095577984f291afba2266c38f5abcd89be0f5b2747eab, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F))) + scratch1 := add(0x2b4cb233ede9ba48264ecd2c8ae50d1ad7a8596a87f29f8a7777a70092393311, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F))) + scratch2 := add(0x2c8fbcb2dd8573dc1dbaf8f4622854776db2eece6d85c4cf4254e7c35e03b07a, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F))) + state0 := mulmod(scratch0, scratch0, F) + scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F) + state0 := add(0x1d6f347725e4816af2ff453f0cd56b199e1b61e9f601e9ade5e88db870949da9, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F))) + state1 := add(0x204b0c397f4ebe71ebc2d8b3df5b913df9e6ac02b68d31324cd49af5c4565529, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F))) + state2 := add(0x0c4cb9dc3c4fd8174f1149b3c63c3c2f9ecb827cd7dc25534ff8fb75bc79c502, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F))) + scratch0 := mulmod(state0, state0, F) + state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F) + scratch0 := add(0x174ad61a1448c899a25416474f4930301e5c49475279e0639a616ddc45bc7b54, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F))) + scratch1 := add(0x1a96177bcf4d8d89f759df4ec2f3cde2eaaa28c177cc0fa13a9816d49a38d2ef, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F))) + scratch2 := add(0x066d04b24331d71cd0ef8054bc60c4ff05202c126a233c1a8242ace360b8a30a, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F))) + state0 := mulmod(scratch0, scratch0, F) + scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F) + state0 := add(0x2a4c4fc6ec0b0cf52195782871c6dd3b381cc65f72e02ad527037a62aa1bd804, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F))) + state1 := add(0x13ab2d136ccf37d447e9f2e14a7cedc95e727f8446f6d9d7e55afc01219fd649, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F))) + state2 := add(0x1121552fca26061619d24d843dc82769c1b04fcec26f55194c2e3e869acc6a9a, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F))) + scratch0 := mulmod(state0, state0, F) + state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F) + scratch0 := add(0x00ef653322b13d6c889bc81715c37d77a6cd267d595c4a8909a5546c7c97cff1, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F))) + scratch1 := add(0x0e25483e45a665208b261d8ba74051e6400c776d652595d9845aca35d8a397d3, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F))) + scratch2 := add(0x29f536dcb9dd7682245264659e15d88e395ac3d4dde92d8c46448db979eeba89, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F))) + state0 := mulmod(scratch0, scratch0, F) + scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F) + state0 := add(0x2a56ef9f2c53febadfda33575dbdbd885a124e2780bbea170e456baace0fa5be, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F))) + state1 := add(0x1c8361c78eb5cf5decfb7a2d17b5c409f2ae2999a46762e8ee416240a8cb9af1, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F))) + state2 := add(0x151aff5f38b20a0fc0473089aaf0206b83e8e68a764507bfd3d0ab4be74319c5, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F))) + scratch0 := mulmod(state0, state0, F) + state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F) + scratch0 := add(0x04c6187e41ed881dc1b239c88f7f9d43a9f52fc8c8b6cdd1e76e47615b51f100, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F))) + scratch1 := add(0x13b37bd80f4d27fb10d84331f6fb6d534b81c61ed15776449e801b7ddc9c2967, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F))) + scratch2 := add(0x01a5c536273c2d9df578bfbd32c17b7a2ce3664c2a52032c9321ceb1c4e8a8e4, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F))) + state0 := mulmod(scratch0, scratch0, F) + scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F) + state0 := add(0x2ab3561834ca73835ad05f5d7acb950b4a9a2c666b9726da832239065b7c3b02, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F))) + state1 := add(0x1d4d8ec291e720db200fe6d686c0d613acaf6af4e95d3bf69f7ed516a597b646, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F))) + state2 := add(0x041294d2cc484d228f5784fe7919fd2bb925351240a04b711514c9c80b65af1d, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F))) + scratch0 := mulmod(state0, state0, F) + state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F) + scratch0 := add(0x154ac98e01708c611c4fa715991f004898f57939d126e392042971dd90e81fc6, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F))) + scratch1 := add(0x0b339d8acca7d4f83eedd84093aef51050b3684c88f8b0b04524563bc6ea4da4, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F))) + scratch2 := add(0x0955e49e6610c94254a4f84cfbab344598f0e71eaff4a7dd81ed95b50839c82e, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F))) + state0 := mulmod(scratch0, scratch0, F) + scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F) + state0 := add(0x06746a6156eba54426b9e22206f15abca9a6f41e6f535c6f3525401ea0654626, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F))) + state1 := add(0x0f18f5a0ecd1423c496f3820c549c27838e5790e2bd0a196ac917c7ff32077fb, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F))) + state2 := add(0x04f6eeca1751f7308ac59eff5beb261e4bb563583ede7bc92a738223d6f76e13, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F))) + scratch0 := mulmod(state0, state0, F) + state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F) + scratch0 := add(0x2b56973364c4c4f5c1a3ec4da3cdce038811eb116fb3e45bc1768d26fc0b3758, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F))) + scratch1 := add(0x123769dd49d5b054dcd76b89804b1bcb8e1392b385716a5d83feb65d437f29ef, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F))) + scratch2 := add(0x2147b424fc48c80a88ee52b91169aacea989f6446471150994257b2fb01c63e9, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F))) + state0 := mulmod(scratch0, scratch0, F) + scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F) + state0 := add(0x0fdc1f58548b85701a6c5505ea332a29647e6f34ad4243c2ea54ad897cebe54d, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F))) + state1 := add(0x12373a8251fea004df68abcf0f7786d4bceff28c5dbbe0c3944f685cc0a0b1f2, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F))) + state2 := add(0x21e4f4ea5f35f85bad7ea52ff742c9e8a642756b6af44203dd8a1f35c1a90035, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F))) + scratch0 := mulmod(state0, state0, F) + state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F) + scratch0 := add(0x16243916d69d2ca3dfb4722224d4c462b57366492f45e90d8a81934f1bc3b147, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F))) + scratch1 := add(0x1efbe46dd7a578b4f66f9adbc88b4378abc21566e1a0453ca13a4159cac04ac2, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F))) + scratch2 := add(0x07ea5e8537cf5dd08886020e23a7f387d468d5525be66f853b672cc96a88969a, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F))) + state0 := mulmod(scratch0, scratch0, F) + scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F) + state0 := add(0x05a8c4f9968b8aa3b7b478a30f9a5b63650f19a75e7ce11ca9fe16c0b76c00bc, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F))) + state1 := add(0x20f057712cc21654fbfe59bd345e8dac3f7818c701b9c7882d9d57b72a32e83f, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F))) + state2 := add(0x04a12ededa9dfd689672f8c67fee31636dcd8e88d01d49019bd90b33eb33db69, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F))) + scratch0 := mulmod(state0, state0, F) + state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F) + scratch0 := add(0x27e88d8c15f37dcee44f1e5425a51decbd136ce5091a6767e49ec9544ccd101a, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F))) + scratch1 := add(0x2feed17b84285ed9b8a5c8c5e95a41f66e096619a7703223176c41ee433de4d1, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F))) + scratch2 := add(0x1ed7cc76edf45c7c404241420f729cf394e5942911312a0d6972b8bd53aff2b8, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F))) + state0 := mulmod(scratch0, scratch0, F) + scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F) + state0 := add(0x15742e99b9bfa323157ff8c586f5660eac6783476144cdcadf2874be45466b1a, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F))) + state1 := add(0x1aac285387f65e82c895fc6887ddf40577107454c6ec0317284f033f27d0c785, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F))) + state2 := add(0x25851c3c845d4790f9ddadbdb6057357832e2e7a49775f71ec75a96554d67c77, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F))) + scratch0 := mulmod(state0, state0, F) + state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F) + scratch0 := add(0x15a5821565cc2ec2ce78457db197edf353b7ebba2c5523370ddccc3d9f146a67, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F))) + scratch1 := add(0x2411d57a4813b9980efa7e31a1db5966dcf64f36044277502f15485f28c71727, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F))) + scratch2 := add(0x002e6f8d6520cd4713e335b8c0b6d2e647e9a98e12f4cd2558828b5ef6cb4c9b, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F))) + state0 := mulmod(scratch0, scratch0, F) + scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F) + state0 := add(0x2ff7bc8f4380cde997da00b616b0fcd1af8f0e91e2fe1ed7398834609e0315d2, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F))) + state1 := add(0x00b9831b948525595ee02724471bcd182e9521f6b7bb68f1e93be4febb0d3cbe, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F))) + state2 := add(0x0a2f53768b8ebf6a86913b0e57c04e011ca408648a4743a87d77adbf0c9c3512, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F))) + scratch0 := mulmod(state0, state0, F) + state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F) + scratch0 := add(0x00248156142fd0373a479f91ff239e960f599ff7e94be69b7f2a290305e1198d, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F))) + scratch1 := add(0x171d5620b87bfb1328cf8c02ab3f0c9a397196aa6a542c2350eb512a2b2bcda9, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F))) + scratch2 := add(0x170a4f55536f7dc970087c7c10d6fad760c952172dd54dd99d1045e4ec34a808, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F))) + state0 := mulmod(scratch0, scratch0, F) + scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F) + state0 := add(0x29aba33f799fe66c2ef3134aea04336ecc37e38c1cd211ba482eca17e2dbfae1, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F))) + state1 := add(0x1e9bc179a4fdd758fdd1bb1945088d47e70d114a03f6a0e8b5ba650369e64973, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F))) + state2 := add(0x1dd269799b660fad58f7f4892dfb0b5afeaad869a9c4b44f9c9e1c43bdaf8f09, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F))) + scratch0 := mulmod(state0, state0, F) + state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F) + scratch0 := add(0x22cdbc8b70117ad1401181d02e15459e7ccd426fe869c7c95d1dd2cb0f24af38, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F))) + scratch1 := add(0x0ef042e454771c533a9f57a55c503fcefd3150f52ed94a7cd5ba93b9c7dacefd, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F))) + scratch2 := add(0x11609e06ad6c8fe2f287f3036037e8851318e8b08a0359a03b304ffca62e8284, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F))) + state0 := mulmod(scratch0, scratch0, F) + scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F) + state0 := add(0x1166d9e554616dba9e753eea427c17b7fecd58c076dfe42708b08f5b783aa9af, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F))) + state1 := add(0x2de52989431a859593413026354413db177fbf4cd2ac0b56f855a888357ee466, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F))) + state2 := add(0x3006eb4ffc7a85819a6da492f3a8ac1df51aee5b17b8e89d74bf01cf5f71e9ad, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F))) + scratch0 := mulmod(state0, state0, F) + state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F) + scratch0 := add(0x2af41fbb61ba8a80fdcf6fff9e3f6f422993fe8f0a4639f962344c8225145086, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F))) + scratch1 := add(0x119e684de476155fe5a6b41a8ebc85db8718ab27889e85e781b214bace4827c3, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F))) + scratch2 := add(0x1835b786e2e8925e188bea59ae363537b51248c23828f047cff784b97b3fd800, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F))) + state0 := mulmod(scratch0, scratch0, F) + scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F) + state0 := add(0x28201a34c594dfa34d794996c6433a20d152bac2a7905c926c40e285ab32eeb6, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F))) + state1 := add(0x083efd7a27d1751094e80fefaf78b000864c82eb571187724a761f88c22cc4e7, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F))) + state2 := add(0x0b6f88a3577199526158e61ceea27be811c16df7774dd8519e079564f61fd13b, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F))) + scratch0 := mulmod(state0, state0, F) + state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F) + scratch0 := add(0x0ec868e6d15e51d9644f66e1d6471a94589511ca00d29e1014390e6ee4254f5b, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F))) + scratch1 := add(0x2af33e3f866771271ac0c9b3ed2e1142ecd3e74b939cd40d00d937ab84c98591, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F))) + scratch2 := add(0x0b520211f904b5e7d09b5d961c6ace7734568c547dd6858b364ce5e47951f178, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F))) + state0 := mulmod(scratch0, scratch0, F) + scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F) + state0 := add(0x0b2d722d0919a1aad8db58f10062a92ea0c56ac4270e822cca228620188a1d40, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F))) + state1 := add(0x1f790d4d7f8cf094d980ceb37c2453e957b54a9991ca38bbe0061d1ed6e562d4, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F))) + state2 := add(0x0171eb95dfbf7d1eaea97cd385f780150885c16235a2a6a8da92ceb01e504233, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F))) + scratch0 := mulmod(state0, state0, F) + state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F) + scratch0 := add(0x0c2d0e3b5fd57549329bf6885da66b9b790b40defd2c8650762305381b168873, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F))) + scratch1 := add(0x1162fb28689c27154e5a8228b4e72b377cbcafa589e283c35d3803054407a18d, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F))) + scratch2 := add(0x2f1459b65dee441b64ad386a91e8310f282c5a92a89e19921623ef8249711bc0, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F))) + state0 := mulmod(scratch0, scratch0, F) + scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F) + state0 := add(0x1e6ff3216b688c3d996d74367d5cd4c1bc489d46754eb712c243f70d1b53cfbb, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F))) + state1 := add(0x01ca8be73832b8d0681487d27d157802d741a6f36cdc2a0576881f9326478875, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F))) + state2 := add(0x1f7735706ffe9fc586f976d5bdf223dc680286080b10cea00b9b5de315f9650e, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F))) + scratch0 := mulmod(state0, state0, F) + state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F) + scratch0 := add(0x2522b60f4ea3307640a0c2dce041fba921ac10a3d5f096ef4745ca838285f019, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F))) + scratch1 := add(0x23f0bee001b1029d5255075ddc957f833418cad4f52b6c3f8ce16c235572575b, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F))) + scratch2 := add(0x2bc1ae8b8ddbb81fcaac2d44555ed5685d142633e9df905f66d9401093082d59, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F))) + state0 := mulmod(scratch0, scratch0, F) + scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F) + state0 := add(0x0f9406b8296564a37304507b8dba3ed162371273a07b1fc98011fcd6ad72205f, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F))) + state1 := add(0x2360a8eb0cc7defa67b72998de90714e17e75b174a52ee4acb126c8cd995f0a8, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F))) + state2 := add(0x15871a5cddead976804c803cbaef255eb4815a5e96df8b006dcbbc2767f88948, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F))) + scratch0 := mulmod(state0, state0, F) + state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F) + scratch0 := add(0x193a56766998ee9e0a8652dd2f3b1da0362f4f54f72379544f957ccdeefb420f, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F))) + scratch1 := add(0x2a394a43934f86982f9be56ff4fab1703b2e63c8ad334834e4309805e777ae0f, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F))) + scratch2 := add(0x1859954cfeb8695f3e8b635dcb345192892cd11223443ba7b4166e8876c0d142, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F))) + state0 := mulmod(scratch0, scratch0, F) + scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F) + state0 := add(0x04e1181763050e58013444dbcb99f1902b11bc25d90bbdca408d3819f4fed32b, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F))) + state1 := add(0x0fdb253dee83869d40c335ea64de8c5bb10eb82db08b5e8b1f5e5552bfd05f23, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F))) + state2 := add(0x058cbe8a9a5027bdaa4efb623adead6275f08686f1c08984a9d7c5bae9b4f1c0, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F))) + scratch0 := mulmod(state0, state0, F) + state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F) + scratch0 := add(0x1382edce9971e186497eadb1aeb1f52b23b4b83bef023ab0d15228b4cceca59a, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F))) + scratch1 := add(0x03464990f045c6ee0819ca51fd11b0be7f61b8eb99f14b77e1e6634601d9e8b5, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F))) + scratch2 := add(0x23f7bfc8720dc296fff33b41f98ff83c6fcab4605db2eb5aaa5bc137aeb70a58, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F))) + state0 := mulmod(scratch0, scratch0, F) + scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F) + state0 := add(0x0a59a158e3eec2117e6e94e7f0e9decf18c3ffd5e1531a9219636158bbaf62f2, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F))) + state1 := add(0x06ec54c80381c052b58bf23b312ffd3ce2c4eba065420af8f4c23ed0075fd07b, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F))) + state2 := add(0x118872dc832e0eb5476b56648e867ec8b09340f7a7bcb1b4962f0ff9ed1f9d01, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F))) + scratch0 := mulmod(state0, state0, F) + state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F) + scratch0 := add(0x13d69fa127d834165ad5c7cba7ad59ed52e0b0f0e42d7fea95e1906b520921b1, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F))) + scratch1 := add(0x169a177f63ea681270b1c6877a73d21bde143942fb71dc55fd8a49f19f10c77b, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F))) + scratch2 := add(0x04ef51591c6ead97ef42f287adce40d93abeb032b922f66ffb7e9a5a7450544d, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F))) + state0 := mulmod(scratch0, scratch0, F) + scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F) + state0 := add(0x256e175a1dc079390ecd7ca703fb2e3b19ec61805d4f03ced5f45ee6dd0f69ec, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F))) + state1 := add(0x30102d28636abd5fe5f2af412ff6004f75cc360d3205dd2da002813d3e2ceeb2, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F))) + state2 := add(0x10998e42dfcd3bbf1c0714bc73eb1bf40443a3fa99bef4a31fd31be182fcc792, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F))) + scratch0 := mulmod(state0, state0, F) + state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F) + scratch0 := add(0x193edd8e9fcf3d7625fa7d24b598a1d89f3362eaf4d582efecad76f879e36860, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F))) + scratch1 := add(0x18168afd34f2d915d0368ce80b7b3347d1c7a561ce611425f2664d7aa51f0b5d, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F))) + scratch2 := add(0x29383c01ebd3b6ab0c017656ebe658b6a328ec77bc33626e29e2e95b33ea6111, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F))) + state0 := mulmod(scratch0, scratch0, F) + scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F) + state0 := add(0x10646d2f2603de39a1f4ae5e7771a64a702db6e86fb76ab600bf573f9010c711, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F))) + state1 := add(0x0beb5e07d1b27145f575f1395a55bf132f90c25b40da7b3864d0242dcb1117fb, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F))) + state2 := add(0x16d685252078c133dc0d3ecad62b5c8830f95bb2e54b59abdffbf018d96fa336, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F))) + scratch0 := mulmod(state0, state0, F) + state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F) + scratch0 := add(0x0a6abd1d833938f33c74154e0404b4b40a555bbbec21ddfafd672dd62047f01a, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F))) + scratch1 := add(0x1a679f5d36eb7b5c8ea12a4c2dedc8feb12dffeec450317270a6f19b34cf1860, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F))) + scratch2 := add(0x0980fb233bd456c23974d50e0ebfde4726a423eada4e8f6ffbc7592e3f1b93d6, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F))) + state0 := mulmod(scratch0, scratch0, F) + scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F) + state0 := add(0x161b42232e61b84cbf1810af93a38fc0cece3d5628c9282003ebacb5c312c72b, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F))) + state1 := add(0x0ada10a90c7f0520950f7d47a60d5e6a493f09787f1564e5d09203db47de1a0b, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F))) + state2 := add(0x1a730d372310ba82320345a29ac4238ed3f07a8a2b4e121bb50ddb9af407f451, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F))) + scratch0 := mulmod(state0, state0, F) + state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F) + scratch0 := add(0x2c8120f268ef054f817064c369dda7ea908377feaba5c4dffbda10ef58e8c556, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F))) + scratch1 := add(0x1c7c8824f758753fa57c00789c684217b930e95313bcb73e6e7b8649a4968f70, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F))) + scratch2 := add(0x2cd9ed31f5f8691c8e39e4077a74faa0f400ad8b491eb3f7b47b27fa3fd1cf77, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F))) + state0 := mulmod(scratch0, scratch0, F) + scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F) + state0 := add(0x23ff4f9d46813457cf60d92f57618399a5e022ac321ca550854ae23918a22eea, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F))) + state1 := add(0x09945a5d147a4f66ceece6405dddd9d0af5a2c5103529407dff1ea58f180426d, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F))) + state2 := add(0x188d9c528025d4c2b67660c6b771b90f7c7da6eaa29d3f268a6dd223ec6fc630, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F))) + scratch0 := mulmod(state0, state0, F) + state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F) + scratch0 := add(0x3050e37996596b7f81f68311431d8734dba7d926d3633595e0c0d8ddf4f0f47f, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F))) + scratch1 := add(0x15af1169396830a91600ca8102c35c426ceae5461e3f95d89d829518d30afd78, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F))) + scratch2 := add(0x1da6d09885432ea9a06d9f37f873d985dae933e351466b2904284da3320d8acc, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F))) + state0 := mulmod(scratch0, scratch0, F) + scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F) + state0 := add(0x2796ea90d269af29f5f8acf33921124e4e4fad3dbe658945e546ee411ddaa9cb, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F))) + state1 := add(0x202d7dd1da0f6b4b0325c8b3307742f01e15612ec8e9304a7cb0319e01d32d60, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F))) + state2 := add(0x096d6790d05bb759156a952ba263d672a2d7f9c788f4c831a29dace4c0f8be5f, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F))) + scratch0 := mulmod(state0, state0, F) + state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F) + scratch0 := add(0x054efa1f65b0fce283808965275d877b438da23ce5b13e1963798cb1447d25a4, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F))) + scratch1 := add(0x1b162f83d917e93edb3308c29802deb9d8aa690113b2e14864ccf6e18e4165f1, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F))) + scratch2 := add(0x21e5241e12564dd6fd9f1cdd2a0de39eedfefc1466cc568ec5ceb745a0506edc, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F))) + state0 := mulmod(scratch0, scratch0, F) + scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F) + state0 := mulmod(scratch1, scratch1, F) + scratch1 := mulmod(mulmod(state0, state0, F), scratch1, F) + state0 := mulmod(scratch2, scratch2, F) + scratch2 := mulmod(mulmod(state0, state0, F), scratch2, F) + state0 := add(0x1cfb5662e8cf5ac9226a80ee17b36abecb73ab5f87e161927b4349e10e4bdf08, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F))) + state1 := add(0x0f21177e302a771bbae6d8d1ecb373b62c99af346220ac0129c53f666eb24100, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F))) + state2 := add(0x1671522374606992affb0dd7f71b12bec4236aede6290546bcef7e1f515c2320, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F))) + scratch0 := mulmod(state0, state0, F) + state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F) + scratch0 := mulmod(state1, state1, F) + state1 := mulmod(mulmod(scratch0, scratch0, F), state1, F) + scratch0 := mulmod(state2, state2, F) + state2 := mulmod(mulmod(scratch0, scratch0, F), state2, F) + scratch0 := add(0x0fa3ec5b9488259c2eb4cf24501bfad9be2ec9e42c5cc8ccd419d2a692cad870, add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F))) + scratch1 := add(0x193c0e04e0bd298357cb266c1506080ed36edce85c648cc085e8c57b1ab54bba, add(add(mulmod(state0, M01, F), mulmod(state1, M11, F)), mulmod(state2, M21, F))) + scratch2 := add(0x102adf8ef74735a27e9128306dcbc3c99f6f7291cd406578ce14ea2adaba68f8, add(add(mulmod(state0, M02, F), mulmod(state1, M12, F)), mulmod(state2, M22, F))) + state0 := mulmod(scratch0, scratch0, F) + scratch0 := mulmod(mulmod(state0, state0, F), scratch0, F) + state0 := mulmod(scratch1, scratch1, F) + scratch1 := mulmod(mulmod(state0, state0, F), scratch1, F) + state0 := mulmod(scratch2, scratch2, F) + scratch2 := mulmod(mulmod(state0, state0, F), scratch2, F) + state0 := add(0x0fe0af7858e49859e2a54d6f1ad945b1316aa24bfbdd23ae40a6d0cb70c3eab1, add(add(mulmod(scratch0, M00, F), mulmod(scratch1, M10, F)), mulmod(scratch2, M20, F))) + state1 := add(0x216f6717bbc7dedb08536a2220843f4e2da5f1daa9ebdefde8a5ea7344798d22, add(add(mulmod(scratch0, M01, F), mulmod(scratch1, M11, F)), mulmod(scratch2, M21, F))) + state2 := add(0x1da55cc900f0d21f4a3e694391918a1b3c23b2ac773c6b3ef88e2e4228325161, add(add(mulmod(scratch0, M02, F), mulmod(scratch1, M12, F)), mulmod(scratch2, M22, F))) + scratch0 := mulmod(state0, state0, F) + state0 := mulmod(mulmod(scratch0, scratch0, F), state0, F) + scratch0 := mulmod(state1, state1, F) + state1 := mulmod(mulmod(scratch0, scratch0, F), state1, F) + scratch0 := mulmod(state2, state2, F) + state2 := mulmod(mulmod(scratch0, scratch0, F), state2, F) + + mstore(0x0, mod(add(add(mulmod(state0, M00, F), mulmod(state1, M10, F)), mulmod(state2, M20, F)), F)) + + return(0, 0x20) + } + } + } +} From 3a43d497f6a995c0cdf75164a561a0ba055fe730 Mon Sep 17 00:00:00 2001 From: Alisander Qoshqosh Date: Wed, 15 Jan 2025 15:12:28 +0400 Subject: [PATCH 61/61] ++ --- lib/crypto/Cargo.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/crypto/Cargo.toml b/lib/crypto/Cargo.toml index 9163c0f06..30950d2b4 100644 --- a/lib/crypto/Cargo.toml +++ b/lib/crypto/Cargo.toml @@ -14,7 +14,9 @@ num-traits.workspace = true zeroize.workspace = true educe.workspace = true hex-literal.workspace = true +# TODO#q: have num-bigint dependency optional num-bigint.workspace = true +# TODO#q: add an optional dependency ruint with conversions [dev-dependencies] rand.workspace = true