use criterion::{criterion_group, criterion_main, Criterion}; use anyhow::Result; use codex_plonky2_circuits::{merkle_tree::merkle_safe::MerkleTree, circuits::safe_tree_circuit::MerkleTreeCircuit}; use plonky2::field::types::Field; use plonky2::plonk::circuit_data::{CircuitConfig, CircuitData}; use plonky2::plonk::config::{AlgebraicHasher, GenericConfig, Hasher, PoseidonGoldilocksConfig}; use plonky2::iop::witness::PartialWitness; use plonky2::hash::hash_types::HashOut; 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::merkle_tree::merkle_safe::MerkleProof; macro_rules! pretty_print { ($($arg:tt)*) => { print!("\x1b[0;36mINFO ===========>\x1b[0m "); println!($($arg)*); } } fn prepare_data(N: usize) -> Result<( MerkleTree, Vec>, Vec, Vec>, HashOut, )> where F: RichField + Extendable<2> + Poseidon2, H: Hasher + AlgebraicHasher + Hasher, { // Total number of leaves in the Merkle tree let nleaves = 1u64 << 16; // Generate leaf data let data = (0..nleaves) .map(|i| F::from_canonical_u64(i as u64)) .collect::>(); // Hash the data to obtain leaf hashes let leaves: Vec> = data .iter() .map(|&element| { PoseidonHash::hash_no_pad(&[element]) }) .collect(); let zero_hash = HashOut { elements: [F::ZERO; 4], }; let tree = MerkleTree::::new(&leaves, zero_hash)?; // 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| tree.get_proof(leaf_index)) .collect::, _>>()?; // Expected Merkle root let expected_root = tree.root()?; Ok((tree, leaves, leaf_indices, proofs, expected_root)) } fn build_circuit( tree: &MerkleTree, leaf_indices: &[usize], ) -> Result<(CircuitData, PartialWitness)> where F: RichField + Extendable + Poseidon2, C: GenericConfig, H: Hasher + AlgebraicHasher + Hasher, { // Create the circuit let config = CircuitConfig::standard_recursion_config(); let mut builder = CircuitBuilder::::new(config); // Create a PartialWitness let mut pw = PartialWitness::new(); // Initialize the circuit instance let mut circuit_instance = MerkleTreeCircuit:: { tree: tree.clone(), _phantom: PhantomData, }; // For each proof, create targets, add constraints, and assign witnesses for &leaf_index in leaf_indices.iter() { // Build the circuit for each proof let mut targets = circuit_instance.build_circuit(&mut builder); // Assign witnesses for each proof circuit_instance.assign_witness(&mut pw, &mut targets, leaf_index)?; } // Build the circuit let data = builder.build::(); Ok((data, pw)) } fn merkle_proof_benchmark(c: &mut Criterion) { let mut group = c.benchmark_group("Merkle 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 (tree, _leaves, leaf_indices, _proofs, _expected_root) = prepare_data::(N).unwrap(); // Benchmark the circuit building group.bench_function("Merkle Proof Build", |b| { b.iter(|| { build_circuit::(&tree, &leaf_indices).unwrap(); }) }); // Build the circuit once to get the data for the proving and verifying steps let (data, pw) = build_circuit::(&tree, &leaf_indices).unwrap(); pretty_print!( "circuit size: 2^{} gates", data.common.degree_bits() ); // Benchmark the proving time group.bench_function("Merkle Proof Prove", |b| { b.iter(|| { let _proof_with_pis = data.prove(pw.clone()).unwrap(); }) }); // Generate the proof once for verification let proof_with_pis = data.prove(pw.clone()).unwrap(); let verifier_data = data.verifier_data(); pretty_print!("proof size: {}", proof_with_pis.to_bytes().len()); // Benchmark the verification time group.bench_function("Merkle Proof Verify", |b| { b.iter(|| { verifier_data.verify(proof_with_pis.clone()).unwrap(); }) }); group.finish(); } // criterion_group!(benches, merkle_proof_benchmark); criterion_group!(name = benches; config = Criterion::default().sample_size(10); targets = merkle_proof_benchmark); criterion_main!(benches);