mirror of
https://github.com/codex-storage/proof-aggregation.git
synced 2025-02-10 13:46:55 +00:00
add cyclic and tree recursion and refactor
This commit is contained in:
parent
cf75968473
commit
e5985184df
@ -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
|
||||
|
249
codex-plonky2-circuits/src/recursion/cyclic_recursion.rs
Normal file
249
codex-plonky2-circuits/src/recursion/cyclic_recursion.rs
Normal 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
|
||||
}
|
@ -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>)>;
|
||||
}
|
@ -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;
|
||||
|
||||
|
@ -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>;
|
||||
|
||||
|
||||
|
@ -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(())
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
@ -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};
|
||||
|
@ -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;
|
||||
|
@ -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 build_circuit(
|
||||
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,
|
||||
builder: &mut CircuitBuilder::<F, D>,
|
||||
) -> Self {
|
||||
todo!()
|
||||
}
|
||||
|
||||
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,
|
||||
pw: &mut PartialWitness<F>,
|
||||
witnesses: RecursionCircuitInput<I>
|
||||
)-> anyhow::Result<()>{
|
||||
todo!()
|
||||
// Ok(())
|
||||
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(
|
||||
&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(())
|
||||
}
|
||||
|
||||
/// 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>,
|
||||
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)
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user