|
1 | | -use std::borrow::Borrow; |
2 | 1 | use std::collections::HashMap; |
3 | | -use std::fmt::Debug; |
4 | 2 |
|
5 | 3 | 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 | | -}; |
12 | 4 | use starknet_patricia::patricia_merkle_tree::node_data::leaf::{Leaf, LeafModifications}; |
13 | 5 | use starknet_patricia::patricia_merkle_tree::original_skeleton_tree::config::OriginalSkeletonTreeConfig; |
14 | | -use starknet_patricia::patricia_merkle_tree::original_skeleton_tree::node::OriginalSkeletonNode; |
15 | 6 | use starknet_patricia::patricia_merkle_tree::original_skeleton_tree::tree::{ |
16 | 7 | OriginalSkeletonTreeImpl, |
17 | 8 | OriginalSkeletonTreeResult, |
18 | 9 | }; |
19 | | -use starknet_patricia::patricia_merkle_tree::traversal::{SubTreeTrait, UnmodifiedChildTraversal}; |
| 10 | +use starknet_patricia::patricia_merkle_tree::traversal::SubTreeTrait; |
20 | 11 | 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; |
22 | 13 | use starknet_patricia_storage::storage_trait::Storage; |
23 | | -use tracing::warn; |
24 | 14 |
|
25 | | -use crate::db::db_layout::NodeLayout; |
26 | 15 | use crate::db::facts_db::db::FactsNodeLayout; |
27 | | -use crate::db::facts_db::traversal::get_roots_from_storage; |
28 | 16 | use crate::db::facts_db::types::FactsSubTree; |
| 17 | +use crate::db::trie_traversal::{fetch_nodes, log_warning_for_empty_leaves}; |
29 | 18 | use crate::patricia_merkle_tree::tree::OriginalSkeletonTrieDontCompareConfig; |
30 | 19 |
|
31 | 20 | #[cfg(test)] |
32 | 21 | #[path = "create_facts_tree_test.rs"] |
33 | 22 | pub mod create_facts_tree_test; |
34 | 23 |
|
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>(¤t_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 | | - |
152 | 24 | pub async fn create_original_skeleton_tree<'a, L: Leaf>( |
153 | 25 | storage: &mut impl Storage, |
154 | 26 | root_hash: HashOutput, |
@@ -239,103 +111,3 @@ pub async fn get_leaves<'a, L: Leaf>( |
239 | 111 | .await?; |
240 | 112 | Ok(previous_leaves) |
241 | 113 | } |
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 | | -} |
0 commit comments