Skip to content

Commit e0517f7

Browse files
committed
starknet_committer: abstract original tree creation
1 parent 85a0005 commit e0517f7

File tree

10 files changed

+470
-431
lines changed

10 files changed

+470
-431
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, ChildData = 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::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: 10 additions & 269 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};
2311
use starknet_patricia_storage::db_object::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,198 +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<
52-
impl SubTreeTrait<
53-
'a,
54-
ChildData = Layout::ChildData,
55-
NodeContext = Layout::DeserializationContext,
56-
>,
57-
>,
58-
storage: &mut impl Storage,
59-
leaf_modifications: &LeafModifications<L>,
60-
config: &impl OriginalSkeletonTreeConfig<L>,
61-
mut previous_leaves: Option<&mut HashMap<NodeIndex, L>>,
62-
key_context: &<L as HasStaticPrefix>::KeyContext,
63-
) -> OriginalSkeletonTreeResult<()>
64-
where
65-
L: Leaf,
66-
Layout: NodeLayout<L>,
67-
SubTree: SubTreeTrait<
68-
'a,
69-
ChildData = Layout::ChildData,
70-
NodeContext = Layout::DeserializationContext,
71-
>,
72-
{
73-
let mut current_subtrees = subtrees;
74-
let mut next_subtrees = Vec::new();
75-
while !current_subtrees.is_empty() {
76-
let should_fetch_modified_leaves =
77-
config.compare_modified_leaves() || previous_leaves.is_some();
78-
let filled_roots =
79-
get_roots_from_storage::<L, Layout>(&current_subtrees, storage, key_context).await?;
80-
for (filled_root, subtree) in filled_roots.into_iter().zip(current_subtrees.iter()) {
81-
match filled_root.data {
82-
// Binary node.
83-
NodeData::Binary(BinaryData { left_data, right_data }) => {
84-
if subtree.is_unmodified() {
85-
skeleton_tree.nodes.insert(
86-
subtree.get_root_index(),
87-
OriginalSkeletonNode::UnmodifiedSubTree(filled_root.hash),
88-
);
89-
continue;
90-
}
91-
skeleton_tree
92-
.nodes
93-
.insert(subtree.get_root_index(), OriginalSkeletonNode::Binary);
94-
let (left_subtree, right_subtree) =
95-
subtree.get_children_subtrees(left_data, right_data);
96-
97-
handle_subtree(
98-
skeleton_tree,
99-
&mut next_subtrees,
100-
left_subtree,
101-
left_data,
102-
should_fetch_modified_leaves,
103-
);
104-
handle_subtree(
105-
skeleton_tree,
106-
&mut next_subtrees,
107-
right_subtree,
108-
right_data,
109-
should_fetch_modified_leaves,
110-
)
111-
}
112-
// Edge node.
113-
NodeData::Edge(EdgeData { bottom_data, path_to_bottom }) => {
114-
skeleton_tree.nodes.insert(
115-
subtree.get_root_index(),
116-
OriginalSkeletonNode::Edge(path_to_bottom),
117-
);
118-
119-
// Parse bottom.
120-
let (bottom_subtree, previously_empty_leaves_indices) =
121-
subtree.get_bottom_subtree(&path_to_bottom, bottom_data);
122-
123-
if subtree.is_unmodified() {
124-
if !SubTree::should_traverse_unmodified_children() {
125-
skeleton_tree.nodes.insert(
126-
path_to_bottom.bottom_index(subtree.get_root_index()),
127-
OriginalSkeletonNode::UnmodifiedSubTree(
128-
SubTree::unmodified_child_hash(bottom_data).unwrap(),
129-
),
130-
);
131-
} else {
132-
// With index layout we need to traverse an unmodified bottom node.
133-
next_subtrees.push(bottom_subtree)
134-
}
135-
continue;
136-
}
137-
138-
if let Some(ref mut leaves) = previous_leaves {
139-
leaves.extend(
140-
previously_empty_leaves_indices
141-
.iter()
142-
.map(|idx| (*idx, L::default()))
143-
.collect::<HashMap<NodeIndex, L>>(),
144-
);
145-
}
146-
log_warning_for_empty_leaves(
147-
&previously_empty_leaves_indices,
148-
leaf_modifications,
149-
config,
150-
)?;
151-
152-
handle_subtree(
153-
skeleton_tree,
154-
&mut next_subtrees,
155-
bottom_subtree,
156-
bottom_data,
157-
should_fetch_modified_leaves,
158-
);
159-
}
160-
// Leaf node.
161-
NodeData::Leaf(previous_leaf) => {
162-
if subtree.is_unmodified() {
163-
skeleton_tree.nodes.insert(
164-
subtree.get_root_index(),
165-
OriginalSkeletonNode::UnmodifiedSubTree(filled_root.hash),
166-
);
167-
} else {
168-
let root_index = subtree.get_root_index();
169-
// Modified leaf.
170-
if config.compare_modified_leaves()
171-
&& L::compare(leaf_modifications, &root_index, &previous_leaf)?
172-
{
173-
log_trivial_modification!(root_index, previous_leaf);
174-
}
175-
// If previous values of modified leaves are requested, add this leaf.
176-
if let Some(ref mut leaves) = previous_leaves {
177-
leaves.insert(root_index, previous_leaf);
178-
}
179-
}
180-
}
181-
}
182-
}
183-
current_subtrees = next_subtrees;
184-
next_subtrees = Vec::new();
185-
}
186-
Ok(())
187-
}
188-
189-
pub async fn create_original_skeleton_tree<'a, L: Leaf>(
190-
storage: &mut impl Storage,
191-
root_hash: HashOutput,
192-
sorted_leaf_indices: SortedLeafIndices<'a>,
193-
config: &impl OriginalSkeletonTreeConfig<L>,
194-
leaf_modifications: &LeafModifications<L>,
195-
key_context: &<L as HasStaticPrefix>::KeyContext,
196-
) -> OriginalSkeletonTreeResult<OriginalSkeletonTreeImpl<'a>> {
197-
if sorted_leaf_indices.is_empty() {
198-
return Ok(OriginalSkeletonTreeImpl::create_unmodified(root_hash));
199-
}
200-
if root_hash == HashOutput::ROOT_OF_EMPTY_TREE {
201-
log_warning_for_empty_leaves(
202-
sorted_leaf_indices.get_indices(),
203-
leaf_modifications,
204-
config,
205-
)?;
206-
return Ok(OriginalSkeletonTreeImpl::create_empty(sorted_leaf_indices));
207-
}
208-
let main_subtree = FactsSubTree { sorted_leaf_indices, root_index: NodeIndex::ROOT, root_hash };
209-
let mut skeleton_tree = OriginalSkeletonTreeImpl { nodes: HashMap::new(), sorted_leaf_indices };
210-
fetch_nodes::<L, FactsNodeLayout, FactsSubTree<'a>>(
211-
&mut skeleton_tree,
212-
vec![main_subtree],
213-
storage,
214-
leaf_modifications,
215-
config,
216-
None,
217-
key_context,
218-
)
219-
.await?;
220-
Ok(skeleton_tree)
221-
}
222-
223-
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 = ()>,
26+
>(
22427
storage: &mut impl Storage,
22528
root_hash: HashOutput,
22629
sorted_leaf_indices: SortedLeafIndices<'a>,
22730
leaf_modifications: &LeafModifications<L>,
228-
config: &impl OriginalSkeletonTreeConfig<L>,
229-
key_context: &<L as HasStaticPrefix>::KeyContext,
31+
config: &impl OriginalSkeletonTreeConfig,
23032
) -> OriginalSkeletonTreeResult<(OriginalSkeletonTreeImpl<'a>, HashMap<NodeIndex, L>)> {
23133
if sorted_leaf_indices.is_empty() {
23234
let unmodified = OriginalSkeletonTreeImpl::create_unmodified(root_hash);
@@ -241,94 +43,33 @@ pub async fn create_original_skeleton_tree_and_get_previous_leaves<'a, L: Leaf>(
24143
let main_subtree = FactsSubTree { sorted_leaf_indices, root_index: NodeIndex::ROOT, root_hash };
24244
let mut skeleton_tree = OriginalSkeletonTreeImpl { nodes: HashMap::new(), sorted_leaf_indices };
24345
let mut leaves = HashMap::new();
244-
fetch_nodes::<L, FactsNodeLayout, FactsSubTree<'a>>(
46+
fetch_nodes::<L, FactsNodeLayout>(
24547
&mut skeleton_tree,
24648
vec![main_subtree],
24749
storage,
24850
leaf_modifications,
24951
config,
25052
Some(&mut leaves),
251-
key_context,
53+
&(),
25254
)
25355
.await?;
25456
Ok((skeleton_tree, leaves))
25557
}
25658

257-
pub async fn get_leaves<'a, L: Leaf>(
59+
pub async fn get_leaves<'a, L: Leaf + HasStaticPrefix<KeyContext = ()>>(
25860
storage: &mut impl Storage,
25961
root_hash: HashOutput,
26062
sorted_leaf_indices: SortedLeafIndices<'a>,
261-
key_context: &<L as HasStaticPrefix>::KeyContext,
26263
) -> OriginalSkeletonTreeResult<HashMap<NodeIndex, L>> {
263-
let config = NoCompareOriginalSkeletonTrieConfig::default();
64+
let config = OriginalSkeletonTrieDontCompareConfig;
26465
let leaf_modifications = LeafModifications::new();
26566
let (_, previous_leaves) = create_original_skeleton_tree_and_get_previous_leaves(
26667
storage,
26768
root_hash,
26869
sorted_leaf_indices,
26970
&leaf_modifications,
27071
&config,
271-
key_context,
27272
)
27373
.await?;
27474
Ok(previous_leaves)
27575
}
276-
277-
/// Handles a subtree referred by an edge or a binary node. Decides whether we deserialize the
278-
/// referred subtree or not.
279-
fn handle_subtree<'a, SubTree: SubTreeTrait<'a>>(
280-
skeleton_tree: &mut OriginalSkeletonTreeImpl<'a>,
281-
next_subtrees: &mut Vec<SubTree>,
282-
subtree: SubTree,
283-
subtree_data: SubTree::ChildData,
284-
should_fetch_modified_leaves: bool,
285-
) {
286-
let is_leaf = subtree.is_leaf();
287-
let is_unmodified = subtree.is_unmodified();
288-
289-
// 1. Internal node → always traverse.
290-
if !is_leaf {
291-
next_subtrees.push(subtree);
292-
return;
293-
}
294-
295-
// 2. Modified leaf.
296-
if !is_unmodified {
297-
if should_fetch_modified_leaves {
298-
next_subtrees.push(subtree);
299-
}
300-
return;
301-
}
302-
303-
// 3. Unmodified leaf sibling.
304-
if !SubTree::should_traverse_unmodified_children() {
305-
skeleton_tree.nodes.insert(
306-
subtree.get_root_index(),
307-
OriginalSkeletonNode::UnmodifiedSubTree(
308-
SubTree::unmodified_child_hash(subtree_data).unwrap(),
309-
),
310-
);
311-
} else {
312-
next_subtrees.push(subtree);
313-
}
314-
}
315-
316-
/// Given leaf indices that were previously empty leaves, logs out a warning for trivial
317-
/// modification if a leaf is modified to an empty leaf.
318-
/// If this check is suppressed by configuration, does nothing.
319-
fn log_warning_for_empty_leaves<L: Leaf, T: Borrow<NodeIndex> + Debug>(
320-
leaf_indices: &[T],
321-
leaf_modifications: &LeafModifications<L>,
322-
config: &impl OriginalSkeletonTreeConfig<L>,
323-
) -> OriginalSkeletonTreeResult<()> {
324-
if !config.compare_modified_leaves() {
325-
return Ok(());
326-
}
327-
let empty_leaf = L::default();
328-
for leaf_index in leaf_indices {
329-
if L::compare(leaf_modifications, leaf_index.borrow(), &empty_leaf)? {
330-
log_trivial_modification!(leaf_index, empty_leaf);
331-
}
332-
}
333-
Ok(())
334-
}

0 commit comments

Comments
 (0)