Skip to content

Commit

Permalink
Merged commits to support Circom and Solidity verifier for custom gates
Browse files Browse the repository at this point in the history
Change visibility of CommonCircuitData members

Change visibility of SelectorsInfo members

Change visibility of fri_all_polys() and fri_zs_polys()

Revert "Change visibility of fri_all_polys() and fri_zs_polys()"

This reverts commit 4fe841c50a3bd8132a1d990d6c00f4fa717ec01f.

Add public_inputs_hash interface

Add hash_public_inputs functions

Use hash_public_inputs

Use hash_public_inputs

Change visibility of GateRef

Add fn export_solidity_verification_code

Add fn export_solidity_verification_code in u32

Implement export_solidity_verification_code() for five gates

Refactor

Enable debugging

Refactoring

Refactoring

low_degree_interpolation gate done

ReducingGate and ReducingExtensionGate done

Refactoring and ArithmeticExtensionGate

MulExtensionGate done

Exponentiation done

RandomAccess done

WIP poseidon gate

Add challenge debug output

Revert "Add challenge debug output"

This reverts commit 35d7b265200b09563e67f27fd138704be1afeb29.

add export circom verification code functions

implemented constraints eval Circom circuits for const and public input gates

finished first set of full rounds in poseidon gate

implement state updates for partial_first_constant_layer

and mds_partial_layer_init

poseidon gate 1/2 done

circom poseidon gate constraints eval done

avoid circom bugs

add circom implementation of base_sum constraints eval

add circom implementation of low_degree_interpolation constraints eval

add circom implementation of reducing_extension constraints eval

add circom implementation of reducing gate constraints eval

add circom implementation of arithmetic gates constraints eval

add circom implementation of MulExtensionGate gates constraints eval

add circom implementation of Exponentiation gate constraints eval

add circom implementation of RandomAccessGate gate constraints eval

add circom implementation of PoseidonMdsGate gate constraints eval

fix a bug in circom implementation of RandomAccessGate constraints eval

remove debug info
  • Loading branch information
sai-deng committed Nov 21, 2022
1 parent d1d0893 commit 3a5abe2
Show file tree
Hide file tree
Showing 35 changed files with 1,367 additions and 20 deletions.
7 changes: 7 additions & 0 deletions insertion/src/insertion_gate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,13 @@ impl<F: RichField + Extendable<D>, const D: usize> Gate<F, D> for InsertionGate<
format!("{self:?}<D={D}>")
}

fn export_circom_verification_code(&self) -> String {
todo!()
}
fn export_solidity_verification_code(&self) -> String {
todo!()
}

fn eval_unfiltered(&self, vars: EvaluationVars<F, D>) -> Vec<F::Extension> {
let insertion_index = vars.local_wires[self.wires_insertion_index()];
let list_items = (0..self.vec_size)
Expand Down
9 changes: 8 additions & 1 deletion plonky2/src/gadgets/hash.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::field::extension::Extendable;
use crate::hash::hash_types::RichField;
use crate::hash::hash_types::{HashOutTarget, RichField};
use crate::hash::hashing::SPONGE_WIDTH;
use crate::iop::target::{BoolTarget, Target};
use crate::plonk::circuit_builder::CircuitBuilder;
Expand All @@ -24,4 +24,11 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
) -> [Target; SPONGE_WIDTH] {
H::permute_swapped(inputs, swap, self)
}

pub fn public_inputs_hash<H: AlgebraicHasher<F>>(
&mut self,
inputs: Vec<Target>,
) -> HashOutTarget {
H::public_inputs_hash(inputs, self)
}
}
45 changes: 45 additions & 0 deletions plonky2/src/gates/arithmetic_base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,51 @@ impl<F: RichField + Extendable<D>, const D: usize> Gate<F, D> for ArithmeticGate
format!("{self:?}")
}

fn export_circom_verification_code(&self) -> String {
let mut template_str = format!(
"template Arithmetic$NUM_OPS() {{
signal input constants[NUM_OPENINGS_CONSTANTS()][2];
signal input wires[NUM_OPENINGS_WIRES()][2];
signal input public_input_hash[4];
signal input constraints[NUM_GATE_CONSTRAINTS()][2];
signal output out[NUM_GATE_CONSTRAINTS()][2];
signal filter[2];
$SET_FILTER;
for (var i = 0; i < $NUM_OPS; i++) {{
out[i] <== ConstraintPush()(constraints[i], filter, GlExtSub()(wires[4 * i + 3], GlExtAdd()(GlExtMul()(GlExtMul()(wires[4 * i], wires[4 * i + 1]), constants[$NUM_SELECTORS + 0]), GlExtMul()(wires[4 * i + 2], constants[$NUM_SELECTORS + 1]))));
}}
for (var i = $NUM_OPS; i < NUM_GATE_CONSTRAINTS(); i++) {{
out[i] <== constraints[i];
}}
}}"
).to_string();
template_str = template_str.replace("$NUM_OPS", &*self.num_ops.to_string());
template_str
}
fn export_solidity_verification_code(&self) -> String {
let mut template_str = format!(
"library Arithmetic$NUM_OPSLib {{
using GoldilocksExtLib for uint64[2];
function set_filter(GatesUtilsLib.EvaluationVars memory ev) internal pure {{
$SET_FILTER;
}}
function eval(GatesUtilsLib.EvaluationVars memory ev, uint64[2][$NUM_GATE_CONSTRAINTS] memory constraints) internal pure {{
for (uint32 i = 0; i < $NUM_OPS; i++) {{
uint64[2] memory constraint;
constraint = ev.wires[4 * i].mul(ev.wires[4 * i + 1]).mul(ev.constants[$NUM_SELECTORS + 0]).add(ev.wires[4 * i + 2].mul(ev.constants[$NUM_SELECTORS + 1]));
GatesUtilsLib.push(constraints, ev.filter, i, ev.wires[4 * i + 3].sub(constraint));
}}
}}
}}"
)
.to_string();
template_str = template_str.replace("$NUM_OPS", &*self.num_ops.to_string());
template_str
}

fn eval_unfiltered(&self, vars: EvaluationVars<F, D>) -> Vec<F::Extension> {
let const_0 = vars.local_constants[0];
let const_1 = vars.local_constants[1];
Expand Down
51 changes: 51 additions & 0 deletions plonky2/src/gates/arithmetic_extension.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,57 @@ impl<F: RichField + Extendable<D>, const D: usize> Gate<F, D> for ArithmeticExte
format!("{self:?}")
}

fn export_circom_verification_code(&self) -> String {
let mut template_str = format!(
"template ArithmeticExtension$NUM_OPS() {{
signal input constants[NUM_OPENINGS_CONSTANTS()][2];
signal input wires[NUM_OPENINGS_WIRES()][2];
signal input public_input_hash[4];
signal input constraints[NUM_GATE_CONSTRAINTS()][2];
signal output out[NUM_GATE_CONSTRAINTS()][2];
signal filter[2];
$SET_FILTER;
signal m[$NUM_OPS][2][2];
for (var i = 0; i < $NUM_OPS; i++) {{
m[i] <== WiresAlgebraMul(4 * $D * i, 4 * $D * i + $D)(wires);
for (var j = 0; j < $D; j++) {{
out[i * $D + j] <== ConstraintPush()(constraints[i * $D + j], filter, GlExtSub()(wires[4 * $D * i + 3 * $D + j], GlExtAdd()(GlExtMul()(m[i][j], constants[$NUM_SELECTORS]), GlExtMul()(wires[4 * $D * i + 2 * $D + j], constants[$NUM_SELECTORS + 1]))));
}}
}}
for (var i = $NUM_OPS * $D; i < NUM_GATE_CONSTRAINTS(); i++) {{
out[i] <== constraints[i];
}}
}}"
).to_string();
template_str = template_str.replace("$NUM_OPS", &*self.num_ops.to_string());
template_str = template_str.replace("$D", &*D.to_string());
template_str
}
fn export_solidity_verification_code(&self) -> String {
let mut template_str = format!(
"library ArithmeticExtension$NUM_OPSLib {{
using GoldilocksExtLib for uint64[2];
function set_filter(GatesUtilsLib.EvaluationVars memory ev) internal pure {{
$SET_FILTER;
}}
function eval(GatesUtilsLib.EvaluationVars memory ev, uint64[2][$NUM_GATE_CONSTRAINTS] memory constraints) internal pure {{
for (uint32 i = 0; i < $NUM_OPS; i++) {{
uint64[2][$D] memory m = GatesUtilsLib.wires_algebra_mul(ev.wires, 4 * $D * i, 4 * $D * i + $D);
for (uint32 j = 0; j < $D; j++) {{
GatesUtilsLib.push(constraints, ev.filter, i * $D + j, ev.wires[4 * $D * i + 3 * $D + j].sub(m[j].mul(ev.constants[$NUM_SELECTORS]).add(ev.wires[4 * $D * i + 2 * $D + j].mul(ev.constants[$NUM_SELECTORS + 1]))));
}}
}}
}}
}}"
)
.to_string();
template_str = template_str.replace("$NUM_OPS", &*self.num_ops.to_string());
template_str
}

fn eval_unfiltered(&self, vars: EvaluationVars<F, D>) -> Vec<F::Extension> {
let const_0 = vars.local_constants[0];
let const_1 = vars.local_constants[1];
Expand Down
70 changes: 70 additions & 0 deletions plonky2/src/gates/base_sum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,76 @@ impl<F: RichField + Extendable<D>, const D: usize, const B: usize> Gate<F, D> fo
format!("{self:?} + Base: {B}")
}

fn export_circom_verification_code(&self) -> String {
let mut template_str = format!(
"template BaseSum$NUM_LIMBS() {{
signal input constants[NUM_OPENINGS_CONSTANTS()][2];
signal input wires[NUM_OPENINGS_WIRES()][2];
signal input public_input_hash[4];
signal input constraints[NUM_GATE_CONSTRAINTS()][2];
signal output out[NUM_GATE_CONSTRAINTS()][2];
signal filter[2];
$SET_FILTER;
component reduce = Reduce($NUM_LIMBS);
reduce.alpha <== GlExt($B, 0)();
reduce.old_eval <== GlExt(0, 0)();
for (var i = 1; i < $NUM_LIMBS + 1; i++) {{
reduce.in[i - 1] <== wires[i];
}}
out[0] <== ConstraintPush()(constraints[0], filter, GlExtSub()(reduce.out, wires[0]));
component product[$NUM_LIMBS][$B - 1];
for (var i = 0; i < $NUM_LIMBS; i++) {{
for (var j = 0; j < $B - 1; j++) {{
product[i][j] = GlExtMul();
if (j == 0) product[i][j].a <== wires[i + 1];
else product[i][j].a <== product[i][j - 1].out;
product[i][j].b <== GlExtSub()(wires[i + 1], GlExt(j + 1, 0)());
}}
out[i + 1] <== ConstraintPush()(constraints[i + 1], filter, product[i][$B - 2].out);
}}
for (var i = $NUM_LIMBS + 1; i < NUM_GATE_CONSTRAINTS(); i++) {{
out[i] <== constraints[i];
}}
}}"
)
.to_string();
template_str = template_str.replace("$NUM_LIMBS", &*self.num_limbs.to_string());
template_str = template_str.replace("$B", &*B.to_string());

template_str
}
fn export_solidity_verification_code(&self) -> String {
let mut template_str = format!("library BaseSum$NUM_LIMBSLib {{
using GoldilocksExtLib for uint64[2];
function set_filter(GatesUtilsLib.EvaluationVars memory ev) internal pure {{
$SET_FILTER;
}}
function eval(GatesUtilsLib.EvaluationVars memory ev, uint64[2][$NUM_GATE_CONSTRAINTS] memory constraints) internal pure {{
uint64[2] memory sum;
for (uint32 i = $NUM_LIMBS + 1; i > 1; i--) {{
sum = sum.mul(GatesUtilsLib.field_ext_from($B, 0)).add(ev.wires[i - 1]);
}}
GatesUtilsLib.push(constraints, ev.filter, 0, sum.sub(ev.wires[0]));
for (uint32 i = 1; i < $NUM_LIMBS + 1; i++) {{
uint64[2] memory product = ev.wires[i];
for (uint32 j = 1; j < $B; j++) {{
product = product.mul(ev.wires[i].sub(GatesUtilsLib.field_ext_from(j, 0)));
}}
GatesUtilsLib.push(constraints, ev.filter, i, product);
}}
}}
}}"
)
.to_string();

template_str = template_str.replace("$NUM_LIMBS", &*self.num_limbs.to_string());
template_str = template_str.replace("$B", &*B.to_string());

template_str
}

fn eval_unfiltered(&self, vars: EvaluationVars<F, D>) -> Vec<F::Extension> {
let sum = vars.local_wires[Self::WIRE_SUM];
let limbs = vars.local_wires[self.limbs()].to_vec();
Expand Down
44 changes: 44 additions & 0 deletions plonky2/src/gates/constant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,50 @@ impl<F: RichField + Extendable<D>, const D: usize> Gate<F, D> for ConstantGate {
format!("{self:?}")
}

fn export_circom_verification_code(&self) -> String {
let mut template_str = format!(
"template Constant$NUM_CONSTANTS() {{
signal input constants[NUM_OPENINGS_CONSTANTS()][2];
signal input wires[NUM_OPENINGS_WIRES()][2];
signal input public_input_hash[4];
signal input constraints[NUM_GATE_CONSTRAINTS()][2];
signal output out[NUM_GATE_CONSTRAINTS()][2];
signal filter[2];
$SET_FILTER;
for (var i = 0; i < $NUM_CONSTANTS; i++) {{
out[i] <== ConstraintPush()(constraints[i], filter, GlExtSub()(constants[$NUM_SELECTORS + i], wires[i]));
}}
for (var i = $NUM_CONSTANTS; i < NUM_GATE_CONSTRAINTS(); i++) {{
out[i] <== constraints[i];
}}
}}"
).to_string();
template_str = template_str.replace("$NUM_CONSTANTS", &*self.num_consts.to_string());
template_str
}
fn export_solidity_verification_code(&self) -> String {
let mut template_str = format!(
"library Constant$NUM_CONSTANTSLib {{
function set_filter(GatesUtilsLib.EvaluationVars memory ev) internal pure {{
$SET_FILTER;
}}
using GoldilocksExtLib for uint64[2];
function eval(GatesUtilsLib.EvaluationVars memory ev, uint64[2][$NUM_GATE_CONSTRAINTS] memory constraints) internal pure {{
for (uint32 i = 0; i < $NUM_CONSTANTS; i++) {{
GatesUtilsLib.push(constraints, ev.filter, i, ev.constants[$NUM_SELECTORS + i].sub(ev.wires[i]));
}}
}}
}}"
)
.to_string();

template_str = template_str.replace("$NUM_CONSTANTS", &*self.num_consts.to_string());

template_str
}

fn eval_unfiltered(&self, vars: EvaluationVars<F, D>) -> Vec<F::Extension> {
(0..self.num_consts)
.map(|i| {
Expand Down
71 changes: 71 additions & 0 deletions plonky2/src/gates/exponentiation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,77 @@ impl<F: RichField + Extendable<D>, const D: usize> Gate<F, D> for Exponentiation
format!("{self:?}<D={D}>")
}

fn export_circom_verification_code(&self) -> String {
let mut template_str = format!(
"template Exponentiation$NUM_POWER_BITS() {{
signal input constants[NUM_OPENINGS_CONSTANTS()][2];
signal input wires[NUM_OPENINGS_WIRES()][2];
signal input public_input_hash[4];
signal input constraints[NUM_GATE_CONSTRAINTS()][2];
signal output out[NUM_GATE_CONSTRAINTS()][2];
signal filter[2];
$SET_FILTER;
out[0] <== ConstraintPush()(constraints[0], filter,
GlExtSub()(GlExtMul()(GlExt(1, 0)(),
GlExtAdd()(GlExtMul()(wires[$NUM_POWER_BITS], wires[0]),
GlExtSub()(GlExt(1, 0)(), wires[$NUM_POWER_BITS])
)
),
wires[$NUM_POWER_BITS + 2]));
for (var i = 1; i < $NUM_POWER_BITS; i++) {{
// prev_intermediate_value * (cur_bit * wires[0] + (1 - cur_bit)) - wires[$NUM_POWER_BITS + 2 + i]
out[i] <== ConstraintPush()(constraints[i], filter,
GlExtSub()(GlExtMul()(GlExtSquare()(wires[$NUM_POWER_BITS + 1 + i]),
GlExtAdd()(GlExtMul()(wires[$NUM_POWER_BITS - i], wires[0]),
GlExtSub()(GlExt(1, 0)(), wires[$NUM_POWER_BITS - i])
)
),
wires[$NUM_POWER_BITS + 2 + i]));
}}
out[$NUM_POWER_BITS] <== ConstraintPush()(constraints[$NUM_POWER_BITS], filter, GlExtSub()(wires[$NUM_POWER_BITS + 1], wires[2 * $NUM_POWER_BITS + 1]));
for (var i = $NUM_POWER_BITS + 1; i < NUM_GATE_CONSTRAINTS(); i++) {{
out[i] <== constraints[i];
}}
}}"
).to_string();
template_str = template_str.replace("$NUM_POWER_BITS", &*self.num_power_bits.to_string());
template_str
}
fn export_solidity_verification_code(&self) -> String {
let mut template_str = format!(
"library Exponentiation$NUM_POWER_BITSLib {{
using GoldilocksExtLib for uint64[2];
function set_filter(GatesUtilsLib.EvaluationVars memory ev) internal pure {{
$SET_FILTER;
}}
function eval(GatesUtilsLib.EvaluationVars memory ev, uint64[2][$NUM_GATE_CONSTRAINTS] memory constraints) internal pure {{
// wire indices
// base: 0
// power_bits: [1 + i]
// output: num_power_bits + 1
// intermediate_values: [num_power_bits + 2 + i]
for (uint32 i = 0; i < $NUM_POWER_BITS; i++) {{
uint64[2] memory prev_intermediate_value;
if (i == 0) {{
prev_intermediate_value = GoldilocksExtLib.one();
}} else {{
prev_intermediate_value = ev.wires[$NUM_POWER_BITS + 1 + i].square();
}}
uint64[2] memory cur_bit = ev.wires[$NUM_POWER_BITS - i];
GatesUtilsLib.push(constraints, ev.filter, i, prev_intermediate_value.mul(cur_bit.mul(ev.wires[0]).add(GoldilocksExtLib.one().sub(cur_bit))).sub(ev.wires[$NUM_POWER_BITS + 2 + i]));
}}
GatesUtilsLib.push(constraints, ev.filter, $NUM_POWER_BITS, ev.wires[$NUM_POWER_BITS + 1].sub(ev.wires[2 * $NUM_POWER_BITS + 1]));
}}
}}"
)
.to_string();
template_str = template_str.replace("$NUM_POWER_BITS", &*self.num_power_bits.to_string());
template_str
}

fn eval_unfiltered(&self, vars: EvaluationVars<F, D>) -> Vec<F::Extension> {
let base = vars.local_wires[self.wire_base()];

Expand Down
5 changes: 4 additions & 1 deletion plonky2/src/gates/gate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ use crate::plonk::vars::{
pub trait Gate<F: RichField + Extendable<D>, const D: usize>: 'static + Send + Sync {
fn id(&self) -> String;

fn export_circom_verification_code(&self) -> String;
fn export_solidity_verification_code(&self) -> String;

fn eval_unfiltered(&self, vars: EvaluationVars<F, D>) -> Vec<F::Extension>;

/// Like `eval_unfiltered`, but specialized for points in the base field.
Expand Down Expand Up @@ -193,7 +196,7 @@ pub trait Gate<F: RichField + Extendable<D>, const D: usize>: 'static + Send + S

/// A wrapper around an `Rc<Gate>` which implements `PartialEq`, `Eq` and `Hash` based on gate IDs.
#[derive(Clone)]
pub struct GateRef<F: RichField + Extendable<D>, const D: usize>(pub(crate) Arc<dyn Gate<F, D>>);
pub struct GateRef<F: RichField + Extendable<D>, const D: usize>(pub Arc<dyn Gate<F, D>>);

impl<F: RichField + Extendable<D>, const D: usize> GateRef<F, D> {
pub fn new<G: Gate<F, D>>(gate: G) -> GateRef<F, D> {
Expand Down
7 changes: 7 additions & 0 deletions plonky2/src/gates/high_degree_interpolation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,13 @@ impl<F: RichField + Extendable<D>, const D: usize> Gate<F, D>
format!("{self:?}<D={D}>")
}

fn export_circom_verification_code(&self) -> String {
todo!()
}
fn export_solidity_verification_code(&self) -> String {
todo!()
}

fn eval_unfiltered(&self, vars: EvaluationVars<F, D>) -> Vec<F::Extension> {
let mut constraints = Vec::with_capacity(self.num_constraints());

Expand Down
Loading

0 comments on commit 3a5abe2

Please sign in to comment.