use kontor_crypto::{
api::{self, Challenge, FieldElement, PorSystem},
build_tree,
commitment::{domain_tags, poseidon_hash_tagged},
config, get_padded_proof_for_leaf, params, FileLedger,
};
use rand::{rngs::StdRng, RngCore, SeedableRng};
use std::collections::BTreeMap;
use std::time::{Duration, Instant};
fn main() {
println!("\nKontor PoR Performance Benchmarks");
println!("================================\n");
bench_microbenchmarks();
bench_setup();
bench_proving();
bench_verifying();
bench_e2e();
println!("\n✅ All benchmarks completed\n");
}
fn time_operation<F, R>(name: &str, mut f: F) -> (Duration, R)
where
F: FnMut() -> R,
{
let start = Instant::now();
let result = f();
let duration = start.elapsed();
println!(
" {:<40} {:>12.3} ms",
format!("{}:", name),
duration.as_secs_f64() * 1000.0
);
(duration, result)
}
fn time_operation_avg<F>(name: &str, iterations: usize, mut f: F) -> Duration
where
F: FnMut(),
{
let start = Instant::now();
for _ in 0..iterations {
f();
}
let total = start.elapsed();
let avg = total / iterations as u32;
println!(
" {:<40} {:>12.3} ms (avg of {} runs)",
format!("{}:", name),
avg.as_secs_f64() * 1000.0,
iterations
);
avg
}
fn bench_microbenchmarks() {
println!("Microbenchmarks");
println!("---------------");
let a = FieldElement::from(config::TEST_RANDOM_SEED);
let b = FieldElement::from(123u64);
time_operation_avg(
"Poseidon hash (domain-separated)",
config::BENCHMARK_HASH_ITERATIONS,
|| {
let _ = poseidon_hash_tagged(domain_tags::node(), a, b);
},
);
let num_leaves = config::BENCHMARK_TREE_LEAVES;
let data: Vec<Vec<u8>> = (0..num_leaves)
.map(|i| format!("leaf_{}", i).into_bytes())
.collect();
let (_, (tree, _root)) =
time_operation("Build tree (256 leaves)", || build_tree(&data).unwrap());
time_operation_avg(
"Generate Merkle proof",
config::BENCHMARK_PROOF_ITERATIONS,
|| {
let _ = get_padded_proof_for_leaf(
&tree,
config::BENCHMARK_PROOF_LEAF_INDEX,
config::BENCHMARK_TREE_DEPTH,
);
},
);
println!();
}
fn bench_setup() {
println!("Setup API");
println!("---------");
let file_size_kb = config::BENCHMARK_FILE_SIZE_MEDIUM;
let data = generate_test_data(file_size_kb * 1024);
time_operation(&format!("Prepare {}KB file", file_size_kb), || {
api::prepare_file(&data, "test_file.dat").unwrap()
});
println!();
}
fn bench_proving() {
println!("Proving API");
println!("----------------------------");
{
let (_params, prepared_file, challenge) =
setup_proof_scenario(config::BENCHMARK_FILE_SIZE_SMALL, 2);
let mut files = BTreeMap::new();
files.insert(challenge.file_metadata.file_id.clone(), &prepared_file);
let mut ledger = FileLedger::new();
ledger
.add_file(
challenge.file_metadata.file_id.clone(),
challenge.file_metadata.root,
api::tree_depth_from_metadata(&challenge.file_metadata),
)
.unwrap();
let challenges = vec![challenge];
let system = PorSystem::new(&ledger);
time_operation("Prove 16KB/2 challenges", || {
system.prove(vec![&prepared_file], &challenges).unwrap()
});
}
{
let (_params, prepared_file, challenge) =
setup_proof_scenario(config::BENCHMARK_FILE_SIZE_MEDIUM, 3);
let mut files = BTreeMap::new();
files.insert(challenge.file_metadata.file_id.clone(), &prepared_file);
let mut ledger = FileLedger::new();
ledger
.add_file(
challenge.file_metadata.file_id.clone(),
challenge.file_metadata.root,
api::tree_depth_from_metadata(&challenge.file_metadata),
)
.unwrap();
let challenges = vec![challenge];
let system = PorSystem::new(&ledger);
time_operation("Prove 32KB/3 challenges", || {
system.prove(vec![&prepared_file], &challenges).unwrap()
});
}
{
let (_params, prepared_file, challenge) =
setup_proof_scenario(config::BENCHMARK_FILE_SIZE_SMALL, 5);
let mut files = BTreeMap::new();
files.insert(challenge.file_metadata.file_id.clone(), &prepared_file);
let mut ledger = FileLedger::new();
ledger
.add_file(
challenge.file_metadata.file_id.clone(),
challenge.file_metadata.root,
api::tree_depth_from_metadata(&challenge.file_metadata),
)
.unwrap();
let challenges = vec![challenge];
let system = PorSystem::new(&ledger);
time_operation("Prove 16KB/5 challenges", || {
system.prove(vec![&prepared_file], &challenges).unwrap()
});
}
println!();
}
fn bench_verifying() {
println!("Verification API");
println!("----------------");
let (_params, prepared_file, challenge) =
setup_proof_scenario(config::BENCHMARK_FILE_SIZE_SMALL, 2);
let mut files = BTreeMap::new();
files.insert(challenge.file_metadata.file_id.clone(), &prepared_file);
let mut ledger = FileLedger::new();
ledger
.add_file(
challenge.file_metadata.file_id.clone(),
challenge.file_metadata.root,
api::tree_depth_from_metadata(&challenge.file_metadata),
)
.unwrap();
let challenges = vec![challenge.clone()];
let system = PorSystem::new(&ledger);
let proof = system.prove(vec![&prepared_file], &challenges).unwrap();
time_operation_avg("Verify proof", config::BENCHMARK_VERIFY_ITERATIONS, || {
let result = system.verify(&proof, &challenges).unwrap();
assert!(result, "Verification should succeed");
});
println!();
}
fn bench_e2e() {
println!("End-to-End Workflow");
println!("-------------------");
let file_size_kb = config::BENCHMARK_FILE_SIZE_SMALL;
let num_challenges = 2;
time_operation(
&format!(
"Full workflow ({}KB/{} challenges)",
file_size_kb, num_challenges
),
|| {
let data = generate_test_data(file_size_kb * 1024);
let (prepared_file, metadata) = api::prepare_file(&data, "e2e_test.dat").unwrap();
let tree_depth = metadata.padded_len.trailing_zeros() as usize;
let (files_per_step, file_tree_depth) = config::derive_shape(1, tree_depth);
let _params =
params::load_or_generate_params(files_per_step, file_tree_depth, 0).unwrap();
let seed = FieldElement::from(42u64);
let challenge = Challenge::new(
metadata.clone(),
1000,
num_challenges,
seed,
String::from("bench_prover"),
);
let mut ledger = FileLedger::new();
ledger
.add_file(
metadata.file_id.clone(),
metadata.root,
api::tree_depth_from_metadata(&metadata),
)
.unwrap();
let mut files = BTreeMap::new();
files.insert(metadata.file_id.clone(), &prepared_file);
let challenges = vec![challenge.clone()];
let system = PorSystem::new(&ledger);
let proof = system.prove(vec![&prepared_file], &challenges).unwrap();
let result = system.verify(&proof, &challenges).unwrap();
assert!(result, "E2E verification should succeed");
},
);
println!();
}
fn generate_test_data(size: usize) -> Vec<u8> {
let mut rng = StdRng::seed_from_u64(config::BENCHMARK_SEED);
let mut data = vec![0u8; size];
rng.fill_bytes(&mut data);
data
}
fn setup_proof_scenario(
file_size_kb: usize,
num_challenges: usize,
) -> (api::PorParams, api::PreparedFile, Challenge) {
let data = generate_test_data(file_size_kb * 1024);
let (prepared_file, metadata) = api::prepare_file(&data, "bench_test.dat").unwrap();
let tree_depth = metadata.padded_len.trailing_zeros() as usize;
let (files_per_step, file_tree_depth) = config::derive_shape(1, tree_depth);
let params = params::load_or_generate_params(files_per_step, file_tree_depth, 0).unwrap();
let seed = FieldElement::from(config::TEST_RANDOM_SEED);
let challenge = Challenge::new(
metadata,
1000,
num_challenges,
seed,
String::from("bench_prover"),
);
(params, prepared_file, challenge)
}