Skip to content

Commit 55b96eb

Browse files
committed
starknet_committer,starknet_committer_and_os_cli,starknet_patricia: make fetch_nodes layout dependent
1 parent 71c89a6 commit 55b96eb

File tree

14 files changed

+221
-93
lines changed

14 files changed

+221
-93
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: 84 additions & 36 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,85 @@ 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<SubTree>,
4850
storage: &mut impl Storage,
4951
leaf_modifications: &LeafModifications<L>,
5052
config: &impl OriginalSkeletonTreeConfig<L>,
5153
mut previous_leaves: Option<&mut HashMap<NodeIndex, L>>,
5254
key_context: &<L as HasStaticPrefix>::KeyContext,
53-
) -> OriginalSkeletonTreeResult<()> {
55+
) -> OriginalSkeletonTreeResult<()>
56+
where
57+
L: Leaf,
58+
Layout: NodeLayout<L>,
59+
SubTree: SubTreeTrait<'a, NodeData = Layout::ChildData, NodeContext = Layout::DeserializationContext>,
60+
{
5461
let mut current_subtrees = subtrees;
5562
let mut next_subtrees = Vec::new();
5663
while !current_subtrees.is_empty() {
5764
let should_fetch_modified_leaves =
5865
config.compare_modified_leaves() || previous_leaves.is_some();
5966
let filled_roots =
60-
calculate_subtrees_roots::<L>(&current_subtrees, storage, key_context).await?;
67+
get_roots_from_storage::<L, Layout>(&current_subtrees, storage, key_context).await?;
6168
for (filled_root, subtree) in filled_roots.into_iter().zip(current_subtrees.iter()) {
6269
match filled_root.data {
6370
// Binary node.
64-
NodeData::Binary(BinaryData { left_hash, right_hash }) => {
71+
NodeData::Binary(BinaryData { left_data, right_data }) => {
6572
if subtree.is_unmodified() {
6673
skeleton_tree.nodes.insert(
67-
subtree.root_index,
74+
subtree.get_root_index(),
6875
OriginalSkeletonNode::UnmodifiedSubTree(filled_root.hash),
6976
);
7077
continue;
7178
}
72-
skeleton_tree.nodes.insert(subtree.root_index, OriginalSkeletonNode::Binary);
79+
skeleton_tree
80+
.nodes
81+
.insert(subtree.get_root_index(), OriginalSkeletonNode::Binary);
7382
let (left_subtree, right_subtree) =
74-
subtree.get_children_subtrees(left_hash, right_hash);
83+
subtree.get_children_subtrees(left_data, right_data);
7584

7685
handle_subtree(
7786
skeleton_tree,
7887
&mut next_subtrees,
7988
left_subtree,
89+
left_data,
8090
should_fetch_modified_leaves,
8191
);
8292
handle_subtree(
8393
skeleton_tree,
8494
&mut next_subtrees,
8595
right_subtree,
96+
right_data,
8697
should_fetch_modified_leaves,
8798
)
8899
}
89100
// 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));
101+
NodeData::Edge(EdgeData { bottom_data, path_to_bottom }) => {
102+
skeleton_tree.nodes.insert(
103+
subtree.get_root_index(),
104+
OriginalSkeletonNode::Edge(path_to_bottom),
105+
);
106+
107+
// Parse bottom.
108+
let (bottom_subtree, previously_empty_leaves_indices) =
109+
subtree.get_bottom_subtree(&path_to_bottom, bottom_data);
110+
94111
if subtree.is_unmodified() {
95-
skeleton_tree.nodes.insert(
96-
path_to_bottom.bottom_index(subtree.root_index),
97-
OriginalSkeletonNode::UnmodifiedSubTree(bottom_hash),
98-
);
112+
if !SubTree::should_traverse_unmodified_children() {
113+
skeleton_tree.nodes.insert(
114+
path_to_bottom.bottom_index(subtree.get_root_index()),
115+
OriginalSkeletonNode::UnmodifiedSubTree(
116+
SubTree::unmodified_child_hash(bottom_data).unwrap(),
117+
),
118+
);
119+
} else {
120+
// With index layout we need to traverse an unmodified bottom node.
121+
next_subtrees.push(bottom_subtree)
122+
}
99123
continue;
100124
}
101-
// Parse bottom.
102-
let (bottom_subtree, previously_empty_leaves_indices) =
103-
subtree.get_bottom_subtree(&path_to_bottom, bottom_hash);
125+
104126
if let Some(ref mut leaves) = previous_leaves {
105127
leaves.extend(
106128
previously_empty_leaves_indices
@@ -119,23 +141,28 @@ async fn fetch_nodes<'a, L: Leaf>(
119141
skeleton_tree,
120142
&mut next_subtrees,
121143
bottom_subtree,
144+
bottom_data,
122145
should_fetch_modified_leaves,
123146
);
124147
}
125148
// Leaf node.
126149
NodeData::Leaf(previous_leaf) => {
127150
if subtree.is_unmodified() {
128-
warn!("Unexpectedly deserialized leaf sibling.")
151+
skeleton_tree.nodes.insert(
152+
subtree.get_root_index(),
153+
OriginalSkeletonNode::UnmodifiedSubTree(filled_root.hash),
154+
);
129155
} else {
156+
let root_index = subtree.get_root_index();
130157
// Modified leaf.
131158
if config.compare_modified_leaves()
132-
&& L::compare(leaf_modifications, &subtree.root_index, &previous_leaf)?
159+
&& L::compare(leaf_modifications, &root_index, &previous_leaf)?
133160
{
134-
log_trivial_modification!(subtree.root_index, previous_leaf);
161+
log_trivial_modification!(root_index, previous_leaf);
135162
}
136163
// If previous values of modified leaves are requested, add this leaf.
137164
if let Some(ref mut leaves) = previous_leaves {
138-
leaves.insert(subtree.root_index, previous_leaf);
165+
leaves.insert(root_index, previous_leaf);
139166
}
140167
}
141168
}
@@ -166,9 +193,9 @@ pub async fn create_original_skeleton_tree<'a, L: Leaf>(
166193
)?;
167194
return Ok(OriginalSkeletonTreeImpl::create_empty(sorted_leaf_indices));
168195
}
169-
let main_subtree = FactsSubTree::create(sorted_leaf_indices, NodeIndex::ROOT, root_hash);
196+
let main_subtree = FactsSubTree { sorted_leaf_indices, root_index: NodeIndex::ROOT, root_hash };
170197
let mut skeleton_tree = OriginalSkeletonTreeImpl { nodes: HashMap::new(), sorted_leaf_indices };
171-
fetch_nodes::<L>(
198+
fetch_nodes::<L, FactsNodeLayout, FactsSubTree<'a>>(
172199
&mut skeleton_tree,
173200
vec![main_subtree],
174201
storage,
@@ -202,7 +229,7 @@ pub async fn create_original_skeleton_tree_and_get_previous_leaves<'a, L: Leaf>(
202229
let main_subtree = FactsSubTree { sorted_leaf_indices, root_index: NodeIndex::ROOT, root_hash };
203230
let mut skeleton_tree = OriginalSkeletonTreeImpl { nodes: HashMap::new(), sorted_leaf_indices };
204231
let mut leaves = HashMap::new();
205-
fetch_nodes::<L>(
232+
fetch_nodes::<L, FactsNodeLayout, FactsSubTree<'a>>(
206233
&mut skeleton_tree,
207234
vec![main_subtree],
208235
storage,
@@ -237,19 +264,40 @@ pub async fn get_leaves<'a, L: Leaf>(
237264

238265
/// Handles a subtree referred by an edge or a binary node. Decides whether we deserialize the
239266
/// referred subtree or not.
240-
fn handle_subtree<'a>(
267+
fn handle_subtree<'a, SubTree: SubTreeTrait<'a>>(
241268
skeleton_tree: &mut OriginalSkeletonTreeImpl<'a>,
242-
next_subtrees: &mut Vec<FactsSubTree<'a>>,
243-
subtree: FactsSubTree<'a>,
269+
next_subtrees: &mut Vec<SubTree>,
270+
subtree: SubTree,
271+
subtree_data: SubTree::NodeData,
244272
should_fetch_modified_leaves: bool,
245273
) {
246-
if !subtree.is_leaf() || (should_fetch_modified_leaves && !subtree.is_unmodified()) {
274+
let is_leaf = subtree.is_leaf();
275+
let is_unmodified = subtree.is_unmodified();
276+
277+
// 1. Internal node → always traverse.
278+
if !is_leaf {
279+
next_subtrees.push(subtree);
280+
return;
281+
}
282+
283+
// 2. Modified leaf.
284+
if !is_unmodified {
285+
if should_fetch_modified_leaves {
286+
next_subtrees.push(subtree);
287+
}
288+
return;
289+
}
290+
291+
// 3. Unmodified leaf sibling.
292+
if !SubTree::should_traverse_unmodified_children() {
293+
skeleton_tree.nodes.insert(
294+
subtree.get_root_index(),
295+
OriginalSkeletonNode::UnmodifiedSubTree(
296+
SubTree::unmodified_child_hash(subtree_data).unwrap(),
297+
),
298+
);
299+
} else {
247300
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));
253301
}
254302
}
255303

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)