feat(backend): use custom gate for Schnorr signature verification (#397)
* schnorr signature prover optimization. TODO: implemented eval_unfiltered_circuit, serialize * Use new gate(s) in MainPod circuit * Formatting * Clean-up * Code review * Code review * Code review * Formatting * Remove unnecessary elements --------- Co-authored-by: Linus Tang <linust@mit.edu>
This commit is contained in:
parent
ca97d9edc4
commit
1479400555
7 changed files with 1310 additions and 163 deletions
|
|
@ -32,7 +32,7 @@ use crate::backends::plonky2::{
|
||||||
primitives::ec::{
|
primitives::ec::{
|
||||||
bits::BigUInt320Target,
|
bits::BigUInt320Target,
|
||||||
field::{get_nnf_target, CircuitBuilderNNF, OEFTarget},
|
field::{get_nnf_target, CircuitBuilderNNF, OEFTarget},
|
||||||
gates::{curve::ECAddHomogOffset, generic::SimpleGate},
|
gates::curve::{ECAddHomogOffsetGate, ECAddXuGate},
|
||||||
},
|
},
|
||||||
Error,
|
Error,
|
||||||
};
|
};
|
||||||
|
|
@ -305,6 +305,17 @@ pub(super) fn add_homog<const D: usize, F: ECFieldExt<D>>(x1: F, u1: F, x2: F, u
|
||||||
[x, z, u, t]
|
[x, z, u, t]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Adds two elliptic curve points in affine coordinates.
|
||||||
|
pub(super) fn add_xu<const D: usize, F: ECFieldExt<D> + std::ops::Div<Output = F>>(
|
||||||
|
x1: F,
|
||||||
|
u1: F,
|
||||||
|
x2: F,
|
||||||
|
u2: F,
|
||||||
|
) -> [F; 2] {
|
||||||
|
let [x, z, u, t] = add_homog(x1, u1, x2, u2);
|
||||||
|
[x / z, u / t]
|
||||||
|
}
|
||||||
|
|
||||||
// See CircuitBuilderEllptic::add_point for an explanation of why we need this function.
|
// See CircuitBuilderEllptic::add_point for an explanation of why we need this function.
|
||||||
// cf. https://github.com/pornin/ecgfp5/blob/ce059c6d1e1662db437aecbf3db6bb67fe63c716/rust/src/curve.rs#L157
|
// cf. https://github.com/pornin/ecgfp5/blob/ce059c6d1e1662db437aecbf3db6bb67fe63c716/rust/src/curve.rs#L157
|
||||||
pub(super) fn add_homog_offset<const D: usize, F: ECFieldExt<D>>(
|
pub(super) fn add_homog_offset<const D: usize, F: ECFieldExt<D>>(
|
||||||
|
|
@ -341,7 +352,7 @@ static GROUP_ORDER_HALF_ROUND_UP: LazyLock<BigUint> =
|
||||||
|
|
||||||
impl Point {
|
impl Point {
|
||||||
const B1_U32: u32 = 263;
|
const B1_U32: u32 = 263;
|
||||||
const B1: GoldilocksField = GoldilocksField(Self::B1_U32 as u64);
|
pub(crate) const B1: GoldilocksField = GoldilocksField(Self::B1_U32 as u64);
|
||||||
|
|
||||||
pub fn b() -> ECField {
|
pub fn b() -> ECField {
|
||||||
ECField::from_basefield_array([
|
ECField::from_basefield_array([
|
||||||
|
|
@ -555,6 +566,75 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait CircuitBuilderSignature {
|
||||||
|
/// Computes `a*g + b*p`, where `g` is the generator of the curve.
|
||||||
|
fn linear_combination_point_gen(
|
||||||
|
&mut self,
|
||||||
|
a: &[BoolTarget; 320],
|
||||||
|
b: &[BoolTarget; 320],
|
||||||
|
p: &PointTarget,
|
||||||
|
) -> PointTarget;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CircuitBuilderSignature for CircuitBuilder<GoldilocksField, 2> {
|
||||||
|
fn linear_combination_point_gen(
|
||||||
|
&mut self,
|
||||||
|
a: &[BoolTarget; 320],
|
||||||
|
b: &[BoolTarget; 320],
|
||||||
|
p: &PointTarget,
|
||||||
|
) -> PointTarget {
|
||||||
|
let y = p;
|
||||||
|
let zero = self.identity_point();
|
||||||
|
let zero_target = self.zero();
|
||||||
|
|
||||||
|
let mut ans = zero.clone(); // accumulator
|
||||||
|
|
||||||
|
for x in 0..107 {
|
||||||
|
// prepare to apply gate
|
||||||
|
let mut inputs = Vec::with_capacity(26);
|
||||||
|
|
||||||
|
// scalar bits for g
|
||||||
|
if x == 0 {
|
||||||
|
inputs.push(zero_target);
|
||||||
|
} else {
|
||||||
|
inputs.push(a[320 - 3 * x].target);
|
||||||
|
}
|
||||||
|
inputs.push(a[319 - 3 * x].target);
|
||||||
|
inputs.push(a[318 - 3 * x].target);
|
||||||
|
|
||||||
|
// scalar bits for p (y)
|
||||||
|
if x == 0 {
|
||||||
|
inputs.push(zero_target);
|
||||||
|
} else {
|
||||||
|
inputs.push(b[320 - 3 * x].target);
|
||||||
|
}
|
||||||
|
inputs.push(b[319 - 3 * x].target);
|
||||||
|
inputs.push(b[318 - 3 * x].target);
|
||||||
|
|
||||||
|
// y point
|
||||||
|
inputs.extend_from_slice(&y.x.components);
|
||||||
|
inputs.extend_from_slice(&y.u.components);
|
||||||
|
|
||||||
|
// accumulator
|
||||||
|
inputs.extend_from_slice(&ans.x.components);
|
||||||
|
inputs.extend_from_slice(&ans.u.components);
|
||||||
|
|
||||||
|
// apply gate
|
||||||
|
let outputs = ECAddXuGate::apply(self, &inputs);
|
||||||
|
let x = FieldTarget::new(array::from_fn(|i| outputs[i]));
|
||||||
|
let u = FieldTarget::new(array::from_fn(|i| outputs[5 + i]));
|
||||||
|
ans = PointTarget {
|
||||||
|
x,
|
||||||
|
u,
|
||||||
|
checked_on_curve: true,
|
||||||
|
checked_in_subgroup: p.checked_in_subgroup,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
ans
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub trait CircuitBuilderElliptic {
|
pub trait CircuitBuilderElliptic {
|
||||||
fn add_virtual_point_target_unsafe(&mut self) -> PointTarget;
|
fn add_virtual_point_target_unsafe(&mut self) -> PointTarget;
|
||||||
fn add_virtual_point_target(&mut self) -> PointTarget;
|
fn add_virtual_point_target(&mut self) -> PointTarget;
|
||||||
|
|
@ -617,7 +697,9 @@ impl CircuitBuilderElliptic for CircuitBuilder<GoldilocksField, 2> {
|
||||||
inputs.extend_from_slice(&p1.u.components);
|
inputs.extend_from_slice(&p1.u.components);
|
||||||
inputs.extend_from_slice(&p2.x.components);
|
inputs.extend_from_slice(&p2.x.components);
|
||||||
inputs.extend_from_slice(&p2.u.components);
|
inputs.extend_from_slice(&p2.u.components);
|
||||||
let outputs = ECAddHomogOffset::apply(self, &inputs);
|
|
||||||
|
let outputs = ECAddHomogOffsetGate::apply(self, &inputs);
|
||||||
|
|
||||||
// plonky2 expects all gate constraints to be satisfied by the zero vector.
|
// plonky2 expects all gate constraints to be satisfied by the zero vector.
|
||||||
// So our elliptic curve addition gate computes [x,z-b,u,t-b], and we have to add the b here.
|
// So our elliptic curve addition gate computes [x,z-b,u,t-b], and we have to add the b here.
|
||||||
let [x, z, u, t] =
|
let [x, z, u, t] =
|
||||||
|
|
@ -638,32 +720,6 @@ impl CircuitBuilderElliptic for CircuitBuilder<GoldilocksField, 2> {
|
||||||
|
|
||||||
fn double_point(&mut self, p: &PointTarget) -> PointTarget {
|
fn double_point(&mut self, p: &PointTarget) -> PointTarget {
|
||||||
self.add_point(p, p)
|
self.add_point(p, p)
|
||||||
/*
|
|
||||||
let t3 = self.nnf_mul(&p.u, &p.u);
|
|
||||||
let one = self.one();
|
|
||||||
let neg_one = self.neg_one();
|
|
||||||
let two = self.two();
|
|
||||||
let neg_four = self.constant(GoldilocksField::from_noncanonical_i64(-4));
|
|
||||||
let four_b = self.constant(GoldilocksField::from_canonical_u32(4 * Point::B1_U32));
|
|
||||||
let w1_1 = self.nnf_add_scalar_times_generator_power(one, 0, &p.x);
|
|
||||||
let w1_2 = self.nnf_add(&w1_1, &w1_1);
|
|
||||||
let w1_3 = self.nnf_mul(&w1_2, &t3);
|
|
||||||
let w1_4 = self.nnf_mul_scalar(neg_one, &w1_3);
|
|
||||||
let w1 = self.nnf_add_scalar_times_generator_power(one, 0, &w1_4);
|
|
||||||
let x_1 = self.nnf_mul_scalar(four_b, &t3);
|
|
||||||
let x = self.nnf_mul_generator(&x_1);
|
|
||||||
let z = self.nnf_mul(&w1, &w1);
|
|
||||||
let u_1 = self.nnf_add(&w1, &p.u);
|
|
||||||
let u_2 = self.nnf_mul(&u_1, &u_1);
|
|
||||||
let u_3 = self.nnf_sub(&u_2, &t3);
|
|
||||||
let u = self.nnf_sub(&u_3, &z);
|
|
||||||
let t_1 = self.nnf_mul_scalar(neg_four, &t3);
|
|
||||||
let t_2 = self.nnf_add_scalar_times_generator_power(two, 0, &t_1);
|
|
||||||
let t = self.nnf_sub(&t_2, &z);
|
|
||||||
let xq = self.nnf_div(&x, &z);
|
|
||||||
let uq = self.nnf_div(&u, &t);
|
|
||||||
PointTarget { x: xq, u: uq }
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn multiply_point(&mut self, p1_scalar: &[BoolTarget; 320], p1: &PointTarget) -> PointTarget {
|
fn multiply_point(&mut self, p1_scalar: &[BoolTarget; 320], p1: &PointTarget) -> PointTarget {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use std::{array, marker::PhantomData};
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
use num::BigUint;
|
use num::BigUint;
|
||||||
use plonky2::{
|
use plonky2::{
|
||||||
|
|
@ -19,10 +19,7 @@ use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
//use super::gates::field::NNFMulGate;
|
//use super::gates::field::NNFMulGate;
|
||||||
use crate::{
|
use crate::{
|
||||||
backends::plonky2::{
|
backends::plonky2::{basetypes::D, primitives::ec::gates::field::NNFMulGate},
|
||||||
basetypes::D,
|
|
||||||
primitives::ec::gates::{field::NNFMulSimple, generic::SimpleGate},
|
|
||||||
},
|
|
||||||
middleware::F,
|
middleware::F,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -219,11 +216,33 @@ impl<const DEG: usize, NNF: OEF<DEG> + FieldExtension<DEG, BaseField = F>>
|
||||||
OEFTarget::new(std::array::from_fn(|i| sub_targets[i]))
|
OEFTarget::new(std::array::from_fn(|i| sub_targets[i]))
|
||||||
}
|
}
|
||||||
fn nnf_mul(&mut self, x: &OEFTarget<DEG, NNF>, y: &OEFTarget<DEG, NNF>) -> OEFTarget<DEG, NNF> {
|
fn nnf_mul(&mut self, x: &OEFTarget<DEG, NNF>, y: &OEFTarget<DEG, NNF>) -> OEFTarget<DEG, NNF> {
|
||||||
let mut inputs = Vec::with_capacity(10);
|
let gate = NNFMulGate::<D, DEG, NNF>::new_from_config(&self.config);
|
||||||
inputs.extend_from_slice(&x.components);
|
let (gate, i) = self.find_slot(gate, &[], &[]);
|
||||||
inputs.extend_from_slice(&y.components);
|
let wires_m0: OEFTarget<DEG, NNF> = OEFTarget::new(
|
||||||
let outputs = NNFMulSimple::<DEG, NNF>::apply(self, &inputs);
|
NNFMulGate::<D, DEG, NNF>::wires_ith_multiplicand_0(i)
|
||||||
OEFTarget::new(array::from_fn(|i| outputs[i]))
|
.map(|i| Target::wire(gate, i))
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.try_into()
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
|
let wires_m1: OEFTarget<DEG, NNF> = OEFTarget::new(
|
||||||
|
NNFMulGate::<D, DEG, NNF>::wires_ith_multiplicand_1(i)
|
||||||
|
.map(|i| Target::wire(gate, i))
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.try_into()
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
|
let output: OEFTarget<DEG, NNF> = OEFTarget::new(
|
||||||
|
NNFMulGate::<D, DEG, NNF>::wires_ith_output(i)
|
||||||
|
.map(|i| Target::wire(gate, i))
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.try_into()
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
|
self.nnf_connect(&wires_m0, x);
|
||||||
|
self.nnf_connect(&wires_m1, y);
|
||||||
|
|
||||||
|
output
|
||||||
}
|
}
|
||||||
fn nnf_div(&mut self, x: &OEFTarget<DEG, NNF>, y: &OEFTarget<DEG, NNF>) -> OEFTarget<DEG, NNF> {
|
fn nnf_div(&mut self, x: &OEFTarget<DEG, NNF>, y: &OEFTarget<DEG, NNF>) -> OEFTarget<DEG, NNF> {
|
||||||
let one = self.nnf_one();
|
let one = self.nnf_one();
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,34 @@
|
||||||
use std::array;
|
use std::{array, ops::Range};
|
||||||
|
|
||||||
use plonky2::field::goldilocks_field::GoldilocksField;
|
use itertools::{zip_eq, Itertools};
|
||||||
|
use plonky2::{
|
||||||
|
field::{
|
||||||
|
extension::{quintic::QuinticExtension, Extendable, FieldExtension, OEF},
|
||||||
|
goldilocks_field::GoldilocksField,
|
||||||
|
types::Field,
|
||||||
|
},
|
||||||
|
gates::gate::Gate,
|
||||||
|
hash::hash_types::RichField,
|
||||||
|
iop::{
|
||||||
|
ext_target::ExtensionTarget,
|
||||||
|
generator::{GeneratedValues, SimpleGenerator, WitnessGeneratorRef},
|
||||||
|
target::Target,
|
||||||
|
witness::{PartitionWitness, Witness, WitnessWrite},
|
||||||
|
},
|
||||||
|
plonk::{
|
||||||
|
circuit_builder::CircuitBuilder,
|
||||||
|
circuit_data::{CircuitConfig, CommonCircuitData},
|
||||||
|
vars::{EvaluationTargets, EvaluationVars},
|
||||||
|
},
|
||||||
|
util::serialization::{Buffer, IoResult, Read, Write},
|
||||||
|
};
|
||||||
|
|
||||||
use crate::backends::plonky2::primitives::ec::{
|
use crate::backends::plonky2::{
|
||||||
curve::{add_homog_offset, ECFieldExt},
|
basetypes::F,
|
||||||
gates::{field::QuinticTensor, generic::SimpleGate},
|
primitives::ec::{
|
||||||
|
curve::{add_homog, add_homog_offset, add_xu, ECFieldExt, Point},
|
||||||
|
gates::field::{nnf_mul_ext, QuinticTensor},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Gate computing the addition of two elliptic curve points in
|
/// Gate computing the addition of two elliptic curve points in
|
||||||
|
|
@ -25,30 +49,835 @@ use crate::backends::plonky2::primitives::ec::{
|
||||||
/// operation when all its witness wire values are zero (so that when the gate is partially used,
|
/// operation when all its witness wire values are zero (so that when the gate is partially used,
|
||||||
/// the unused slots still pass the constraints). This is the reason why this gate doesn't add the
|
/// the unused slots still pass the constraints). This is the reason why this gate doesn't add the
|
||||||
/// final offset: if it did, the constraints wouldn't pass on the zero witness values.
|
/// final offset: if it did, the constraints wouldn't pass on the zero witness values.
|
||||||
#[derive(Debug, Default, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct ECAddHomogOffset;
|
pub struct ECAddHomogOffsetGate {
|
||||||
|
/// Number of (offset) EC additions performed by the gate.
|
||||||
|
pub num_ops: usize,
|
||||||
|
}
|
||||||
|
|
||||||
impl SimpleGate for ECAddHomogOffset {
|
impl ECAddHomogOffsetGate {
|
||||||
type F = GoldilocksField;
|
pub const fn new_from_config(config: &CircuitConfig) -> Self {
|
||||||
const INPUTS_PER_OP: usize = 20;
|
Self {
|
||||||
const OUTPUTS_PER_OP: usize = 20;
|
num_ops: Self::num_ops(config),
|
||||||
const DEGREE: usize = 4;
|
}
|
||||||
const ID: &'static str = "ECAddHomog";
|
}
|
||||||
fn eval<const D: usize>(
|
|
||||||
wires: &[<Self::F as plonky2::field::extension::Extendable<D>>::Extension],
|
/// Determine the maximum number of operations that can fit in one gate for the given config.
|
||||||
) -> Vec<<Self::F as plonky2::field::extension::Extendable<D>>::Extension>
|
pub(crate) const fn num_ops(config: &CircuitConfig) -> usize {
|
||||||
|
let wires_per_op = 40;
|
||||||
|
config.num_routed_wires / wires_per_op
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) const fn wires_ith_addend_0(i: usize) -> Range<usize> {
|
||||||
|
40 * i..40 * i + 10
|
||||||
|
}
|
||||||
|
pub(crate) const fn wires_ith_addend_1(i: usize) -> Range<usize> {
|
||||||
|
40 * i + 10..40 * i + 20
|
||||||
|
}
|
||||||
|
pub(crate) const fn wires_ith_output(i: usize) -> Range<usize> {
|
||||||
|
40 * i + 20..40 * (i + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn apply<const D: usize>(
|
||||||
|
builder: &mut CircuitBuilder<F, D>,
|
||||||
|
targets: &[Target],
|
||||||
|
) -> Vec<Target>
|
||||||
where
|
where
|
||||||
Self::F: plonky2::field::extension::Extendable<D>,
|
F: Extendable<D>,
|
||||||
|
{
|
||||||
|
let gate = ECAddHomogOffsetGate::new_from_config(&builder.config);
|
||||||
|
let (row, op_num) = builder.find_slot(gate, &[], &[]);
|
||||||
|
let wires_a0 = Self::wires_ith_addend_0(op_num)
|
||||||
|
.map(|i| Target::wire(row, i))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
let wires_a1 = Self::wires_ith_addend_1(op_num)
|
||||||
|
.map(|i| Target::wire(row, i))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
let outputs = Self::wires_ith_output(op_num)
|
||||||
|
.map(|i| Target::wire(row, i))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
zip_eq(targets, [wires_a0, wires_a1].concat()).for_each(|(i, w)| builder.connect(*i, w));
|
||||||
|
|
||||||
|
outputs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<const D: usize> Gate<F, D> for ECAddHomogOffsetGate
|
||||||
|
where
|
||||||
|
F: Extendable<D>,
|
||||||
|
{
|
||||||
|
fn id(&self) -> String {
|
||||||
|
format!("{self:?}")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize(&self, dst: &mut Vec<u8>, _common_data: &CommonCircuitData<F, D>) -> IoResult<()> {
|
||||||
|
dst.write_usize(self.num_ops)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize(src: &mut Buffer, _common_data: &CommonCircuitData<F, D>) -> IoResult<Self> {
|
||||||
|
let num_ops = src.read_usize()?;
|
||||||
|
Ok(Self { num_ops })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn eval_unfiltered(&self, vars: EvaluationVars<F, D>) -> Vec<<F as Extendable<D>>::Extension> {
|
||||||
|
let mut constraints = Vec::with_capacity(self.num_ops * 20);
|
||||||
|
let extract_point = |range: Range<usize>| -> (QuinticTensor<D>, QuinticTensor<D>) {
|
||||||
|
let components = vars.local_wires[range].to_vec();
|
||||||
|
(
|
||||||
|
QuinticTensor::from_base(array::from_fn::<_, 5, _>(|i| components[i])),
|
||||||
|
QuinticTensor::from_base(array::from_fn::<_, 5, _>(|i| components[i + 5])),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
for i in 0..self.num_ops {
|
||||||
|
let (a_0x, a_0u) = extract_point(Self::wires_ith_addend_0(i));
|
||||||
|
let (a_1x, a_1u) = extract_point(Self::wires_ith_addend_1(i));
|
||||||
|
let output_vec = vars.local_wires[Self::wires_ith_output(i)]
|
||||||
|
.iter()
|
||||||
|
.chunks(5)
|
||||||
|
.into_iter()
|
||||||
|
.map(|chunk| {
|
||||||
|
let chunk_vec = chunk.collect::<Vec<_>>();
|
||||||
|
QuinticTensor::from_base(array::from_fn(|i| *chunk_vec[i]))
|
||||||
|
})
|
||||||
|
.collect::<Vec<QuinticTensor<D>>>();
|
||||||
|
let computed_output = add_homog_offset(a_0x, a_0u, a_1x, a_1u);
|
||||||
|
|
||||||
|
let new_constraints =
|
||||||
|
zip_eq(output_vec, computed_output).flat_map(|(o, co)| (o - co).components);
|
||||||
|
|
||||||
|
constraints.extend(new_constraints);
|
||||||
|
}
|
||||||
|
|
||||||
|
constraints
|
||||||
|
}
|
||||||
|
|
||||||
|
fn eval_unfiltered_circuit(
|
||||||
|
&self,
|
||||||
|
builder: &mut CircuitBuilder<F, D>,
|
||||||
|
vars: EvaluationTargets<D>,
|
||||||
|
) -> Vec<ExtensionTarget<D>> {
|
||||||
|
let mut constraints = Vec::with_capacity(self.num_ops * 20);
|
||||||
|
|
||||||
|
let extract_point =
|
||||||
|
|range: Range<usize>| -> ([ExtensionTarget<D>; 5], [ExtensionTarget<D>; 5]) {
|
||||||
|
let components = vars.local_wires[range].to_vec();
|
||||||
|
(
|
||||||
|
array::from_fn::<_, 5, _>(|i| components[i]),
|
||||||
|
array::from_fn::<_, 5, _>(|i| components[i + 5]),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
for i in 0..self.num_ops {
|
||||||
|
let (x1, u1) = extract_point(Self::wires_ith_addend_0(i));
|
||||||
|
let (x2, u2) = extract_point(Self::wires_ith_addend_1(i));
|
||||||
|
let computed_output = ec_target_add_homog_offset(builder, &x1, &u1, &x2, &u2)
|
||||||
|
.into_iter()
|
||||||
|
.flatten();
|
||||||
|
|
||||||
|
let output: [ExtensionTarget<D>; 20] = vars.local_wires[Self::wires_ith_output(i)]
|
||||||
|
.try_into()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let diffs = zip_eq(output, computed_output)
|
||||||
|
.map(|(o, co)| builder.sub_extension(o, co))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
constraints.extend(diffs);
|
||||||
|
}
|
||||||
|
|
||||||
|
constraints
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generators(&self, row: usize, _local_constants: &[F]) -> Vec<WitnessGeneratorRef<F, D>> {
|
||||||
|
(0..self.num_ops)
|
||||||
|
.map(|i| WitnessGeneratorRef::new(ECAddHomogOffsetGenerator { row, i }.adapter()))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn num_wires(&self) -> usize {
|
||||||
|
self.num_ops * 40
|
||||||
|
}
|
||||||
|
|
||||||
|
fn num_constants(&self) -> usize {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn degree(&self) -> usize {
|
||||||
|
4
|
||||||
|
}
|
||||||
|
|
||||||
|
fn num_constraints(&self) -> usize {
|
||||||
|
self.num_ops * 20
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default)]
|
||||||
|
pub struct ECAddHomogOffsetGenerator {
|
||||||
|
row: usize,
|
||||||
|
i: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<const D: usize> SimpleGenerator<F, D> for ECAddHomogOffsetGenerator
|
||||||
|
where
|
||||||
|
F: Extendable<D>,
|
||||||
|
{
|
||||||
|
fn id(&self) -> String {
|
||||||
|
"ECAddHomogOffsetGenerator".to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dependencies(&self) -> Vec<Target> {
|
||||||
|
ECAddHomogOffsetGate::wires_ith_addend_0(self.i)
|
||||||
|
.chain(ECAddHomogOffsetGate::wires_ith_addend_1(self.i))
|
||||||
|
.map(|i| Target::wire(self.row, i))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_once(
|
||||||
|
&self,
|
||||||
|
witness: &PartitionWitness<F>,
|
||||||
|
out_buffer: &mut GeneratedValues<F>,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
|
let extract_point = |range: Range<usize>| -> anyhow::Result<_> {
|
||||||
|
let components = range
|
||||||
|
.map(|i| witness.get_target(Target::wire(self.row, i)))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
Ok((
|
||||||
|
QuinticExtension::from_basefield_array(array::from_fn::<_, 5, _>(|i| {
|
||||||
|
components[i]
|
||||||
|
})),
|
||||||
|
QuinticExtension::from_basefield_array(array::from_fn::<_, 5, _>(|i| {
|
||||||
|
components[i + 5]
|
||||||
|
})),
|
||||||
|
))
|
||||||
|
};
|
||||||
|
|
||||||
|
let addend_0 = extract_point(ECAddHomogOffsetGate::wires_ith_addend_0(self.i))?;
|
||||||
|
let addend_1 = extract_point(ECAddHomogOffsetGate::wires_ith_addend_1(self.i))?;
|
||||||
|
|
||||||
|
let output_targets: [Target; 20] = ECAddHomogOffsetGate::wires_ith_output(self.i)
|
||||||
|
.map(|i| Target::wire(self.row, i))
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.try_into()
|
||||||
|
.map_err(|e| anyhow::anyhow!("{:?}", e))?;
|
||||||
|
|
||||||
|
let computed_output = add_homog_offset(addend_0.0, addend_0.1, addend_1.0, addend_1.1)
|
||||||
|
.iter()
|
||||||
|
.flat_map(<QuinticExtension<F> as FieldExtension<5>>::to_basefield_array)
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
out_buffer.set_target_arr(&output_targets, &computed_output)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize(&self, dst: &mut Vec<u8>, _common_data: &CommonCircuitData<F, D>) -> IoResult<()> {
|
||||||
|
dst.write_usize(self.row)?;
|
||||||
|
dst.write_usize(self.i)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize(src: &mut Buffer, _common_data: &CommonCircuitData<F, D>) -> IoResult<Self> {
|
||||||
|
let row = src.read_usize()?;
|
||||||
|
let i = src.read_usize()?;
|
||||||
|
Ok(Self { row, i })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default)]
|
||||||
|
pub struct ECAddXuGenerator {
|
||||||
|
row: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<const D: usize> SimpleGenerator<F, D> for ECAddXuGenerator
|
||||||
|
where
|
||||||
|
F: Extendable<D>,
|
||||||
|
{
|
||||||
|
fn serialize(
|
||||||
|
&self,
|
||||||
|
dst: &mut Vec<u8>,
|
||||||
|
_common_data: &CommonCircuitData<GoldilocksField, D>,
|
||||||
|
) -> IoResult<()> {
|
||||||
|
dst.write_usize(self.row)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize(
|
||||||
|
src: &mut Buffer,
|
||||||
|
_common_data: &CommonCircuitData<GoldilocksField, D>,
|
||||||
|
) -> IoResult<Self>
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
Ok(Self {
|
||||||
|
row: src.read_usize()?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn id(&self) -> String {
|
||||||
|
"ECAddXuGenerator".into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dependencies(&self) -> Vec<Target> {
|
||||||
|
(0..26).map(|i| Target::wire(self.row, i)).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_once(
|
||||||
|
&self,
|
||||||
|
witness: &PartitionWitness<GoldilocksField>,
|
||||||
|
out_buffer: &mut GeneratedValues<GoldilocksField>,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
|
let deps = self.dependencies();
|
||||||
|
|
||||||
|
let selectors_g: Vec<GoldilocksField> = deps[0..3]
|
||||||
|
.iter() // binary selectors for g
|
||||||
|
.map(|&target| witness.get_target(target))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let selectors_y: Vec<GoldilocksField> = deps[3..6]
|
||||||
|
.iter() // binary selectors for y
|
||||||
|
.map(|&target| witness.get_target(target))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
// extract element of quintic extension of Goldilocks field from five consecutive targets
|
||||||
|
let extract_quintic = |start_idx: usize| -> QuinticExtension<GoldilocksField> {
|
||||||
|
QuinticExtension::<GoldilocksField>::from_basefield_array([
|
||||||
|
witness.get_target(deps[start_idx]),
|
||||||
|
witness.get_target(deps[start_idx + 1]),
|
||||||
|
witness.get_target(deps[start_idx + 2]),
|
||||||
|
witness.get_target(deps[start_idx + 3]),
|
||||||
|
witness.get_target(deps[start_idx + 4]),
|
||||||
|
])
|
||||||
|
};
|
||||||
|
|
||||||
|
let g = Point::generator();
|
||||||
|
|
||||||
|
let g_x = g.x;
|
||||||
|
let g_u = g.u;
|
||||||
|
let y_x = extract_quintic(6);
|
||||||
|
let y_u = extract_quintic(11);
|
||||||
|
|
||||||
|
let mut p_x = extract_quintic(16);
|
||||||
|
let mut p_u = extract_quintic(21);
|
||||||
|
|
||||||
|
let mut write_quintic =
|
||||||
|
|start_wire: usize, value: &QuinticExtension<GoldilocksField>| -> anyhow::Result<()> {
|
||||||
|
let array: [GoldilocksField; 5] =
|
||||||
|
QuinticExtension::<GoldilocksField>::to_basefield_array(value);
|
||||||
|
for (j, &num) in array.iter().enumerate() {
|
||||||
|
out_buffer.set_target(Target::wire(self.row, start_wire + j), num)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
};
|
||||||
|
|
||||||
|
// Double and add three times.
|
||||||
|
// Write points from right to left so that the result of the fifth add
|
||||||
|
// lies on a routable wire and thus can be copied to the next row.
|
||||||
|
(0..3).try_for_each(|i| {
|
||||||
|
// Double, write to wires [106-30*i..116-30*i]
|
||||||
|
[p_x, p_u] = add_xu::<1, QuinticExtension<GoldilocksField>>(p_x, p_u, p_x, p_u);
|
||||||
|
write_quintic(106 - 30 * i, &p_x)?;
|
||||||
|
write_quintic(111 - 30 * i, &p_u)?;
|
||||||
|
|
||||||
|
// Possibly add g, depending on selector. Write to wires [96-30*i..106-30*i]
|
||||||
|
if selectors_g[i] == GoldilocksField::ONE {
|
||||||
|
[p_x, p_u] = add_xu::<1, QuinticExtension<GoldilocksField>>(p_x, p_u, g_x, g_u);
|
||||||
|
}
|
||||||
|
write_quintic(96 - 30 * i, &p_x)?;
|
||||||
|
write_quintic(101 - 30 * i, &p_u)?;
|
||||||
|
|
||||||
|
// Possibly add y, depending on selector. Write to wires [86-30*i..96-30*i]
|
||||||
|
if selectors_y[i] == GoldilocksField::ONE {
|
||||||
|
[p_x, p_u] = add_xu::<1, QuinticExtension<GoldilocksField>>(p_x, p_u, y_x, y_u);
|
||||||
|
}
|
||||||
|
write_quintic(86 - 30 * i, &p_x)?;
|
||||||
|
write_quintic(91 - 30 * i, &p_u)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gate that selectively carries out three rounds of the
|
||||||
|
/// double-and-add algorithm loop applied to the curve generator and a
|
||||||
|
/// given point.
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct ECAddXuGate;
|
||||||
|
|
||||||
|
impl ECAddXuGate {
|
||||||
|
const INPUTS_PER_OP: usize = 26;
|
||||||
|
const OUTPUTS_PER_OP: usize = 90;
|
||||||
|
const WIRES_PER_OP: usize = Self::INPUTS_PER_OP + Self::OUTPUTS_PER_OP;
|
||||||
|
const DEGREE: usize = 6;
|
||||||
|
const ID: &'static str = "ECAddXuGate";
|
||||||
|
|
||||||
|
pub fn apply<const D: usize>(
|
||||||
|
builder: &mut CircuitBuilder<F, D>,
|
||||||
|
targets: &[Target],
|
||||||
|
) -> Vec<Target>
|
||||||
|
where
|
||||||
|
F: Extendable<D>,
|
||||||
|
{
|
||||||
|
assert!(targets.len() == Self::INPUTS_PER_OP);
|
||||||
|
let (row, _) = builder.find_slot(ECAddXuGate::new(), &[], &[]);
|
||||||
|
for (i, &t) in targets.iter().enumerate() {
|
||||||
|
builder.connect(t, Target::wire(row, i));
|
||||||
|
}
|
||||||
|
|
||||||
|
(0..10)
|
||||||
|
.map(|i| Target::wire(row, Self::INPUTS_PER_OP + i))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_numerator_denominator<const D: usize>(
|
||||||
|
wires: &[<GoldilocksField as plonky2::field::extension::Extendable<D>>::Extension],
|
||||||
|
) -> Vec<<GoldilocksField as plonky2::field::extension::Extendable<D>>::Extension>
|
||||||
|
where
|
||||||
|
GoldilocksField: plonky2::field::extension::Extendable<D>,
|
||||||
{
|
{
|
||||||
let mut ans = Vec::with_capacity(20);
|
let mut ans = Vec::with_capacity(20);
|
||||||
let [x1, u1, x2, u2] =
|
let x1 = QuinticTensor::from_base(wires[0..5].try_into().unwrap());
|
||||||
array::from_fn(|j| QuinticTensor::from_base(array::from_fn(|i| wires[5 * j + i])));
|
let u1 = QuinticTensor::from_base(wires[5..10].try_into().unwrap());
|
||||||
let out = add_homog_offset(x1, u1, x2, u2);
|
let x2 = QuinticTensor::from_base(wires[10..15].try_into().unwrap());
|
||||||
|
let u2 = QuinticTensor::from_base(wires[15..20].try_into().unwrap());
|
||||||
|
let out = add_homog(x1, u1, x2, u2);
|
||||||
for v in out {
|
for v in out {
|
||||||
ans.extend(v.to_base());
|
ans.extend(v.to_base());
|
||||||
}
|
}
|
||||||
ans
|
ans
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn convert<const D: usize>(
|
||||||
|
wires: &[<GoldilocksField as plonky2::field::extension::Extendable<D>>::Extension],
|
||||||
|
) -> Vec<<GoldilocksField as plonky2::field::extension::Extendable<D>>::Extension>
|
||||||
|
where
|
||||||
|
GoldilocksField: plonky2::field::extension::Extendable<D>,
|
||||||
|
{
|
||||||
|
let mut ans = Vec::with_capacity(10);
|
||||||
|
let x1 = QuinticTensor::from_base(wires[0..5].try_into().unwrap());
|
||||||
|
let u1 = QuinticTensor::from_base(wires[5..10].try_into().unwrap());
|
||||||
|
ans.extend(x1.to_base());
|
||||||
|
ans.extend(u1.to_base());
|
||||||
|
ans
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<const D: usize> Gate<F, D> for ECAddXuGate
|
||||||
|
where
|
||||||
|
F: Extendable<D>,
|
||||||
|
{
|
||||||
|
fn id(&self) -> String {
|
||||||
|
Self::ID.to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize(
|
||||||
|
&self,
|
||||||
|
_dst: &mut Vec<u8>,
|
||||||
|
_common_data: &CommonCircuitData<GoldilocksField, D>,
|
||||||
|
) -> IoResult<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize(
|
||||||
|
_src: &mut Buffer<'_>,
|
||||||
|
_common_data: &CommonCircuitData<GoldilocksField, D>,
|
||||||
|
) -> IoResult<Self>
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
Ok(Self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn eval_unfiltered(
|
||||||
|
&self,
|
||||||
|
vars: EvaluationVars<'_, GoldilocksField, D>,
|
||||||
|
) -> Vec<<GoldilocksField as plonky2::field::extension::Extendable<D>>::Extension> {
|
||||||
|
let mut constraints = Vec::new();
|
||||||
|
|
||||||
|
let g = Point::generator();
|
||||||
|
|
||||||
|
let double_constraint = |i: usize, j: usize| {
|
||||||
|
let x1 = QuinticTensor::from_base(vars.local_wires[i..i + 5].try_into().unwrap());
|
||||||
|
let u1 = QuinticTensor::from_base(vars.local_wires[i + 5..i + 10].try_into().unwrap());
|
||||||
|
let [x, z, u, t] = add_homog(x1, u1, x1, u1);
|
||||||
|
let mut new_constraints: Vec<
|
||||||
|
<GoldilocksField as plonky2::field::extension::Extendable<D>>::Extension,
|
||||||
|
> = Vec::with_capacity(10);
|
||||||
|
let x2 = QuinticTensor::from_base(vars.local_wires[j..j + 5].try_into().unwrap());
|
||||||
|
let u2 = QuinticTensor::from_base(vars.local_wires[j + 5..j + 10].try_into().unwrap());
|
||||||
|
|
||||||
|
let first_constraints: Vec<_> = (x2 * z - x).components.to_vec();
|
||||||
|
let second_constraints: Vec<_> = (u2 * t - u).components.to_vec();
|
||||||
|
|
||||||
|
new_constraints.extend(&first_constraints);
|
||||||
|
new_constraints.extend(&second_constraints);
|
||||||
|
new_constraints
|
||||||
|
};
|
||||||
|
|
||||||
|
let select_and_add_constraint = |i: usize, j: usize, selector_index: usize, add_y: bool| {
|
||||||
|
let zero =
|
||||||
|
<GoldilocksField as plonky2::field::extension::Extendable<D>>::Extension::ZERO;
|
||||||
|
|
||||||
|
let one = <GoldilocksField as plonky2::field::extension::Extendable<D>>::Extension::ONE;
|
||||||
|
let one_full = QuinticTensor::from_base([one, zero, zero, zero, zero]);
|
||||||
|
|
||||||
|
let sel = vars.local_wires[selector_index];
|
||||||
|
let sel_full = QuinticTensor::from_base([sel, zero, zero, zero, zero]);
|
||||||
|
|
||||||
|
let x1 = QuinticTensor::from_base(vars.local_wires[i..i + 5].try_into().unwrap());
|
||||||
|
let u1 = QuinticTensor::from_base(vars.local_wires[i + 5..i + 10].try_into().unwrap());
|
||||||
|
|
||||||
|
let (x2, u2);
|
||||||
|
if add_y {
|
||||||
|
// (using hardcoded location of y)
|
||||||
|
x2 = QuinticTensor::from_base(vars.local_wires[6..11].try_into().unwrap());
|
||||||
|
u2 = QuinticTensor::from_base(vars.local_wires[11..16].try_into().unwrap());
|
||||||
|
} else {
|
||||||
|
let mut base_array: [GoldilocksField; 5] = g.x.to_basefield_array();
|
||||||
|
x2 = QuinticTensor::from_base(base_array.map(Into::into));
|
||||||
|
base_array = g.u.to_basefield_array();
|
||||||
|
u2 = QuinticTensor::from_base(base_array.map(Into::into));
|
||||||
|
}
|
||||||
|
let [x, z, u, t] = add_homog(x1, u1, x2, u2);
|
||||||
|
|
||||||
|
let mut new_constraints: Vec<
|
||||||
|
<GoldilocksField as plonky2::field::extension::Extendable<D>>::Extension,
|
||||||
|
> = Vec::with_capacity(10);
|
||||||
|
let x3 = QuinticTensor::from_base(vars.local_wires[j..j + 5].try_into().unwrap());
|
||||||
|
let u3 = QuinticTensor::from_base(vars.local_wires[j + 5..j + 10].try_into().unwrap());
|
||||||
|
|
||||||
|
let first_constraints: Vec<_> = (x3 * z - sel_full * x
|
||||||
|
+ (sel_full - one_full) * x1 * z)
|
||||||
|
.components
|
||||||
|
.to_vec();
|
||||||
|
let second_constraints: Vec<_> = (u3 * t - sel_full * u
|
||||||
|
+ (sel_full - one_full) * u1 * t)
|
||||||
|
.components
|
||||||
|
.to_vec();
|
||||||
|
|
||||||
|
new_constraints.extend_from_slice(&first_constraints[0..5]);
|
||||||
|
new_constraints.extend_from_slice(&second_constraints[0..5]);
|
||||||
|
new_constraints
|
||||||
|
};
|
||||||
|
|
||||||
|
constraints.extend(double_constraint(16, 106));
|
||||||
|
constraints.extend(select_and_add_constraint(106, 96, 0, false));
|
||||||
|
constraints.extend(select_and_add_constraint(96, 86, 3, true));
|
||||||
|
|
||||||
|
constraints.extend(double_constraint(86, 76));
|
||||||
|
constraints.extend(select_and_add_constraint(76, 66, 1, false));
|
||||||
|
constraints.extend(select_and_add_constraint(66, 56, 4, true));
|
||||||
|
|
||||||
|
constraints.extend(double_constraint(56, 46));
|
||||||
|
constraints.extend(select_and_add_constraint(46, 36, 2, false));
|
||||||
|
constraints.extend(select_and_add_constraint(36, 26, 5, true));
|
||||||
|
|
||||||
|
constraints
|
||||||
|
}
|
||||||
|
|
||||||
|
fn eval_unfiltered_circuit(
|
||||||
|
&self,
|
||||||
|
builder: &mut CircuitBuilder<GoldilocksField, D>,
|
||||||
|
vars: EvaluationTargets<'_, D>,
|
||||||
|
) -> Vec<ExtensionTarget<D>> {
|
||||||
|
let mut constraints = Vec::new();
|
||||||
|
|
||||||
|
type Nnf = QuinticExtension<F>;
|
||||||
|
|
||||||
|
let zero = builder.zero_extension();
|
||||||
|
|
||||||
|
let g = Point::generator();
|
||||||
|
let [g_x, g_u]: [[F; 5]; 2] = [g.x.to_basefield_array(), g.u.to_basefield_array()];
|
||||||
|
let [g_x_ext_target, g_u_ext_target] = [
|
||||||
|
array::from_fn(|i| builder.add_const_extension(zero, g_x[i])),
|
||||||
|
array::from_fn(|i| builder.add_const_extension(zero, g_u[i])),
|
||||||
|
];
|
||||||
|
|
||||||
|
let double_constraint =
|
||||||
|
|builder: &mut CircuitBuilder<GoldilocksField, D>, i: usize, j: usize| {
|
||||||
|
let x1 = array::from_fn(|k| vars.local_wires[i + k]);
|
||||||
|
let u1 = array::from_fn(|k| vars.local_wires[i + 5 + k]);
|
||||||
|
let [x, z, u, t] = homog_ec_target_add(builder, &x1, &u1, &x1, &u1);
|
||||||
|
|
||||||
|
let mut new_constraints = Vec::<ExtensionTarget<_>>::with_capacity(10);
|
||||||
|
let x2 = array::from_fn(|k| vars.local_wires[j + k]);
|
||||||
|
let u2 = array::from_fn(|k| vars.local_wires[j + 5 + k]);
|
||||||
|
|
||||||
|
let [expected_x, expected_u] = [
|
||||||
|
nnf_mul_ext::<D, 5, Nnf>(builder, &x2, &z),
|
||||||
|
nnf_mul_ext::<D, 5, Nnf>(builder, &u2, &t),
|
||||||
|
];
|
||||||
|
let first_constraints = nnf_ext_target_sub::<D, 5, Nnf>(builder, &expected_x, &x);
|
||||||
|
let second_constraints = nnf_ext_target_sub::<D, 5, Nnf>(builder, &expected_u, &u);
|
||||||
|
|
||||||
|
new_constraints.extend(&first_constraints);
|
||||||
|
new_constraints.extend(&second_constraints);
|
||||||
|
new_constraints
|
||||||
|
};
|
||||||
|
|
||||||
|
let select_and_add_constraint = |builder: &mut CircuitBuilder<GoldilocksField, D>,
|
||||||
|
i: usize,
|
||||||
|
j: usize,
|
||||||
|
selector_index: usize,
|
||||||
|
add_y: bool| {
|
||||||
|
let one = builder.one_extension();
|
||||||
|
let sel = vars.local_wires[selector_index];
|
||||||
|
|
||||||
|
let x1 = array::from_fn(|k| vars.local_wires[i + k]);
|
||||||
|
let u1 = array::from_fn(|k| vars.local_wires[i + 5 + k]);
|
||||||
|
|
||||||
|
let (x2, u2) = if add_y {
|
||||||
|
// (using hardcoded location of y)
|
||||||
|
(
|
||||||
|
array::from_fn(|k| vars.local_wires[6 + k]),
|
||||||
|
array::from_fn(|k| vars.local_wires[11 + k]),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
(g_x_ext_target, g_u_ext_target)
|
||||||
|
};
|
||||||
|
|
||||||
|
let [x, z, u, t] = homog_ec_target_add(builder, &x1, &u1, &x2, &u2);
|
||||||
|
|
||||||
|
let mut new_constraints = Vec::<ExtensionTarget<_>>::with_capacity(10);
|
||||||
|
let x3 = array::from_fn(|k| vars.local_wires[j + k]);
|
||||||
|
let u3 = array::from_fn(|k| vars.local_wires[j + 5 + k]);
|
||||||
|
|
||||||
|
let sel_minus_one = builder.sub_extension(sel, one);
|
||||||
|
let first_constraints = {
|
||||||
|
let term1 = nnf_mul_ext::<D, 5, Nnf>(builder, &x3, &z);
|
||||||
|
let term2 = array::from_fn(|i| builder.mul_extension(sel, x[i]));
|
||||||
|
let term3_1 = array::from_fn(|i| builder.mul_extension(sel_minus_one, x1[i]));
|
||||||
|
let term3 = nnf_mul_ext::<D, 5, Nnf>(builder, &term3_1, &z);
|
||||||
|
let partial_sum = nnf_ext_target_sub::<D, 5, Nnf>(builder, &term1, &term2);
|
||||||
|
nnf_ext_target_add::<D, 5, Nnf>(builder, &partial_sum, &term3)
|
||||||
|
};
|
||||||
|
|
||||||
|
let second_constraints = {
|
||||||
|
let term1 = nnf_mul_ext::<D, 5, Nnf>(builder, &u3, &t);
|
||||||
|
let term2 = array::from_fn(|i| builder.mul_extension(sel, u[i]));
|
||||||
|
let term3_1 = array::from_fn(|i| builder.mul_extension(sel_minus_one, u1[i]));
|
||||||
|
let term3 = nnf_mul_ext::<D, 5, Nnf>(builder, &term3_1, &t);
|
||||||
|
let partial_sum = nnf_ext_target_sub::<D, 5, Nnf>(builder, &term1, &term2);
|
||||||
|
nnf_ext_target_add::<D, 5, Nnf>(builder, &partial_sum, &term3)
|
||||||
|
};
|
||||||
|
|
||||||
|
new_constraints.extend(first_constraints);
|
||||||
|
new_constraints.extend(second_constraints);
|
||||||
|
new_constraints
|
||||||
|
};
|
||||||
|
|
||||||
|
constraints.extend(double_constraint(builder, 16, 106));
|
||||||
|
constraints.extend(select_and_add_constraint(builder, 106, 96, 0, false));
|
||||||
|
constraints.extend(select_and_add_constraint(builder, 96, 86, 3, true));
|
||||||
|
|
||||||
|
constraints.extend(double_constraint(builder, 86, 76));
|
||||||
|
constraints.extend(select_and_add_constraint(builder, 76, 66, 1, false));
|
||||||
|
constraints.extend(select_and_add_constraint(builder, 66, 56, 4, true));
|
||||||
|
|
||||||
|
constraints.extend(double_constraint(builder, 56, 46));
|
||||||
|
constraints.extend(select_and_add_constraint(builder, 46, 36, 2, false));
|
||||||
|
constraints.extend(select_and_add_constraint(builder, 36, 26, 5, true));
|
||||||
|
|
||||||
|
constraints
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generators(
|
||||||
|
&self,
|
||||||
|
row: usize,
|
||||||
|
_local_constants: &[GoldilocksField],
|
||||||
|
) -> Vec<WitnessGeneratorRef<GoldilocksField, D>> {
|
||||||
|
vec![WitnessGeneratorRef::new(ECAddXuGenerator { row }.adapter())]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn num_wires(&self) -> usize {
|
||||||
|
Self::WIRES_PER_OP
|
||||||
|
}
|
||||||
|
|
||||||
|
fn degree(&self) -> usize {
|
||||||
|
Self::DEGREE
|
||||||
|
}
|
||||||
|
|
||||||
|
fn num_ops(&self) -> usize {
|
||||||
|
1
|
||||||
|
}
|
||||||
|
|
||||||
|
fn num_constants(&self) -> usize {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn num_constraints(&self) -> usize {
|
||||||
|
90
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Useful auxiliary methods for defining the above gate follow.
|
||||||
|
fn nnf_ext_target_add<const D: usize, const DEG: usize, NNF: OEF<DEG>>(
|
||||||
|
builder: &mut CircuitBuilder<NNF::BaseField, D>,
|
||||||
|
x: &[ExtensionTarget<D>; DEG],
|
||||||
|
y: &[ExtensionTarget<D>; DEG],
|
||||||
|
) -> [ExtensionTarget<D>; DEG]
|
||||||
|
where
|
||||||
|
NNF::BaseField: RichField + Extendable<D>,
|
||||||
|
{
|
||||||
|
let sum_target = zip_eq(x, y)
|
||||||
|
.map(|(a, b)| builder.add_extension(*a, *b))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
array::from_fn(|i| sum_target[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn nnf_ext_target_sub<const D: usize, const DEG: usize, NNF: OEF<DEG>>(
|
||||||
|
builder: &mut CircuitBuilder<NNF::BaseField, D>,
|
||||||
|
x: &[ExtensionTarget<D>; DEG],
|
||||||
|
y: &[ExtensionTarget<D>; DEG],
|
||||||
|
) -> [ExtensionTarget<D>; DEG]
|
||||||
|
where
|
||||||
|
NNF::BaseField: RichField + Extendable<D>,
|
||||||
|
{
|
||||||
|
let diff_target = zip_eq(x, y)
|
||||||
|
.map(|(a, b)| builder.sub_extension(*a, *b))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
array::from_fn(|i| diff_target[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn nnf_ext_target_add_field_gen<const D: usize, const DEG: usize, NNF: OEF<DEG>>(
|
||||||
|
builder: &mut CircuitBuilder<NNF::BaseField, D>,
|
||||||
|
x: &[ExtensionTarget<D>; DEG],
|
||||||
|
factor: NNF::BaseField,
|
||||||
|
) -> [ExtensionTarget<D>; DEG]
|
||||||
|
where
|
||||||
|
NNF::BaseField: RichField + Extendable<D>,
|
||||||
|
{
|
||||||
|
array::from_fn(|i| {
|
||||||
|
if i == 1 {
|
||||||
|
builder.add_const_extension(x[1], factor)
|
||||||
|
} else {
|
||||||
|
x[i]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn nnf_ext_target_mul_field_gen<const D: usize, const DEG: usize, NNF: OEF<DEG>>(
|
||||||
|
builder: &mut CircuitBuilder<NNF::BaseField, D>,
|
||||||
|
x: &[ExtensionTarget<D>; DEG],
|
||||||
|
factor: NNF::BaseField,
|
||||||
|
) -> [ExtensionTarget<D>; DEG]
|
||||||
|
where
|
||||||
|
NNF::BaseField: RichField + Extendable<D>,
|
||||||
|
{
|
||||||
|
array::from_fn(|i| {
|
||||||
|
if i == 0 {
|
||||||
|
builder.mul_const_extension(factor * NNF::W, x[DEG - 1])
|
||||||
|
} else {
|
||||||
|
builder.mul_const_extension(factor, x[i - 1])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn nnf_ext_target_add_scalar<const D: usize, const DEG: usize, NNF: OEF<DEG>>(
|
||||||
|
builder: &mut CircuitBuilder<NNF::BaseField, D>,
|
||||||
|
x: &[ExtensionTarget<D>; DEG],
|
||||||
|
scal: NNF::BaseField,
|
||||||
|
) -> [ExtensionTarget<D>; DEG]
|
||||||
|
where
|
||||||
|
NNF::BaseField: RichField + Extendable<D>,
|
||||||
|
{
|
||||||
|
array::from_fn(|i| {
|
||||||
|
if i == 0 {
|
||||||
|
builder.add_const_extension(x[0], scal)
|
||||||
|
} else {
|
||||||
|
x[i]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn homog_ec_target_addition_terms<const D: usize>(
|
||||||
|
builder: &mut CircuitBuilder<F, D>,
|
||||||
|
x1: &[ExtensionTarget<D>; 5],
|
||||||
|
u1: &[ExtensionTarget<D>; 5],
|
||||||
|
x2: &[ExtensionTarget<D>; 5],
|
||||||
|
u2: &[ExtensionTarget<D>; 5],
|
||||||
|
) -> [[ExtensionTarget<D>; 5]; 5]
|
||||||
|
where
|
||||||
|
F: Extendable<D>,
|
||||||
|
{
|
||||||
|
type Nnf = QuinticExtension<F>;
|
||||||
|
|
||||||
|
let t1 = nnf_mul_ext::<D, 5, Nnf>(builder, x1, x2);
|
||||||
|
let t3 = nnf_mul_ext::<D, 5, Nnf>(builder, u1, u2);
|
||||||
|
let t5 = nnf_ext_target_add::<D, 5, Nnf>(builder, x1, x2);
|
||||||
|
let t6 = nnf_ext_target_add::<D, 5, Nnf>(builder, u1, u2);
|
||||||
|
let t7 = nnf_ext_target_add_field_gen::<D, 5, Nnf>(builder, &t1, Point::B1);
|
||||||
|
|
||||||
|
let twice_t7 = nnf_ext_target_add::<D, 5, Nnf>(builder, &t7, &t7);
|
||||||
|
let t5_mul_fg2b =
|
||||||
|
nnf_ext_target_mul_field_gen::<D, 5, Nnf>(builder, &t5, Point::B1 + Point::B1);
|
||||||
|
let t5_mul_fg2b_plus_twice_t7 =
|
||||||
|
nnf_ext_target_add::<D, 5, Nnf>(builder, &t5_mul_fg2b, &twice_t7);
|
||||||
|
let t9 = nnf_mul_ext::<D, 5, Nnf>(builder, &t3, &t5_mul_fg2b_plus_twice_t7);
|
||||||
|
|
||||||
|
let t5_plus_t7 = nnf_ext_target_add::<D, 5, Nnf>(builder, &t5, &t7);
|
||||||
|
let twice_t3 = nnf_ext_target_add::<D, 5, Nnf>(builder, &t3, &t3);
|
||||||
|
let twice_t3_plus_one =
|
||||||
|
nnf_ext_target_add_scalar::<D, 5, Nnf>(builder, &twice_t3, GoldilocksField::ONE);
|
||||||
|
let t10 = nnf_mul_ext::<D, 5, Nnf>(builder, &twice_t3_plus_one, &t5_plus_t7);
|
||||||
|
[t1, t6, t7, t9, t10]
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Make this more generic?
|
||||||
|
/// Analogue of `add_homog` for extension targets.
|
||||||
|
fn homog_ec_target_add<const D: usize>(
|
||||||
|
builder: &mut CircuitBuilder<F, D>,
|
||||||
|
x1: &[ExtensionTarget<D>; 5],
|
||||||
|
u1: &[ExtensionTarget<D>; 5],
|
||||||
|
x2: &[ExtensionTarget<D>; 5],
|
||||||
|
u2: &[ExtensionTarget<D>; 5],
|
||||||
|
) -> [[ExtensionTarget<D>; 5]; 4]
|
||||||
|
where
|
||||||
|
F: Extendable<D>,
|
||||||
|
{
|
||||||
|
type Nnf = QuinticExtension<F>;
|
||||||
|
|
||||||
|
let [t1, t6, t7, t9, t10] = homog_ec_target_addition_terms(builder, x1, u1, x2, u2);
|
||||||
|
|
||||||
|
let t10_minus_t7 = nnf_ext_target_sub::<D, 5, Nnf>(builder, &t10, &t7);
|
||||||
|
let x = nnf_ext_target_mul_field_gen::<D, 5, Nnf>(builder, &t10_minus_t7, Point::B1);
|
||||||
|
|
||||||
|
let z = nnf_ext_target_sub::<D, 5, Nnf>(builder, &t7, &t9);
|
||||||
|
|
||||||
|
let minus_t1 = array::from_fn(|i| builder.mul_const_extension(-F::ONE, t1[i]));
|
||||||
|
let minus_t1_plus_fgpb =
|
||||||
|
nnf_ext_target_add_field_gen::<D, 5, Nnf>(builder, &minus_t1, Point::B1);
|
||||||
|
let u = nnf_mul_ext::<D, 5, Nnf>(builder, &t6, &minus_t1_plus_fgpb);
|
||||||
|
|
||||||
|
let t = nnf_ext_target_add::<D, 5, Nnf>(builder, &t7, &t9);
|
||||||
|
|
||||||
|
[x, z, u, t]
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Analogue of `add_homog_offset` for extension targets.
|
||||||
|
fn ec_target_add_homog_offset<const D: usize>(
|
||||||
|
builder: &mut CircuitBuilder<F, D>,
|
||||||
|
x1: &[ExtensionTarget<D>; 5],
|
||||||
|
u1: &[ExtensionTarget<D>; 5],
|
||||||
|
x2: &[ExtensionTarget<D>; 5],
|
||||||
|
u2: &[ExtensionTarget<D>; 5],
|
||||||
|
) -> [[ExtensionTarget<D>; 5]; 4]
|
||||||
|
where
|
||||||
|
F: Extendable<D>,
|
||||||
|
{
|
||||||
|
type Nnf = QuinticExtension<F>;
|
||||||
|
|
||||||
|
let [t1, t6, t7, t9, t10] = homog_ec_target_addition_terms(builder, x1, u1, x2, u2);
|
||||||
|
|
||||||
|
let t10_minus_t7 = nnf_ext_target_sub::<D, 5, Nnf>(builder, &t10, &t7);
|
||||||
|
let x = nnf_ext_target_mul_field_gen::<D, 5, Nnf>(builder, &t10_minus_t7, Point::B1);
|
||||||
|
|
||||||
|
let z = nnf_ext_target_sub::<D, 5, Nnf>(builder, &t1, &t9);
|
||||||
|
|
||||||
|
let minus_t1 = array::from_fn(|i| builder.mul_const_extension(-F::ONE, t1[i]));
|
||||||
|
let minus_t1_plus_fgpb =
|
||||||
|
nnf_ext_target_add_field_gen::<D, 5, Nnf>(builder, &minus_t1, Point::B1);
|
||||||
|
let u = nnf_mul_ext::<D, 5, Nnf>(builder, &t6, &minus_t1_plus_fgpb);
|
||||||
|
|
||||||
|
let t = nnf_ext_target_add::<D, 5, Nnf>(builder, &t1, &t9);
|
||||||
|
|
||||||
|
[x, z, u, t]
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
@ -58,40 +887,39 @@ mod test {
|
||||||
plonk::{circuit_data::CircuitConfig, config::PoseidonGoldilocksConfig},
|
plonk::{circuit_data::CircuitConfig, config::PoseidonGoldilocksConfig},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::backends::plonky2::primitives::ec::gates::{
|
use crate::backends::plonky2::primitives::ec::gates::curve::{
|
||||||
curve::ECAddHomogOffset, generic::GateAdapter,
|
ECAddHomogOffsetGate, ECAddXuGate,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_recursion() -> Result<(), anyhow::Error> {
|
fn test_ec_add_gate() -> Result<(), anyhow::Error> {
|
||||||
let config = CircuitConfig::standard_recursion_config();
|
let config = CircuitConfig::standard_recursion_config();
|
||||||
let gate = GateAdapter::<ECAddHomogOffset>::new_from_config(&config);
|
let gate = ECAddHomogOffsetGate::new_from_config(&config);
|
||||||
|
|
||||||
test_eval_fns::<_, PoseidonGoldilocksConfig, _, 2>(gate)
|
test_eval_fns::<_, PoseidonGoldilocksConfig, _, 2>(gate)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_low_degree_orig() -> Result<(), anyhow::Error> {
|
fn test_ec_add_xu_gate() -> Result<(), anyhow::Error> {
|
||||||
|
let gate = ECAddXuGate::new();
|
||||||
|
|
||||||
|
test_eval_fns::<_, PoseidonGoldilocksConfig, _, 2>(gate)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ec_add_gate_low_degree() -> Result<(), anyhow::Error> {
|
||||||
let config = CircuitConfig::standard_recursion_config();
|
let config = CircuitConfig::standard_recursion_config();
|
||||||
let gate = GateAdapter::<ECAddHomogOffset>::new_from_config(&config);
|
let gate = ECAddHomogOffsetGate::new_from_config(&config);
|
||||||
|
|
||||||
test_low_degree::<_, _, 2>(gate);
|
test_low_degree::<_, _, 2>(gate);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_low_degree_recursive() -> Result<(), anyhow::Error> {
|
fn test_ec_add_xu_gate_low_degree() -> Result<(), anyhow::Error> {
|
||||||
let config = CircuitConfig::standard_recursion_config();
|
let gate = ECAddXuGate::new();
|
||||||
let orig_gate = GateAdapter::<ECAddHomogOffset>::new_from_config(&config);
|
|
||||||
|
|
||||||
test_low_degree::<_, _, 2>(orig_gate.recursive_gate());
|
test_low_degree::<_, _, 2>(gate);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_double_recursion() -> Result<(), anyhow::Error> {
|
|
||||||
let config = CircuitConfig::standard_recursion_config();
|
|
||||||
let orig_gate = GateAdapter::<ECAddHomogOffset>::new_from_config(&config);
|
|
||||||
test_eval_fns::<_, PoseidonGoldilocksConfig, _, 2>(orig_gate.recursive_gate())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,33 @@
|
||||||
use std::{
|
use std::{
|
||||||
array,
|
array,
|
||||||
marker::PhantomData,
|
marker::PhantomData,
|
||||||
ops::{Add, Mul, Neg, Sub},
|
ops::{Add, Mul, Neg, Range, Sub},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use itertools::zip_eq;
|
||||||
use plonky2::{
|
use plonky2::{
|
||||||
field::{
|
field::{
|
||||||
extension::{quintic::QuinticExtension, Extendable, FieldExtension, OEF},
|
extension::{quintic::QuinticExtension, Extendable, FieldExtension, OEF},
|
||||||
goldilocks_field::GoldilocksField,
|
goldilocks_field::GoldilocksField,
|
||||||
types::Field,
|
types::Field,
|
||||||
},
|
},
|
||||||
|
gates::gate::Gate,
|
||||||
hash::hash_types::RichField,
|
hash::hash_types::RichField,
|
||||||
|
iop::{
|
||||||
|
ext_target::ExtensionTarget,
|
||||||
|
generator::{GeneratedValues, SimpleGenerator, WitnessGeneratorRef},
|
||||||
|
target::Target,
|
||||||
|
witness::{PartitionWitness, Witness, WitnessWrite},
|
||||||
|
},
|
||||||
|
plonk::{
|
||||||
|
circuit_builder::CircuitBuilder,
|
||||||
|
circuit_data::{CircuitConfig, CommonCircuitData},
|
||||||
|
vars::{EvaluationTargets, EvaluationVars},
|
||||||
|
},
|
||||||
|
util::serialization::{Buffer, IoResult, Read, Write},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::backends::plonky2::primitives::ec::{curve::ECFieldExt, gates::generic::SimpleGate};
|
use crate::backends::plonky2::primitives::ec::curve::ECFieldExt;
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||||
pub struct TensorProduct<const D1: usize, const D2: usize, F1, F2>
|
pub struct TensorProduct<const D1: usize, const D2: usize, F1, F2>
|
||||||
|
|
@ -137,42 +151,284 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Default)]
|
/// A gate which can perform a multiplication on OEF.
|
||||||
pub struct NNFMulSimple<const DEG: usize, NNF: OEF<DEG>> {
|
/// If the config has enough routed wires, it can support several such operations in one gate.
|
||||||
_phantom_data: PhantomData<fn(NNF) -> NNF>,
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct NNFMulGate<const D: usize, const DEG: usize, NNF: OEF<DEG>> {
|
||||||
|
/// Number of multiplications performed by the gate.
|
||||||
|
pub num_ops: usize,
|
||||||
|
_phantom_data: PhantomData<NNF>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<const DEG: usize, NNF: OEF<DEG>> NNFMulSimple<DEG, NNF> {
|
impl<const D: usize, const DEG: usize, NNF: OEF<DEG>> NNFMulGate<D, DEG, NNF> {
|
||||||
pub fn new() -> Self {
|
pub const fn new_from_config(config: &CircuitConfig) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
num_ops: Self::num_ops(config),
|
||||||
_phantom_data: PhantomData,
|
_phantom_data: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Determine the maximum number of operations that can fit in one gate for the given config.
|
||||||
|
pub(crate) const fn num_ops(config: &CircuitConfig) -> usize {
|
||||||
|
let wires_per_op = 3 * DEG;
|
||||||
|
config.num_routed_wires / wires_per_op
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) const fn wires_ith_multiplicand_0(i: usize) -> Range<usize> {
|
||||||
|
3 * DEG * i..3 * DEG * i + DEG
|
||||||
|
}
|
||||||
|
pub(crate) const fn wires_ith_multiplicand_1(i: usize) -> Range<usize> {
|
||||||
|
3 * DEG * i + DEG..3 * DEG * i + 2 * DEG
|
||||||
|
}
|
||||||
|
pub(crate) const fn wires_ith_output(i: usize) -> Range<usize> {
|
||||||
|
3 * DEG * i + 2 * DEG..3 * DEG * (i + 1)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<NNF, const NNF_DEG: usize> SimpleGate for NNFMulSimple<NNF_DEG, NNF>
|
impl<const D: usize, const DEG: usize, NNF: OEF<DEG>> Gate<NNF::BaseField, D>
|
||||||
|
for NNFMulGate<D, DEG, NNF>
|
||||||
where
|
where
|
||||||
NNF: OEF<NNF_DEG>,
|
NNF::BaseField: RichField + Extendable<D>,
|
||||||
NNF::BaseField: RichField + Extendable<1>,
|
|
||||||
{
|
{
|
||||||
type F = NNF::BaseField;
|
fn id(&self) -> String {
|
||||||
const INPUTS_PER_OP: usize = 2 * NNF_DEG;
|
format!("{self:?}")
|
||||||
const OUTPUTS_PER_OP: usize = NNF_DEG;
|
|
||||||
const DEGREE: usize = 2;
|
|
||||||
const ID: &'static str = "NNFSimpleGate";
|
|
||||||
|
|
||||||
fn eval<const D: usize>(
|
|
||||||
wires: &[<Self::F as Extendable<D>>::Extension],
|
|
||||||
) -> Vec<<Self::F as Extendable<D>>::Extension>
|
|
||||||
where
|
|
||||||
Self::F: Extendable<D>,
|
|
||||||
{
|
|
||||||
let x: TensorProduct<NNF_DEG, D, NNF, <Self::F as Extendable<D>>::Extension> =
|
|
||||||
TensorProduct::new(array::from_fn(|i| wires[i]));
|
|
||||||
let y = TensorProduct::new(array::from_fn(|i| wires[NNF_DEG + i]));
|
|
||||||
let prod = x * y;
|
|
||||||
prod.components.into()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn serialize(
|
||||||
|
&self,
|
||||||
|
dst: &mut Vec<u8>,
|
||||||
|
_common_data: &CommonCircuitData<NNF::BaseField, D>,
|
||||||
|
) -> IoResult<()> {
|
||||||
|
dst.write_usize(self.num_ops)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize(
|
||||||
|
src: &mut Buffer,
|
||||||
|
_common_data: &CommonCircuitData<NNF::BaseField, D>,
|
||||||
|
) -> IoResult<Self> {
|
||||||
|
let num_ops = src.read_usize()?;
|
||||||
|
Ok(Self {
|
||||||
|
num_ops,
|
||||||
|
_phantom_data: PhantomData,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn eval_unfiltered(
|
||||||
|
&self,
|
||||||
|
vars: EvaluationVars<NNF::BaseField, D>,
|
||||||
|
) -> Vec<<NNF::BaseField as Extendable<D>>::Extension> {
|
||||||
|
let mut constraints = Vec::with_capacity(self.num_ops * DEG);
|
||||||
|
for i in 0..self.num_ops {
|
||||||
|
let multiplicand_0: TensorProduct<DEG, D, NNF, _> = TensorProduct::new(
|
||||||
|
vars.local_wires[Self::wires_ith_multiplicand_0(i)]
|
||||||
|
.try_into()
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
|
let multiplicand_1 = TensorProduct::new(
|
||||||
|
vars.local_wires[Self::wires_ith_multiplicand_1(i)]
|
||||||
|
.try_into()
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
|
let output = TensorProduct::new(
|
||||||
|
vars.local_wires[Self::wires_ith_output(i)]
|
||||||
|
.try_into()
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
|
let computed_output = multiplicand_0 * multiplicand_1;
|
||||||
|
|
||||||
|
constraints.extend((output - computed_output).components);
|
||||||
|
}
|
||||||
|
|
||||||
|
constraints
|
||||||
|
}
|
||||||
|
|
||||||
|
fn eval_unfiltered_circuit(
|
||||||
|
&self,
|
||||||
|
builder: &mut CircuitBuilder<NNF::BaseField, D>,
|
||||||
|
vars: EvaluationTargets<D>,
|
||||||
|
) -> Vec<ExtensionTarget<D>> {
|
||||||
|
let mut constraints = Vec::with_capacity(self.num_ops * DEG);
|
||||||
|
for i in 0..self.num_ops {
|
||||||
|
let multiplicand_0: [ExtensionTarget<D>; DEG] = vars.local_wires
|
||||||
|
[Self::wires_ith_multiplicand_0(i)]
|
||||||
|
.try_into()
|
||||||
|
.unwrap();
|
||||||
|
let multiplicand_1: [ExtensionTarget<D>; DEG] = vars.local_wires
|
||||||
|
[Self::wires_ith_multiplicand_1(i)]
|
||||||
|
.try_into()
|
||||||
|
.unwrap();
|
||||||
|
let output: [ExtensionTarget<D>; DEG] = vars.local_wires[Self::wires_ith_output(i)]
|
||||||
|
.try_into()
|
||||||
|
.unwrap();
|
||||||
|
let computed_output =
|
||||||
|
nnf_mul_ext::<_, DEG, NNF>(builder, &multiplicand_0, &multiplicand_1);
|
||||||
|
|
||||||
|
let diffs = zip_eq(output, computed_output)
|
||||||
|
.map(|(o, co)| builder.sub_extension(o, co))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
constraints.extend(diffs);
|
||||||
|
}
|
||||||
|
|
||||||
|
constraints
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generators(
|
||||||
|
&self,
|
||||||
|
row: usize,
|
||||||
|
_local_constants: &[NNF::BaseField],
|
||||||
|
) -> Vec<WitnessGeneratorRef<NNF::BaseField, D>> {
|
||||||
|
(0..self.num_ops)
|
||||||
|
.map(|i| {
|
||||||
|
WitnessGeneratorRef::new(
|
||||||
|
NNFMulGenerator::<D, DEG, NNF> {
|
||||||
|
row,
|
||||||
|
i,
|
||||||
|
phantom_data: PhantomData,
|
||||||
|
}
|
||||||
|
.adapter(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn num_wires(&self) -> usize {
|
||||||
|
self.num_ops * 3 * DEG
|
||||||
|
}
|
||||||
|
|
||||||
|
fn num_constants(&self) -> usize {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn degree(&self) -> usize {
|
||||||
|
3
|
||||||
|
}
|
||||||
|
|
||||||
|
fn num_constraints(&self) -> usize {
|
||||||
|
self.num_ops * DEG
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default)]
|
||||||
|
pub struct NNFMulGenerator<const D: usize, const DEG: usize, NNF: OEF<DEG>> {
|
||||||
|
row: usize,
|
||||||
|
i: usize,
|
||||||
|
phantom_data: PhantomData<NNF>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<const D: usize, const DEG: usize, NNF: OEF<DEG>> SimpleGenerator<NNF::BaseField, D>
|
||||||
|
for NNFMulGenerator<D, DEG, NNF>
|
||||||
|
where
|
||||||
|
NNF::BaseField: RichField + Extendable<D>,
|
||||||
|
{
|
||||||
|
fn id(&self) -> String {
|
||||||
|
"NNFMulGenerator".to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dependencies(&self) -> Vec<Target> {
|
||||||
|
NNFMulGate::<D, DEG, NNF>::wires_ith_multiplicand_0(self.i)
|
||||||
|
.chain(NNFMulGate::<D, DEG, NNF>::wires_ith_multiplicand_1(self.i))
|
||||||
|
.map(|i| Target::wire(self.row, i))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_once(
|
||||||
|
&self,
|
||||||
|
witness: &PartitionWitness<NNF::BaseField>,
|
||||||
|
out_buffer: &mut GeneratedValues<NNF::BaseField>,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
|
let extract_nnf = |range: Range<usize>| -> anyhow::Result<NNF> {
|
||||||
|
let components: [NNF::BaseField; DEG] = range
|
||||||
|
.map(|i| witness.get_target(Target::wire(self.row, i)))
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.try_into()
|
||||||
|
.map_err(|e| anyhow::anyhow!("{:?}", e))?;
|
||||||
|
Ok(NNF::from_basefield_array(components))
|
||||||
|
};
|
||||||
|
|
||||||
|
let multiplicand_0 =
|
||||||
|
extract_nnf(NNFMulGate::<D, DEG, NNF>::wires_ith_multiplicand_0(self.i))?;
|
||||||
|
let multiplicand_1 =
|
||||||
|
extract_nnf(NNFMulGate::<D, DEG, NNF>::wires_ith_multiplicand_1(self.i))?;
|
||||||
|
|
||||||
|
let output_targets: [Target; DEG] = NNFMulGate::<D, DEG, NNF>::wires_ith_output(self.i)
|
||||||
|
.map(|i| Target::wire(self.row, i))
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.try_into()
|
||||||
|
.map_err(|e| anyhow::anyhow!("{:?}", e))?;
|
||||||
|
|
||||||
|
let computed_output = multiplicand_0 * multiplicand_1;
|
||||||
|
|
||||||
|
out_buffer.set_target_arr(&output_targets, &computed_output.to_basefield_array())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize(
|
||||||
|
&self,
|
||||||
|
dst: &mut Vec<u8>,
|
||||||
|
_common_data: &CommonCircuitData<NNF::BaseField, D>,
|
||||||
|
) -> IoResult<()> {
|
||||||
|
dst.write_usize(self.row)?;
|
||||||
|
dst.write_usize(self.i)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize(
|
||||||
|
src: &mut Buffer,
|
||||||
|
_common_data: &CommonCircuitData<NNF::BaseField, D>,
|
||||||
|
) -> IoResult<Self> {
|
||||||
|
let row = src.read_usize()?;
|
||||||
|
let i = src.read_usize()?;
|
||||||
|
Ok(Self {
|
||||||
|
row,
|
||||||
|
i,
|
||||||
|
phantom_data: PhantomData,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn nnf_mul_ext<const D: usize, const DEG: usize, NNF: OEF<DEG>>(
|
||||||
|
builder: &mut CircuitBuilder<NNF::BaseField, D>,
|
||||||
|
x: &[ExtensionTarget<D>; DEG],
|
||||||
|
y: &[ExtensionTarget<D>; DEG],
|
||||||
|
) -> [ExtensionTarget<D>; DEG]
|
||||||
|
where
|
||||||
|
NNF::BaseField: RichField + Extendable<D>,
|
||||||
|
{
|
||||||
|
let zero = builder.zero_extension();
|
||||||
|
let mul_targets = (0..DEG - 1)
|
||||||
|
.map(|k| {
|
||||||
|
let term1 = (0..=k)
|
||||||
|
.map(|i| builder.mul_extension(x[i], y[k - i]))
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.into_iter()
|
||||||
|
.reduce(|sum, summand| builder.add_extension(sum, summand))
|
||||||
|
.expect("Missing summands");
|
||||||
|
let term2 = (k + 1..DEG)
|
||||||
|
.map(|i| {
|
||||||
|
builder.arithmetic_extension(
|
||||||
|
NNF::W,
|
||||||
|
NNF::BaseField::ZERO,
|
||||||
|
x[i],
|
||||||
|
y[DEG + k - i],
|
||||||
|
zero,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.into_iter()
|
||||||
|
.reduce(|sum, summand| builder.add_extension(sum, summand))
|
||||||
|
.expect("Missing summands");
|
||||||
|
builder.add_extension(term1, term2)
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.into_iter()
|
||||||
|
.chain(std::iter::once(
|
||||||
|
(0..DEG)
|
||||||
|
.map(|i| builder.mul_extension(x[i], y[DEG - 1 - i]))
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.into_iter()
|
||||||
|
.reduce(|sum, summand| builder.add_extension(sum, summand))
|
||||||
|
.expect("Missing summands"),
|
||||||
|
))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
std::array::from_fn(|i| mul_targets[i])
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
@ -183,52 +439,22 @@ mod test {
|
||||||
plonk::{circuit_data::CircuitConfig, config::PoseidonGoldilocksConfig},
|
plonk::{circuit_data::CircuitConfig, config::PoseidonGoldilocksConfig},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::backends::plonky2::primitives::ec::gates::{
|
use crate::backends::plonky2::{basetypes::D, primitives::ec::gates::field::NNFMulGate};
|
||||||
field::NNFMulSimple, generic::GateAdapter,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_recursion() -> Result<(), anyhow::Error> {
|
fn test_nnf_mul_gate() -> Result<(), anyhow::Error> {
|
||||||
let config = CircuitConfig::standard_recursion_config();
|
let config = CircuitConfig::standard_recursion_config();
|
||||||
let gate =
|
let gate = NNFMulGate::<D, 5, QuinticExtension<GoldilocksField>>::new_from_config(&config);
|
||||||
GateAdapter::<NNFMulSimple<5, QuinticExtension<GoldilocksField>>>::new_from_config(
|
|
||||||
&config,
|
|
||||||
);
|
|
||||||
|
|
||||||
test_eval_fns::<_, PoseidonGoldilocksConfig, _, 2>(gate)
|
test_eval_fns::<_, PoseidonGoldilocksConfig, _, 2>(gate)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_low_degree_orig() -> Result<(), anyhow::Error> {
|
fn test_nnf_mul_gate_low_degree() -> Result<(), anyhow::Error> {
|
||||||
let config = CircuitConfig::standard_recursion_config();
|
let config = CircuitConfig::standard_recursion_config();
|
||||||
let gate =
|
let gate = NNFMulGate::<D, 5, QuinticExtension<GoldilocksField>>::new_from_config(&config);
|
||||||
GateAdapter::<NNFMulSimple<5, QuinticExtension<GoldilocksField>>>::new_from_config(
|
|
||||||
&config,
|
|
||||||
);
|
|
||||||
|
|
||||||
test_low_degree::<_, _, 2>(gate);
|
test_low_degree::<_, _, 2>(gate);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_low_degree_recursive() -> Result<(), anyhow::Error> {
|
|
||||||
let config = CircuitConfig::standard_recursion_config();
|
|
||||||
let orig_gate =
|
|
||||||
GateAdapter::<NNFMulSimple<5, QuinticExtension<GoldilocksField>>>::new_from_config(
|
|
||||||
&config,
|
|
||||||
);
|
|
||||||
|
|
||||||
test_low_degree::<_, _, 2>(orig_gate.recursive_gate());
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_double_recursion() -> Result<(), anyhow::Error> {
|
|
||||||
let config = CircuitConfig::standard_recursion_config();
|
|
||||||
let orig_gate =
|
|
||||||
GateAdapter::<NNFMulSimple<5, QuinticExtension<GoldilocksField>>>::new_from_config(
|
|
||||||
&config,
|
|
||||||
);
|
|
||||||
test_eval_fns::<_, PoseidonGoldilocksConfig, _, 2>(orig_gate.recursive_gate())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ use crate::{
|
||||||
deserialize_bytes,
|
deserialize_bytes,
|
||||||
primitives::ec::{
|
primitives::ec::{
|
||||||
bits::{BigUInt320Target, CircuitBuilderBits},
|
bits::{BigUInt320Target, CircuitBuilderBits},
|
||||||
curve::{CircuitBuilderElliptic, PointTarget, WitnessWriteCurve, GROUP_ORDER},
|
curve::{CircuitBuilderSignature, PointTarget, WitnessWriteCurve, GROUP_ORDER},
|
||||||
},
|
},
|
||||||
serialize_bytes, Error,
|
serialize_bytes, Error,
|
||||||
},
|
},
|
||||||
|
|
@ -155,14 +155,14 @@ impl SignatureTarget {
|
||||||
msg: HashOutTarget,
|
msg: HashOutTarget,
|
||||||
public_key: &PointTarget,
|
public_key: &PointTarget,
|
||||||
) -> BoolTarget {
|
) -> BoolTarget {
|
||||||
let g = builder.constant_point(Point::generator());
|
|
||||||
let sig1_bits = self.s.bits;
|
let sig1_bits = self.s.bits;
|
||||||
let sig2_bits = self.e.bits;
|
let sig2_bits = self.e.bits;
|
||||||
let r = builder.linear_combination_points(&sig1_bits, &sig2_bits, &g, public_key);
|
let r = builder.linear_combination_point_gen(&sig1_bits, &sig2_bits, public_key);
|
||||||
let u_arr = r.u.components;
|
let u_arr = r.u.components;
|
||||||
let inputs = u_arr.into_iter().chain(msg.elements).collect::<Vec<_>>();
|
let inputs = u_arr.into_iter().chain(msg.elements).collect::<Vec<_>>();
|
||||||
let e_hash = hash_array_circuit(builder, &inputs);
|
let e_hash = hash_array_circuit(builder, &inputs);
|
||||||
let e = builder.field_elements_to_biguint(&e_hash);
|
let e = builder.field_elements_to_biguint(&e_hash);
|
||||||
|
|
||||||
builder.is_equal_slice(&self.e.limbs, &e.limbs)
|
builder.is_equal_slice(&self.e.limbs, &e.limbs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -405,22 +405,38 @@ mod test {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_verify_signature_circuit() -> Result<(), anyhow::Error> {
|
fn test_verify_signature_circuit() -> Result<(), anyhow::Error> {
|
||||||
let (public_key, msg, sig) = gen_signed_message();
|
let (public_key, msg, sig) = gen_signed_message(); // gets signed message
|
||||||
let config = CircuitConfig::standard_recursion_config();
|
let config = CircuitConfig::standard_recursion_config();
|
||||||
let mut builder = CircuitBuilder::<GoldilocksField, 2>::new(config);
|
let mut builder = CircuitBuilder::<GoldilocksField, 2>::new(config);
|
||||||
|
|
||||||
let key_t = builder.add_virtual_point_target();
|
let key_t = builder.add_virtual_point_target();
|
||||||
let msg_t = builder.add_virtual_hash();
|
let msg_t = builder.add_virtual_hash();
|
||||||
let sig_t = SignatureTarget::add_virtual_target(&mut builder);
|
let sig_t = SignatureTarget::add_virtual_target(&mut builder);
|
||||||
|
|
||||||
let verified = sig_t.verify(&mut builder, msg_t, &key_t);
|
let verified = sig_t.verify(&mut builder, msg_t, &key_t);
|
||||||
|
|
||||||
builder.assert_one(verified.target);
|
builder.assert_one(verified.target);
|
||||||
let mut pw = PartialWitness::new();
|
let mut pw = PartialWitness::new();
|
||||||
pw.set_point_target(&key_t, &public_key)?;
|
pw.set_point_target(&key_t, &public_key)?;
|
||||||
pw.set_hash_target(msg_t, msg.0.into())?;
|
pw.set_hash_target(msg_t, msg.0.into())?;
|
||||||
pw.set_biguint320_target(&sig_t.s, &sig.s)?;
|
pw.set_biguint320_target(&sig_t.s, &sig.s)?;
|
||||||
pw.set_biguint320_target(&sig_t.e, &sig.e)?;
|
pw.set_biguint320_target(&sig_t.e, &sig.e)?;
|
||||||
|
|
||||||
|
println!("Building circuit...");
|
||||||
|
let start = std::time::Instant::now();
|
||||||
let data = builder.build::<PoseidonGoldilocksConfig>();
|
let data = builder.build::<PoseidonGoldilocksConfig>();
|
||||||
|
println!("Circuit built in {:?}", start.elapsed());
|
||||||
|
|
||||||
|
println!("Generating proof...");
|
||||||
|
let start = std::time::Instant::now();
|
||||||
let proof = data.prove(pw)?;
|
let proof = data.prove(pw)?;
|
||||||
|
println!("Proof generated in {:?}", start.elapsed());
|
||||||
|
|
||||||
|
println!("Verifying proof...");
|
||||||
|
let start = std::time::Instant::now();
|
||||||
data.verify(proof)?;
|
data.verify(proof)?;
|
||||||
|
println!("Proof verified in {:?}", start.elapsed());
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -38,9 +38,6 @@ use crate::{
|
||||||
backends::plonky2::{
|
backends::plonky2::{
|
||||||
basetypes::{C, D},
|
basetypes::{C, D},
|
||||||
error::Result,
|
error::Result,
|
||||||
primitives::ec::gates::{
|
|
||||||
curve::ECAddHomogOffset, field::NNFMulSimple, generic::GateAdapter,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
measure_gates_begin, measure_gates_end,
|
measure_gates_begin, measure_gates_end,
|
||||||
middleware::F,
|
middleware::F,
|
||||||
|
|
@ -364,9 +361,6 @@ fn coset_interpolation_gate(
|
||||||
/// NOTE: The overhead between verifying any proof with just the `NoopGate` and verifying a proof
|
/// NOTE: The overhead between verifying any proof with just the `NoopGate` and verifying a proof
|
||||||
/// with all these standard gates is about 400 num_gates (rows), no matter the circuit size.
|
/// with all these standard gates is about 400 num_gates (rows), no matter the circuit size.
|
||||||
fn standard_gates(config: &CircuitConfig) -> Vec<GateRef<F, D>> {
|
fn standard_gates(config: &CircuitConfig) -> Vec<GateRef<F, D>> {
|
||||||
let nnf_mul_simple =
|
|
||||||
GateAdapter::<NNFMulSimple<5, QuinticExtension<F>>>::new_from_config(config);
|
|
||||||
let ec_add_homog_offset = GateAdapter::<ECAddHomogOffset>::new_from_config(config);
|
|
||||||
vec![
|
vec![
|
||||||
GateRef::new(plonky2::gates::noop::NoopGate {}),
|
GateRef::new(plonky2::gates::noop::NoopGate {}),
|
||||||
GateRef::new(plonky2::gates::constant::ConstantGate::new(
|
GateRef::new(plonky2::gates::constant::ConstantGate::new(
|
||||||
|
|
@ -391,10 +385,19 @@ fn standard_gates(config: &CircuitConfig) -> Vec<GateRef<F, D>> {
|
||||||
GateRef::new(plonky2::gates::random_access::RandomAccessGate::new_from_config(config, 4)),
|
GateRef::new(plonky2::gates::random_access::RandomAccessGate::new_from_config(config, 4)),
|
||||||
GateRef::new(plonky2::gates::random_access::RandomAccessGate::new_from_config(config, 5)),
|
GateRef::new(plonky2::gates::random_access::RandomAccessGate::new_from_config(config, 5)),
|
||||||
GateRef::new(plonky2::gates::random_access::RandomAccessGate::new_from_config(config, 6)),
|
GateRef::new(plonky2::gates::random_access::RandomAccessGate::new_from_config(config, 6)),
|
||||||
GateRef::new(nnf_mul_simple.recursive_gate()),
|
GateRef::new(
|
||||||
GateRef::new(nnf_mul_simple),
|
crate::backends::plonky2::primitives::ec::gates::field::NNFMulGate::<
|
||||||
GateRef::new(ec_add_homog_offset.recursive_gate()),
|
D,
|
||||||
GateRef::new(ec_add_homog_offset),
|
5,
|
||||||
|
QuinticExtension<F>,
|
||||||
|
>::new_from_config(config),
|
||||||
|
),
|
||||||
|
GateRef::new(
|
||||||
|
crate::backends::plonky2::primitives::ec::gates::curve::ECAddXuGate::new(),
|
||||||
|
),
|
||||||
|
GateRef::new(
|
||||||
|
crate::backends::plonky2::primitives::ec::gates::curve::ECAddHomogOffsetGate::new_from_config(config),
|
||||||
|
),
|
||||||
GateRef::new(plonky2::gates::exponentiation::ExponentiationGate::new_from_config(config)),
|
GateRef::new(plonky2::gates::exponentiation::ExponentiationGate::new_from_config(config)),
|
||||||
// It would be better do `CosetInterpolationGate::with_max_degree(4, 6)` but unfortunately
|
// It would be better do `CosetInterpolationGate::with_max_degree(4, 6)` but unfortunately
|
||||||
// that plonk2 method is `pub(crate)`, so we need to get around that somehow.
|
// that plonk2 method is `pub(crate)`, so we need to get around that somehow.
|
||||||
|
|
|
||||||
|
|
@ -27,9 +27,10 @@ use crate::backends::plonky2::{
|
||||||
curve::PointSquareRootGenerator,
|
curve::PointSquareRootGenerator,
|
||||||
field::QuotientGeneratorOEF,
|
field::QuotientGeneratorOEF,
|
||||||
gates::{
|
gates::{
|
||||||
curve::ECAddHomogOffset,
|
curve::{
|
||||||
field::NNFMulSimple,
|
ECAddHomogOffsetGate, ECAddHomogOffsetGenerator, ECAddXuGate, ECAddXuGenerator,
|
||||||
generic::{GateAdapter, RecursiveGateAdapter, RecursiveGenerator},
|
},
|
||||||
|
field::{NNFMulGate, NNFMulGenerator},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
@ -56,10 +57,9 @@ impl GateSerializer<F, D> for Pod2GateSerializer {
|
||||||
ReducingExtensionGate<D>,
|
ReducingExtensionGate<D>,
|
||||||
ReducingGate<D>,
|
ReducingGate<D>,
|
||||||
// pod2 custom gates
|
// pod2 custom gates
|
||||||
GateAdapter::<NNFMulSimple<5, QuinticExtension<F>>>,
|
NNFMulGate::<D, 5, QuinticExtension<F>>,
|
||||||
RecursiveGateAdapter::<D, NNFMulSimple<5, QuinticExtension<F>>>,
|
ECAddXuGate,
|
||||||
GateAdapter::<ECAddHomogOffset>,
|
ECAddHomogOffsetGate,
|
||||||
RecursiveGateAdapter::<D, ECAddHomogOffset>,
|
|
||||||
ComparisonGate::<F, D>
|
ComparisonGate::<F, D>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -127,10 +127,9 @@ impl WitnessGeneratorSerializer<F, D> for Pod2GeneratorSerializer {
|
||||||
QuotientGeneratorOEF<5, QuinticExtension<F>>,
|
QuotientGeneratorOEF<5, QuinticExtension<F>>,
|
||||||
PointSquareRootGenerator,
|
PointSquareRootGenerator,
|
||||||
ConditionalZeroGenerator<F, D>,
|
ConditionalZeroGenerator<F, D>,
|
||||||
RecursiveGenerator<D, NNFMulSimple<5, QuinticExtension<F>>>,
|
NNFMulGenerator::<D, 5, QuinticExtension<F>>,
|
||||||
RecursiveGenerator<1, NNFMulSimple<5, QuinticExtension<F>>>,
|
ECAddXuGenerator,
|
||||||
RecursiveGenerator<D, ECAddHomogOffset>,
|
ECAddHomogOffsetGenerator,
|
||||||
RecursiveGenerator<1, ECAddHomogOffset>,
|
|
||||||
ComparisonGenerator<F, D>,
|
ComparisonGenerator<F, D>,
|
||||||
TableGetGenerator
|
TableGetGenerator
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue