Skip to content

Commit 8a16efd

Browse files
starknet_patricia: from merkle node to preimage
1 parent 4dec46f commit 8a16efd

File tree

5 files changed

+173
-1
lines changed

5 files changed

+173
-1
lines changed

Cargo.lock

Lines changed: 66 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,7 @@ simple_logger = "4.0.0"
353353
socket2 = "0.5.8"
354354
starknet-core = "0.16.0"
355355
starknet-crypto = "0.8.1"
356+
starknet-rust-core = "0.16.0"
356357
starknet-types-core = "0.2.4"
357358
starknet_api = { path = "crates/starknet_api", version = "0.0.0" }
358359
starknet_committer.path = "crates/starknet_committer"

crates/starknet_patricia/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ serde_json.workspace = true
3030
starknet-types-core.workspace = true
3131
starknet_api.workspace = true
3232
starknet_patricia_storage.workspace = true
33+
starknet-rust-core.workspace = true
3334
strum.workspace = true
3435
strum_macros.workspace = true
3536
thiserror.workspace = true

crates/starknet_patricia/src/patricia_merkle_tree/node_data/inner_node.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use std::collections::HashMap;
22

33
use ethnum::U256;
44
use starknet_api::hash::HashOutput;
5+
use starknet_rust_core::types::MerkleNode;
56
use starknet_types_core::felt::Felt;
67

78
use crate::patricia_merkle_tree::node_data::errors::{
@@ -238,6 +239,39 @@ impl Preimage {
238239
}
239240
}
240241

242+
impl From<&MerkleNode> for Preimage {
243+
fn from(node: &MerkleNode) -> Self {
244+
match node {
245+
MerkleNode::BinaryNode(binary_node) => Preimage::Binary(BinaryData {
246+
left_hash: HashOutput(binary_node.left),
247+
right_hash: HashOutput(binary_node.right),
248+
}),
249+
MerkleNode::EdgeNode(edge_node) => {
250+
let length = u8::try_from(edge_node.length).unwrap_or_else(|_| {
251+
panic!(
252+
"EdgeNode length {} exceeds u8::MAX when converting to Preimage",
253+
edge_node.length
254+
)
255+
});
256+
Preimage::Edge(EdgeData {
257+
bottom_hash: HashOutput(edge_node.child),
258+
path_to_bottom: PathToBottom::new(
259+
EdgePath(U256::from_be_bytes(edge_node.path.to_bytes_be())),
260+
EdgePathLength(length),
261+
)
262+
.unwrap_or_else(|_| {
263+
panic!(
264+
"Failed to create PathToBottom from MerkleNode edge: path={:?}, \
265+
length={}",
266+
edge_node.path, edge_node.length
267+
)
268+
}),
269+
})
270+
}
271+
}
272+
}
273+
}
274+
241275
impl TryFrom<&Vec<Felt>> for Preimage {
242276
type Error = PreimageError;
243277

crates/starknet_patricia/src/patricia_merkle_tree/node_data/inner_node_tests.rs

Lines changed: 71 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,17 @@
11
use ethnum::U256;
22
use rstest::rstest;
3+
use starknet_api::hash::HashOutput;
4+
use starknet_rust_core::types::{BinaryNode, EdgeNode, MerkleNode};
5+
use starknet_types_core::felt::Felt;
36

4-
use crate::patricia_merkle_tree::node_data::inner_node::{EdgePathLength, PathToBottom};
7+
use crate::patricia_merkle_tree::node_data::inner_node::{
8+
BinaryData,
9+
EdgeData,
10+
EdgePath,
11+
EdgePathLength,
12+
PathToBottom,
13+
Preimage,
14+
};
515

616
#[rstest]
717
#[case(PathToBottom::from("1011"), 1, PathToBottom::from("011"))]
@@ -20,3 +30,63 @@ fn test_remove_first_edges(
2030
expected
2131
);
2232
}
33+
34+
#[rstest]
35+
#[case(Felt::ONE, Felt::TWO)]
36+
#[case(Felt::ZERO, Felt::ZERO)]
37+
#[case(Felt::from(0x1234_u128), Felt::from(0xABCD_u128))]
38+
fn test_preimage_from_binary_merkle_node(#[case] left: Felt, #[case] right: Felt) {
39+
let merkle_node = MerkleNode::BinaryNode(BinaryNode { left, right });
40+
41+
let preimage = Preimage::from(&merkle_node);
42+
43+
let expected =
44+
Preimage::Binary(BinaryData { left_hash: HashOutput(left), right_hash: HashOutput(right) });
45+
assert_eq!(preimage, expected);
46+
}
47+
48+
#[rstest]
49+
#[case(Felt::ONE, Felt::from(0b101_u128), 3)]
50+
#[case(Felt::from(0xBEEF_u128), Felt::from(42_u128), 7)]
51+
#[case(Felt::ZERO, Felt::ZERO, 0)]
52+
fn test_preimage_from_edge_merkle_node(
53+
#[case] child: Felt,
54+
#[case] path: Felt,
55+
#[case] length: u64,
56+
) {
57+
let merkle_node = MerkleNode::EdgeNode(EdgeNode { child, path, length });
58+
59+
let preimage = Preimage::from(&merkle_node);
60+
61+
let expected = Preimage::Edge(EdgeData {
62+
bottom_hash: HashOutput(child),
63+
path_to_bottom: PathToBottom::new(
64+
EdgePath(U256::from_be_bytes(path.to_bytes_be())),
65+
EdgePathLength::new(u8::try_from(length).unwrap()).unwrap(),
66+
)
67+
.unwrap(),
68+
});
69+
assert_eq!(preimage, expected);
70+
}
71+
72+
#[test]
73+
#[should_panic(expected = "EdgeNode length 256 exceeds u8::MAX")]
74+
fn test_preimage_from_edge_merkle_node_length_exceeds_u8() {
75+
let merkle_node =
76+
MerkleNode::EdgeNode(EdgeNode { child: Felt::ONE, path: Felt::ZERO, length: 256 });
77+
78+
let _ = Preimage::from(&merkle_node);
79+
}
80+
81+
#[test]
82+
#[should_panic(expected = "Failed to create PathToBottom from MerkleNode edge")]
83+
fn test_preimage_from_edge_merkle_node_path_mismatch() {
84+
// Path 0b1111 (4 bits) with length 2 should fail - path is too long for the stated length.
85+
let merkle_node = MerkleNode::EdgeNode(EdgeNode {
86+
child: Felt::ONE,
87+
path: Felt::from(0b1111_u128),
88+
length: 2,
89+
});
90+
91+
let _ = Preimage::from(&merkle_node);
92+
}

0 commit comments

Comments
 (0)