Skip to content

Commit eb4397f

Browse files
committed
starknet_committer: abstract original tree creation
1 parent c4b9c98 commit eb4397f

File tree

13 files changed

+504
-440
lines changed

13 files changed

+504
-440
lines changed

crates/starknet_committer/src/db.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ pub mod external_test_utils;
44
pub mod facts_db;
55
pub mod forest_trait;
66
pub mod index_db;
7+
pub mod trie_traversal;
Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,26 @@
1+
use starknet_api::hash::HashOutput;
12
use starknet_patricia::patricia_merkle_tree::filled_tree::node::FilledNode;
23
use starknet_patricia::patricia_merkle_tree::node_data::leaf::Leaf;
4+
use starknet_patricia::patricia_merkle_tree::traversal::SubTreeTrait;
5+
use starknet_patricia::patricia_merkle_tree::types::{NodeIndex, SortedLeafIndices};
6+
use starknet_patricia_storage::db_object::HasStaticPrefix;
37
use starknet_patricia_storage::errors::DeserializationError;
48
use starknet_patricia_storage::storage_trait::DbValue;
59

6-
pub trait NodeLayout<L: Leaf> {
10+
use crate::db::index_db::leaves::TrieType;
11+
12+
pub trait NodeLayout<'a, L: Leaf> {
713
type ChildData: Copy;
814
type DeserializationContext;
15+
type SubTree: SubTreeTrait<'a, NodeData = Self::ChildData, NodeContext = Self::DeserializationContext>;
916
fn deserialize_node(
1017
value: &DbValue,
1118
deserialize_context: &Self::DeserializationContext,
1219
) -> Result<FilledNode<L, Self::ChildData>, DeserializationError>;
20+
fn create_subtree(
21+
sorted_leaf_indices: SortedLeafIndices<'a>,
22+
root_index: NodeIndex,
23+
root_hash: HashOutput,
24+
) -> Self::SubTree;
25+
fn generate_key_context(trie_type: TrieType) -> <L as HasStaticPrefix>::KeyContext;
1326
}

crates/starknet_committer/src/db/external_test_utils.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ use starknet_patricia::patricia_merkle_tree::updated_skeleton_tree::tree::{
1818
use starknet_patricia_storage::db_object::{EmptyKeyContext, HasStaticPrefix};
1919
use starknet_patricia_storage::map_storage::MapStorage;
2020

21-
use crate::db::facts_db::create_facts_tree::create_original_skeleton_tree;
21+
use crate::db::facts_db::db::FactsNodeLayout;
22+
use crate::db::trie_traversal::create_original_skeleton_tree;
2223

2324
pub async fn tree_computation_flow<L, TH>(
2425
leaf_modifications: LeafModifications<L>,
@@ -32,7 +33,7 @@ where
3233
{
3334
let mut sorted_leaf_indices: Vec<NodeIndex> = leaf_modifications.keys().copied().collect();
3435
let sorted_leaf_indices = SortedLeafIndices::new(&mut sorted_leaf_indices);
35-
let mut original_skeleton = create_original_skeleton_tree(
36+
let mut original_skeleton = create_original_skeleton_tree::<L, FactsNodeLayout>(
3637
storage,
3738
root_hash,
3839
sorted_leaf_indices,
Lines changed: 11 additions & 260 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,17 @@
1-
use std::borrow::Borrow;
21
use std::collections::HashMap;
3-
use std::fmt::Debug;
42

53
use starknet_api::hash::HashOutput;
6-
use starknet_patricia::patricia_merkle_tree::node_data::inner_node::{
7-
BinaryData,
8-
EdgeData,
9-
NodeData,
10-
};
114
use starknet_patricia::patricia_merkle_tree::node_data::leaf::{Leaf, LeafModifications};
12-
use starknet_patricia::patricia_merkle_tree::original_skeleton_tree::config::{
13-
NoCompareOriginalSkeletonTrieConfig,
14-
OriginalSkeletonTreeConfig,
15-
};
16-
use starknet_patricia::patricia_merkle_tree::original_skeleton_tree::node::OriginalSkeletonNode;
5+
use starknet_patricia::patricia_merkle_tree::original_skeleton_tree::config::OriginalSkeletonTreeConfig;
176
use starknet_patricia::patricia_merkle_tree::original_skeleton_tree::tree::{
187
OriginalSkeletonTreeImpl,
198
OriginalSkeletonTreeResult,
209
};
21-
use starknet_patricia::patricia_merkle_tree::traversal::SubTreeTrait;
2210
use starknet_patricia::patricia_merkle_tree::types::{NodeIndex, SortedLeafIndices};
23-
use starknet_patricia_storage::db_object::HasStaticPrefix;
11+
use starknet_patricia_storage::db_object::{EmptyKeyContext, HasStaticPrefix};
2412
use starknet_patricia_storage::storage_trait::Storage;
25-
use tracing::warn;
2613

27-
use crate::db::db_layout::NodeLayout;
2814
use crate::db::facts_db::db::FactsNodeLayout;
29-
use crate::db::facts_db::traversal::get_roots_from_storage;
3015
use crate::db::facts_db::types::FactsSubTree;
3116
use crate::db::trie_traversal::fetch_nodes;
3217
use crate::patricia_merkle_tree::tree::OriginalSkeletonTrieDontCompareConfig;
@@ -35,188 +20,15 @@ use crate::patricia_merkle_tree::tree::OriginalSkeletonTrieDontCompareConfig;
3520
#[path = "create_facts_tree_test.rs"]
3621
pub mod create_facts_tree_test;
3722

38-
/// Logs out a warning of a trivial modification.
39-
macro_rules! log_trivial_modification {
40-
($index:expr, $value:expr) => {
41-
warn!("Encountered a trivial modification at index {:?}, with value {:?}", $index, $value);
42-
};
43-
}
44-
45-
/// Fetches the Patricia witnesses, required to build the original skeleton tree from storage.
46-
/// Given a list of subtrees, traverses towards their leaves and fetches all non-empty,
47-
/// unmodified nodes. If `compare_modified_leaves` is set, function logs out a warning when
48-
/// encountering a trivial modification. Fills the previous leaf values if it is not none.
49-
async fn fetch_nodes<'a, L, Layout, SubTree>(
50-
skeleton_tree: &mut OriginalSkeletonTreeImpl<'a>,
51-
subtrees: Vec<SubTree>,
52-
storage: &mut impl Storage,
53-
leaf_modifications: &LeafModifications<L>,
54-
config: &impl OriginalSkeletonTreeConfig<L>,
55-
mut previous_leaves: Option<&mut HashMap<NodeIndex, L>>,
56-
key_context: &<L as HasStaticPrefix>::KeyContext,
57-
) -> OriginalSkeletonTreeResult<()>
58-
where
59-
L: Leaf,
60-
Layout: NodeLayout<L>,
61-
SubTree: SubTreeTrait<'a, NodeData = Layout::ChildData, NodeContext = Layout::DeserializationContext>,
62-
{
63-
let mut current_subtrees = subtrees;
64-
let mut next_subtrees = Vec::new();
65-
while !current_subtrees.is_empty() {
66-
let should_fetch_modified_leaves =
67-
config.compare_modified_leaves() || previous_leaves.is_some();
68-
let filled_roots =
69-
get_roots_from_storage::<L, Layout>(&current_subtrees, storage, key_context).await?;
70-
for (filled_root, subtree) in filled_roots.into_iter().zip(current_subtrees.iter()) {
71-
match filled_root.data {
72-
// Binary node.
73-
NodeData::Binary(BinaryData { left_data, right_data }) => {
74-
if subtree.is_unmodified() {
75-
skeleton_tree.nodes.insert(
76-
subtree.get_root_index(),
77-
OriginalSkeletonNode::UnmodifiedSubTree(filled_root.hash),
78-
);
79-
continue;
80-
}
81-
skeleton_tree
82-
.nodes
83-
.insert(subtree.get_root_index(), OriginalSkeletonNode::Binary);
84-
let (left_subtree, right_subtree) =
85-
subtree.get_children_subtrees(left_data, right_data);
86-
87-
handle_subtree(
88-
skeleton_tree,
89-
&mut next_subtrees,
90-
left_subtree,
91-
left_data,
92-
should_fetch_modified_leaves,
93-
);
94-
handle_subtree(
95-
skeleton_tree,
96-
&mut next_subtrees,
97-
right_subtree,
98-
right_data,
99-
should_fetch_modified_leaves,
100-
)
101-
}
102-
// Edge node.
103-
NodeData::Edge(EdgeData { bottom_data, path_to_bottom }) => {
104-
skeleton_tree.nodes.insert(
105-
subtree.get_root_index(),
106-
OriginalSkeletonNode::Edge(path_to_bottom),
107-
);
108-
109-
// Parse bottom.
110-
let (bottom_subtree, previously_empty_leaves_indices) =
111-
subtree.get_bottom_subtree(&path_to_bottom, bottom_data);
112-
113-
if subtree.is_unmodified() {
114-
if !SubTree::should_traverse_unmodified_children() {
115-
skeleton_tree.nodes.insert(
116-
path_to_bottom.bottom_index(subtree.get_root_index()),
117-
OriginalSkeletonNode::UnmodifiedSubTree(
118-
SubTree::unmodified_child_hash(bottom_data).unwrap(),
119-
),
120-
);
121-
} else {
122-
// With index layout we need to traverse an unmodified bottom node.
123-
next_subtrees.push(bottom_subtree)
124-
}
125-
continue;
126-
}
127-
128-
if let Some(ref mut leaves) = previous_leaves {
129-
leaves.extend(
130-
previously_empty_leaves_indices
131-
.iter()
132-
.map(|idx| (*idx, L::default()))
133-
.collect::<HashMap<NodeIndex, L>>(),
134-
);
135-
}
136-
log_warning_for_empty_leaves(
137-
&previously_empty_leaves_indices,
138-
leaf_modifications,
139-
config,
140-
)?;
141-
142-
handle_subtree(
143-
skeleton_tree,
144-
&mut next_subtrees,
145-
bottom_subtree,
146-
bottom_data,
147-
should_fetch_modified_leaves,
148-
);
149-
}
150-
// Leaf node.
151-
NodeData::Leaf(previous_leaf) => {
152-
if subtree.is_unmodified() {
153-
skeleton_tree.nodes.insert(
154-
subtree.get_root_index(),
155-
OriginalSkeletonNode::UnmodifiedSubTree(filled_root.hash),
156-
);
157-
} else {
158-
let root_index = subtree.get_root_index();
159-
// Modified leaf.
160-
if config.compare_modified_leaves()
161-
&& L::compare(leaf_modifications, &root_index, &previous_leaf)?
162-
{
163-
log_trivial_modification!(root_index, previous_leaf);
164-
}
165-
// If previous values of modified leaves are requested, add this leaf.
166-
if let Some(ref mut leaves) = previous_leaves {
167-
leaves.insert(root_index, previous_leaf);
168-
}
169-
}
170-
}
171-
}
172-
}
173-
current_subtrees = next_subtrees;
174-
next_subtrees = Vec::new();
175-
}
176-
Ok(())
177-
}
178-
179-
pub async fn create_original_skeleton_tree<'a, L: Leaf>(
180-
storage: &mut impl Storage,
181-
root_hash: HashOutput,
182-
sorted_leaf_indices: SortedLeafIndices<'a>,
183-
config: &impl OriginalSkeletonTreeConfig<L>,
184-
leaf_modifications: &LeafModifications<L>,
185-
key_context: &<L as HasStaticPrefix>::KeyContext,
186-
) -> OriginalSkeletonTreeResult<OriginalSkeletonTreeImpl<'a>> {
187-
if sorted_leaf_indices.is_empty() {
188-
return Ok(OriginalSkeletonTreeImpl::create_unmodified(root_hash));
189-
}
190-
if root_hash == HashOutput::ROOT_OF_EMPTY_TREE {
191-
log_warning_for_empty_leaves(
192-
sorted_leaf_indices.get_indices(),
193-
leaf_modifications,
194-
config,
195-
)?;
196-
return Ok(OriginalSkeletonTreeImpl::create_empty(sorted_leaf_indices));
197-
}
198-
let main_subtree = FactsSubTree { sorted_leaf_indices, root_index: NodeIndex::ROOT, root_hash };
199-
let mut skeleton_tree = OriginalSkeletonTreeImpl { nodes: HashMap::new(), sorted_leaf_indices };
200-
fetch_nodes::<L, FactsNodeLayout, FactsSubTree<'a>>(
201-
&mut skeleton_tree,
202-
vec![main_subtree],
203-
storage,
204-
leaf_modifications,
205-
config,
206-
None,
207-
key_context,
208-
)
209-
.await?;
210-
Ok(skeleton_tree)
211-
}
212-
213-
pub async fn create_original_skeleton_tree_and_get_previous_leaves<'a, L: Leaf>(
23+
pub async fn create_original_skeleton_tree_and_get_previous_leaves<
24+
'a,
25+
L: Leaf + HasStaticPrefix<KeyContext = EmptyKeyContext>,
26+
>(
21427
storage: &mut impl Storage,
21528
root_hash: HashOutput,
21629
sorted_leaf_indices: SortedLeafIndices<'a>,
21730
leaf_modifications: &LeafModifications<L>,
218-
config: &impl OriginalSkeletonTreeConfig<L>,
219-
key_context: &<L as HasStaticPrefix>::KeyContext,
31+
config: &impl OriginalSkeletonTreeConfig,
22032
) -> OriginalSkeletonTreeResult<(OriginalSkeletonTreeImpl<'a>, HashMap<NodeIndex, L>)> {
22133
if sorted_leaf_indices.is_empty() {
22234
let unmodified = OriginalSkeletonTreeImpl::create_unmodified(root_hash);
@@ -231,94 +43,33 @@ pub async fn create_original_skeleton_tree_and_get_previous_leaves<'a, L: Leaf>(
23143
let main_subtree = FactsSubTree { sorted_leaf_indices, root_index: NodeIndex::ROOT, root_hash };
23244
let mut skeleton_tree = OriginalSkeletonTreeImpl { nodes: HashMap::new(), sorted_leaf_indices };
23345
let mut leaves = HashMap::new();
234-
fetch_nodes::<L, FactsNodeLayout, FactsSubTree<'a>>(
46+
fetch_nodes::<L, FactsNodeLayout>(
23547
&mut skeleton_tree,
23648
vec![main_subtree],
23749
storage,
23850
leaf_modifications,
23951
config,
24052
Some(&mut leaves),
241-
key_context,
53+
&EmptyKeyContext,
24254
)
24355
.await?;
24456
Ok((skeleton_tree, leaves))
24557
}
24658

247-
pub async fn get_leaves<'a, L: Leaf>(
59+
pub async fn get_leaves<'a, L: Leaf + HasStaticPrefix<KeyContext = EmptyKeyContext>>(
24860
storage: &mut impl Storage,
24961
root_hash: HashOutput,
25062
sorted_leaf_indices: SortedLeafIndices<'a>,
251-
key_context: &<L as HasStaticPrefix>::KeyContext,
25263
) -> OriginalSkeletonTreeResult<HashMap<NodeIndex, L>> {
253-
let config = NoCompareOriginalSkeletonTrieConfig::default();
64+
let config = OriginalSkeletonTrieDontCompareConfig;
25465
let leaf_modifications = LeafModifications::new();
25566
let (_, previous_leaves) = create_original_skeleton_tree_and_get_previous_leaves(
25667
storage,
25768
root_hash,
25869
sorted_leaf_indices,
25970
&leaf_modifications,
26071
&config,
261-
key_context,
26272
)
26373
.await?;
26474
Ok(previous_leaves)
26575
}
266-
267-
/// Handles a subtree referred by an edge or a binary node. Decides whether we deserialize the
268-
/// referred subtree or not.
269-
fn handle_subtree<'a, SubTree: SubTreeTrait<'a>>(
270-
skeleton_tree: &mut OriginalSkeletonTreeImpl<'a>,
271-
next_subtrees: &mut Vec<SubTree>,
272-
subtree: SubTree,
273-
subtree_data: SubTree::NodeData,
274-
should_fetch_modified_leaves: bool,
275-
) {
276-
let is_leaf = subtree.is_leaf();
277-
let is_unmodified = subtree.is_unmodified();
278-
279-
// 1. Internal node → always traverse.
280-
if !is_leaf {
281-
next_subtrees.push(subtree);
282-
return;
283-
}
284-
285-
// 2. Modified leaf.
286-
if !is_unmodified {
287-
if should_fetch_modified_leaves {
288-
next_subtrees.push(subtree);
289-
}
290-
return;
291-
}
292-
293-
// 3. Unmodified leaf sibling.
294-
if !SubTree::should_traverse_unmodified_children() {
295-
skeleton_tree.nodes.insert(
296-
subtree.get_root_index(),
297-
OriginalSkeletonNode::UnmodifiedSubTree(
298-
SubTree::unmodified_child_hash(subtree_data).unwrap(),
299-
),
300-
);
301-
} else {
302-
next_subtrees.push(subtree);
303-
}
304-
}
305-
306-
/// Given leaf indices that were previously empty leaves, logs out a warning for trivial
307-
/// modification if a leaf is modified to an empty leaf.
308-
/// If this check is suppressed by configuration, does nothing.
309-
fn log_warning_for_empty_leaves<L: Leaf, T: Borrow<NodeIndex> + Debug>(
310-
leaf_indices: &[T],
311-
leaf_modifications: &LeafModifications<L>,
312-
config: &impl OriginalSkeletonTreeConfig<L>,
313-
) -> OriginalSkeletonTreeResult<()> {
314-
if !config.compare_modified_leaves() {
315-
return Ok(());
316-
}
317-
let empty_leaf = L::default();
318-
for leaf_index in leaf_indices {
319-
if L::compare(leaf_modifications, leaf_index.borrow(), &empty_leaf)? {
320-
log_trivial_modification!(leaf_index, empty_leaf);
321-
}
322-
}
323-
Ok(())
324-
}

crates/starknet_committer/src/db/facts_db/create_facts_tree_test.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ use starknet_patricia_storage::map_storage::MapStorage;
2222
use starknet_patricia_storage::storage_trait::{DbHashMap, DbKey, DbValue};
2323
use starknet_types_core::felt::Felt;
2424

25-
use crate::db::facts_db::create_facts_tree::create_original_skeleton_tree;
25+
use crate::db::facts_db::db::FactsNodeLayout;
26+
use crate::db::trie_traversal::create_original_skeleton_tree;
2627
use crate::patricia_merkle_tree::tree::OriginalSkeletonTrieConfig;
2728

2829
#[tokio::test]
@@ -211,7 +212,7 @@ async fn test_create_tree(
211212
let config = OriginalSkeletonTrieConfig::new(compare_modified_leaves);
212213
let mut sorted_leaf_indices: Vec<NodeIndex> = leaf_modifications.keys().copied().collect();
213214
let sorted_leaf_indices = SortedLeafIndices::new(&mut sorted_leaf_indices);
214-
let skeleton_tree = create_original_skeleton_tree(
215+
let skeleton_tree = create_original_skeleton_tree::<MockLeaf, FactsNodeLayout>(
215216
&mut storage,
216217
root_hash,
217218
sorted_leaf_indices,

0 commit comments

Comments
 (0)