diff --git a/crates/primitives/src/algebra/group/mod.rs b/crates/primitives/src/algebra/group/mod.rs index c37b3799b..e116799e3 100644 --- a/crates/primitives/src/algebra/group/mod.rs +++ b/crates/primitives/src/algebra/group/mod.rs @@ -5,16 +5,11 @@ use ark_ec::{ AffineRepr, CurveGroup, PrimeGroup, short_weierstrass::{Projective, SWCurveConfig}, }; -use ark_ff::{AdditiveGroup, Field, One, PrimeField, Zero}; +use ark_ff::{Field, One, PrimeField, Zero}; use ark_r1cs_std::{ - GR1CSVar, - boolean::Boolean, convert::ToConstraintFieldGadget, - fields::{FieldVar, fp::FpVar}, - groups::{ - CurveVar, - curves::short_weierstrass::{ProjectiveVar, non_zero_affine::NonZeroAffineVar}, - }, + fields::fp::FpVar, + groups::{CurveVar, curves::short_weierstrass::ProjectiveVar}, }; use ark_relations::gr1cs::SynthesisError; @@ -40,10 +35,7 @@ pub trait SonobeCurve: + Inputize + InputizeEmulated + Val< - Var: CurveVar - + AbsorbableVar - + WitnessToPublic - + JointScalarMul, + Var: CurveVar + AbsorbableVar + WitnessToPublic, EmulatedVar = EmulatedAffineVar, > { @@ -123,141 +115,3 @@ impl> WitnessToPublic self.to_constraint_field()?[..2].mark_as_public() } } - -/// [`JointScalarMul`] extends arkworks' [`CurveVar`] for more efficient MSM -/// operations in-circuit, without relying on a fork. -pub trait JointScalarMul: Sized { - /// [`JointScalarMul::joint_scalar_mul_be`] computes `s1 * self + s2 * p`, - /// where `s1` and `s2` are the scalars represented in big-endian `Boolean`. - /// - /// `self` and `p` are non-zero and `self` ≠ `-p`. - fn joint_scalar_mul_be<'a>( - &self, - p: &Self, - s1: impl Iterator>, - s2: impl Iterator>, - ) -> Result; -} - -impl> JointScalarMul - for ProjectiveVar> -{ - fn joint_scalar_mul_be<'a>( - &self, - p: &Self, - bits1: impl Iterator::BasePrimeField>>, - bits2: impl Iterator::BasePrimeField>>, - ) -> Result { - // prepare bits decomposition - let mut bits1 = bits1.collect::>(); - if bits1.is_empty() { - return Ok(Self::zero()); - } - // Remove unnecessary constant zeros in the most-significant positions. - bits1 = bits1 - .into_iter() - // We iterate from the MSB down. - .rev() - // Skip leading zeros, if they are constants. - .skip_while(|b| b.is_constant() && !b.value().unwrap()) - .collect(); - - let mut bits2 = bits2.collect::>(); - if bits2.is_empty() { - return Ok(Self::zero()); - } - // Remove unnecessary constant zeros in the most-significant positions. - bits2 = bits2 - .into_iter() - // We iterate from the MSB down. - .rev() - // Skip leading zeros, if they are constants. - .skip_while(|b| b.is_constant() && !b.value().unwrap()) - .collect(); - - // precompute points - let aff1 = self.to_affine()?; - let nz_aff1 = NonZeroAffineVar::::new(aff1.x, aff1.y); - - let aff2 = p.to_affine()?; - let nz_aff2 = NonZeroAffineVar::new(aff2.x, aff2.y); - - let mut aff1_neg = NonZeroAffineVar::new(nz_aff1.x.clone(), nz_aff1.y.negate()?); - let mut aff2_neg = NonZeroAffineVar::new(nz_aff2.x.clone(), nz_aff2.y.negate()?); - let acc = nz_aff1.double()?; - - let sum = nz_aff1.add_unchecked(&nz_aff2)?; - let diff = nz_aff1.add_unchecked(&aff2_neg)?; - let NonZeroAffineVar { mut x, mut y, .. } = acc; - - // double-and-add loop - for (bit1, bit2) in (bits1.iter().rev().skip(1).rev()).zip(bits2.iter().rev().skip(1).rev()) - { - let xor = *bit1 ^ *bit2; - let xx = xor.select(&diff.x, &sum.x)?; - let yy = xor.select(&diff.y, &sum.y)?; - let yy = bit1.select(&yy, &yy.negate()?)?; - - if [&x, &y].is_constant() || ([&xx, &yy].is_constant()) { - let p = NonZeroAffineVar::::new(x.clone(), y.clone()) - .double()? - .add_unchecked(&NonZeroAffineVar::new(xx, yy))?; - x = p.x; - y = p.y; - } else { - let lambda_1 = (&yy - &y).mul_by_inverse_unchecked(&(&xx - &x))?; - let lambda_1_square = lambda_1.square()?; - - let lambda_2 = y - .mul_by_inverse_unchecked(&(&x.double()? + &xx - &lambda_1_square))? - .double()? - - lambda_1; - - let x4 = lambda_2.square()? - lambda_1_square + &xx; - let y4 = lambda_2 * &(&x - &x4) - &y; - x = x4; - y = y4; - }; - } - - let mut acc = NonZeroAffineVar::new(x, y); - // last bit - aff1_neg = aff1_neg.add_unchecked(&acc)?; - acc = bits1[bits1.len() - 1].select(&acc, &aff1_neg)?; - aff2_neg = aff2_neg.add_unchecked(&acc)?; - acc = bits2[bits1.len() - 1].select(&acc, &aff2_neg)?; - - let acc = acc.into_projective(); - let mut p = diff; - for _ in 0..bits1.len() - 1 { - p = p.double()?; - } - - // [`ProjectiveVar::add_mixed`] - let (x1, y1, z1) = (&acc.x, &acc.y, &acc.z); - let (x2, y2) = (&p.x, &p.y.negate()?); - let three_b = P::COEFF_B.double() + P::COEFF_B; - - let xx = x1 * x2; // 1 - let yy = y1 * y2; // 2 - let xy_pairs = (x1 + y1) * (x2 + y2) - (&xx + &yy); // 4, 5, 6, 7, 8 - let xz_pairs = x2 * z1 + x1; // 8, 9 - let yz_pairs = y2 * z1 + y1; // 10, 11 - - let bz3_part = &xz_pairs * P::COEFF_A + z1 * three_b; // 12, 13, 14 - - let yy_m_bz3 = &yy - &bz3_part; // 15 - let yy_p_bz3 = &yy + &bz3_part; // 16 - - let azz = z1 * P::COEFF_A; // 20 - let xx3_p_azz = xx.double()? + &xx + &azz; // 18, 19, 22 - - let b3_xz_pairs = (&xx - &azz) * P::COEFF_A + &xz_pairs * three_b; // 21, 23, 24, 25 - - Ok(ProjectiveVar::new( - &yy_m_bz3 * &xy_pairs - &yz_pairs * &b3_xz_pairs, // 28, 29, 30 - &yy_p_bz3 * &yy_m_bz3 + &xx3_p_azz * b3_xz_pairs, // 17, 26, 27 - &yy_p_bz3 * &yz_pairs + xy_pairs * xx3_p_azz, // 31, 32, 33 - )) - } -} diff --git a/crates/primitives/src/commitments/pedersen.rs b/crates/primitives/src/commitments/pedersen.rs index 80ce66d59..6048eda67 100644 --- a/crates/primitives/src/commitments/pedersen.rs +++ b/crates/primitives/src/commitments/pedersen.rs @@ -20,10 +20,7 @@ use ark_std::{UniformRand, borrow::Borrow, iter::repeat_with, marker::PhantomDat use super::{CommitmentDef, CommitmentDefGadget, CommitmentKey, CommitmentOps, Error}; use crate::{ - algebra::{ - field::emulated::EmulatedFieldVar, - group::{JointScalarMul, emulated::EmulatedAffineVar}, - }, + algebra::{field::emulated::EmulatedFieldVar, group::emulated::EmulatedAffineVar}, commitments::{CommitmentOpsGadget, GroupBasedCommitment}, traits::{CF1, CF2, SonobeCurve}, utils::null::Null, @@ -225,22 +222,8 @@ impl PedersenGadget { /// with the given generators `g` and scalar bits `v`. fn msm(g: &[C::Var], v: &[Vec>>]) -> Result { let mut res = C::Var::zero(); - let n = v.len(); - if n % 2 == 1 { - res += g[n - 1].scalar_mul_le(v[n - 1].to_bits_le()?.iter())?; - } else { - res += g[n - 1].joint_scalar_mul_be( - &g[n - 2], - v[n - 1].to_bits_le()?.iter(), - v[n - 2].to_bits_le()?.iter(), - )?; - } - for i in (1..n - 1).step_by(2) { - res += g[i - 1].joint_scalar_mul_be( - &g[i], - v[i - 1].to_bits_le()?.iter(), - v[i].to_bits_le()?.iter(), - )?; + for (g_i, v_i) in g.iter().zip(v) { + res += g_i.scalar_mul_le(v_i.to_bits_le()?.iter())?; } Ok(res) }