diff --git a/codex-plonky2-circuits/src/circuits/params.rs b/codex-plonky2-circuits/src/circuits/params.rs index b7466d0..d9aea94 100644 --- a/codex-plonky2-circuits/src/circuits/params.rs +++ b/codex-plonky2-circuits/src/circuits/params.rs @@ -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 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 diff --git a/codex-plonky2-circuits/src/recursion/cyclic_recursion.rs b/codex-plonky2-circuits/src/recursion/cyclic_recursion.rs new file mode 100644 index 0000000..38b3bb5 --- /dev/null +++ b/codex-plonky2-circuits/src/recursion/cyclic_recursion.rs @@ -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>, + pub cyclic_circuit_data: Option>, + pub common_data: Option>, + pub latest_proof: Option>, +} + +/// 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, + pub verifier_data: VerifierCircuitTarget, +} + +impl< + I: InnerCircuit, +> CyclicCircuit { + + /// 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::::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::(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::(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::( + condition, + &inner_cyclic_proof_with_pis, + &common_data, + )?; + + // build the cyclic circuit + let cyclic_circuit_data = builder.build::(); + + // assign targets + let cyc_t = CyclicCircuitTargets::{ + 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>{ + + 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::( + &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, + ) -> Result>{ + + // 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 +{ + // layer 1 + let config = CircuitConfig::standard_recursion_config(); + let builder = CircuitBuilder::::new(config); + let data = builder.build::(); + // layer 2 + let config = CircuitConfig::standard_recursion_config(); + let mut builder = CircuitBuilder::::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::(&proof, &verifier_data, &data.common); + let data = builder.build::(); + // layer 3 + let config = CircuitConfig::standard_recursion_config(); + let mut builder = CircuitBuilder::::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::(&proof, &verifier_data, &data.common); + // pad with noop gates + while builder.num_gates() < 1 << 12 { + builder.add_gate(NoopGate, vec![]); + } + builder.build::().common +} \ No newline at end of file diff --git a/codex-plonky2-circuits/src/recursion/traits.rs b/codex-plonky2-circuits/src/recursion/inner_circuit.rs similarity index 69% rename from codex-plonky2-circuits/src/recursion/traits.rs rename to codex-plonky2-circuits/src/recursion/inner_circuit.rs index fd452b4..3dabe9e 100644 --- a/codex-plonky2-circuits/src/recursion/traits.rs +++ b/codex-plonky2-circuits/src/recursion/inner_circuit.rs @@ -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, ) -> anyhow::Result; /// assign the actual witness values for the current instance of the circuit. fn assign_targets( + &self, pw: &mut PartialWitness, 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)>; } diff --git a/codex-plonky2-circuits/src/recursion/mod.rs b/codex-plonky2-circuits/src/recursion/mod.rs index ba0481e..8e6b259 100644 --- a/codex-plonky2-circuits/src/recursion/mod.rs +++ b/codex-plonky2-circuits/src/recursion/mod.rs @@ -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; diff --git a/codex-plonky2-circuits/src/recursion/params.rs b/codex-plonky2-circuits/src/recursion/params.rs index d87c1a8..a60f992 100644 --- a/codex-plonky2-circuits/src/recursion/params.rs +++ b/codex-plonky2-circuits/src/recursion/params.rs @@ -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; diff --git a/codex-plonky2-circuits/src/recursion/sampling.rs b/codex-plonky2-circuits/src/recursion/sampling.rs deleted file mode 100644 index 31b4ecb..0000000 --- a/codex-plonky2-circuits/src/recursion/sampling.rs +++ /dev/null @@ -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; - - /// build the circuit - /// TODO: this build the circuit with default circuit params -> make it generic - fn build(builder: &mut CircuitBuilder) -> anyhow::Result { - let circ = SampleCircuit::new(CircuitParams::default()); - Ok(circ.sample_slot_circuit(builder)) - } - - fn assign_targets(pw: &mut PartialWitness, targets: &Self::Targets, input: &Self::Input) -> anyhow::Result<()> { - let circ = SampleCircuit::::new(CircuitParams::default()); - // circ.sample_slot_assign_witness(pw,targets,input); - todo!() - // Ok(()) - } -} \ No newline at end of file diff --git a/codex-plonky2-circuits/src/recursion/sampling_inner_circuit.rs b/codex-plonky2-circuits/src/recursion/sampling_inner_circuit.rs new file mode 100644 index 0000000..24b1afb --- /dev/null +++ b/codex-plonky2-circuits/src/recursion/sampling_inner_circuit.rs @@ -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, +} + +impl Default for SamplingRecursion { + fn default() -> Self { + Self{ + sampling_circ: SampleCircuit::new(CircuitParams::default()) + } + } +} + + +impl InnerCircuit for SamplingRecursion{ + type Targets = SampleTargets; + type Input = SampleCircuitInput; + + /// build the circuit + fn build(&self, builder: &mut CircuitBuilder) -> anyhow::Result { + Ok(self.sampling_circ.sample_slot_circuit(builder)) + } + + fn assign_targets(&self, pw: &mut PartialWitness, 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)> { + 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) + } +} \ No newline at end of file diff --git a/codex-plonky2-circuits/src/recursion/simple_recursion.rs b/codex-plonky2-circuits/src/recursion/simple_recursion.rs index 30eea4c..da4392a 100644 --- a/codex-plonky2-circuits/src/recursion/simple_recursion.rs +++ b/codex-plonky2-circuits/src/recursion/simple_recursion.rs @@ -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}; diff --git a/codex-plonky2-circuits/src/recursion/simple_recursion2.rs b/codex-plonky2-circuits/src/recursion/simple_recursion2.rs index 11c1a6d..e00b344 100644 --- a/codex-plonky2-circuits/src/recursion/simple_recursion2.rs +++ b/codex-plonky2-circuits/src/recursion/simple_recursion2.rs @@ -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; diff --git a/codex-plonky2-circuits/src/recursion/structs.rs b/codex-plonky2-circuits/src/recursion/structs.rs deleted file mode 100644 index e69de29..0000000 diff --git a/codex-plonky2-circuits/src/recursion/tree_recursion.rs b/codex-plonky2-circuits/src/recursion/tree_recursion.rs index 72058fd..56f0cfc 100644 --- a/codex-plonky2-circuits/src/recursion/tree_recursion.rs +++ b/codex-plonky2-circuits/src/recursion/tree_recursion.rs @@ -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, - pub proof_targets: Vec>, - pub verifier_data_targets: VerifierCircuitTarget, - pub verifier_data: VerifierCircuitData, - pub condition_targets: Vec, -} - -pub struct RecursionCircuitInput< - I: InnerCircuit, ->{ - pub inner_circuit_input: Vec, - pub proofs: Plonky2Proof, - pub conditions: Vec + node_circ: NodeCircuit } impl< I: InnerCircuit, -> RecursionCircuit { + const M: usize, + const N: usize, +> TreeRecursion { + pub fn new(node_circ: NodeCircuit) -> 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; N]>, + is_leaf: bool, + ) -> Result>{ + + 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, + ) -> Result>{ + todo!() + } + + /// verifies the proof generated + pub fn verify_proof( + &self, + proof: ProofWithPublicInputs + ) -> 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>, + pub cyclic_circuit_data: Option>, + pub common_data: Option>, +} + +/// 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; N], + pub verifier_data: VerifierCircuitTarget, +} + +impl< + I: InnerCircuit, + const M: usize, + const N: usize, +> NodeCircuit { + + /// 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::, - ) -> 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::::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::>>()? + .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; N] = (0..N) + .map(|_| builder.add_virtual_proof_with_pis(&common_data)) + .collect::>() + .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::(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::(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::(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::(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::( + condition, + &inner_cyclic_proof_with_pis[i], + &common_data, + )?; + } + + // build the cyclic circuit + let cyclic_circuit_data = builder.build::(); + + // assign targets + let cyc_t = NodeCircuitTargets::{ + 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{ - todo!() - } - - pub fn get_dummy_proof(circuit_data: CircuitData) -> 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; N]>, pw: &mut PartialWitness, - witnesses: RecursionCircuitInput - )-> 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::( + &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(()) } -} \ No newline at end of file + + /// 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> + { + // layer 1 + let config = CircuitConfig::standard_recursion_config(); + let builder = CircuitBuilder::::new(config); + let data = builder.build::(); + + // layer 2 + let config = CircuitConfig::standard_recursion_config(); + let mut builder = CircuitBuilder::::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::(&proof, &verifier_data, &data.common); + } + let data = builder.build::(); + + // layer 3 + let config = CircuitConfig::standard_recursion_config(); + let mut builder = CircuitBuilder::::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::(&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::().common) + } + +}