@@ -3,6 +3,7 @@ use std::collections::HashMap;
33use std:: fmt:: Debug ;
44
55use starknet_api:: hash:: HashOutput ;
6+ use starknet_patricia:: patricia_merkle_tree:: filled_tree:: node:: FilledNode ;
67use starknet_patricia:: patricia_merkle_tree:: node_data:: inner_node:: {
78 BinaryData ,
89 EdgeData ,
@@ -18,13 +19,15 @@ use starknet_patricia::patricia_merkle_tree::original_skeleton_tree::tree::{
1819 OriginalSkeletonTreeImpl ,
1920 OriginalSkeletonTreeResult ,
2021} ;
21- use starknet_patricia:: patricia_merkle_tree:: traversal:: SubTreeTrait ;
22+ use starknet_patricia:: patricia_merkle_tree:: traversal:: { SubTreeTrait , UnmodifiedChildTraversal } ;
2223use starknet_patricia:: patricia_merkle_tree:: types:: { NodeIndex , SortedLeafIndices } ;
23- use starknet_patricia_storage:: db_object:: HasStaticPrefix ;
24+ use starknet_patricia_storage:: db_object:: { DBObject , HasStaticPrefix } ;
2425use starknet_patricia_storage:: storage_trait:: Storage ;
2526use tracing:: warn;
2627
27- use crate :: db:: facts_db:: traversal:: calculate_subtrees_roots;
28+ use crate :: db:: db_layout:: NodeLayout ;
29+ use crate :: db:: facts_db:: db:: FactsNodeLayout ;
30+ use crate :: db:: facts_db:: traversal:: get_roots_from_storage;
2831use crate :: db:: facts_db:: types:: FactsSubTree ;
2932
3033#[ cfg( test) ]
@@ -39,68 +42,72 @@ macro_rules! log_trivial_modification {
3942}
4043
4144/// Fetches the Patricia witnesses, required to build the original skeleton tree from storage.
45+ ///
4246/// Given a list of subtrees, traverses towards their leaves and fetches all non-empty,
4347/// unmodified nodes. If `compare_modified_leaves` is set, function logs out a warning when
4448/// encountering a trivial modification. Fills the previous leaf values if it is not none.
45- async fn fetch_nodes < ' a , L : Leaf > (
49+ ///
50+ /// The function is generic over the DB layout (`Layout`), which controls the concrete node data
51+ /// (`Layout::NodeData`) and traversal strategy (via `Layout::SubTree`).
52+ async fn fetch_nodes < ' a , L , Layout > (
4653 skeleton_tree : & mut OriginalSkeletonTreeImpl < ' a > ,
47- subtrees : Vec < FactsSubTree < ' a > > ,
54+ subtrees : Vec < Layout :: SubTree > ,
4855 storage : & mut impl Storage ,
4956 leaf_modifications : & LeafModifications < L > ,
5057 config : & impl OriginalSkeletonTreeConfig < L > ,
5158 mut previous_leaves : Option < & mut HashMap < NodeIndex , L > > ,
5259 key_context : & <L as HasStaticPrefix >:: KeyContext ,
53- ) -> OriginalSkeletonTreeResult < ( ) > {
60+ ) -> OriginalSkeletonTreeResult < ( ) >
61+ where
62+ L : Leaf ,
63+ Layout : NodeLayout < ' a , L > ,
64+ FilledNode < L , Layout :: NodeData > : DBObject < DeserializeContext = Layout :: DeserializationContext > ,
65+ {
5466 let mut current_subtrees = subtrees;
5567 let mut next_subtrees = Vec :: new ( ) ;
68+ let should_fetch_modified_leaves =
69+ config. compare_modified_leaves ( ) || previous_leaves. is_some ( ) ;
5670 while !current_subtrees. is_empty ( ) {
57- let should_fetch_modified_leaves =
58- config. compare_modified_leaves ( ) || previous_leaves. is_some ( ) ;
5971 let filled_roots =
60- calculate_subtrees_roots :: < L > ( & current_subtrees, storage, key_context) . await ?;
61- for ( filled_root, subtree) in filled_roots. into_iter ( ) . zip ( current_subtrees. iter ( ) ) {
72+ get_roots_from_storage :: < L , Layout > ( & current_subtrees, storage, key_context) . await ?;
73+ for ( filled_root, subtree) in filled_roots. into_iter ( ) . zip ( current_subtrees. into_iter ( ) ) {
74+ if subtree. is_unmodified ( ) {
75+ handle_unmodified_subtree ( skeleton_tree, & mut next_subtrees, filled_root, subtree) ;
76+ continue ;
77+ }
78+ let root_index = subtree. get_root_index ( ) ;
6279 match filled_root. data {
6380 // Binary node.
6481 NodeData :: Binary ( BinaryData { left_data, right_data } ) => {
65- if subtree. is_unmodified ( ) {
66- skeleton_tree. nodes . insert (
67- subtree. root_index ,
68- OriginalSkeletonNode :: UnmodifiedSubTree ( filled_root. hash ) ,
69- ) ;
70- continue ;
71- }
72- skeleton_tree. nodes . insert ( subtree. root_index , OriginalSkeletonNode :: Binary ) ;
82+ skeleton_tree. nodes . insert ( root_index, OriginalSkeletonNode :: Binary ) ;
7383 let ( left_subtree, right_subtree) =
7484 subtree. get_children_subtrees ( left_data, right_data) ;
7585
76- handle_subtree (
86+ handle_child_subtree (
7787 skeleton_tree,
7888 & mut next_subtrees,
7989 left_subtree,
90+ left_data,
8091 should_fetch_modified_leaves,
8192 ) ;
82- handle_subtree (
93+ handle_child_subtree (
8394 skeleton_tree,
8495 & mut next_subtrees,
8596 right_subtree,
97+ right_data,
8698 should_fetch_modified_leaves,
8799 )
88100 }
89101 // Edge node.
90102 NodeData :: Edge ( EdgeData { bottom_data, path_to_bottom } ) => {
91103 skeleton_tree
92104 . nodes
93- . insert ( subtree. root_index , OriginalSkeletonNode :: Edge ( path_to_bottom) ) ;
94- if subtree. is_unmodified ( ) {
95- skeleton_tree. nodes . insert (
96- path_to_bottom. bottom_index ( subtree. root_index ) ,
97- OriginalSkeletonNode :: UnmodifiedSubTree ( bottom_data) ,
98- ) ;
99- continue ;
100- }
105+ . insert ( root_index, OriginalSkeletonNode :: Edge ( path_to_bottom) ) ;
106+
101107 // Parse bottom.
102108 let ( bottom_subtree, previously_empty_leaves_indices) =
103109 subtree. get_bottom_subtree ( & path_to_bottom, bottom_data) ;
110+
104111 if let Some ( ref mut leaves) = previous_leaves {
105112 leaves. extend (
106113 previously_empty_leaves_indices
@@ -115,28 +122,25 @@ async fn fetch_nodes<'a, L: Leaf>(
115122 config,
116123 ) ?;
117124
118- handle_subtree (
125+ handle_child_subtree (
119126 skeleton_tree,
120127 & mut next_subtrees,
121128 bottom_subtree,
129+ bottom_data,
122130 should_fetch_modified_leaves,
123131 ) ;
124132 }
125133 // Leaf node.
126134 NodeData :: Leaf ( previous_leaf) => {
127- if subtree. is_unmodified ( ) {
128- warn ! ( "Unexpectedly deserialized leaf sibling." )
129- } else {
130- // Modified leaf.
131- if config. compare_modified_leaves ( )
132- && L :: compare ( leaf_modifications, & subtree. root_index , & previous_leaf) ?
133- {
134- log_trivial_modification ! ( subtree. root_index, previous_leaf) ;
135- }
136- // If previous values of modified leaves are requested, add this leaf.
137- if let Some ( ref mut leaves) = previous_leaves {
138- leaves. insert ( subtree. root_index , previous_leaf) ;
139- }
135+ // Modified leaf.
136+ if config. compare_modified_leaves ( )
137+ && L :: compare ( leaf_modifications, & root_index, & previous_leaf) ?
138+ {
139+ log_trivial_modification ! ( root_index, previous_leaf) ;
140+ }
141+ // If previous values of modified leaves are requested, add this leaf.
142+ if let Some ( ref mut leaves) = previous_leaves {
143+ leaves. insert ( root_index, previous_leaf) ;
140144 }
141145 }
142146 }
@@ -168,7 +172,7 @@ pub async fn create_original_skeleton_tree<'a, L: Leaf>(
168172 }
169173 let main_subtree = FactsSubTree :: create ( sorted_leaf_indices, NodeIndex :: ROOT , root_hash) ;
170174 let mut skeleton_tree = OriginalSkeletonTreeImpl { nodes : HashMap :: new ( ) , sorted_leaf_indices } ;
171- fetch_nodes :: < L > (
175+ fetch_nodes :: < L , FactsNodeLayout > (
172176 & mut skeleton_tree,
173177 vec ! [ main_subtree] ,
174178 storage,
@@ -202,7 +206,7 @@ pub async fn create_original_skeleton_tree_and_get_previous_leaves<'a, L: Leaf>(
202206 let main_subtree = FactsSubTree :: create ( sorted_leaf_indices, NodeIndex :: ROOT , root_hash) ;
203207 let mut skeleton_tree = OriginalSkeletonTreeImpl { nodes : HashMap :: new ( ) , sorted_leaf_indices } ;
204208 let mut leaves = HashMap :: new ( ) ;
205- fetch_nodes :: < L > (
209+ fetch_nodes :: < L , FactsNodeLayout > (
206210 & mut skeleton_tree,
207211 vec ! [ main_subtree] ,
208212 storage,
@@ -238,21 +242,86 @@ pub async fn get_leaves<'a, L: Leaf>(
238242 Ok ( previous_leaves)
239243}
240244
245+ /// Adds the subtree root to the skeleton tree. If the root is an edge node, and
246+ /// `should_traverse_unmodified_child` is `Skip`, then the corresponding bottom node is also
247+ /// added to the skeleton. Otherwise, the bottom subtree is added to the next subtrees.
248+ fn handle_unmodified_subtree < ' a , L : Leaf , SubTree : SubTreeTrait < ' a > > (
249+ skeleton_tree : & mut OriginalSkeletonTreeImpl < ' a > ,
250+ next_subtrees : & mut Vec < SubTree > ,
251+ filled_root : FilledNode < L , SubTree :: NodeData > ,
252+ subtree : SubTree ,
253+ ) where
254+ SubTree :: NodeData : Copy ,
255+ {
256+ // Sanity check.
257+ assert ! ( subtree. is_unmodified( ) , "Called `handle_unmodified_subtree` for a modified subtree." ) ;
258+
259+ let root_index = subtree. get_root_index ( ) ;
260+
261+ match filled_root. data {
262+ NodeData :: Edge ( EdgeData { bottom_data, path_to_bottom } ) => {
263+ // Even if a subtree rooted at an edge node is unmodified, we still need an
264+ // `OriginalSkeletonNode::Edge` node in the skeleton in case we need to manipulate it
265+ // later (e.g. unify the edge node with an ancestor edge node).
266+ skeleton_tree. nodes . insert ( root_index, OriginalSkeletonNode :: Edge ( path_to_bottom) ) ;
267+ match SubTree :: should_traverse_unmodified_child ( bottom_data) {
268+ UnmodifiedChildTraversal :: Traverse => {
269+ let ( bottom_subtree, _) =
270+ subtree. get_bottom_subtree ( & path_to_bottom, bottom_data) ;
271+ next_subtrees. push ( bottom_subtree) ;
272+ }
273+ UnmodifiedChildTraversal :: Skip ( unmodified_child_hash) => {
274+ skeleton_tree. nodes . insert (
275+ path_to_bottom. bottom_index ( root_index) ,
276+ OriginalSkeletonNode :: UnmodifiedSubTree ( unmodified_child_hash) ,
277+ ) ;
278+ }
279+ }
280+ }
281+ NodeData :: Binary ( _) | NodeData :: Leaf ( _) => {
282+ skeleton_tree
283+ . nodes
284+ . insert ( root_index, OriginalSkeletonNode :: UnmodifiedSubTree ( filled_root. hash ) ) ;
285+ }
286+ }
287+ }
288+
241289/// Handles a subtree referred by an edge or a binary node. Decides whether we deserialize the
242- /// referred subtree or not.
243- fn handle_subtree < ' a > (
290+ /// referred subtree or not, and if we should continue traversing the child's direction .
291+ fn handle_child_subtree < ' a , SubTree : SubTreeTrait < ' a > > (
244292 skeleton_tree : & mut OriginalSkeletonTreeImpl < ' a > ,
245- next_subtrees : & mut Vec < FactsSubTree < ' a > > ,
246- subtree : FactsSubTree < ' a > ,
293+ next_subtrees : & mut Vec < SubTree > ,
294+ subtree : SubTree ,
295+ subtree_data : SubTree :: NodeData ,
247296 should_fetch_modified_leaves : bool ,
248297) {
249- if !subtree. is_leaf ( ) || ( should_fetch_modified_leaves && !subtree. is_unmodified ( ) ) {
298+ let is_modified = !subtree. is_unmodified ( ) ;
299+
300+ // Internal node → always traverse.
301+ if !subtree. is_leaf ( ) {
250302 next_subtrees. push ( subtree) ;
251- } else if subtree. is_unmodified ( ) {
252- // Leaf sibling.
253- skeleton_tree
254- . nodes
255- . insert ( subtree. root_index , OriginalSkeletonNode :: UnmodifiedSubTree ( subtree. root_hash ) ) ;
303+ return ;
304+ }
305+
306+ // Modified leaf.
307+ if is_modified {
308+ if should_fetch_modified_leaves {
309+ next_subtrees. push ( subtree) ;
310+ }
311+ return ;
312+ }
313+
314+ // Unmodified leaf sibling.
315+ match SubTree :: should_traverse_unmodified_child ( subtree_data) {
316+ UnmodifiedChildTraversal :: Traverse => {
317+ next_subtrees. push ( subtree) ;
318+ }
319+ UnmodifiedChildTraversal :: Skip ( unmodified_child_hash) => {
320+ skeleton_tree. nodes . insert (
321+ subtree. get_root_index ( ) ,
322+ OriginalSkeletonNode :: UnmodifiedSubTree ( unmodified_child_hash) ,
323+ ) ;
324+ }
256325 }
257326}
258327
0 commit comments