fix benchmarks
This commit is contained in:
parent
e843d68a4d
commit
31d8cb1447
|
@ -10,4 +10,12 @@ Repository organization
|
|||
|
||||
- [`codex-plonky2-circuits`](./codex-plonky2-circuits) contains the codex proof circuits tailored specifically for plonky2. These circuits have the functionality as those in [**here**](https://github.com/codex-storage/codex-storage-proofs-circuits)
|
||||
|
||||
- [`proof-input`](./proof-input) contains the lib code to generate proof input for the circuit from fake dataset.
|
||||
|
||||
- [`workflow`](./workflow) contains the scripts and example code to generate input, run the circuits, generate a proof, and verify the proof.
|
||||
|
||||
Documentation
|
||||
-----------------
|
||||
To be added soon.
|
||||
|
||||
**WARNING**: This repository contains work-in-progress prototypes, and has not received careful code review. It is NOT ready for production use.
|
||||
|
|
|
@ -26,4 +26,4 @@ writeup coming soon...
|
|||
see [`workflow`](../workflow) for how to use the circuits and run them.
|
||||
|
||||
## Benchmarks
|
||||
see [`BENCHMARKS.md`](../proof-input/BENCHMARKS.md)
|
||||
see [`BENCHMARKS.md`](../workflow/BENCHMARKS.md)
|
||||
|
|
|
@ -21,6 +21,26 @@ pub struct CircuitParams{
|
|||
pub n_samples: usize,
|
||||
}
|
||||
|
||||
// hardcoded default constants
|
||||
const DEFAULT_MAX_DEPTH:usize = 32;
|
||||
const DEFAULT_MAX_LOG2_N_SLOTS:usize = 8;
|
||||
const DEFAULT_BLOCK_TREE_DEPTH:usize = 5;
|
||||
const DEFAULT_N_FIELD_ELEMS_PER_CELL:usize = 272;
|
||||
const DEFAULT_N_SAMPLES:usize = 5;
|
||||
|
||||
/// Implement the Default trait for Params using the hardcoded constants
|
||||
impl Default for CircuitParams {
|
||||
fn default() -> Self {
|
||||
Self{
|
||||
max_depth: DEFAULT_MAX_DEPTH,
|
||||
max_log2_n_slots: DEFAULT_MAX_LOG2_N_SLOTS,
|
||||
block_tree_depth: DEFAULT_BLOCK_TREE_DEPTH,
|
||||
n_field_elems_per_cell: DEFAULT_N_FIELD_ELEMS_PER_CELL,
|
||||
n_samples: DEFAULT_N_SAMPLES,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CircuitParams {
|
||||
/// Creates a new `CircuitParams` struct from environment.
|
||||
///
|
||||
|
@ -32,12 +52,12 @@ impl CircuitParams {
|
|||
///
|
||||
/// Returns an error if any environment variable is missing or fails to parse.
|
||||
pub fn from_env() -> Result<Self> {
|
||||
let max_depth = env::var("MAX_DEPTH")
|
||||
let MAX_DEPTH = env::var("MAX_DEPTH")
|
||||
.context("MAX_DEPTH is not set")?
|
||||
.parse::<usize>()
|
||||
.context("MAX_DEPTH must be a valid usize")?;
|
||||
|
||||
let max_log2_n_slots = env::var("MAX_LOG2_N_SLOTS")
|
||||
let MAX_LOG2_N_SLOTS = env::var("MAX_LOG2_N_SLOTS")
|
||||
.context("MAX_LOG2_N_SLOTS is not set")?
|
||||
.parse::<usize>()
|
||||
.context("MAX_LOG2_N_SLOTS must be a valid usize")?;
|
||||
|
@ -58,8 +78,8 @@ impl CircuitParams {
|
|||
.context("N_SAMPLES must be a valid usize")?;
|
||||
|
||||
Ok(CircuitParams {
|
||||
max_depth,
|
||||
max_log2_n_slots,
|
||||
max_depth: MAX_DEPTH,
|
||||
max_log2_n_slots: MAX_LOG2_N_SLOTS,
|
||||
block_tree_depth,
|
||||
n_field_elems_per_cell,
|
||||
n_samples,
|
||||
|
|
|
@ -14,15 +14,3 @@ plonky2 = { version = "0.2.2" }
|
|||
plonky2_field = { version = "0.2.2", default-features = false }
|
||||
plonky2_poseidon2 = { path = "../plonky2_poseidon2" }
|
||||
codex-plonky2-circuits = { path = "../codex-plonky2-circuits" }
|
||||
|
||||
[[bench]]
|
||||
name = "safe_circuit"
|
||||
harness = false
|
||||
|
||||
[[bench]]
|
||||
name = "prove_cells"
|
||||
harness = false
|
||||
|
||||
[[bench]]
|
||||
name = "sample_cells"
|
||||
harness = false
|
|
@ -20,4 +20,4 @@ the [`plonky2 codex proof circuits`](../codex-plonky2-circuits). Currently only
|
|||
see [`workflow`](../workflow) for how to generate proof input.
|
||||
|
||||
## Benchmarks
|
||||
see [`BENCHMARKS.md`](../proof-input/BENCHMARKS.md)
|
||||
see [`BENCHMARKS.md`](../workflow/BENCHMARKS.md)
|
||||
|
|
|
@ -1,152 +0,0 @@
|
|||
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<D> + Poseidon2,
|
||||
C: GenericConfig<D, F = F>,
|
||||
const D: usize,
|
||||
H: Hasher<F> + AlgebraicHasher<F>,
|
||||
>(N: usize) -> Result<(
|
||||
SlotTreeCircuit<F, C, D, H>,
|
||||
Vec<usize>,
|
||||
Vec<MerkleProof<F, H>>,
|
||||
)> {
|
||||
// Initialize the slot tree with default data
|
||||
let slot_tree = SlotTreeCircuit::<F, C,D, H>::default();
|
||||
|
||||
// Select N leaf indices to prove
|
||||
let leaf_indices: Vec<usize> = (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<D> + Poseidon2,
|
||||
C: GenericConfig<D, F = F>,
|
||||
const D: usize,
|
||||
H: Hasher<F> + AlgebraicHasher<F>,
|
||||
>(
|
||||
slot_tree: &SlotTreeCircuit<F, C, D, H>,
|
||||
leaf_indices: &[usize],
|
||||
proofs: &[MerkleProof<F, H>],
|
||||
) -> Result<(CircuitData<F, C, D>, PartialWitness<F>)>
|
||||
{
|
||||
// Create the circuit
|
||||
let config = CircuitConfig::standard_recursion_config();
|
||||
let mut builder = CircuitBuilder::<F, D>::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::<F,C,D,H>::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::<C>();
|
||||
|
||||
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 = <C as GenericConfig<D>>::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::<F, C, D, H>(N).unwrap();
|
||||
|
||||
// Benchmark the circuit building
|
||||
group.bench_function("Single Cell Proof Build", |b| {
|
||||
b.iter(|| {
|
||||
build_circuit::<F, C, D, H>(&slot_tree, &leaf_indices, &proofs).unwrap();
|
||||
})
|
||||
});
|
||||
|
||||
// Build the circuit
|
||||
let (data, pw) = build_circuit::<F, C, D, H>(&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);
|
|
@ -1,129 +0,0 @@
|
|||
use criterion::{criterion_group, criterion_main, Criterion};
|
||||
use anyhow::Result;
|
||||
use std::time::{Duration, Instant};
|
||||
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 plonky2::plonk::circuit_builder::CircuitBuilder;
|
||||
use codex_plonky2_circuits::circuits::params::TESTING_SLOT_INDEX;
|
||||
use codex_plonky2_circuits::circuits::sample_cells::SampleCircuit;
|
||||
|
||||
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<D> + Poseidon2,
|
||||
C: GenericConfig<D, F = F>,
|
||||
const D: usize,
|
||||
H: Hasher<F> + AlgebraicHasher<F>,
|
||||
>() -> Result<(
|
||||
SampleCircuit<F, C, D, H>,
|
||||
usize,
|
||||
usize,
|
||||
)> {
|
||||
// Initialize the dataset tree with testing data
|
||||
let mut dataset_t = SampleCircuit::<F,C,D,H>::new_for_testing();
|
||||
|
||||
let slot_index = TESTING_SLOT_INDEX;
|
||||
let entropy = 123;
|
||||
|
||||
Ok((dataset_t, slot_index, entropy))
|
||||
}
|
||||
|
||||
fn build_circuit<
|
||||
F: RichField + Extendable<D> + Poseidon2,
|
||||
C: GenericConfig<D, F = F>,
|
||||
const D: usize,
|
||||
H: Hasher<F> + AlgebraicHasher<F>,
|
||||
>(
|
||||
dataset_tree: &mut SampleCircuit<F, C, D, H>,
|
||||
slot_index: usize,
|
||||
entropy: usize,
|
||||
// proofs: &[MerkleProof<F, H>],
|
||||
) -> Result<(CircuitData<F, C, D>, PartialWitness<F>)>
|
||||
{
|
||||
// Create the circuit
|
||||
let config = CircuitConfig::standard_recursion_config();
|
||||
let mut builder = CircuitBuilder::<F, D>::new(config);
|
||||
|
||||
let mut targets = dataset_tree.sample_slot_circuit(&mut builder);
|
||||
|
||||
// Create a PartialWitness
|
||||
let mut pw = PartialWitness::new();
|
||||
dataset_tree.sample_slot_assign_witness(&mut pw, &mut targets,slot_index,entropy);
|
||||
|
||||
// Build the circuit
|
||||
let data = builder.build::<C>();
|
||||
|
||||
Ok((data, pw))
|
||||
}
|
||||
|
||||
fn sampling_benchmark(c: &mut Criterion) {
|
||||
let mut group = c.benchmark_group("Sampling Benchmark");
|
||||
|
||||
// Circuit parameters
|
||||
const D: usize = 2;
|
||||
type C = PoseidonGoldilocksConfig;
|
||||
type F = <C as GenericConfig<D>>::F;
|
||||
type H = PoseidonHash;
|
||||
|
||||
// Prepare the data that will be used in all steps
|
||||
let (mut dataset_tree, slot_index, entropy) = prepare_data::<F, C, D, H>().unwrap();
|
||||
|
||||
// Benchmark the circuit building
|
||||
group.bench_function("Single Cell Proof Build", |b| {
|
||||
b.iter(|| {
|
||||
build_circuit::<F, C, D, H>(&mut dataset_tree, slot_index, entropy).unwrap();
|
||||
})
|
||||
});
|
||||
|
||||
// Build the circuit
|
||||
let (data, pw) = build_circuit::<F, C, D, H>(&mut dataset_tree, slot_index, entropy).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 = sampling_benchmark);
|
||||
criterion_main!(benches);
|
File diff suppressed because it is too large
Load Diff
|
@ -326,7 +326,7 @@ mod tests {
|
|||
assert_eq!(original_circ_input, imported_circ_input, "circuit input are not equal");
|
||||
|
||||
// cleanup: Remove the generated JSON file
|
||||
fs::remove_file("input.json")?;
|
||||
// fs::remove_file("input.json")?;
|
||||
|
||||
println!("Test passed: Original and imported circuit input are equal.");
|
||||
|
||||
|
@ -383,7 +383,7 @@ mod tests {
|
|||
// reads the json input and verify (non-circuit)
|
||||
// NOTE: expects that the json input proof uses the default params
|
||||
#[test]
|
||||
fn test_read_json_and_verify() -> anyhow::Result<()> {
|
||||
fn test_read_json_and_verify() -> Result<()> {
|
||||
let params = TestParams::default();
|
||||
|
||||
// Import the circuit input from JSON
|
||||
|
|
|
@ -13,7 +13,7 @@ use crate::sponge::hash_n_with_padding;
|
|||
// --------- helper functions ---------
|
||||
|
||||
/// Converts an index to a vector of bits (LSB first) no padding.
|
||||
pub(crate) fn usize_to_bits_le(index: usize, bit_length: usize) -> Vec<bool> {
|
||||
pub fn usize_to_bits_le(index: usize, bit_length: usize) -> Vec<bool> {
|
||||
// Assert that the index can fit within the given bit length.
|
||||
assert!(
|
||||
index < (1 << bit_length),
|
||||
|
@ -32,7 +32,7 @@ pub(crate) fn usize_to_bits_le(index: usize, bit_length: usize) -> Vec<bool> {
|
|||
}
|
||||
|
||||
/// returns the first bit_length bits of index
|
||||
pub(crate) fn low_bits(index: usize, bit_length: usize) -> Vec<bool> {
|
||||
pub fn low_bits(index: usize, bit_length: usize) -> Vec<bool> {
|
||||
|
||||
let mut bits = Vec::with_capacity(bit_length);
|
||||
|
||||
|
@ -46,7 +46,7 @@ pub(crate) fn low_bits(index: usize, bit_length: usize) -> Vec<bool> {
|
|||
|
||||
/// calculate the sampled cell index from entropy, slot root, and counter
|
||||
/// this is the non-circuit version for testing
|
||||
pub(crate) fn calculate_cell_index_bits<
|
||||
pub fn calculate_cell_index_bits<
|
||||
F: RichField + Extendable<D> + Poseidon2,
|
||||
const D: usize
|
||||
>(entropy: &Vec<F>, slot_root: HashOut<F>, ctr: usize, depth: usize, mask_bits: Vec<bool>) -> Vec<bool> {
|
||||
|
@ -72,7 +72,7 @@ pub(crate) fn calculate_cell_index_bits<
|
|||
}
|
||||
|
||||
/// Converts a vector of bits (LSB first) into an index (usize).
|
||||
pub(crate) fn bits_le_padded_to_usize(bits: &[bool]) -> usize {
|
||||
pub fn bits_le_padded_to_usize(bits: &[bool]) -> usize {
|
||||
bits.iter().enumerate().fold(0usize, |acc, (i, &bit)| {
|
||||
if bit {
|
||||
acc | (1 << i)
|
||||
|
|
|
@ -16,6 +16,10 @@ plonky2_poseidon2 = { path = "../plonky2_poseidon2" }
|
|||
codex-plonky2-circuits = { path = "../codex-plonky2-circuits" }
|
||||
proof-input = { path = "../proof-input" }
|
||||
|
||||
[dev-dependencies]
|
||||
criterion = { version = "0.5.1", default-features = false }
|
||||
tynm = { version = "0.1.6", default-features = false }
|
||||
|
||||
[[bin]]
|
||||
name = "prove_and_verify"
|
||||
path = "src/bin/prove_and_verify.rs"
|
||||
|
@ -31,3 +35,11 @@ path = "src/bin/build_circ.rs"
|
|||
[[bin]]
|
||||
name = "prove"
|
||||
path = "src/bin/prove.rs"
|
||||
|
||||
[[bench]]
|
||||
name = "safe_circuit"
|
||||
harness = false
|
||||
|
||||
[[bench]]
|
||||
name = "sample_cells"
|
||||
harness = false
|
|
@ -5,15 +5,19 @@ use codex_plonky2_circuits::{merkle_tree::merkle_safe::MerkleTree, circuits::mer
|
|||
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::iop::witness::{PartialWitness, WitnessWrite};
|
||||
use plonky2::hash::hash_types::{HashOut, NUM_HASH_OUT_ELTS};
|
||||
use plonky2::hash::poseidon::PoseidonHash;
|
||||
use plonky2::field::extension::Extendable;
|
||||
use plonky2::hash::hash_types::RichField;
|
||||
use plonky2_poseidon2::poseidon2_hash::poseidon2::Poseidon2;
|
||||
use plonky2_poseidon2::poseidon2_hash::poseidon2::{Poseidon2, Poseidon2Hash};
|
||||
use std::marker::PhantomData;
|
||||
use plonky2::plonk::circuit_builder::CircuitBuilder;
|
||||
use codex_plonky2_circuits::merkle_tree::merkle_safe::MerkleProof;
|
||||
use plonky2_field::goldilocks_field::GoldilocksField;
|
||||
use proof_input::tests::merkle_circuit;
|
||||
use proof_input::tests::merkle_circuit::{assign_witness, MerkleTreeCircuitInput};
|
||||
use proof_input::utils::usize_to_bits_le;
|
||||
|
||||
macro_rules! pretty_print {
|
||||
($($arg:tt)*) => {
|
||||
|
@ -22,37 +26,34 @@ macro_rules! pretty_print {
|
|||
}
|
||||
}
|
||||
|
||||
fn prepare_data<F, H>(N: usize) -> Result<(
|
||||
MerkleTree<F, H>,
|
||||
Vec<HashOut<F>>,
|
||||
Vec<usize>,
|
||||
Vec<MerkleProof<F, H>>,
|
||||
fn prepare_data<
|
||||
F: RichField + Extendable<D> + Poseidon2,
|
||||
const D: usize,
|
||||
C: GenericConfig<D, F = F>,
|
||||
H: Hasher<F> + AlgebraicHasher<F>,
|
||||
>(N: usize, max_depth: usize) -> Result<(
|
||||
Vec<MerkleTreeCircuitInput<F, D>>,
|
||||
HashOut<F>,
|
||||
)>
|
||||
where
|
||||
F: RichField + Extendable<2> + Poseidon2,
|
||||
H: Hasher<F> + AlgebraicHasher<F> + Hasher<F>,
|
||||
{
|
||||
// Total number of leaves in the Merkle tree
|
||||
let nleaves = 1u64 << 16;
|
||||
|
||||
// Generate leaf data
|
||||
)> {
|
||||
// Generate random leaf data
|
||||
let nleaves = 16; // Number of leaves
|
||||
let data = (0..nleaves)
|
||||
.map(|i| F::from_canonical_u64(i as u64))
|
||||
.map(|i| F::from_canonical_u64(i))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// Hash the data to obtain leaf hashes
|
||||
let leaves: Vec<HashOut<F>> = data
|
||||
.iter()
|
||||
.map(|&element| {
|
||||
// Hash each field element to get the leaf hash
|
||||
PoseidonHash::hash_no_pad(&[element])
|
||||
})
|
||||
.collect();
|
||||
|
||||
//initialize the Merkle tree
|
||||
let zero_hash = HashOut {
|
||||
elements: [F::ZERO; 4],
|
||||
};
|
||||
let tree = MerkleTree::<F, H>::new(&leaves, zero_hash)?;
|
||||
let tree = MerkleTree::<F, D>::new(&leaves, zero_hash)?;
|
||||
|
||||
// Select N leaf indices to prove
|
||||
let leaf_indices: Vec<usize> = (0..N).collect();
|
||||
|
@ -63,20 +64,42 @@ fn prepare_data<F, H>(N: usize) -> Result<(
|
|||
.map(|&leaf_index| tree.get_proof(leaf_index))
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
let mut circ_inputs = vec![];
|
||||
|
||||
for i in 0..N{
|
||||
let path_bits = usize_to_bits_le(leaf_indices[i], max_depth);
|
||||
let last_index = (nleaves - 1) as usize;
|
||||
let last_bits = usize_to_bits_le(last_index, max_depth);
|
||||
let mask_bits = usize_to_bits_le(last_index, max_depth+1);
|
||||
|
||||
// circuit input
|
||||
let circuit_input = MerkleTreeCircuitInput::<F, D>{
|
||||
leaf: tree.layers[0][leaf_indices[i]],
|
||||
path_bits,
|
||||
last_bits,
|
||||
mask_bits,
|
||||
merkle_path: proofs[i].path.clone(),
|
||||
};
|
||||
|
||||
circ_inputs.push(circuit_input);
|
||||
}
|
||||
|
||||
// Expected Merkle root
|
||||
let expected_root = tree.root()?;
|
||||
|
||||
Ok((tree, leaves, leaf_indices, proofs, expected_root))
|
||||
Ok((circ_inputs, expected_root))
|
||||
}
|
||||
|
||||
fn build_circuit<F, C, const D: usize, H>(
|
||||
tree: &MerkleTree<F, H>,
|
||||
leaf_indices: &[usize],
|
||||
fn build_circuit<
|
||||
F: RichField + Extendable<D> + Poseidon2,
|
||||
const D: usize,
|
||||
C: GenericConfig<D, F = F>,
|
||||
H: Hasher<F> + AlgebraicHasher<F>,
|
||||
>(
|
||||
circ_inputs: Vec<MerkleTreeCircuitInput<F, D>>,
|
||||
expected_root: HashOut<F>,
|
||||
max_depth: usize,
|
||||
) -> Result<(CircuitData<F, C, D>, PartialWitness<F>)>
|
||||
where
|
||||
F: RichField + Extendable<D> + Poseidon2,
|
||||
C: GenericConfig<D, F = F>,
|
||||
H: Hasher<F> + AlgebraicHasher<F> + Hasher<F>,
|
||||
{
|
||||
// Create the circuit
|
||||
let config = CircuitConfig::standard_recursion_config();
|
||||
|
@ -85,19 +108,20 @@ fn build_circuit<F, C, const D: usize, H>(
|
|||
// Create a PartialWitness
|
||||
let mut pw = PartialWitness::new();
|
||||
|
||||
// Initialize the circuit instance
|
||||
let mut circuit_instance = MerkleTreeCircuit::<F, C, D, H> {
|
||||
tree: tree.clone(),
|
||||
_phantom: PhantomData,
|
||||
};
|
||||
for i in 0..circ_inputs.len() {
|
||||
let (mut targets, reconstructed_root_target) = merkle_circuit::build_circuit(&mut builder, max_depth);
|
||||
|
||||
// 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, _root) = circuit_instance.build_circuit(&mut builder);
|
||||
// expected Merkle root
|
||||
let expected_root_target = builder.add_virtual_hash();
|
||||
|
||||
// Assign witnesses for each proof
|
||||
circuit_instance.assign_witness(&mut pw, &mut targets, leaf_index)?;
|
||||
// check equality with expected root
|
||||
for i in 0..NUM_HASH_OUT_ELTS {
|
||||
builder.connect(expected_root_target.elements[i], reconstructed_root_target.elements[i]);
|
||||
}
|
||||
|
||||
//assign input
|
||||
assign_witness(&mut pw, &mut targets, circ_inputs[i].clone())?;
|
||||
pw.set_hash_target(expected_root_target, expected_root);
|
||||
}
|
||||
|
||||
// Build the circuit
|
||||
|
@ -106,28 +130,28 @@ fn build_circuit<F, C, const D: usize, H>(
|
|||
Ok((data, pw))
|
||||
}
|
||||
|
||||
fn merkle_proof_benchmark(c: &mut Criterion) {
|
||||
fn merkle_proof_benchmark<
|
||||
F: RichField + Extendable<D> + Poseidon2,
|
||||
const D: usize,
|
||||
C: GenericConfig<D, F = F>,
|
||||
H: Hasher<F> + AlgebraicHasher<F>,
|
||||
>(c: &mut Criterion) {
|
||||
let mut group = c.benchmark_group("Merkle Proof Benchmark");
|
||||
|
||||
// Circuit parameters
|
||||
const D: usize = 2;
|
||||
type C = PoseidonGoldilocksConfig;
|
||||
type F = <C as GenericConfig<D>>::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::<F, H>(N).unwrap();
|
||||
let max_depth = 4;
|
||||
let (circ_input, expected_root) = prepare_data::<F, D,C,H>(N, max_depth).unwrap();
|
||||
|
||||
// Benchmark the circuit building
|
||||
group.bench_function("Merkle Proof Build", |b| {
|
||||
b.iter(|| {
|
||||
build_circuit::<F, C, D, H>(&tree, &leaf_indices).unwrap();
|
||||
build_circuit::<F, D, C, H>(circ_input.clone(), expected_root.clone(), max_depth).unwrap();
|
||||
})
|
||||
});
|
||||
|
||||
// Build the circuit once to get the data for the proving and verifying steps
|
||||
let (data, pw) = build_circuit::<F, C, D, H>(&tree, &leaf_indices).unwrap();
|
||||
let (data, pw) = build_circuit::<F, D, C, H>(circ_input.clone(), expected_root.clone(), max_depth).unwrap();
|
||||
|
||||
pretty_print!(
|
||||
"circuit size: 2^{} gates",
|
||||
|
@ -157,8 +181,18 @@ fn merkle_proof_benchmark(c: &mut Criterion) {
|
|||
group.finish();
|
||||
}
|
||||
|
||||
fn run_bench(c: &mut Criterion){
|
||||
// Circuit types
|
||||
const D: usize = 2;
|
||||
type C = PoseidonGoldilocksConfig;
|
||||
type F = <C as GenericConfig<D>>::F;
|
||||
type H = Poseidon2Hash;
|
||||
|
||||
merkle_proof_benchmark::<F,D,C,H>(c);
|
||||
}
|
||||
|
||||
// criterion_group!(benches, merkle_proof_benchmark);
|
||||
criterion_group!(name = benches;
|
||||
config = Criterion::default().sample_size(10);
|
||||
targets = merkle_proof_benchmark);
|
||||
targets = run_bench);
|
||||
criterion_main!(benches);
|
|
@ -0,0 +1,87 @@
|
|||
use anyhow::Result;
|
||||
use criterion::{criterion_group, criterion_main, Criterion};
|
||||
use plonky2::iop::witness::PartialWitness;
|
||||
use plonky2::plonk::circuit_builder::CircuitBuilder;
|
||||
use plonky2::plonk::circuit_data::CircuitConfig;
|
||||
use plonky2::plonk::config::GenericConfig;
|
||||
|
||||
use proof_input::json::import_circ_input_from_json;
|
||||
use codex_plonky2_circuits::circuits::sample_cells::{SampleCircuit, SampleCircuitInput};
|
||||
use codex_plonky2_circuits::circuits::params::CircuitParams;
|
||||
use proof_input::params::{D, C, F, Params};
|
||||
|
||||
/// Benchmark for building, proving, and verifying the Plonky2 circuit.
|
||||
fn bench_prove_verify(c: &mut Criterion) {
|
||||
// get default parameters
|
||||
let circuit_params = CircuitParams::default();
|
||||
|
||||
// Import the circuit input from a JSON file
|
||||
let circ_input: SampleCircuitInput<F, D> = import_circ_input_from_json("input.json").expect("Failed to import circuit input from JSON");
|
||||
println!("Witness imported from input.json");
|
||||
|
||||
// Create the circuit configuration
|
||||
let config = CircuitConfig::standard_recursion_config();
|
||||
let mut builder = CircuitBuilder::<F, D>::new(config);
|
||||
|
||||
// Initialize the SampleCircuit with the parameters
|
||||
let circ = SampleCircuit::new(circuit_params.clone());
|
||||
let mut targets = circ.sample_slot_circuit(&mut builder);
|
||||
|
||||
// Create a PartialWitness and assign the circuit input
|
||||
let mut pw = PartialWitness::new();
|
||||
circ.sample_slot_assign_witness(&mut pw, &mut targets, circ_input.clone());
|
||||
|
||||
// Benchmark Group: Separate benchmarks for building, proving, and verifying
|
||||
let mut group = c.benchmark_group("Prove and Verify");
|
||||
|
||||
// Benchmark the Circuit Building Phase
|
||||
group.bench_function("Build Circuit", |b| {
|
||||
b.iter(|| {
|
||||
let config = CircuitConfig::standard_recursion_config();
|
||||
let mut local_builder = CircuitBuilder::<F, D>::new(config);
|
||||
let local_circ = SampleCircuit::new(circuit_params.clone());
|
||||
let mut local_targets = local_circ.sample_slot_circuit(&mut local_builder);
|
||||
let mut local_pw = PartialWitness::new();
|
||||
local_circ.sample_slot_assign_witness(&mut local_pw, &mut local_targets, circ_input.clone());
|
||||
let _data = local_builder.build::<C>();
|
||||
})
|
||||
});
|
||||
|
||||
// Build the circuit once for proving and verifying benchmarks
|
||||
let build_start = std::time::Instant::now();
|
||||
let data = builder.build::<C>();
|
||||
let build_duration = build_start.elapsed();
|
||||
println!("Build time: {:?}", build_duration);
|
||||
println!("Circuit size (degree bits): {:?}", data.common.degree_bits());
|
||||
|
||||
// Benchmark the Proving Phase
|
||||
group.bench_function("Prove Circuit", |b| {
|
||||
b.iter(|| {
|
||||
let local_pw = pw.clone();
|
||||
data.prove(local_pw).expect("Failed to prove circuit")
|
||||
})
|
||||
});
|
||||
|
||||
// Generate the proof once for verification benchmarking
|
||||
let proof_with_pis = data.prove(pw.clone()).expect("Failed to prove circuit");
|
||||
let verifier_data = data.verifier_data();
|
||||
|
||||
println!("Proof size: {} bytes", proof_with_pis.to_bytes().len());
|
||||
|
||||
// Benchmark the Verifying Phase
|
||||
group.bench_function("Verify Proof", |b| {
|
||||
b.iter(|| {
|
||||
verifier_data.verify(proof_with_pis.clone()).expect("Failed to verify proof");
|
||||
})
|
||||
});
|
||||
|
||||
group.finish();
|
||||
}
|
||||
|
||||
/// Criterion benchmark group
|
||||
criterion_group!{
|
||||
name = prove_verify_benches;
|
||||
config = Criterion::default().sample_size(10);
|
||||
targets = bench_prove_verify
|
||||
}
|
||||
criterion_main!(prove_verify_benches);
|
Loading…
Reference in New Issue