Skip to content

Commit 466fffe

Browse files
committed
starknet_committer,starknet_committer_and_os_cli,starknet_patricia: make fetch_nodes layout dependent
1 parent b6aa42e commit 466fffe

File tree

14 files changed

+224
-90
lines changed

14 files changed

+224
-90
lines changed

crates/starknet_committer/src/block_committer/random_structs.rs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ use starknet_patricia::patricia_merkle_tree::external_test_utils::{
1919
get_random_u256,
2020
u256_try_into_felt,
2121
};
22-
use starknet_patricia::patricia_merkle_tree::filled_tree::node::FilledNode;
2322
use starknet_patricia::patricia_merkle_tree::node_data::inner_node::{
2423
BinaryData,
2524
EdgeData,
@@ -34,6 +33,7 @@ use starknet_types_core::felt::Felt;
3433
use strum::IntoEnumIterator;
3534

3635
use crate::block_committer::input::StarknetStorageValue;
36+
use crate::db::facts_db::types::FactDbFilledNode;
3737
use crate::forest::filled_forest::FilledForest;
3838
use crate::patricia_merkle_tree::leaf::leaf_impl::ContractState;
3939
use crate::patricia_merkle_tree::types::{
@@ -178,7 +178,7 @@ impl RandomValue for NodeIndex {
178178

179179
macro_rules! random_filled_node {
180180
($leaf:ty) => {
181-
impl RandomValue for FilledNode<$leaf> {
181+
impl RandomValue for FactDbFilledNode<$leaf> {
182182
fn random<R: Rng>(rng: &mut R, max: Option<U256>) -> Self {
183183
Self { data: NodeData::random(rng, max), hash: HashOutput::random(rng, max) }
184184
}
@@ -216,11 +216,13 @@ macro_rules! random_filled_tree {
216216
}
217217
.as_usize();
218218

219-
let mut nodes: Vec<(NodeIndex, FilledNode<$leaf>)> = (0..max_node_number)
220-
.map(|_| (NodeIndex::random(rng, max_size), FilledNode::random(rng, max_size)))
219+
let mut nodes: Vec<(NodeIndex, FactDbFilledNode<$leaf>)> = (0..max_node_number)
220+
.map(|_| {
221+
(NodeIndex::random(rng, max_size), FactDbFilledNode::random(rng, max_size))
222+
})
221223
.collect();
222224

223-
nodes.push((NodeIndex::ROOT, FilledNode::random(rng, max_size)));
225+
nodes.push((NodeIndex::ROOT, FactDbFilledNode::random(rng, max_size)));
224226

225227
Self {
226228
tree_map: nodes.into_iter().collect(),

crates/starknet_committer/src/db.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
pub mod db_layout;
12
#[cfg(any(feature = "testing", test))]
23
pub mod external_test_utils;
34
pub mod facts_db;
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
use starknet_patricia::patricia_merkle_tree::filled_tree::node::FilledNode;
2+
use starknet_patricia::patricia_merkle_tree::node_data::leaf::Leaf;
3+
use starknet_patricia_storage::errors::DeserializationError;
4+
use starknet_patricia_storage::storage_trait::DbValue;
5+
6+
pub trait NodeLayout<L: Leaf> {
7+
type ChildData: Copy;
8+
type DeserializationContext;
9+
fn deserialize_node(
10+
value: &DbValue,
11+
deserialize_context: &Self::DeserializationContext,
12+
) -> Result<FilledNode<L, Self::ChildData>, DeserializationError>;
13+
}

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

Lines changed: 93 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,9 @@ use starknet_patricia_storage::db_object::HasStaticPrefix;
2424
use starknet_patricia_storage::storage_trait::Storage;
2525
use tracing::warn;
2626

27-
use crate::db::facts_db::traversal::calculate_subtrees_roots;
27+
use crate::db::db_layout::NodeLayout;
28+
use crate::db::facts_db::db::FactsNodeLayout;
29+
use crate::db::facts_db::traversal::get_roots_from_storage;
2830
use crate::db::facts_db::types::FactsSubTree;
2931

3032
#[cfg(test)]
@@ -42,65 +44,95 @@ macro_rules! log_trivial_modification {
4244
/// Given a list of subtrees, traverses towards their leaves and fetches all non-empty,
4345
/// unmodified nodes. If `compare_modified_leaves` is set, function logs out a warning when
4446
/// encountering a trivial modification. Fills the previous leaf values if it is not none.
45-
async fn fetch_nodes<'a, L: Leaf>(
47+
async fn fetch_nodes<'a, L, Layout, SubTree>(
4648
skeleton_tree: &mut OriginalSkeletonTreeImpl<'a>,
47-
subtrees: Vec<FactsSubTree<'a>>,
49+
subtrees: Vec<
50+
impl SubTreeTrait<
51+
'a,
52+
ChildData = Layout::ChildData,
53+
NodeContext = Layout::DeserializationContext,
54+
>,
55+
>,
4856
storage: &mut impl Storage,
4957
leaf_modifications: &LeafModifications<L>,
5058
config: &impl OriginalSkeletonTreeConfig<L>,
5159
mut previous_leaves: Option<&mut HashMap<NodeIndex, L>>,
5260
key_context: &<L as HasStaticPrefix>::KeyContext,
53-
) -> OriginalSkeletonTreeResult<()> {
61+
) -> OriginalSkeletonTreeResult<()>
62+
where
63+
L: Leaf,
64+
Layout: NodeLayout<L>,
65+
SubTree: SubTreeTrait<
66+
'a,
67+
ChildData = Layout::ChildData,
68+
NodeContext = Layout::DeserializationContext,
69+
>,
70+
{
5471
let mut current_subtrees = subtrees;
5572
let mut next_subtrees = Vec::new();
5673
while !current_subtrees.is_empty() {
5774
let should_fetch_modified_leaves =
5875
config.compare_modified_leaves() || previous_leaves.is_some();
5976
let filled_roots =
60-
calculate_subtrees_roots::<L>(&current_subtrees, storage, key_context).await?;
77+
get_roots_from_storage::<L, Layout>(&current_subtrees, storage, key_context).await?;
6178
for (filled_root, subtree) in filled_roots.into_iter().zip(current_subtrees.iter()) {
6279
match filled_root.data {
6380
// Binary node.
64-
NodeData::Binary(BinaryData { left_hash, right_hash }) => {
81+
NodeData::Binary(BinaryData { left_data, right_data }) => {
6582
if subtree.is_unmodified() {
6683
skeleton_tree.nodes.insert(
67-
subtree.root_index,
84+
subtree.get_root_index(),
6885
OriginalSkeletonNode::UnmodifiedSubTree(filled_root.hash),
6986
);
7087
continue;
7188
}
72-
skeleton_tree.nodes.insert(subtree.root_index, OriginalSkeletonNode::Binary);
89+
skeleton_tree
90+
.nodes
91+
.insert(subtree.get_root_index(), OriginalSkeletonNode::Binary);
7392
let (left_subtree, right_subtree) =
74-
subtree.get_children_subtrees(left_hash, right_hash);
93+
subtree.get_children_subtrees(left_data, right_data);
7594

7695
handle_subtree(
7796
skeleton_tree,
7897
&mut next_subtrees,
7998
left_subtree,
99+
left_data,
80100
should_fetch_modified_leaves,
81101
);
82102
handle_subtree(
83103
skeleton_tree,
84104
&mut next_subtrees,
85105
right_subtree,
106+
right_data,
86107
should_fetch_modified_leaves,
87108
)
88109
}
89110
// Edge node.
90-
NodeData::Edge(EdgeData { bottom_hash, path_to_bottom }) => {
91-
skeleton_tree
92-
.nodes
93-
.insert(subtree.root_index, OriginalSkeletonNode::Edge(path_to_bottom));
111+
NodeData::Edge(EdgeData { bottom_data, path_to_bottom }) => {
112+
skeleton_tree.nodes.insert(
113+
subtree.get_root_index(),
114+
OriginalSkeletonNode::Edge(path_to_bottom),
115+
);
116+
117+
// Parse bottom.
118+
let (bottom_subtree, previously_empty_leaves_indices) =
119+
subtree.get_bottom_subtree(&path_to_bottom, bottom_data);
120+
94121
if subtree.is_unmodified() {
95-
skeleton_tree.nodes.insert(
96-
path_to_bottom.bottom_index(subtree.root_index),
97-
OriginalSkeletonNode::UnmodifiedSubTree(bottom_hash),
98-
);
122+
if !SubTree::should_traverse_unmodified_children() {
123+
skeleton_tree.nodes.insert(
124+
path_to_bottom.bottom_index(subtree.get_root_index()),
125+
OriginalSkeletonNode::UnmodifiedSubTree(
126+
SubTree::unmodified_child_hash(bottom_data).unwrap(),
127+
),
128+
);
129+
} else {
130+
// With index layout we need to traverse an unmodified bottom node.
131+
next_subtrees.push(bottom_subtree)
132+
}
99133
continue;
100134
}
101-
// Parse bottom.
102-
let (bottom_subtree, previously_empty_leaves_indices) =
103-
subtree.get_bottom_subtree(&path_to_bottom, bottom_hash);
135+
104136
if let Some(ref mut leaves) = previous_leaves {
105137
leaves.extend(
106138
previously_empty_leaves_indices
@@ -119,23 +151,28 @@ async fn fetch_nodes<'a, L: Leaf>(
119151
skeleton_tree,
120152
&mut next_subtrees,
121153
bottom_subtree,
154+
bottom_data,
122155
should_fetch_modified_leaves,
123156
);
124157
}
125158
// Leaf node.
126159
NodeData::Leaf(previous_leaf) => {
127160
if subtree.is_unmodified() {
128-
warn!("Unexpectedly deserialized leaf sibling.")
161+
skeleton_tree.nodes.insert(
162+
subtree.get_root_index(),
163+
OriginalSkeletonNode::UnmodifiedSubTree(filled_root.hash),
164+
);
129165
} else {
166+
let root_index = subtree.get_root_index();
130167
// Modified leaf.
131168
if config.compare_modified_leaves()
132-
&& L::compare(leaf_modifications, &subtree.root_index, &previous_leaf)?
169+
&& L::compare(leaf_modifications, &root_index, &previous_leaf)?
133170
{
134-
log_trivial_modification!(subtree.root_index, previous_leaf);
171+
log_trivial_modification!(root_index, previous_leaf);
135172
}
136173
// If previous values of modified leaves are requested, add this leaf.
137174
if let Some(ref mut leaves) = previous_leaves {
138-
leaves.insert(subtree.root_index, previous_leaf);
175+
leaves.insert(root_index, previous_leaf);
139176
}
140177
}
141178
}
@@ -168,7 +205,7 @@ pub async fn create_original_skeleton_tree<'a, L: Leaf>(
168205
}
169206
let main_subtree = FactsSubTree { sorted_leaf_indices, root_index: NodeIndex::ROOT, root_hash };
170207
let mut skeleton_tree = OriginalSkeletonTreeImpl { nodes: HashMap::new(), sorted_leaf_indices };
171-
fetch_nodes::<L>(
208+
fetch_nodes::<L, FactsNodeLayout, FactsSubTree<'a>>(
172209
&mut skeleton_tree,
173210
vec![main_subtree],
174211
storage,
@@ -202,7 +239,7 @@ pub async fn create_original_skeleton_tree_and_get_previous_leaves<'a, L: Leaf>(
202239
let main_subtree = FactsSubTree { sorted_leaf_indices, root_index: NodeIndex::ROOT, root_hash };
203240
let mut skeleton_tree = OriginalSkeletonTreeImpl { nodes: HashMap::new(), sorted_leaf_indices };
204241
let mut leaves = HashMap::new();
205-
fetch_nodes::<L>(
242+
fetch_nodes::<L, FactsNodeLayout, FactsSubTree<'a>>(
206243
&mut skeleton_tree,
207244
vec![main_subtree],
208245
storage,
@@ -237,19 +274,40 @@ pub async fn get_leaves<'a, L: Leaf>(
237274

238275
/// Handles a subtree referred by an edge or a binary node. Decides whether we deserialize the
239276
/// referred subtree or not.
240-
fn handle_subtree<'a>(
277+
fn handle_subtree<'a, SubTree: SubTreeTrait<'a>>(
241278
skeleton_tree: &mut OriginalSkeletonTreeImpl<'a>,
242-
next_subtrees: &mut Vec<FactsSubTree<'a>>,
243-
subtree: FactsSubTree<'a>,
279+
next_subtrees: &mut Vec<SubTree>,
280+
subtree: SubTree,
281+
subtree_data: SubTree::ChildData,
244282
should_fetch_modified_leaves: bool,
245283
) {
246-
if !subtree.is_leaf() || (should_fetch_modified_leaves && !subtree.is_unmodified()) {
284+
let is_leaf = subtree.is_leaf();
285+
let is_unmodified = subtree.is_unmodified();
286+
287+
// 1. Internal node → always traverse.
288+
if !is_leaf {
289+
next_subtrees.push(subtree);
290+
return;
291+
}
292+
293+
// 2. Modified leaf.
294+
if !is_unmodified {
295+
if should_fetch_modified_leaves {
296+
next_subtrees.push(subtree);
297+
}
298+
return;
299+
}
300+
301+
// 3. Unmodified leaf sibling.
302+
if !SubTree::should_traverse_unmodified_children() {
303+
skeleton_tree.nodes.insert(
304+
subtree.get_root_index(),
305+
OriginalSkeletonNode::UnmodifiedSubTree(
306+
SubTree::unmodified_child_hash(subtree_data).unwrap(),
307+
),
308+
);
309+
} else {
247310
next_subtrees.push(subtree);
248-
} else if subtree.is_unmodified() {
249-
// Leaf sibling.
250-
skeleton_tree
251-
.nodes
252-
.insert(subtree.root_index, OriginalSkeletonNode::UnmodifiedSubTree(subtree.root_hash));
253311
}
254312
}
255313

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

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,27 @@ use std::collections::HashMap;
22

33
use starknet_api::core::ContractAddress;
44
use starknet_api::hash::HashOutput;
5-
use starknet_patricia::patricia_merkle_tree::node_data::leaf::LeafModifications;
5+
use starknet_patricia::patricia_merkle_tree::filled_tree::node_serde::NodeContext;
6+
use starknet_patricia::patricia_merkle_tree::node_data::leaf::{Leaf, LeafModifications};
67
use starknet_patricia::patricia_merkle_tree::original_skeleton_tree::tree::OriginalSkeletonTreeImpl;
78
use starknet_patricia::patricia_merkle_tree::types::{NodeIndex, SortedLeafIndices};
9+
use starknet_patricia_storage::db_object::DBObject;
10+
use starknet_patricia_storage::errors::DeserializationError;
811
use starknet_patricia_storage::map_storage::MapStorage;
9-
use starknet_patricia_storage::storage_trait::Storage;
12+
use starknet_patricia_storage::storage_trait::{DbValue, Storage};
1013

1114
use crate::block_committer::input::{
1215
contract_address_into_node_index,
1316
Config,
1417
ConfigImpl,
1518
StarknetStorageValue,
1619
};
20+
use crate::db::db_layout::NodeLayout;
1721
use crate::db::facts_db::create_facts_tree::{
1822
create_original_skeleton_tree,
1923
create_original_skeleton_tree_and_get_previous_leaves,
2024
};
25+
use crate::db::facts_db::types::FactDbFilledNode;
2126
use crate::db::forest_trait::{ForestReader, ForestWriter};
2227
use crate::forest::filled_forest::FilledForest;
2328
use crate::forest::forest_errors::{ForestError, ForestResult};
@@ -30,6 +35,18 @@ use crate::patricia_merkle_tree::tree::{
3035
};
3136
use crate::patricia_merkle_tree::types::CompiledClassHash;
3237

38+
pub struct FactsNodeLayout {}
39+
40+
impl<L: Leaf> NodeLayout<L> for FactsNodeLayout {
41+
type ChildData = HashOutput;
42+
type DeserializationContext = NodeContext;
43+
fn deserialize_node(
44+
value: &DbValue,
45+
deserialize_context: &Self::DeserializationContext,
46+
) -> Result<FactDbFilledNode<L>, DeserializationError> {
47+
DBObject::deserialize(value, deserialize_context)
48+
}
49+
}
3350
pub struct FactsDb<S: Storage> {
3451
// TODO(Yoav): Define StorageStats trait and impl it here. Then, make the storage field
3552
// private.

0 commit comments

Comments
 (0)