mirror of
https://github.com/logos-storage/plonky2.git
synced 2026-01-08 16:53:07 +00:00
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:
commit
50f722d83e
@ -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(())
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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> {
|
||||||
|
|||||||
110
plonky2/src/gadgets/curve_fixed_base.rs
Normal file
110
plonky2/src/gadgets/curve_fixed_base.rs
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
133
plonky2/src/gadgets/curve_msm.rs
Normal file
133
plonky2/src/gadgets/curve_msm.rs
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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
|
||||||
|
|||||||
@ -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())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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();
|
||||||
|
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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 (<, &l) in target.limbs.iter().zip(&value.to_u32_digits()) {
|
for (<, 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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>,
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user