Skip to content

Commit f3c6103

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 8a66770 commit f3c6103

File tree

11 files changed

+242
-213
lines changed

11 files changed

+242
-213
lines changed

data/src/hash.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,10 @@ pub struct Hash {
5656
}
5757

5858
impl Hash {
59+
pub fn new(digest: [u8; 32]) -> Self {
60+
Hash { digest }
61+
}
62+
5963
/// Hash a slice of bytes
6064
pub fn blake3_hash_bytes(bytes: &[u8]) -> Self {
6165
let digest = blake3::hash(bytes).into();
@@ -107,6 +111,10 @@ impl Hash {
107111
pub fn from_foldable(foldable: &impl Foldable<HashFold>) -> Self {
108112
foldable.fold(HashFold)
109113
}
114+
115+
pub fn to_digest(self) -> [u8; 32] {
116+
self.digest
117+
}
110118
}
111119

112120
impl std::fmt::Display for Hash {

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: 89 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@ 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;
119
use crate::tree::Tree;
1210

@@ -77,6 +75,58 @@ pub enum MerkleProofLeaf {
7775
Read(Vec<u8>),
7876
}
7977

78+
enum NodeLeaf {
79+
Node,
80+
Leaf,
81+
}
82+
83+
struct HashState {
84+
node_leaf: NodeLeaf,
85+
parent_index: usize,
86+
digests: Vec<[u8; 32]>,
87+
}
88+
89+
impl HashState {
90+
pub fn new(node_leaf: NodeLeaf, parent_index: usize) -> Self {
91+
Self {
92+
node_leaf,
93+
parent_index,
94+
digests: vec![],
95+
}
96+
}
97+
98+
pub fn new_with_digest(node_leaf: NodeLeaf, parent_index: usize, digest: [u8; 32]) -> Self {
99+
Self {
100+
node_leaf,
101+
parent_index,
102+
digests: vec![digest],
103+
}
104+
}
105+
106+
pub fn push(&mut self, digest: [u8; 32]) {
107+
self.digests.push(digest);
108+
}
109+
110+
pub fn get_digest(&self) -> [u8; 32] {
111+
match self.node_leaf {
112+
NodeLeaf::Node => {
113+
let mut hasher = blake3::Hasher::new();
114+
115+
for digest in self.digests.iter() {
116+
hasher.update(digest);
117+
}
118+
119+
hasher.finalize().into()
120+
}
121+
NodeLeaf::Leaf => self.digests[0],
122+
}
123+
}
124+
125+
pub fn get_parent_index(&self) -> usize {
126+
self.parent_index
127+
}
128+
}
129+
80130
impl MerkleProof {
81131
/// Create a new Merkle proof as a read leaf.
82132
pub fn leaf_read(data: Vec<u8>) -> Self {
@@ -90,18 +140,43 @@ impl MerkleProof {
90140

91141
/// Compute the root hash of the Merkle proof.
92142
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::blake3_hash_bytes(data.as_slice()),
102-
},
103-
|(), leaves| Hash::combine(leaves),
104-
)
143+
let mut nodes: Vec<(&MerkleProof, usize)> = vec![(self, 0)];
144+
let mut hash_states: Vec<HashState> = vec![];
145+
146+
while let Some((node, parent_index)) = nodes.pop() {
147+
match node {
148+
Tree::Leaf(MerkleProofLeaf::Blind(hash)) => {
149+
hash_states.push(HashState::new_with_digest(
150+
NodeLeaf::Leaf,
151+
parent_index,
152+
hash.to_digest(),
153+
));
154+
}
155+
Tree::Leaf(MerkleProofLeaf::Read(data)) => {
156+
hash_states.push(HashState::new_with_digest(
157+
NodeLeaf::Leaf,
158+
parent_index,
159+
Hash::blake3_hash_bytes(data.as_slice()).to_digest(),
160+
));
161+
}
162+
Tree::Node(children) => {
163+
hash_states.push(HashState::new(NodeLeaf::Node, parent_index));
164+
let new_parent_index = hash_states.len() - 1;
165+
for child in children.iter() {
166+
nodes.push((child, new_parent_index));
167+
}
168+
}
169+
}
170+
}
171+
172+
while let Some(hash_state) = hash_states.pop() {
173+
if hash_states.is_empty() {
174+
return Hash::new(hash_state.get_digest());
175+
}
176+
hash_states[hash_state.get_parent_index()].push(hash_state.get_digest());
177+
}
178+
179+
unreachable!("hash_states can't be an empty vector");
105180
}
106181
}
107182

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 <contact@trili.tech>
2-
//
1+
// SPDX-FileCopyrightText: 2025 TriliTech <contact@trili.tech>
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: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,21 @@ use crate::foldable::Foldable;
88
use crate::foldable::NodeFold;
99
use crate::hash::Hash;
1010

11+
#[derive(Debug, Clone)]
12+
pub struct MerkleTreeLeafData {
13+
pub hash: Hash,
14+
pub access_info: bool,
15+
pub data: Vec<u8>,
16+
}
17+
1118
/// A variable-width Merkle tree with access metadata for leaves.
1219
///
1320
/// Values of this type are produced by the proof-generating backend to capture
1421
/// a snapshot of the machine state along with access information for leaves
1522
/// which hold data that was used in a particular evaluation step.
1623
#[derive(Debug, Clone)]
1724
pub enum MerkleTree {
18-
Leaf(Hash, bool, Vec<u8>),
25+
Leaf(MerkleTreeLeafData),
1926
Node(Hash, Vec<Self>),
2027
}
2128

@@ -38,14 +45,18 @@ impl MerkleTree {
3845
pub fn root_hash(&self) -> Hash {
3946
match self {
4047
Self::Node(hash, _) => *hash,
41-
Self::Leaf(hash, _, _) => *hash,
48+
Self::Leaf(MerkleTreeLeafData { hash, .. }) => *hash,
4249
}
4350
}
4451

4552
/// Creates a merkle tree which is a single leaf
4653
pub fn make_merkle_leaf(data: Vec<u8>, access_info: bool) -> Self {
4754
let hash = Hash::blake3_hash_bytes(&data);
48-
MerkleTree::Leaf(hash, access_info, data)
55+
MerkleTree::Leaf(MerkleTreeLeafData {
56+
hash,
57+
access_info,
58+
data,
59+
})
4960
}
5061

5162
/// Takes a list of children nodes and creates a
@@ -65,7 +76,9 @@ impl MerkleTree {
6576

6677
while let Some(node) = deque.pop_front() {
6778
let is_valid_hash = match node {
68-
Self::Leaf(hash, _, data) => &Hash::blake3_hash_bytes(data) == hash,
79+
Self::Leaf(MerkleTreeLeafData { hash, data, .. }) => {
80+
&Hash::blake3_hash_bytes(data) == hash
81+
}
6982
Self::Node(hash, children) => {
7083
let children_hashes: Vec<Hash> = children
7184
.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
@@ -47,7 +47,8 @@ use crate::state_backend::ManagerBase;
4747
use crate::state_backend::ManagerClone;
4848
use crate::state_backend::ProofTree;
4949
use crate::state_backend::proof_backend::ProofWrapper;
50-
use crate::state_backend::proof_backend::merkle::merkle_tree_to_merkle_proof;
50+
use crate::state_backend::proof_backend::merkle::CompressedMerkleTree;
51+
use crate::state_backend::proof_backend::merkle::merkle_tree_to_compressed_merkle_tree;
5152
use crate::state_backend::proof_backend::proof::Proof;
5253
use crate::state_backend::proof_backend::proof::deserialise_owned;
5354
use crate::struct_layout;
@@ -396,7 +397,9 @@ where
396397
let _ = self.input_request();
397398

398399
let merkle_tree = MerkleTree::from_foldable(self);
399-
let merkle_proof = merkle_tree_to_merkle_proof(merkle_tree);
400+
let compressed_merkle_tree: CompressedMerkleTree =
401+
merkle_tree_to_compressed_merkle_tree(merkle_tree);
402+
let merkle_proof: MerkleProof = compressed_merkle_tree.to_proof();
400403

401404
let final_hash = Hash::from_foldable(self);
402405
let proof = Proof::new(merkle_proof, final_hash);

src/riscv/lib/src/state_backend/layout.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -309,7 +309,8 @@ mod tests {
309309
use crate::state_backend::ProofLayout;
310310
use crate::state_backend::ProofPart;
311311
use crate::state_backend::proof_backend::ProofWrapper;
312-
use crate::state_backend::proof_backend::merkle::merkle_tree_to_merkle_proof;
312+
use crate::state_backend::proof_backend::merkle::CompressedMerkleTree;
313+
use crate::state_backend::proof_backend::merkle::merkle_tree_to_compressed_merkle_tree;
313314
use crate::state_backend::proof_backend::proof::deserialise_owned;
314315
use crate::state_backend::verify_backend::handle_stepper_panics;
315316

@@ -387,7 +388,9 @@ mod tests {
387388
assert_eq!(hash, tree_root_hash);
388389

389390
// Produce a proof
390-
let proof = merkle_tree_to_merkle_proof(tree);
391+
let compressed_merkle_tree: CompressedMerkleTree =
392+
merkle_tree_to_compressed_merkle_tree(tree);
393+
let proof = compressed_merkle_tree.to_proof();
391394
let proof_hash = proof.root_hash();
392395
assert_eq!(hash, proof_hash);
393396

src/riscv/lib/src/state_backend/proof_backend.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -469,6 +469,7 @@ mod tests {
469469

470470
use octez_riscv_data::hash::Hash;
471471
use octez_riscv_data::merkle_tree::MerkleTree;
472+
use octez_riscv_data::merkle_tree::MerkleTreeLeafData;
472473
use octez_riscv_data::mode::Normal;
473474
use proptest::array;
474475
use proptest::prop_assert;
@@ -547,7 +548,7 @@ mod tests {
547548
let merkle_tree = MerkleTree::from_foldable(&proof_cells);
548549
merkle_tree.check_root_hash();
549550
match merkle_tree {
550-
MerkleTree::Leaf(hash, access_info, _) => {
551+
MerkleTree::Leaf(MerkleTreeLeafData {hash, access_info, ..}) => {
551552
prop_assert_eq!(hash, initial_root_hash);
552553
prop_assert!(access_info);
553554
}
@@ -678,7 +679,7 @@ mod tests {
678679
let mut queue = VecDeque::with_capacity(LEAVES + 1);
679680

680681
let pages_tree = match merkle_tree {
681-
MerkleTree::Leaf(_, _, _) => panic!("Did not expect leaf"),
682+
MerkleTree::Leaf{ .. } => panic!("Did not expect leaf"),
682683
MerkleTree::Node(_, mut children) => {
683684
// The node for the pages is the second child.
684685
children.remove(1)
@@ -690,7 +691,7 @@ mod tests {
690691
while let Some(node) = queue.pop_front() {
691692
match node {
692693
MerkleTree::Node(_, children) => queue.extend(children),
693-
MerkleTree::Leaf(_, access_info, _) => {
694+
MerkleTree::Leaf(MerkleTreeLeafData{access_info, ..}) => {
694695
prop_assert_eq!(
695696
access_info,
696697
read_leaves.contains(&leaf) ||

0 commit comments

Comments
 (0)