ECDSA merge

This commit is contained in:
Nicholas Ward 2022-01-20 16:06:00 -08:00
parent 9cac6d3a4d
commit 08fa4031ba
8 changed files with 370 additions and 19 deletions

View File

@ -4,6 +4,7 @@ use crate::gates::arithmetic_u32::U32ArithmeticGate;
use crate::gates::subtraction_u32::U32SubtractionGate;
use crate::hash::hash_types::RichField;
use crate::iop::target::Target;
use crate::iop::witness::{PartitionWitness, Witness};
use crate::plonk::circuit_builder::CircuitBuilder;
#[derive(Clone, Copy, Debug)]
@ -152,4 +153,85 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
(output_result, output_borrow)
}
pub fn split_to_u32(&mut self, x: Target) -> (U32Target, U32Target) {
let low = self.add_virtual_u32_target();
let high = self.add_virtual_u32_target();
let base = self.constant(F::from_canonical_u64(1u64 << 32));
let combined = self.mul_add(high.0, base, low.0);
self.connect(x, combined);
self.add_simple_generator(SplitToU32Generator::<F, D> {
x: x.clone(),
low: low.clone(),
high: high.clone(),
_phantom: PhantomData,
});
(low, high)
}
}
#[derive(Debug)]
struct SplitToU32Generator<F: RichField + Extendable<D>, const D: usize> {
x: Target,
low: U32Target,
high: U32Target,
_phantom: PhantomData<F>,
}
impl<F: RichField + Extendable<D>, const D: usize> SimpleGenerator<F>
for SplitToU32Generator<F, D>
{
fn dependencies(&self) -> Vec<Target> {
vec![self.x]
}
fn run_once(&self, witness: &PartitionWitness<F>, out_buffer: &mut GeneratedValues<F>) {
let x = witness.get_target(self.x.clone());
let x_u64 = x.to_canonical_u64();
let low = x_u64 as u32;
let high: u32 = (x_u64 >> 32).try_into().unwrap();
println!("LOW: {}", low);
println!("HIGH: {}", high);
out_buffer.set_u32_target(self.low.clone(), low);
out_buffer.set_u32_target(self.high.clone(), high);
}
}
#[cfg(test)]
mod tests {
use anyhow::Result;
use rand::{thread_rng, Rng};
use crate::field::goldilocks_field::GoldilocksField;
use crate::iop::witness::PartialWitness;
use crate::plonk::circuit_builder::CircuitBuilder;
use crate::plonk::circuit_data::CircuitConfig;
use crate::plonk::verifier::verify;
#[test]
pub fn test_add_many_u32s() -> Result<()> {
type F = GoldilocksField;
const D: usize = 4;
let config = CircuitConfig::standard_recursion_config();
let pw = PartialWitness::new();
let mut builder = CircuitBuilder::<F, D>::new(config);
let mut rng = thread_rng();
let mut to_add = Vec::new();
for _ in 0..10 {
to_add.push(builder.constant_u32(rng.gen()));
}
let _ = builder.add_many_u32(&to_add);
let data = builder.build();
let proof = data.prove(pw).unwrap();
verify(proof, &data.verifier_only, &data.common)
}
}

View File

@ -2,6 +2,7 @@ pub mod arithmetic;
pub mod arithmetic_extension;
pub mod arithmetic_u32;
pub mod biguint;
pub mod binary_arithmetic;
pub mod curve;
pub mod ecdsa;
pub mod hash;

View File

@ -60,8 +60,13 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
/// Helper function for comparing, specifically, lists of `U32Target`s.
pub fn list_le_u32(&mut self, a: Vec<U32Target>, b: Vec<U32Target>) -> BoolTarget {
let a_targets = a.iter().map(|&t| t.0).collect();
let b_targets = b.iter().map(|&t| t.0).collect();
// let a_targets = a.iter().map(|&t| t.0).collect();
// let b_targets = b.iter().map(|&t| t.0).collect();
// self.list_le(a_targets, b_targets, 32)
let num = a.len() / 2;
let a_targets = self.add_virtual_targets(num);
let b_targets = self.add_virtual_targets(num);
self.list_le(a_targets, b_targets, 32)
}
}

View File

@ -39,6 +39,10 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
self.biguint_to_nonnative(&x_biguint)
}
pub fn zero_nonnative<FF: Field>(&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>(
&mut self,
@ -70,6 +74,22 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
self.reduce(&result)
}
pub fn add_many_nonnative<FF: Field>(
&mut self,
to_add: &[NonNativeTarget<FF>],
) -> NonNativeTarget<FF> {
if to_add.len() == 1 {
return to_add[0].clone();
}
let mut result = self.add_biguint(&to_add[0].value, &to_add[1].value);
for i in 2..to_add.len() {
result = self.add_biguint(&result, &to_add[i].value);
}
self.reduce(&result)
}
// Subtract two `NonNativeTarget`s.
pub fn sub_nonnative<FF: Field>(
&mut self,
@ -94,6 +114,22 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
self.reduce(&result)
}
pub fn mul_many_nonnative<FF: Field>(
&mut self,
to_mul: &[NonNativeTarget<FF>],
) -> NonNativeTarget<FF> {
if to_mul.len() == 1 {
return to_mul[0].clone();
}
let mut result = self.mul_biguint(&to_mul[0].value, &to_mul[1].value);
for i in 2..to_mul.len() {
result = self.mul_biguint(&result, &to_mul[i].value);
}
self.reduce(&result)
}
pub fn neg_nonnative<FF: Field>(&mut self, x: &NonNativeTarget<FF>) -> NonNativeTarget<FF> {
let zero_target = self.constant_biguint(&BigUint::zero());
let zero_ff = self.biguint_to_nonnative(&zero_target);
@ -104,21 +140,27 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
pub fn inv_nonnative<FF: Field>(&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);
self.add_simple_generator(NonNativeInverseGenerator::<F, D, FF> {
x: x.clone(),
inv: inv_biguint.clone(),
div: div.clone(),
_phantom: PhantomData,
});
let product = self.mul_biguint(&x.value, &inv_biguint);
let modulus = self.constant_biguint(&FF::order());
let mod_times_div = self.mul_biguint(&modulus, &div);
let one = self.constant_biguint(&BigUint::one());
let expected_product = self.add_biguint(&mod_times_div, &one);
self.connect_biguint(&product, &expected_product);
let inv = NonNativeTarget::<FF> {
value: inv_biguint,
_phantom: PhantomData,
};
self.add_simple_generator(NonNativeInverseGenerator::<F, D, FF> {
x: x.clone(),
inv: inv.clone(),
_phantom: PhantomData,
});
let product = self.mul_nonnative(x, &inv);
let one = self.constant_nonnative(FF::ONE);
self.connect_nonnative(&product, &one);
inv
}
@ -138,10 +180,70 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
/// Returns `x % |FF|` as a `NonNativeTarget`.
fn reduce<FF: Field>(&mut self, x: &BigUintTarget) -> NonNativeTarget<FF> {
println!("NUM LIMBS: {}", x.limbs.len());
let before = self.num_gates();
let modulus = FF::order();
let order_target = self.constant_biguint(&modulus);
let value = self.rem_biguint(x, &order_target);
println!("NUMBER OF GATES: {}", self.num_gates() - before);
println!("OUTPUT LIMBS: {}", value.limbs.len());
NonNativeTarget {
value,
_phantom: PhantomData,
}
}
/// Returns `x % |FF|` as a `NonNativeTarget`.
fn reduce_by_bits<FF: Field>(&mut self, x: &BigUintTarget) -> NonNativeTarget<FF> {
println!("NUM LIMBS: {}", x.limbs.len());
let before = self.num_gates();
let mut powers_of_two = Vec::new();
let mut cur_power_of_two = FF::ONE;
let two = FF::TWO;
let mut max_num_limbs = 0;
for _ in 0..(x.limbs.len() * 32) {
let cur_power = self.constant_biguint(&cur_power_of_two.to_biguint());
max_num_limbs = max_num_limbs.max(cur_power.limbs.len());
powers_of_two.push(cur_power.limbs);
cur_power_of_two *= two;
}
let mut result_limbs_unreduced = vec![self.zero(); max_num_limbs];
for i in 0..x.limbs.len() {
let this_limb = x.limbs[i];
let bits = self.split_le(this_limb.0, 32);
for b in 0..bits.len() {
let this_power = powers_of_two[32 * i + b].clone();
for x in 0..this_power.len() {
result_limbs_unreduced[x] = self.mul_add(bits[b].target, this_power[x].0, result_limbs_unreduced[x]);
}
}
}
let mut result_limbs_reduced = Vec::new();
let mut carry = self.zero_u32();
for i in 0..result_limbs_unreduced.len() {
println!("{}", i);
let (low, high) = self.split_to_u32(result_limbs_unreduced[i]);
let (cur, overflow) = self.add_u32(carry, low);
let (new_carry, _) = self.add_many_u32(&[overflow, high, carry]);
result_limbs_reduced.push(cur);
carry = new_carry;
}
result_limbs_reduced.push(carry);
let value = BigUintTarget {
limbs: result_limbs_reduced,
};
println!("NUMBER OF GATES: {}", self.num_gates() - before);
println!("OUTPUT LIMBS: {}", value.limbs.len());
NonNativeTarget {
value,
_phantom: PhantomData,
@ -190,7 +292,8 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
#[derive(Debug)]
struct NonNativeInverseGenerator<F: RichField + Extendable<D>, const D: usize, FF: Field> {
x: NonNativeTarget<FF>,
inv: NonNativeTarget<FF>,
inv: BigUintTarget,
div: BigUintTarget,
_phantom: PhantomData<F>,
}
@ -205,7 +308,14 @@ impl<F: RichField + Extendable<D>, const D: usize, FF: Field> SimpleGenerator<F>
let x = witness.get_nonnative_target(self.x.clone());
let inv = x.inverse();
out_buffer.set_nonnative_target(self.inv.clone(), inv);
let x_biguint = x.to_biguint();
let inv_biguint = inv.to_biguint();
let prod = x_biguint * &inv_biguint;
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);
}
}
@ -247,6 +357,43 @@ mod tests {
verify(proof, &data.verifier_only, &data.common)
}
#[test]
fn test_nonnative_many_adds() -> Result<()> {
type FF = Secp256K1Base;
let a_ff = FF::rand();
let b_ff = FF::rand();
let c_ff = FF::rand();
let d_ff = FF::rand();
let e_ff = FF::rand();
let f_ff = FF::rand();
let g_ff = FF::rand();
let h_ff = FF::rand();
let sum_ff = a_ff + b_ff + c_ff + d_ff + e_ff + f_ff + g_ff + h_ff;
type F = GoldilocksField;
let config = CircuitConfig::standard_recursion_config();
let pw = PartialWitness::new();
let mut builder = CircuitBuilder::<F, 4>::new(config);
let a = builder.constant_nonnative(a_ff);
let b = builder.constant_nonnative(b_ff);
let c = builder.constant_nonnative(c_ff);
let d = builder.constant_nonnative(d_ff);
let e = builder.constant_nonnative(e_ff);
let f = builder.constant_nonnative(f_ff);
let g = builder.constant_nonnative(g_ff);
let h = builder.constant_nonnative(h_ff);
let all = [a, b, c, d, e, f, g, h];
let sum = builder.add_many_nonnative(&all);
let sum_expected = builder.constant_nonnative(sum_ff);
builder.connect_nonnative(&sum, &sum_expected);
let data = builder.build();
let proof = data.prove(pw).unwrap();
verify(proof, &data.verifier_only, &data.common)
}
#[test]
fn test_nonnative_sub() -> Result<()> {
type FF = Secp256K1Base;
@ -285,6 +432,7 @@ mod tests {
let x_ff = FF::rand();
let y_ff = FF::rand();
let product_ff = x_ff * y_ff;
println!("PRODUCT FF: {:?}", product_ff);
let config = CircuitConfig::standard_recursion_config();
let pw = PartialWitness::new();
@ -302,6 +450,38 @@ mod tests {
verify(proof, &data.verifier_only, &data.common)
}
fn test_nonnative_many_muls_helper(num: usize) {
type FF = Secp256K1Base;
type F = GoldilocksField;
let config = CircuitConfig::standard_recursion_config();
let mut unop_builder = CircuitBuilder::<F, 4>::new(config.clone());
let mut op_builder = CircuitBuilder::<F, 4>::new(config);
println!("NUM: {}", num);
let ffs: Vec<_> = (0..num).map(|_| FF::rand()).collect();
let op_targets: Vec<_> = ffs.iter().map(|&x| op_builder.constant_nonnative(x)).collect();
op_builder.mul_many_nonnative(&op_targets);
println!("OPTIMIZED GATE COUNT: {}", op_builder.num_gates());
let unop_targets: Vec<_> = ffs.iter().map(|&x| unop_builder.constant_nonnative(x)).collect();
let mut result = unop_targets[0].clone();
for i in 1..unop_targets.len() {
result = unop_builder.mul_nonnative(&result, &unop_targets[i]);
}
println!("UNOPTIMIZED GATE COUNT: {}", unop_builder.num_gates());
}
#[test]
fn test_nonnative_many_muls() {
for num in 2..10 {
test_nonnative_many_muls_helper(num);
}
}
#[test]
fn test_nonnative_neg() -> Result<()> {
type FF = Secp256K1Base;

View File

@ -3,6 +3,8 @@
pub mod arithmetic_base;
pub mod arithmetic_extension;
pub mod binary_arithmetic;
pub mod binary_subtraction;
pub mod arithmetic_u32;
pub mod assert_le;
pub mod base_sum;

View File

@ -7,6 +7,7 @@ use plonky2_field::field_types::Field;
use crate::gadgets::arithmetic_u32::U32Target;
use crate::gadgets::biguint::BigUintTarget;
use crate::gadgets::binary_arithmetic::BinaryTarget;
use crate::gadgets::nonnative::NonNativeTarget;
use crate::hash::hash_types::{HashOut, HashOutTarget, RichField};
use crate::iop::ext_target::ExtensionTarget;
@ -161,10 +162,14 @@ impl<F: Field> GeneratedValues<F> {
self.target_values.push((target, value))
}
fn set_u32_target(&mut self, target: U32Target, value: u32) {
pub fn set_u32_target(&mut self, target: U32Target, value: u32) {
self.set_target(target.0, F::from_canonical_u32(value))
}
pub fn set_binary_target<const BITS: usize>(&mut self, target: BinaryTarget<BITS>, value: F) {
self.set_target(target.0, 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());

View File

@ -19,6 +19,8 @@ use crate::gadgets::polynomial::PolynomialCoeffsExtTarget;
use crate::gates::arithmetic_base::ArithmeticGate;
use crate::gates::arithmetic_extension::ArithmeticExtensionGate;
use crate::gates::arithmetic_u32::U32ArithmeticGate;
use crate::gates::binary_arithmetic::BinaryArithmeticGate;
use crate::gates::binary_subtraction::BinarySubtractionGate;
use crate::gates::constant::ConstantGate;
use crate::gates::gate::{Gate, GateInstance, GateRef, PrefixedGate};
use crate::gates::gate_tree::Tree;
@ -222,6 +224,11 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
let gate_ref = GateRef::new(gate_type);
self.gates.insert(gate_ref.clone());
/*println!("ADDING GATE {}: {:?}", index, gate_ref);
if index == 145 {
panic!();
}*/
self.gate_instances.push(GateInstance {
gate_ref,
constants,
@ -346,6 +353,11 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
U32Target(self.constant(F::from_canonical_u32(c)))
}
/// Returns a BinaryTarget for the value `c`, which is assumed to be at most BITS bits.
pub fn constant_binary<const BITS: usize>(&mut self, c: F) -> BinaryTarget<BITS> {
BinaryTarget(self.constant(c))
}
/// If the given target is a constant (i.e. it was created by the `constant(F)` method), returns
/// its constant value. Otherwise, returns `None`.
pub fn target_as_constant(&self, target: Target) -> Option<F> {
@ -818,10 +830,14 @@ pub struct BatchedGates<F: RichField + Extendable<D>, const D: usize> {
/// The `U32ArithmeticGate` currently being filled (so new u32 arithmetic operations will be added to this gate before creating a new one)
pub(crate) current_u32_arithmetic_gate: Option<(usize, usize)>,
/// The `U32SubtractionGate` currently being filled (so new u32 subtraction operations will be added to this gate before creating a new one)
pub(crate) current_u32_subtraction_gate: Option<(usize, usize)>,
/// A map `b -> (g, i)` from `b` bits to an available `BinaryArithmeticGate` for number of bits `b`.
pub(crate) free_binary_arithmetic_gate: HashMap<usize, (usize, usize)>,
/// A map `b -> (g, i)` from `b` bits to an available `BinarySubtractionGate` for number of bits `b`.
pub(crate) free_binary_subtraction_gate: HashMap<usize, (usize, usize)>,
/// An available `ConstantGate` instance, if any.
pub(crate) free_constant: Option<(usize, usize)>,
}
@ -836,6 +852,8 @@ impl<F: RichField + Extendable<D>, const D: usize> BatchedGates<F, D> {
current_switch_gates: Vec::new(),
current_u32_arithmetic_gate: None,
current_u32_subtraction_gate: None,
free_binary_arithmetic_gate: HashMap::new(),
free_binary_subtraction_gate: HashMap::new(),
free_constant: None,
}
}
@ -931,8 +949,8 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
(gate, i)
}
/// Finds the last available random access gate with the given `vec_size` or add one if there aren't any.
/// Returns `(g,i)` such that there is a random access gate with the given `vec_size` at index
/// Finds the last available random access gate with the given `bits` or add one if there aren't any.
/// Returns `(g,i)` such that there is a random access gate for the given `bits` at index
/// `g` and the gate's `i`-th random access is available.
pub(crate) fn find_random_access_gate(&mut self, bits: usize) -> (usize, usize) {
let (gate, i) = self
@ -1031,6 +1049,64 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
(gate_index, copy)
}
/// Finds the last available binary arithmetic with the given `bits` or add one if there aren't any.
/// Returns `(g,i)` such that there is a binary arithmetic for the given `bits` at index
/// `g` and the gate's `i`-th copy is available.
pub(crate) fn find_binary_arithmetic_gate<const BITS: usize>(&mut self) -> (usize, usize) {
let (gate, i) = self
.batched_gates
.free_binary_arithmetic_gate
.get(&BITS)
.copied()
.unwrap_or_else(|| {
let gate = self.add_gate(
BinaryArithmeticGate::<F, D, BITS>::new_from_config(&self.config),
vec![],
);
(gate, 0)
});
// Update `free_binary_arithmetic` with new values.
if i + 1 < BinaryArithmeticGate::<F, D, BITS>::new_from_config(&self.config).num_ops {
self.batched_gates
.free_random_access
.insert(BITS, (gate, i + 1));
} else {
self.batched_gates.free_random_access.remove(&BITS);
}
(gate, i)
}
/// Finds the last available binary subtraction with the given `bits` or add one if there aren't any.
/// Returns `(g,i)` such that there is a binary subtraction for the given `bits` at index
/// `g` and the gate's `i`-th copy is available.
pub(crate) fn find_binary_subtraction_gate<const BITS: usize>(&mut self) -> (usize, usize) {
let (gate, i) = self
.batched_gates
.free_binary_subtraction_gate
.get(&BITS)
.copied()
.unwrap_or_else(|| {
let gate = self.add_gate(
BinarySubtractionGate::<F, D, BITS>::new_from_config(&self.config),
vec![],
);
(gate, 0)
});
// Update `free_binary_subtraction` with new values.
if i + 1 < BinarySubtractionGate::<F, D, BITS>::new_from_config(&self.config).num_ops {
self.batched_gates
.free_random_access
.insert(BITS, (gate, i + 1));
} else {
self.batched_gates.free_random_access.remove(&BITS);
}
(gate, i)
}
/// Returns the gate index and copy index of a free `ConstantGate` slot, potentially adding a
/// new `ConstantGate` if needed.