use criterion::{criterion_group, criterion_main, Criterion}; use anyhow::Result; use std::time::{Duration, Instant}; use codex_plonky2_circuits::{ merkle_tree::merkle_safe::MerkleProof, circuits::merkle_circuit::MerkleTreeCircuit, }; use plonky2::plonk::circuit_data::{CircuitConfig, CircuitData}; use plonky2::plonk::config::{AlgebraicHasher, GenericConfig, Hasher, PoseidonGoldilocksConfig}; use plonky2::iop::witness::PartialWitness; use plonky2::hash::poseidon::PoseidonHash; use plonky2::field::extension::Extendable; use plonky2::hash::hash_types::RichField; use plonky2_poseidon2::poseidon2_hash::poseidon2::Poseidon2; use std::marker::PhantomData; use plonky2::plonk::circuit_builder::CircuitBuilder; use codex_plonky2_circuits::circuits::prove_single_cell::SlotTreeCircuit; macro_rules! pretty_print { ($($arg:tt)*) => { print!("\x1b[0;36mINFO ===========>\x1b[0m "); println!($($arg)*); } } // Hash function used type HF = PoseidonHash; fn prepare_data< F: RichField + Extendable + Poseidon2, C: GenericConfig, const D: usize, H: Hasher + AlgebraicHasher, >(N: usize) -> Result<( SlotTreeCircuit, Vec, Vec>, )> { // Initialize the slot tree with default data let slot_tree = SlotTreeCircuit::::default(); // Select N leaf indices to prove let leaf_indices: Vec = (0..N).collect(); // Get the Merkle proofs for the selected leaves let proofs: Vec<_> = leaf_indices .iter() .map(|&leaf_index| slot_tree.get_proof(leaf_index)) .collect(); Ok((slot_tree, leaf_indices, proofs)) } fn build_circuit< F: RichField + Extendable + Poseidon2, C: GenericConfig, const D: usize, H: Hasher + AlgebraicHasher, >( slot_tree: &SlotTreeCircuit, leaf_indices: &[usize], proofs: &[MerkleProof], ) -> Result<(CircuitData, PartialWitness)> { // Create the circuit let config = CircuitConfig::standard_recursion_config(); let mut builder = CircuitBuilder::::new(config); // Create a PartialWitness let mut pw = PartialWitness::new(); // For each proof, create targets, add constraints, and assign witnesses for (i, &leaf_index) in leaf_indices.iter().enumerate() { // Build the circuit for each proof let mut targets = SlotTreeCircuit::::prove_single_cell(&mut builder); // Assign witnesses for each proof slot_tree.single_cell_assign_witness( &mut pw, &mut targets, leaf_index, &slot_tree.cell_data[leaf_index], proofs[i].clone(), )?; } // Build the circuit let data = builder.build::(); Ok((data, pw)) } fn single_cell_proof_benchmark(c: &mut Criterion) { let mut group = c.benchmark_group("Single Cell Proof Benchmark"); // Circuit parameters const D: usize = 2; type C = PoseidonGoldilocksConfig; type F = >::F; type H = PoseidonHash; // Prepare the data that will be used in all steps let N = 5; // Number of leaves to prove let (slot_tree, leaf_indices, proofs) = prepare_data::(N).unwrap(); // Benchmark the circuit building group.bench_function("Single Cell Proof Build", |b| { b.iter(|| { build_circuit::(&slot_tree, &leaf_indices, &proofs).unwrap(); }) }); // Build the circuit let (data, pw) = build_circuit::(&slot_tree, &leaf_indices, &proofs).unwrap(); pretty_print!( "Circuit size: 2^{} gates", data.common.degree_bits() ); let start_time = Instant::now(); let proof_with_pis = data.prove(pw.clone()).unwrap(); println!("prove_time = {:?}", start_time.elapsed()); // Benchmark the proving time group.bench_function("Single Cell Proof Prove", |b| { b.iter(|| { let _proof_with_pis = data.prove(pw.clone()).unwrap(); }) }); // Generate the proof let proof_with_pis = data.prove(pw.clone()).unwrap(); let verifier_data = data.verifier_data(); pretty_print!("Proof size: {} bytes", proof_with_pis.to_bytes().len()); // Benchmark the verification time group.bench_function("Single Cell Proof Verify", |b| { b.iter(|| { verifier_data.verify(proof_with_pis.clone()).unwrap(); }) }); group.finish(); } criterion_group!(name = benches; config = Criterion::default().sample_size(10); targets = single_cell_proof_benchmark); criterion_main!(benches);