Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
154 changes: 4 additions & 150 deletions crates/primitives/src/algebra/group/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -40,10 +35,7 @@ pub trait SonobeCurve:
+ Inputize<Self::BaseField>
+ InputizeEmulated<Self::ScalarField>
+ Val<
Var: CurveVar<Self, Self::BaseField>
+ AbsorbableVar<Self::BaseField>
+ WitnessToPublic
+ JointScalarMul<Self::BaseField>,
Var: CurveVar<Self, Self::BaseField> + AbsorbableVar<Self::BaseField> + WitnessToPublic,
EmulatedVar<Self::ScalarField> = EmulatedAffineVar<Self::ScalarField, Self>,
>
{
Expand Down Expand Up @@ -123,141 +115,3 @@ impl<P: SWCurveConfig<BaseField: PrimeField>> 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<F: Field>: 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<Item = &'a Boolean<F>>,
s2: impl Iterator<Item = &'a Boolean<F>>,
) -> Result<Self, SynthesisError>;
}

impl<P: SWCurveConfig<BaseField: PrimeField>> JointScalarMul<P::BaseField>
for ProjectiveVar<P, FpVar<P::BaseField>>
{
fn joint_scalar_mul_be<'a>(
&self,
p: &Self,
bits1: impl Iterator<Item = &'a Boolean<<P::BaseField as Field>::BasePrimeField>>,
bits2: impl Iterator<Item = &'a Boolean<<P::BaseField as Field>::BasePrimeField>>,
) -> Result<Self, SynthesisError> {
// prepare bits decomposition
let mut bits1 = bits1.collect::<Vec<_>>();
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::<Vec<_>>();
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::<P, _>::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::<P, _>::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
))
}
}
23 changes: 3 additions & 20 deletions crates/primitives/src/commitments/pedersen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -225,22 +222,8 @@ impl<C: SonobeCurve, const H: bool> PedersenGadget<C, H> {
/// with the given generators `g` and scalar bits `v`.
fn msm(g: &[C::Var], v: &[Vec<Boolean<CF2<C>>>]) -> Result<C::Var, SynthesisError> {
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)
}
Expand Down
Loading