Skip to content

Commit 841cce4

Browse files
danielpapp-trilitechjakab922
authored andcommitted
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 d68008e commit 841cce4

File tree

11 files changed

+236
-211
lines changed

11 files changed

+236
-211
lines changed

data/src/hash.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,11 @@ pub struct Hash {
4949
}
5050

5151
impl Hash {
52+
/// Creates a new [`crate::hash::Hash`] object from a digest.
53+
pub fn new(digest: [u8; DIGEST_SIZE]) -> Self {
54+
Hash { digest }
55+
}
56+
5257
/// Hashes a byte slice into a [`struct@Hash`].
5358
pub fn hash_bytes(bytes: &[u8]) -> Self {
5459
let digest = blake3::hash(bytes).into();
@@ -89,6 +94,10 @@ impl Hash {
8994
pub const fn digest_len() -> usize {
9095
DIGEST_SIZE
9196
}
97+
/// Creates a new digest from a [`crate::hash::Hash`] object.
98+
pub fn to_digest(self) -> [u8; 32] {
99+
self.digest
100+
}
92101
}
93102

94103
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: 86 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,54 @@ 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+
digests: Vec<[u8; Hash::digest_len()]>,
88+
}
89+
90+
impl HashState {
91+
fn new(
92+
node_leaf: NodeLeaf,
93+
parent_index: usize,
94+
digests: Vec<[u8; Hash::digest_len()]>,
95+
) -> Self {
96+
Self {
97+
node_leaf,
98+
parent_index,
99+
digests,
100+
}
101+
}
102+
103+
fn push(&mut self, digest: [u8; Hash::digest_len()]) {
104+
self.digests.push(digest);
105+
}
106+
107+
fn get_digest(&self) -> [u8; Hash::digest_len()] {
108+
match self.node_leaf {
109+
NodeLeaf::Node => {
110+
let mut hasher = Hasher::default();
111+
112+
for digest in self.digests.iter() {
113+
hasher.update_with_bytes(digest);
114+
}
115+
116+
hasher.to_hash().into()
117+
}
118+
NodeLeaf::Leaf => self.digests[0],
119+
}
120+
}
121+
122+
fn get_parent_index(&self) -> usize {
123+
self.parent_index
124+
}
125+
}
126+
80127
impl MerkleProof {
81128
/// Create a new Merkle proof as a read leaf.
82129
pub fn leaf_read(data: Vec<u8>) -> Self {
@@ -90,18 +137,43 @@ impl MerkleProof {
90137

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

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/proof_backend.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,7 @@ mod tests {
263263
use octez_riscv_data::components::atom::Atom;
264264
use octez_riscv_data::hash::Hash;
265265
use octez_riscv_data::merkle_tree::MerkleTree;
266+
use octez_riscv_data::merkle_tree::MerkleTreeLeafData;
266267
use octez_riscv_data::mode::Normal;
267268
use proptest::array;
268269
use proptest::prop_assert;
@@ -328,7 +329,7 @@ mod tests {
328329
let merkle_tree = MerkleTree::from_foldable(&proof_atoms);
329330
merkle_tree.check_root_hash();
330331
match merkle_tree {
331-
MerkleTree::Leaf(hash, access_info, _) => {
332+
MerkleTree::Leaf(MerkleTreeLeafData {hash, access_info, ..}) => {
332333
prop_assert_eq!(hash, initial_root_hash);
333334
prop_assert!(access_info);
334335
}
@@ -459,7 +460,7 @@ mod tests {
459460
let mut queue = VecDeque::with_capacity(LEAVES + 1);
460461

461462
let pages_tree = match merkle_tree {
462-
MerkleTree::Leaf(_, _, _) => panic!("Did not expect leaf"),
463+
MerkleTree::Leaf{ .. } => panic!("Did not expect leaf"),
463464
MerkleTree::Node(_, mut children) => {
464465
// The node for the pages is the second child.
465466
children.remove(1)
@@ -471,7 +472,7 @@ mod tests {
471472
while let Some(node) = queue.pop_front() {
472473
match node {
473474
MerkleTree::Node(_, children) => queue.extend(children),
474-
MerkleTree::Leaf(_, access_info, _) => {
475+
MerkleTree::Leaf(MerkleTreeLeafData{access_info, ..}) => {
475476
prop_assert_eq!(
476477
access_info,
477478
read_leaves.contains(&leaf) ||

0 commit comments

Comments
 (0)