mirror of
https://github.com/arnaucube/plonky2-recursion-experiment.git
synced 2026-01-18 03:41:33 +01:00
Add InnerCircuit abstraction
Abstract signature gadget into InnerCircuit. So that PODs just need to implement the InnerCircuit and then plug and recurse.
This commit is contained in:
73
src/example_innercircuit.rs
Normal file
73
src/example_innercircuit.rs
Normal file
@@ -0,0 +1,73 @@
|
||||
/// This file contains a simple example implementing the InnerCircuit trait, by a circuit that
|
||||
/// checks a signature over the given msg.
|
||||
use anyhow::Result;
|
||||
use plonky2::iop::target::BoolTarget;
|
||||
use plonky2::iop::witness::PartialWitness;
|
||||
use plonky2::plonk::circuit_builder::CircuitBuilder;
|
||||
|
||||
use sch::schnorr::*;
|
||||
use sch::schnorr_prover::*;
|
||||
|
||||
use super::tree_recursion::{selector_gate, InnerCircuit};
|
||||
use super::{C, D, F};
|
||||
|
||||
pub struct ExampleGadgetInput {
|
||||
pub pk: SchnorrPublicKey,
|
||||
pub sig: SchnorrSignature,
|
||||
}
|
||||
|
||||
pub struct ExampleGadgetTargets {
|
||||
pub pk_targ: SchnorrPublicKeyTarget,
|
||||
pub sig_targ: SchnorrSignatureTarget,
|
||||
}
|
||||
|
||||
/// The logic of this gadget verifies the given signature if `selector==0`.
|
||||
///
|
||||
/// It implements the InnerCircuit trait, so it contains the methods to `add_targets` (ie. create
|
||||
/// the targets, the logic of the circuit), and `set_targets` (ie. set the specific values to be
|
||||
/// used for the previously created targets).
|
||||
pub struct ExampleGadget {}
|
||||
|
||||
impl InnerCircuit for ExampleGadget {
|
||||
type Input = ExampleGadgetInput;
|
||||
type Targets = ExampleGadgetTargets;
|
||||
|
||||
fn add_targets(
|
||||
mut builder: &mut CircuitBuilder<F, D>,
|
||||
selector_booltarg: &BoolTarget,
|
||||
msg_targ: &MessageTarget,
|
||||
) -> Result<Self::Targets> {
|
||||
// signature verification:
|
||||
let sb: SchnorrBuilder = SchnorrBuilder {};
|
||||
let pk_targ = SchnorrPublicKeyTarget::new_virtual(&mut builder);
|
||||
let sig_targ = SchnorrSignatureTarget::new_virtual(&mut builder);
|
||||
let sig_verif_targ = sb.verify_sig::<C>(&mut builder, &sig_targ, &msg_targ, &pk_targ);
|
||||
|
||||
// if selector==0: verify the signature; else: don't check it. ie:
|
||||
// if selector=0: check that sig_verif==1
|
||||
// if selector=1: check that one==1
|
||||
let one = builder.one();
|
||||
let expected = selector_gate(
|
||||
builder,
|
||||
sig_verif_targ.target,
|
||||
one,
|
||||
selector_booltarg.target,
|
||||
);
|
||||
let one_2 = builder.one();
|
||||
builder.connect(expected, one_2);
|
||||
|
||||
Ok(Self::Targets { pk_targ, sig_targ })
|
||||
}
|
||||
|
||||
fn set_targets(
|
||||
pw: &mut PartialWitness<F>,
|
||||
targets: &Self::Targets,
|
||||
pod: &Self::Input,
|
||||
) -> Result<()> {
|
||||
// set signature related values:
|
||||
targets.pk_targ.set_witness(pw, &pod.pk).unwrap();
|
||||
targets.sig_targ.set_witness(pw, &pod.sig).unwrap();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
#![allow(non_upper_case_globals)]
|
||||
#![allow(non_camel_case_types)]
|
||||
|
||||
pub mod sig_gadget;
|
||||
pub mod example_innercircuit;
|
||||
pub mod tree_recursion;
|
||||
|
||||
use plonky2::field::goldilocks_field::GoldilocksField;
|
||||
|
||||
@@ -1,105 +0,0 @@
|
||||
use anyhow::Result;
|
||||
use plonky2::iop::target::{BoolTarget, Target};
|
||||
use plonky2::iop::witness::{PartialWitness, WitnessWrite};
|
||||
use plonky2::plonk::circuit_builder::CircuitBuilder;
|
||||
|
||||
use sch::schnorr::*;
|
||||
use sch::schnorr_prover::*;
|
||||
|
||||
use super::{C, D, F};
|
||||
|
||||
/// if s==0: returns x
|
||||
/// if s==1: returns y
|
||||
/// Warning: this method assumes all input values are ensured to be \in {0,1}
|
||||
fn selector_gate(builder: &mut CircuitBuilder<F, D>, x: Target, y: Target, s: Target) -> Target {
|
||||
// z = x + s(y-x)
|
||||
let y_x = builder.sub(y, x);
|
||||
// z = x+s(y-x) <==> mul_add(s, yx, x)=s*(y-x)+x
|
||||
builder.mul_add(s, y_x, x)
|
||||
}
|
||||
|
||||
/// ensures b \in {0,1}
|
||||
fn binary_check(builder: &mut CircuitBuilder<F, D>, b: Target) {
|
||||
let zero = builder.zero();
|
||||
let one = builder.one();
|
||||
// b * (b-1) == 0
|
||||
let b_1 = builder.sub(b, one);
|
||||
let r = builder.mul(b, b_1);
|
||||
builder.connect(r, zero);
|
||||
}
|
||||
|
||||
pub struct PODInput {
|
||||
pub pk: SchnorrPublicKey,
|
||||
pub sig: SchnorrSignature,
|
||||
}
|
||||
|
||||
/// The logic of this gadget verifies the given signature if `selector==0`.
|
||||
/// We reuse this gadget for all the the signature verifications in the node of the recursion tree.
|
||||
///
|
||||
/// Contains the methods to `add_targets` (ie. create the targets, the logic of the circuit), and
|
||||
/// `set_targets` (ie. set the specific values to be used for the previously created targets).
|
||||
pub struct PODGadgetTargets {
|
||||
pub selector_targ: Target,
|
||||
pub selector_booltarg: BoolTarget,
|
||||
|
||||
pub pk_targ: SchnorrPublicKeyTarget,
|
||||
pub sig_targ: SchnorrSignatureTarget,
|
||||
}
|
||||
|
||||
impl PODGadgetTargets {
|
||||
pub fn add_targets(
|
||||
mut builder: &mut CircuitBuilder<F, D>,
|
||||
msg_targ: &MessageTarget,
|
||||
) -> Result<Self> {
|
||||
let selector_targ = builder.add_virtual_target();
|
||||
// ensure that selector_booltarg is \in {0,1}
|
||||
binary_check(builder, selector_targ);
|
||||
let selector_booltarg = BoolTarget::new_unsafe(selector_targ);
|
||||
|
||||
// signature verification:
|
||||
let sb: SchnorrBuilder = SchnorrBuilder {};
|
||||
let pk_targ = SchnorrPublicKeyTarget::new_virtual(&mut builder);
|
||||
let sig_targ = SchnorrSignatureTarget::new_virtual(&mut builder);
|
||||
let sig_verif_targ = sb.verify_sig::<C>(&mut builder, &sig_targ, &msg_targ, &pk_targ);
|
||||
|
||||
// - if selector=0
|
||||
// verify_sig==1 && proof_enabled=0
|
||||
// - if selector=1
|
||||
// verify_sig==NaN && proof_enabled=1 (don't check the sig)
|
||||
//
|
||||
// if selector=0: check that sig_verif==1
|
||||
// if selector=1: check that one==1
|
||||
let one = builder.one();
|
||||
let expected = selector_gate(
|
||||
builder,
|
||||
sig_verif_targ.target,
|
||||
one,
|
||||
selector_booltarg.target,
|
||||
);
|
||||
let one_2 = builder.one();
|
||||
builder.connect(expected, one_2);
|
||||
|
||||
Ok(Self {
|
||||
selector_targ,
|
||||
selector_booltarg,
|
||||
pk_targ,
|
||||
sig_targ,
|
||||
})
|
||||
}
|
||||
pub fn set_targets(
|
||||
&mut self,
|
||||
pw: &mut PartialWitness<F>,
|
||||
// if `selector` set to 0 will verify the given signature, if set to 1 won't (and the
|
||||
// recursion layer will verify the respective plonky2 proof)
|
||||
selector: F,
|
||||
pod: &PODInput,
|
||||
) -> Result<()> {
|
||||
pw.set_target(self.selector_targ, selector)?;
|
||||
|
||||
// set signature related values:
|
||||
self.pk_targ.set_witness(pw, &pod.pk).unwrap();
|
||||
self.sig_targ.set_witness(pw, &pod.sig).unwrap();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,97 +1,150 @@
|
||||
/// N-arity tree of recursion with conditionals.
|
||||
///
|
||||
/// p_root
|
||||
/// ▲
|
||||
/// │
|
||||
/// ┌────────┐
|
||||
/// │ F │
|
||||
/// └────────┘
|
||||
/// ▲ ▲ ▲ ▲
|
||||
/// ┌─┘ │ │ └─┐
|
||||
/// ┌────┘ ┌─┘ └┐ └───┐
|
||||
/// │ │ ... │ │
|
||||
/// ┌────────┐┌┴┐┌─┐┌┴┐ ┌────────┐
|
||||
/// │ F ││.││.││.│ │ F │
|
||||
/// └────────┘└─┘└─┘└─┘ └────────┘
|
||||
/// ▲ ▲ ▲ ▲ ▲ ▲ ▲ ▲
|
||||
/// ┌─┘ │ └┐ └─┐ ┌─┘┌┘ └┐ └┐
|
||||
/// │ │ │ │ │ │ │ │
|
||||
/// p_1 p_2 ... p_n p'_1 p'_2... p'_n
|
||||
///
|
||||
///
|
||||
/// where each p_i is either
|
||||
/// - signature verification
|
||||
/// - recursive plonky2 proof (proof that verifies previous proof)
|
||||
/// (generated by `RecursiveCircuit::prove_step` method)
|
||||
/// in other words, each p_i is checking:
|
||||
/// `(signature proof OR recursive proof)`
|
||||
///
|
||||
/// Each node of the recursion tree, ie. each F, verifies the N incoming p_i's, that is
|
||||
/// `(signature proof OR recursive proof) AND ... AND (signature proof OR recursive proof)`
|
||||
/// and produces a new proof.
|
||||
///
|
||||
/// For example, if N is set to N=2, then we work with a binary recursion tree:
|
||||
/// p_root
|
||||
/// ▲
|
||||
/// │
|
||||
/// ┌─┴─┐
|
||||
/// │ F │
|
||||
/// └───┘
|
||||
/// ▲ ▲
|
||||
/// ┌─┘ └─┐
|
||||
/// ┌───┘ └───┐
|
||||
/// │p_5 │p_6
|
||||
/// ┌─┴─┐ ┌─┴─┐
|
||||
/// │ F │ │ F │
|
||||
/// └───┘ └───┘
|
||||
/// ▲ ▲ ▲ ▲
|
||||
/// ┌─┘ └─┐ ┌─┘ └─┐
|
||||
/// │ │ │ │
|
||||
/// p_1 p_2 p_3 p_4
|
||||
///
|
||||
/// So that each node (F box) is verifying 2 p_i's, ie:
|
||||
/// `(signature proof OR recursive proof) AND (signature proof OR recursive proof)`
|
||||
///
|
||||
///
|
||||
/// With N=3, each node will be verifying 3 p_i's.
|
||||
/// `(signature proof OR recursive proof) AND (signature proof OR recursive proof) AND (signature proof OR recursive proof)`
|
||||
///
|
||||
///
|
||||
///
|
||||
/// Also, notice that if we set N=1, it is directly a linear chain of recursive proofs ('tree' of
|
||||
/// arity 1):
|
||||
/// ┌─┐ ┌─┐ ┌─┐ ┌─┐
|
||||
/// ─────►│F├────►│F├────►│F├────►│F├────►
|
||||
/// p_1 └─┘ p_2 └─┘ p_3 └─┘ p_4 └─┘ p_5
|
||||
///
|
||||
/// where each p_i is proving: `(signature proof OR recursive proof)`.
|
||||
///
|
||||
///
|
||||
/// To run the tests that checks this logic:
|
||||
/// cargo test --release test_tree_recursion -- --nocapture
|
||||
/*
|
||||
N-arity tree of recursion with conditionals.
|
||||
|
||||
p_root
|
||||
▲
|
||||
│
|
||||
┌────────┐
|
||||
│ F │
|
||||
└────────┘
|
||||
▲ ▲ ▲ ▲
|
||||
┌─┘ │ │ └─┐
|
||||
┌────┘ ┌─┘ └┐ └───┐
|
||||
│ │ ... │ │
|
||||
┌────────┐┌┴┐┌─┐┌┴┐ ┌────────┐
|
||||
│ F ││.││.││.│ │ F │
|
||||
└────────┘└─┘└─┘└─┘ └────────┘
|
||||
▲ ▲ ▲ ▲ ▲ ▲ ▲ ▲
|
||||
┌─┘ │ └┐ └─┐ ┌─┘┌┘ └┐ └┐
|
||||
│ │ │ │ │ │ │ │
|
||||
p_1 p_2 ... p_n p'_1 p'_2... p'_n
|
||||
|
||||
|
||||
where each p_i is either
|
||||
- InnerCircuit verification
|
||||
- recursive plonky2 proof (proof that verifies previous proof)
|
||||
(generated by `RecursiveCircuit::prove_step` method)
|
||||
in other words, each p_i is checking:
|
||||
`(InnerCircuit OR recursive proof verify)`
|
||||
|
||||
Each node of the recursion tree, ie. each F, verifies the N incoming p_i's, that is
|
||||
`(InnerCircuit OR recursive proof verify) AND ... AND (InnerCircuit OR recursive proof verify)`
|
||||
and produces a new proof.
|
||||
|
||||
For example, if N is set to N=2, then we work with a binary recursion tree:
|
||||
p_root
|
||||
▲
|
||||
│
|
||||
┌─┴─┐
|
||||
│ F │
|
||||
└───┘
|
||||
▲ ▲
|
||||
┌─┘ └─┐
|
||||
┌───┘ └───┐
|
||||
│p_5 │p_6
|
||||
┌─┴─┐ ┌─┴─┐
|
||||
│ F │ │ F │
|
||||
└───┘ └───┘
|
||||
▲ ▲ ▲ ▲
|
||||
┌─┘ └─┐ ┌─┘ └─┐
|
||||
│ │ │ │
|
||||
p_1 p_2 p_3 p_4
|
||||
|
||||
p_i: `(InnerCircuit OR recursive-proof-verification)`
|
||||
|
||||
|
||||
So that each node (F box) is verifying 2 p_i's, ie:
|
||||
`(InnerCircuit OR recursive-proof-verification) AND (InnerCircuit OR recursive-proof-verification)`
|
||||
|
||||
|
||||
With N=3, each node will be verifying 3 p_i's.
|
||||
`(InnerCircuit OR recursive-proof-verification) AND (InnerCircuit OR recursive-proof-verification) AND (InnerCircuit OR recursive-proof-verification)`
|
||||
|
||||
|
||||
|
||||
Also, notice that if we set N=1, it is directly a linear chain of recursive proofs ('tree' of
|
||||
arity 1):
|
||||
┌─┐ ┌─┐ ┌─┐ ┌─┐
|
||||
─────►│F├────►│F├────►│F├────►│F├────►
|
||||
p_1 └─┘ p_2 └─┘ p_3 └─┘ p_4 └─┘ p_5
|
||||
|
||||
where each p_i is proving: `(InnerCircuit OR recursive-proof-verification)`.
|
||||
|
||||
|
||||
To run the tests that checks this logic:
|
||||
cargo test --release test_tree_recursion -- --nocapture
|
||||
*/
|
||||
use anyhow::{anyhow, Result};
|
||||
use plonky2::field::types::Field;
|
||||
use plonky2::gates::noop::NoopGate;
|
||||
use plonky2::iop::target::{BoolTarget, Target};
|
||||
use plonky2::iop::witness::{PartialWitness, WitnessWrite};
|
||||
use plonky2::plonk::circuit_builder::CircuitBuilder;
|
||||
use plonky2::plonk::circuit_data::{
|
||||
CircuitConfig, CircuitData, VerifierCircuitData, VerifierCircuitTarget,
|
||||
};
|
||||
use plonky2::plonk::proof::{ProofWithPublicInputs, ProofWithPublicInputsTarget};
|
||||
use std::marker::PhantomData;
|
||||
use std::time::Instant;
|
||||
|
||||
use sch::schnorr_prover::*;
|
||||
|
||||
use super::{
|
||||
sig_gadget::{PODGadgetTargets, PODInput},
|
||||
PlonkyProof, C, D, F,
|
||||
};
|
||||
use super::{PlonkyProof, C, D, F};
|
||||
|
||||
/// Contains the methods to `add_targets` (ie. create the targets, the logic of the circuit), and
|
||||
/// `set_targets` (ie. set the specific values to be used for the previously created targets).
|
||||
pub struct RecursiveCircuit<const N: usize> {
|
||||
/// if s==0: returns x
|
||||
/// if s==1: returns y
|
||||
/// Warning: this method assumes all input values are ensured to be \in {0,1}
|
||||
pub fn selector_gate(
|
||||
builder: &mut CircuitBuilder<F, D>,
|
||||
x: Target,
|
||||
y: Target,
|
||||
s: Target,
|
||||
) -> Target {
|
||||
// z = x + s(y-x)
|
||||
let y_x = builder.sub(y, x);
|
||||
// z = x+s(y-x) <==> mul_add(s, yx, x)=s*(y-x)+x
|
||||
builder.mul_add(s, y_x, x)
|
||||
}
|
||||
|
||||
/// ensures b \in {0,1}
|
||||
pub fn binary_check(builder: &mut CircuitBuilder<F, D>, b: Target) {
|
||||
let zero = builder.zero();
|
||||
let one = builder.one();
|
||||
// b * (b-1) == 0
|
||||
let b_1 = builder.sub(b, one);
|
||||
let r = builder.mul(b, b_1);
|
||||
builder.connect(r, zero);
|
||||
}
|
||||
|
||||
/// InnerCircuit is the trait that is used to define the logic of the circuit that is used at each
|
||||
/// node of the recursive tree.
|
||||
pub trait InnerCircuit {
|
||||
type Input;
|
||||
type Targets;
|
||||
|
||||
fn add_targets(
|
||||
builder: &mut CircuitBuilder<F, D>,
|
||||
selector_booltarg: &BoolTarget,
|
||||
msg_targ: &MessageTarget,
|
||||
) -> Result<Self::Targets>;
|
||||
|
||||
fn set_targets(
|
||||
pw: &mut PartialWitness<F>,
|
||||
targets: &Self::Targets,
|
||||
input: &Self::Input,
|
||||
) -> Result<()>;
|
||||
}
|
||||
|
||||
/// RecursiveCircuit defines the circuit used on each node of the recursion tree, which is doing
|
||||
/// `(InnerCircuit OR recursive-proof-verification)` N times, and generating a new proof that can
|
||||
/// be verified by the same circuit itself.
|
||||
///
|
||||
/// It contains the methods to `add_targets` (ie. create the targets, the logic of the circuit),
|
||||
/// and `set_targets` (ie. set the specific values to be used for the previously created targets).
|
||||
pub struct RecursiveCircuit<I: InnerCircuit, const N: usize> {
|
||||
msg_targ: MessageTarget,
|
||||
sigs_targ: Vec<PODGadgetTargets>,
|
||||
selectors_targ: Vec<Target>,
|
||||
inner_circuit_targ: Vec<I::Targets>,
|
||||
proofs_targ: Vec<ProofWithPublicInputsTarget<D>>,
|
||||
// the next two are common for all the given proofs. It is the data for this circuit itself
|
||||
// (cyclic circuit).
|
||||
@@ -99,7 +152,7 @@ pub struct RecursiveCircuit<const N: usize> {
|
||||
verifier_data: VerifierCircuitData<F, C, D>,
|
||||
}
|
||||
|
||||
impl<const N: usize> RecursiveCircuit<N> {
|
||||
impl<I: InnerCircuit, const N: usize> RecursiveCircuit<I, N> {
|
||||
pub fn prepare_public_inputs(
|
||||
verifier_data: VerifierCircuitData<F, C, D>,
|
||||
msg: Vec<F>,
|
||||
@@ -130,11 +183,23 @@ impl<const N: usize> RecursiveCircuit<N> {
|
||||
// set msg as public input
|
||||
builder.register_public_inputs(&msg_targ.msg);
|
||||
|
||||
// build the signature verification logic
|
||||
let mut sigs_targ: Vec<PODGadgetTargets> = vec![];
|
||||
// build the InnerCircuit logic. Also set the selectors, used both by the InnerCircuit and
|
||||
// by the recursive proofs verifications.
|
||||
let mut selectors_targ: Vec<Target> = vec![];
|
||||
let mut selectors_bool_targ: Vec<BoolTarget> = vec![];
|
||||
let mut inner_circuit_targ: Vec<I::Targets> = vec![];
|
||||
for _ in 0..N {
|
||||
let sig_targets = PODGadgetTargets::add_targets(builder, &msg_targ)?;
|
||||
sigs_targ.push(sig_targets);
|
||||
// selectors:
|
||||
let selector_F_targ = builder.add_virtual_target();
|
||||
// ensure that selector_booltarg is \in {0,1}
|
||||
binary_check(builder, selector_F_targ);
|
||||
let selector_bool_targ = BoolTarget::new_unsafe(selector_F_targ);
|
||||
selectors_targ.push(selector_F_targ);
|
||||
selectors_bool_targ.push(selector_bool_targ);
|
||||
|
||||
// inner circuits:
|
||||
let inner_circuit_targets = I::add_targets(builder, &selector_bool_targ, &msg_targ)?;
|
||||
inner_circuit_targ.push(inner_circuit_targets);
|
||||
}
|
||||
|
||||
// proof verification:
|
||||
@@ -146,7 +211,7 @@ impl<const N: usize> RecursiveCircuit<N> {
|
||||
for i in 0..N {
|
||||
let proof_targ = builder.add_virtual_proof_with_pis(&common_data);
|
||||
builder.conditionally_verify_cyclic_proof_or_dummy::<C>(
|
||||
sigs_targ[i].selector_booltarg,
|
||||
selectors_bool_targ[i],
|
||||
&proof_targ,
|
||||
&common_data,
|
||||
)?;
|
||||
@@ -155,7 +220,8 @@ impl<const N: usize> RecursiveCircuit<N> {
|
||||
|
||||
Ok(Self {
|
||||
msg_targ,
|
||||
sigs_targ,
|
||||
selectors_targ,
|
||||
inner_circuit_targ,
|
||||
proofs_targ,
|
||||
verifier_data_targ,
|
||||
verifier_data,
|
||||
@@ -166,18 +232,19 @@ impl<const N: usize> RecursiveCircuit<N> {
|
||||
&mut self,
|
||||
pw: &mut PartialWitness<F>,
|
||||
msg: &Vec<F>,
|
||||
// if selectors[i]==0: verify pods[i] signature. if selectors[i]==1: verify
|
||||
// recursive_proof[i]
|
||||
// if selectors[i]==0: verify InnerCircuit. if selectors[i]==1: verify recursive_proof[i]
|
||||
selectors: Vec<F>,
|
||||
pods_input: Vec<PODInput>,
|
||||
inner_circuit_input: Vec<I::Input>,
|
||||
recursive_proofs: &Vec<PlonkyProof>,
|
||||
) -> Result<()> {
|
||||
// set the msg value (used by all N sig gadgets)
|
||||
// set the msg value (used by all N InnerCircuit gadgets)
|
||||
self.msg_targ.set_witness(pw, &msg).unwrap();
|
||||
|
||||
// set the signature related values
|
||||
// set the InnerCircuit related values
|
||||
for i in 0..N {
|
||||
self.sigs_targ[i].set_targets(pw, selectors[i], &pods_input[i])?;
|
||||
pw.set_target(self.selectors_targ[i], selectors[i])?;
|
||||
|
||||
I::set_targets(pw, &self.inner_circuit_targ[i], &inner_circuit_input[i])?;
|
||||
}
|
||||
|
||||
// set proof related values:
|
||||
@@ -185,8 +252,10 @@ impl<const N: usize> RecursiveCircuit<N> {
|
||||
// recursive proofs verification
|
||||
pw.set_verifier_data_target(&self.verifier_data_targ, &self.verifier_data.verifier_only)?;
|
||||
|
||||
let public_inputs =
|
||||
RecursiveCircuit::<N>::prepare_public_inputs(self.verifier_data.clone(), msg.clone());
|
||||
let public_inputs = RecursiveCircuit::<I, N>::prepare_public_inputs(
|
||||
self.verifier_data.clone(),
|
||||
msg.clone(),
|
||||
);
|
||||
for i in 0..N {
|
||||
pw.set_proof_with_pis_target(
|
||||
&self.proofs_targ[i],
|
||||
@@ -201,10 +270,9 @@ impl<const N: usize> RecursiveCircuit<N> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Recursion<const N: usize> {}
|
||||
|
||||
pub fn common_data_for_recursion<const N: usize>(msg_len: usize) -> Result<CircuitData<F, C, D>> {
|
||||
pub fn common_data_for_recursion<I: InnerCircuit, const N: usize>(
|
||||
msg_len: usize,
|
||||
) -> Result<CircuitData<F, C, D>> {
|
||||
// 1st
|
||||
let config = CircuitConfig::standard_recursion_config();
|
||||
let builder = CircuitBuilder::<F, D>::new(config);
|
||||
@@ -226,7 +294,6 @@ pub fn common_data_for_recursion<const N: usize>(msg_len: usize) -> Result<Circu
|
||||
let config = CircuitConfig::standard_recursion_config();
|
||||
let mut builder = CircuitBuilder::<F, D>::new(config.clone());
|
||||
let msg_targ = MessageTarget::new_with_size(&mut builder, msg_len);
|
||||
// sigs verify
|
||||
builder.register_public_inputs(&msg_targ.msg);
|
||||
|
||||
builder.add_gate(
|
||||
@@ -238,8 +305,13 @@ pub fn common_data_for_recursion<const N: usize>(msg_len: usize) -> Result<Circu
|
||||
vec![],
|
||||
);
|
||||
|
||||
let _ = PODGadgetTargets::add_targets(&mut builder, &msg_targ).unwrap();
|
||||
let _ = PODGadgetTargets::add_targets(&mut builder, &msg_targ).unwrap();
|
||||
// InnerCircuits targets
|
||||
for _ in 0..N {
|
||||
let selector_F_targ = builder.add_virtual_target();
|
||||
binary_check(&mut builder, selector_F_targ);
|
||||
let b = BoolTarget::new_unsafe(selector_F_targ);
|
||||
let _ = I::add_targets(&mut builder, &b, &msg_targ).unwrap();
|
||||
}
|
||||
|
||||
// proofs verify
|
||||
let verifier_data = builder.add_verifier_data_public_inputs();
|
||||
@@ -277,15 +349,20 @@ fn compute_num_gates<const N: usize>() -> Result<usize> {
|
||||
Ok(n_gates)
|
||||
}
|
||||
|
||||
impl<const N: usize> Recursion<N> {
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Recursion<I: InnerCircuit, const N: usize> {
|
||||
_i: PhantomData<I>,
|
||||
}
|
||||
|
||||
impl<I: InnerCircuit, const N: usize> Recursion<I, N> {
|
||||
/// returns the full-recursive CircuitData
|
||||
pub fn circuit_data(msg_len: usize) -> Result<CircuitData<F, C, D>> {
|
||||
let mut data = common_data_for_recursion::<N>(msg_len)?;
|
||||
let mut data = common_data_for_recursion::<I, N>(msg_len)?;
|
||||
|
||||
// build the actual RecursiveCircuit circuit data
|
||||
let config = CircuitConfig::standard_recursion_config();
|
||||
let mut builder = CircuitBuilder::new(config);
|
||||
let _ = RecursiveCircuit::<N>::add_targets(&mut builder, data.verifier_data(), msg_len)?;
|
||||
let _ = RecursiveCircuit::<I, N>::add_targets(&mut builder, data.verifier_data(), msg_len)?;
|
||||
dbg!(builder.num_gates());
|
||||
data = builder.build::<C>();
|
||||
|
||||
@@ -294,22 +371,18 @@ impl<const N: usize> Recursion<N> {
|
||||
|
||||
pub fn prove_step(
|
||||
verifier_data: VerifierCircuitData<F, C, D>,
|
||||
msg: &Vec<F>, // will be an array of "pod roots (hashes)'
|
||||
// if selectors[i]==0: verify pods[i] signature. if selectors[i]==1: verify
|
||||
// recursive_proof[i]
|
||||
msg: &Vec<F>,
|
||||
// if selectors[i]==0: verify InnerCircuit. if selectors[i]==1: verify recursive_proof[i]
|
||||
selectors: Vec<F>,
|
||||
pods_input: Vec<PODInput>,
|
||||
inner_circuits_input: Vec<I::Input>,
|
||||
recursive_proofs: &Vec<PlonkyProof>,
|
||||
) -> Result<PlonkyProof> {
|
||||
println!("prove_step:");
|
||||
for i in 0..N {
|
||||
if selectors[i].is_nonzero() {
|
||||
println!(" (pods_input[{}].selector==1), verify {}-th proof", i, i);
|
||||
println!(" (selectors[{}]==1), verify {}-th proof", i, i);
|
||||
} else {
|
||||
println!(
|
||||
" (pods_input[{}].selector==0), verify {}-th signature",
|
||||
i, i
|
||||
);
|
||||
println!(" (selectors[{}]==0), verify {}-th inner circuit", i, i);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -319,13 +392,19 @@ impl<const N: usize> Recursion<N> {
|
||||
// assign the targets
|
||||
let start = Instant::now();
|
||||
let mut circuit =
|
||||
RecursiveCircuit::<N>::add_targets(&mut builder, verifier_data.clone(), msg.len())?;
|
||||
RecursiveCircuit::<I, N>::add_targets(&mut builder, verifier_data.clone(), msg.len())?;
|
||||
println!("RecursiveCircuit::add_targets(): {:?}", start.elapsed());
|
||||
|
||||
// fill the targets
|
||||
let mut pw = PartialWitness::new();
|
||||
let start = Instant::now();
|
||||
circuit.set_targets(&mut pw, msg, selectors, pods_input, recursive_proofs)?;
|
||||
circuit.set_targets(
|
||||
&mut pw,
|
||||
msg,
|
||||
selectors,
|
||||
inner_circuits_input,
|
||||
recursive_proofs,
|
||||
)?;
|
||||
println!("circuit.set_targets(): {:?}", start.elapsed());
|
||||
|
||||
let start = Instant::now();
|
||||
@@ -367,6 +446,7 @@ mod tests {
|
||||
use std::time::Instant;
|
||||
|
||||
use super::*;
|
||||
use crate::example_innercircuit::{ExampleGadget, ExampleGadgetInput};
|
||||
use sch::schnorr::*;
|
||||
|
||||
// this sets the plonky2 internal logs level
|
||||
@@ -384,7 +464,7 @@ mod tests {
|
||||
// For testing: change the following `N` value to try different arities of the recursion tree:
|
||||
test_tree_recursion_opt::<2>()?; // N=2
|
||||
|
||||
// test_tree_recursion_opt::<3>()?; // N=3
|
||||
test_tree_recursion_opt::<3>()?; // N=3
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -422,7 +502,7 @@ mod tests {
|
||||
.collect();
|
||||
|
||||
// build the circuit_data & verifier_data for the recursive circuit
|
||||
let circuit_data = Recursion::<N>::circuit_data(MSG_LEN)?;
|
||||
let circuit_data = Recursion::<ExampleGadget, N>::circuit_data(MSG_LEN)?;
|
||||
let verifier_data = circuit_data.verifier_data();
|
||||
|
||||
let dummy_proof_pis = cyclic_base_proof(
|
||||
@@ -461,9 +541,9 @@ mod tests {
|
||||
|
||||
// prepare the inputs for the `Recursion::prove_step` call
|
||||
let selectors = (0..N).into_iter().map(|_| proof_enabled.clone()).collect();
|
||||
let pods_input: Vec<PODInput> = (0..N)
|
||||
let innercircuits_input: Vec<ExampleGadgetInput> = (0..N)
|
||||
.into_iter()
|
||||
.map(|k| PODInput {
|
||||
.map(|k| ExampleGadgetInput {
|
||||
pk: pk_vec[j + k],
|
||||
sig: sig_vec[j + k],
|
||||
})
|
||||
@@ -476,11 +556,11 @@ mod tests {
|
||||
|
||||
// do the recursive step
|
||||
let start = Instant::now();
|
||||
let new_proof = Recursion::<N>::prove_step(
|
||||
let new_proof = Recursion::<ExampleGadget, N>::prove_step(
|
||||
verifier_data.clone(),
|
||||
&msg,
|
||||
selectors,
|
||||
pods_input,
|
||||
innercircuits_input,
|
||||
&proofs,
|
||||
)?;
|
||||
println!(
|
||||
@@ -491,7 +571,7 @@ mod tests {
|
||||
);
|
||||
|
||||
// verify the recursive proof
|
||||
let public_inputs = RecursiveCircuit::<N>::prepare_public_inputs(
|
||||
let public_inputs = RecursiveCircuit::<ExampleGadget, N>::prepare_public_inputs(
|
||||
verifier_data.clone(),
|
||||
msg.clone(),
|
||||
);
|
||||
@@ -509,8 +589,10 @@ mod tests {
|
||||
let last_proof = proofs_at_level_i[0].clone();
|
||||
|
||||
// verify the last proof
|
||||
let public_inputs =
|
||||
RecursiveCircuit::<N>::prepare_public_inputs(verifier_data.clone(), msg.clone());
|
||||
let public_inputs = RecursiveCircuit::<ExampleGadget, N>::prepare_public_inputs(
|
||||
verifier_data.clone(),
|
||||
msg.clone(),
|
||||
);
|
||||
verifier_data.clone().verify(ProofWithPublicInputs {
|
||||
proof: last_proof.clone(),
|
||||
public_inputs: public_inputs.clone(),
|
||||
|
||||
Reference in New Issue
Block a user