Skip to content

Commit cf6a398

Browse files
committed
refactor!: Drop MerkleTreeMaker
Previously, constructing a Merkle tree required selecting an implementor of the trait `MerkleTreeMaker`. Now, the `MerkleTree` struct provides two different but equivalent methods for constructing a Merkle tree, `sequential_new` and `par_new`. To migrate, replace - `MerkleTree::new::<CpuParallel>` with `MerkleTree::par_new`, and - `MerkleTree::new::<CpuSequential>` with `MerkleTree::sequential_new`.
1 parent 0a5a8b3 commit cf6a398

File tree

8 files changed

+95
-148
lines changed

8 files changed

+95
-148
lines changed

twenty-first/benches/merkle_tree.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ use criterion::BenchmarkId;
66
use criterion::Criterion;
77
use twenty_first::math::digest::Digest;
88
use twenty_first::math::other::random_elements;
9-
use twenty_first::util_types::merkle_tree::CpuParallel;
109
use twenty_first::util_types::merkle_tree::MerkleTree;
1110

1211
fn merkle_tree(c: &mut Criterion) {
@@ -20,7 +19,7 @@ fn merkle_tree(c: &mut Criterion) {
2019
let elements: Vec<Digest> = random_elements(size);
2120

2221
group.bench_function(BenchmarkId::new("merkle_tree", size), |bencher| {
23-
bencher.iter(|| MerkleTree::new::<CpuParallel>(&elements).unwrap());
22+
bencher.iter(|| MerkleTree::par_new(&elements).unwrap());
2423
});
2524
}
2625

twenty-first/benches/merkle_tree_auth_structure_size.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ use rand::RngCore;
1212
use rand::SeedableRng;
1313
use twenty_first::math::bfield_codec::BFieldCodec;
1414
use twenty_first::math::tip5::Tip5;
15-
use twenty_first::util_types::merkle_tree::CpuParallel;
1615
use twenty_first::util_types::merkle_tree::MerkleTree;
1716

1817
#[derive(Debug, Clone, Copy)]
@@ -74,7 +73,7 @@ fn auth_structure_len(c: &mut Criterion<AuthStructureEncodingLength>) {
7473
let num_leafs = 1 << tree_height;
7574
let leafs = (0..num_leafs).map(|_| rng.next_u64()).collect_vec();
7675
let leaf_digests = leafs.iter().map(Tip5::hash).collect_vec();
77-
let mt = MerkleTree::new::<CpuParallel>(&leaf_digests).unwrap();
76+
let mt = MerkleTree::par_new(&leaf_digests).unwrap();
7877

7978
let num_opened_indices = 40;
8079
let mut group = c.benchmark_group("merkle_tree_auth_structure_size");

twenty-first/benches/merkle_tree_authenticate.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ impl MerkleTreeSampler {
6969

7070
fn tree(&mut self) -> MerkleTree {
7171
let leaf_digests = self.leaf_digests();
72-
MerkleTree::new::<CpuParallel>(&leaf_digests).unwrap()
72+
MerkleTree::par_new(&leaf_digests).unwrap()
7373
}
7474

7575
fn indices_to_open(&mut self) -> Vec<usize> {

twenty-first/src/lib.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,6 @@ pub(crate) mod tests {
5151
implements_usual_auto_traits::<Digest>();
5252
implements_usual_auto_traits::<Tip5>();
5353
implements_usual_auto_traits::<XFieldElement>();
54-
implements_usual_auto_traits::<CpuParallel>();
5554
implements_usual_auto_traits::<MerkleTree>();
5655
implements_usual_auto_traits::<MerkleTreeInclusionProof>();
5756
implements_usual_auto_traits::<MmrMembershipProof>();

twenty-first/src/mock/mmr/mock_mmr.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -395,7 +395,7 @@ mod mmr_test {
395395
let num_leafs_in_tree = 1 << tree_height;
396396
let leaf_digests =
397397
&digests[num_processed_digests..num_processed_digests + num_leafs_in_tree];
398-
let tree = MerkleTree::new::<CpuParallel>(leaf_digests).unwrap();
398+
let tree = MerkleTree::par_new(leaf_digests).unwrap();
399399
num_processed_digests += num_leafs_in_tree;
400400
trees.push(tree);
401401
}

twenty-first/src/prelude.rs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,8 @@ pub use crate::math::traits::Inverse;
1313
pub use crate::math::traits::ModPowU32;
1414
pub use crate::math::x_field_element;
1515
pub use crate::math::x_field_element::XFieldElement;
16-
pub use crate::util_types::merkle_tree::CpuParallel;
17-
pub use crate::util_types::merkle_tree::CpuSequential;
1816
pub use crate::util_types::merkle_tree::MerkleTree;
1917
pub use crate::util_types::merkle_tree::MerkleTreeInclusionProof;
20-
pub use crate::util_types::merkle_tree::MerkleTreeMaker;
2118
pub use crate::util_types::mmr::mmr_membership_proof::MmrMembershipProof;
2219
pub use crate::util_types::mmr::mmr_trait::Mmr;
2320
pub use crate::util_types::sponge::Sponge;

twenty-first/src/util_types/merkle_tree.rs

Lines changed: 87 additions & 134 deletions
Original file line numberDiff line numberDiff line change
@@ -48,54 +48,6 @@ pub struct MerkleTree {
4848
nodes: Vec<Digest>,
4949
}
5050

51-
/// A trait for creating a [`MerkleTree`] from a list of [`Digest`]s.
52-
///
53-
/// This trait defines the method required to construct a `MerkleTree` given an
54-
/// array of `Digest` values. Implementations of this trait can define different
55-
/// strategies for building the tree, for example parallel or sequential
56-
/// construction, using the CPU or a GPU, et cetera.
57-
///
58-
/// It might be more intuitive to use [`MerkleTree::new`] over directly calling
59-
/// implementors of this trait. For example:
60-
///
61-
/// ```
62-
/// # use twenty_first::error::MerkleTreeError;
63-
/// # use twenty_first::prelude::*;
64-
///
65-
/// # fn wrapper_fn() -> Result<(), MerkleTreeError> {
66-
/// let leafs = [bfe_vec![42, 43, 44], bfe_vec![1337, 1338]];
67-
/// let leaf_digests = leafs.map(|leaf| Tip5::hash_varlen(&leaf));
68-
///
69-
/// let tree = MerkleTree::new::<CpuParallel>(&leaf_digests)?;
70-
///
71-
/// // also works:
72-
/// let tree = CpuParallel::from_digests(&leaf_digests)?;
73-
/// # Ok(())
74-
/// # }
75-
/// # wrapper_fn().unwrap();
76-
/// ```
77-
///
78-
/// # Implementing
79-
///
80-
/// Implementing this trait requires intimate knowledge of the internal
81-
/// representation of a [`MerkleTree`], in particular, the indexing of its
82-
/// nodes. For a starting point, see [`MerkleTree::authentication_structure`].
83-
///
84-
/// ## Errors
85-
///
86-
/// Implementations of this trait should return an error if:
87-
/// - The number of digests is zero.
88-
/// - The number of digests is not a power of two.
89-
pub trait MerkleTreeMaker {
90-
/// Build a MerkleTree. The passed-in digests are copied as the leafs of the tree.
91-
///
92-
/// # Errors
93-
///
94-
/// - If the number of digests is zero.
95-
/// - If the number of digests is not a power of two.
96-
fn from_digests(digests: &[Digest]) -> Result<MerkleTree>;
97-
}
98-
9951
/// A full inclusion proof for the leafs at the supplied indices, including the
10052
/// leafs themselves. The proof is relative to some [Merkle tree](MerkleTree),
10153
/// which is not necessarily (and generally cannot be) known in its entirety by
@@ -152,8 +104,79 @@ impl MerkleTree {
152104
/// If you need to read the root, try [`root()`](Self::root) instead.
153105
pub(crate) const ROOT_INDEX: usize = 1;
154106

155-
pub fn new<Maker: MerkleTreeMaker>(leafs: &[Digest]) -> Result<Self> {
156-
Maker::from_digests(leafs)
107+
/// Build a MerkleTree with the given leafs.
108+
///
109+
/// [`MerkleTree::par_new`] is equivalent and usually faster.
110+
///
111+
/// # Errors
112+
///
113+
/// - If the number of leafs is zero.
114+
/// - If the number of leafs is not a power of two.
115+
pub fn sequential_new(leafs: &[Digest]) -> Result<Self> {
116+
let mut nodes = Self::initialize_merkle_tree_nodes(leafs)?;
117+
118+
for i in (MerkleTree::ROOT_INDEX..leafs.len()).rev() {
119+
nodes[i] = Tip5::hash_pair(nodes[i * 2], nodes[i * 2 + 1]);
120+
}
121+
122+
Ok(MerkleTree { nodes })
123+
}
124+
125+
/// Build a MerkleTree with the given leafs.
126+
///
127+
/// Uses [`rayon`] to parallelize Merkle tree construction. If the use of
128+
/// [`rayon`] is not an option in your context, use
129+
/// [`MerkleTree::sequential_new`], which is equivalent but usually slower.
130+
///
131+
/// # Errors
132+
///
133+
/// - If the number of leafs is zero.
134+
/// - If the number of leafs is not a power of two.
135+
pub fn par_new(leafs: &[Digest]) -> Result<Self> {
136+
let mut nodes = Self::initialize_merkle_tree_nodes(leafs)?;
137+
138+
// parallel
139+
let mut num_nodes_on_this_level = leafs.len();
140+
while num_nodes_on_this_level >= *PARALLELIZATION_CUTOFF {
141+
num_nodes_on_this_level /= 2;
142+
let node_indices_on_this_level = num_nodes_on_this_level..2 * num_nodes_on_this_level;
143+
let nodes_on_this_level = node_indices_on_this_level
144+
.clone()
145+
.into_par_iter()
146+
.map(|i| Tip5::hash_pair(nodes[i * 2], nodes[i * 2 + 1]))
147+
.collect::<Vec<_>>();
148+
nodes[node_indices_on_this_level].copy_from_slice(&nodes_on_this_level);
149+
}
150+
151+
// sequential
152+
let num_remaining_nodes = num_nodes_on_this_level;
153+
for i in (MerkleTree::ROOT_INDEX..num_remaining_nodes).rev() {
154+
nodes[i] = Tip5::hash_pair(nodes[i * 2], nodes[i * 2 + 1]);
155+
}
156+
157+
Ok(MerkleTree { nodes })
158+
}
159+
160+
/// Helps to kick off Merkle tree construction. Sets up the Merkle tree's
161+
/// internal nodes if (and only if) it is possible to construct a Merkle
162+
/// tree with the given leafs.
163+
fn initialize_merkle_tree_nodes(leafs: &[Digest]) -> Result<Vec<Digest>> {
164+
if leafs.is_empty() {
165+
return Err(MerkleTreeError::TooFewLeafs);
166+
}
167+
168+
let num_leafs = leafs.len();
169+
if !num_leafs.is_power_of_two() {
170+
return Err(MerkleTreeError::IncorrectNumberOfLeafs);
171+
}
172+
if num_leafs > MAX_NUM_LEAFS {
173+
return Err(MerkleTreeError::TreeTooHigh);
174+
}
175+
176+
let mut nodes = vec![Digest::default(); 2 * num_leafs];
177+
nodes[num_leafs..].copy_from_slice(leafs);
178+
179+
Ok(nodes)
157180
}
158181

159182
/// Given a list of leaf indices, return the indices of exactly those nodes that
@@ -307,7 +330,7 @@ impl<'a> Arbitrary<'a> for MerkleTree {
307330
let leaf_digests: arbitrary::Result<Vec<_>> =
308331
(0..num_leafs).map(|_| u.arbitrary()).collect();
309332

310-
let tree = Self::new::<CpuParallel>(&leaf_digests?).unwrap();
333+
let tree = Self::par_new(&leaf_digests?).unwrap();
311334
Ok(tree)
312335
}
313336
}
@@ -518,76 +541,6 @@ impl TryFrom<MerkleTreeInclusionProof> for PartialMerkleTree {
518541
}
519542
}
520543

521-
/// [Build a Merkle tree](MerkleTreeMaker) using parallelism.
522-
#[derive(Debug, Copy, Clone, Eq, PartialEq, Default)]
523-
pub struct CpuParallel;
524-
525-
impl MerkleTreeMaker for CpuParallel {
526-
fn from_digests(digests: &[Digest]) -> Result<MerkleTree> {
527-
let mut nodes = initialize_merkle_tree_nodes(digests)?;
528-
529-
// parallel
530-
let mut num_nodes_on_this_level = digests.len();
531-
while num_nodes_on_this_level >= *PARALLELIZATION_CUTOFF {
532-
num_nodes_on_this_level /= 2;
533-
let node_indices_on_this_level = num_nodes_on_this_level..2 * num_nodes_on_this_level;
534-
let nodes_on_this_level = node_indices_on_this_level
535-
.clone()
536-
.into_par_iter()
537-
.map(|i| Tip5::hash_pair(nodes[i * 2], nodes[i * 2 + 1]))
538-
.collect::<Vec<_>>();
539-
nodes[node_indices_on_this_level].copy_from_slice(&nodes_on_this_level);
540-
}
541-
542-
// sequential
543-
let num_remaining_nodes = num_nodes_on_this_level;
544-
for i in (MerkleTree::ROOT_INDEX..num_remaining_nodes).rev() {
545-
nodes[i] = Tip5::hash_pair(nodes[i * 2], nodes[i * 2 + 1]);
546-
}
547-
548-
Ok(MerkleTree { nodes })
549-
}
550-
}
551-
552-
/// [Build a Merkle tree](MerkleTreeMaker) without any parallelism.
553-
#[derive(Debug, Copy, Clone, Eq, PartialEq, Default)]
554-
pub struct CpuSequential;
555-
556-
impl MerkleTreeMaker for CpuSequential {
557-
fn from_digests(digests: &[Digest]) -> Result<MerkleTree> {
558-
let mut nodes = initialize_merkle_tree_nodes(digests)?;
559-
560-
let num_leafs = digests.len();
561-
for i in (MerkleTree::ROOT_INDEX..num_leafs).rev() {
562-
nodes[i] = Tip5::hash_pair(nodes[i * 2], nodes[i * 2 + 1]);
563-
}
564-
565-
Ok(MerkleTree { nodes })
566-
}
567-
}
568-
569-
/// Helps to kick off Merkle tree construction. Sets up the Merkle tree's
570-
/// internal nodes if (and only if) it is possible to construct a Merkle
571-
/// tree with the given leafs.
572-
fn initialize_merkle_tree_nodes(digests: &[Digest]) -> Result<Vec<Digest>> {
573-
if digests.is_empty() {
574-
return Err(MerkleTreeError::TooFewLeafs);
575-
}
576-
577-
let num_leafs = digests.len();
578-
if !num_leafs.is_power_of_two() {
579-
return Err(MerkleTreeError::IncorrectNumberOfLeafs);
580-
}
581-
if num_leafs > MAX_NUM_LEAFS {
582-
return Err(MerkleTreeError::TreeTooHigh);
583-
}
584-
585-
let mut nodes = vec![Digest::default(); 2 * num_leafs];
586-
nodes[num_leafs..].copy_from_slice(digests);
587-
588-
Ok(nodes)
589-
}
590-
591544
#[derive(Debug, Clone, Copy, PartialEq, Eq, Error)]
592545
pub enum MerkleTreeError {
593546
#[error("All leaf indices must be valid, i.e., less than {num_leafs}.")]
@@ -633,7 +586,7 @@ pub mod merkle_tree_test {
633586
let num_leafs = 1 << tree_height;
634587
let leafs = (0..num_leafs).map(BFieldElement::new);
635588
let leaf_digests = leafs.map(|bfe| Tip5::hash_varlen(&[bfe])).collect_vec();
636-
let tree = Self::new::<CpuParallel>(&leaf_digests).unwrap();
589+
let tree = Self::par_new(&leaf_digests).unwrap();
637590
assert!(leaf_digests.iter().all_unique());
638591
tree
639592
}
@@ -674,22 +627,22 @@ pub mod merkle_tree_test {
674627

675628
#[test]
676629
fn building_merkle_tree_from_empty_list_of_digests_fails_with_expected_error() {
677-
let maybe_tree = MerkleTree::new::<CpuParallel>(&[]);
630+
let maybe_tree = MerkleTree::par_new(&[]);
678631
let err = maybe_tree.unwrap_err();
679632
assert_eq!(MerkleTreeError::TooFewLeafs, err);
680633
}
681634

682635
#[test]
683636
fn merkle_tree_with_one_leaf_has_expected_height_and_number_of_leafs() {
684637
let digest = Digest::default();
685-
let tree = MerkleTree::new::<CpuParallel>(&[digest]).unwrap();
638+
let tree = MerkleTree::par_new(&[digest]).unwrap();
686639
assert_eq!(1, tree.num_leafs());
687640
assert_eq!(0, tree.height());
688641
}
689642

690643
#[proptest]
691644
fn building_merkle_tree_from_one_digest_makes_that_digest_the_root(digest: Digest) {
692-
let tree = MerkleTree::new::<CpuParallel>(&[digest]).unwrap();
645+
let tree = MerkleTree::par_new(&[digest]).unwrap();
693646
assert_eq!(digest, tree.root());
694647
}
695648

@@ -701,26 +654,26 @@ pub mod merkle_tree_test {
701654
) {
702655
let digest = Digest::default();
703656
let digests = vec![digest; num_leafs];
704-
let maybe_tree = MerkleTree::new::<CpuParallel>(&digests);
657+
let maybe_tree = MerkleTree::par_new(&digests);
705658
let err = maybe_tree.unwrap_err();
706659
assert_eq!(MerkleTreeError::IncorrectNumberOfLeafs, err);
707660
}
708661

709662
#[proptest]
710-
fn merkle_tree_makers_behave_identically_on_random_input(leafs: Vec<Digest>) {
711-
let parallel = MerkleTree::new::<CpuParallel>(&leafs);
712-
let sequential = MerkleTree::new::<CpuSequential>(&leafs);
713-
prop_assert_eq!(parallel, sequential);
663+
fn merkle_tree_construction_strategies_behave_identically_on_random_input(leafs: Vec<Digest>) {
664+
let sequential = MerkleTree::sequential_new(&leafs);
665+
let parallel = MerkleTree::par_new(&leafs);
666+
prop_assert_eq!(sequential, parallel);
714667
}
715668

716669
#[proptest]
717-
fn merkle_tree_makers_produce_identical_trees(
670+
fn merkle_tree_construction_strategies_produce_identical_trees(
718671
#[strategy(0_usize..10)] _tree_height: usize,
719672
#[strategy(vec(arb(), 1 << #_tree_height))] leafs: Vec<Digest>,
720673
) {
721-
let parallel = MerkleTree::new::<CpuParallel>(&leafs)?;
722-
let sequential = MerkleTree::new::<CpuSequential>(&leafs)?;
723-
prop_assert_eq!(parallel, sequential);
674+
let sequential = MerkleTree::sequential_new(&leafs)?;
675+
let parallel = MerkleTree::par_new(&leafs)?;
676+
prop_assert_eq!(sequential, parallel);
724677
}
725678

726679
#[proptest(cases = 100)]

twenty-first/src/util_types/mmr/mmr_successor_proof.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ impl MmrSuccessorProof {
4444
}
4545

4646
let leafs_in_initial_right_tree = &new_leafs[..num_leafs_in_lowest_peak];
47-
let initial_right_tree = MerkleTree::new::<CpuParallel>(leafs_in_initial_right_tree)
47+
let initial_right_tree = MerkleTree::par_new(leafs_in_initial_right_tree)
4848
.expect("internal error: should be able to build Merkle tree");
4949

5050
let num_total_leafs =
@@ -69,7 +69,7 @@ impl MmrSuccessorProof {
6969
let indices_of_leafs_in_right_tree =
7070
first_unused_new_leaf_idx..first_unused_new_leaf_idx + num_leafs_in_right_tree;
7171
let leafs_in_right_tree = &new_leafs[indices_of_leafs_in_right_tree];
72-
let right_tree = MerkleTree::new::<CpuParallel>(leafs_in_right_tree)
72+
let right_tree = MerkleTree::par_new(leafs_in_right_tree)
7373
.expect("internal error: should be able to build Merkle tree");
7474
first_unused_new_leaf_idx += num_leafs_in_right_tree;
7575

@@ -363,11 +363,11 @@ mod test {
363363
assert_eq!(2, relation.proof.paths.len());
364364

365365
let first_merkle_tree_leafs = [42_u64, 43].map(|i| Tip5::hash(&i));
366-
let first_merkle_tree = MerkleTree::new::<CpuParallel>(&first_merkle_tree_leafs).unwrap();
366+
let first_merkle_tree = MerkleTree::par_new(&first_merkle_tree_leafs).unwrap();
367367
assert_eq!(first_merkle_tree.root(), relation.proof.paths[0]);
368368

369369
let second_merkle_tree_leafs = [44_u64, 45, 46, 47].map(|i| Tip5::hash(&i));
370-
let second_merkle_tree = MerkleTree::new::<CpuParallel>(&second_merkle_tree_leafs).unwrap();
370+
let second_merkle_tree = MerkleTree::par_new(&second_merkle_tree_leafs).unwrap();
371371
assert_eq!(second_merkle_tree.root(), relation.proof.paths[1]);
372372

373373
relation.verify().unwrap();

0 commit comments

Comments
 (0)