Skip to content

Commit 93c06bd

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 5985f20 commit 93c06bd

File tree

11 files changed

+235
-213
lines changed

11 files changed

+235
-213
lines changed

data/src/hash.rs

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

5959
impl Hash {
60+
/// Creates a new [`crate::hash::Hash`] object from a digest.
61+
pub fn new(digest: [u8; DIGEST_SIZE]) -> Self {
62+
Hash { digest }
63+
}
64+
6065
/// Hash a slice of bytes
6166
pub fn blake3_hash_bytes(bytes: &[u8]) -> Self {
6267
let digest = blake3::hash(bytes).into();
@@ -108,6 +113,11 @@ impl Hash {
108113
pub fn from_foldable(foldable: &impl Foldable<HashFold>) -> Self {
109114
foldable.fold(HashFold)
110115
}
116+
117+
/// Creates a new digest from a [`crate::hash::Hash`] object.
118+
pub fn to_digest(self) -> [u8; 32] {
119+
self.digest
120+
}
111121
}
112122

113123
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: 77 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,50 @@ 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+
fn new(node_leaf: NodeLeaf, parent_index: usize, digests: Vec<[u8; 32]>) -> Self {
91+
Self {
92+
node_leaf,
93+
parent_index,
94+
digests,
95+
}
96+
}
97+
98+
fn push(&mut self, digest: [u8; 32]) {
99+
self.digests.push(digest);
100+
}
101+
102+
fn get_digest(&self) -> [u8; 32] {
103+
match self.node_leaf {
104+
NodeLeaf::Node => {
105+
let mut hasher = blake3::Hasher::new();
106+
107+
for digest in self.digests.iter() {
108+
hasher.update(digest);
109+
}
110+
111+
hasher.finalize().into()
112+
}
113+
NodeLeaf::Leaf => self.digests[0],
114+
}
115+
}
116+
117+
fn get_parent_index(&self) -> usize {
118+
self.parent_index
119+
}
120+
}
121+
80122
impl MerkleProof {
81123
/// Create a new Merkle proof as a read leaf.
82124
pub fn leaf_read(data: Vec<u8>) -> Self {
@@ -90,18 +132,39 @@ impl MerkleProof {
90132

91133
/// Compute the root hash of the Merkle proof.
92134
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-
)
135+
let mut nodes: Vec<(&MerkleProof, usize)> = vec![(self, 0)];
136+
let mut hash_states: Vec<HashState> = vec![];
137+
138+
while let Some((node, parent_index)) = nodes.pop() {
139+
match node {
140+
Tree::Leaf(MerkleProofLeaf::Blind(hash)) => {
141+
hash_states.push(HashState::new(NodeLeaf::Leaf, parent_index, vec![
142+
hash.to_digest(),
143+
]));
144+
}
145+
Tree::Leaf(MerkleProofLeaf::Read(data)) => {
146+
hash_states.push(HashState::new(NodeLeaf::Leaf, parent_index, vec![
147+
Hash::blake3_hash_bytes(data.as_slice()).to_digest(),
148+
]));
149+
}
150+
Tree::Node(children) => {
151+
hash_states.push(HashState::new(NodeLeaf::Node, parent_index, vec![]));
152+
let new_parent_index = hash_states.len() - 1;
153+
for child in children.iter() {
154+
nodes.push((child, new_parent_index));
155+
}
156+
}
157+
}
158+
}
159+
160+
while let Some(hash_state) = hash_states.pop() {
161+
if hash_states.is_empty() {
162+
return Hash::new(hash_state.get_digest());
163+
}
164+
hash_states[hash_state.get_parent_index()].push(hash_state.get_digest());
165+
}
166+
167+
unreachable!("hash_states can't be an empty vector");
105168
}
106169
}
107170

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: 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::blake3_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::blake3_hash_bytes(data) == hash,
80+
Self::Leaf(MerkleTreeLeafData { hash, data, .. }) => {
81+
&Hash::blake3_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::ManagerBase;
5151
use crate::state_backend::ManagerClone;
5252
use crate::state_backend::ProofTree;
5353
use crate::state_backend::proof_backend::ProofWrapper;
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
use crate::struct_layout;
@@ -400,7 +401,9 @@ where
400401
let _ = self.input_request();
401402

402403
let merkle_tree = MerkleTree::from_foldable(self);
403-
let merkle_proof = merkle_tree_to_merkle_proof(merkle_tree);
404+
let compressed_merkle_tree: CompressedMerkleTree =
405+
merkle_tree_to_compressed_merkle_tree(merkle_tree);
406+
let merkle_proof: MerkleProof = compressed_merkle_tree.to_proof();
404407

405408
let final_hash = Hash::from_foldable(self);
406409
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
@@ -287,7 +287,8 @@ mod tests {
287287
use crate::state_backend::ProofLayout;
288288
use crate::state_backend::ProofPart;
289289
use crate::state_backend::proof_backend::ProofWrapper;
290-
use crate::state_backend::proof_backend::merkle::merkle_tree_to_merkle_proof;
290+
use crate::state_backend::proof_backend::merkle::CompressedMerkleTree;
291+
use crate::state_backend::proof_backend::merkle::merkle_tree_to_compressed_merkle_tree;
291292
use crate::state_backend::proof_backend::proof::deserialise_owned;
292293
use crate::state_backend::verify_backend::handle_stepper_panics;
293294

@@ -365,7 +366,9 @@ mod tests {
365366
assert_eq!(hash, tree_root_hash);
366367

367368
// Produce a proof
368-
let proof = merkle_tree_to_merkle_proof(tree);
369+
let compressed_merkle_tree: CompressedMerkleTree =
370+
merkle_tree_to_compressed_merkle_tree(tree);
371+
let proof = compressed_merkle_tree.to_proof();
369372
let proof_hash = proof.root_hash();
370373
assert_eq!(hash, proof_hash);
371374

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)