Skip to content

Commit 8a66770

Browse files
kurtiscdanielpapp-trilitech
authored andcommitted
feat(dursto): construction of MerkleLayer
- Removes unnecessary `empty` constructor. - Changes `new` to be infallible. - Updates references of the fallible `new` and `empty` to use the infallible `new`. - Adds `Debug` derivation to `PersistenceLayer`. - Fills in `new` implementation to take a `PersistenceLayer`.
1 parent 318cafb commit 8a66770

File tree

16 files changed

+372
-336
lines changed

16 files changed

+372
-336
lines changed

data/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,4 @@ pub mod merkle_proof;
1010
pub mod merkle_tree;
1111
pub mod mode;
1212
pub mod serialisation;
13+
pub mod tree;

data/src/merkle_proof.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@
55

66
//! Merkle proofs
77
8+
pub mod proof_tree;
9+
pub mod tag;
10+
pub mod transform;
11+
812
use std::error;
913

1014
use bincode::Decode;
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
// SPDX-FileCopyrightText: 2025 TriliTech <contact@trili.tech>
2+
// SPDX-License-Identifier: MIT
3+
4+
use bincode::enc::write::Writer;
5+
6+
use super::tag::LeafTag;
7+
use super::tag::Tag;
8+
use super::transform::ModifyResult;
9+
use super::transform::impl_modify_map_collect;
10+
use crate::hash::Hash;
11+
use crate::tree::Tree;
12+
13+
/// Merkle proof tree structure.
14+
///
15+
/// Leaves can be read and/or written to.
16+
/// If a read was done, the content will be stored in the proof.
17+
/// If a write was done, the written content is not necessary since the semantics of running the step will
18+
/// deduce the written contents.
19+
///
20+
/// A proof will have the shape of a subtree of a [`MerkleTree`].
21+
/// The structure of the full [`MerkleTree`] is known statically (since it represents the whole state of the PVM)
22+
/// so the number of children of a node and the sizes of the leaves
23+
/// do not need to be stored in either the proof or its encoding.
24+
///
25+
/// [`MerkleTree`]: crate::merkle_tree::MerkleTree
26+
pub type MerkleProof = Tree<MerkleProofLeaf>;
27+
28+
impl bincode::Encode for MerkleProof {
29+
fn encode<E: bincode::enc::Encoder>(
30+
&self,
31+
encoder: &mut E,
32+
) -> Result<(), bincode::error::EncodeError> {
33+
let mut nodes = vec![self];
34+
35+
while let Some(node) = nodes.pop() {
36+
match node {
37+
Self::Node(trees) => {
38+
Tag::Node.encode(encoder)?;
39+
40+
// We add the children in reverse order so that when we pop them from the
41+
// `nodes` stack, they are in the original order.
42+
nodes.extend(trees.iter().rev());
43+
}
44+
45+
Self::Leaf(MerkleProofLeaf::Read(data)) => {
46+
Tag::Leaf(LeafTag::Read).encode(encoder)?;
47+
48+
// We want to write the raw data, and avoid the bincode length prefix. The decoder
49+
// will know how many bytes to read.
50+
encoder.writer().write(data.as_slice())?;
51+
}
52+
53+
Self::Leaf(MerkleProofLeaf::Blind(hash)) => {
54+
Tag::Leaf(LeafTag::Blind).encode(encoder)?;
55+
hash.encode(encoder)?;
56+
}
57+
}
58+
}
59+
60+
Ok(())
61+
}
62+
}
63+
64+
/// Type used to describe the leaves of a [`MerkleProof`].
65+
/// For more details see the documentation of [`MerkleProof`].
66+
#[derive(Clone, Debug, PartialEq)]
67+
pub enum MerkleProofLeaf {
68+
/// A leaf that is not read. It may be written.
69+
/// Contains the hash of the contents from initial state.
70+
///
71+
/// Note: a blinded leaf can correspond to a blinded subtree
72+
/// in a [`crate::merkle_tree::MerkleTree`] due to compression.
73+
Blind(Hash),
74+
/// A leaf that is read. It may also be written.
75+
/// Contains the read data from the initial state.
76+
/// The initial hash can be deduced based on the read data.
77+
Read(Vec<u8>),
78+
}
79+
80+
impl MerkleProof {
81+
/// Create a new Merkle proof as a read leaf.
82+
pub fn leaf_read(data: Vec<u8>) -> Self {
83+
MerkleProof::Leaf(MerkleProofLeaf::Read(data))
84+
}
85+
86+
/// Create a new Merkle proof as a blind leaf.
87+
pub fn leaf_blind(hash: Hash) -> Self {
88+
MerkleProof::Leaf(MerkleProofLeaf::Blind(hash))
89+
}
90+
91+
/// Compute the root hash of the Merkle proof.
92+
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+
)
105+
}
106+
}
107+
108+
impl From<&MerkleProof> for Tag {
109+
fn from(value: &MerkleProof) -> Self {
110+
match value {
111+
MerkleProof::Node(_) => Tag::Node,
112+
MerkleProof::Leaf(MerkleProofLeaf::Blind(_)) => Tag::Leaf(LeafTag::Blind),
113+
MerkleProof::Leaf(MerkleProofLeaf::Read(_)) => Tag::Leaf(LeafTag::Read),
114+
}
115+
}
116+
}
117+
118+
#[cfg(test)]
119+
mod tests {
120+
use super::*;
121+
122+
fn get_bincode_config() -> bincode::config::Configuration {
123+
bincode::config::standard()
124+
}
125+
126+
#[test]
127+
fn merkle_proofs_can_be_encoded() {
128+
let merkle_proofs = [
129+
MerkleProof::leaf_read([1, 2, 3].to_vec()),
130+
MerkleProof::leaf_blind(Hash::blake3_hash_bytes(&[1, 3, 4])),
131+
Tree::Node(
132+
[
133+
MerkleProof::leaf_read([1, 2, 3].to_vec()),
134+
MerkleProof::leaf_blind(Hash::blake3_hash_bytes(&[1, 3, 4])),
135+
]
136+
.to_vec(),
137+
),
138+
];
139+
for merkle_proof in merkle_proofs.iter() {
140+
bincode::encode_to_vec(merkle_proof, get_bincode_config())
141+
.expect("Failed to encode the merkle proof");
142+
}
143+
}
144+
145+
#[test]
146+
fn we_can_take_the_merkle_proof_of_the_root_hash() {
147+
let node = Tree::Node(
148+
[
149+
MerkleProof::leaf_read([1, 2, 3].to_vec()),
150+
MerkleProof::leaf_blind(Hash::blake3_hash_bytes(&[1, 3, 4])),
151+
]
152+
.to_vec(),
153+
);
154+
let _ = node.root_hash();
155+
}
156+
}

data/src/merkle_proof/tag.rs

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
// SPDX-FileCopyrightText: 2025 TriliTech <contact@trili.tech>
2+
// SPDX-License-Identifier: MIT
3+
4+
use bincode::Encode;
5+
6+
/// Get the bitmask formed of `n` ones.
7+
pub const fn ones(n: u64) -> u64 {
8+
// This function should not panic
9+
let sh_amt = 64_u64.saturating_sub(n);
10+
match n {
11+
0 => 0,
12+
_ => !0 >> sh_amt,
13+
}
14+
}
15+
16+
/// Tag of a node
17+
pub const TAG_NODE: u8 = 0b00;
18+
/// Tag of a blind leaf
19+
pub const TAG_BLIND: u8 = 0b10;
20+
/// Tag of a read leaf
21+
pub const TAG_READ: u8 = 0b11;
22+
23+
/// Number of bits used to represent a tag
24+
const TAG_BITS: u32 = 2;
25+
/// Number of tags that can fit in a single byte
26+
const TAGS_PER_BYTE: usize = u8::BITS as usize / TAG_BITS as usize;
27+
/// Bitmask for tags
28+
const TAG_MASK: u8 = ones(TAG_BITS as u64) as u8;
29+
30+
/// Return the offset of the `index`-th tag in a byte.
31+
const fn tag_offset(index: usize) -> usize {
32+
debug_assert!(index < TAGS_PER_BYTE);
33+
u8::BITS as usize - (index + 1) * TAG_BITS as usize
34+
}
35+
36+
#[derive(Clone, Copy, Debug, PartialEq)]
37+
pub enum LeafTag {
38+
Blind,
39+
Read,
40+
}
41+
42+
impl Encode for LeafTag {
43+
fn encode<E: bincode::enc::Encoder>(
44+
&self,
45+
encoder: &mut E,
46+
) -> Result<(), bincode::error::EncodeError> {
47+
match self {
48+
LeafTag::Blind => TAG_BLIND,
49+
LeafTag::Read => TAG_READ,
50+
}
51+
.encode(encoder)
52+
}
53+
}
54+
55+
/// The tag is invalid.
56+
#[derive(Debug, Clone, PartialEq, thiserror::Error)]
57+
#[error("Invalid tag")]
58+
pub struct InvalidTagError;
59+
60+
#[derive(Clone, Copy, Debug, PartialEq)]
61+
pub enum Tag {
62+
Node,
63+
Leaf(LeafTag),
64+
}
65+
66+
impl Tag {
67+
/// Obtain the parsed tags from the most significant bits to the lower ones.
68+
pub fn ordered_tags_from_u8(byte: u8) -> [Result<Tag, InvalidTagError>; TAGS_PER_BYTE] {
69+
core::array::from_fn(tag_offset)
70+
.map(|offset| (byte >> offset) & TAG_MASK)
71+
.map(Tag::try_from)
72+
}
73+
}
74+
75+
impl bincode::Encode for Tag {
76+
fn encode<E: bincode::enc::Encoder>(
77+
&self,
78+
encoder: &mut E,
79+
) -> Result<(), bincode::error::EncodeError> {
80+
match self {
81+
Tag::Node => TAG_NODE.encode(encoder),
82+
Tag::Leaf(leaf_tag) => leaf_tag.encode(encoder),
83+
}
84+
}
85+
}
86+
87+
impl<'de, C> bincode::BorrowDecode<'de, C> for Tag {
88+
fn borrow_decode<D: bincode::de::BorrowDecoder<'de, Context = C>>(
89+
decoder: &mut D,
90+
) -> Result<Self, bincode::error::DecodeError> {
91+
let byte = u8::borrow_decode(decoder)?;
92+
Tag::try_from(byte)
93+
.map_err(|error| bincode::error::DecodeError::OtherString(error.to_string()))
94+
}
95+
}
96+
97+
impl<C> bincode::Decode<C> for Tag {
98+
fn decode<D: bincode::de::Decoder<Context = C>>(
99+
decoder: &mut D,
100+
) -> Result<Self, bincode::error::DecodeError> {
101+
let byte = u8::decode(decoder)?;
102+
Tag::try_from(byte)
103+
.map_err(|error| bincode::error::DecodeError::OtherString(error.to_string()))
104+
}
105+
}
106+
107+
impl From<Tag> for u8 {
108+
fn from(value: Tag) -> Self {
109+
match value {
110+
Tag::Node => TAG_NODE,
111+
Tag::Leaf(leaf_tag) => match leaf_tag {
112+
LeafTag::Blind => TAG_BLIND,
113+
LeafTag::Read => TAG_READ,
114+
},
115+
}
116+
}
117+
}
118+
119+
impl From<LeafTag> for Tag {
120+
fn from(value: LeafTag) -> Self {
121+
Tag::Leaf(value)
122+
}
123+
}
124+
125+
impl TryFrom<u8> for Tag {
126+
type Error = InvalidTagError;
127+
128+
fn try_from(value: u8) -> Result<Self, Self::Error> {
129+
match value {
130+
TAG_NODE => Ok(Self::Node),
131+
TAG_BLIND => Ok(Self::Leaf(LeafTag::Blind)),
132+
TAG_READ => Ok(Self::Leaf(LeafTag::Read)),
133+
_ => Err(InvalidTagError),
134+
}
135+
}
136+
}
137+
138+
#[cfg(test)]
139+
mod tests {
140+
use super::*;
141+
142+
fn get_bincode_config() -> bincode::config::Configuration {
143+
bincode::config::standard()
144+
}
145+
146+
fn tag_encode_cycle_checker(tag: &Tag) {
147+
let mut encoded =
148+
bincode::encode_to_vec(tag, get_bincode_config()).expect("Failed to encode the tag");
149+
let (decoded, _): (Tag, usize) =
150+
bincode::decode_from_slice(encoded.as_mut_slice(), get_bincode_config())
151+
.expect("Failed to decode the tag");
152+
assert_eq!(*tag, decoded);
153+
}
154+
155+
#[test]
156+
fn can_encode_and_decode_tags() {
157+
let tags = [
158+
Tag::Node,
159+
Tag::Leaf(LeafTag::Blind),
160+
Tag::Leaf(LeafTag::Read),
161+
];
162+
for tag in tags.iter() {
163+
tag_encode_cycle_checker(tag);
164+
}
165+
}
166+
}

0 commit comments

Comments
 (0)