archive recursion experiments and refactor

This commit is contained in:
M Alghazwi 2025-02-07 11:00:29 +01:00
parent db9bc823e2
commit db4653a2a4
No known key found for this signature in database
GPG Key ID: 646E567CAD7DB607
54 changed files with 1374 additions and 90 deletions

View File

@ -1,8 +1,2 @@
pub mod cyclic;
pub mod circuits;
pub mod simple;
pub mod tree1;
pub mod tree2;
pub mod hybrid;
pub mod utils;
pub mod uniform;

View File

@ -7,7 +7,7 @@ use plonky2::plonk::config::{AlgebraicHasher, GenericConfig};
use plonky2::plonk::proof::{ProofWithPublicInputs, ProofWithPublicInputsTarget};
use plonky2_field::extension::Extendable;
use plonky2_poseidon2::poseidon2_hash::poseidon2::Poseidon2;
use crate::recursion::circuits::inner_circuit::InnerCircuit;
// use crate::recursion::circuits::inner_circuit::InnerCircuit;
use crate::{error::CircuitError,Result};
/// recursion leaf circuit - verifies N inner proof

View File

@ -7,7 +7,7 @@ use plonky2::plonk::config::{AlgebraicHasher, GenericConfig};
use plonky2::plonk::proof::{ProofWithPublicInputs, ProofWithPublicInputsTarget};
use plonky2_field::extension::Extendable;
use plonky2_poseidon2::poseidon2_hash::poseidon2::Poseidon2;
use crate::recursion::circuits::inner_circuit::InnerCircuit;
// use crate::recursion::circuits::inner_circuit::InnerCircuit;
use crate::{error::CircuitError,Result};
use crate::circuits::utils::vec_to_array;

View File

@ -6,7 +6,7 @@ use plonky2::plonk::circuit_data::{CircuitConfig, CircuitData, CommonCircuitData
use plonky2::plonk::config::{AlgebraicHasher, GenericConfig};
use plonky2::plonk::proof::ProofWithPublicInputs;
use plonky2_poseidon2::poseidon2_hash::poseidon2::Poseidon2;
use crate::recursion::circuits::inner_circuit::InnerCircuit;
// use crate::recursion::circuits::inner_circuit::InnerCircuit;
use plonky2_field::extension::Extendable;
use crate::{error::CircuitError, Result};
use crate::recursion::uniform::{leaf::{LeafTargets,LeafCircuit},node::{NodeTargets,NodeCircuit}};

View File

@ -10,9 +10,9 @@ use plonky2_poseidon2::poseidon2_hash::poseidon2::Poseidon2Hash;
// test types
pub const D: usize = 2;
pub type C = PoseidonGoldilocksConfig;
pub type C = Poseidon2GoldilocksConfig;
pub type F = <C as GenericConfig<D>>::F; // this is the goldilocks field
pub type HF = PoseidonHash;
pub type HF = Poseidon2Hash;
// hardcoded default params for generating proof input
const DEFAULT_MAX_DEPTH: usize = 32; // depth of big tree (slot tree depth, includes block tree depth)

View File

@ -1,7 +1 @@
pub mod simple_recursion;
pub mod simple_tree;
pub mod cyclic_recursion;
pub mod tree1;
pub mod tree2;
mod hybrid;
mod uniform;
pub mod uniform;

View File

@ -35,23 +35,14 @@ mod tests {
println!("sampling circuit degree bits = {:?}", inner_data.common.degree_bits());
let inner_proof = inner_data.prove(pw)?;
let proofs: Vec<ProofWithPublicInputs<F, C, D>> = (0..16).map(|i| inner_proof.clone()).collect();
let proofs: Vec<ProofWithPublicInputs<F, C, D>> = (0..4).map(|i| inner_proof.clone()).collect();
// ------------------- tree --------------------
const N: usize = 1;
const M: usize = 4;
const M: usize = 2;
let mut tree = TreeRecursion::<F,D,C,HF, N, M>::build(inner_data.common.clone())?;
// serialize circuit into JSON
let common_circuit_data_serialized = serde_json::to_string(&tree.get_leaf_verifier_data().common ).unwrap();
fs::write("leaf_common.json" , common_circuit_data_serialized) .expect("Unable to write file");
// serialize circuit into JSON
let common_circuit_data_serialized = serde_json::to_string(&tree.get_node_verifier_data().common ).unwrap();
fs::write("node_common.json" , common_circuit_data_serialized) .expect("Unable to write file");
let root = tree.prove_tree(&proofs, &inner_data.verifier_only)?;
println!("pub input size = {}", root.public_inputs.len());

View File

@ -0,0 +1,5 @@
Proof Aggregation Experiments
================================
In here, we archive the experiments done on various approaches to recursion before setting on the uniform approach.
See [here](../codex-plonky2-circuits/src/recursion) for the best recursion circuit option for our use case that we settled on.

View File

@ -15,7 +15,7 @@ use proof_input::params::Params;
/// Benchmark for building, proving, and verifying the Plonky2 tree recursion circuit.
fn bench_hybrid_recursion<const N: usize, const M: usize, const K: usize>(c: &mut Criterion) -> anyhow::Result<()>{
let mut group = c.benchmark_group(format!("Tree Recursion - Approach 2 Benchmark for N={}",K));
let mut group = c.benchmark_group(format!("Tree Recursion - Approach 2 Benchmark for N={}, leaf ={}, Node ={}",K, M,N));
//------------ sampling inner circuit ----------------------
// Circuit that does the sampling - default input
@ -77,9 +77,18 @@ fn bench_tree_recursion_approach2(c: &mut Criterion){
const N: usize = 2; // number of child nodes - binary here
const M: usize = 64; // number of proofs aggregated in leaves
const K: usize = 128; // number of proofs to be aggregated in the tree
bench_hybrid_recursion::<N,8,128>(c);
bench_hybrid_recursion::<N,4,128>(c);
// bench_hybrid_recursion::<N,M,K>(c);
bench_hybrid_recursion::<N,4,4>(c);
bench_hybrid_recursion::<N,8,8>(c);
bench_hybrid_recursion::<N,4,16>(c);
bench_hybrid_recursion::<N,8,16>(c);
bench_hybrid_recursion::<N,16,16>(c);
bench_hybrid_recursion::<N,8,32>(c);
bench_hybrid_recursion::<N,16,32>(c);
bench_hybrid_recursion::<N,32,32>(c);
bench_hybrid_recursion::<N,16,64>(c);
bench_hybrid_recursion::<N,32,64>(c);
bench_hybrid_recursion::<N,16,128>(c);
bench_hybrid_recursion::<4,16,128>(c);
}
/// Criterion benchmark group

View File

@ -0,0 +1,89 @@
use criterion::{Criterion, criterion_group, criterion_main};
use plonky2::iop::witness::PartialWitness;
use plonky2::plonk::circuit_builder::CircuitBuilder;
use plonky2::plonk::circuit_data::{CircuitConfig};
use plonky2::plonk::config::GenericConfig;
use plonky2::plonk::proof::ProofWithPublicInputs;
use codex_plonky2_circuits::circuits::sample_cells::{SampleCircuit};
use codex_plonky2_circuits::recursion::uniform::tree::TreeRecursion;
use proof_input::params::{C, D, F,HF};
use proof_input::gen_input::gen_testing_circuit_input;
use proof_input::params::Params;
/// Benchmark for building, proving, and verifying the Plonky2 tree recursion circuit.
fn bench_uniform_recursion<const N: usize,>(c: &mut Criterion) -> anyhow::Result<()>{
let mut group = c.benchmark_group(format!("Uniform Tree Recursion Benchmark for N={}",N));
//------------ sampling inner circuit ----------------------
// Circuit that does the sampling - 100 samples
let config = CircuitConfig::standard_recursion_config();
let mut sampling_builder = CircuitBuilder::<F, D>::new(config);
let mut params = Params::default();
params.input_params.n_samples = 100;
params.circuit_params.n_samples = 100;
let one_circ_input = gen_testing_circuit_input::<F,D>(&params.input_params);
let samp_circ = SampleCircuit::<F,D,HF>::new(params.circuit_params);
let inner_tar = samp_circ.sample_slot_circuit_with_public_input(&mut sampling_builder)?;
// get generate a sampling proof
let mut pw = PartialWitness::<F>::new();
samp_circ.sample_slot_assign_witness(&mut pw,&inner_tar,&one_circ_input)?;
let inner_data = sampling_builder.build::<C>();
let inner_proof = inner_data.prove(pw.clone())?;
let proofs: Vec<ProofWithPublicInputs<F, C, D>> = (0..N).map(|i| inner_proof.clone()).collect();
// ------------------- tree --------------------
let mut tree : Option<TreeRecursion<F, D, C, HF>> = None;
// Building phase
group.bench_function("build tree", |b| {
b.iter(|| {
tree = Some(TreeRecursion::<F,D,C,HF>::build(inner_data.common.clone()).unwrap());
})
});
let mut tree = tree.unwrap();
let mut proof: Option<ProofWithPublicInputs<F, C, D>> = None;
// Proving Phase
group.bench_function("prove tree", |b| {
b.iter(|| {
proof = Some(tree.prove_tree(&proofs, &inner_data.verifier_only).unwrap());
})
});
let proof = proof.unwrap();
// Verifying Phase
// group.bench_function("verify tree circuit", |b| {
// b.iter(|| {
// verifier_data.verify(proof.clone()).expect("verify fail");
// })
// });
group.finish();
Ok(())
}
fn bench_uniform_tree_recursion(c: &mut Criterion){
const N: usize = 2; // number of child nodes - binary here
bench_uniform_recursion::<2>(c).expect("bench failed");
bench_uniform_recursion::<4>(c).expect("bench failed");
bench_uniform_recursion::<8>(c).expect("bench failed");
bench_uniform_recursion::<16>(c).expect("bench failed");
bench_uniform_recursion::<32>(c).expect("bench failed");
bench_uniform_recursion::<64>(c).expect("bench failed");
bench_uniform_recursion::<128>(c).expect("bench failed");
bench_uniform_recursion::<256>(c).expect("bench failed");
}
/// Criterion benchmark group
criterion_group!{
name = recursion;
config = Criterion::default().sample_size(10);
targets = bench_uniform_tree_recursion
}
criterion_main!(recursion);

View File

@ -0,0 +1,8 @@
pub mod cyclic;
pub mod circuits;
pub mod simple;
pub mod tree1;
pub mod tree2;
pub mod hybrid;
pub mod utils;
pub mod uniform;

View File

@ -0,0 +1,139 @@
use std::marker::PhantomData;
use plonky2::hash::hash_types::RichField;
use plonky2::iop::witness::{PartialWitness, WitnessWrite};
use plonky2::plonk::circuit_builder::CircuitBuilder;
use plonky2::plonk::circuit_data::{CircuitConfig, CircuitData, CommonCircuitData, VerifierCircuitTarget, VerifierOnlyCircuitData};
use plonky2::plonk::config::{AlgebraicHasher, GenericConfig};
use plonky2::plonk::proof::{ProofWithPublicInputs, ProofWithPublicInputsTarget};
use plonky2_field::extension::Extendable;
use plonky2_poseidon2::poseidon2_hash::poseidon2::Poseidon2;
use crate::recursion::circuits::inner_circuit::InnerCircuit;
use crate::{error::CircuitError,Result};
/// recursion leaf circuit - verifies N inner proof
#[derive(Clone, Debug)]
pub struct LeafCircuit<
F: RichField + Extendable<D> + Poseidon2,
const D: usize,
C: GenericConfig<D, F = F>,
H: AlgebraicHasher<F>,
const N: usize,
> where
<C as GenericConfig<D>>::Hasher: AlgebraicHasher<F>
{
inner_common_data: CommonCircuitData<F, D>,
phantom_data: PhantomData<(C,H)>
}
#[derive(Clone, Debug)]
pub struct LeafTargets <
const D: usize,
>{
pub inner_proof: Vec<ProofWithPublicInputsTarget<D>>,
pub verifier_data: VerifierCircuitTarget,
}
impl<
F: RichField + Extendable<D> + Poseidon2,
const D: usize,
C: GenericConfig<D, F = F>,
H: AlgebraicHasher<F>,
const N: usize,
> LeafCircuit<F,D,C,H,N> where
<C as GenericConfig<D>>::Hasher: AlgebraicHasher<F>
{
pub fn new(inner_common_data: CommonCircuitData<F,D>) -> Self {
Self{
inner_common_data,
phantom_data:PhantomData::default(),
}
}
/// build the leaf circuit
pub fn build(&self, builder: &mut CircuitBuilder<F, D>) -> Result<LeafTargets<D>> {
let inner_common = self.inner_common_data.clone();
// the proof virtual targets
let mut pub_input = vec![];
let mut vir_proofs = vec![];
for _i in 0..N {
let vir_proof = builder.add_virtual_proof_with_pis(&inner_common);
let inner_pub_input = vir_proof.public_inputs.clone();
vir_proofs.push(vir_proof);
pub_input.extend_from_slice(&inner_pub_input);
}
// hash the public input & make it public
let hash_inner_pub_input = builder.hash_n_to_hash_no_pad::<H>(pub_input);
builder.register_public_inputs(&hash_inner_pub_input.elements);
// virtual target for the verifier data
let inner_verifier_data = builder.add_virtual_verifier_data(inner_common.config.fri_config.cap_height);
// register verifier data hash as public input.
let mut vd_pub_input = vec![];
vd_pub_input.extend_from_slice(&inner_verifier_data.circuit_digest.elements);
for i in 0..builder.config.fri_config.num_cap_elements() {
vd_pub_input.extend_from_slice(&inner_verifier_data.constants_sigmas_cap.0[i].elements);
}
let hash_inner_vd_pub_input = builder.hash_n_to_hash_no_pad::<H>(vd_pub_input);
builder.register_public_inputs(&hash_inner_vd_pub_input.elements);
// verify the proofs in-circuit
for i in 0..N {
builder.verify_proof::<C>(&vir_proofs[i], &inner_verifier_data, &inner_common);
}
// return targets
let t = LeafTargets {
inner_proof: vir_proofs,
verifier_data: inner_verifier_data,
};
Ok(t)
}
/// assign the leaf targets with given input
pub fn assign_targets(
&self, pw: &mut PartialWitness<F>,
targets: &LeafTargets<D>,
inner_proof: &[ProofWithPublicInputs<F, C, D>],
verifier_only_data: &VerifierOnlyCircuitData<C, D>,
) -> Result<()> {
assert_eq!(inner_proof.len(), N);
// assign the proofs
for i in 0..N {
pw.set_proof_with_pis_target(&targets.inner_proof[i], &inner_proof[i])
.map_err(|e| {
CircuitError::ProofTargetAssignmentError("inner-proof".to_string(), e.to_string())
})?;
}
// assign the verifier data
pw.set_verifier_data_target(&targets.verifier_data, verifier_only_data)
.map_err(|e| {
CircuitError::VerifierDataTargetAssignmentError(e.to_string())
})?;
Ok(())
}
/// returns the leaf circuit data
pub fn get_circuit_data (&self) -> Result<CircuitData<F, C, D>>
where
<C as GenericConfig<D>>::Hasher: AlgebraicHasher<F>
{
let config = CircuitConfig::standard_recursion_config();
let mut builder = CircuitBuilder::<F, D>::new(config.clone());
self.build(&mut builder)?;
let circ_data = builder.build::<C>();
Ok(circ_data)
}
}

View File

@ -0,0 +1,3 @@
pub mod leaf;
pub mod node;
pub mod tree;

View File

@ -0,0 +1,152 @@
use std::marker::PhantomData;
use plonky2::hash::hash_types::RichField;
use plonky2::iop::witness::{PartialWitness, WitnessWrite};
use plonky2::plonk::circuit_builder::CircuitBuilder;
use plonky2::plonk::circuit_data::{CircuitConfig, CircuitData, CommonCircuitData, VerifierCircuitTarget, VerifierOnlyCircuitData};
use plonky2::plonk::config::{AlgebraicHasher, GenericConfig};
use plonky2::plonk::proof::{ProofWithPublicInputs, ProofWithPublicInputsTarget};
use plonky2_field::extension::Extendable;
use plonky2_poseidon2::poseidon2_hash::poseidon2::Poseidon2;
use crate::recursion::circuits::inner_circuit::InnerCircuit;
use crate::{error::CircuitError,Result};
use crate::circuits::utils::vec_to_array;
/// recursion node circuit - verifies 2 leaf proofs
#[derive(Clone, Debug)]
pub struct NodeCircuit<
F: RichField + Extendable<D> + Poseidon2,
const D: usize,
C: GenericConfig<D, F = F>,
H: AlgebraicHasher<F>,
const M: usize,
> where
<C as GenericConfig<D>>::Hasher: AlgebraicHasher<F>
{
leaf_common_data: CommonCircuitData<F, D>,
phantom_data: PhantomData<(C,H)>
}
#[derive(Clone, Debug)]
pub struct NodeTargets<
const D: usize,
>{
pub leaf_proofs: Vec<ProofWithPublicInputsTarget<D>>,
pub verifier_data: VerifierCircuitTarget,
}
impl<
F: RichField + Extendable<D> + Poseidon2,
const D: usize,
C: GenericConfig<D, F = F>,
H: AlgebraicHasher<F>,
const M: usize,
> NodeCircuit<F,D,C,H,M> where
<C as GenericConfig<D>>::Hasher: AlgebraicHasher<F>
{
pub fn new(inner_common_data: CommonCircuitData<F,D>) -> Self {
Self{
leaf_common_data: inner_common_data,
phantom_data:PhantomData::default(),
}
}
/// build the leaf circuit
pub fn build(&self, builder: &mut CircuitBuilder<F, D>) -> Result<NodeTargets<D>> {
let inner_common = self.leaf_common_data.clone();
// assert public input is of size 8 - 2 hashout
assert_eq!(inner_common.num_public_inputs, 8);
// the proof virtual targets - M proofs
let mut vir_proofs = vec![];
let mut pub_input = vec![];
let mut inner_vd_hashes = vec![];
for _i in 0..M {
let vir_proof = builder.add_virtual_proof_with_pis(&inner_common);
let inner_pub_input = vir_proof.public_inputs.clone();
vir_proofs.push(vir_proof);
pub_input.extend_from_slice(&inner_pub_input[0..4]);
inner_vd_hashes.extend_from_slice(&inner_pub_input[4..8]);
}
// hash the public input & make it public
let hash_inner_pub_input = builder.hash_n_to_hash_no_pad::<H>(pub_input);
builder.register_public_inputs(&hash_inner_pub_input.elements);
// virtual target for the verifier data
let inner_verifier_data = builder.add_virtual_verifier_data(inner_common.config.fri_config.cap_height);
// register verifier data hash as public input. H(H_l, H_l, H_n) -> public input
let mut vd_pub_input = vec![];
vd_pub_input.extend_from_slice(&inner_verifier_data.circuit_digest.elements);
for i in 0..builder.config.fri_config.num_cap_elements() {
vd_pub_input.extend_from_slice(&inner_verifier_data.constants_sigmas_cap.0[i].elements);
}
let vd_hash = builder.hash_n_to_hash_no_pad::<H>(vd_pub_input);
inner_vd_hashes.extend_from_slice(&vd_hash.elements);
let vd_hash_all = builder.hash_n_to_hash_no_pad::<H>(inner_vd_hashes);
builder.register_public_inputs(&vd_hash_all.elements);
// verify the proofs in-circuit - M proofs
for i in 0..M {
builder.verify_proof::<C>(&vir_proofs[i], &inner_verifier_data, &inner_common);
}
// let proofs = vec_to_array::<2, ProofWithPublicInputsTarget<D>>(vir_proofs)?;
// return targets
let t = NodeTargets {
leaf_proofs: vir_proofs,
verifier_data: inner_verifier_data,
};
Ok(t)
}
/// assign the leaf targets with given input
pub fn assign_targets(
&self, pw: &mut PartialWitness<F>,
targets: &NodeTargets<D>,
node_proofs: &[ProofWithPublicInputs<F, C, D>],
verifier_only_data: &VerifierOnlyCircuitData<C, D>,
) -> Result<()> {
// assert size of proofs vec
assert_eq!(node_proofs.len(), M);
// assign the proofs
for i in 0..M {
pw.set_proof_with_pis_target(&targets.leaf_proofs[i], &node_proofs[i])
.map_err(|e| {
CircuitError::ProofTargetAssignmentError("inner-proof".to_string(), e.to_string())
})?;
}
// assign the verifier data
pw.set_verifier_data_target(&targets.verifier_data, &verifier_only_data)
.map_err(|e| {
CircuitError::VerifierDataTargetAssignmentError(e.to_string())
})?;
Ok(())
}
/// returns the leaf circuit data
pub fn get_circuit_data (&self) -> Result<CircuitData<F, C, D>>
where
<C as GenericConfig<D>>::Hasher: AlgebraicHasher<F>
{
let config = CircuitConfig::standard_recursion_config();
let mut builder = CircuitBuilder::<F, D>::new(config.clone());
self.build(&mut builder)?;
let circ_data = builder.build::<C>();
Ok(circ_data)
}
}

View File

@ -0,0 +1,252 @@
use std::marker::PhantomData;
use plonky2::hash::hash_types::{HashOut, RichField};
use plonky2::iop::witness::PartialWitness;
use plonky2::plonk::circuit_builder::CircuitBuilder;
use plonky2::plonk::circuit_data::{CircuitConfig, CircuitData, CommonCircuitData, VerifierCircuitData, VerifierOnlyCircuitData};
use plonky2::plonk::config::{AlgebraicHasher, GenericConfig};
use plonky2::plonk::proof::ProofWithPublicInputs;
use plonky2_poseidon2::poseidon2_hash::poseidon2::Poseidon2;
use crate::recursion::circuits::inner_circuit::InnerCircuit;
use plonky2_field::extension::Extendable;
use crate::{error::CircuitError, Result};
use crate::recursion::uniform::{leaf::{LeafTargets,LeafCircuit},node::{NodeTargets,NodeCircuit}};
/// tree recursion
pub struct TreeRecursion<
F: RichField + Extendable<D> + Poseidon2,
const D: usize,
C: GenericConfig<D, F = F>,
H: AlgebraicHasher<F>,
const N: usize,
const M: usize,
> where
<C as GenericConfig<D>>::Hasher: AlgebraicHasher<F>
{
leaf: LeafCircuit<F, D, C, H, N>,
node: NodeCircuit<F, D, C, H, M>,
leaf_circ_data: CircuitData<F, C, D>,
node_circ_data: CircuitData<F, C, D>,
leaf_targets: LeafTargets<D>,
node_targets: NodeTargets<D>,
phantom_data: PhantomData<(H)>
}
impl<
F: RichField + Extendable<D> + Poseidon2,
const D: usize,
C: GenericConfig<D, F = F>,
H: AlgebraicHasher<F>,
const N: usize,
const M: usize,
> TreeRecursion<F, D, C, H, N, M> where
<C as GenericConfig<D>>::Hasher: AlgebraicHasher<F>
{
pub fn build(
inner_common_data: CommonCircuitData<F,D>
) -> Result<Self> {
// build leaf with standard recursion config
let config = CircuitConfig::standard_recursion_config();
let mut builder = CircuitBuilder::<F, D>::new(config);
let leaf = LeafCircuit::<_,D,_,_,N>::new(inner_common_data.clone());
let leaf_targets = leaf.build(&mut builder)?;
let leaf_circ_data = builder.build::<C>();
// println!("leaf circuit size = {:?}", leaf_circ_data.common.degree_bits());
// build node with standard recursion config
let config = CircuitConfig::standard_recursion_config();
let mut builder = CircuitBuilder::<F, D>::new(config);
let node = NodeCircuit::<_,D,_,_,M>::new(leaf_circ_data.common.clone());
let node_targets = node.build(&mut builder)?;
let node_circ_data = builder.build::<C>();
// println!("node circuit size = {:?}", node_circ_data.common.degree_bits());
Ok(Self{
leaf,
node,
leaf_circ_data,
node_circ_data,
leaf_targets,
node_targets,
phantom_data: Default::default(),
})
}
pub fn get_leaf_verifier_data(&self) -> VerifierCircuitData<F, C, D>{
self.leaf_circ_data.verifier_data()
}
pub fn get_node_verifier_data(&self) -> VerifierCircuitData<F, C, D>{
self.node_circ_data.verifier_data()
}
pub fn prove_tree
(
&mut self,
proofs_with_pi: &[ProofWithPublicInputs<F, C, D>],
inner_verifier_only_data: &VerifierOnlyCircuitData<C, D>,
) -> Result<(ProofWithPublicInputs<F, C, D>)>
{
if proofs_with_pi.len() % 2 != 0 {
return
Err(CircuitError::RecursionTreeError(format!(
"input proofs must be divisible by {}, got {}", 2, proofs_with_pi.len())
))
}
// process leaves
let leaf_proofs = self.get_leaf_proofs(
proofs_with_pi,
inner_verifier_only_data,
)?;
// process nodes
let (root_proof, vd) =
self.prove(&leaf_proofs,&self.leaf_circ_data.verifier_only)?;
Ok(root_proof)
}
fn get_leaf_proofs
(
&mut self,
proofs_with_pi: &[ProofWithPublicInputs<F, C, D>],
inner_verifier_only_data: &VerifierOnlyCircuitData<C, D>,
) -> Result<(Vec<ProofWithPublicInputs<F, C, D>>)> {
let mut leaf_proofs = vec![];
for proof in proofs_with_pi.chunks(N){
let mut pw = PartialWitness::<F>::new();
self.leaf.assign_targets(&mut pw,&self.leaf_targets,proof,inner_verifier_only_data)?;
let proof = self.leaf_circ_data.prove(pw).unwrap();
leaf_proofs.push(proof);
}
Ok(leaf_proofs)
}
/// generates a proof
fn prove(
&self,
proofs_with_pi: &[ProofWithPublicInputs<F, C, D>],
verifier_only_data: &VerifierOnlyCircuitData<C, D>,
) -> Result<(ProofWithPublicInputs<F, C, D>, VerifierOnlyCircuitData<C, D>)> where
<C as GenericConfig<D>>::Hasher: AlgebraicHasher<F>
{
if proofs_with_pi.len() == 1 {
return Ok((proofs_with_pi[0].clone(), verifier_only_data.clone()));
}
let mut new_proofs = vec![];
for chunk in proofs_with_pi.chunks(M) {
let mut inner_pw = PartialWitness::new();
self.node.assign_targets(
&mut inner_pw,
&self.node_targets,
chunk,
verifier_only_data,
)?;
let proof = self.node_circ_data.prove(inner_pw)
.map_err(|e| CircuitError::ProofGenerationError(e.to_string()))?;
new_proofs.push(proof);
}
self.prove(&new_proofs, &self.node_circ_data.verifier_only)
}
pub fn verify_proof_and_public_input(
&self,
proof: ProofWithPublicInputs<F, C, D>,
inner_public_input: Vec<Vec<F>>,
inner_verifier_data: &VerifierCircuitData<F, C, D>) -> Result<()>
{
let public_input = proof.public_inputs.clone();
self.node_circ_data.verify(proof)
.map_err(|e| CircuitError::InvalidProofError(e.to_string()))?;
self.verify_public_input(public_input, inner_public_input, inner_verifier_data)
}
pub fn verify_public_input(&self, public_input: Vec<F>, inner_public_input: Vec<Vec<F>>, inner_verifier_data: &VerifierCircuitData<F, C, D>) -> Result<()>{
assert_eq!(public_input.len(), 8);
let given_input_hash = &public_input[0..4];
let given_vd_hash = &public_input[4..8];
let inner_hash = Self::get_hash_of_verifier_data(&inner_verifier_data);
let leaf_hash = Self::get_hash_of_verifier_data(&self.leaf_circ_data.verifier_data());
let node_hash = Self::get_hash_of_verifier_data(&self.node_circ_data.verifier_data());
let mut pub_in_hashes = vec![];
let mut inner_vd_hashes = vec![];
for pub_in in inner_public_input.chunks(N){
let pub_in_flat: Vec<F> = pub_in
.iter()
.flat_map(|v| v.iter().cloned())
.collect();
let hash = H::hash_no_pad(&pub_in_flat);
pub_in_hashes.push(hash);
inner_vd_hashes.push(inner_hash.clone());
}
let mut level = 0;
while pub_in_hashes.len() > 1 {
let mut next_level_pi_hashes = Vec::new();
let mut next_level_vd_hashes = Vec::new();
for (pi_chunk, vd_chunk) in pub_in_hashes.chunks(M).zip(inner_vd_hashes.chunks(M)) {
// collect field elements
let pi_chunk_f: Vec<F> = pi_chunk.iter()
.flat_map(|h| h.elements.iter().cloned())
.collect();
let mut vd_chunk_f: Vec<F> = vd_chunk.iter()
.flat_map(|h| h.elements.iter().cloned())
.collect();
let hash_n = if level == 0 {leaf_hash} else{node_hash};
vd_chunk_f.extend_from_slice(&hash_n.elements);
// Compute Poseidon2 hash of the concatenated chunk
let pi_hash = H::hash_no_pad(&pi_chunk_f);
let vd_hash = H::hash_no_pad(&vd_chunk_f);
next_level_pi_hashes.push(pi_hash);
next_level_vd_hashes.push(vd_hash);
}
pub_in_hashes = next_level_pi_hashes;
inner_vd_hashes = next_level_vd_hashes;
level +=1;
}
//check expected hash
let expected_pi_hash = pub_in_hashes[0];
let expected_vd_hash = inner_vd_hashes[0];
assert_eq!(given_input_hash, expected_pi_hash.elements);
assert_eq!(given_vd_hash, expected_vd_hash.elements);
Ok(())
}
/// helper fn to generate hash of verifier data
fn get_hash_of_verifier_data(verifier_data: &VerifierCircuitData<F, C, D>) -> HashOut<F>{
let mut vd = vec![];
let digest: &HashOut<F> = &verifier_data.verifier_only.circuit_digest;
let caps = &verifier_data.verifier_only.constants_sigmas_cap;
vd.extend_from_slice(&digest.elements);
for i in 0..verifier_data.common.config.fri_config.num_cap_elements() {
let cap_hash = caps.0[i] as HashOut<F>;
vd.extend_from_slice(&cap_hash.elements);
}
H::hash_no_pad(&vd)
}
}

View File

@ -0,0 +1,121 @@
use plonky2::gates::noop::NoopGate;
use plonky2::hash::hash_types::RichField;
use plonky2::iop::target::BoolTarget;
use plonky2::plonk::circuit_builder::CircuitBuilder;
use plonky2::plonk::circuit_data::{CircuitData, CommonCircuitData, VerifierCircuitTarget};
use plonky2::plonk::config::{AlgebraicHasher, GenericConfig};
use plonky2::plonk::proof::ProofWithPublicInputsTarget;
use plonky2_field::extension::Extendable;
use plonky2_poseidon2::poseidon2_hash::poseidon2::Poseidon2;
/// this takes verifier data (not public) and doesn't check the verifier data for consistency
pub fn conditionally_verify_recursion_proof_or_dummy<F: RichField + Extendable<D> + Poseidon2, const D: usize ,C: GenericConfig<D, F = F> + 'static>(
builder: &mut CircuitBuilder<F, D>,
condition: BoolTarget,
cyclic_proof_with_pis: &ProofWithPublicInputsTarget<D>,
verifier_data: &VerifierCircuitTarget,
common_data: &CommonCircuitData<F, D>,
) -> anyhow::Result<()>
where
C::Hasher: AlgebraicHasher<F>,
{
let (dummy_proof_with_pis_target, dummy_verifier_data_target) =
dummy_proof_and_vk_no_generator::<F, D, C>(builder, common_data)?;
// TODO: make verifier data public
// // Connect previous verifier data to current one. This guarantees that every proof in the cycle uses the same verifier data.
// self.connect_hashes(
// inner_cyclic_pis.circuit_digest,
// verifier_data.circuit_digest,
// );
// self.connect_merkle_caps(
// &inner_cyclic_pis.constants_sigmas_cap,
// &verifier_data.constants_sigmas_cap,
// );
// Verify the cyclic proof if `condition` is set to true, otherwise verify the other proof.
builder.conditionally_verify_proof::<C>(
condition,
cyclic_proof_with_pis,
verifier_data,
&dummy_proof_with_pis_target,
&dummy_verifier_data_target,
common_data,
);
// Make sure we have every gate to match `common_data`.
for g in &common_data.gates {
builder.add_gate_to_gate_set(g.clone());
}
Ok(())
}
/// Conditionally verify a proof with a new generated dummy proof.
pub fn conditionally_verify_proof_or_dummy<F: RichField + Extendable<D> + Poseidon2, const D: usize ,C: GenericConfig<D, F = F> + 'static>(
builder: &mut CircuitBuilder<F, D>,
condition: BoolTarget,
proof_with_pis: &ProofWithPublicInputsTarget<D>,
inner_verifier_data: &VerifierCircuitTarget,
inner_common_data: &CommonCircuitData<F, D>,
) -> anyhow::Result<()>
where
C::Hasher: AlgebraicHasher<F>,
{
let (dummy_proof_with_pis_target, dummy_verifier_data_target) =
dummy_proof_and_vk_no_generator::<F, D, C>(builder, inner_common_data)?;
builder.conditionally_verify_proof::<C>(
condition,
proof_with_pis,
inner_verifier_data,
&dummy_proof_with_pis_target,
&dummy_verifier_data_target,
inner_common_data,
);
Ok(())
}
/// Generate a circuit matching a given `CommonCircuitData`.
pub(crate) fn dummy_circuit<F: RichField + Extendable<D> + Poseidon2, C: GenericConfig<D, F = F>, const D: usize>(
common_data: &CommonCircuitData<F, D>,
) -> CircuitData<F, C, D> {
let config = common_data.config.clone();
assert!(
!common_data.config.zero_knowledge,
"Degree calculation can be off if zero-knowledge is on."
);
// Number of `NoopGate`s to add to get a circuit of size `degree` in the end.
// Need to account for public input hashing, a `PublicInputGate` and a `ConstantGate`.
let degree = common_data.degree();
let num_noop_gate = degree - common_data.num_public_inputs.div_ceil(8) - 2;
let mut builder = CircuitBuilder::<F, D>::new(config);
for _ in 0..num_noop_gate {
builder.add_gate(NoopGate, vec![]);
}
for gate in &common_data.gates {
builder.add_gate_to_gate_set(gate.clone());
}
for _ in 0..common_data.num_public_inputs {
builder.add_virtual_public_input();
}
let circuit = builder.build::<C>();
assert_eq!(&circuit.common, common_data);
circuit
}
pub(crate) fn dummy_proof_and_vk_no_generator<F: RichField + Extendable<D> + Poseidon2, const D: usize ,C: GenericConfig<D, F = F> + 'static> (
builder: &mut CircuitBuilder<F, D>,
common_data: &CommonCircuitData<F, D>,
) -> anyhow::Result<(ProofWithPublicInputsTarget<D>, VerifierCircuitTarget)>
where
C::Hasher: AlgebraicHasher<F>,
{
let dummy_circuit = dummy_circuit::<F, C, D>(common_data);
let dummy_proof_with_pis_target = builder.add_virtual_proof_with_pis(common_data);
let dummy_verifier_data_target = builder.constant_verifier_data(&dummy_circuit.verifier_only);
Ok((dummy_proof_with_pis_target, dummy_verifier_data_target))
}

View File

@ -0,0 +1,79 @@
use std::marker::PhantomData;
use plonky2::plonk::circuit_data::{CommonCircuitData, VerifierOnlyCircuitData};
use plonky2::plonk::proof::{ProofWithPublicInputs};
use plonky2::recursion::dummy_circuit::{dummy_proof};
use crate::recursion::utils::conditional_verifier::dummy_circuit;
use hashbrown::HashMap;
use plonky2::hash::hash_types::{ RichField};
use plonky2::plonk::config::{AlgebraicHasher, GenericConfig};
use plonky2_field::extension::Extendable;
use plonky2_poseidon2::poseidon2_hash::poseidon2::Poseidon2;
use crate::{error::CircuitError, Result};
use crate::circuits::utils::vec_to_array;
/// A generator for creating dummy proofs.
pub struct DummyProofGen<
F: RichField + Extendable<D> + Poseidon2,
const D: usize,
C: GenericConfig<D, F = F>,
> where
<C as GenericConfig<D>>::Hasher: AlgebraicHasher<F>
{
phantom_data: PhantomData<(F,C)>,
}
impl<
F: RichField + Extendable<D> + Poseidon2,
const D: usize,
C: GenericConfig<D, F = F>,
> DummyProofGen<F, D, C>
where
<C as GenericConfig<D>>::Hasher: AlgebraicHasher<F>
{
/// Generates a single dummy leaf proof.
pub fn gen_dummy_leaf_proof(
common_data: &CommonCircuitData<F, D>,
) -> Result<ProofWithPublicInputs<F, C, D>> {
dummy_proof::<F, C, D>(&dummy_circuit::<F, C, D>(common_data), HashMap::new())
.map_err(|e| CircuitError::DummyProofGenerationError(e.to_string()))
}
/// Generates a single dummy node proof.
pub fn get_dummy_node_proof(
node_common: &CommonCircuitData<F, D>,
node_verifier_only_data: &VerifierOnlyCircuitData<C, D>,
) -> ProofWithPublicInputs<F, C, D> {
Self::recursion_base_proof(node_common, HashMap::new())
}
fn recursion_base_proof(
common_data: &CommonCircuitData<F, D>,
mut nonzero_public_inputs: HashMap<usize, F>
) -> ProofWithPublicInputs<F, C, D>{
dummy_proof::<F, C, D>(
&dummy_circuit::<F, C, D>(common_data),
nonzero_public_inputs,
)
.unwrap()
}
/// Generates an array of `N` dummy leaf proofs.
pub fn gen_n_dummy_leaf_proofs<const N: usize>(
common_data: &CommonCircuitData<F, D>,
) -> Result<[ProofWithPublicInputs<F, C, D>; N]> {
let dummy_proof = Self::gen_dummy_leaf_proof(common_data)?;
let n_dummy_vec = (0..N).map(|_| dummy_proof.clone()).collect::<Vec<_>>();
vec_to_array::<N, ProofWithPublicInputs<F, C, D>>(n_dummy_vec)
}
/// Generates an array of `N` dummy node proofs.
pub fn gen_n_dummy_node_proofs<const N: usize>(
node_common: &CommonCircuitData<F, D>,
node_verifier_only_data: &VerifierOnlyCircuitData<C, D>,
) -> Result<[ProofWithPublicInputs<F, C, D>; N]> {
let dummy_proof = Self::get_dummy_node_proof(node_common, node_verifier_only_data);
let n_dummy_vec = (0..N).map(|_| dummy_proof.clone()).collect::<Vec<_>>();
vec_to_array::<N, ProofWithPublicInputs<F, C, D>>(n_dummy_vec)
}
}

View File

@ -0,0 +1,2 @@
pub mod dummy_gen;
pub mod conditional_verifier;

View File

@ -0,0 +1,7 @@
pub mod simple_recursion;
pub mod simple_tree;
pub mod cyclic_recursion;
pub mod tree1;
pub mod tree2;
mod hybrid;
mod uniform;

View File

@ -0,0 +1,67 @@
// some tests for approach 2 of the tree recursion
#[cfg(test)]
mod tests {
use std::fs;
use plonky2::iop::witness::{PartialWitness};
use plonky2::plonk::circuit_builder::CircuitBuilder;
use plonky2::plonk::circuit_data::{CircuitConfig};
use plonky2::plonk::config::{ GenericConfig};
use plonky2::plonk::proof::{ProofWithPublicInputs};
use codex_plonky2_circuits::circuits::sample_cells::SampleCircuit;
use crate::params::{F, D, C, HF};
use crate::gen_input::gen_testing_circuit_input;
use crate::params::Params;
use codex_plonky2_circuits::recursion::uniform::{tree::TreeRecursion};
#[test]
fn test_uniform_recursion() -> anyhow::Result<()> {
let config = CircuitConfig::standard_recursion_config();
let mut sampling_builder = CircuitBuilder::<F, D>::new(config);
//------------ sampling inner circuit ----------------------
// Circuit that does the sampling - 100 samples
let mut params = Params::default();
params.input_params.n_samples = 100;
params.circuit_params.n_samples = 100;
let one_circ_input = gen_testing_circuit_input::<F,D>(&params.input_params);
let samp_circ = SampleCircuit::<F,D,HF>::new(params.circuit_params);
let inner_tar = samp_circ.sample_slot_circuit_with_public_input(&mut sampling_builder)?;
// get generate a sampling proof
let mut pw = PartialWitness::<F>::new();
samp_circ.sample_slot_assign_witness(&mut pw,&inner_tar,&one_circ_input)?;
let inner_data = sampling_builder.build::<C>();
println!("sampling circuit degree bits = {:?}", inner_data.common.degree_bits());
let inner_proof = inner_data.prove(pw)?;
let proofs: Vec<ProofWithPublicInputs<F, C, D>> = (0..16).map(|i| inner_proof.clone()).collect();
// ------------------- tree --------------------
const N: usize = 1;
const M: usize = 4;
let mut tree = TreeRecursion::<F,D,C,HF, N, M>::build(inner_data.common.clone())?;
// serialize circuit into JSON
let common_circuit_data_serialized = serde_json::to_string(&tree.get_leaf_verifier_data().common ).unwrap();
fs::write("leaf_common.json" , common_circuit_data_serialized) .expect("Unable to write file");
// serialize circuit into JSON
let common_circuit_data_serialized = serde_json::to_string(&tree.get_node_verifier_data().common ).unwrap();
fs::write("node_common.json" , common_circuit_data_serialized) .expect("Unable to write file");
let root = tree.prove_tree(&proofs, &inner_data.verifier_only)?;
println!("pub input size = {}", root.public_inputs.len());
let inner_pi: Vec<Vec<F>> = proofs.iter().map(|p| p.public_inputs.clone()).collect();
assert!(
tree.verify_proof_and_public_input(root,inner_pi,&inner_data.verifier_data()).is_ok(),
"proof verification failed"
);
Ok(())
}
}

View File

@ -0,0 +1,395 @@
// some tests for approach 2 of the tree recursion
#[cfg(test)]
mod tests {
use std::fs;
use std::time::Instant;
use plonky2::gates::constant::ConstantGate;
use plonky2::iop::witness::{PartialWitness, WitnessWrite};
use plonky2::plonk::circuit_builder::CircuitBuilder;
use plonky2::plonk::circuit_data::{CircuitConfig, CommonCircuitData, VerifierCircuitTarget};
use plonky2::plonk::config::{AlgebraicHasher, GenericConfig, Hasher};
use plonky2::plonk::proof::{ProofWithPublicInputs, ProofWithPublicInputsTarget};
use codex_plonky2_circuits::circuits::sample_cells::SampleCircuit;
use crate::params::{F, D, C, HF};
use codex_plonky2_circuits::recursion::circuits::sampling_inner_circuit::SamplingRecursion;
// use codex_plonky2_circuits::recursion::circuits::inner_circuit::InnerCircuit;
// use codex_plonky2_circuits::recursion::circuits::leaf_circuit::{LeafCircuit};
// use plonky2_poseidon2::poseidon2_hash::poseidon2::{Poseidon2, Poseidon2Hash};
use crate::gen_input::gen_testing_circuit_input;
use crate::params::Params;
use codex_plonky2_circuits::recursion::uniform::{leaf::{LeafCircuit,LeafInput,LeafTargets},node::{NodeCircuit,NodeInput,NodeTargets}, tree::TreeRecursion};
#[test]
fn test_treeuniform() -> anyhow::Result<()> {
let config = CircuitConfig::standard_recursion_config();
let mut sampling_builder = CircuitBuilder::<F, D>::new(config);
//------------ sampling inner circuit ----------------------
// Circuit that does the sampling - default input
let mut params = Params::default();
params.input_params.n_samples = 100;
params.circuit_params.n_samples = 100;
let one_circ_input = gen_testing_circuit_input::<F,D>(&params.input_params);
let samp_circ = SampleCircuit::<F,D,HF>::new(params.circuit_params);
let inner_tar = samp_circ.sample_slot_circuit_with_public_input(&mut sampling_builder)?;
// get generate a sampling proof
let mut pw = PartialWitness::<F>::new();
samp_circ.sample_slot_assign_witness(&mut pw,&inner_tar,&one_circ_input)?;
let inner_data = sampling_builder.build::<C>();
println!("sampling circuit degree bits = {:?}", inner_data.common.degree_bits());
let inner_proof = inner_data.prove(pw)?;
let proofs: Vec<ProofWithPublicInputs<F, C, D>> = (0..4).map(|i| inner_proof.clone()).collect();
// ------------------- tree --------------------
let mut tree = TreeRecursion::<F,D,C,HF>::build(inner_data.common.clone())?;
let root = tree.prove_tree(&proofs, inner_data.verifier_data())?;
Ok(())
}
#[test]
fn test_2uniform() -> anyhow::Result<()> {
let config = CircuitConfig::standard_recursion_config();
let mut sampling_builder = CircuitBuilder::<F, D>::new(config);
//------------ sampling inner circuit ----------------------
// Circuit that does the sampling - default input
let mut params = Params::default();
params.input_params.n_samples = 100;
params.circuit_params.n_samples = 100;
let one_circ_input = gen_testing_circuit_input::<F,D>(&params.input_params);
let samp_circ = SampleCircuit::<F,D,HF>::new(params.circuit_params);
let inner_tar = samp_circ.sample_slot_circuit_with_public_input(&mut sampling_builder)?;
// get generate a sampling proof
let mut pw = PartialWitness::<F>::new();
samp_circ.sample_slot_assign_witness(&mut pw,&inner_tar,&one_circ_input)?;
let inner_data = sampling_builder.build::<C>();
println!("sampling circuit degree bits = {:?}", inner_data.common.degree_bits());
let inner_proof = inner_data.prove(pw)?;
// serialize circuit into JSON
let common_circuit_data_serialized = serde_json::to_string(&inner_data.common ).unwrap();
fs::write("circ_common.json" , common_circuit_data_serialized) .expect("Unable to write file");
// ------------------- leaf --------------------
let config = CircuitConfig::standard_recursion_config();
let mut builder = CircuitBuilder::<F, D>::new(config.clone());
let leaf_circ = LeafCircuit::<F,D,C,HF>::new(inner_data.common.clone());
let leaf_targ = leaf_circ.build(&mut builder)?;
// let (proof_targ, vd_targ) = build_proof_ver_circuit::<HF>(&mut builder,&inner_data.common).unwrap();
// let leaf_targets = leaf_circuit.build::<C,HF>(&mut builder)?;
// // add a ConstantGate
// builder.add_gate(
// ConstantGate::new(config.num_constants),
// vec![],
// );
let leaf_data = builder.build::<C>();
println!("leaf circuit size = {:?}", leaf_data.common.degree_bits());
// serialize circuit into JSON
let common_circuit_data_serialized = serde_json::to_string(&leaf_data.common ).unwrap();
fs::write("leaf_common.json" , common_circuit_data_serialized) .expect("Unable to write file");
// prove
let mut pw = PartialWitness::<F>::new();
// pw.set_proof_with_pis_target(&proof_targ, &inner_proof)?;
// pw.set_verifier_data_target(&vd_targ,&inner_data.verifier_only)?;
let leaf_in = LeafInput{
inner_proof: inner_proof.clone(),
verifier_data: inner_data.verifier_data().clone(),
};
leaf_circ.assign_targets(&mut pw, &leaf_targ, &leaf_in)?;
let leaf_proof = leaf_data.prove(pw)?;
leaf_data.verify(leaf_proof.clone())?;
// ------------- node1 circuit ------------------
let config = CircuitConfig::standard_recursion_config();
let mut builder = CircuitBuilder::<F, D>::new(config);
let node_circ = NodeCircuit::<F,D,C,HF>::new(leaf_data.common.clone());
let node_targ = node_circ.build(&mut builder)?;
// let (proof_targ, vd_targ) = build_node_proof_circuit::<HF>(&mut builder,&leaf_data.common).unwrap();
// let leaf_targets = leaf_circuit.build::<C,HF>(&mut builder)?;
let node_data = builder.build::<C>();
println!("node circuit size = {:?}", node_data.common.degree_bits());
// serialize circuit into JSON
let common_circuit_data_serialized = serde_json::to_string(&node_data.common ).unwrap();
fs::write("node_common.json" , common_circuit_data_serialized) .expect("Unable to write file");
// prove
let mut pw = PartialWitness::<F>::new();
// pw.set_proof_with_pis_target(&proof_targ[0], &leaf_proof)?;
// pw.set_proof_with_pis_target(&proof_targ[1], &leaf_proof)?;
// pw.set_verifier_data_target(&vd_targ,&leaf_data.verifier_only)?;
let node_in = NodeInput{
node_proofs: [leaf_proof.clone(),leaf_proof.clone()],
verifier_data: leaf_data.verifier_data(),
};
node_circ.assign_targets(&mut pw, &node_targ,&node_in)?;
let node_proof = node_data.prove(pw)?;
node_data.verify(node_proof.clone())?;
// ------------- check ----------------
// prove
let mut pw = PartialWitness::<F>::new();
let node_in = NodeInput{
node_proofs: [node_proof.clone(),node_proof.clone()],
verifier_data: node_data.verifier_data(),
};
node_circ.assign_targets(&mut pw, &node_targ,&node_in)?;
let node2_proof = node_data.prove(pw)?;
node_data.verify(node2_proof.clone())?;
// ------------- node2 circuit ------------------
// let config = CircuitConfig::standard_recursion_config();
// let mut builder = CircuitBuilder::<F, D>::new(config);
//
// let (proof_targ, vd_targ) = build_node_proof_circuit::<HF>(&mut builder,&node_data.common).unwrap();
// // let leaf_targets = leaf_circuit.build::<C,HF>(&mut builder)?;
//
// let node2_data = builder.build::<C>();
// println!("node2 circuit size = {:?}", node2_data.common.degree_bits());
//
// // serialize circuit into JSON
// let common_circuit_data_serialized = serde_json::to_string(&node2_data.common ).unwrap();
// fs::write("node2_common.json" , common_circuit_data_serialized) .expect("Unable to write file");
//
// // prove
// let mut pw = PartialWitness::<F>::new();
// pw.set_proof_with_pis_target(&proof_targ[0], &leaf_proof)?;
// pw.set_proof_with_pis_target(&proof_targ[1], &leaf_proof)?;
// pw.set_verifier_data_target(&vd_targ,&leaf_data.verifier_only)?;
//
// let node_proof = node2_data.prove(pw)?;
// prove node
// let mut pw = PartialWitness::<F>::new();
// pw.set_proof_with_pis_target(&proof_targ[0], &node_proof)?;
// pw.set_proof_with_pis_target(&proof_targ[1], &node_proof)?;
// pw.set_verifier_data_target(&vd_targ,&node_data.verifier_only)?;
//
// let node2_proof = node_data.prove(pw)?;
Ok(())
}
#[test]
fn test_uniform_recursion() -> anyhow::Result<()> {
// const N: usize = 2; // binary tree
// const M: usize = 1; // number of proofs in leaves
// const K: usize = 8;
let config = CircuitConfig::standard_recursion_config();
let mut sampling_builder = CircuitBuilder::<F, D>::new(config);
//------------ sampling inner circuit ----------------------
// Circuit that does the sampling - default input
let mut params = Params::default();
params.input_params.n_samples = 100;
params.circuit_params.n_samples = 100;
let one_circ_input = gen_testing_circuit_input::<F,D>(&params.input_params);
let samp_circ = SampleCircuit::<F,D,HF>::new(params.circuit_params);
let inner_tar = samp_circ.sample_slot_circuit_with_public_input(&mut sampling_builder)?;
// get generate a sampling proof
let mut pw = PartialWitness::<F>::new();
samp_circ.sample_slot_assign_witness(&mut pw,&inner_tar,&one_circ_input)?;
let inner_data = sampling_builder.build::<C>();
println!("sampling circuit degree bits = {:?}", inner_data.common.degree_bits());
let inner_proof = inner_data.prove(pw)?;
// serialize circuit into JSON
let common_circuit_data_serialized = serde_json::to_string(&inner_data.common ).unwrap();
fs::write("circ_common.json" , common_circuit_data_serialized) .expect("Unable to write file");
// ------------------- leaf --------------------
let config = CircuitConfig::standard_recursion_config();
let mut builder = CircuitBuilder::<F, D>::new(config.clone());
let (proof_targ, vd_targ) = build_proof_ver_circuit::<HF>(&mut builder,&inner_data.common).unwrap();
// let leaf_targets = leaf_circuit.build::<C,HF>(&mut builder)?;
// // add a ConstantGate
// builder.add_gate(
// ConstantGate::new(config.num_constants),
// vec![],
// );
let leaf_data = builder.build::<C>();
println!("leaf circuit size = {:?}", leaf_data.common.degree_bits());
// serialize circuit into JSON
let common_circuit_data_serialized = serde_json::to_string(&leaf_data.common ).unwrap();
fs::write("leaf_common.json" , common_circuit_data_serialized) .expect("Unable to write file");
// prove
let mut pw = PartialWitness::<F>::new();
pw.set_proof_with_pis_target(&proof_targ, &inner_proof)?;
pw.set_verifier_data_target(&vd_targ,&inner_data.verifier_only)?;
let leaf_proof = leaf_data.prove(pw)?;
// ------------- node1 circuit ------------------
let config = CircuitConfig::standard_recursion_config();
let mut builder = CircuitBuilder::<F, D>::new(config);
let (proof_targ, vd_targ) = build_node_proof_circuit::<HF>(&mut builder,&leaf_data.common).unwrap();
// let leaf_targets = leaf_circuit.build::<C,HF>(&mut builder)?;
let node_data = builder.build::<C>();
println!("node circuit size = {:?}", node_data.common.degree_bits());
// serialize circuit into JSON
let common_circuit_data_serialized = serde_json::to_string(&node_data.common ).unwrap();
fs::write("node_common.json" , common_circuit_data_serialized) .expect("Unable to write file");
// prove
let mut pw = PartialWitness::<F>::new();
pw.set_proof_with_pis_target(&proof_targ[0], &leaf_proof)?;
pw.set_proof_with_pis_target(&proof_targ[1], &leaf_proof)?;
pw.set_verifier_data_target(&vd_targ,&leaf_data.verifier_only)?;
let node_proof = node_data.prove(pw)?;
// ------------- node2 circuit ------------------
// let config = CircuitConfig::standard_recursion_config();
// let mut builder = CircuitBuilder::<F, D>::new(config);
//
// let (proof_targ, vd_targ) = build_node_proof_circuit::<HF>(&mut builder,&node_data.common).unwrap();
// // let leaf_targets = leaf_circuit.build::<C,HF>(&mut builder)?;
//
// let node2_data = builder.build::<C>();
// println!("node2 circuit size = {:?}", node2_data.common.degree_bits());
//
// // serialize circuit into JSON
// let common_circuit_data_serialized = serde_json::to_string(&node2_data.common ).unwrap();
// fs::write("node2_common.json" , common_circuit_data_serialized) .expect("Unable to write file");
//
// // prove
// let mut pw = PartialWitness::<F>::new();
// pw.set_proof_with_pis_target(&proof_targ[0], &leaf_proof)?;
// pw.set_proof_with_pis_target(&proof_targ[1], &leaf_proof)?;
// pw.set_verifier_data_target(&vd_targ,&leaf_data.verifier_only)?;
//
// let node_proof = node2_data.prove(pw)?;
// prove node
let mut pw = PartialWitness::<F>::new();
pw.set_proof_with_pis_target(&proof_targ[0], &node_proof)?;
pw.set_proof_with_pis_target(&proof_targ[1], &node_proof)?;
pw.set_verifier_data_target(&vd_targ,&node_data.verifier_only)?;
let node2_proof = node_data.prove(pw)?;
Ok(())
}
/// builds the node circuit
pub fn build_proof_ver_circuit<
H: AlgebraicHasher<F>,
>(
builder: &mut CircuitBuilder<F, D>,
common_data: &CommonCircuitData<F, D>,
) -> anyhow::Result<(ProofWithPublicInputsTarget<D>, VerifierCircuitTarget)>{
// the proof virtual targets
// let mut proof_targets = vec![];
let mut inner_pub_input = vec![];
// for _i in 0..N {
let vir_proof = builder.add_virtual_proof_with_pis(common_data);
// collect the public input
inner_pub_input.extend_from_slice(&vir_proof.public_inputs);
// collect the proof targets
// proof_targets.push(vir_proof);
// }
// hash the public input & make it public
let hash_inner_pub_input = builder.hash_n_to_hash_no_pad::<H>(inner_pub_input);
builder.register_public_inputs(&hash_inner_pub_input.elements);
// virtual target for the verifier data
let inner_verifier_data = builder.add_virtual_verifier_data(common_data.config.fri_config.cap_height);
// verify the proofs in-circuit
// for i in 0..N {
builder.verify_proof::<C>(&vir_proof,&inner_verifier_data,&common_data);
// }
// let proof_target_array = vec_to_array::<N,ProofWithPublicInputsTarget<D>>(proof_targets)?;
Ok(
(vir_proof,
inner_verifier_data)
)
}
/// builds the node circuit
pub fn build_node_proof_circuit<
H: AlgebraicHasher<F>,
>(
builder: &mut CircuitBuilder<F, D>,
common_data: &CommonCircuitData<F, D>,
) -> anyhow::Result<(Vec<ProofWithPublicInputsTarget<D>>, VerifierCircuitTarget)>{
// the proof virtual targets
let mut proof_targets = vec![];
let mut inner_pub_input = vec![];
for _i in 0..2 {
let vir_proof = builder.add_virtual_proof_with_pis(common_data);
// collect the public input
inner_pub_input.extend_from_slice(&vir_proof.public_inputs);
// collect the proof targets
proof_targets.push(vir_proof);
}
// hash the public input & make it public
let hash_inner_pub_input = builder.hash_n_to_hash_no_pad::<H>(inner_pub_input);
builder.register_public_inputs(&hash_inner_pub_input.elements);
// virtual target for the verifier data
let inner_verifier_data = builder.add_virtual_verifier_data(common_data.config.fri_config.cap_height);
// verify the proofs in-circuit
for i in 0..2 {
builder.verify_proof::<C>(&proof_targets[i],&inner_verifier_data,&common_data);
}
// let proof_target_array = vec_to_array::<N,ProofWithPublicInputsTarget<D>>(proof_targets)?;
Ok(
(proof_targets,
inner_verifier_data)
)
}
}

View File

@ -46,34 +46,6 @@ harness = false
name = "sample_cells"
harness = false
[[bench]]
name = "simple_recursion"
harness = false
[[bench]]
name = "simple_recursion_hashed_pi"
harness = false
[[bench]]
name = "cyclic_recursion"
harness = false
[[bench]]
name = "tree_recursion1"
harness = false
[[bench]]
name = "tree_recursion2"
harness = false
[[bench]]
name = "simple_tree_recursion"
harness = false
[[bench]]
name = "hybrid_recursion"
harness = false
[[bench]]
name = "uniform_recursion"
harness = false

View File

@ -44,9 +44,10 @@ fn bench_prove_verify(c: &mut Criterion) -> Result<()>{
b.iter(|| {
let config = CircuitConfig::standard_recursion_config();
let mut local_builder = CircuitBuilder::<F, D>::new(config);
let local_targets = targets.clone();
let mut local_pw = pw.clone();
circ.sample_slot_assign_witness(&mut local_pw, &local_targets, &circ_input.clone());
let _targets = circ.sample_slot_circuit_with_public_input(&mut local_builder);
// let local_targets = targets.clone();
// let mut local_pw = pw.clone();
// circ.sample_slot_assign_witness(&mut local_pw, &local_targets, &circ_input.clone());
let _data = local_builder.build::<C>();
})
});
@ -58,14 +59,14 @@ fn bench_prove_verify(c: &mut Criterion) -> Result<()>{
println!("Build time: {:?}", build_duration);
println!("Circuit size (degree bits): {:?}", data.common.degree_bits());
let num_constr: usize = data.common
.gates
.iter()
.map(|gate| gate.0.num_constraints())
.sum();
println!("Number of constraints: {}", num_constr);
println!("Number of gates used: {}", data.common.gates.len());
// let num_constr: usize = data.common
// .gates
// .iter()
// .map(|gate| gate.0.num_constraints())
// .sum();
//
// println!("Number of constraints: {}", num_constr);
// println!("Number of gates used: {}", data.common.gates.len());
// Benchmark the Proving Phase
group.bench_function("Prove Circuit", |b| {

View File

@ -11,9 +11,9 @@ use proof_input::gen_input::gen_testing_circuit_input;
use proof_input::params::Params;
/// Benchmark for building, proving, and verifying the Plonky2 tree recursion circuit.
fn bench_uniform_recursion<const N: usize,>(c: &mut Criterion) -> anyhow::Result<()>{
fn bench_uniform_recursion<const K: usize,const N: usize,const M: usize,>(c: &mut Criterion) -> anyhow::Result<()>{
let mut group = c.benchmark_group(format!("Uniform Tree Recursion Benchmark for N={}",N));
let mut group = c.benchmark_group(format!("Uniform Tree Recursion Benchmark for aggregating {} proofs with params: N={}, M={}",K,N,M));
//------------ sampling inner circuit ----------------------
// Circuit that does the sampling - 100 samples
@ -35,12 +35,12 @@ fn bench_uniform_recursion<const N: usize,>(c: &mut Criterion) -> anyhow::Result
// ------------------- tree --------------------
let mut tree : Option<TreeRecursion<F, D, C, HF>> = None;
let mut tree : Option<TreeRecursion<F, D, C, HF, N, M>> = None;
// Building phase
group.bench_function("build tree", |b| {
b.iter(|| {
tree = Some(TreeRecursion::<F,D,C,HF>::build(inner_data.common.clone()).unwrap());
tree = Some(TreeRecursion::<F,D,C,HF, N, M>::build(inner_data.common.clone()).unwrap());
})
});
@ -57,27 +57,31 @@ fn bench_uniform_recursion<const N: usize,>(c: &mut Criterion) -> anyhow::Result
let proof = proof.unwrap();
let inner_pi: Vec<Vec<F>> = proofs.iter().map(|p| p.public_inputs.clone()).collect();
// Verifying Phase
// group.bench_function("verify tree circuit", |b| {
// b.iter(|| {
// verifier_data.verify(proof.clone()).expect("verify fail");
// })
// });
group.bench_function("verify root proof", |b| {
b.iter(|| {
tree.verify_proof_and_public_input(proof.clone(),inner_pi.clone(),&inner_data.verifier_data())
})
});
group.finish();
Ok(())
}
fn bench_uniform_tree_recursion(c: &mut Criterion){
const N: usize = 2; // number of child nodes - binary here
bench_uniform_recursion::<2>(c).expect("bench failed");
bench_uniform_recursion::<4>(c).expect("bench failed");
bench_uniform_recursion::<8>(c).expect("bench failed");
bench_uniform_recursion::<16>(c).expect("bench failed");
bench_uniform_recursion::<32>(c).expect("bench failed");
bench_uniform_recursion::<64>(c).expect("bench failed");
bench_uniform_recursion::<128>(c).expect("bench failed");
bench_uniform_recursion::<256>(c).expect("bench failed");
const K: usize = 32; // number of inner proofs to aggregate
const N: usize = 1; // number of inner proofs in the leaf
const M: usize = 2; // number of leaf proofs in the node
bench_uniform_recursion::<2, N, M>(c).expect("bench failed");
bench_uniform_recursion::<4, N, M>(c).expect("bench failed");
bench_uniform_recursion::<8, N, M>(c).expect("bench failed");
bench_uniform_recursion::<16, N, M>(c).expect("bench failed");
bench_uniform_recursion::<32, N, M>(c).expect("bench failed");
bench_uniform_recursion::<64, N, M>(c).expect("bench failed");
bench_uniform_recursion::<128, N, M>(c).expect("bench failed");
bench_uniform_recursion::<256, N, M>(c).expect("bench failed");
}
/// Criterion benchmark group