Skip to content

Commit eba7d33

Browse files
refactor(data): first steps in rv-829
- Replaced MerkleTree::Leaf tuple with a struct - Removed the impl_modify_map_collect function - Replaced the uses of the mentioned function with a non-recursive direct implementation.
1 parent 11278e4 commit eba7d33

File tree

12 files changed

+229
-213
lines changed

12 files changed

+229
-213
lines changed

data/src/components/atom/tests.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use crate::components::atom::Atom;
1212
use crate::hash::Hash;
1313
use crate::hash::PartialHash;
1414
use crate::merkle_tree::MerkleTree;
15+
use crate::merkle_tree::MerkleTreeLeafData;
1516
use crate::mode::Mode;
1617
use crate::mode::Normal;
1718
use crate::mode::Prove;
@@ -219,7 +220,11 @@ fn proof_gen() {
219220
let merkle_tree = MerkleTree::from_foldable(&proof_atoms);
220221
merkle_tree.check_root_hash();
221222
match merkle_tree {
222-
MerkleTree::Leaf(hash, access_info, _) => {
223+
MerkleTree::Leaf(MerkleTreeLeafData {
224+
hash,
225+
access_info,
226+
..
227+
}) => {
223228
prop_assert_eq!(hash, initial_root_hash);
224229
prop_assert!(access_info);
225230
}

data/src/components/data_space/tests.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ use super::PageId;
2020
use crate::components::data_space::DataSpace;
2121
use crate::hash::Hash;
2222
use crate::merkle_tree::MerkleTree;
23+
use crate::merkle_tree::MerkleTreeLeafData;
2324
use crate::mode::Normal;
2425
use crate::mode::Prove;
2526
use crate::mode::utils::assert_eq_found;
@@ -261,7 +262,7 @@ fn generate_proof() {
261262
let mut queue = VecDeque::with_capacity(LEAVES + 1);
262263

263264
let pages_tree = match merkle_tree {
264-
MerkleTree::Leaf(_, _, _) => panic!("Did not expect leaf"),
265+
MerkleTree::Leaf(_) => panic!("Did not expect leaf"),
265266
MerkleTree::Node(_, mut children) => {
266267
// The node for the pages is the second child.
267268
children.remove(1)
@@ -273,7 +274,10 @@ fn generate_proof() {
273274
while let Some(node) = queue.pop_front() {
274275
match node {
275276
MerkleTree::Node(_, children) => queue.extend(children),
276-
MerkleTree::Leaf(_, access_info, _) => {
277+
MerkleTree::Leaf(MerkleTreeLeafData {
278+
access_info,
279+
..
280+
}) => {
277281
prop_assert_eq!(
278282
access_info,
279283
read_leaves.contains(&leaf) ||

data/src/merkle_proof.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
88
pub mod proof_tree;
99
pub mod tag;
10-
pub mod transform;
1110

1211
use std::error;
1312

data/src/merkle_proof/proof_tree.rs

Lines changed: 78 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,8 @@ use bincode::enc::write::Writer;
55

66
use super::tag::LeafTag;
77
use super::tag::Tag;
8-
use super::transform::ModifyResult;
9-
use super::transform::impl_modify_map_collect;
108
use crate::hash::Hash;
9+
use crate::hash::Hasher;
1110
use crate::tree::Tree;
1211

1312
/// Merkle proof tree structure.
@@ -77,6 +76,50 @@ pub enum MerkleProofLeaf {
7776
Read(Vec<u8>),
7877
}
7978

79+
enum NodeLeaf {
80+
Node,
81+
Leaf,
82+
}
83+
84+
struct HashState {
85+
node_leaf: NodeLeaf,
86+
parent_index: usize,
87+
hashes: Vec<Hash>,
88+
}
89+
90+
impl HashState {
91+
fn new(node_leaf: NodeLeaf, parent_index: usize, hashes: Vec<Hash>) -> Self {
92+
Self {
93+
node_leaf,
94+
parent_index,
95+
hashes,
96+
}
97+
}
98+
99+
fn push(&mut self, hash: Hash) {
100+
self.hashes.push(hash);
101+
}
102+
103+
fn get_hash(&self) -> Hash {
104+
match self.node_leaf {
105+
NodeLeaf::Node => {
106+
let mut hasher = Hasher::default();
107+
108+
for hash in self.hashes.iter() {
109+
hasher.update_with_hash(hash);
110+
}
111+
112+
hasher.to_hash()
113+
}
114+
NodeLeaf::Leaf => self.hashes[0],
115+
}
116+
}
117+
118+
fn get_parent_index(&self) -> usize {
119+
self.parent_index
120+
}
121+
}
122+
80123
impl MerkleProof {
81124
/// Create a new Merkle proof as a read leaf.
82125
pub fn leaf_read(data: Vec<u8>) -> Self {
@@ -90,18 +133,39 @@ impl MerkleProof {
90133

91134
/// Compute the root hash of the Merkle proof.
92135
pub fn root_hash(&self) -> Hash {
93-
impl_modify_map_collect(
94-
self,
95-
|subtree| match subtree {
96-
Tree::Node(vec) => ModifyResult::NodeContinue((), vec.iter().collect()),
97-
Tree::Leaf(data) => ModifyResult::LeafStop(data),
98-
},
99-
|leaf| match leaf {
100-
MerkleProofLeaf::Blind(hash) => *hash,
101-
MerkleProofLeaf::Read(data) => Hash::hash_bytes(data.as_slice()),
102-
},
103-
|(), leaves| Hash::combine_hashes(leaves),
104-
)
136+
let mut nodes: Vec<(&MerkleProof, usize)> = vec![(self, 0)];
137+
let mut hash_states: Vec<HashState> = vec![];
138+
139+
while let Some((node, parent_index)) = nodes.pop() {
140+
match node {
141+
Tree::Leaf(MerkleProofLeaf::Blind(hash)) => {
142+
hash_states.push(HashState::new(NodeLeaf::Leaf, parent_index, vec![*hash]));
143+
}
144+
Tree::Leaf(MerkleProofLeaf::Read(data)) => {
145+
hash_states.push(HashState::new(
146+
NodeLeaf::Leaf,
147+
parent_index,
148+
vec![Hash::hash_bytes(data.as_slice())],
149+
));
150+
}
151+
Tree::Node(children) => {
152+
hash_states.push(HashState::new(NodeLeaf::Node, parent_index, vec![]));
153+
let new_parent_index = hash_states.len() - 1;
154+
for child in children.iter() {
155+
nodes.push((child, new_parent_index));
156+
}
157+
}
158+
}
159+
}
160+
161+
while let Some(hash_state) = hash_states.pop() {
162+
if hash_states.is_empty() {
163+
return hash_state.get_hash();
164+
}
165+
hash_states[hash_state.get_parent_index()].push(hash_state.get_hash());
166+
}
167+
168+
unreachable!("hash_states can't be an empty vector");
105169
}
106170
}
107171

data/src/merkle_proof/transform.rs

Lines changed: 1 addition & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -1,89 +1,3 @@
1-
// SPDX-FileCopyrightText: 2024 TriliTech <[email protected]>
2-
//
1+
// SPDX-FileCopyrightText: 2025 TriliTech <[email protected]>
32
// SPDX-License-Identifier: MIT
43

5-
//! Module for tree utils like types, traversals.
6-
//!
7-
//! All the traversals implemented in this module should be the same to maintain consistency,
8-
//! which is required for serialisation / deserialisation
9-
10-
/// Intermediary either-like type for implementing [`impl_modify_map_collect`]
11-
#[derive(Clone)]
12-
pub enum ModifyResult<D, N, L> {
13-
/// Current subtree should be replaced with a node containing the given children,
14-
/// and an auxiliary data for extra context if needed.
15-
/// Traversal should continue recursively.
16-
NodeContinue(D, Vec<N>),
17-
/// Current subtree is replaced by a leaf containing the given data.
18-
LeafStop(L),
19-
}
20-
21-
/// Perform generic modify_map_collect
22-
///
23-
/// This is done in 3 steps while traversing the tree in a pre-order DFS traversal:
24-
/// 1. Apply `modify` on current subtree: This operation changes the structure of the current
25-
/// subtree before traversing its children.
26-
/// 2. When encountering leaves, `map` is called to transform a leaf from `A` to `B` type.
27-
/// This is done on children of subtrees which have been traversed after `modify` was called.
28-
/// 3. After modifying & mapping the children of a node, the `collect` method gathers the newly
29-
/// modified & mapped subtrees to create the new subtree.
30-
pub fn impl_modify_map_collect<
31-
InterimLeafData, // InterimLeafData -> FinalLeafData when applying `map`
32-
FinalLeafData, // [FinalLeafData] -> FinalLeafData when applying `collect`
33-
AuxTreeData, // Type of auxiliary data held for a subtree
34-
InputTree,
35-
OutputTree: From<FinalLeafData>,
36-
TreeModifier: FnMut(InputTree) -> ModifyResult<AuxTreeData, InputTree, InterimLeafData>,
37-
LeafMapper: FnMut(InterimLeafData) -> FinalLeafData,
38-
Collector: FnMut(AuxTreeData, Vec<OutputTree>) -> OutputTree,
39-
>(
40-
root: InputTree,
41-
mut modify: TreeModifier,
42-
mut map: LeafMapper,
43-
mut collect: Collector,
44-
) -> OutputTree {
45-
enum ProcessEvents<ProcessEvent, CollectAuxTreeData> {
46-
Node(ProcessEvent),
47-
Collect(CollectAuxTreeData, usize),
48-
}
49-
50-
let mut process = vec![ProcessEvents::Node(root)];
51-
let mut done: Vec<OutputTree> = vec![];
52-
53-
while let Some(event) = process.pop() {
54-
match event {
55-
ProcessEvents::Node(subtree) => match modify(subtree) {
56-
ModifyResult::LeafStop(data) => {
57-
// Instead of pushing a single leaf process on the Process-queue,
58-
// map the data and append it directly to the Done-queue
59-
done.push(OutputTree::from(map(data)));
60-
}
61-
ModifyResult::NodeContinue(node_data, children) => {
62-
// the only case where we push further process events in the process queue
63-
// We have to first push a collect event to know how many children should be collected when forming back the current subtree
64-
process.push(ProcessEvents::Collect(node_data, children.len()));
65-
66-
process.extend(
67-
children
68-
.into_iter()
69-
.rev()
70-
.map(|child| ProcessEvents::Node(child)),
71-
);
72-
}
73-
},
74-
ProcessEvents::Collect(node_data, count) => {
75-
// We need to reconstruct a subtree which is made of `count` children
76-
// No panic: We are guaranteed count < done.len() since every Collect(size)
77-
// corresponds to size nodes pushed to Done-queue
78-
let children = done.split_off(done.len() - count);
79-
done.push(collect(node_data, children));
80-
}
81-
}
82-
}
83-
84-
// No Panic: We only add a single node as root at the beginning of the algorithm
85-
// which corresponds to this last node in the Done-queue
86-
done.pop()
87-
.filter(|_| done.is_empty())
88-
.expect("Unexpected number of results")
89-
}

data/src/merkle_tree.rs

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,22 @@ use crate::foldable::Foldable;
88
use crate::foldable::NodeFold;
99
use crate::hash::Hash;
1010

11+
/// Struct which holds data for the leaves of a [`crate::merkle_tree::MerkleTree`]
12+
#[derive(Debug, Clone)]
13+
pub struct MerkleTreeLeafData {
14+
pub hash: Hash,
15+
pub access_info: bool,
16+
pub data: Vec<u8>,
17+
}
18+
1119
/// A variable-width Merkle tree with access metadata for leaves.
1220
///
1321
/// Values of this type are produced by the proof-generating backend to capture
1422
/// a snapshot of the machine state along with access information for leaves
1523
/// which hold data that was used in a particular evaluation step.
1624
#[derive(Debug, Clone)]
1725
pub enum MerkleTree {
18-
Leaf(Hash, bool, Vec<u8>),
26+
Leaf(MerkleTreeLeafData),
1927
Node(Hash, Vec<Self>),
2028
}
2129

@@ -38,14 +46,18 @@ impl MerkleTree {
3846
pub fn root_hash(&self) -> Hash {
3947
match self {
4048
Self::Node(hash, _) => *hash,
41-
Self::Leaf(hash, _, _) => *hash,
49+
Self::Leaf(MerkleTreeLeafData { hash, .. }) => *hash,
4250
}
4351
}
4452

4553
/// Creates a merkle tree which is a single leaf
4654
pub fn make_merkle_leaf(data: Vec<u8>, access_info: bool) -> Self {
4755
let hash = Hash::hash_bytes(&data);
48-
MerkleTree::Leaf(hash, access_info, data)
56+
MerkleTree::Leaf(MerkleTreeLeafData {
57+
hash,
58+
access_info,
59+
data,
60+
})
4961
}
5062

5163
/// Takes a list of children nodes and creates a
@@ -65,7 +77,9 @@ impl MerkleTree {
6577

6678
while let Some(node) = deque.pop_front() {
6779
let is_valid_hash = match node {
68-
Self::Leaf(hash, _, data) => &Hash::hash_bytes(data) == hash,
80+
Self::Leaf(MerkleTreeLeafData { hash, data, .. }) => {
81+
&Hash::hash_bytes(data) == hash
82+
}
6983
Self::Node(hash, children) => {
7084
let children_hashes: Vec<Hash> = children
7185
.iter()

data/src/tree.rs

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,3 @@ pub enum Tree<A> {
88
Node(Vec<Self>),
99
Leaf(A),
1010
}
11-
12-
/// Used in [`crate::merkle_proof::transform::impl_modify_map_collect`]
13-
impl<D> From<D> for Tree<D> {
14-
fn from(value: D) -> Self {
15-
Tree::Leaf(value)
16-
}
17-
}

src/riscv/lib/src/pvm/common.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,8 @@ use crate::state_backend;
5151
use crate::state_backend::ManagerBase;
5252
use crate::state_backend::ManagerClone;
5353
use crate::state_backend::ProofTree;
54-
use crate::state_backend::proof_backend::merkle::merkle_tree_to_merkle_proof;
54+
use crate::state_backend::proof_backend::merkle::CompressedMerkleTree;
55+
use crate::state_backend::proof_backend::merkle::merkle_tree_to_compressed_merkle_tree;
5556
use crate::state_backend::proof_backend::proof::Proof;
5657
use crate::state_backend::proof_backend::proof::deserialise_owned;
5758

@@ -375,7 +376,9 @@ where
375376
let _ = self.input_request();
376377

377378
let merkle_tree = MerkleTree::from_foldable(self);
378-
let merkle_proof = merkle_tree_to_merkle_proof(merkle_tree);
379+
let compressed_merkle_tree: CompressedMerkleTree =
380+
merkle_tree_to_compressed_merkle_tree(merkle_tree);
381+
let merkle_proof: MerkleProof = compressed_merkle_tree.to_proof();
379382

380383
let final_hash = Hash::from_foldable(self);
381384
let proof = Proof::new(merkle_proof, final_hash);

src/riscv/lib/src/state_backend.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ mod tests {
134134
use rand::RngCore;
135135

136136
use super::*;
137-
use crate::state_backend::proof_backend::merkle::merkle_tree_to_merkle_proof;
137+
use crate::state_backend::proof_backend::merkle::merkle_tree_to_compressed_merkle_tree;
138138
use crate::state_backend::proof_backend::proof::deserialise_owned::ProofTreeDeserialiser;
139139

140140
/// Data structure whose [`Elem`] implementation only writes to part of the given space
@@ -192,7 +192,7 @@ mod tests {
192192

193193
// The Verify mode needs a proof, so we generate it from the Prove mode
194194
let merkle_tree = MerkleTree::from_foldable(&mem_prove);
195-
let proof_tree = merkle_tree_to_merkle_proof(merkle_tree);
195+
let proof_tree = merkle_tree_to_compressed_merkle_tree(merkle_tree).to_proof();
196196
let proof_deser = ProofTreeDeserialiser::from(ProofTree::Present(&proof_tree));
197197
let mut mem_verify = DataSpace::from_proof(proof_deser).unwrap().into_result();
198198

0 commit comments

Comments
 (0)