From caee77e6296e49da40ccadc5b2e4e438ad86d05b Mon Sep 17 00:00:00 2001 From: Pratyush Mishra Date: Tue, 27 Feb 2024 10:50:17 -0500 Subject: [PATCH 1/4] Fix `MontConfig` --- ff-macros/src/montgomery/mod.rs | 65 ++- ff/src/biginteger/mod.rs | 38 ++ ff/src/const_helpers.rs | 1 + ff/src/fields/models/fp/mod.rs | 4 +- ff/src/fields/models/fp/montgomery/backend.rs | 319 +++++++++++++++ .../mod.rs} | 380 ++---------------- 6 files changed, 459 insertions(+), 348 deletions(-) create mode 100644 ff/src/fields/models/fp/montgomery/backend.rs rename ff/src/fields/models/fp/{montgomery_backend.rs => montgomery/mod.rs} (62%) diff --git a/ff-macros/src/montgomery/mod.rs b/ff-macros/src/montgomery/mod.rs index 7c4cd0b66..392536537 100644 --- a/ff-macros/src/montgomery/mod.rs +++ b/ff-macros/src/montgomery/mod.rs @@ -23,6 +23,7 @@ use sum_of_products::sum_of_products_impl; use crate::utils; pub fn mont_config_helper( + // The modulus p of the field modulus: BigUint, generator: BigUint, small_subgroup_base: Option, @@ -38,12 +39,14 @@ pub fn mont_config_helper( } } - // modulus - 1 = 2^s * t + // Compute trace t and 2-adicity s such that p - 1 = 2^s * t let mut trace = &modulus - BigUint::from_str("1").unwrap(); while !trace.bit(0) { trace >>= 1u8; } + let two_adicity = (&modulus - 1u8).trailing_zeros().expect("two_adicity should fit in u32") as u32; + // Compute 2^s root of unity given the generator let remaining_subgroup_size = match (small_subgroup_base, small_subgroup_power) { (Some(base), Some(power)) => Some(&trace / BigUint::from(base).pow(power)), @@ -54,9 +57,37 @@ pub fn mont_config_helper( let large_subgroup_generator = remaining_subgroup_size .as_ref() .map(|e| generator.modpow(e, &modulus).to_string()); + + // Compute R = 2**(64 * limbs) mod m + let r = (BigUint::one() << (limbs * 64)) % &modulus; + let r_squared = ((&r * &r) % &modulus).to_string(); + let r = r.to_string(); + let modulus_mod_4 = (&modulus % 4u64).try_into().unwrap(); + + let modulus_plus_one_div_four = ((&modulus + 1u8) / 4u8).to_u64_digits(); + let trace_minus_one_div_two = ((&trace - 1u8) / 2u8).to_u64_digits(); + let sqrt_precomp = match modulus_mod_4 { + 3 => quote::quote!(Some(SqrtPrecomputation::Case3Mod4 { + modulus_plus_one_div_four: &[ #( #modulus_plus_one_div_four ),* ] + })), + 1 => quote::quote!(Some(SqrtPrecomputation::TonelliShanks { + two_adicity: Self::TWO_ADICITY, + quadratic_nonresidue_to_trace: Self::TWO_ADIC_ROOT_OF_UNITY, + trace_of_modulus_minus_one_div_two: &[ #( #trace_minus_one_div_two ),* ] + })), + _ => panic!("Modulus must be odd"), + }; + + let modulus_plus_one_div_four = if modulus_mod_4 == 3u8 { + quote::quote! { Some(BigInt([ #( #modulus_plus_one_div_four ),* ])) } + } else { + quote::quote! { None } + }; + let modulus = modulus.to_string(); let generator = generator.to_string(); let two_adic_root_of_unity = two_adic_root_of_unity.to_string(); + let modulus_limbs = utils::str_to_limbs_u64(&modulus).1; let modulus_has_spare_bit = modulus_limbs.last().unwrap() >> 63 == 0; let can_use_no_carry_mul_opt = { @@ -67,6 +98,14 @@ pub fn mont_config_helper( first_limb_check } }; + + let mut inv = 1u64; + for _ in 0..63 { + inv = inv.wrapping_mul(inv); + inv = inv.wrapping_mul(modulus_limbs[0]); + } + inv = inv.wrapping_neg(); + let modulus = quote::quote! { BigInt([ #( #modulus_limbs ),* ]) }; let add_with_carry = add_with_carry_impl(limbs); @@ -113,8 +152,32 @@ pub fn mont_config_helper( const GENERATOR: F = ark_ff::MontFp!(#generator); + const TWO_ADICITY: u32 = #two_adicity; + const TWO_ADIC_ROOT_OF_UNITY: F = ark_ff::MontFp!(#two_adic_root_of_unity); + const R: B = ark_ff::BigInt!(#r); + + const R2: B = ark_ff::BigInt!(#r_squared); + + const INV: u64 = #inv; + + #[doc(hidden)] + const CAN_USE_NO_CARRY_MUL_OPT: bool = #can_use_no_carry_mul_opt; + + #[doc(hidden)] + const CAN_USE_NO_CARRY_SQUARE_OPT: bool = #can_use_no_carry_mul_opt; + + #[doc(hidden)] + const MODULUS_HAS_SPARE_BIT: bool = #modulus_has_spare_bit; + + + /// (MODULUS + 1) / 4 when MODULUS % 4 == 3. Used for square root precomputations. + #[doc(hidden)] + const MODULUS_PLUS_ONE_DIV_FOUR: Option = #modulus_plus_one_div_four; + + const SQRT_PRECOMP: Option> = #sqrt_precomp; + #mixed_radix #[inline(always)] diff --git a/ff/src/biginteger/mod.rs b/ff/src/biginteger/mod.rs index 1a0aa9219..2355712f9 100644 --- a/ff/src/biginteger/mod.rs +++ b/ff/src/biginteger/mod.rs @@ -290,6 +290,44 @@ impl BigInt { crate::const_helpers::R2Buffer::([0u64; N], [0u64; N], 1); const_modulo!(two_pow_n_times_64_square, self) } + + /// Compute `-self^{-1}` mod 2^64. + pub const fn montgomery_inv(&self) -> 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(self.0[0]); + }); + inv.wrapping_neg() + } + + pub const fn msb(&self) -> bool { + self.0[N - 1] >> 63 == 1 + } + + pub const fn plus_one_div_four(&self) -> Option { + if self.mod_4() == 3 { + let (modulus_plus_one, carry) = self.const_add_with_carry(&BigInt::::one()); + let mut result = modulus_plus_one.divide_by_2_round_down(); + // Since modulus_plus_one is even, dividing by 2 results in a MSB of 0. + // Thus we can set MSB to `carry` to get the correct result of (MODULUS + 1) // 2: + result.0[N - 1] |= (carry as u64) << 63; + Some(result.divide_by_2_round_down()) + } else { + None + } + } } impl BigInteger for BigInt { diff --git a/ff/src/const_helpers.rs b/ff/src/const_helpers.rs index 6c7d85893..7796d4a27 100644 --- a/ff/src/const_helpers.rs +++ b/ff/src/const_helpers.rs @@ -4,6 +4,7 @@ use ark_std::ops::{Index, IndexMut}; use crate::BigInt; /// A helper macro for emulating `for` loops in a `const` context. +/// /// # Usage /// ```rust /// # use ark_ff::const_for; diff --git a/ff/src/fields/models/fp/mod.rs b/ff/src/fields/models/fp/mod.rs index 4d7487a16..972e9af92 100644 --- a/ff/src/fields/models/fp/mod.rs +++ b/ff/src/fields/models/fp/mod.rs @@ -15,8 +15,8 @@ use ark_std::{ }; #[macro_use] -mod montgomery_backend; -pub use montgomery_backend::*; +mod montgomery; +pub use montgomery::*; use crate::{ AdditiveGroup, BigInt, BigInteger, FftField, Field, LegendreSymbol, PrimeField, diff --git a/ff/src/fields/models/fp/montgomery/backend.rs b/ff/src/fields/models/fp/montgomery/backend.rs new file mode 100644 index 000000000..cbe145672 --- /dev/null +++ b/ff/src/fields/models/fp/montgomery/backend.rs @@ -0,0 +1,319 @@ +use super::*; +/// Construct a [`Fp, N>`] element from a literal string. This +/// should be used primarily for constructing constant field elements; in a +/// non-const context, [`Fp::from_str`](`ark_std::str::FromStr::from_str`) is +/// preferable. +/// +/// # Panics +/// +/// If the integer represented by the string cannot fit in the number +/// of limbs of the `Fp`, this macro results in a +/// * compile-time error if used in a const context +/// * run-time error otherwise. +/// +/// # Usage +/// +/// ```rust +/// # use ark_test_curves::MontFp; +/// # use ark_test_curves::bls12_381 as ark_bls12_381; +/// # use ark_std::{One, str::FromStr}; +/// use ark_bls12_381::Fq; +/// const ONE: Fq = MontFp!("1"); +/// const NEG_ONE: Fq = MontFp!("-1"); +/// +/// fn check_correctness() { +/// assert_eq!(ONE, Fq::one()); +/// assert_eq!(Fq::from_str("1").unwrap(), ONE); +/// assert_eq!(NEG_ONE, -Fq::one()); +/// } +/// ``` +#[macro_export] +macro_rules! MontFp { + ($c0:expr) => {{ + let (is_positive, limbs) = $crate::ark_ff_macros::to_sign_and_limbs!($c0); + $crate::Fp::from_sign_and_limbs(is_positive, &limbs) + }}; +} + +pub use ark_ff_macros::MontConfig; + +pub use MontFp; + +pub struct MontBackend, const N: usize>(PhantomData); + +impl, const N: usize> FpConfig for MontBackend { + /// The modulus of the field. + const MODULUS: crate::BigInt = T::MODULUS; + + /// A multiplicative generator of the field. + /// `Self::GENERATOR` is an element having multiplicative order + /// `Self::MODULUS - 1`. + const GENERATOR: Fp = T::GENERATOR; + + /// Additive identity of the field, i.e. the element `e` + /// such that, for all elements `f` of the field, `e + f = f`. + const ZERO: Fp = Fp::new_unchecked(BigInt([0u64; N])); + + /// Multiplicative identity of the field, i.e. the element `e` + /// such that, for all elements `f` of the field, `e * f = f`. + const ONE: Fp = Fp::new_unchecked(T::R); + + const TWO_ADICITY: u32 = T::TWO_ADICITY; + const TWO_ADIC_ROOT_OF_UNITY: Fp = T::TWO_ADIC_ROOT_OF_UNITY; + const SMALL_SUBGROUP_BASE: Option = T::SMALL_SUBGROUP_BASE; + const SMALL_SUBGROUP_BASE_ADICITY: Option = T::SMALL_SUBGROUP_BASE_ADICITY; + const LARGE_SUBGROUP_ROOT_OF_UNITY: Option> = T::LARGE_SUBGROUP_ROOT_OF_UNITY; + const SQRT_PRECOMP: Option>> = T::SQRT_PRECOMP; + + fn add_assign(a: &mut Fp, b: &Fp) { + T::add_assign(a, b) + } + + fn sub_assign(a: &mut Fp, b: &Fp) { + T::sub_assign(a, b) + } + + fn double_in_place(a: &mut Fp) { + T::double_in_place(a) + } + + fn neg_in_place(a: &mut Fp) { + T::neg_in_place(a) + } + + /// This modular multiplication algorithm uses Montgomery + /// reduction for efficient implementation. It also additionally + /// uses the "no-carry optimization" outlined + /// [here](https://hackmd.io/@zkteam/modular_multiplication) if + /// `P::MODULUS` has (a) a non-zero MSB, and (b) at least one + /// zero bit in the rest of the modulus. + #[inline] + fn mul_assign(a: &mut Fp, b: &Fp) { + T::mul_assign(a, b) + } + + fn sum_of_products(a: &[Fp; M], b: &[Fp; M]) -> Fp { + T::sum_of_products(a, b) + } + + #[inline] + #[allow(unused_braces, clippy::absurd_extreme_comparisons)] + fn square_in_place(a: &mut Fp) { + T::square_in_place(a) + } + + fn inverse(a: &Fp) -> Option> { + T::inverse(a) + } + + fn from_bigint(r: BigInt) -> Option> { + T::from_bigint(r) + } + + #[inline] + #[allow(clippy::modulo_one)] + fn into_bigint(a: Fp) -> BigInt { + T::into_bigint(a) + } +} + +impl, const N: usize> Fp, N> { + #[doc(hidden)] + pub const R: BigInt = T::R; + #[doc(hidden)] + pub const R2: BigInt = T::R2; + #[doc(hidden)] + pub const INV: u64 = T::INV; + + /// 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.mul(&Fp(T::R2, PhantomData)); + r + } + } + + /// Construct a new field element from its underlying + /// [`struct@BigInt`] data type. + /// + /// Unlike [`Self::new`], this method does not perform Montgomery reduction. + /// Thus, this method should be used only when constructing + /// an element from an integer that has already been put in + /// Montgomery form. + #[inline] + pub const fn new_unchecked(element: BigInt) -> Self { + Self(element, PhantomData) + } + + const fn const_is_zero(&self) -> bool { + self.0.const_is_zero() + } + + #[doc(hidden)] + const fn const_neg(self) -> Self { + if !self.const_is_zero() { + Self::new_unchecked(Self::sub_with_borrow(&T::MODULUS, &self.0)) + } else { + self + } + } + + /// 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() + } + } + + pub(super) 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(T::INV); + let mut carry; + mac!(lo[i], tmp, T::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, T::MODULUS.0[j], &mut carry); + } else { + lo[k] = mac_with_carry!(lo[k], tmp, T::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) + } + + const fn mul(self, other: &Self) -> Self { + let (carry, res) = self.mul_without_cond_subtract(other); + if T::MODULUS_HAS_SPARE_BIT { + res.const_subtract_modulus() + } else { + res.const_subtract_modulus_with_carry(carry) + } + } + + const fn const_is_valid(&self) -> bool { + crate::const_for!((i in 0..N) { + if (self.0).0[N - i - 1] < T::MODULUS.0[N - i - 1] { + return true + } else if (self.0).0[N - i - 1] > T::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, &T::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, &T::MODULUS); + } + self + } + + const fn sub_with_borrow(a: &BigInt, b: &BigInt) -> BigInt { + a.const_sub_with_borrow(b).0 + } + + pub const fn sqrt_precomputation() -> Option> { + match T::MODULUS.mod_4() { + 3 => match T::MODULUS_PLUS_ONE_DIV_FOUR.as_ref() { + Some(m) => Some(SqrtPrecomputation::Case3Mod4 { + modulus_plus_one_div_four: &m.0, + }), + None => None, + }, + _ => Some(SqrtPrecomputation::TonelliShanks { + two_adicity: >::TWO_ADICITY, + quadratic_nonresidue_to_trace: T::TWO_ADIC_ROOT_OF_UNITY, + trace_of_modulus_minus_one_div_two: + &, N>>::TRACE_MINUS_ONE_DIV_TWO.0, + }), + } + } +} + +#[cfg(test)] +mod test { + use ark_std::{str::FromStr, vec::Vec}; + use ark_test_curves::secp256k1::Fr; + use num_bigint::{BigInt, BigUint, Sign}; + + #[test] + fn test_mont_macro_correctness() { + let (is_positive, limbs) = str_to_limbs_u64( + "111192936301596926984056301862066282284536849596023571352007112326586892541694", + ); + let t = Fr::from_sign_and_limbs(is_positive, &limbs); + + let result: BigUint = t.into(); + let expected = BigUint::from_str( + "111192936301596926984056301862066282284536849596023571352007112326586892541694", + ) + .unwrap(); + + assert_eq!(result, expected); + } + + fn str_to_limbs_u64(num: &str) -> (bool, Vec) { + let (sign, digits) = BigInt::from_str(num) + .expect("could not parse to bigint") + .to_radix_le(16); + let limbs = digits + .chunks(16) + .map(|chunk| { + let mut this = 0u64; + for (i, hexit) in chunk.iter().enumerate() { + this += (*hexit as u64) << (4 * i); + } + this + }) + .collect::>(); + + let sign_is_positive = sign != Sign::Minus; + (sign_is_positive, limbs) + } +} diff --git a/ff/src/fields/models/fp/montgomery_backend.rs b/ff/src/fields/models/fp/montgomery/mod.rs similarity index 62% rename from ff/src/fields/models/fp/montgomery_backend.rs rename to ff/src/fields/models/fp/montgomery/mod.rs index 3ea75b884..5a0e41bb8 100644 --- a/ff/src/fields/models/fp/montgomery_backend.rs +++ b/ff/src/fields/models/fp/montgomery/mod.rs @@ -4,6 +4,9 @@ use super::{Fp, FpConfig}; use crate::{biginteger::arithmetic as fa, BigInt, BigInteger, PrimeField, SqrtPrecomputation}; use ark_ff_macros::unroll_for_loops; +mod backend; +pub use backend::*; + /// A trait that specifies the constants and arithmetic procedures /// for Montgomery arithmetic over the prime field defined by `MODULUS`. /// @@ -17,13 +20,22 @@ pub trait MontConfig: 'static + Sync + Send + Sized { /// Let `M` be the power of 2^64 nearest to `Self::MODULUS_BITS`. Then /// `R = M % Self::MODULUS`. - const R: BigInt = Self::MODULUS.montgomery_r(); + /// + /// Can be computed if the `BigInt` type has a `montgomery_r` method. + /// For example, via `Self::MODULUS.montgomery_r()` + const R: BigInt; /// R2 = R^2 % Self::MODULUS - const R2: BigInt = Self::MODULUS.montgomery_r2(); + /// + /// Can be computed if the `BigInt` type has a `montgomery_r2` method. + /// For example, via `Self::MODULUS.montgomery_r2()` + const R2: BigInt; /// INV = -MODULUS^{-1} mod 2^64 - const INV: u64 = inv::(); + /// + /// Can be computed if the `BigInt` type has a suitable `montgomery_inv` method. + /// For example, via `Self::MODULUS.montgomery_inv()` + const INV: u64; /// A multiplicative generator of the field. /// `Self::GENERATOR` is an element having multiplicative order @@ -36,8 +48,10 @@ pub trait MontConfig: 'static + Sync + Send + Sized { /// This optimization applies if /// (a) `Self::MODULUS[N-1] < u64::MAX >> 1`, and /// (b) the bits of the modulus are not all 1. + /// + /// Can be computed automatically via the `can_use_no_carry_mul_optimization` method. #[doc(hidden)] - const CAN_USE_NO_CARRY_MUL_OPT: bool = can_use_no_carry_mul_optimization::(); + const CAN_USE_NO_CARRY_MUL_OPT: bool; /// Can we use the no-carry optimization for squaring /// outlined [here](https://hackmd.io/@gnark/modular_multiplication)? @@ -45,15 +59,25 @@ pub trait MontConfig: 'static + Sync + Send + Sized { /// This optimization applies if /// (a) `Self::MODULUS[N-1] < u64::MAX >> 2`, and /// (b) the bits of the modulus are not all 1. + /// + /// Can be computed automatically via the `can_use_no_carry_mul_optimization` method. #[doc(hidden)] - const CAN_USE_NO_CARRY_SQUARE_OPT: bool = can_use_no_carry_mul_optimization::(); + const CAN_USE_NO_CARRY_SQUARE_OPT: bool; - /// Does the modulus have a spare unused bit + /// Does the modulus have a spare unused bit (i.e., the MSB is 0) /// /// This condition applies if /// (a) `Self::MODULUS[N-1] >> 63 == 0` + /// + /// Can be computed if the `BigInt` type has a `msb()` method as `Self::MODULUS.msb() == 0` #[doc(hidden)] - const MODULUS_HAS_SPARE_BIT: bool = modulus_has_spare_bit::(); + const MODULUS_HAS_SPARE_BIT: bool; + + /// The 2-adicity of the modulus. + /// This is the largest integer `s` such that `2^s` divides `MODULUS - 1`. + /// + /// Can be computed if the `BigInt` type has a `two_adic_valuation()` method. + const TWO_ADICITY: u32; /// 2^s root of unity computed by GENERATOR^t const TWO_ADIC_ROOT_OF_UNITY: Fp, N>; @@ -73,25 +97,13 @@ pub trait MontConfig: 'static + Sync + Send + Sized { /// Precomputed material for use when computing square roots. /// The default is to use the standard Tonelli-Shanks algorithm. - const SQRT_PRECOMP: Option, N>>> = - sqrt_precomputation::(); + const SQRT_PRECOMP: Option, N>>>; /// (MODULUS + 1) / 4 when MODULUS % 4 == 3. Used for square root precomputations. + /// + /// Can be computed if the `BigInt` type has a `plus_one_div_four` method. #[doc(hidden)] - const MODULUS_PLUS_ONE_DIV_FOUR: Option> = { - match Self::MODULUS.mod_4() == 3 { - true => { - let (modulus_plus_one, carry) = - Self::MODULUS.const_add_with_carry(&BigInt::::one()); - let mut result = modulus_plus_one.divide_by_2_round_down(); - // Since modulus_plus_one is even, dividing by 2 results in a MSB of 0. - // Thus we can set MSB to `carry` to get the correct result of (MODULUS + 1) // 2: - result.0[N - 1] |= (carry as u64) << 63; - Some(result.divide_by_2_round_down()) - }, - false => None, - } - }; + const MODULUS_PLUS_ONE_DIV_FOUR: Option>; /// Sets `a = a + b`. #[inline(always)] @@ -493,26 +505,6 @@ pub trait MontConfig: 'static + Sync + Send + Sized { } } -/// 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() -} #[inline] pub const fn can_use_no_carry_mul_optimization, const N: usize>() -> bool { @@ -558,305 +550,3 @@ pub const fn sqrt_precomputation>( }), } } - -/// Construct a [`Fp, N>`] element from a literal string. This -/// should be used primarily for constructing constant field elements; in a -/// non-const context, [`Fp::from_str`](`ark_std::str::FromStr::from_str`) is -/// preferable. -/// -/// # Panics -/// -/// If the integer represented by the string cannot fit in the number -/// of limbs of the `Fp`, this macro results in a -/// * compile-time error if used in a const context -/// * run-time error otherwise. -/// -/// # Usage -/// -/// ```rust -/// # use ark_test_curves::MontFp; -/// # use ark_test_curves::bls12_381 as ark_bls12_381; -/// # use ark_std::{One, str::FromStr}; -/// use ark_bls12_381::Fq; -/// const ONE: Fq = MontFp!("1"); -/// const NEG_ONE: Fq = MontFp!("-1"); -/// -/// fn check_correctness() { -/// assert_eq!(ONE, Fq::one()); -/// assert_eq!(Fq::from_str("1").unwrap(), ONE); -/// assert_eq!(NEG_ONE, -Fq::one()); -/// } -/// ``` -#[macro_export] -macro_rules! MontFp { - ($c0:expr) => {{ - let (is_positive, limbs) = $crate::ark_ff_macros::to_sign_and_limbs!($c0); - $crate::Fp::from_sign_and_limbs(is_positive, &limbs) - }}; -} - -pub use ark_ff_macros::MontConfig; - -pub use MontFp; - -pub struct MontBackend, const N: usize>(PhantomData); - -impl, const N: usize> FpConfig for MontBackend { - /// The modulus of the field. - const MODULUS: crate::BigInt = T::MODULUS; - - /// A multiplicative generator of the field. - /// `Self::GENERATOR` is an element having multiplicative order - /// `Self::MODULUS - 1`. - const GENERATOR: Fp = T::GENERATOR; - - /// Additive identity of the field, i.e. the element `e` - /// such that, for all elements `f` of the field, `e + f = f`. - const ZERO: Fp = Fp::new_unchecked(BigInt([0u64; N])); - - /// Multiplicative identity of the field, i.e. the element `e` - /// such that, for all elements `f` of the field, `e * f = f`. - const ONE: Fp = Fp::new_unchecked(T::R); - - const TWO_ADICITY: u32 = Self::MODULUS.two_adic_valuation(); - const TWO_ADIC_ROOT_OF_UNITY: Fp = T::TWO_ADIC_ROOT_OF_UNITY; - const SMALL_SUBGROUP_BASE: Option = T::SMALL_SUBGROUP_BASE; - const SMALL_SUBGROUP_BASE_ADICITY: Option = T::SMALL_SUBGROUP_BASE_ADICITY; - const LARGE_SUBGROUP_ROOT_OF_UNITY: Option> = T::LARGE_SUBGROUP_ROOT_OF_UNITY; - const SQRT_PRECOMP: Option>> = T::SQRT_PRECOMP; - - fn add_assign(a: &mut Fp, b: &Fp) { - T::add_assign(a, b) - } - - fn sub_assign(a: &mut Fp, b: &Fp) { - T::sub_assign(a, b) - } - - fn double_in_place(a: &mut Fp) { - T::double_in_place(a) - } - - fn neg_in_place(a: &mut Fp) { - T::neg_in_place(a) - } - - /// This modular multiplication algorithm uses Montgomery - /// reduction for efficient implementation. It also additionally - /// uses the "no-carry optimization" outlined - /// [here](https://hackmd.io/@zkteam/modular_multiplication) if - /// `P::MODULUS` has (a) a non-zero MSB, and (b) at least one - /// zero bit in the rest of the modulus. - #[inline] - fn mul_assign(a: &mut Fp, b: &Fp) { - T::mul_assign(a, b) - } - - fn sum_of_products(a: &[Fp; M], b: &[Fp; M]) -> Fp { - T::sum_of_products(a, b) - } - - #[inline] - #[allow(unused_braces, clippy::absurd_extreme_comparisons)] - fn square_in_place(a: &mut Fp) { - T::square_in_place(a) - } - - fn inverse(a: &Fp) -> Option> { - T::inverse(a) - } - - fn from_bigint(r: BigInt) -> Option> { - T::from_bigint(r) - } - - #[inline] - #[allow(clippy::modulo_one)] - fn into_bigint(a: Fp) -> BigInt { - T::into_bigint(a) - } -} - -impl, const N: usize> Fp, N> { - #[doc(hidden)] - pub const R: BigInt = T::R; - #[doc(hidden)] - pub const R2: BigInt = T::R2; - #[doc(hidden)] - pub const INV: u64 = T::INV; - - /// 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.mul(&Fp(T::R2, PhantomData)); - r - } - } - - /// Construct a new field element from its underlying - /// [`struct@BigInt`] data type. - /// - /// Unlike [`Self::new`], this method does not perform Montgomery reduction. - /// Thus, this method should be used only when constructing - /// an element from an integer that has already been put in - /// Montgomery form. - #[inline] - pub const fn new_unchecked(element: BigInt) -> Self { - Self(element, PhantomData) - } - - const fn const_is_zero(&self) -> bool { - self.0.const_is_zero() - } - - #[doc(hidden)] - const fn const_neg(self) -> Self { - if !self.const_is_zero() { - Self::new_unchecked(Self::sub_with_borrow(&T::MODULUS, &self.0)) - } else { - self - } - } - - /// 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() - } - } - - 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(T::INV); - let mut carry; - mac!(lo[i], tmp, T::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, T::MODULUS.0[j], &mut carry); - } else { - lo[k] = mac_with_carry!(lo[k], tmp, T::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) - } - - const fn mul(self, other: &Self) -> Self { - let (carry, res) = self.mul_without_cond_subtract(other); - if T::MODULUS_HAS_SPARE_BIT { - res.const_subtract_modulus() - } else { - res.const_subtract_modulus_with_carry(carry) - } - } - - const fn const_is_valid(&self) -> bool { - crate::const_for!((i in 0..N) { - if (self.0).0[N - i - 1] < T::MODULUS.0[N - i - 1] { - return true - } else if (self.0).0[N - i - 1] > T::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, &T::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, &T::MODULUS); - } - self - } - - const fn sub_with_borrow(a: &BigInt, b: &BigInt) -> BigInt { - a.const_sub_with_borrow(b).0 - } -} - -#[cfg(test)] -mod test { - use ark_std::{str::FromStr, vec::Vec}; - use ark_test_curves::secp256k1::Fr; - use num_bigint::{BigInt, BigUint, Sign}; - - #[test] - fn test_mont_macro_correctness() { - let (is_positive, limbs) = str_to_limbs_u64( - "111192936301596926984056301862066282284536849596023571352007112326586892541694", - ); - let t = Fr::from_sign_and_limbs(is_positive, &limbs); - - let result: BigUint = t.into(); - let expected = BigUint::from_str( - "111192936301596926984056301862066282284536849596023571352007112326586892541694", - ) - .unwrap(); - - assert_eq!(result, expected); - } - - fn str_to_limbs_u64(num: &str) -> (bool, Vec) { - let (sign, digits) = BigInt::from_str(num) - .expect("could not parse to bigint") - .to_radix_le(16); - let limbs = digits - .chunks(16) - .map(|chunk| { - let mut this = 0u64; - for (i, hexit) in chunk.iter().enumerate() { - this += (*hexit as u64) << (4 * i); - } - this - }) - .collect::>(); - - let sign_is_positive = sign != Sign::Minus; - (sign_is_positive, limbs) - } -} From a3dafc7aa0dded155e24673a14f637937a668764 Mon Sep 17 00:00:00 2001 From: Pratyush Mishra Date: Tue, 27 Feb 2024 19:12:10 -0500 Subject: [PATCH 2/4] fmt --- ff-macros/src/montgomery/mod.rs | 6 ++++-- ff/src/const_helpers.rs | 2 +- ff/src/fields/models/fp/montgomery/backend.rs | 2 +- ff/src/fields/models/fp/montgomery/mod.rs | 15 +++++++-------- 4 files changed, 13 insertions(+), 12 deletions(-) diff --git a/ff-macros/src/montgomery/mod.rs b/ff-macros/src/montgomery/mod.rs index 36b4ce90d..a943f4818 100644 --- a/ff-macros/src/montgomery/mod.rs +++ b/ff-macros/src/montgomery/mod.rs @@ -44,7 +44,9 @@ pub fn mont_config_helper( trace >>= 1u8; } - let two_adicity = (&modulus - 1u8).trailing_zeros().expect("two_adicity should fit in u32") as u32; + let two_adicity = (&modulus - 1u8) + .trailing_zeros() + .expect("two_adicity should fit in u32") as u32; // Compute 2^s root of unity given the generator let remaining_subgroup_size = match (small_subgroup_base, small_subgroup_power) { @@ -172,7 +174,7 @@ pub fn mont_config_helper( /// (MODULUS + 1) / 4 when MODULUS % 4 == 3. Used for square root precomputations. #[doc(hidden)] - const MODULUS_PLUS_ONE_DIV_FOUR: Option = #modulus_plus_one_div_four; + const MODULUS_PLUS_ONE_DIV_FOUR: Option = #modulus_plus_one_div_four; const SQRT_PRECOMP: Option> = #sqrt_precomp; diff --git a/ff/src/const_helpers.rs b/ff/src/const_helpers.rs index 7796d4a27..7324f6ab3 100644 --- a/ff/src/const_helpers.rs +++ b/ff/src/const_helpers.rs @@ -4,7 +4,7 @@ use ark_std::ops::{Index, IndexMut}; use crate::BigInt; /// A helper macro for emulating `for` loops in a `const` context. -/// +/// /// # Usage /// ```rust /// # use ark_ff::const_for; diff --git a/ff/src/fields/models/fp/montgomery/backend.rs b/ff/src/fields/models/fp/montgomery/backend.rs index f55184700..82df01ea5 100644 --- a/ff/src/fields/models/fp/montgomery/backend.rs +++ b/ff/src/fields/models/fp/montgomery/backend.rs @@ -278,7 +278,7 @@ impl, const N: usize> Fp, N> { #[cfg(test)] mod test { -use ark_std::{str::FromStr, vec::*}; + use ark_std::{str::FromStr, vec::*}; use ark_test_curves::secp256k1::Fr; use num_bigint::{BigInt, BigUint, Sign}; diff --git a/ff/src/fields/models/fp/montgomery/mod.rs b/ff/src/fields/models/fp/montgomery/mod.rs index d8e808fe0..066c074b4 100644 --- a/ff/src/fields/models/fp/montgomery/mod.rs +++ b/ff/src/fields/models/fp/montgomery/mod.rs @@ -21,7 +21,7 @@ pub trait MontConfig: 'static + Sync + Send + Sized { /// Let `M` be the power of 2^64 nearest to `Self::MODULUS_BITS`. Then /// `R = M % Self::MODULUS`. - /// + /// /// Can be computed if the `BigInt` type has a `montgomery_r` method. /// For example, via `Self::MODULUS.montgomery_r()` const R: BigInt; @@ -33,7 +33,7 @@ pub trait MontConfig: 'static + Sync + Send + Sized { const R2: BigInt; /// INV = -MODULUS^{-1} mod 2^64 - /// + /// /// Can be computed if the `BigInt` type has a suitable `montgomery_inv` method. /// For example, via `Self::MODULUS.montgomery_inv()` const INV: u64; @@ -49,7 +49,7 @@ pub trait MontConfig: 'static + Sync + Send + Sized { /// This optimization applies if /// (a) `Self::MODULUS[N-1] < u64::MAX >> 1`, and /// (b) the bits of the modulus are not all 1. - /// + /// /// Can be computed automatically via the `can_use_no_carry_mul_optimization` method. #[doc(hidden)] const CAN_USE_NO_CARRY_MUL_OPT: bool; @@ -60,7 +60,7 @@ pub trait MontConfig: 'static + Sync + Send + Sized { /// This optimization applies if /// (a) `Self::MODULUS[N-1] < u64::MAX >> 2`, and /// (b) the bits of the modulus are not all 1. - /// + /// /// Can be computed automatically via the `can_use_no_carry_mul_optimization` method. #[doc(hidden)] const CAN_USE_NO_CARRY_SQUARE_OPT: bool; @@ -69,14 +69,14 @@ pub trait MontConfig: 'static + Sync + Send + Sized { /// /// This condition applies if /// (a) `Self::MODULUS[N-1] >> 63 == 0` - /// + /// /// Can be computed if the `BigInt` type has a `msb()` method as `Self::MODULUS.msb() == 0` #[doc(hidden)] const MODULUS_HAS_SPARE_BIT: bool; /// The 2-adicity of the modulus. /// This is the largest integer `s` such that `2^s` divides `MODULUS - 1`. - /// + /// /// Can be computed if the `BigInt` type has a `two_adic_valuation()` method. const TWO_ADICITY: u32; @@ -101,7 +101,7 @@ pub trait MontConfig: 'static + Sync + Send + Sized { const SQRT_PRECOMP: Option, N>>>; /// (MODULUS + 1) / 4 when MODULUS % 4 == 3. Used for square root precomputations. - /// + /// /// Can be computed if the `BigInt` type has a `plus_one_div_four` method. #[doc(hidden)] const MODULUS_PLUS_ONE_DIV_FOUR: Option>; @@ -505,7 +505,6 @@ pub trait MontConfig: 'static + Sync + Send + Sized { } } - #[inline] pub const fn can_use_no_carry_mul_optimization, const N: usize>() -> bool { // Checking the modulus at compile time From 22b03edb924ebe7dbac61fcb6c4d560eabf74ddc Mon Sep 17 00:00:00 2001 From: Pratyush Mishra Date: Tue, 27 Feb 2024 19:19:34 -0500 Subject: [PATCH 3/4] update --- ff/src/fields/models/fp/montgomery/backend.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ff/src/fields/models/fp/montgomery/backend.rs b/ff/src/fields/models/fp/montgomery/backend.rs index 82df01ea5..f890a390c 100644 --- a/ff/src/fields/models/fp/montgomery/backend.rs +++ b/ff/src/fields/models/fp/montgomery/backend.rs @@ -267,10 +267,10 @@ impl, const N: usize> Fp, N> { None => None, }, _ => Some(SqrtPrecomputation::TonelliShanks { - two_adicity: >::TWO_ADICITY, + two_adicity: T::TWO_ADICITY, quadratic_nonresidue_to_trace: T::TWO_ADIC_ROOT_OF_UNITY, trace_of_modulus_minus_one_div_two: - &, N>>::TRACE_MINUS_ONE_DIV_TWO.0, + &Self::TRACE_MINUS_ONE_DIV_TWO.0, }), } } From eca38153a9b3135f03972e9c5c612082627582e6 Mon Sep 17 00:00:00 2001 From: Pratyush Mishra Date: Tue, 27 Feb 2024 19:19:46 -0500 Subject: [PATCH 4/4] fmt --- ff/src/fields/models/fp/montgomery/backend.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ff/src/fields/models/fp/montgomery/backend.rs b/ff/src/fields/models/fp/montgomery/backend.rs index f890a390c..c2fae9594 100644 --- a/ff/src/fields/models/fp/montgomery/backend.rs +++ b/ff/src/fields/models/fp/montgomery/backend.rs @@ -269,8 +269,7 @@ impl, const N: usize> Fp, N> { _ => Some(SqrtPrecomputation::TonelliShanks { two_adicity: T::TWO_ADICITY, quadratic_nonresidue_to_trace: T::TWO_ADIC_ROOT_OF_UNITY, - trace_of_modulus_minus_one_div_two: - &Self::TRACE_MINUS_ONE_DIV_TWO.0, + trace_of_modulus_minus_one_div_two: &Self::TRACE_MINUS_ONE_DIV_TWO.0, }), } }