Skip to content

Commit 05ffc20

Browse files
committed
starknet_committer: group generic layout logic
1 parent 45d74f5 commit 05ffc20

File tree

4 files changed

+279
-262
lines changed

4 files changed

+279
-262
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: 3 additions & 231 deletions
Original file line numberDiff line numberDiff line change
@@ -1,154 +1,26 @@
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::filled_tree::node::FilledNode;
7-
use starknet_patricia::patricia_merkle_tree::node_data::inner_node::{
8-
BinaryData,
9-
EdgeData,
10-
NodeData,
11-
};
124
use starknet_patricia::patricia_merkle_tree::node_data::leaf::{Leaf, LeafModifications};
135
use starknet_patricia::patricia_merkle_tree::original_skeleton_tree::config::OriginalSkeletonTreeConfig;
14-
use starknet_patricia::patricia_merkle_tree::original_skeleton_tree::node::OriginalSkeletonNode;
156
use starknet_patricia::patricia_merkle_tree::original_skeleton_tree::tree::{
167
OriginalSkeletonTreeImpl,
178
OriginalSkeletonTreeResult,
189
};
19-
use starknet_patricia::patricia_merkle_tree::traversal::{SubTreeTrait, UnmodifiedChildTraversal};
10+
use starknet_patricia::patricia_merkle_tree::traversal::SubTreeTrait;
2011
use starknet_patricia::patricia_merkle_tree::types::{NodeIndex, SortedLeafIndices};
21-
use starknet_patricia_storage::db_object::{DBObject, HasStaticPrefix};
12+
use starknet_patricia_storage::db_object::HasStaticPrefix;
2213
use starknet_patricia_storage::storage_trait::Storage;
23-
use tracing::warn;
2414

25-
use crate::db::db_layout::NodeLayout;
2615
use crate::db::facts_db::db::FactsNodeLayout;
27-
use crate::db::facts_db::traversal::get_roots_from_storage;
2816
use crate::db::facts_db::types::FactsSubTree;
17+
use crate::db::trie_traversal::{fetch_nodes, log_warning_for_empty_leaves};
2918
use crate::patricia_merkle_tree::tree::OriginalSkeletonTrieDontCompareConfig;
3019

3120
#[cfg(test)]
3221
#[path = "create_facts_tree_test.rs"]
3322
pub mod create_facts_tree_test;
3423

35-
/// Logs out a warning of a trivial modification.
36-
macro_rules! log_trivial_modification {
37-
($index:expr, $value:expr) => {
38-
warn!("Encountered a trivial modification at index {:?}, with value {:?}", $index, $value);
39-
};
40-
}
41-
42-
/// Fetches the Patricia witnesses, required to build the original skeleton tree from storage.
43-
///
44-
/// Given a list of subtrees, traverses towards their leaves and fetches all non-empty,
45-
/// unmodified nodes. If `compare_modified_leaves` is set, function logs out a warning when
46-
/// encountering a trivial modification. Fills the previous leaf values if it is not none.
47-
///
48-
/// The function is generic over the DB layout (`Layout`), which controls the concrete node data
49-
/// (`Layout::NodeData`) and traversal strategy (via `Layout::SubTree`).
50-
async fn fetch_nodes<'a, L, Layout>(
51-
skeleton_tree: &mut OriginalSkeletonTreeImpl<'a>,
52-
subtrees: Vec<Layout::SubTree>,
53-
storage: &mut impl Storage,
54-
leaf_modifications: &LeafModifications<L>,
55-
config: &impl OriginalSkeletonTreeConfig,
56-
mut previous_leaves: Option<&mut HashMap<NodeIndex, L>>,
57-
key_context: &<L as HasStaticPrefix>::KeyContext,
58-
) -> OriginalSkeletonTreeResult<()>
59-
where
60-
L: Leaf,
61-
Layout: NodeLayout<'a, L>,
62-
FilledNode<L, Layout::NodeData>: DBObject<DeserializeContext = Layout::DeserializationContext>,
63-
{
64-
let mut current_subtrees = subtrees;
65-
let mut next_subtrees = Vec::new();
66-
let should_fetch_modified_leaves =
67-
config.compare_modified_leaves() || previous_leaves.is_some();
68-
while !current_subtrees.is_empty() {
69-
let filled_roots =
70-
get_roots_from_storage::<L, Layout>(&current_subtrees, storage, key_context).await?;
71-
for (filled_root, subtree) in filled_roots.into_iter().zip(current_subtrees.into_iter()) {
72-
if subtree.is_unmodified() {
73-
handle_unmodified_subtree(skeleton_tree, &mut next_subtrees, filled_root, subtree);
74-
continue;
75-
}
76-
let root_index = subtree.get_root_index();
77-
match filled_root.data {
78-
// Binary node.
79-
NodeData::Binary(BinaryData { left_data, right_data }) => {
80-
skeleton_tree.nodes.insert(root_index, OriginalSkeletonNode::Binary);
81-
let (left_subtree, right_subtree) =
82-
subtree.get_children_subtrees(left_data, right_data);
83-
84-
handle_child_subtree(
85-
skeleton_tree,
86-
&mut next_subtrees,
87-
left_subtree,
88-
left_data,
89-
should_fetch_modified_leaves,
90-
);
91-
handle_child_subtree(
92-
skeleton_tree,
93-
&mut next_subtrees,
94-
right_subtree,
95-
right_data,
96-
should_fetch_modified_leaves,
97-
)
98-
}
99-
// Edge node.
100-
NodeData::Edge(EdgeData { bottom_data, path_to_bottom }) => {
101-
skeleton_tree
102-
.nodes
103-
.insert(root_index, OriginalSkeletonNode::Edge(path_to_bottom));
104-
105-
// Parse bottom.
106-
let (bottom_subtree, previously_empty_leaves_indices) =
107-
subtree.get_bottom_subtree(&path_to_bottom, bottom_data);
108-
109-
if let Some(ref mut leaves) = previous_leaves {
110-
leaves.extend(
111-
previously_empty_leaves_indices
112-
.iter()
113-
.map(|idx| (**idx, L::default()))
114-
.collect::<HashMap<NodeIndex, L>>(),
115-
);
116-
}
117-
log_warning_for_empty_leaves(
118-
&previously_empty_leaves_indices,
119-
leaf_modifications,
120-
config,
121-
)?;
122-
123-
handle_child_subtree(
124-
skeleton_tree,
125-
&mut next_subtrees,
126-
bottom_subtree,
127-
bottom_data,
128-
should_fetch_modified_leaves,
129-
);
130-
}
131-
// Leaf node.
132-
NodeData::Leaf(previous_leaf) => {
133-
// Modified leaf.
134-
if config.compare_modified_leaves()
135-
&& L::compare(leaf_modifications, &root_index, &previous_leaf)?
136-
{
137-
log_trivial_modification!(root_index, previous_leaf);
138-
}
139-
// If previous values of modified leaves are requested, add this leaf.
140-
if let Some(ref mut leaves) = previous_leaves {
141-
leaves.insert(root_index, previous_leaf);
142-
}
143-
}
144-
}
145-
}
146-
current_subtrees = next_subtrees;
147-
next_subtrees = Vec::new();
148-
}
149-
Ok(())
150-
}
151-
15224
pub async fn create_original_skeleton_tree<'a, L: Leaf>(
15325
storage: &mut impl Storage,
15426
root_hash: HashOutput,
@@ -239,103 +111,3 @@ pub async fn get_leaves<'a, L: Leaf>(
239111
.await?;
240112
Ok(previous_leaves)
241113
}
242-
243-
/// Adds the subtree root to the skeleton tree. If the root is an edge node, and
244-
/// `should_traverse_unmodified_child` is `Skip`, then the corresponding bottom node is also
245-
/// added to the skeleton. Otherwise, the bottom subtree is added to the next subtrees.
246-
fn handle_unmodified_subtree<'a, L: Leaf, SubTree: SubTreeTrait<'a>>(
247-
skeleton_tree: &mut OriginalSkeletonTreeImpl<'a>,
248-
next_subtrees: &mut Vec<SubTree>,
249-
filled_root: FilledNode<L, SubTree::NodeData>,
250-
subtree: SubTree,
251-
) where
252-
SubTree::NodeData: Copy,
253-
{
254-
// Sanity check.
255-
assert!(subtree.is_unmodified(), "Called `handle_unmodified_subtree` for a modified subtree.");
256-
257-
let root_index = subtree.get_root_index();
258-
259-
if let NodeData::Edge(EdgeData { bottom_data, path_to_bottom }) = filled_root.data {
260-
// Even if a subtree rooted at an edge node is unmodified, we still need an
261-
// `OriginalSkeletonNode::Edge` node in the skeleton in case we need to manipulate it later
262-
// (e.g. unify the edge node with an ancestor edge node).
263-
skeleton_tree.nodes.insert(root_index, OriginalSkeletonNode::Edge(path_to_bottom));
264-
match SubTree::should_traverse_unmodified_child(bottom_data) {
265-
UnmodifiedChildTraversal::Traverse => {
266-
let (bottom_subtree, _) = subtree.get_bottom_subtree(&path_to_bottom, bottom_data);
267-
next_subtrees.push(bottom_subtree);
268-
}
269-
UnmodifiedChildTraversal::Skip(unmodified_child_hash) => {
270-
skeleton_tree.nodes.insert(
271-
path_to_bottom.bottom_index(root_index),
272-
OriginalSkeletonNode::UnmodifiedSubTree(unmodified_child_hash),
273-
);
274-
}
275-
}
276-
} else {
277-
skeleton_tree
278-
.nodes
279-
.insert(root_index, OriginalSkeletonNode::UnmodifiedSubTree(filled_root.hash));
280-
}
281-
}
282-
283-
/// Handles a subtree referred by an edge or a binary node. Decides whether we deserialize the
284-
/// referred subtree or not, and if we should continue traversing the child's direction.
285-
fn handle_child_subtree<'a, SubTree: SubTreeTrait<'a>>(
286-
skeleton_tree: &mut OriginalSkeletonTreeImpl<'a>,
287-
next_subtrees: &mut Vec<SubTree>,
288-
subtree: SubTree,
289-
subtree_data: SubTree::NodeData,
290-
should_fetch_modified_leaves: bool,
291-
) {
292-
let is_leaf = subtree.is_leaf();
293-
let is_modified = !subtree.is_unmodified();
294-
295-
// Internal node → always traverse.
296-
if !is_leaf {
297-
next_subtrees.push(subtree);
298-
return;
299-
}
300-
301-
// Modified leaf.
302-
if is_modified {
303-
if should_fetch_modified_leaves {
304-
next_subtrees.push(subtree);
305-
}
306-
return;
307-
}
308-
309-
// Unmodified leaf sibling.
310-
match SubTree::should_traverse_unmodified_child(subtree_data) {
311-
UnmodifiedChildTraversal::Traverse => {
312-
next_subtrees.push(subtree);
313-
}
314-
UnmodifiedChildTraversal::Skip(unmodified_child_hash) => {
315-
skeleton_tree.nodes.insert(
316-
subtree.get_root_index(),
317-
OriginalSkeletonNode::UnmodifiedSubTree(unmodified_child_hash),
318-
);
319-
}
320-
}
321-
}
322-
323-
/// Given leaf indices that were previously empty leaves, logs out a warning for trivial
324-
/// modification if a leaf is modified to an empty leaf.
325-
/// If this check is suppressed by configuration, does nothing.
326-
fn log_warning_for_empty_leaves<L: Leaf, T: Borrow<NodeIndex> + Debug>(
327-
leaf_indices: &[T],
328-
leaf_modifications: &LeafModifications<L>,
329-
config: &impl OriginalSkeletonTreeConfig,
330-
) -> OriginalSkeletonTreeResult<()> {
331-
if !config.compare_modified_leaves() {
332-
return Ok(());
333-
}
334-
let empty_leaf = L::default();
335-
for leaf_index in leaf_indices {
336-
if L::compare(leaf_modifications, leaf_index.borrow(), &empty_leaf)? {
337-
log_trivial_modification!(leaf_index, empty_leaf);
338-
}
339-
}
340-
Ok(())
341-
}

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

Lines changed: 3 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
use std::collections::HashMap;
22

33
use starknet_api::hash::HashOutput;
4-
use starknet_patricia::patricia_merkle_tree::filled_tree::node::FilledNode;
54
use starknet_patricia::patricia_merkle_tree::node_data::inner_node::{
65
NodeData,
76
Preimage,
@@ -10,44 +9,17 @@ use starknet_patricia::patricia_merkle_tree::node_data::inner_node::{
109
use starknet_patricia::patricia_merkle_tree::node_data::leaf::Leaf;
1110
use starknet_patricia::patricia_merkle_tree::traversal::{SubTreeTrait, TraversalResult};
1211
use starknet_patricia::patricia_merkle_tree::types::{NodeIndex, SortedLeafIndices};
13-
use starknet_patricia_storage::db_object::{DBObject, HasStaticPrefix};
14-
use starknet_patricia_storage::errors::StorageError;
15-
use starknet_patricia_storage::storage_trait::{create_db_key, DbKey, Storage};
12+
use starknet_patricia_storage::db_object::HasStaticPrefix;
13+
use starknet_patricia_storage::storage_trait::Storage;
1614

17-
use crate::db::db_layout::NodeLayout;
1815
use crate::db::facts_db::db::FactsNodeLayout;
1916
use crate::db::facts_db::types::FactsSubTree;
17+
use crate::db::trie_traversal::get_roots_from_storage;
2018

2119
#[cfg(test)]
2220
#[path = "traversal_test.rs"]
2321
pub mod traversal_test;
2422

25-
// TODO(Aviv, 17/07/2024): Split between storage prefix implementation and function logic.
26-
pub async fn get_roots_from_storage<'a, L: Leaf, Layout: NodeLayout<'a, L>>(
27-
subtrees: &[Layout::SubTree],
28-
storage: &mut impl Storage,
29-
key_context: &<L as HasStaticPrefix>::KeyContext,
30-
) -> TraversalResult<Vec<FilledNode<L, Layout::NodeData>>>
31-
where
32-
FilledNode<L, Layout::NodeData>: DBObject<DeserializeContext = Layout::DeserializationContext>,
33-
{
34-
let mut subtrees_roots = vec![];
35-
let db_keys: Vec<DbKey> = subtrees
36-
.iter()
37-
.map(|subtree| {
38-
create_db_key(subtree.get_root_prefix::<L>(key_context), &subtree.get_root_suffix())
39-
})
40-
.collect();
41-
42-
let db_vals = storage.mget(&db_keys.iter().collect::<Vec<&DbKey>>()).await?;
43-
for ((subtree, optional_val), db_key) in subtrees.iter().zip(db_vals.iter()).zip(db_keys) {
44-
let Some(val) = optional_val else { Err(StorageError::MissingKey(db_key))? };
45-
let filled_node = FilledNode::deserialize(val, &subtree.get_root_context())?;
46-
subtrees_roots.push(filled_node);
47-
}
48-
Ok(subtrees_roots)
49-
}
50-
5123
/// Returns the Patricia inner nodes ([PreimageMap]) in the paths to the given `leaf_indices` in the
5224
/// given tree according to the `root_hash`.
5325
/// If `leaves` is not `None`, it also fetches the modified leaves and inserts them into the

0 commit comments

Comments
 (0)