Merge pull request #516 from mir-protocol/ecdsa_module

Move nonnative/curve/ecdsa code to `ecdsa` module
This commit is contained in:
wborgeaud 2022-03-16 18:06:51 +01:00 committed by GitHub
commit deec6a784c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 836 additions and 550 deletions

View File

@ -1,5 +1,5 @@
[workspace]
members = ["field", "insertion", "plonky2", "starky", "system_zero", "util", "waksman"]
members = ["field", "insertion", "plonky2", "starky", "system_zero", "util", "waksman", "ecdsa"]
[profile.release]
opt-level = 3

17
ecdsa/Cargo.toml Normal file
View File

@ -0,0 +1,17 @@
[package]
name = "plonky2_ecdsa"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
plonky2 = { path = "../plonky2" }
plonky2_util = { path = "../util" }
plonky2_field = { path = "../field" }
num = "0.4.0"
itertools = "0.10.0"
rayon = "1.5.1"
serde = { version = "1.0", features = ["derive"] }
anyhow = "1.0.40"
rand = "0.8.4"

View File

@ -36,6 +36,7 @@ impl<C: Curve> ProjectivePoint<C> {
MultiplicationPrecomputation { powers }
}
#[must_use]
pub fn mul_with_precomputation(
&self,
scalar: C::ScalarField,

View File

@ -78,6 +78,7 @@ impl<C: Curve> AffinePoint<C> {
affine_points.iter().map(Self::to_projective).collect()
}
#[must_use]
pub fn double(&self) -> Self {
let AffinePoint { x: x1, y: y1, zero } = *self;
@ -187,6 +188,7 @@ impl<C: Curve> ProjectivePoint<C> {
}
// From https://www.hyperelliptic.org/EFD/g1p/data/shortw/projective/doubling/dbl-2007-bl
#[must_use]
pub fn double(&self) -> Self {
let Self { x, y, z } = *self;
if z == C::BaseField::ZERO {
@ -222,6 +224,7 @@ impl<C: Curve> ProjectivePoint<C> {
.collect()
}
#[must_use]
pub fn neg(&self) -> Self {
Self {
x: self.x,

View File

@ -1,8 +1,8 @@
use plonky2_field::field_types::Field;
use serde::{Deserialize, Serialize};
use crate::curve::curve_msm::msm_parallel;
use crate::curve::curve_types::{base_to_scalar, AffinePoint, Curve, CurveScalar};
use crate::field::field_types::Field;
#[derive(Copy, Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub struct ECDSASignature<C: Curve> {
@ -57,11 +57,12 @@ pub fn verify_message<C: Curve>(
#[cfg(test)]
mod tests {
use plonky2_field::field_types::Field;
use plonky2_field::secp256k1_scalar::Secp256K1Scalar;
use crate::curve::curve_types::{Curve, CurveScalar};
use crate::curve::ecdsa::{sign_message, verify_message, ECDSAPublicKey, ECDSASecretKey};
use crate::curve::secp256k1::Secp256K1;
use crate::field::field_types::Field;
use crate::field::secp256k1_scalar::Secp256K1Scalar;
#[test]
fn test_ecdsa_native() {

View File

@ -1,14 +1,14 @@
use std::marker::PhantomData;
use num::{BigUint, Integer, Zero};
use plonky2::gadgets::arithmetic_u32::U32Target;
use plonky2::hash::hash_types::RichField;
use plonky2::iop::generator::{GeneratedValues, SimpleGenerator};
use plonky2::iop::target::{BoolTarget, Target};
use plonky2::iop::witness::{PartitionWitness, Witness};
use plonky2::plonk::circuit_builder::CircuitBuilder;
use plonky2_field::extension_field::Extendable;
use crate::gadgets::arithmetic_u32::U32Target;
use crate::hash::hash_types::RichField;
use crate::iop::generator::{GeneratedValues, SimpleGenerator};
use crate::iop::target::{BoolTarget, Target};
use crate::iop::witness::{PartitionWitness, Witness};
use crate::plonk::circuit_builder::CircuitBuilder;
use plonky2_field::field_types::PrimeField;
#[derive(Clone, Debug)]
pub struct BigUintTarget {
@ -25,19 +25,67 @@ impl BigUintTarget {
}
}
impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
pub fn constant_biguint(&mut self, value: &BigUint) -> BigUintTarget {
pub trait CircuitBuilderBiguint<F: RichField + Extendable<D>, const D: usize> {
fn constant_biguint(&mut self, value: &BigUint) -> BigUintTarget;
fn zero_biguint(&mut self) -> BigUintTarget;
fn connect_biguint(&mut self, lhs: &BigUintTarget, rhs: &BigUintTarget);
fn pad_biguints(
&mut self,
a: &BigUintTarget,
b: &BigUintTarget,
) -> (BigUintTarget, BigUintTarget);
fn cmp_biguint(&mut self, a: &BigUintTarget, b: &BigUintTarget) -> BoolTarget;
fn add_virtual_biguint_target(&mut self, num_limbs: usize) -> BigUintTarget;
/// Add two `BigUintTarget`s.
fn add_biguint(&mut self, a: &BigUintTarget, b: &BigUintTarget) -> BigUintTarget;
/// Subtract two `BigUintTarget`s. We assume that the first is larger than the second.
fn sub_biguint(&mut self, a: &BigUintTarget, b: &BigUintTarget) -> BigUintTarget;
fn mul_biguint(&mut self, a: &BigUintTarget, b: &BigUintTarget) -> BigUintTarget;
fn mul_biguint_by_bool(&mut self, a: &BigUintTarget, b: BoolTarget) -> BigUintTarget;
/// Returns x * y + z. This is no more efficient than mul-then-add; it's purely for convenience (only need to call one CircuitBuilder function).
fn mul_add_biguint(
&mut self,
x: &BigUintTarget,
y: &BigUintTarget,
z: &BigUintTarget,
) -> BigUintTarget;
fn div_rem_biguint(
&mut self,
a: &BigUintTarget,
b: &BigUintTarget,
) -> (BigUintTarget, BigUintTarget);
fn div_biguint(&mut self, a: &BigUintTarget, b: &BigUintTarget) -> BigUintTarget;
fn rem_biguint(&mut self, a: &BigUintTarget, b: &BigUintTarget) -> BigUintTarget;
}
impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilderBiguint<F, D>
for CircuitBuilder<F, D>
{
fn constant_biguint(&mut self, value: &BigUint) -> BigUintTarget {
let limb_values = value.to_u32_digits();
let limbs = limb_values.iter().map(|&l| self.constant_u32(l)).collect();
BigUintTarget { limbs }
}
pub fn zero_biguint(&mut self) -> BigUintTarget {
fn zero_biguint(&mut self) -> BigUintTarget {
self.constant_biguint(&BigUint::zero())
}
pub fn connect_biguint(&mut self, lhs: &BigUintTarget, rhs: &BigUintTarget) {
fn connect_biguint(&mut self, lhs: &BigUintTarget, rhs: &BigUintTarget) {
let min_limbs = lhs.num_limbs().min(rhs.num_limbs());
for i in 0..min_limbs {
self.connect_u32(lhs.get_limb(i), rhs.get_limb(i));
@ -51,7 +99,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
}
}
pub fn pad_biguints(
fn pad_biguints(
&mut self,
a: &BigUintTarget,
b: &BigUintTarget,
@ -73,20 +121,19 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
}
}
pub fn cmp_biguint(&mut self, a: &BigUintTarget, b: &BigUintTarget) -> BoolTarget {
fn cmp_biguint(&mut self, a: &BigUintTarget, b: &BigUintTarget) -> BoolTarget {
let (a, b) = self.pad_biguints(a, b);
self.list_le_u32(a.limbs, b.limbs)
}
pub fn add_virtual_biguint_target(&mut self, num_limbs: usize) -> BigUintTarget {
fn add_virtual_biguint_target(&mut self, num_limbs: usize) -> BigUintTarget {
let limbs = self.add_virtual_u32_targets(num_limbs);
BigUintTarget { limbs }
}
// Add two `BigUintTarget`s.
pub fn add_biguint(&mut self, a: &BigUintTarget, b: &BigUintTarget) -> BigUintTarget {
fn add_biguint(&mut self, a: &BigUintTarget, b: &BigUintTarget) -> BigUintTarget {
let num_limbs = a.num_limbs().max(b.num_limbs());
let mut combined_limbs = vec![];
@ -110,8 +157,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
}
}
// Subtract two `BigUintTarget`s. We assume that the first is larger than the second.
pub fn sub_biguint(&mut self, a: &BigUintTarget, b: &BigUintTarget) -> BigUintTarget {
fn sub_biguint(&mut self, a: &BigUintTarget, b: &BigUintTarget) -> BigUintTarget {
let (a, b) = self.pad_biguints(a, b);
let num_limbs = a.limbs.len();
@ -130,7 +176,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
}
}
pub fn mul_biguint(&mut self, a: &BigUintTarget, b: &BigUintTarget) -> BigUintTarget {
fn mul_biguint(&mut self, a: &BigUintTarget, b: &BigUintTarget) -> BigUintTarget {
let total_limbs = a.limbs.len() + b.limbs.len();
let mut to_add = vec![vec![]; total_limbs];
@ -156,7 +202,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
}
}
pub fn mul_biguint_by_bool(&mut self, a: &BigUintTarget, b: BoolTarget) -> BigUintTarget {
fn mul_biguint_by_bool(&mut self, a: &BigUintTarget, b: BoolTarget) -> BigUintTarget {
let t = b.target;
BigUintTarget {
@ -168,8 +214,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
}
}
// Returns x * y + z. This is no more efficient than mul-then-add; it's purely for convenience (only need to call one CircuitBuilder function).
pub fn mul_add_biguint(
fn mul_add_biguint(
&mut self,
x: &BigUintTarget,
y: &BigUintTarget,
@ -179,7 +224,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
self.add_biguint(&prod, z)
}
pub fn div_rem_biguint(
fn div_rem_biguint(
&mut self,
a: &BigUintTarget,
b: &BigUintTarget,
@ -212,17 +257,56 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
(div, rem)
}
pub fn div_biguint(&mut self, a: &BigUintTarget, b: &BigUintTarget) -> BigUintTarget {
fn div_biguint(&mut self, a: &BigUintTarget, b: &BigUintTarget) -> BigUintTarget {
let (div, _rem) = self.div_rem_biguint(a, b);
div
}
pub fn rem_biguint(&mut self, a: &BigUintTarget, b: &BigUintTarget) -> BigUintTarget {
fn rem_biguint(&mut self, a: &BigUintTarget, b: &BigUintTarget) -> BigUintTarget {
let (_div, rem) = self.div_rem_biguint(a, b);
rem
}
}
pub fn witness_get_biguint_target<W: Witness<F>, F: PrimeField>(
witness: &W,
bt: BigUintTarget,
) -> BigUint {
let base = BigUint::from(1usize << 32);
bt.limbs
.into_iter()
.rev()
.fold(BigUint::zero(), |acc, limb| {
acc * &base + witness.get_target(limb.0).to_canonical_biguint()
})
}
pub fn witness_set_biguint_target<W: Witness<F>, F: PrimeField>(
witness: &mut W,
target: &BigUintTarget,
value: &BigUint,
) {
let mut limbs = value.to_u32_digits();
assert!(target.num_limbs() >= limbs.len());
limbs.resize(target.num_limbs(), 0);
for i in 0..target.num_limbs() {
witness.set_u32_target(target.limbs[i], limbs[i]);
}
}
pub fn buffer_set_biguint_target<F: PrimeField>(
buffer: &mut GeneratedValues<F>,
target: &BigUintTarget,
value: &BigUint,
) {
let mut limbs = value.to_u32_digits();
assert!(target.num_limbs() >= limbs.len());
limbs.resize(target.num_limbs(), 0);
for i in 0..target.num_limbs() {
buffer.set_u32_target(target.get_limb(i), limbs[i]);
}
}
#[derive(Debug)]
struct BigUintDivRemGenerator<F: RichField + Extendable<D>, const D: usize> {
a: BigUintTarget,
@ -245,12 +329,12 @@ impl<F: RichField + Extendable<D>, const D: usize> SimpleGenerator<F>
}
fn run_once(&self, witness: &PartitionWitness<F>, out_buffer: &mut GeneratedValues<F>) {
let a = witness.get_biguint_target(self.a.clone());
let b = witness.get_biguint_target(self.b.clone());
let a = witness_get_biguint_target(witness, self.a.clone());
let b = witness_get_biguint_target(witness, self.b.clone());
let (div, rem) = a.div_rem(&b);
out_buffer.set_biguint_target(self.div.clone(), div);
out_buffer.set_biguint_target(self.rem.clone(), rem);
buffer_set_biguint_target(out_buffer, &self.div, &div);
buffer_set_biguint_target(out_buffer, &self.rem, &rem);
}
}
@ -258,14 +342,14 @@ impl<F: RichField + Extendable<D>, const D: usize> SimpleGenerator<F>
mod tests {
use anyhow::Result;
use num::{BigUint, FromPrimitive, Integer};
use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
use plonky2::{
iop::witness::PartialWitness,
plonk::{circuit_builder::CircuitBuilder, circuit_data::CircuitConfig},
};
use rand::Rng;
use crate::iop::witness::Witness;
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
use crate::{
iop::witness::PartialWitness,
plonk::{circuit_builder::CircuitBuilder, circuit_data::CircuitConfig, verifier::verify},
};
use crate::gadgets::biguint::{witness_set_biguint_target, CircuitBuilderBiguint};
#[test]
fn test_biguint_add() -> Result<()> {
@ -288,13 +372,13 @@ mod tests {
let expected_z = builder.add_virtual_biguint_target(expected_z_value.to_u32_digits().len());
builder.connect_biguint(&z, &expected_z);
pw.set_biguint_target(&x, &x_value);
pw.set_biguint_target(&y, &y_value);
pw.set_biguint_target(&expected_z, &expected_z_value);
witness_set_biguint_target(&mut pw, &x, &x_value);
witness_set_biguint_target(&mut pw, &y, &y_value);
witness_set_biguint_target(&mut pw, &expected_z, &expected_z_value);
let data = builder.build::<C>();
let proof = data.prove(pw).unwrap();
verify(proof, &data.verifier_only, &data.common)
data.verify(proof)
}
#[test]
@ -324,7 +408,7 @@ mod tests {
let data = builder.build::<C>();
let proof = data.prove(pw).unwrap();
verify(proof, &data.verifier_only, &data.common)
data.verify(proof)
}
#[test]
@ -348,13 +432,13 @@ mod tests {
let expected_z = builder.add_virtual_biguint_target(expected_z_value.to_u32_digits().len());
builder.connect_biguint(&z, &expected_z);
pw.set_biguint_target(&x, &x_value);
pw.set_biguint_target(&y, &y_value);
pw.set_biguint_target(&expected_z, &expected_z_value);
witness_set_biguint_target(&mut pw, &x, &x_value);
witness_set_biguint_target(&mut pw, &y, &y_value);
witness_set_biguint_target(&mut pw, &expected_z, &expected_z_value);
let data = builder.build::<C>();
let proof = data.prove(pw).unwrap();
verify(proof, &data.verifier_only, &data.common)
data.verify(proof)
}
#[test]
@ -380,7 +464,7 @@ mod tests {
let data = builder.build::<C>();
let proof = data.prove(pw).unwrap();
verify(proof, &data.verifier_only, &data.common)
data.verify(proof)
}
#[test]
@ -413,6 +497,6 @@ mod tests {
let data = builder.build::<C>();
let proof = data.prove(pw).unwrap();
verify(proof, &data.verifier_only, &data.common)
data.verify(proof)
}
}

View File

@ -1,11 +1,11 @@
use plonky2::hash::hash_types::RichField;
use plonky2::iop::target::BoolTarget;
use plonky2::plonk::circuit_builder::CircuitBuilder;
use plonky2_field::extension_field::Extendable;
use plonky2_field::field_types::Field;
use crate::curve::curve_types::{AffinePoint, Curve, CurveScalar};
use crate::gadgets::nonnative::NonNativeTarget;
use crate::hash::hash_types::RichField;
use crate::iop::target::BoolTarget;
use crate::plonk::circuit_builder::CircuitBuilder;
use crate::gadgets::nonnative::{CircuitBuilderNonNative, NonNativeTarget};
/// A Target representing an affine point on the curve `C`. We use incomplete arithmetic for efficiency,
/// so we assume these points are not zero.
@ -21,11 +21,60 @@ impl<C: Curve> AffinePointTarget<C> {
}
}
impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
pub fn constant_affine_point<C: Curve>(
pub trait CircuitBuilderCurve<F: RichField + Extendable<D>, const D: usize> {
fn constant_affine_point<C: Curve>(&mut self, point: AffinePoint<C>) -> AffinePointTarget<C>;
fn connect_affine_point<C: Curve>(
&mut self,
point: AffinePoint<C>,
) -> AffinePointTarget<C> {
lhs: &AffinePointTarget<C>,
rhs: &AffinePointTarget<C>,
);
fn add_virtual_affine_point_target<C: Curve>(&mut self) -> AffinePointTarget<C>;
fn curve_assert_valid<C: Curve>(&mut self, p: &AffinePointTarget<C>);
fn curve_neg<C: Curve>(&mut self, p: &AffinePointTarget<C>) -> AffinePointTarget<C>;
fn curve_conditional_neg<C: Curve>(
&mut self,
p: &AffinePointTarget<C>,
b: BoolTarget,
) -> AffinePointTarget<C>;
fn curve_double<C: Curve>(&mut self, p: &AffinePointTarget<C>) -> AffinePointTarget<C>;
fn curve_repeated_double<C: Curve>(
&mut self,
p: &AffinePointTarget<C>,
n: usize,
) -> AffinePointTarget<C>;
/// Add two points, which are assumed to be non-equal.
fn curve_add<C: Curve>(
&mut self,
p1: &AffinePointTarget<C>,
p2: &AffinePointTarget<C>,
) -> AffinePointTarget<C>;
fn curve_conditional_add<C: Curve>(
&mut self,
p1: &AffinePointTarget<C>,
p2: &AffinePointTarget<C>,
b: BoolTarget,
) -> AffinePointTarget<C>;
fn curve_scalar_mul<C: Curve>(
&mut self,
p: &AffinePointTarget<C>,
n: &NonNativeTarget<C::ScalarField>,
) -> AffinePointTarget<C>;
}
impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilderCurve<F, D>
for CircuitBuilder<F, D>
{
fn constant_affine_point<C: Curve>(&mut self, point: AffinePoint<C>) -> AffinePointTarget<C> {
debug_assert!(!point.zero);
AffinePointTarget {
x: self.constant_nonnative(point.x),
@ -33,7 +82,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
}
}
pub fn connect_affine_point<C: Curve>(
fn connect_affine_point<C: Curve>(
&mut self,
lhs: &AffinePointTarget<C>,
rhs: &AffinePointTarget<C>,
@ -42,14 +91,14 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
self.connect_nonnative(&lhs.y, &rhs.y);
}
pub fn add_virtual_affine_point_target<C: Curve>(&mut self) -> AffinePointTarget<C> {
fn add_virtual_affine_point_target<C: Curve>(&mut self) -> AffinePointTarget<C> {
let x = self.add_virtual_nonnative_target();
let y = self.add_virtual_nonnative_target();
AffinePointTarget { x, y }
}
pub fn curve_assert_valid<C: Curve>(&mut self, p: &AffinePointTarget<C>) {
fn curve_assert_valid<C: Curve>(&mut self, p: &AffinePointTarget<C>) {
let a = self.constant_nonnative(C::A);
let b = self.constant_nonnative(C::B);
@ -63,7 +112,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
self.connect_nonnative(&y_squared, &rhs);
}
pub fn curve_neg<C: Curve>(&mut self, p: &AffinePointTarget<C>) -> AffinePointTarget<C> {
fn curve_neg<C: Curve>(&mut self, p: &AffinePointTarget<C>) -> AffinePointTarget<C> {
let neg_y = self.neg_nonnative(&p.y);
AffinePointTarget {
x: p.x.clone(),
@ -71,7 +120,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
}
}
pub fn curve_conditional_neg<C: Curve>(
fn curve_conditional_neg<C: Curve>(
&mut self,
p: &AffinePointTarget<C>,
b: BoolTarget,
@ -82,7 +131,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
}
}
pub fn curve_double<C: Curve>(&mut self, p: &AffinePointTarget<C>) -> AffinePointTarget<C> {
fn curve_double<C: Curve>(&mut self, p: &AffinePointTarget<C>) -> AffinePointTarget<C> {
let AffinePointTarget { x, y } = p;
let double_y = self.add_nonnative(y, y);
let inv_double_y = self.inv_nonnative(&double_y);
@ -106,7 +155,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
AffinePointTarget { x: x3, y: y3 }
}
pub fn curve_repeated_double<C: Curve>(
fn curve_repeated_double<C: Curve>(
&mut self,
p: &AffinePointTarget<C>,
n: usize,
@ -120,8 +169,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
result
}
// Add two points, which are assumed to be non-equal.
pub fn curve_add<C: Curve>(
fn curve_add<C: Curve>(
&mut self,
p1: &AffinePointTarget<C>,
p2: &AffinePointTarget<C>,
@ -143,7 +191,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
AffinePointTarget { x: x3, y: y3 }
}
pub fn curve_conditional_add<C: Curve>(
fn curve_conditional_add<C: Curve>(
&mut self,
p1: &AffinePointTarget<C>,
p2: &AffinePointTarget<C>,
@ -162,7 +210,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
AffinePointTarget { x, y }
}
pub fn curve_scalar_mul<C: Curve>(
fn curve_scalar_mul<C: Curve>(
&mut self,
p: &AffinePointTarget<C>,
n: &NonNativeTarget<C::ScalarField>,
@ -209,17 +257,18 @@ mod tests {
use std::ops::Neg;
use anyhow::Result;
use plonky2::iop::witness::PartialWitness;
use plonky2::plonk::circuit_builder::CircuitBuilder;
use plonky2::plonk::circuit_data::CircuitConfig;
use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
use plonky2_field::field_types::Field;
use plonky2_field::secp256k1_base::Secp256K1Base;
use plonky2_field::secp256k1_scalar::Secp256K1Scalar;
use crate::curve::curve_types::{AffinePoint, Curve, CurveScalar};
use crate::curve::secp256k1::Secp256K1;
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;
use crate::gadgets::curve::CircuitBuilderCurve;
use crate::gadgets::nonnative::CircuitBuilderNonNative;
#[test]
fn test_curve_point_is_valid() -> Result<()> {
@ -242,7 +291,7 @@ mod tests {
let data = builder.build::<C>();
let proof = data.prove(pw).unwrap();
verify(proof, &data.verifier_only, &data.common)
data.verify(proof)
}
#[test]
@ -270,7 +319,7 @@ mod tests {
let data = builder.build::<C>();
let proof = data.prove(pw).unwrap();
verify(proof, &data.verifier_only, &data.common).unwrap();
data.verify(proof).unwrap()
}
#[test]
@ -307,7 +356,7 @@ mod tests {
let data = builder.build::<C>();
let proof = data.prove(pw).unwrap();
verify(proof, &data.verifier_only, &data.common)
data.verify(proof)
}
#[test]
@ -337,7 +386,7 @@ mod tests {
let data = builder.build::<C>();
let proof = data.prove(pw).unwrap();
verify(proof, &data.verifier_only, &data.common)
data.verify(proof)
}
#[test]
@ -369,7 +418,7 @@ mod tests {
let data = builder.build::<C>();
let proof = data.prove(pw).unwrap();
verify(proof, &data.verifier_only, &data.common)
data.verify(proof)
}
#[test]
@ -402,7 +451,7 @@ mod tests {
let data = builder.build::<C>();
let proof = data.prove(pw).unwrap();
verify(proof, &data.verifier_only, &data.common)
data.verify(proof)
}
#[test]
@ -429,6 +478,6 @@ mod tests {
let data = builder.build::<C>();
let proof = data.prove(pw).unwrap();
verify(proof, &data.verifier_only, &data.common)
data.verify(proof)
}
}

View File

@ -0,0 +1,113 @@
use num::BigUint;
use plonky2::hash::hash_types::RichField;
use plonky2::hash::keccak::KeccakHash;
use plonky2::plonk::circuit_builder::CircuitBuilder;
use plonky2::plonk::config::{GenericHashOut, Hasher};
use plonky2_field::extension_field::Extendable;
use plonky2_field::field_types::Field;
use crate::curve::curve_types::{AffinePoint, Curve, CurveScalar};
use crate::gadgets::curve::{AffinePointTarget, CircuitBuilderCurve};
use crate::gadgets::curve_windowed_mul::CircuitBuilderWindowedMul;
use crate::gadgets::nonnative::NonNativeTarget;
use crate::gadgets::split_nonnative::CircuitBuilderSplit;
/// Compute windowed fixed-base scalar multiplication, using a 4-bit window.
pub fn fixed_base_curve_mul_circuit<C: Curve, F: RichField + Extendable<D>, const D: usize>(
builder: &mut CircuitBuilder<F, D>,
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 = builder.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 = builder.zero();
let mut result = builder.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| builder.constant_affine_point(p))
.collect::<Vec<_>>();
let is_zero = builder.is_equal(limb, zero);
let should_add = builder.not(is_zero);
// `r = s_i * P_i`
let r = builder.random_access_curve_points(limb, muls_point);
result = builder.curve_conditional_add(&result, &r, should_add);
}
let to_add = builder.constant_affine_point(-rando);
builder.curve_add(&result, &to_add)
}
#[cfg(test)]
mod tests {
use anyhow::Result;
use plonky2::iop::witness::PartialWitness;
use plonky2::plonk::circuit_builder::CircuitBuilder;
use plonky2::plonk::circuit_data::CircuitConfig;
use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
use plonky2_field::field_types::Field;
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::gadgets::biguint::witness_set_biguint_target;
use crate::gadgets::curve::CircuitBuilderCurve;
use crate::gadgets::curve_fixed_base::fixed_base_curve_mul_circuit;
use crate::gadgets::nonnative::CircuitBuilderNonNative;
#[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>();
witness_set_biguint_target(&mut pw, &n_target.value, &n.to_canonical_biguint());
let res_target = fixed_base_curve_mul_circuit(&mut builder, 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();
data.verify(proof)
}
}

View File

@ -0,0 +1,136 @@
use num::BigUint;
use plonky2::hash::hash_types::RichField;
use plonky2::hash::keccak::KeccakHash;
use plonky2::plonk::circuit_builder::CircuitBuilder;
use plonky2::plonk::config::{GenericHashOut, Hasher};
use plonky2_field::extension_field::Extendable;
use plonky2_field::field_types::Field;
use crate::curve::curve_types::{Curve, CurveScalar};
use crate::gadgets::curve::{AffinePointTarget, CircuitBuilderCurve};
use crate::gadgets::curve_windowed_mul::CircuitBuilderWindowedMul;
use crate::gadgets::nonnative::NonNativeTarget;
use crate::gadgets::split_nonnative::CircuitBuilderSplit;
/// 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_circuit<C: Curve, F: RichField + Extendable<D>, const D: usize>(
builder: &mut CircuitBuilder<F, D>,
p: &AffinePointTarget<C>,
q: &AffinePointTarget<C>,
n: &NonNativeTarget<C::ScalarField>,
m: &NonNativeTarget<C::ScalarField>,
) -> AffinePointTarget<C> {
let limbs_n = builder.split_nonnative_to_2_bit_limbs(n);
let limbs_m = builder.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 = builder.constant_affine_point(rando);
let neg_rando = builder.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 = builder.curve_add(&cur_p, p);
cur_q = builder.curve_add(&cur_q, q);
}
for i in 1..4 {
precomputation[i] = builder.curve_add(&precomputation[i], &neg_rando);
precomputation[4 * i] = builder.curve_add(&precomputation[4 * i], &neg_rando);
}
for i in 1..4 {
for j in 1..4 {
precomputation[i + 4 * j] =
builder.curve_add(&precomputation[i], &precomputation[4 * j]);
}
}
let four = builder.constant(F::from_canonical_usize(4));
let zero = builder.zero();
let mut result = rando_t;
for (limb_n, limb_m) in limbs_n.into_iter().zip(limbs_m).rev() {
result = builder.curve_repeated_double(&result, 2);
let index = builder.mul_add(four, limb_m, limb_n);
let r = builder.random_access_curve_points(index, precomputation.clone());
let is_zero = builder.is_equal(index, zero);
let should_add = builder.not(is_zero);
result = builder.curve_conditional_add(&result, &r, should_add);
}
let starting_point_multiplied = (0..2 * num_limbs).fold(rando, |acc, _| acc.double());
let to_add = builder.constant_affine_point(-starting_point_multiplied);
result = builder.curve_add(&result, &to_add);
result
}
#[cfg(test)]
mod tests {
use anyhow::Result;
use plonky2::iop::witness::PartialWitness;
use plonky2::plonk::circuit_builder::CircuitBuilder;
use plonky2::plonk::circuit_data::CircuitConfig;
use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
use plonky2_field::field_types::Field;
use plonky2_field::secp256k1_scalar::Secp256K1Scalar;
use crate::curve::curve_types::{Curve, CurveScalar};
use crate::curve::secp256k1::Secp256K1;
use crate::gadgets::curve::CircuitBuilderCurve;
use crate::gadgets::curve_msm::curve_msm_circuit;
use crate::gadgets::nonnative::CircuitBuilderNonNative;
#[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 =
curve_msm_circuit(&mut builder, &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();
data.verify(proof)
}
}

View File

@ -1,24 +1,53 @@
use std::marker::PhantomData;
use num::BigUint;
use plonky2::gadgets::arithmetic_u32::U32Target;
use plonky2::hash::hash_types::RichField;
use plonky2::hash::keccak::KeccakHash;
use plonky2::iop::target::{BoolTarget, Target};
use plonky2::plonk::circuit_builder::CircuitBuilder;
use plonky2::plonk::config::{GenericHashOut, Hasher};
use plonky2_field::extension_field::Extendable;
use plonky2_field::field_types::Field;
use crate::curve::curve_types::{Curve, CurveScalar};
use crate::gadgets::arithmetic_u32::U32Target;
use crate::gadgets::biguint::BigUintTarget;
use crate::gadgets::curve::AffinePointTarget;
use crate::gadgets::nonnative::NonNativeTarget;
use crate::hash::hash_types::RichField;
use crate::hash::keccak::KeccakHash;
use crate::iop::target::{BoolTarget, Target};
use crate::plonk::circuit_builder::CircuitBuilder;
use crate::plonk::config::{GenericHashOut, Hasher};
use crate::gadgets::curve::{AffinePointTarget, CircuitBuilderCurve};
use crate::gadgets::nonnative::{CircuitBuilderNonNative, NonNativeTarget};
use crate::gadgets::split_nonnative::CircuitBuilderSplit;
const WINDOW_SIZE: usize = 4;
impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
pub fn precompute_window<C: Curve>(
pub trait CircuitBuilderWindowedMul<F: RichField + Extendable<D>, const D: usize> {
fn precompute_window<C: Curve>(
&mut self,
p: &AffinePointTarget<C>,
) -> Vec<AffinePointTarget<C>>;
fn random_access_curve_points<C: Curve>(
&mut self,
access_index: Target,
v: Vec<AffinePointTarget<C>>,
) -> AffinePointTarget<C>;
fn if_affine_point<C: Curve>(
&mut self,
b: BoolTarget,
p1: &AffinePointTarget<C>,
p2: &AffinePointTarget<C>,
) -> AffinePointTarget<C>;
fn curve_scalar_mul_windowed<C: Curve>(
&mut self,
p: &AffinePointTarget<C>,
n: &NonNativeTarget<C::ScalarField>,
) -> AffinePointTarget<C>;
}
impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilderWindowedMul<F, D>
for CircuitBuilder<F, D>
{
fn precompute_window<C: Curve>(
&mut self,
p: &AffinePointTarget<C>,
) -> Vec<AffinePointTarget<C>> {
@ -39,7 +68,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
multiples
}
pub fn random_access_curve_points<C: Curve>(
fn random_access_curve_points<C: Curve>(
&mut self,
access_index: Target,
v: Vec<AffinePointTarget<C>>,
@ -85,7 +114,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
AffinePointTarget { x, y }
}
pub fn if_affine_point<C: Curve>(
fn if_affine_point<C: Curve>(
&mut self,
b: BoolTarget,
p1: &AffinePointTarget<C>,
@ -96,7 +125,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
AffinePointTarget { x: new_x, y: new_y }
}
pub fn curve_scalar_mul_windowed<C: Curve>(
fn curve_scalar_mul_windowed<C: Curve>(
&mut self,
p: &AffinePointTarget<C>,
n: &NonNativeTarget<C::ScalarField>,
@ -143,17 +172,19 @@ mod tests {
use std::ops::Neg;
use anyhow::Result;
use plonky2::iop::witness::PartialWitness;
use plonky2::plonk::circuit_builder::CircuitBuilder;
use plonky2::plonk::circuit_data::CircuitConfig;
use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
use plonky2_field::field_types::Field;
use plonky2_field::secp256k1_scalar::Secp256K1Scalar;
use rand::Rng;
use crate::curve::curve_types::{Curve, CurveScalar};
use crate::curve::secp256k1::Secp256K1;
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;
use crate::gadgets::curve::CircuitBuilderCurve;
use crate::gadgets::curve_windowed_mul::CircuitBuilderWindowedMul;
use crate::gadgets::nonnative::CircuitBuilderNonNative;
#[test]
fn test_random_access_curve_points() -> Result<()> {
@ -186,10 +217,11 @@ mod tests {
let data = builder.build::<C>();
let proof = data.prove(pw).unwrap();
verify(proof, &data.verifier_only, &data.common)
data.verify(proof)
}
#[test]
#[ignore]
fn test_curve_windowed_mul() -> Result<()> {
const D: usize = 2;
type C = PoseidonGoldilocksConfig;
@ -219,6 +251,6 @@ mod tests {
let data = builder.build::<C>();
let proof = data.prove(pw).unwrap();
verify(proof, &data.verifier_only, &data.common)
data.verify(proof)
}
}

View File

@ -1,14 +1,16 @@
use std::marker::PhantomData;
use plonky2::hash::hash_types::RichField;
use plonky2::plonk::circuit_builder::CircuitBuilder;
use plonky2_field::extension_field::Extendable;
use plonky2_field::secp256k1_scalar::Secp256K1Scalar;
use crate::curve::curve_types::Curve;
use crate::curve::secp256k1::Secp256K1;
use crate::field::extension_field::Extendable;
use crate::gadgets::curve::AffinePointTarget;
use crate::gadgets::nonnative::NonNativeTarget;
use crate::hash::hash_types::RichField;
use crate::plonk::circuit_builder::CircuitBuilder;
use crate::gadgets::curve::{AffinePointTarget, CircuitBuilderCurve};
use crate::gadgets::curve_fixed_base::fixed_base_curve_mul_circuit;
use crate::gadgets::glv::CircuitBuilderGlv;
use crate::gadgets::nonnative::{CircuitBuilderNonNative, NonNativeTarget};
#[derive(Clone, Debug)]
pub struct ECDSASecretKeyTarget<C: Curve>(NonNativeTarget<C::ScalarField>);
@ -22,48 +24,48 @@ pub struct ECDSASignatureTarget<C: Curve> {
pub s: NonNativeTarget<C::ScalarField>,
}
impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
pub fn verify_message(
&mut self,
msg: NonNativeTarget<Secp256K1Scalar>,
sig: ECDSASignatureTarget<Secp256K1>,
pk: ECDSAPublicKeyTarget<Secp256K1>,
) {
let ECDSASignatureTarget { r, s } = sig;
pub fn verify_message_circuit<F: RichField + Extendable<D>, const D: usize>(
builder: &mut CircuitBuilder<F, D>,
msg: NonNativeTarget<Secp256K1Scalar>,
sig: ECDSASignatureTarget<Secp256K1>,
pk: ECDSAPublicKeyTarget<Secp256K1>,
) {
let ECDSASignatureTarget { r, s } = sig;
self.curve_assert_valid(&pk.0);
builder.curve_assert_valid(&pk.0);
let c = self.inv_nonnative(&s);
let u1 = self.mul_nonnative(&msg, &c);
let u2 = self.mul_nonnative(&r, &c);
let c = builder.inv_nonnative(&s);
let u1 = builder.mul_nonnative(&msg, &c);
let u2 = builder.mul_nonnative(&r, &c);
let point1 = self.fixed_base_curve_mul(Secp256K1::GENERATOR_AFFINE, &u1);
let point2 = self.glv_mul(&pk.0, &u2);
let point = self.curve_add(&point1, &point2);
let point1 = fixed_base_curve_mul_circuit(builder, Secp256K1::GENERATOR_AFFINE, &u1);
let point2 = builder.glv_mul(&pk.0, &u2);
let point = builder.curve_add(&point1, &point2);
let x = NonNativeTarget::<Secp256K1Scalar> {
value: point.x.value,
_phantom: PhantomData,
};
self.connect_nonnative(&r, &x);
}
let x = NonNativeTarget::<Secp256K1Scalar> {
value: point.x.value,
_phantom: PhantomData,
};
builder.connect_nonnative(&r, &x);
}
#[cfg(test)]
mod tests {
use anyhow::Result;
use plonky2::iop::witness::PartialWitness;
use plonky2::plonk::circuit_builder::CircuitBuilder;
use plonky2::plonk::circuit_data::CircuitConfig;
use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
use plonky2_field::field_types::Field;
use plonky2_field::secp256k1_scalar::Secp256K1Scalar;
use super::{ECDSAPublicKeyTarget, ECDSASignatureTarget};
use crate::curve::curve_types::{Curve, CurveScalar};
use crate::curve::ecdsa::{sign_message, ECDSAPublicKey, ECDSASecretKey, ECDSASignature};
use crate::curve::secp256k1::Secp256K1;
use crate::field::field_types::Field;
use crate::field::secp256k1_scalar::Secp256K1Scalar;
use crate::gadgets::ecdsa::{ECDSAPublicKeyTarget, ECDSASignatureTarget};
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;
use crate::gadgets::curve::CircuitBuilderCurve;
use crate::gadgets::ecdsa::verify_message_circuit;
use crate::gadgets::nonnative::CircuitBuilderNonNative;
fn test_ecdsa_circuit_with_config(config: CircuitConfig) -> Result<()> {
const D: usize = 2;
@ -93,12 +95,12 @@ mod tests {
s: s_target,
};
builder.verify_message(msg_target, sig_target, pk_target);
verify_message_circuit(&mut builder, msg_target, sig_target, pk_target);
dbg!(builder.num_gates());
let data = builder.build::<C>();
let proof = data.prove(pw).unwrap();
verify(proof, &data.verifier_only, &data.common)
data.verify(proof)
}
#[test]

View File

@ -1,25 +1,50 @@
use std::marker::PhantomData;
use plonky2::hash::hash_types::RichField;
use plonky2::iop::generator::{GeneratedValues, SimpleGenerator};
use plonky2::iop::target::{BoolTarget, Target};
use plonky2::iop::witness::PartitionWitness;
use plonky2::plonk::circuit_builder::CircuitBuilder;
use plonky2_field::extension_field::Extendable;
use plonky2_field::field_types::{Field, PrimeField};
use plonky2_field::secp256k1_base::Secp256K1Base;
use plonky2_field::secp256k1_scalar::Secp256K1Scalar;
use crate::curve::glv::{decompose_secp256k1_scalar, GLV_BETA, GLV_S};
use crate::curve::secp256k1::Secp256K1;
use crate::gadgets::curve::AffinePointTarget;
use crate::gadgets::nonnative::NonNativeTarget;
use crate::hash::hash_types::RichField;
use crate::iop::generator::{GeneratedValues, SimpleGenerator};
use crate::iop::target::{BoolTarget, Target};
use crate::iop::witness::{PartitionWitness, Witness};
use crate::plonk::circuit_builder::CircuitBuilder;
use crate::gadgets::biguint::{buffer_set_biguint_target, witness_get_biguint_target};
use crate::gadgets::curve::{AffinePointTarget, CircuitBuilderCurve};
use crate::gadgets::curve_msm::curve_msm_circuit;
use crate::gadgets::nonnative::{CircuitBuilderNonNative, NonNativeTarget};
impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
pub fn secp256k1_glv_beta(&mut self) -> NonNativeTarget<Secp256K1Base> {
pub trait CircuitBuilderGlv<F: RichField + Extendable<D>, const D: usize> {
fn secp256k1_glv_beta(&mut self) -> NonNativeTarget<Secp256K1Base>;
fn decompose_secp256k1_scalar(
&mut self,
k: &NonNativeTarget<Secp256K1Scalar>,
) -> (
NonNativeTarget<Secp256K1Scalar>,
NonNativeTarget<Secp256K1Scalar>,
BoolTarget,
BoolTarget,
);
fn glv_mul(
&mut self,
p: &AffinePointTarget<Secp256K1>,
k: &NonNativeTarget<Secp256K1Scalar>,
) -> AffinePointTarget<Secp256K1>;
}
impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilderGlv<F, D>
for CircuitBuilder<F, D>
{
fn secp256k1_glv_beta(&mut self) -> NonNativeTarget<Secp256K1Base> {
self.constant_nonnative(GLV_BETA)
}
pub fn decompose_secp256k1_scalar(
fn decompose_secp256k1_scalar(
&mut self,
k: &NonNativeTarget<Secp256K1Scalar>,
) -> (
@ -53,7 +78,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
(k1, k2, k1_neg, k2_neg)
}
pub fn glv_mul(
fn glv_mul(
&mut self,
p: &AffinePointTarget<Secp256K1>,
k: &NonNativeTarget<Secp256K1Scalar>,
@ -69,7 +94,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
let p_neg = self.curve_conditional_neg(p, k1_neg);
let sp_neg = self.curve_conditional_neg(&sp, k2_neg);
self.curve_msm(&p_neg, &sp_neg, &k1, &k2)
curve_msm_circuit(self, &p_neg, &sp_neg, &k1, &k2)
}
}
@ -91,11 +116,15 @@ impl<F: RichField + Extendable<D>, const D: usize> SimpleGenerator<F>
}
fn run_once(&self, witness: &PartitionWitness<F>, out_buffer: &mut GeneratedValues<F>) {
let k = witness.get_nonnative_target(self.k.clone());
let k = Secp256K1Scalar::from_biguint(witness_get_biguint_target(
witness,
self.k.value.clone(),
));
let (k1, k2, k1_neg, k2_neg) = decompose_secp256k1_scalar(k);
out_buffer.set_nonnative_target(self.k1.clone(), k1);
out_buffer.set_nonnative_target(self.k2.clone(), k2);
buffer_set_biguint_target(out_buffer, &self.k1.value, &k1.to_canonical_biguint());
buffer_set_biguint_target(out_buffer, &self.k2.value, &k2.to_canonical_biguint());
out_buffer.set_bool_target(self.k1_neg, k1_neg);
out_buffer.set_bool_target(self.k2_neg, k2_neg);
}
@ -104,19 +133,22 @@ impl<F: RichField + Extendable<D>, const D: usize> SimpleGenerator<F>
#[cfg(test)]
mod tests {
use anyhow::Result;
use plonky2::iop::witness::PartialWitness;
use plonky2::plonk::circuit_builder::CircuitBuilder;
use plonky2::plonk::circuit_data::CircuitConfig;
use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
use plonky2_field::field_types::Field;
use plonky2_field::secp256k1_scalar::Secp256K1Scalar;
use crate::curve::curve_types::{Curve, CurveScalar};
use crate::curve::glv::glv_mul;
use crate::curve::secp256k1::Secp256K1;
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;
use crate::gadgets::curve::CircuitBuilderCurve;
use crate::gadgets::glv::CircuitBuilderGlv;
use crate::gadgets::nonnative::CircuitBuilderNonNative;
#[test]
#[ignore]
fn test_glv_gadget() -> Result<()> {
const D: usize = 2;
type C = PoseidonGoldilocksConfig;
@ -143,6 +175,6 @@ mod tests {
let data = builder.build::<C>();
let proof = data.prove(pw).unwrap();
verify(proof, &data.verifier_only, &data.common)
data.verify(proof)
}
}

9
ecdsa/src/gadgets/mod.rs Normal file
View File

@ -0,0 +1,9 @@
pub mod biguint;
pub mod curve;
pub mod curve_fixed_base;
pub mod curve_msm;
pub mod curve_windowed_mul;
pub mod ecdsa;
pub mod glv;
pub mod nonnative;
pub mod split_nonnative;

View File

@ -1,17 +1,19 @@
use std::marker::PhantomData;
use num::{BigUint, Integer, One, Zero};
use plonky2::gadgets::arithmetic_u32::U32Target;
use plonky2::hash::hash_types::RichField;
use plonky2::iop::generator::{GeneratedValues, SimpleGenerator};
use plonky2::iop::target::{BoolTarget, Target};
use plonky2::iop::witness::PartitionWitness;
use plonky2::plonk::circuit_builder::CircuitBuilder;
use plonky2_field::field_types::PrimeField;
use plonky2_field::{extension_field::Extendable, field_types::Field};
use plonky2_util::ceil_div_usize;
use crate::gadgets::arithmetic_u32::U32Target;
use crate::gadgets::biguint::BigUintTarget;
use crate::hash::hash_types::RichField;
use crate::iop::generator::{GeneratedValues, SimpleGenerator};
use crate::iop::target::{BoolTarget, Target};
use crate::iop::witness::{PartitionWitness, Witness};
use crate::plonk::circuit_builder::CircuitBuilder;
use crate::gadgets::biguint::{
buffer_set_biguint_target, witness_get_biguint_target, BigUintTarget, CircuitBuilderBiguint,
};
#[derive(Clone, Debug)]
pub struct NonNativeTarget<FF: Field> {
@ -19,36 +21,131 @@ pub struct NonNativeTarget<FF: Field> {
pub(crate) _phantom: PhantomData<FF>,
}
impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
pub trait CircuitBuilderNonNative<F: RichField + Extendable<D>, const D: usize> {
fn num_nonnative_limbs<FF: Field>() -> usize {
ceil_div_usize(FF::BITS, 32)
}
pub fn biguint_to_nonnative<FF: Field>(&mut self, x: &BigUintTarget) -> NonNativeTarget<FF> {
fn biguint_to_nonnative<FF: Field>(&mut self, x: &BigUintTarget) -> NonNativeTarget<FF>;
fn nonnative_to_canonical_biguint<FF: Field>(
&mut self,
x: &NonNativeTarget<FF>,
) -> BigUintTarget;
fn constant_nonnative<FF: PrimeField>(&mut self, x: FF) -> NonNativeTarget<FF>;
fn zero_nonnative<FF: PrimeField>(&mut self) -> NonNativeTarget<FF>;
// Assert that two NonNativeTarget's, both assumed to be in reduced form, are equal.
fn connect_nonnative<FF: Field>(
&mut self,
lhs: &NonNativeTarget<FF>,
rhs: &NonNativeTarget<FF>,
);
fn add_virtual_nonnative_target<FF: Field>(&mut self) -> NonNativeTarget<FF>;
fn add_virtual_nonnative_target_sized<FF: Field>(
&mut self,
num_limbs: usize,
) -> NonNativeTarget<FF>;
fn add_nonnative<FF: PrimeField>(
&mut self,
a: &NonNativeTarget<FF>,
b: &NonNativeTarget<FF>,
) -> NonNativeTarget<FF>;
fn mul_nonnative_by_bool<FF: Field>(
&mut self,
a: &NonNativeTarget<FF>,
b: BoolTarget,
) -> NonNativeTarget<FF>;
fn if_nonnative<FF: PrimeField>(
&mut self,
b: BoolTarget,
x: &NonNativeTarget<FF>,
y: &NonNativeTarget<FF>,
) -> NonNativeTarget<FF>;
fn add_many_nonnative<FF: PrimeField>(
&mut self,
to_add: &[NonNativeTarget<FF>],
) -> NonNativeTarget<FF>;
// Subtract two `NonNativeTarget`s.
fn sub_nonnative<FF: PrimeField>(
&mut self,
a: &NonNativeTarget<FF>,
b: &NonNativeTarget<FF>,
) -> NonNativeTarget<FF>;
fn mul_nonnative<FF: PrimeField>(
&mut self,
a: &NonNativeTarget<FF>,
b: &NonNativeTarget<FF>,
) -> NonNativeTarget<FF>;
fn mul_many_nonnative<FF: PrimeField>(
&mut self,
to_mul: &[NonNativeTarget<FF>],
) -> NonNativeTarget<FF>;
fn neg_nonnative<FF: PrimeField>(&mut self, x: &NonNativeTarget<FF>) -> NonNativeTarget<FF>;
fn inv_nonnative<FF: PrimeField>(&mut self, x: &NonNativeTarget<FF>) -> NonNativeTarget<FF>;
/// Returns `x % |FF|` as a `NonNativeTarget`.
fn reduce<FF: Field>(&mut self, x: &BigUintTarget) -> NonNativeTarget<FF>;
fn reduce_nonnative<FF: Field>(&mut self, x: &NonNativeTarget<FF>) -> NonNativeTarget<FF>;
fn bool_to_nonnative<FF: Field>(&mut self, b: &BoolTarget) -> NonNativeTarget<FF>;
// Split a nonnative field element to bits.
fn split_nonnative_to_bits<FF: Field>(&mut self, x: &NonNativeTarget<FF>) -> Vec<BoolTarget>;
fn nonnative_conditional_neg<FF: PrimeField>(
&mut self,
x: &NonNativeTarget<FF>,
b: BoolTarget,
) -> NonNativeTarget<FF>;
}
impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilderNonNative<F, D>
for CircuitBuilder<F, D>
{
fn num_nonnative_limbs<FF: Field>() -> usize {
ceil_div_usize(FF::BITS, 32)
}
fn biguint_to_nonnative<FF: Field>(&mut self, x: &BigUintTarget) -> NonNativeTarget<FF> {
NonNativeTarget {
value: x.clone(),
_phantom: PhantomData,
}
}
pub fn nonnative_to_canonical_biguint<FF: Field>(
fn nonnative_to_canonical_biguint<FF: Field>(
&mut self,
x: &NonNativeTarget<FF>,
) -> BigUintTarget {
x.value.clone()
}
pub fn constant_nonnative<FF: PrimeField>(&mut self, x: FF) -> NonNativeTarget<FF> {
fn constant_nonnative<FF: PrimeField>(&mut self, x: FF) -> NonNativeTarget<FF> {
let x_biguint = self.constant_biguint(&x.to_canonical_biguint());
self.biguint_to_nonnative(&x_biguint)
}
pub fn zero_nonnative<FF: PrimeField>(&mut self) -> NonNativeTarget<FF> {
fn zero_nonnative<FF: PrimeField>(&mut self) -> NonNativeTarget<FF> {
self.constant_nonnative(FF::ZERO)
}
// Assert that two NonNativeTarget's, both assumed to be in reduced form, are equal.
pub fn connect_nonnative<FF: Field>(
fn connect_nonnative<FF: Field>(
&mut self,
lhs: &NonNativeTarget<FF>,
rhs: &NonNativeTarget<FF>,
@ -56,7 +153,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
self.connect_biguint(&lhs.value, &rhs.value);
}
pub fn add_virtual_nonnative_target<FF: Field>(&mut self) -> NonNativeTarget<FF> {
fn add_virtual_nonnative_target<FF: Field>(&mut self) -> NonNativeTarget<FF> {
let num_limbs = Self::num_nonnative_limbs::<FF>();
let value = self.add_virtual_biguint_target(num_limbs);
@ -66,7 +163,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
}
}
pub fn add_virtual_nonnative_target_sized<FF: Field>(
fn add_virtual_nonnative_target_sized<FF: Field>(
&mut self,
num_limbs: usize,
) -> NonNativeTarget<FF> {
@ -78,7 +175,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
}
}
pub fn add_nonnative<FF: PrimeField>(
fn add_nonnative<FF: PrimeField>(
&mut self,
a: &NonNativeTarget<FF>,
b: &NonNativeTarget<FF>,
@ -110,7 +207,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
sum
}
pub fn mul_nonnative_by_bool<FF: Field>(
fn mul_nonnative_by_bool<FF: Field>(
&mut self,
a: &NonNativeTarget<FF>,
b: BoolTarget,
@ -121,7 +218,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
}
}
pub fn if_nonnative<FF: PrimeField>(
fn if_nonnative<FF: PrimeField>(
&mut self,
b: BoolTarget,
x: &NonNativeTarget<FF>,
@ -133,7 +230,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
self.add_nonnative(&maybe_x, &maybe_y)
}
pub fn add_many_nonnative<FF: PrimeField>(
fn add_many_nonnative<FF: PrimeField>(
&mut self,
to_add: &[NonNativeTarget<FF>],
) -> NonNativeTarget<FF> {
@ -177,7 +274,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
}
// Subtract two `NonNativeTarget`s.
pub fn sub_nonnative<FF: PrimeField>(
fn sub_nonnative<FF: PrimeField>(
&mut self,
a: &NonNativeTarget<FF>,
b: &NonNativeTarget<FF>,
@ -205,7 +302,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
diff
}
pub fn mul_nonnative<FF: PrimeField>(
fn mul_nonnative<FF: PrimeField>(
&mut self,
a: &NonNativeTarget<FF>,
b: &NonNativeTarget<FF>,
@ -236,7 +333,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
prod
}
pub fn mul_many_nonnative<FF: PrimeField>(
fn mul_many_nonnative<FF: PrimeField>(
&mut self,
to_mul: &[NonNativeTarget<FF>],
) -> NonNativeTarget<FF> {
@ -245,26 +342,20 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
}
let mut accumulator = self.mul_nonnative(&to_mul[0], &to_mul[1]);
for i in 2..to_mul.len() {
accumulator = self.mul_nonnative(&accumulator, &to_mul[i]);
for t in to_mul.iter().skip(2) {
accumulator = self.mul_nonnative(&accumulator, t);
}
accumulator
}
pub fn neg_nonnative<FF: PrimeField>(
&mut self,
x: &NonNativeTarget<FF>,
) -> NonNativeTarget<FF> {
fn neg_nonnative<FF: PrimeField>(&mut self, x: &NonNativeTarget<FF>) -> NonNativeTarget<FF> {
let zero_target = self.constant_biguint(&BigUint::zero());
let zero_ff = self.biguint_to_nonnative(&zero_target);
self.sub_nonnative(&zero_ff, x)
}
pub fn inv_nonnative<FF: PrimeField>(
&mut self,
x: &NonNativeTarget<FF>,
) -> NonNativeTarget<FF> {
fn inv_nonnative<FF: PrimeField>(&mut self, x: &NonNativeTarget<FF>) -> NonNativeTarget<FF> {
let num_limbs = x.value.num_limbs();
let inv_biguint = self.add_virtual_biguint_target(num_limbs);
let div = self.add_virtual_biguint_target(num_limbs);
@ -302,12 +393,12 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
}
}
pub fn reduce_nonnative<FF: Field>(&mut self, x: &NonNativeTarget<FF>) -> NonNativeTarget<FF> {
fn reduce_nonnative<FF: Field>(&mut self, x: &NonNativeTarget<FF>) -> NonNativeTarget<FF> {
let x_biguint = self.nonnative_to_canonical_biguint(x);
self.reduce(&x_biguint)
}
pub fn bool_to_nonnative<FF: Field>(&mut self, b: &BoolTarget) -> NonNativeTarget<FF> {
fn bool_to_nonnative<FF: Field>(&mut self, b: &BoolTarget) -> NonNativeTarget<FF> {
let limbs = vec![U32Target(b.target)];
let value = BigUintTarget { limbs };
@ -318,10 +409,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
}
// Split a nonnative field element to bits.
pub fn split_nonnative_to_bits<FF: Field>(
&mut self,
x: &NonNativeTarget<FF>,
) -> Vec<BoolTarget> {
fn split_nonnative_to_bits<FF: Field>(&mut self, x: &NonNativeTarget<FF>) -> Vec<BoolTarget> {
let num_limbs = x.value.num_limbs();
let mut result = Vec::with_capacity(num_limbs * 32);
@ -339,7 +427,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
result
}
pub fn nonnative_conditional_neg<FF: PrimeField>(
fn nonnative_conditional_neg<FF: PrimeField>(
&mut self,
x: &NonNativeTarget<FF>,
b: BoolTarget,
@ -377,8 +465,8 @@ impl<F: RichField + Extendable<D>, const D: usize, FF: PrimeField> SimpleGenerat
}
fn run_once(&self, witness: &PartitionWitness<F>, out_buffer: &mut GeneratedValues<F>) {
let a = witness.get_nonnative_target(self.a.clone());
let b = witness.get_nonnative_target(self.b.clone());
let a = FF::from_biguint(witness_get_biguint_target(witness, self.a.value.clone()));
let b = FF::from_biguint(witness_get_biguint_target(witness, self.b.value.clone()));
let a_biguint = a.to_canonical_biguint();
let b_biguint = b.to_canonical_biguint();
let sum_biguint = a_biguint + b_biguint;
@ -389,7 +477,7 @@ impl<F: RichField + Extendable<D>, const D: usize, FF: PrimeField> SimpleGenerat
(false, sum_biguint)
};
out_buffer.set_biguint_target(self.sum.value.clone(), sum_reduced);
buffer_set_biguint_target(out_buffer, &self.sum.value, &sum_reduced);
out_buffer.set_bool_target(self.overflow, overflow);
}
}
@ -417,7 +505,9 @@ impl<F: RichField + Extendable<D>, const D: usize, FF: PrimeField> SimpleGenerat
let summands: Vec<_> = self
.summands
.iter()
.map(|summand| witness.get_nonnative_target(summand.clone()))
.map(|summand| {
FF::from_biguint(witness_get_biguint_target(witness, summand.value.clone()))
})
.collect();
let summand_biguints: Vec<_> = summands
.iter()
@ -432,7 +522,7 @@ impl<F: RichField + Extendable<D>, const D: usize, FF: PrimeField> SimpleGenerat
let (overflow_biguint, sum_reduced) = sum_biguint.div_rem(&modulus);
let overflow = overflow_biguint.to_u64_digits()[0] as u32;
out_buffer.set_biguint_target(self.sum.value.clone(), sum_reduced);
buffer_set_biguint_target(out_buffer, &self.sum.value, &sum_reduced);
out_buffer.set_u32_target(self.overflow, overflow);
}
}
@ -461,8 +551,8 @@ impl<F: RichField + Extendable<D>, const D: usize, FF: PrimeField> SimpleGenerat
}
fn run_once(&self, witness: &PartitionWitness<F>, out_buffer: &mut GeneratedValues<F>) {
let a = witness.get_nonnative_target(self.a.clone());
let b = witness.get_nonnative_target(self.b.clone());
let a = FF::from_biguint(witness_get_biguint_target(witness, self.a.value.clone()));
let b = FF::from_biguint(witness_get_biguint_target(witness, self.b.value.clone()));
let a_biguint = a.to_canonical_biguint();
let b_biguint = b.to_canonical_biguint();
@ -473,7 +563,7 @@ impl<F: RichField + Extendable<D>, const D: usize, FF: PrimeField> SimpleGenerat
(modulus + a_biguint - b_biguint, true)
};
out_buffer.set_biguint_target(self.diff.value.clone(), diff_biguint);
buffer_set_biguint_target(out_buffer, &self.diff.value, &diff_biguint);
out_buffer.set_bool_target(self.overflow, overflow);
}
}
@ -502,8 +592,8 @@ impl<F: RichField + Extendable<D>, const D: usize, FF: PrimeField> SimpleGenerat
}
fn run_once(&self, witness: &PartitionWitness<F>, out_buffer: &mut GeneratedValues<F>) {
let a = witness.get_nonnative_target(self.a.clone());
let b = witness.get_nonnative_target(self.b.clone());
let a = FF::from_biguint(witness_get_biguint_target(witness, self.a.value.clone()));
let b = FF::from_biguint(witness_get_biguint_target(witness, self.b.value.clone()));
let a_biguint = a.to_canonical_biguint();
let b_biguint = b.to_canonical_biguint();
@ -512,8 +602,8 @@ impl<F: RichField + Extendable<D>, const D: usize, FF: PrimeField> SimpleGenerat
let modulus = FF::order();
let (overflow_biguint, prod_reduced) = prod_biguint.div_rem(&modulus);
out_buffer.set_biguint_target(self.prod.value.clone(), prod_reduced);
out_buffer.set_biguint_target(self.overflow.clone(), overflow_biguint);
buffer_set_biguint_target(out_buffer, &self.prod.value, &prod_reduced);
buffer_set_biguint_target(out_buffer, &self.overflow, &overflow_biguint);
}
}
@ -533,7 +623,7 @@ impl<F: RichField + Extendable<D>, const D: usize, FF: PrimeField> SimpleGenerat
}
fn run_once(&self, witness: &PartitionWitness<F>, out_buffer: &mut GeneratedValues<F>) {
let x = witness.get_nonnative_target(self.x.clone());
let x = FF::from_biguint(witness_get_biguint_target(witness, self.x.value.clone()));
let inv = x.inverse();
let x_biguint = x.to_canonical_biguint();
@ -542,22 +632,22 @@ impl<F: RichField + Extendable<D>, const D: usize, FF: PrimeField> SimpleGenerat
let modulus = FF::order();
let (div, _rem) = prod.div_rem(&modulus);
out_buffer.set_biguint_target(self.div.clone(), div);
out_buffer.set_biguint_target(self.inv.clone(), inv_biguint);
buffer_set_biguint_target(out_buffer, &self.div, &div);
buffer_set_biguint_target(out_buffer, &self.inv, &inv_biguint);
}
}
#[cfg(test)]
mod tests {
use anyhow::Result;
use plonky2::iop::witness::PartialWitness;
use plonky2::plonk::circuit_builder::CircuitBuilder;
use plonky2::plonk::circuit_data::CircuitConfig;
use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
use plonky2_field::field_types::{Field, PrimeField};
use plonky2_field::secp256k1_base::Secp256K1Base;
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;
use crate::gadgets::nonnative::CircuitBuilderNonNative;
#[test]
fn test_nonnative_add() -> Result<()> {
@ -583,7 +673,7 @@ mod tests {
let data = builder.build::<C>();
let proof = data.prove(pw).unwrap();
verify(proof, &data.verifier_only, &data.common)
data.verify(proof)
}
#[test]
@ -623,7 +713,7 @@ mod tests {
let data = builder.build::<C>();
let proof = data.prove(pw).unwrap();
verify(proof, &data.verifier_only, &data.common)
data.verify(proof)
}
#[test]
@ -653,7 +743,7 @@ mod tests {
let data = builder.build::<C>();
let proof = data.prove(pw).unwrap();
verify(proof, &data.verifier_only, &data.common)
data.verify(proof)
}
#[test]
@ -679,7 +769,7 @@ mod tests {
let data = builder.build::<C>();
let proof = data.prove(pw).unwrap();
verify(proof, &data.verifier_only, &data.common)
data.verify(proof)
}
#[test]
@ -703,7 +793,7 @@ mod tests {
let data = builder.build::<C>();
let proof = data.prove(pw).unwrap();
verify(proof, &data.verifier_only, &data.common)
data.verify(proof)
}
#[test]
@ -727,6 +817,6 @@ mod tests {
let data = builder.build::<C>();
let proof = data.prove(pw).unwrap();
verify(proof, &data.verifier_only, &data.common)
data.verify(proof)
}
}

View File

@ -1,18 +1,40 @@
use std::marker::PhantomData;
use itertools::Itertools;
use plonky2::gadgets::arithmetic_u32::U32Target;
use plonky2::hash::hash_types::RichField;
use plonky2::iop::target::Target;
use plonky2::plonk::circuit_builder::CircuitBuilder;
use plonky2_field::extension_field::Extendable;
use plonky2_field::field_types::Field;
use crate::gadgets::arithmetic_u32::U32Target;
use crate::gadgets::biguint::BigUintTarget;
use crate::gadgets::nonnative::NonNativeTarget;
use crate::hash::hash_types::RichField;
use crate::iop::target::Target;
use crate::plonk::circuit_builder::CircuitBuilder;
impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
pub fn split_u32_to_4_bit_limbs(&mut self, val: U32Target) -> Vec<Target> {
pub trait CircuitBuilderSplit<F: RichField + Extendable<D>, const D: usize> {
fn split_u32_to_4_bit_limbs(&mut self, val: U32Target) -> Vec<Target>;
fn split_nonnative_to_4_bit_limbs<FF: Field>(
&mut self,
val: &NonNativeTarget<FF>,
) -> Vec<Target>;
fn split_nonnative_to_2_bit_limbs<FF: Field>(
&mut self,
val: &NonNativeTarget<FF>,
) -> Vec<Target>;
// Note: assumes its inputs are 4-bit limbs, and does not range-check.
fn recombine_nonnative_4_bit_limbs<FF: Field>(
&mut self,
limbs: Vec<Target>,
) -> NonNativeTarget<FF>;
}
impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilderSplit<F, D>
for CircuitBuilder<F, D>
{
fn split_u32_to_4_bit_limbs(&mut self, val: U32Target) -> Vec<Target> {
let two_bit_limbs = self.split_le_base::<4>(val.0, 16);
let four = self.constant(F::from_canonical_usize(4));
let combined_limbs = two_bit_limbs
@ -24,7 +46,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
combined_limbs
}
pub fn split_nonnative_to_4_bit_limbs<FF: Field>(
fn split_nonnative_to_4_bit_limbs<FF: Field>(
&mut self,
val: &NonNativeTarget<FF>,
) -> Vec<Target> {
@ -35,7 +57,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
.collect()
}
pub fn split_nonnative_to_2_bit_limbs<FF: Field>(
fn split_nonnative_to_2_bit_limbs<FF: Field>(
&mut self,
val: &NonNativeTarget<FF>,
) -> Vec<Target> {
@ -47,7 +69,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
}
// Note: assumes its inputs are 4-bit limbs, and does not range-check.
pub fn recombine_nonnative_4_bit_limbs<FF: Field>(
fn recombine_nonnative_4_bit_limbs<FF: Field>(
&mut self,
limbs: Vec<Target>,
) -> NonNativeTarget<FF> {
@ -74,15 +96,15 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
#[cfg(test)]
mod tests {
use anyhow::Result;
use plonky2::iop::witness::PartialWitness;
use plonky2::plonk::circuit_builder::CircuitBuilder;
use plonky2::plonk::circuit_data::CircuitConfig;
use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
use plonky2_field::field_types::Field;
use plonky2_field::secp256k1_scalar::Secp256K1Scalar;
use crate::gadgets::nonnative::NonNativeTarget;
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;
use crate::gadgets::nonnative::{CircuitBuilderNonNative, NonNativeTarget};
use crate::gadgets::split_nonnative::CircuitBuilderSplit;
#[test]
fn test_split_nonnative() -> Result<()> {
@ -104,6 +126,6 @@ mod tests {
let data = builder.build::<C>();
let proof = data.prove(pw).unwrap();
verify(proof, &data.verifier_only, &data.common)
data.verify(proof)
}
}

4
ecdsa/src/lib.rs Normal file
View File

@ -0,0 +1,4 @@
#![allow(clippy::needless_range_loop)]
pub mod curve;
pub mod gadgets;

View File

@ -1,110 +0,0 @@
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

@ -1,133 +0,0 @@
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

@ -1,21 +1,12 @@
pub mod arithmetic;
pub mod arithmetic_extension;
pub mod arithmetic_u32;
pub mod biguint;
pub mod curve;
pub mod curve_fixed_base;
pub mod curve_msm;
pub mod curve_windowed_mul;
pub mod ecdsa;
pub mod glv;
pub mod hash;
pub mod interpolation;
pub mod multiple_comparison;
pub mod nonnative;
pub mod polynomial;
pub mod random_access;
pub mod range_check;
pub mod select;
pub mod split_base;
pub(crate) mod split_join;
pub mod split_nonnative;

View File

@ -1,13 +1,10 @@
use std::fmt::Debug;
use std::marker::PhantomData;
use num::BigUint;
use plonky2_field::extension_field::{Extendable, FieldExtension};
use plonky2_field::field_types::{Field, PrimeField};
use plonky2_field::field_types::Field;
use crate::gadgets::arithmetic_u32::U32Target;
use crate::gadgets::biguint::BigUintTarget;
use crate::gadgets::nonnative::NonNativeTarget;
use crate::hash::hash_types::{HashOut, HashOutTarget, RichField};
use crate::iop::ext_target::ExtensionTarget;
use crate::iop::target::{BoolTarget, Target};
@ -169,21 +166,6 @@ impl<F: Field> GeneratedValues<F> {
self.set_target(target.0, F::from_canonical_u32(value))
}
pub fn set_biguint_target(&mut self, target: BigUintTarget, value: BigUint) {
let mut limbs = value.to_u32_digits();
assert!(target.num_limbs() >= limbs.len());
limbs.resize(target.num_limbs(), 0);
for i in 0..target.num_limbs() {
self.set_u32_target(target.get_limb(i), limbs[i]);
}
}
pub fn set_nonnative_target<FF: PrimeField>(&mut self, target: NonNativeTarget<FF>, value: FF) {
self.set_biguint_target(target.value, value.to_canonical_biguint())
}
pub fn set_hash_target(&mut self, ht: HashOutTarget, value: HashOut<F>) {
ht.elements
.iter()

View File

@ -1,16 +1,12 @@
use std::collections::HashMap;
use std::iter::repeat;
use itertools::Itertools;
use num::{BigUint, FromPrimitive, Zero};
use plonky2_field::extension_field::{Extendable, FieldExtension};
use plonky2_field::field_types::{Field, PrimeField};
use plonky2_field::field_types::Field;
use crate::fri::structure::{FriOpenings, FriOpeningsTarget};
use crate::fri::witness_util::set_fri_proof_target;
use crate::gadgets::arithmetic_u32::U32Target;
use crate::gadgets::biguint::BigUintTarget;
use crate::gadgets::nonnative::NonNativeTarget;
use crate::hash::hash_types::HashOutTarget;
use crate::hash::hash_types::RichField;
use crate::hash::hash_types::{HashOut, MerkleCapTarget};
@ -64,30 +60,6 @@ pub trait Witness<F: Field> {
panic!("not a bool")
}
fn get_biguint_target(&self, target: BigUintTarget) -> BigUint
where
F: PrimeField,
{
let mut result = BigUint::zero();
let limb_base = BigUint::from_u64(1 << 32u64).unwrap();
for i in (0..target.num_limbs()).rev() {
let limb = target.get_limb(i);
result *= &limb_base;
result += self.get_target(limb.0).to_canonical_biguint();
}
result
}
fn get_nonnative_target<FF: PrimeField>(&self, target: NonNativeTarget<FF>) -> FF
where
F: PrimeField,
{
let val = self.get_biguint_target(target.value);
FF::from_biguint(val)
}
fn get_hash_target(&self, ht: HashOutTarget) -> HashOut<F> {
HashOut {
elements: self.get_targets(&ht.elements).try_into().unwrap(),
@ -160,16 +132,6 @@ pub trait Witness<F: Field> {
self.set_target(target.0, F::from_canonical_u32(value))
}
fn set_biguint_target(&mut self, target: &BigUintTarget, value: &BigUint) {
for (&lt, l) in target
.limbs
.iter()
.zip(value.to_u32_digits().into_iter().chain(repeat(0)))
{
self.set_u32_target(lt, l);
}
}
/// Set the targets in a `ProofWithPublicInputsTarget` to their corresponding values in a
/// `ProofWithPublicInputs`.
fn set_proof_with_pis_target<C: GenericConfig<D, F = F>, const D: usize>(

View File

@ -12,7 +12,6 @@
pub use plonky2_field as field;
pub mod curve;
pub mod fri;
pub mod gadgets;
pub mod gates;