Skip to content

Commit 7a0236b

Browse files
committed
starknet_committer,starknet_patricia: layout-based original tree creation tests
1 parent c834010 commit 7a0236b

File tree

9 files changed

+664
-238
lines changed

9 files changed

+664
-238
lines changed

Cargo.lock

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

crates/starknet_committer/Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ hex.workspace = true
1919
pretty_assertions.workspace = true
2020
rand.workspace = true
2121
rand_distr.workspace = true
22-
rstest.workspace = true
2322
serde = { workspace = true, features = ["derive"] }
2423
serde_json.workspace = true
2524
starknet-types-core = { workspace = true, features = ["hash", "papyrus-serialization"] }
@@ -32,6 +31,9 @@ tokio = { workspace = true, features = ["rt"] }
3231
tracing.workspace = true
3332

3433
[dev-dependencies]
34+
async-recursion.workspace = true
35+
rstest.workspace = true
36+
rstest_reuse.workspace = true
3537
starknet_api = { workspace = true, features = ["testing"] }
3638
starknet_patricia = { workspace = true, features = ["testing"] }
3739

crates/starknet_committer/src/db.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
#[cfg(test)]
2+
pub mod create_original_skeleton_tests;
13
pub mod db_layout;
24
#[cfg(any(feature = "testing", test))]
35
pub mod external_test_utils;
Lines changed: 294 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,294 @@
1+
use std::collections::HashMap;
2+
3+
use rstest_reuse::template;
4+
use starknet_api::hash::HashOutput;
5+
use starknet_patricia::db_layout::NodeLayout;
6+
use starknet_patricia::patricia_merkle_tree::node_data::leaf::{Leaf, LeafModifications};
7+
use starknet_patricia::patricia_merkle_tree::original_skeleton_tree::node::OriginalSkeletonNode;
8+
use starknet_patricia::patricia_merkle_tree::types::{NodeIndex, SortedLeafIndices, SubTreeHeight};
9+
use starknet_patricia_storage::db_object::{EmptyKeyContext, HasStaticPrefix};
10+
use starknet_patricia_storage::map_storage::MapStorage;
11+
12+
use crate::db::trie_traversal::create_original_skeleton_tree;
13+
use crate::patricia_merkle_tree::tree::OriginalSkeletonTrieConfig;
14+
15+
pub(crate) mod case_helpers {
16+
use std::collections::HashMap;
17+
18+
use starknet_api::hash::HashOutput;
19+
use starknet_patricia::patricia_merkle_tree::external_test_utils::{
20+
create_binary_entry_from_u128,
21+
create_binary_skeleton_node,
22+
create_edge_entry_from_u128,
23+
create_edge_skeleton_node,
24+
create_expected_skeleton_nodes,
25+
create_root_edge_entry,
26+
create_unmodified_subtree_skeleton_node,
27+
AdditionHash,
28+
MockLeaf,
29+
};
30+
use starknet_patricia::patricia_merkle_tree::node_data::leaf::LeafModifications;
31+
use starknet_patricia::patricia_merkle_tree::original_skeleton_tree::node::OriginalSkeletonNode;
32+
use starknet_patricia::patricia_merkle_tree::types::{NodeIndex, SubTreeHeight};
33+
use starknet_patricia_storage::db_object::{DBObject, EmptyKeyContext};
34+
use starknet_patricia_storage::map_storage::MapStorage;
35+
use starknet_patricia_storage::storage_trait::{DbHashMap, DbKey, DbValue};
36+
use starknet_types_core::felt::Felt;
37+
38+
pub(crate) fn create_mock_leaf_entry(val: u128) -> (DbKey, DbValue) {
39+
let leaf = MockLeaf(Felt::from(val));
40+
(leaf.get_db_key(&EmptyKeyContext, &leaf.0.to_bytes_be()), leaf.serialize().unwrap())
41+
}
42+
43+
fn create_mock_leaf_modifications(
44+
leaf_modifications: Vec<(u128, u128)>,
45+
) -> LeafModifications<MockLeaf> {
46+
leaf_modifications
47+
.into_iter()
48+
.map(|(idx, val)| (NodeIndex::from(idx), MockLeaf(Felt::from(val))))
49+
.collect()
50+
}
51+
52+
pub(crate) struct CreateTreeCase {
53+
pub storage: MapStorage,
54+
pub leaf_modifications: LeafModifications<MockLeaf>,
55+
pub root_hash: HashOutput,
56+
pub expected_skeleton_nodes: HashMap<NodeIndex, OriginalSkeletonNode>,
57+
pub subtree_height: SubTreeHeight,
58+
}
59+
60+
pub(crate) fn simple_tree_of_height_3() -> CreateTreeCase {
61+
CreateTreeCase {
62+
storage: MapStorage(DbHashMap::from([
63+
create_root_edge_entry(50, SubTreeHeight::new(3)),
64+
create_binary_entry_from_u128::<AdditionHash>(8, 9),
65+
create_edge_entry_from_u128::<AdditionHash>(11, 1, 1),
66+
create_binary_entry_from_u128::<AdditionHash>(17, 13),
67+
create_edge_entry_from_u128::<AdditionHash>(15, 3, 2),
68+
create_binary_entry_from_u128::<AdditionHash>(30, 20),
69+
create_mock_leaf_entry(8),
70+
create_mock_leaf_entry(9),
71+
create_mock_leaf_entry(11),
72+
create_mock_leaf_entry(15),
73+
])),
74+
leaf_modifications: create_mock_leaf_modifications(vec![(8, 8), (10, 3), (13, 2)]),
75+
root_hash: HashOutput(Felt::from(50_u128 + 248_u128)),
76+
expected_skeleton_nodes: create_expected_skeleton_nodes(
77+
vec![
78+
create_binary_skeleton_node(1),
79+
create_binary_skeleton_node(2),
80+
create_edge_skeleton_node(3, 3, 2),
81+
create_binary_skeleton_node(4),
82+
create_edge_skeleton_node(5, 1, 1),
83+
create_unmodified_subtree_skeleton_node(9, 9),
84+
create_unmodified_subtree_skeleton_node(15, 15),
85+
create_unmodified_subtree_skeleton_node(11, 11),
86+
],
87+
3,
88+
),
89+
subtree_height: SubTreeHeight::new(3),
90+
}
91+
}
92+
93+
pub(crate) fn another_simple_tree_of_height_3() -> CreateTreeCase {
94+
CreateTreeCase {
95+
storage: MapStorage(DbHashMap::from([
96+
create_root_edge_entry(29, SubTreeHeight::new(3)),
97+
create_binary_entry_from_u128::<AdditionHash>(10, 2),
98+
create_edge_entry_from_u128::<AdditionHash>(3, 1, 1),
99+
create_binary_entry_from_u128::<AdditionHash>(4, 7),
100+
create_edge_entry_from_u128::<AdditionHash>(12, 0, 1),
101+
create_binary_entry_from_u128::<AdditionHash>(5, 11),
102+
create_binary_entry_from_u128::<AdditionHash>(13, 16),
103+
create_mock_leaf_entry(10),
104+
create_mock_leaf_entry(2),
105+
create_mock_leaf_entry(3),
106+
create_mock_leaf_entry(4),
107+
create_mock_leaf_entry(7),
108+
])),
109+
leaf_modifications: create_mock_leaf_modifications(vec![(8, 5), (11, 1), (13, 3)]),
110+
root_hash: HashOutput(Felt::from(29_u128 + 248_u128)),
111+
expected_skeleton_nodes: create_expected_skeleton_nodes(
112+
vec![
113+
create_binary_skeleton_node(1),
114+
create_edge_skeleton_node(2, 0, 1),
115+
create_binary_skeleton_node(3),
116+
create_binary_skeleton_node(4),
117+
create_edge_skeleton_node(6, 1, 1),
118+
create_unmodified_subtree_skeleton_node(7, 11),
119+
create_unmodified_subtree_skeleton_node(9, 2),
120+
],
121+
3,
122+
),
123+
subtree_height: SubTreeHeight::new(3),
124+
}
125+
}
126+
127+
pub(crate) fn tree_of_height_4_with_long_edge() -> CreateTreeCase {
128+
CreateTreeCase {
129+
storage: MapStorage(DbHashMap::from([
130+
create_root_edge_entry(116, SubTreeHeight::new(4)),
131+
create_binary_entry_from_u128::<AdditionHash>(11, 13),
132+
create_edge_entry_from_u128::<AdditionHash>(5, 0, 1),
133+
create_binary_entry_from_u128::<AdditionHash>(19, 40),
134+
create_edge_entry_from_u128::<AdditionHash>(20, 3, 2),
135+
create_binary_entry_from_u128::<AdditionHash>(6, 59),
136+
create_edge_entry_from_u128::<AdditionHash>(24, 0, 2),
137+
create_binary_entry_from_u128::<AdditionHash>(25, 65),
138+
create_binary_entry_from_u128::<AdditionHash>(26, 90),
139+
create_mock_leaf_entry(11),
140+
create_mock_leaf_entry(13),
141+
create_mock_leaf_entry(20),
142+
create_mock_leaf_entry(5),
143+
create_mock_leaf_entry(19),
144+
create_mock_leaf_entry(40),
145+
])),
146+
leaf_modifications: create_mock_leaf_modifications(vec![
147+
(18, 5),
148+
(25, 1),
149+
(29, 15),
150+
(30, 19),
151+
]),
152+
root_hash: HashOutput(Felt::from(116_u128 + 247_u128)),
153+
expected_skeleton_nodes: create_expected_skeleton_nodes(
154+
vec![
155+
create_binary_skeleton_node(1),
156+
create_edge_skeleton_node(2, 0, 2),
157+
create_binary_skeleton_node(3),
158+
create_edge_skeleton_node(6, 3, 2),
159+
create_binary_skeleton_node(7),
160+
create_unmodified_subtree_skeleton_node(8, 24),
161+
create_edge_skeleton_node(14, 0, 1),
162+
create_binary_skeleton_node(15),
163+
create_unmodified_subtree_skeleton_node(27, 20),
164+
create_unmodified_subtree_skeleton_node(28, 5),
165+
create_unmodified_subtree_skeleton_node(31, 40),
166+
],
167+
4,
168+
),
169+
subtree_height: SubTreeHeight::new(4),
170+
}
171+
}
172+
}
173+
174+
pub(crate) async fn test_create_original_skeleton<L, Layout>(
175+
storage: &mut MapStorage,
176+
leaf_modifications: &LeafModifications<L>,
177+
root_hash: HashOutput,
178+
expected_skeleton_nodes: &HashMap<NodeIndex, OriginalSkeletonNode>,
179+
subtree_height: SubTreeHeight,
180+
compare_modified_leaves: bool,
181+
) where
182+
L: Leaf + HasStaticPrefix<KeyContext = EmptyKeyContext>,
183+
Layout: for<'a> NodeLayout<'a, L>,
184+
{
185+
let leaf_modifications: LeafModifications<L> = leaf_modifications
186+
.iter()
187+
.map(|(idx, leaf)| (NodeIndex::from_subtree_index(*idx, subtree_height), leaf.clone()))
188+
.collect();
189+
190+
let config = OriginalSkeletonTrieConfig::new_for_tests(compare_modified_leaves);
191+
192+
let mut sorted_leaf_indices: Vec<NodeIndex> = leaf_modifications.keys().copied().collect();
193+
let sorted_leaf_indices = SortedLeafIndices::new(&mut sorted_leaf_indices);
194+
195+
let skeleton_tree = create_original_skeleton_tree::<L, Layout>(
196+
storage,
197+
root_hash,
198+
sorted_leaf_indices,
199+
&config,
200+
&leaf_modifications,
201+
None,
202+
&EmptyKeyContext,
203+
)
204+
.await
205+
.unwrap();
206+
207+
assert_eq!(&skeleton_tree.nodes, expected_skeleton_nodes);
208+
}
209+
210+
#[template]
211+
#[rstest]
212+
// This test uses addition hash for simplicity (i.e hash(a,b) = a + b).
213+
/// Old tree structure:
214+
///
215+
/// 50
216+
/// / \
217+
/// 30 20
218+
/// / \ \
219+
/// 17 13 *
220+
/// / \ \ \
221+
/// 8 9 11 15
222+
///
223+
/// Modified leaves indices: [8, 10, 13]
224+
///
225+
/// Expected skeleton:
226+
///
227+
/// B
228+
/// / \
229+
/// B E
230+
/// / \ \
231+
/// B E *
232+
/// / \ \ \
233+
/// NZ 9 11 15
234+
235+
#[case::simple_tree_of_height_3(
236+
crate::db::create_original_skeleton_tests::case_helpers::simple_tree_of_height_3()
237+
)]
238+
/// Old tree structure:
239+
///
240+
/// 29
241+
/// / \
242+
/// 13 16
243+
/// / / \
244+
/// 12 5 11
245+
/// / \ \ / \
246+
/// 10 2 3 4 7
247+
///
248+
/// Modified leaves indices: [8, 11, 13]
249+
///
250+
/// Expected skeleton:
251+
///
252+
/// B
253+
/// / \
254+
/// E B
255+
/// / / \
256+
/// B E E
257+
/// / \ \ \
258+
/// NZ 2 NZ NZ
259+
#[case::another_simple_tree_of_height_3(
260+
crate::db::create_original_skeleton_tests::case_helpers::another_simple_tree_of_height_3()
261+
)]
262+
/// Old tree structure:
263+
///
264+
/// 116
265+
/// / \
266+
/// 26 90
267+
/// / / \
268+
/// / 25 65
269+
/// / \ / \
270+
/// 24 \ 6 59
271+
/// / \ \ / / \
272+
/// 11 13 20 5 19 40
273+
///
274+
/// Modified leaves indices: [18, 25, 29, 30]
275+
///
276+
/// Expected skeleton:
277+
///
278+
/// B
279+
/// / \
280+
/// E B
281+
/// / / \
282+
/// / E B
283+
/// / \ / \
284+
/// 24 \ E B
285+
/// \ / \
286+
/// 20 5 40
287+
#[case::tree_of_height_4_with_long_edge(
288+
crate::db::create_original_skeleton_tests::case_helpers::tree_of_height_4_with_long_edge()
289+
)]
290+
pub fn create_tree_cases(
291+
#[case] mut case: case_helpers::CreateTreeCase,
292+
#[values(true, false)] compare_modified_leaves: bool,
293+
) {
294+
}

0 commit comments

Comments
 (0)