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)
|
- [`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.
|
**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.
|
see [`workflow`](../workflow) for how to use the circuits and run them.
|
||||||
|
|
||||||
## Benchmarks
|
## 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,
|
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 {
|
impl CircuitParams {
|
||||||
/// Creates a new `CircuitParams` struct from environment.
|
/// 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.
|
/// Returns an error if any environment variable is missing or fails to parse.
|
||||||
pub fn from_env() -> Result<Self> {
|
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")?
|
.context("MAX_DEPTH is not set")?
|
||||||
.parse::<usize>()
|
.parse::<usize>()
|
||||||
.context("MAX_DEPTH must be a valid 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")?
|
.context("MAX_LOG2_N_SLOTS is not set")?
|
||||||
.parse::<usize>()
|
.parse::<usize>()
|
||||||
.context("MAX_LOG2_N_SLOTS must be a valid 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")?;
|
.context("N_SAMPLES must be a valid usize")?;
|
||||||
|
|
||||||
Ok(CircuitParams {
|
Ok(CircuitParams {
|
||||||
max_depth,
|
max_depth: MAX_DEPTH,
|
||||||
max_log2_n_slots,
|
max_log2_n_slots: MAX_LOG2_N_SLOTS,
|
||||||
block_tree_depth,
|
block_tree_depth,
|
||||||
n_field_elems_per_cell,
|
n_field_elems_per_cell,
|
||||||
n_samples,
|
n_samples,
|
||||||
|
|
|
@ -14,15 +14,3 @@ plonky2 = { version = "0.2.2" }
|
||||||
plonky2_field = { version = "0.2.2", default-features = false }
|
plonky2_field = { version = "0.2.2", default-features = false }
|
||||||
plonky2_poseidon2 = { path = "../plonky2_poseidon2" }
|
plonky2_poseidon2 = { path = "../plonky2_poseidon2" }
|
||||||
codex-plonky2-circuits = { path = "../codex-plonky2-circuits" }
|
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.
|
see [`workflow`](../workflow) for how to generate proof input.
|
||||||
|
|
||||||
## Benchmarks
|
## 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");
|
assert_eq!(original_circ_input, imported_circ_input, "circuit input are not equal");
|
||||||
|
|
||||||
// cleanup: Remove the generated JSON file
|
// 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.");
|
println!("Test passed: Original and imported circuit input are equal.");
|
||||||
|
|
||||||
|
@ -383,7 +383,7 @@ mod tests {
|
||||||
// reads the json input and verify (non-circuit)
|
// reads the json input and verify (non-circuit)
|
||||||
// NOTE: expects that the json input proof uses the default params
|
// NOTE: expects that the json input proof uses the default params
|
||||||
#[test]
|
#[test]
|
||||||
fn test_read_json_and_verify() -> anyhow::Result<()> {
|
fn test_read_json_and_verify() -> Result<()> {
|
||||||
let params = TestParams::default();
|
let params = TestParams::default();
|
||||||
|
|
||||||
// Import the circuit input from JSON
|
// Import the circuit input from JSON
|
||||||
|
|
|
@ -13,7 +13,7 @@ use crate::sponge::hash_n_with_padding;
|
||||||
// --------- helper functions ---------
|
// --------- helper functions ---------
|
||||||
|
|
||||||
/// Converts an index to a vector of bits (LSB first) no padding.
|
/// 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 that the index can fit within the given bit length.
|
||||||
assert!(
|
assert!(
|
||||||
index < (1 << bit_length),
|
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
|
/// 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);
|
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
|
/// calculate the sampled cell index from entropy, slot root, and counter
|
||||||
/// this is the non-circuit version for testing
|
/// 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,
|
F: RichField + Extendable<D> + Poseidon2,
|
||||||
const D: usize
|
const D: usize
|
||||||
>(entropy: &Vec<F>, slot_root: HashOut<F>, ctr: usize, depth: usize, mask_bits: Vec<bool>) -> Vec<bool> {
|
>(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).
|
/// 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)| {
|
bits.iter().enumerate().fold(0usize, |acc, (i, &bit)| {
|
||||||
if bit {
|
if bit {
|
||||||
acc | (1 << i)
|
acc | (1 << i)
|
||||||
|
|
|
@ -16,6 +16,10 @@ plonky2_poseidon2 = { path = "../plonky2_poseidon2" }
|
||||||
codex-plonky2-circuits = { path = "../codex-plonky2-circuits" }
|
codex-plonky2-circuits = { path = "../codex-plonky2-circuits" }
|
||||||
proof-input = { path = "../proof-input" }
|
proof-input = { path = "../proof-input" }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
criterion = { version = "0.5.1", default-features = false }
|
||||||
|
tynm = { version = "0.1.6", default-features = false }
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "prove_and_verify"
|
name = "prove_and_verify"
|
||||||
path = "src/bin/prove_and_verify.rs"
|
path = "src/bin/prove_and_verify.rs"
|
||||||
|
@ -31,3 +35,11 @@ path = "src/bin/build_circ.rs"
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "prove"
|
name = "prove"
|
||||||
path = "src/bin/prove.rs"
|
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::field::types::Field;
|
||||||
use plonky2::plonk::circuit_data::{CircuitConfig, CircuitData};
|
use plonky2::plonk::circuit_data::{CircuitConfig, CircuitData};
|
||||||
use plonky2::plonk::config::{AlgebraicHasher, GenericConfig, Hasher, PoseidonGoldilocksConfig};
|
use plonky2::plonk::config::{AlgebraicHasher, GenericConfig, Hasher, PoseidonGoldilocksConfig};
|
||||||
use plonky2::iop::witness::PartialWitness;
|
use plonky2::iop::witness::{PartialWitness, WitnessWrite};
|
||||||
use plonky2::hash::hash_types::HashOut;
|
use plonky2::hash::hash_types::{HashOut, NUM_HASH_OUT_ELTS};
|
||||||
use plonky2::hash::poseidon::PoseidonHash;
|
use plonky2::hash::poseidon::PoseidonHash;
|
||||||
use plonky2::field::extension::Extendable;
|
use plonky2::field::extension::Extendable;
|
||||||
use plonky2::hash::hash_types::RichField;
|
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 std::marker::PhantomData;
|
||||||
use plonky2::plonk::circuit_builder::CircuitBuilder;
|
use plonky2::plonk::circuit_builder::CircuitBuilder;
|
||||||
use codex_plonky2_circuits::merkle_tree::merkle_safe::MerkleProof;
|
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 {
|
macro_rules! pretty_print {
|
||||||
($($arg:tt)*) => {
|
($($arg:tt)*) => {
|
||||||
|
@ -22,37 +26,34 @@ macro_rules! pretty_print {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn prepare_data<F, H>(N: usize) -> Result<(
|
fn prepare_data<
|
||||||
MerkleTree<F, H>,
|
F: RichField + Extendable<D> + Poseidon2,
|
||||||
Vec<HashOut<F>>,
|
const D: usize,
|
||||||
Vec<usize>,
|
C: GenericConfig<D, F = F>,
|
||||||
Vec<MerkleProof<F, H>>,
|
H: Hasher<F> + AlgebraicHasher<F>,
|
||||||
|
>(N: usize, max_depth: usize) -> Result<(
|
||||||
|
Vec<MerkleTreeCircuitInput<F, D>>,
|
||||||
HashOut<F>,
|
HashOut<F>,
|
||||||
)>
|
)> {
|
||||||
where
|
// Generate random leaf data
|
||||||
F: RichField + Extendable<2> + Poseidon2,
|
let nleaves = 16; // Number of leaves
|
||||||
H: Hasher<F> + AlgebraicHasher<F> + Hasher<F>,
|
|
||||||
{
|
|
||||||
// Total number of leaves in the Merkle tree
|
|
||||||
let nleaves = 1u64 << 16;
|
|
||||||
|
|
||||||
// Generate leaf data
|
|
||||||
let data = (0..nleaves)
|
let data = (0..nleaves)
|
||||||
.map(|i| F::from_canonical_u64(i as u64))
|
.map(|i| F::from_canonical_u64(i))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
// Hash the data to obtain leaf hashes
|
// Hash the data to obtain leaf hashes
|
||||||
let leaves: Vec<HashOut<F>> = data
|
let leaves: Vec<HashOut<F>> = data
|
||||||
.iter()
|
.iter()
|
||||||
.map(|&element| {
|
.map(|&element| {
|
||||||
|
// Hash each field element to get the leaf hash
|
||||||
PoseidonHash::hash_no_pad(&[element])
|
PoseidonHash::hash_no_pad(&[element])
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
//initialize the Merkle tree
|
||||||
let zero_hash = HashOut {
|
let zero_hash = HashOut {
|
||||||
elements: [F::ZERO; 4],
|
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
|
// Select N leaf indices to prove
|
||||||
let leaf_indices: Vec<usize> = (0..N).collect();
|
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))
|
.map(|&leaf_index| tree.get_proof(leaf_index))
|
||||||
.collect::<Result<Vec<_>, _>>()?;
|
.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
|
// Expected Merkle root
|
||||||
let expected_root = tree.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>(
|
fn build_circuit<
|
||||||
tree: &MerkleTree<F, H>,
|
|
||||||
leaf_indices: &[usize],
|
|
||||||
) -> Result<(CircuitData<F, C, D>, PartialWitness<F>)>
|
|
||||||
where
|
|
||||||
F: RichField + Extendable<D> + Poseidon2,
|
F: RichField + Extendable<D> + Poseidon2,
|
||||||
|
const D: usize,
|
||||||
C: GenericConfig<D, F = F>,
|
C: GenericConfig<D, F = F>,
|
||||||
H: Hasher<F> + AlgebraicHasher<F> + Hasher<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>)>
|
||||||
{
|
{
|
||||||
// Create the circuit
|
// Create the circuit
|
||||||
let config = CircuitConfig::standard_recursion_config();
|
let config = CircuitConfig::standard_recursion_config();
|
||||||
|
@ -85,19 +108,20 @@ fn build_circuit<F, C, const D: usize, H>(
|
||||||
// Create a PartialWitness
|
// Create a PartialWitness
|
||||||
let mut pw = PartialWitness::new();
|
let mut pw = PartialWitness::new();
|
||||||
|
|
||||||
// Initialize the circuit instance
|
for i in 0..circ_inputs.len() {
|
||||||
let mut circuit_instance = MerkleTreeCircuit::<F, C, D, H> {
|
let (mut targets, reconstructed_root_target) = merkle_circuit::build_circuit(&mut builder, max_depth);
|
||||||
tree: tree.clone(),
|
|
||||||
_phantom: PhantomData,
|
|
||||||
};
|
|
||||||
|
|
||||||
// For each proof, create targets, add constraints, and assign witnesses
|
// expected Merkle root
|
||||||
for &leaf_index in leaf_indices.iter() {
|
let expected_root_target = builder.add_virtual_hash();
|
||||||
// Build the circuit for each proof
|
|
||||||
let (mut targets, _root) = circuit_instance.build_circuit(&mut builder);
|
|
||||||
|
|
||||||
// Assign witnesses for each proof
|
// check equality with expected root
|
||||||
circuit_instance.assign_witness(&mut pw, &mut targets, leaf_index)?;
|
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
|
// Build the circuit
|
||||||
|
@ -106,28 +130,28 @@ fn build_circuit<F, C, const D: usize, H>(
|
||||||
Ok((data, pw))
|
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");
|
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
|
// Prepare the data that will be used in all steps
|
||||||
let N = 5; // Number of leaves to prove
|
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
|
// Benchmark the circuit building
|
||||||
group.bench_function("Merkle Proof Build", |b| {
|
group.bench_function("Merkle Proof Build", |b| {
|
||||||
b.iter(|| {
|
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
|
// 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!(
|
pretty_print!(
|
||||||
"circuit size: 2^{} gates",
|
"circuit size: 2^{} gates",
|
||||||
|
@ -157,8 +181,18 @@ fn merkle_proof_benchmark(c: &mut Criterion) {
|
||||||
group.finish();
|
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!(benches, merkle_proof_benchmark);
|
||||||
criterion_group!(name = benches;
|
criterion_group!(name = benches;
|
||||||
config = Criterion::default().sample_size(10);
|
config = Criterion::default().sample_size(10);
|
||||||
targets = merkle_proof_benchmark);
|
targets = run_bench);
|
||||||
criterion_main!(benches);
|
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