Merge pull request #511 from mir-protocol/gadget_curve_msm

Gadget for curve MSM, fixed-base scalar multiplication and ECDSA verification in under `2^17` or `2^16` gates
This commit is contained in:
wborgeaud 2022-03-05 02:50:57 +01:00 committed by GitHub
commit 50f722d83e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 344 additions and 45 deletions

View File

@ -8,14 +8,14 @@ use crate::curve::curve_msm::msm_parallel;
use crate::curve::curve_types::{AffinePoint, ProjectivePoint}; use crate::curve::curve_types::{AffinePoint, ProjectivePoint};
use crate::curve::secp256k1::Secp256K1; use crate::curve::secp256k1::Secp256K1;
pub const BETA: Secp256K1Base = Secp256K1Base([ pub const GLV_BETA: Secp256K1Base = Secp256K1Base([
13923278643952681454, 13923278643952681454,
11308619431505398165, 11308619431505398165,
7954561588662645993, 7954561588662645993,
8856726876819556112, 8856726876819556112,
]); ]);
const S: Secp256K1Scalar = Secp256K1Scalar([ pub const GLV_S: Secp256K1Scalar = Secp256K1Scalar([
16069571880186789234, 16069571880186789234,
1310022930574435960, 1310022930574435960,
11900229862571533402, 11900229862571533402,
@ -52,7 +52,7 @@ pub fn decompose_secp256k1_scalar(
let k1_raw = k - c1 * A1 - c2 * A2; let k1_raw = k - c1 * A1 - c2 * A2;
let k2_raw = c1 * MINUS_B1 - c2 * B2; let k2_raw = c1 * MINUS_B1 - c2 * B2;
debug_assert!(k1_raw + S * k2_raw == k); debug_assert!(k1_raw + GLV_S * k2_raw == k);
let two = BigUint::from_slice(&[2]); let two = BigUint::from_slice(&[2]);
let k1_neg = k1_raw.to_canonical_biguint() > p.clone() / two.clone(); let k1_neg = k1_raw.to_canonical_biguint() > p.clone() / two.clone();
@ -80,7 +80,7 @@ pub fn glv_mul(p: ProjectivePoint<Secp256K1>, k: Secp256K1Scalar) -> ProjectiveP
let p_affine = p.to_affine(); let p_affine = p.to_affine();
let sp = AffinePoint::<Secp256K1> { let sp = AffinePoint::<Secp256K1> {
x: p_affine.x * BETA, x: p_affine.x * GLV_BETA,
y: p_affine.y, y: p_affine.y,
zero: p_affine.zero, zero: p_affine.zero,
}; };
@ -102,7 +102,7 @@ mod tests {
use plonky2_field::secp256k1_scalar::Secp256K1Scalar; use plonky2_field::secp256k1_scalar::Secp256K1Scalar;
use crate::curve::curve_types::{Curve, CurveScalar}; use crate::curve::curve_types::{Curve, CurveScalar};
use crate::curve::glv::{decompose_secp256k1_scalar, glv_mul, S}; use crate::curve::glv::{decompose_secp256k1_scalar, glv_mul, GLV_S};
use crate::curve::secp256k1::Secp256K1; use crate::curve::secp256k1::Secp256K1;
#[test] #[test]
@ -113,7 +113,7 @@ mod tests {
let m1 = if k1_neg { -one } else { one }; let m1 = if k1_neg { -one } else { one };
let m2 = if k2_neg { -one } else { one }; let m2 = if k2_neg { -one } else { one };
assert!(k1 * m1 + S * k2 * m2 == k); assert!(k1 * m1 + GLV_S * k2 * m2 == k);
Ok(()) Ok(())
} }

View File

@ -76,17 +76,10 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
p: &AffinePointTarget<C>, p: &AffinePointTarget<C>,
b: BoolTarget, b: BoolTarget,
) -> AffinePointTarget<C> { ) -> AffinePointTarget<C> {
let not_b = self.not(b); AffinePointTarget {
let neg = self.curve_neg(p); x: p.x.clone(),
let x_if_true = self.mul_nonnative_by_bool(&neg.x, b); y: self.nonnative_conditional_neg(&p.y, b),
let y_if_true = self.mul_nonnative_by_bool(&neg.y, b); }
let x_if_false = self.mul_nonnative_by_bool(&p.x, not_b);
let y_if_false = self.mul_nonnative_by_bool(&p.y, not_b);
let x = self.add_nonnative(&x_if_true, &x_if_false);
let y = self.add_nonnative(&y_if_true, &y_if_false);
AffinePointTarget { x, y }
} }
pub fn curve_double<C: Curve>(&mut self, p: &AffinePointTarget<C>) -> AffinePointTarget<C> { pub fn curve_double<C: Curve>(&mut self, p: &AffinePointTarget<C>) -> AffinePointTarget<C> {

View File

@ -0,0 +1,110 @@
use num::BigUint;
use plonky2_field::extension_field::Extendable;
use crate::curve::curve_types::{AffinePoint, Curve, CurveScalar};
use crate::field::field_types::Field;
use crate::gadgets::curve::AffinePointTarget;
use crate::gadgets::nonnative::NonNativeTarget;
use crate::hash::hash_types::RichField;
use crate::hash::keccak::KeccakHash;
use crate::plonk::circuit_builder::CircuitBuilder;
use crate::plonk::config::{GenericHashOut, Hasher};
impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
/// Compute windowed fixed-base scalar multiplication, using a 4-bit window.
pub fn fixed_base_curve_mul<C: Curve>(
&mut self,
base: AffinePoint<C>,
scalar: &NonNativeTarget<C::ScalarField>,
) -> AffinePointTarget<C> {
// Holds `(16^i) * base` for `i=0..scalar.value.limbs.len() * 8`.
let scaled_base = (0..scalar.value.limbs.len() * 8).scan(base, |acc, _| {
let tmp = *acc;
for _ in 0..4 {
*acc = acc.double();
}
Some(tmp)
});
let limbs = self.split_nonnative_to_4_bit_limbs(scalar);
let hash_0 = KeccakHash::<32>::hash_no_pad(&[F::ZERO]);
let hash_0_scalar = C::ScalarField::from_biguint(BigUint::from_bytes_le(
&GenericHashOut::<F>::to_bytes(&hash_0),
));
let rando = (CurveScalar(hash_0_scalar) * C::GENERATOR_PROJECTIVE).to_affine();
let zero = self.zero();
let mut result = self.constant_affine_point(rando);
// `s * P = sum s_i * P_i` with `P_i = (16^i) * P` and `s = sum s_i * (16^i)`.
for (limb, point) in limbs.into_iter().zip(scaled_base) {
// `muls_point[t] = t * P_i` for `t=0..16`.
let muls_point = (0..16)
.scan(AffinePoint::ZERO, |acc, _| {
let tmp = *acc;
*acc = (point + *acc).to_affine();
Some(tmp)
})
.map(|p| self.constant_affine_point(p))
.collect::<Vec<_>>();
let is_zero = self.is_equal(limb, zero);
let should_add = self.not(is_zero);
// `r = s_i * P_i`
let r = self.random_access_curve_points(limb, muls_point);
result = self.curve_conditional_add(&result, &r, should_add);
}
let to_add = self.constant_affine_point(-rando);
self.curve_add(&result, &to_add)
}
}
#[cfg(test)]
mod tests {
use anyhow::Result;
use plonky2_field::field_types::PrimeField;
use plonky2_field::secp256k1_scalar::Secp256K1Scalar;
use crate::curve::curve_types::{Curve, CurveScalar};
use crate::curve::secp256k1::Secp256K1;
use crate::field::field_types::Field;
use crate::iop::witness::{PartialWitness, Witness};
use crate::plonk::circuit_builder::CircuitBuilder;
use crate::plonk::circuit_data::CircuitConfig;
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
use crate::plonk::verifier::verify;
#[test]
#[ignore]
fn test_fixed_base() -> Result<()> {
const D: usize = 2;
type C = PoseidonGoldilocksConfig;
type F = <C as GenericConfig<D>>::F;
let config = CircuitConfig::standard_ecc_config();
let mut pw = PartialWitness::new();
let mut builder = CircuitBuilder::<F, D>::new(config);
let g = Secp256K1::GENERATOR_AFFINE;
let n = Secp256K1Scalar::rand();
let res = (CurveScalar(n) * g.to_projective()).to_affine();
let res_expected = builder.constant_affine_point(res);
builder.curve_assert_valid(&res_expected);
let n_target = builder.add_virtual_nonnative_target::<Secp256K1Scalar>();
pw.set_biguint_target(&n_target.value, &n.to_canonical_biguint());
let res_target = builder.fixed_base_curve_mul(g, &n_target);
builder.curve_assert_valid(&res_target);
builder.connect_affine_point(&res_target, &res_expected);
dbg!(builder.num_gates());
let data = builder.build::<C>();
let proof = data.prove(pw).unwrap();
verify(proof, &data.verifier_only, &data.common)
}
}

View File

@ -0,0 +1,133 @@
use num::BigUint;
use plonky2_field::extension_field::Extendable;
use crate::curve::curve_types::{Curve, CurveScalar};
use crate::field::field_types::Field;
use crate::gadgets::curve::AffinePointTarget;
use crate::gadgets::nonnative::NonNativeTarget;
use crate::hash::hash_types::RichField;
use crate::hash::keccak::KeccakHash;
use crate::plonk::circuit_builder::CircuitBuilder;
use crate::plonk::config::{GenericHashOut, Hasher};
impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
/// Computes `n*p + m*q` using windowed MSM, with a 2-bit window.
/// See Algorithm 9.23 in Handbook of Elliptic and Hyperelliptic Curve Cryptography for a
/// description.
/// Note: Doesn't work if `p == q`.
pub fn curve_msm<C: Curve>(
&mut self,
p: &AffinePointTarget<C>,
q: &AffinePointTarget<C>,
n: &NonNativeTarget<C::ScalarField>,
m: &NonNativeTarget<C::ScalarField>,
) -> AffinePointTarget<C> {
let limbs_n = self.split_nonnative_to_2_bit_limbs(n);
let limbs_m = self.split_nonnative_to_2_bit_limbs(m);
assert_eq!(limbs_n.len(), limbs_m.len());
let num_limbs = limbs_n.len();
let hash_0 = KeccakHash::<32>::hash_no_pad(&[F::ZERO]);
let hash_0_scalar = C::ScalarField::from_biguint(BigUint::from_bytes_le(
&GenericHashOut::<F>::to_bytes(&hash_0),
));
let rando = (CurveScalar(hash_0_scalar) * C::GENERATOR_PROJECTIVE).to_affine();
let rando_t = self.constant_affine_point(rando);
let neg_rando = self.constant_affine_point(-rando);
// Precomputes `precomputation[i + 4*j] = i*p + j*q` for `i,j=0..4`.
let mut precomputation = vec![p.clone(); 16];
let mut cur_p = rando_t.clone();
let mut cur_q = rando_t.clone();
for i in 0..4 {
precomputation[i] = cur_p.clone();
precomputation[4 * i] = cur_q.clone();
cur_p = self.curve_add(&cur_p, p);
cur_q = self.curve_add(&cur_q, q);
}
for i in 1..4 {
precomputation[i] = self.curve_add(&precomputation[i], &neg_rando);
precomputation[4 * i] = self.curve_add(&precomputation[4 * i], &neg_rando);
}
for i in 1..4 {
for j in 1..4 {
precomputation[i + 4 * j] =
self.curve_add(&precomputation[i], &precomputation[4 * j]);
}
}
let four = self.constant(F::from_canonical_usize(4));
let zero = self.zero();
let mut result = rando_t;
for (limb_n, limb_m) in limbs_n.into_iter().zip(limbs_m).rev() {
result = self.curve_repeated_double(&result, 2);
let index = self.mul_add(four, limb_m, limb_n);
let r = self.random_access_curve_points(index, precomputation.clone());
let is_zero = self.is_equal(index, zero);
let should_add = self.not(is_zero);
result = self.curve_conditional_add(&result, &r, should_add);
}
let starting_point_multiplied = (0..2 * num_limbs).fold(rando, |acc, _| acc.double());
let to_add = self.constant_affine_point(-starting_point_multiplied);
result = self.curve_add(&result, &to_add);
result
}
}
#[cfg(test)]
mod tests {
use anyhow::Result;
use plonky2_field::secp256k1_scalar::Secp256K1Scalar;
use crate::curve::curve_types::{Curve, CurveScalar};
use crate::curve::secp256k1::Secp256K1;
use crate::field::field_types::Field;
use crate::iop::witness::PartialWitness;
use crate::plonk::circuit_builder::CircuitBuilder;
use crate::plonk::circuit_data::CircuitConfig;
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
use crate::plonk::verifier::verify;
#[test]
#[ignore]
fn test_curve_msm() -> Result<()> {
const D: usize = 2;
type C = PoseidonGoldilocksConfig;
type F = <C as GenericConfig<D>>::F;
let config = CircuitConfig::standard_ecc_config();
let pw = PartialWitness::new();
let mut builder = CircuitBuilder::<F, D>::new(config);
let p =
(CurveScalar(Secp256K1Scalar::rand()) * Secp256K1::GENERATOR_PROJECTIVE).to_affine();
let q =
(CurveScalar(Secp256K1Scalar::rand()) * Secp256K1::GENERATOR_PROJECTIVE).to_affine();
let n = Secp256K1Scalar::rand();
let m = Secp256K1Scalar::rand();
let res =
(CurveScalar(n) * p.to_projective() + CurveScalar(m) * q.to_projective()).to_affine();
let res_expected = builder.constant_affine_point(res);
builder.curve_assert_valid(&res_expected);
let p_target = builder.constant_affine_point(p);
let q_target = builder.constant_affine_point(q);
let n_target = builder.constant_nonnative(n);
let m_target = builder.constant_nonnative(m);
let res_target = builder.curve_msm(&p_target, &q_target, &n_target, &m_target);
builder.curve_assert_valid(&res_target);
builder.connect_affine_point(&res_target, &res_expected);
dbg!(builder.num_gates());
let data = builder.build::<C>();
let proof = data.prove(pw).unwrap();
verify(proof, &data.verifier_only, &data.common)
}
}

View File

@ -44,12 +44,21 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
access_index: Target, access_index: Target,
v: Vec<AffinePointTarget<C>>, v: Vec<AffinePointTarget<C>>,
) -> AffinePointTarget<C> { ) -> AffinePointTarget<C> {
let num_limbs = v[0].x.value.num_limbs(); let num_limbs = C::BaseField::BITS / 32;
let zero = self.zero_u32();
let x_limbs: Vec<Vec<_>> = (0..num_limbs) let x_limbs: Vec<Vec<_>> = (0..num_limbs)
.map(|i| v.iter().map(|p| p.x.value.limbs[i].0).collect()) .map(|i| {
v.iter()
.map(|p| p.x.value.limbs.get(i).unwrap_or(&zero).0)
.collect()
})
.collect(); .collect();
let y_limbs: Vec<Vec<_>> = (0..num_limbs) let y_limbs: Vec<Vec<_>> = (0..num_limbs)
.map(|i| v.iter().map(|p| p.y.value.limbs[i].0).collect()) .map(|i| {
v.iter()
.map(|p| p.y.value.limbs.get(i).unwrap_or(&zero).0)
.collect()
})
.collect(); .collect();
let selected_x_limbs: Vec<_> = x_limbs let selected_x_limbs: Vec<_> = x_limbs

View File

@ -1,6 +1,9 @@
use std::marker::PhantomData; use std::marker::PhantomData;
use plonky2_field::secp256k1_scalar::Secp256K1Scalar;
use crate::curve::curve_types::Curve; use crate::curve::curve_types::Curve;
use crate::curve::secp256k1::Secp256K1;
use crate::field::extension_field::Extendable; use crate::field::extension_field::Extendable;
use crate::gadgets::curve::AffinePointTarget; use crate::gadgets::curve::AffinePointTarget;
use crate::gadgets::nonnative::NonNativeTarget; use crate::gadgets::nonnative::NonNativeTarget;
@ -20,11 +23,11 @@ pub struct ECDSASignatureTarget<C: Curve> {
} }
impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> { impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
pub fn verify_message<C: Curve>( pub fn verify_message(
&mut self, &mut self,
msg: NonNativeTarget<C::ScalarField>, msg: NonNativeTarget<Secp256K1Scalar>,
sig: ECDSASignatureTarget<C>, sig: ECDSASignatureTarget<Secp256K1>,
pk: ECDSAPublicKeyTarget<C>, pk: ECDSAPublicKeyTarget<Secp256K1>,
) { ) {
let ECDSASignatureTarget { r, s } = sig; let ECDSASignatureTarget { r, s } = sig;
@ -34,12 +37,11 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
let u1 = self.mul_nonnative(&msg, &c); let u1 = self.mul_nonnative(&msg, &c);
let u2 = self.mul_nonnative(&r, &c); let u2 = self.mul_nonnative(&r, &c);
let g = self.constant_affine_point(C::GENERATOR_AFFINE); let point1 = self.fixed_base_curve_mul(Secp256K1::GENERATOR_AFFINE, &u1);
let point1 = self.curve_scalar_mul(&g, &u1); let point2 = self.glv_mul(&pk.0, &u2);
let point2 = self.curve_scalar_mul(&pk.0, &u2);
let point = self.curve_add(&point1, &point2); let point = self.curve_add(&point1, &point2);
let x = NonNativeTarget::<C::ScalarField> { let x = NonNativeTarget::<Secp256K1Scalar> {
value: point.x.value, value: point.x.value,
_phantom: PhantomData, _phantom: PhantomData,
}; };
@ -63,17 +65,13 @@ mod tests {
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig}; use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
use crate::plonk::verifier::verify; use crate::plonk::verifier::verify;
#[test] fn test_ecdsa_circuit_with_config(config: CircuitConfig) -> Result<()> {
#[ignore]
fn test_ecdsa_circuit() -> Result<()> {
const D: usize = 2; const D: usize = 2;
type C = PoseidonGoldilocksConfig; type C = PoseidonGoldilocksConfig;
type F = <C as GenericConfig<D>>::F; type F = <C as GenericConfig<D>>::F;
type Curve = Secp256K1; type Curve = Secp256K1;
let config = CircuitConfig::standard_ecc_config();
let pw = PartialWitness::new(); let pw = PartialWitness::new();
let mut builder = CircuitBuilder::<F, D>::new(config); let mut builder = CircuitBuilder::<F, D>::new(config);
@ -97,8 +95,21 @@ mod tests {
builder.verify_message(msg_target, sig_target, pk_target); builder.verify_message(msg_target, sig_target, pk_target);
dbg!(builder.num_gates());
let data = builder.build::<C>(); let data = builder.build::<C>();
let proof = data.prove(pw).unwrap(); let proof = data.prove(pw).unwrap();
verify(proof, &data.verifier_only, &data.common) verify(proof, &data.verifier_only, &data.common)
} }
#[test]
#[ignore]
fn test_ecdsa_circuit_narrow() -> Result<()> {
test_ecdsa_circuit_with_config(CircuitConfig::standard_ecc_config())
}
#[test]
#[ignore]
fn test_ecdsa_circuit_wide() -> Result<()> {
test_ecdsa_circuit_with_config(CircuitConfig::wide_ecc_config())
}
} }

View File

@ -4,7 +4,7 @@ use plonky2_field::extension_field::Extendable;
use plonky2_field::secp256k1_base::Secp256K1Base; use plonky2_field::secp256k1_base::Secp256K1Base;
use plonky2_field::secp256k1_scalar::Secp256K1Scalar; use plonky2_field::secp256k1_scalar::Secp256K1Scalar;
use crate::curve::glv::{decompose_secp256k1_scalar, BETA}; use crate::curve::glv::{decompose_secp256k1_scalar, GLV_BETA, GLV_S};
use crate::curve::secp256k1::Secp256K1; use crate::curve::secp256k1::Secp256K1;
use crate::gadgets::curve::AffinePointTarget; use crate::gadgets::curve::AffinePointTarget;
use crate::gadgets::nonnative::NonNativeTarget; use crate::gadgets::nonnative::NonNativeTarget;
@ -16,7 +16,7 @@ use crate::plonk::circuit_builder::CircuitBuilder;
impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> { impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
pub fn secp256k1_glv_beta(&mut self) -> NonNativeTarget<Secp256K1Base> { pub fn secp256k1_glv_beta(&mut self) -> NonNativeTarget<Secp256K1Base> {
self.constant_nonnative(BETA) self.constant_nonnative(GLV_BETA)
} }
pub fn decompose_secp256k1_scalar( pub fn decompose_secp256k1_scalar(
@ -42,6 +42,14 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
_phantom: PhantomData, _phantom: PhantomData,
}); });
// Check that `k1_raw + GLV_S * k2_raw == k`.
let k1_raw = self.nonnative_conditional_neg(&k1, k1_neg);
let k2_raw = self.nonnative_conditional_neg(&k2, k2_neg);
let s = self.constant_nonnative(GLV_S);
let mut should_be_k = self.mul_nonnative(&s, &k2_raw);
should_be_k = self.add_nonnative(&should_be_k, &k1_raw);
self.connect_nonnative(&should_be_k, k);
(k1, k2, k1_neg, k2_neg) (k1, k2, k1_neg, k2_neg)
} }
@ -59,12 +67,9 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
y: p.y.clone(), y: p.y.clone(),
}; };
let part1 = self.curve_scalar_mul_windowed(p, &k1); let p_neg = self.curve_conditional_neg(p, k1_neg);
let part1_neg = self.curve_conditional_neg(&part1, k1_neg); let sp_neg = self.curve_conditional_neg(&sp, k2_neg);
let part2 = self.curve_scalar_mul_windowed(&sp, &k2); self.curve_msm(&p_neg, &sp_neg, &k1, &k2)
let part2_neg = self.curve_conditional_neg(&part2, k2_neg);
self.curve_add(&part1_neg, &part2_neg)
} }
} }
@ -134,6 +139,7 @@ mod tests {
let actual = builder.glv_mul(&randot, &scalar_target); let actual = builder.glv_mul(&randot, &scalar_target);
builder.connect_affine_point(&expected, &actual); builder.connect_affine_point(&expected, &actual);
dbg!(builder.num_gates());
let data = builder.build::<C>(); let data = builder.build::<C>();
let proof = data.prove(pw).unwrap(); let proof = data.prove(pw).unwrap();

View File

@ -3,8 +3,9 @@ pub mod arithmetic_extension;
pub mod arithmetic_u32; pub mod arithmetic_u32;
pub mod biguint; pub mod biguint;
pub mod curve; pub mod curve;
pub mod curve_fixed_base;
pub mod curve_msm;
pub mod curve_windowed_mul; pub mod curve_windowed_mul;
// pub mod curve_msm;
pub mod ecdsa; pub mod ecdsa;
pub mod glv; pub mod glv;
pub mod hash; pub mod hash;

View File

@ -338,6 +338,19 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
result result
} }
pub fn nonnative_conditional_neg<FF: PrimeField>(
&mut self,
x: &NonNativeTarget<FF>,
b: BoolTarget,
) -> NonNativeTarget<FF> {
let not_b = self.not(b);
let neg = self.neg_nonnative(x);
let x_if_true = self.mul_nonnative_by_bool(&neg, b);
let x_if_false = self.mul_nonnative_by_bool(x, not_b);
self.add_nonnative(&x_if_true, &x_if_false)
}
} }
#[derive(Debug)] #[derive(Debug)]
@ -454,7 +467,7 @@ impl<F: RichField + Extendable<D>, const D: usize, FF: PrimeField> SimpleGenerat
let b_biguint = b.to_canonical_biguint(); let b_biguint = b.to_canonical_biguint();
let modulus = FF::order(); let modulus = FF::order();
let (diff_biguint, overflow) = if a_biguint > b_biguint { let (diff_biguint, overflow) = if a_biguint >= b_biguint {
(a_biguint - b_biguint, false) (a_biguint - b_biguint, false)
} else { } else {
(modulus + a_biguint - b_biguint, true) (modulus + a_biguint - b_biguint, true)

View File

@ -35,6 +35,17 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
.collect() .collect()
} }
pub fn split_nonnative_to_2_bit_limbs<FF: Field>(
&mut self,
val: &NonNativeTarget<FF>,
) -> Vec<Target> {
val.value
.limbs
.iter()
.flat_map(|&l| self.split_le_base::<4>(l.0, 16))
.collect()
}
// Note: assumes its inputs are 4-bit limbs, and does not range-check. // Note: assumes its inputs are 4-bit limbs, and does not range-check.
pub fn recombine_nonnative_4_bit_limbs<FF: Field>( pub fn recombine_nonnative_4_bit_limbs<FF: Field>(
&mut self, &mut self,

View File

@ -1,4 +1,5 @@
use std::collections::HashMap; use std::collections::HashMap;
use std::iter::repeat;
use itertools::Itertools; use itertools::Itertools;
use num::{BigUint, FromPrimitive, Zero}; use num::{BigUint, FromPrimitive, Zero};
@ -160,7 +161,11 @@ pub trait Witness<F: Field> {
} }
fn set_biguint_target(&mut self, target: &BigUintTarget, value: &BigUint) { fn set_biguint_target(&mut self, target: &BigUintTarget, value: &BigUint) {
for (&lt, &l) in target.limbs.iter().zip(&value.to_u32_digits()) { for (&lt, l) in target
.limbs
.iter()
.zip(value.to_u32_digits().into_iter().chain(repeat(0)))
{
self.set_u32_target(lt, l); self.set_u32_target(lt, l);
} }
} }

View File

@ -70,7 +70,7 @@ pub struct CircuitBuilder<F: RichField + Extendable<D>, const D: usize> {
marked_targets: Vec<MarkedTargets<D>>, marked_targets: Vec<MarkedTargets<D>>,
/// Generators used to generate the witness. /// Generators used to generate the witness.
generators: Vec<Box<dyn WitnessGenerator<F>>>, pub generators: Vec<Box<dyn WitnessGenerator<F>>>,
constants_to_targets: HashMap<F, Target>, constants_to_targets: HashMap<F, Target>,
targets_to_constants: HashMap<Target, F>, targets_to_constants: HashMap<Target, F>,

View File

@ -86,6 +86,13 @@ impl CircuitConfig {
} }
} }
pub fn wide_ecc_config() -> Self {
Self {
num_wires: 234,
..Self::standard_recursion_config()
}
}
pub fn standard_recursion_zk_config() -> Self { pub fn standard_recursion_zk_config() -> Self {
CircuitConfig { CircuitConfig {
zero_knowledge: true, zero_knowledge: true,