add cyclic and tree recursion and refactor

This commit is contained in:
M Alghazwi 2024-12-13 16:36:26 +03:00
parent cf75968473
commit e5985184df
No known key found for this signature in database
GPG Key ID: 646E567CAD7DB607
11 changed files with 665 additions and 79 deletions

View File

@ -8,7 +8,7 @@ use plonky2_poseidon2::poseidon2_hash::poseidon2::Poseidon2Hash;
// hash function used. this is hackish way of doing it because
// H::Hash is not consistent with HashOut<F> and causing a lot of headache
// will look into this later.
pub type HF = Poseidon2Hash;
pub type HF = PoseidonHash;
/// params used for the circuits
/// should be defined prior to building the circuit

View File

@ -0,0 +1,249 @@
use hashbrown::HashMap;
use plonky2::hash::hash_types::{HashOut, HashOutTarget, RichField};
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, CommonCircuitData, VerifierCircuitData, VerifierCircuitTarget};
use plonky2::plonk::config::{AlgebraicHasher, GenericConfig};
use plonky2::plonk::proof::{ProofWithPublicInputs, ProofWithPublicInputsTarget};
use plonky2::recursion::dummy_circuit::cyclic_base_proof;
use plonky2_field::extension::Extendable;
use plonky2_poseidon2::poseidon2_hash::poseidon2::Poseidon2;
use crate::recursion::params::{F,D,C,Plonky2Proof,H};
use crate::recursion::inner_circuit::InnerCircuit;
use anyhow::Result;
use plonky2::gates::noop::NoopGate;
use plonky2::recursion::cyclic_recursion::check_cyclic_proof_verifier_data;
use crate::circuits::utils::select_hash;
/// cyclic circuit struct
/// contains necessary data
/// note: only keeps track of latest proof not all proofs.
pub struct CyclicCircuit<
I: InnerCircuit,
>{
pub layer: usize,
pub circ: I,
pub cyclic_target: Option<CyclicCircuitTargets<I>>,
pub cyclic_circuit_data: Option<CircuitData<F, C, D>>,
pub common_data: Option<CommonCircuitData<F, D>>,
pub latest_proof: Option<ProofWithPublicInputs<F, C, D>>,
}
/// targets need to be assigned for the cyclic circuit
#[derive(Clone)]
pub struct CyclicCircuitTargets<
I: InnerCircuit,
>{
pub inner_targets: I::Targets,
pub condition: BoolTarget,
pub inner_cyclic_proof_with_pis: ProofWithPublicInputsTarget<D>,
pub verifier_data: VerifierCircuitTarget,
}
impl<
I: InnerCircuit,
> CyclicCircuit<I> {
/// create a new cyclic circuit
pub fn new(circ: I) -> Self{
Self{
layer: 0,
circ,
cyclic_target: None,
cyclic_circuit_data: None,
common_data: None,
latest_proof: None,
}
}
/// builds the cyclic recursion circuit using any inner circuit I
/// returns the circuit data
pub fn build_circuit(
&mut self,
) -> Result<()>{
// if the circuit data is already build then no need to rebuild
if self.cyclic_circuit_data.is_some(){
return Ok(());
}
// builder with standard recursion config
let config = CircuitConfig::standard_recursion_config();
let mut builder = CircuitBuilder::<F, D>::new(config);
//build the inner circuit
let inner_t = self.circ.build(& mut builder)?;
// common data for recursion
let mut common_data = common_data_for_recursion();
// the hash of the public input
let pub_input_hash = builder.add_virtual_hash_public_input();
// verifier data for inner proofs
let verifier_data_target = builder.add_verifier_data_public_inputs();
// common data should have same num of public input as inner proofs
common_data.num_public_inputs = builder.num_public_inputs();
// condition
let condition = builder.add_virtual_bool_target_safe();
// inner proof with public input
let inner_cyclic_proof_with_pis = builder.add_virtual_proof_with_pis(&common_data);
// get the hash of the pub input
let inner_cyclic_pis = &inner_cyclic_proof_with_pis.public_inputs;
let inner_pub_input_hash = HashOutTarget::try_from(&inner_cyclic_pis[0..4]).unwrap();
// now hash the current public input
let outer_pis = I::get_pub_input_targets(&inner_t)?;
let outer_pi_hash = builder.hash_n_to_hash_no_pad::<H>(outer_pis);
let zero_hash = HashOutTarget::from_vec([builder.zero(); 4].to_vec());
// if leaf pad with zeros
let inner_pi_hash_or_zero_hash = select_hash(&mut builder, condition, inner_pub_input_hash, zero_hash);
// hash current public input with previous hash
let mut hash_input = vec![];
hash_input.extend_from_slice(&outer_pi_hash.elements);
hash_input.extend_from_slice(&inner_pi_hash_or_zero_hash.elements);
let outer_pi_hash = builder.hash_n_to_hash_no_pad::<H>(hash_input);
// connect this up one to `pub_input_hash`
builder.connect_hashes(pub_input_hash,outer_pi_hash);
// connect entropy?
// verify proof in-circuit
builder.conditionally_verify_cyclic_proof_or_dummy::<C>(
condition,
&inner_cyclic_proof_with_pis,
&common_data,
)?;
// build the cyclic circuit
let cyclic_circuit_data = builder.build::<C>();
// assign targets
let cyc_t = CyclicCircuitTargets::<I>{
inner_targets: inner_t,
condition,
inner_cyclic_proof_with_pis,
verifier_data: verifier_data_target
};
// assign the data
self.cyclic_circuit_data = Some(cyclic_circuit_data);
self.common_data = Some(common_data);
self.cyclic_target = Some(cyc_t);
Ok(())
}
/// generates a proof with only one recursion layer
/// takes circuit input
pub fn prove_one_layer(
&mut self,
circ_input: &I::Input,
) -> Result<ProofWithPublicInputs<F, C, D>>{
if self.cyclic_circuit_data.is_none(){
panic!("circuit data not found") // TODO: replace with err
}
let circ_data = self.cyclic_circuit_data.as_ref().unwrap();
let cyc_targets = self.cyclic_target.as_ref().unwrap();
let common_data = self.common_data.as_ref().unwrap();
// assign targets
let mut pw = PartialWitness::new();
self.circ.assign_targets(&mut pw,&cyc_targets.inner_targets,&circ_input)?;
// if leaf add dummy proof
if(self.layer == 0) {
pw.set_bool_target(cyc_targets.condition, false)?;
pw.set_proof_with_pis_target::<C, D>(
&cyc_targets.inner_cyclic_proof_with_pis,
&cyclic_base_proof(
common_data,
&circ_data.verifier_only,
HashMap::new(),
),
)?;
}else{ // else add last proof
pw.set_bool_target(cyc_targets.condition, true)?;
let last_proof = self.latest_proof.as_ref().unwrap();
pw.set_proof_with_pis_target(&cyc_targets.inner_cyclic_proof_with_pis, last_proof)?;
}
// assign verifier data
pw.set_verifier_data_target(&cyc_targets.verifier_data, &circ_data.verifier_only)?;
// prove
let proof = circ_data.prove(pw)?;
// check that the correct verifier data is consistent
check_cyclic_proof_verifier_data(
&proof,
&circ_data.verifier_only,
&circ_data.common,
)?;
self.latest_proof = Some(proof.clone());
self.layer = self.layer + 1;
Ok(proof)
}
/// prove n recursive layers
/// the function takes
/// - n: the number of layers and
/// - circ_input: vector of n inputs
pub fn prove_n_layers(
&mut self,
n: usize,
circ_input: Vec<I::Input>,
) -> Result<ProofWithPublicInputs<F, C, D>>{
// asserts that n equals the number of input
assert_eq!(n, circ_input.len());
for i in 0..n {
self.prove_one_layer(&circ_input[i])?;
}
Ok(self.latest_proof.clone().unwrap())
}
/// verifies the latest proof generated
pub fn verify_latest_proof(
&mut self,
) -> Result<()>{
if(self.cyclic_circuit_data.is_none() || self.latest_proof.is_none()){
panic!("no circuit data or proof found");
}
let circ_data = self.cyclic_circuit_data.as_ref().unwrap();
let proof = self.latest_proof.clone().unwrap();
circ_data.verify(proof)?;
Ok(())
}
}
/// Generates `CommonCircuitData` usable for recursion.
pub fn common_data_for_recursion() -> CommonCircuitData<F, D>
{
// layer 1
let config = CircuitConfig::standard_recursion_config();
let builder = CircuitBuilder::<F, D>::new(config);
let data = builder.build::<C>();
// layer 2
let config = CircuitConfig::standard_recursion_config();
let mut builder = CircuitBuilder::<F, D>::new(config);
let proof = builder.add_virtual_proof_with_pis(&data.common);
let verifier_data =
builder.add_virtual_verifier_data(data.common.config.fri_config.cap_height);
builder.verify_proof::<C>(&proof, &verifier_data, &data.common);
let data = builder.build::<C>();
// layer 3
let config = CircuitConfig::standard_recursion_config();
let mut builder = CircuitBuilder::<F, D>::new(config);
let proof = builder.add_virtual_proof_with_pis(&data.common);
let verifier_data =
builder.add_virtual_verifier_data(data.common.config.fri_config.cap_height);
builder.verify_proof::<C>(&proof, &verifier_data, &data.common);
// pad with noop gates
while builder.num_gates() < 1 << 12 {
builder.add_gate(NoopGate, vec![]);
}
builder.build::<C>().common
}

View File

@ -1,4 +1,4 @@
use plonky2::iop::target::BoolTarget;
use plonky2::iop::target::{BoolTarget, Target};
use plonky2::iop::witness::PartialWitness;
use plonky2::plonk::circuit_builder::CircuitBuilder;
use crate::recursion::params::{F,C,D};
@ -13,13 +13,21 @@ pub trait InnerCircuit<
/// build the circuit logic and return targets to be assigned later
fn build(
&self,
builder: &mut CircuitBuilder<F, D>,
) -> anyhow::Result<Self::Targets>;
/// assign the actual witness values for the current instance of the circuit.
fn assign_targets(
&self,
pw: &mut PartialWitness<F>,
targets: &Self::Targets,
input: &Self::Input,
) -> anyhow::Result<()>;
/// from the set of the targets, return only the targets which are public
/// TODO: this can probably be replaced with enum for Public/Private targets
fn get_pub_input_targets(
targets: &Self::Targets,
) -> anyhow::Result<(Vec<Target>)>;
}

View File

@ -1,8 +1,8 @@
pub mod structs;
pub mod traits;
pub mod inner_circuit;
pub mod simple_recursion;
pub mod simple_recursion2;
pub mod tree_recursion;
pub mod params;
pub mod sampling;
pub mod sampling_inner_circuit;
pub mod cyclic_recursion;

View File

@ -1,11 +1,15 @@
use plonky2::hash::poseidon::PoseidonHash;
use plonky2::plonk::config::PoseidonGoldilocksConfig;
use plonky2::plonk::proof::ProofWithPublicInputs;
use plonky2_field::goldilocks_field::GoldilocksField;
use plonky2_poseidon2::config::Poseidon2GoldilocksConfig;
// recursion param
// TODO: make it more generic or use global params
pub type F = GoldilocksField;
pub const D: usize = 2;
pub type C = Poseidon2GoldilocksConfig;
pub type C = PoseidonGoldilocksConfig;
pub type H = PoseidonHash;
pub type Plonky2Proof = ProofWithPublicInputs<F, C, D>;

View File

@ -1,28 +0,0 @@
use plonky2::iop::target::BoolTarget;
use plonky2::iop::witness::PartialWitness;
use plonky2::plonk::circuit_builder::CircuitBuilder;
use crate::circuits::params::CircuitParams;
use crate::circuits::sample_cells::{SampleCircuit, SampleCircuitInput, SampleTargets};
use crate::recursion::params::{D, F};
use crate::recursion::traits::InnerCircuit;
pub struct SamplingRecursion {}
impl InnerCircuit for SamplingRecursion{
type Targets = SampleTargets;
type Input = SampleCircuitInput<F, D>;
/// build the circuit
/// TODO: this build the circuit with default circuit params -> make it generic
fn build(builder: &mut CircuitBuilder<F, D>) -> anyhow::Result<Self::Targets> {
let circ = SampleCircuit::new(CircuitParams::default());
Ok(circ.sample_slot_circuit(builder))
}
fn assign_targets(pw: &mut PartialWitness<F>, targets: &Self::Targets, input: &Self::Input) -> anyhow::Result<()> {
let circ = SampleCircuit::<F,D>::new(CircuitParams::default());
// circ.sample_slot_assign_witness(pw,targets,input);
todo!()
// Ok(())
}
}

View File

@ -0,0 +1,48 @@
use plonky2::iop::target::{BoolTarget, Target};
use plonky2::iop::witness::PartialWitness;
use plonky2::plonk::circuit_builder::CircuitBuilder;
use crate::circuits::params::CircuitParams;
use crate::circuits::sample_cells::{SampleCircuit, SampleCircuitInput, SampleTargets};
use crate::recursion::params::{D, F};
use crate::recursion::inner_circuit::InnerCircuit;
use crate::circuits::params;
/// recursion Inner circuit for the sampling circuit
#[derive(Clone, Debug)]
pub struct SamplingRecursion {
pub sampling_circ: SampleCircuit<F,D>,
}
impl Default for SamplingRecursion {
fn default() -> Self {
Self{
sampling_circ: SampleCircuit::new(CircuitParams::default())
}
}
}
impl InnerCircuit for SamplingRecursion{
type Targets = SampleTargets;
type Input = SampleCircuitInput<F, D>;
/// build the circuit
fn build(&self, builder: &mut CircuitBuilder<F, D>) -> anyhow::Result<Self::Targets> {
Ok(self.sampling_circ.sample_slot_circuit(builder))
}
fn assign_targets(&self, pw: &mut PartialWitness<F>, targets: &Self::Targets, input: &Self::Input) -> anyhow::Result<()> {
Ok(self.sampling_circ.sample_slot_assign_witness(pw, targets, input))
}
/// returns the public input specific for this circuit which are:
/// `[slot_index, dataset_root, entropy]`
fn get_pub_input_targets(targets: &Self::Targets) -> anyhow::Result<(Vec<Target>)> {
let mut pub_targets = vec![];
pub_targets.push(targets.slot_index.clone());
pub_targets.extend_from_slice(&targets.dataset_root.elements);
pub_targets.extend_from_slice(&targets.entropy.elements);
Ok(pub_targets)
}
}

View File

@ -1,3 +1,6 @@
// this file is mainly draft implementation and experimentation of multiple simple approaches
// NOTE: will be deleted later on ...
use plonky2::hash::hash_types::{HashOut, HashOutTarget, RichField};
use plonky2::iop::target::Target;
use plonky2::iop::witness::{PartialWitness, WitnessWrite};

View File

@ -1,3 +1,6 @@
// this is still simple recursion approach but written differently,
// still needs to be improved/removed.
use plonky2::hash::hash_types::{HashOut, HashOutTarget, RichField};
use plonky2::iop::witness::{PartialWitness, WitnessWrite};
use plonky2::plonk::circuit_builder::CircuitBuilder;

View File

@ -1,69 +1,368 @@
use hashbrown::HashMap;
use plonky2::hash::hash_types::{HashOut, HashOutTarget, RichField};
use plonky2::iop::target::Target;
use plonky2::iop::target::{BoolTarget, Target};
use plonky2::iop::witness::{PartialWitness, WitnessWrite};
use plonky2::plonk::circuit_builder::CircuitBuilder;
use plonky2::plonk::circuit_data::{CircuitData, VerifierCircuitData, VerifierCircuitTarget};
use plonky2::plonk::circuit_data::{CircuitConfig, CircuitData, CommonCircuitData, VerifierCircuitData, VerifierCircuitTarget};
use plonky2::plonk::config::{AlgebraicHasher, GenericConfig};
use plonky2::plonk::proof::{ProofWithPublicInputs, ProofWithPublicInputsTarget};
use plonky2::recursion::dummy_circuit::cyclic_base_proof;
use plonky2_field::extension::Extendable;
use plonky2_poseidon2::poseidon2_hash::poseidon2::Poseidon2;
use crate::recursion::params::RecursionTreeParams;
use crate::recursion::params::{F,D,C,Plonky2Proof};
use crate::recursion::traits::InnerCircuit;
use crate::recursion::params::{F,D,C,Plonky2Proof,H};
use crate::recursion::inner_circuit::InnerCircuit;
use anyhow::{anyhow, Result};
use plonky2::gates::noop::NoopGate;
use plonky2::recursion::cyclic_recursion::check_cyclic_proof_verifier_data;
use crate::circuits::utils::select_hash;
pub struct RecursionCircuit<
/// the tree recursion struct simplifies the process
/// of building, proving and verifying
/// the two consts are:
/// - M: number of inner circuits to run
/// - N: number of inner proofs to verify
pub struct TreeRecursion<
I: InnerCircuit,
const M: usize,
const N: usize,
>{
pub recursion_tree_params: RecursionTreeParams,
pub inner_circuit_targets: Vec<I::Targets>,
pub proof_targets: Vec<ProofWithPublicInputsTarget<D>>,
pub verifier_data_targets: VerifierCircuitTarget,
pub verifier_data: VerifierCircuitData<F, C, D>,
pub condition_targets: Vec<Target>,
}
pub struct RecursionCircuitInput<
I: InnerCircuit,
>{
pub inner_circuit_input: Vec<I::Targets>,
pub proofs: Plonky2Proof,
pub conditions: Vec<F>
node_circ: NodeCircuit<I, M, N>
}
impl<
I: InnerCircuit,
> RecursionCircuit<I> {
const M: usize,
const N: usize,
> TreeRecursion<I, M, N> {
pub fn new(node_circ: NodeCircuit<I,M,N>) -> Self{
Self{
node_circ,
}
}
pub fn build(
&mut self
) -> Result<()>{
self.node_circ.build_circuit()
}
/// generates a proof - only one node
/// takes M circuit input and N proofs
pub fn prove(
&mut self,
circ_input: &[I::Input; M],
proofs_option: Option<[ProofWithPublicInputs<F, C, D>; N]>,
is_leaf: bool,
) -> Result<ProofWithPublicInputs<F, C, D>>{
if self.node_circ.cyclic_circuit_data.is_none(){
panic!("circuit data not found") // TODO: replace with err
}
let mut pw = PartialWitness::new();
self.node_circ.assign_targets(
circ_input,
proofs_option,
&mut pw,
is_leaf,
)?;
let circ_data = self.node_circ.cyclic_circuit_data.as_ref().unwrap();
let cyc_targets = self.node_circ.cyclic_target.as_ref().unwrap();
pw.set_verifier_data_target(&cyc_targets.verifier_data, &circ_data.verifier_only)?;
let proof = circ_data.prove(pw)?;
check_cyclic_proof_verifier_data(
&proof,
&circ_data.verifier_only,
&circ_data.common,
)?;
Ok(proof)
}
/// prove n in a tree structure recursively
/// the function takes
/// - circ_input: vector of circuit inputs
pub fn prove_n_nodes(
&mut self,
circ_input: Vec<I::Input>,
) -> Result<ProofWithPublicInputs<F, C, D>>{
todo!()
}
/// verifies the proof generated
pub fn verify_proof(
&self,
proof: ProofWithPublicInputs<F, C, D>
) -> Result<()>{
if self.node_circ.cyclic_circuit_data.is_none() {
panic!("no circuit data or proof found");
}
let circ_data = self.node_circ.cyclic_circuit_data.as_ref().unwrap();
circ_data.verify(proof)?;
Ok(())
}
}
/// Node circuit struct
/// contains necessary data
/// M: number of inner-circuits to run
/// N: number of proofs verified in-circuit (so num of child nodes)
pub struct NodeCircuit<
I: InnerCircuit,
const M: usize,
const N: usize,
>{
pub circ: I,
pub cyclic_target: Option<NodeCircuitTargets<I,M,N>>,
pub cyclic_circuit_data: Option<CircuitData<F, C, D>>,
pub common_data: Option<CommonCircuitData<F, D>>,
}
/// Node circuit targets
/// assumes that all inner proofs use the same verifier data
#[derive(Clone, Debug)]
pub struct NodeCircuitTargets<
I: InnerCircuit,
const M: usize,
const N: usize,
>{
pub inner_targets: [I::Targets; M],
pub condition: BoolTarget,
pub inner_proofs_with_pis: [ProofWithPublicInputsTarget<D>; N],
pub verifier_data: VerifierCircuitTarget,
}
impl<
I: InnerCircuit,
const M: usize,
const N: usize,
> NodeCircuit<I, M, N> {
/// create a new cyclic circuit
pub fn new(circ: I) -> Self{
Self{
circ,
cyclic_target: None,
cyclic_circuit_data: None,
common_data: None,
}
}
/// builds the cyclic recursion circuit using any inner circuit I
/// returns the circuit data
pub fn build_circuit(
&self,
builder: &mut CircuitBuilder::<F, D>,
) -> Self {
todo!()
&mut self,
) -> Result<()>{
// if the circuit data is already build then no need to rebuild
if self.cyclic_circuit_data.is_some(){
return Ok(());
}
// builder with standard recursion config
let config = CircuitConfig::standard_recursion_config();
let mut builder = CircuitBuilder::<F, D>::new(config);
//build M inner circuits
// let mut inner_t = Vec::with_capacity(M);
// for i in 0..M {
// inner_t.push( self.circ.build(&mut builder)?);
// }
let inner_t: [I::Targets; M] = (0..M)
.map(|_| self.circ.build(&mut builder))
.collect::<Result<Vec<_>>>()?
.try_into()
.map_err(|_| anyhow!("Expected exactly M inner circuits"))?;
// common data for recursion
let mut common_data = self.common_data_for_node()?;
// let outer_pis = I::get_pub_input_targets(&inner_t)?;
let pub_input_hash = builder.add_virtual_hash_public_input();
let verifier_data_target = builder.add_verifier_data_public_inputs();
common_data.num_public_inputs = builder.num_public_inputs();
// condition
let condition = builder.add_virtual_bool_target_safe();
// inner proofs targets - N proof targets
// let mut inner_cyclic_proof_with_pis = vec![];
// for i in 0..N {
// inner_cyclic_proof_with_pis.push(builder.add_virtual_proof_with_pis(&common_data));
// }
let inner_cyclic_proof_with_pis: [ProofWithPublicInputsTarget<D>; N] = (0..N)
.map(|_| builder.add_virtual_proof_with_pis(&common_data))
.collect::<Vec<_>>()
.try_into()
.map_err(|_| anyhow!("Expected exactly N proof targets"))?;
// get the public input hash from all inner proof targets
let mut inner_pub_input_hashes = vec![];
for i in 0..N {
let inner_cyclic_pis = &inner_cyclic_proof_with_pis[i].public_inputs;
inner_pub_input_hashes.extend_from_slice(&inner_cyclic_pis[0..4]);
}
// hash all the inner public input h = H(h_1 | h_2 | ... | h_N)
let inner_pub_input_hash = builder.hash_n_to_hash_no_pad::<H>(inner_pub_input_hashes);
// get the public input of the inner circuit
let mut outer_pis = vec![];
for i in 0..M {
outer_pis.push( I::get_pub_input_targets(&inner_t[i])?);
}
// hash all the public input -> generate one hashout at the end
// this is not an optimal way to do it, verification might be ugly if M > 1
// TODO: optimize this
let mut outer_pi_hashes = vec![];
for i in 0..M {
let hash_res = builder.hash_n_to_hash_no_pad::<H>(outer_pis[i].clone());
outer_pi_hashes.extend_from_slice(&hash_res.elements)
}
// the final public input hash
let outer_pi_hash = builder.hash_n_to_hash_no_pad::<H>(outer_pi_hashes);
// zero hash for leaves
let zero_hash = HashOutTarget::from_vec([builder.zero(); 4].to_vec());
// if the inner proofs are dummy then use zero hash for public input
let inner_pi_hash_or_zero_hash = select_hash(&mut builder, condition, inner_pub_input_hash, zero_hash);
// now hash the public input of the inner proofs and outer proof so we have one public hash
let mut hash_input = vec![];
hash_input.extend_from_slice(&outer_pi_hash.elements);
hash_input.extend_from_slice(&inner_pi_hash_or_zero_hash.elements);
let outer_pi_hash = builder.hash_n_to_hash_no_pad::<H>(hash_input);
// connect this up one to `pub_input_hash`
builder.connect_hashes(pub_input_hash,outer_pi_hash);
// we can connect entropy, since all share same entropy, but might be more work
// TODO: look into entropy
// verify all N proofs in-circuit
for i in 0..N {
builder.conditionally_verify_cyclic_proof_or_dummy::<C>(
condition,
&inner_cyclic_proof_with_pis[i],
&common_data,
)?;
}
// build the cyclic circuit
let cyclic_circuit_data = builder.build::<C>();
// assign targets
let cyc_t = NodeCircuitTargets::<I, M, N>{
inner_targets: inner_t,
condition,
inner_proofs_with_pis: inner_cyclic_proof_with_pis,
verifier_data: verifier_data_target
};
// assign the data
self.cyclic_circuit_data = Some(cyclic_circuit_data);
self.common_data = Some(common_data);
self.cyclic_target = Some(cyc_t);
Ok(())
}
pub fn get_circuit_data() -> CircuitData<F, C, D>{
todo!()
}
pub fn get_dummy_proof(circuit_data: CircuitData<F, C, D>) -> Plonky2Proof {
let verifier_data = circuit_data.verifier_data();
let dummy_proof_with_pis = cyclic_base_proof(
&circuit_data.common,
&verifier_data.verifier_only,
HashMap::new(),
);
dummy_proof_with_pis
}
pub fn assign_witness(
&self,
/// assigns the targets for the Node circuit
/// takes circuit input
pub fn assign_targets(
&mut self,
circ_input: &[I::Input; M],
proof_options: Option<[ProofWithPublicInputs<F, C, D>; N]>,
pw: &mut PartialWitness<F>,
witnesses: RecursionCircuitInput<I>
)-> anyhow::Result<()>{
todo!()
// Ok(())
is_leaf: bool,
) -> Result<()>{
if self.cyclic_circuit_data.is_none(){
panic!("circuit data not found") // TODO: replace with err
}
let circ_data = self.cyclic_circuit_data.as_ref().unwrap();
let cyc_targets = self.cyclic_target.as_ref().unwrap();
let common_data = self.common_data.as_ref().unwrap();
for i in 0..M {
self.circ.assign_targets(pw, &cyc_targets.inner_targets[i], &circ_input[i])?;
}
if(is_leaf == true) {
pw.set_bool_target(cyc_targets.condition, false)?;
for i in 0..N {
pw.set_proof_with_pis_target::<C, D>(
&cyc_targets.inner_proofs_with_pis[i],
&cyclic_base_proof(
common_data,
&circ_data.verifier_only,
HashMap::new(),
),
)?;
}
}else{
pw.set_bool_target(cyc_targets.condition, true)?;
let proofs = proof_options.unwrap(); // add error check
for i in 0..N {
pw.set_proof_with_pis_target(&cyc_targets.inner_proofs_with_pis[i], &proofs[i])?;
}
}
pw.set_verifier_data_target(&cyc_targets.verifier_data, &circ_data.verifier_only)?;
Ok(())
}
}
/// Generates `CommonCircuitData` usable for node recursion.
/// the circuit being built here depends on M and N so must be re-generated
/// if the params change
pub fn common_data_for_node(&self) -> Result<CommonCircuitData<F, D>>
{
// layer 1
let config = CircuitConfig::standard_recursion_config();
let builder = CircuitBuilder::<F, D>::new(config);
let data = builder.build::<C>();
// layer 2
let config = CircuitConfig::standard_recursion_config();
let mut builder = CircuitBuilder::<F, D>::new(config.clone());
let verifier_data = builder.add_virtual_verifier_data(data.common.config.fri_config.cap_height);
// generate and verify N number of proofs
for _ in 0..N {
let proof = builder.add_virtual_proof_with_pis(&data.common);
builder.verify_proof::<C>(&proof, &verifier_data, &data.common);
}
let data = builder.build::<C>();
// layer 3
let config = CircuitConfig::standard_recursion_config();
let mut builder = CircuitBuilder::<F, D>::new(config.clone());
// add a ConstantGate
builder.add_gate(
plonky2::gates::constant::ConstantGate::new(config.num_constants),
vec![],
);
// build M inner circuits
for i in 0..M {
self.circ.build(&mut builder)?;
}
// generate and verify N number of proofs
let verifier_data = builder.add_verifier_data_public_inputs();
for _ in 0..N {
let proof = builder.add_virtual_proof_with_pis(&data.common);
builder.verify_proof::<C>(&proof, &verifier_data, &data.common);
}
// pad. TODO: optimize this padding to only needed number of gates
while builder.num_gates() < 1 << 13 {
builder.add_gate(NoopGate, vec![]);
}
Ok(builder.build::<C>().common)
}
}