From 98e5e0a5b20e995c0fcc4234a123d3ea47557d5e Mon Sep 17 00:00:00 2001 From: Qyriad Date: Wed, 23 Oct 2024 19:26:42 -0600 Subject: [PATCH] WIP(smt): impl simple subtree8 hashing and benchmarks for it bench(smt-subtree): add a benchmark for single-leaf subtrees make build_subtree also return the next leaf row convert (col, hash) tuples to a dedicated struct --- Cargo.toml | 4 ++ benches/smt-subtree.rs | 136 +++++++++++++++++++++++++++++++++++++ src/merkle/mod.rs | 2 +- src/merkle/smt/full/mod.rs | 9 ++- src/merkle/smt/mod.rs | 2 +- 5 files changed, 150 insertions(+), 3 deletions(-) create mode 100644 benches/smt-subtree.rs diff --git a/Cargo.toml b/Cargo.toml index 5d124c6..ec59d42 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,6 +27,10 @@ harness = false name = "smt" harness = false +[[bench]] +name = "smt-subtree" +harness = false + [[bench]] name = "store" harness = false diff --git a/benches/smt-subtree.rs b/benches/smt-subtree.rs new file mode 100644 index 0000000..cd7454a --- /dev/null +++ b/benches/smt-subtree.rs @@ -0,0 +1,136 @@ +use std::{fmt::Debug, hint, mem, time::Duration}; + +use criterion::{criterion_group, criterion_main, BatchSize, BenchmarkId, Criterion}; +use miden_crypto::{ + hash::rpo::RpoDigest, + merkle::{NodeIndex, Smt, SmtLeaf, SubtreeLeaf, SMT_DEPTH}, + Felt, Word, ONE, +}; +use rand_utils::prng_array; +use winter_utils::Randomizable; + +const PAIR_COUNTS: [u64; 5] = [1, 64, 128, 192, 256]; + +fn smt_subtree_even(c: &mut Criterion) { + let mut seed = [0u8; 32]; + + let mut group = c.benchmark_group("subtree8-even"); + + for pair_count in PAIR_COUNTS { + let bench_id = BenchmarkId::from_parameter(pair_count); + group.bench_with_input(bench_id, &pair_count, |b, &pair_count| { + b.iter_batched( + || { + // Setup. + let entries: Vec<(RpoDigest, Word)> = (0..pair_count) + .map(|n| { + // A single depth-8 subtree can have a maximum of 255 leaves. + let leaf_index = ((n as f64 / pair_count as f64) * 255.0) as u64; + let key = RpoDigest::new([ + generate_value(&mut seed), + ONE, + Felt::new(n), + Felt::new(leaf_index), + ]); + let value = generate_word(&mut seed); + (key, value) + }) + .collect(); + + let mut leaves: Vec<_> = entries + .iter() + .map(|(key, value)| { + let leaf = SmtLeaf::new_single(*key, *value); + let col = NodeIndex::from(leaf.index()).value(); + let hash = leaf.hash(); + SubtreeLeaf { col, hash } + }) + .collect(); + leaves.sort(); + leaves.dedup_by_key(|leaf| leaf.col); + leaves + }, + |leaves| { + // Benchmarked function. + let (subtree, _) = + Smt::build_subtree(hint::black_box(leaves), hint::black_box(SMT_DEPTH)); + assert!(!subtree.is_empty()); + }, + BatchSize::SmallInput, + ); + }); + } +} + +fn smt_subtree_random(c: &mut Criterion) { + let mut seed = [0u8; 32]; + + let mut group = c.benchmark_group("subtree8-rand"); + + for pair_count in PAIR_COUNTS { + let bench_id = BenchmarkId::from_parameter(pair_count); + group.bench_with_input(bench_id, &pair_count, |b, &pair_count| { + b.iter_batched( + || { + // Setup. + let entries: Vec<(RpoDigest, Word)> = (0..pair_count) + .map(|i| { + let leaf_index: u8 = generate_value(&mut seed); + let key = RpoDigest::new([ + ONE, + ONE, + Felt::new(i), + Felt::new(leaf_index as u64), + ]); + let value = generate_word(&mut seed); + (key, value) + }) + .collect(); + + let mut leaves: Vec<_> = entries + .iter() + .map(|(key, value)| { + let leaf = SmtLeaf::new_single(*key, *value); + let col = NodeIndex::from(leaf.index()).value(); + let hash = leaf.hash(); + SubtreeLeaf { col, hash } + }) + .collect(); + leaves.sort(); + leaves + }, + |leaves| { + let (subtree, _) = + Smt::build_subtree(hint::black_box(leaves), hint::black_box(SMT_DEPTH)); + assert!(!subtree.is_empty()); + }, + BatchSize::SmallInput, + ); + }); + } +} + +criterion_group! { + name = smt_subtree_group; + config = Criterion::default() + .measurement_time(Duration::from_secs(40)) + .sample_size(60) + .configure_from_args(); + targets = smt_subtree_even, smt_subtree_random +} +criterion_main!(smt_subtree_group); + +// HELPER FUNCTIONS +// -------------------------------------------------------------------------------------------- + +fn generate_value(seed: &mut [u8; 32]) -> T { + mem::swap(seed, &mut prng_array(*seed)); + let value: [T; 1] = rand_utils::prng_array(*seed); + value[0] +} + +fn generate_word(seed: &mut [u8; 32]) -> Word { + mem::swap(seed, &mut prng_array(*seed)); + let nums: [u64; 4] = prng_array(*seed); + [Felt::new(nums[0]), Felt::new(nums[1]), Felt::new(nums[2]), Felt::new(nums[3])] +} diff --git a/src/merkle/mod.rs b/src/merkle/mod.rs index a562aa5..dc897dc 100644 --- a/src/merkle/mod.rs +++ b/src/merkle/mod.rs @@ -23,7 +23,7 @@ pub use path::{MerklePath, RootPath, ValuePath}; mod smt; pub use smt::{ LeafIndex, MutationSet, SimpleSmt, Smt, SmtLeaf, SmtLeafError, SmtProof, SmtProofError, - SMT_DEPTH, SMT_MAX_DEPTH, SMT_MIN_DEPTH, + SubtreeLeaf, SMT_DEPTH, SMT_MAX_DEPTH, SMT_MIN_DEPTH, }; mod mmr; diff --git a/src/merkle/smt/full/mod.rs b/src/merkle/smt/full/mod.rs index 1e2c574..89d0702 100644 --- a/src/merkle/smt/full/mod.rs +++ b/src/merkle/smt/full/mod.rs @@ -6,7 +6,7 @@ use alloc::{ use super::{ EmptySubtreeRoots, Felt, InnerNode, InnerNodeInfo, LeafIndex, MerkleError, MerklePath, - MutationSet, NodeIndex, Rpo256, RpoDigest, SparseMerkleTree, Word, EMPTY_WORD, + MutationSet, NodeIndex, Rpo256, RpoDigest, SparseMerkleTree, SubtreeLeaf, Word, EMPTY_WORD, }; mod error; @@ -249,6 +249,13 @@ impl Smt { None } } + + pub fn build_subtree( + leaves: Vec, + bottom_depth: u8, + ) -> (BTreeMap, Vec) { + >::build_subtree(leaves, bottom_depth) + } } impl SparseMerkleTree for Smt { diff --git a/src/merkle/smt/mod.rs b/src/merkle/smt/mod.rs index fc6ace7..0c87724 100644 --- a/src/merkle/smt/mod.rs +++ b/src/merkle/smt/mod.rs @@ -496,7 +496,7 @@ pub(crate) trait SparseMerkleTree { #[derive(Debug, Default, Clone, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] -pub(crate) struct InnerNode { +pub struct InnerNode { pub left: RpoDigest, pub right: RpoDigest, }