|
| 1 | +use std::collections::HashMap; |
| 2 | + |
| 3 | +use rstest_reuse::template; |
| 4 | +use starknet_api::hash::HashOutput; |
| 5 | +use starknet_patricia::db_layout::NodeLayout; |
| 6 | +use starknet_patricia::patricia_merkle_tree::node_data::leaf::{Leaf, LeafModifications}; |
| 7 | +use starknet_patricia::patricia_merkle_tree::original_skeleton_tree::node::OriginalSkeletonNode; |
| 8 | +use starknet_patricia::patricia_merkle_tree::types::{NodeIndex, SortedLeafIndices, SubTreeHeight}; |
| 9 | +use starknet_patricia_storage::db_object::{EmptyKeyContext, HasStaticPrefix}; |
| 10 | +use starknet_patricia_storage::map_storage::MapStorage; |
| 11 | + |
| 12 | +use crate::db::trie_traversal::create_original_skeleton_tree; |
| 13 | +use crate::patricia_merkle_tree::tree::OriginalSkeletonTrieConfig; |
| 14 | + |
| 15 | +pub(crate) mod case_helpers { |
| 16 | + use std::collections::HashMap; |
| 17 | + |
| 18 | + use starknet_api::hash::HashOutput; |
| 19 | + use starknet_patricia::patricia_merkle_tree::external_test_utils::{ |
| 20 | + create_binary_entry_from_u128, |
| 21 | + create_binary_skeleton_node, |
| 22 | + create_edge_entry_from_u128, |
| 23 | + create_edge_skeleton_node, |
| 24 | + create_expected_skeleton_nodes, |
| 25 | + create_root_edge_entry, |
| 26 | + create_unmodified_subtree_skeleton_node, |
| 27 | + AdditionHash, |
| 28 | + MockLeaf, |
| 29 | + }; |
| 30 | + use starknet_patricia::patricia_merkle_tree::node_data::leaf::LeafModifications; |
| 31 | + use starknet_patricia::patricia_merkle_tree::original_skeleton_tree::node::OriginalSkeletonNode; |
| 32 | + use starknet_patricia::patricia_merkle_tree::types::{NodeIndex, SubTreeHeight}; |
| 33 | + use starknet_patricia_storage::db_object::{DBObject, EmptyKeyContext}; |
| 34 | + use starknet_patricia_storage::map_storage::MapStorage; |
| 35 | + use starknet_patricia_storage::storage_trait::{DbHashMap, DbKey, DbValue}; |
| 36 | + use starknet_types_core::felt::Felt; |
| 37 | + |
| 38 | + pub(crate) fn create_mock_leaf_entry(val: u128) -> (DbKey, DbValue) { |
| 39 | + let leaf = MockLeaf(Felt::from(val)); |
| 40 | + (leaf.get_db_key(&EmptyKeyContext, &leaf.0.to_bytes_be()), leaf.serialize().unwrap()) |
| 41 | + } |
| 42 | + |
| 43 | + fn create_mock_leaf_modifications( |
| 44 | + leaf_modifications: Vec<(u128, u128)>, |
| 45 | + ) -> LeafModifications<MockLeaf> { |
| 46 | + leaf_modifications |
| 47 | + .into_iter() |
| 48 | + .map(|(idx, val)| (NodeIndex::from(idx), MockLeaf(Felt::from(val)))) |
| 49 | + .collect() |
| 50 | + } |
| 51 | + |
| 52 | + pub(crate) struct CreateTreeCase { |
| 53 | + pub storage: MapStorage, |
| 54 | + pub leaf_modifications: LeafModifications<MockLeaf>, |
| 55 | + pub root_hash: HashOutput, |
| 56 | + pub expected_skeleton_nodes: HashMap<NodeIndex, OriginalSkeletonNode>, |
| 57 | + pub subtree_height: SubTreeHeight, |
| 58 | + } |
| 59 | + |
| 60 | + pub(crate) fn simple_tree_of_height_3() -> CreateTreeCase { |
| 61 | + CreateTreeCase { |
| 62 | + storage: MapStorage(DbHashMap::from([ |
| 63 | + create_root_edge_entry(50, SubTreeHeight::new(3)), |
| 64 | + create_binary_entry_from_u128::<AdditionHash>(8, 9), |
| 65 | + create_edge_entry_from_u128::<AdditionHash>(11, 1, 1), |
| 66 | + create_binary_entry_from_u128::<AdditionHash>(17, 13), |
| 67 | + create_edge_entry_from_u128::<AdditionHash>(15, 3, 2), |
| 68 | + create_binary_entry_from_u128::<AdditionHash>(30, 20), |
| 69 | + create_mock_leaf_entry(8), |
| 70 | + create_mock_leaf_entry(9), |
| 71 | + create_mock_leaf_entry(11), |
| 72 | + create_mock_leaf_entry(15), |
| 73 | + ])), |
| 74 | + leaf_modifications: create_mock_leaf_modifications(vec![(8, 8), (10, 3), (13, 2)]), |
| 75 | + root_hash: HashOutput(Felt::from(50_u128 + 248_u128)), |
| 76 | + expected_skeleton_nodes: create_expected_skeleton_nodes( |
| 77 | + vec![ |
| 78 | + create_binary_skeleton_node(1), |
| 79 | + create_binary_skeleton_node(2), |
| 80 | + create_edge_skeleton_node(3, 3, 2), |
| 81 | + create_binary_skeleton_node(4), |
| 82 | + create_edge_skeleton_node(5, 1, 1), |
| 83 | + create_unmodified_subtree_skeleton_node(9, 9), |
| 84 | + create_unmodified_subtree_skeleton_node(15, 15), |
| 85 | + create_unmodified_subtree_skeleton_node(11, 11), |
| 86 | + ], |
| 87 | + 3, |
| 88 | + ), |
| 89 | + subtree_height: SubTreeHeight::new(3), |
| 90 | + } |
| 91 | + } |
| 92 | + |
| 93 | + pub(crate) fn another_simple_tree_of_height_3() -> CreateTreeCase { |
| 94 | + CreateTreeCase { |
| 95 | + storage: MapStorage(DbHashMap::from([ |
| 96 | + create_root_edge_entry(29, SubTreeHeight::new(3)), |
| 97 | + create_binary_entry_from_u128::<AdditionHash>(10, 2), |
| 98 | + create_edge_entry_from_u128::<AdditionHash>(3, 1, 1), |
| 99 | + create_binary_entry_from_u128::<AdditionHash>(4, 7), |
| 100 | + create_edge_entry_from_u128::<AdditionHash>(12, 0, 1), |
| 101 | + create_binary_entry_from_u128::<AdditionHash>(5, 11), |
| 102 | + create_binary_entry_from_u128::<AdditionHash>(13, 16), |
| 103 | + create_mock_leaf_entry(10), |
| 104 | + create_mock_leaf_entry(2), |
| 105 | + create_mock_leaf_entry(3), |
| 106 | + create_mock_leaf_entry(4), |
| 107 | + create_mock_leaf_entry(7), |
| 108 | + ])), |
| 109 | + leaf_modifications: create_mock_leaf_modifications(vec![(8, 5), (11, 1), (13, 3)]), |
| 110 | + root_hash: HashOutput(Felt::from(29_u128 + 248_u128)), |
| 111 | + expected_skeleton_nodes: create_expected_skeleton_nodes( |
| 112 | + vec![ |
| 113 | + create_binary_skeleton_node(1), |
| 114 | + create_edge_skeleton_node(2, 0, 1), |
| 115 | + create_binary_skeleton_node(3), |
| 116 | + create_binary_skeleton_node(4), |
| 117 | + create_edge_skeleton_node(6, 1, 1), |
| 118 | + create_unmodified_subtree_skeleton_node(7, 11), |
| 119 | + create_unmodified_subtree_skeleton_node(9, 2), |
| 120 | + ], |
| 121 | + 3, |
| 122 | + ), |
| 123 | + subtree_height: SubTreeHeight::new(3), |
| 124 | + } |
| 125 | + } |
| 126 | + |
| 127 | + pub(crate) fn tree_of_height_4_with_long_edge() -> CreateTreeCase { |
| 128 | + CreateTreeCase { |
| 129 | + storage: MapStorage(DbHashMap::from([ |
| 130 | + create_root_edge_entry(116, SubTreeHeight::new(4)), |
| 131 | + create_binary_entry_from_u128::<AdditionHash>(11, 13), |
| 132 | + create_edge_entry_from_u128::<AdditionHash>(5, 0, 1), |
| 133 | + create_binary_entry_from_u128::<AdditionHash>(19, 40), |
| 134 | + create_edge_entry_from_u128::<AdditionHash>(20, 3, 2), |
| 135 | + create_binary_entry_from_u128::<AdditionHash>(6, 59), |
| 136 | + create_edge_entry_from_u128::<AdditionHash>(24, 0, 2), |
| 137 | + create_binary_entry_from_u128::<AdditionHash>(25, 65), |
| 138 | + create_binary_entry_from_u128::<AdditionHash>(26, 90), |
| 139 | + create_mock_leaf_entry(11), |
| 140 | + create_mock_leaf_entry(13), |
| 141 | + create_mock_leaf_entry(20), |
| 142 | + create_mock_leaf_entry(5), |
| 143 | + create_mock_leaf_entry(19), |
| 144 | + create_mock_leaf_entry(40), |
| 145 | + ])), |
| 146 | + leaf_modifications: create_mock_leaf_modifications(vec![ |
| 147 | + (18, 5), |
| 148 | + (25, 1), |
| 149 | + (29, 15), |
| 150 | + (30, 19), |
| 151 | + ]), |
| 152 | + root_hash: HashOutput(Felt::from(116_u128 + 247_u128)), |
| 153 | + expected_skeleton_nodes: create_expected_skeleton_nodes( |
| 154 | + vec![ |
| 155 | + create_binary_skeleton_node(1), |
| 156 | + create_edge_skeleton_node(2, 0, 2), |
| 157 | + create_binary_skeleton_node(3), |
| 158 | + create_edge_skeleton_node(6, 3, 2), |
| 159 | + create_binary_skeleton_node(7), |
| 160 | + create_unmodified_subtree_skeleton_node(8, 24), |
| 161 | + create_edge_skeleton_node(14, 0, 1), |
| 162 | + create_binary_skeleton_node(15), |
| 163 | + create_unmodified_subtree_skeleton_node(27, 20), |
| 164 | + create_unmodified_subtree_skeleton_node(28, 5), |
| 165 | + create_unmodified_subtree_skeleton_node(31, 40), |
| 166 | + ], |
| 167 | + 4, |
| 168 | + ), |
| 169 | + subtree_height: SubTreeHeight::new(4), |
| 170 | + } |
| 171 | + } |
| 172 | +} |
| 173 | + |
| 174 | +pub(crate) async fn test_create_original_skeleton<L, Layout>( |
| 175 | + storage: &mut MapStorage, |
| 176 | + leaf_modifications: &LeafModifications<L>, |
| 177 | + root_hash: HashOutput, |
| 178 | + expected_skeleton_nodes: &HashMap<NodeIndex, OriginalSkeletonNode>, |
| 179 | + subtree_height: SubTreeHeight, |
| 180 | + compare_modified_leaves: bool, |
| 181 | +) where |
| 182 | + L: Leaf + HasStaticPrefix<KeyContext = EmptyKeyContext>, |
| 183 | + Layout: for<'a> NodeLayout<'a, L>, |
| 184 | +{ |
| 185 | + let leaf_modifications: LeafModifications<L> = leaf_modifications |
| 186 | + .iter() |
| 187 | + .map(|(idx, leaf)| (NodeIndex::from_subtree_index(*idx, subtree_height), leaf.clone())) |
| 188 | + .collect(); |
| 189 | + |
| 190 | + let config = OriginalSkeletonTrieConfig::new_for_tests(compare_modified_leaves); |
| 191 | + |
| 192 | + let mut sorted_leaf_indices: Vec<NodeIndex> = leaf_modifications.keys().copied().collect(); |
| 193 | + let sorted_leaf_indices = SortedLeafIndices::new(&mut sorted_leaf_indices); |
| 194 | + |
| 195 | + let skeleton_tree = create_original_skeleton_tree::<L, Layout>( |
| 196 | + storage, |
| 197 | + root_hash, |
| 198 | + sorted_leaf_indices, |
| 199 | + &config, |
| 200 | + &leaf_modifications, |
| 201 | + None, |
| 202 | + &EmptyKeyContext, |
| 203 | + ) |
| 204 | + .await |
| 205 | + .unwrap(); |
| 206 | + |
| 207 | + assert_eq!(&skeleton_tree.nodes, expected_skeleton_nodes); |
| 208 | +} |
| 209 | + |
| 210 | +#[template] |
| 211 | +#[rstest] |
| 212 | +// This test uses addition hash for simplicity (i.e hash(a,b) = a + b). |
| 213 | +/// Old tree structure: |
| 214 | +/// |
| 215 | +/// 50 |
| 216 | +/// / \ |
| 217 | +/// 30 20 |
| 218 | +/// / \ \ |
| 219 | +/// 17 13 * |
| 220 | +/// / \ \ \ |
| 221 | +/// 8 9 11 15 |
| 222 | +/// |
| 223 | +/// Modified leaves indices: [8, 10, 13] |
| 224 | +/// |
| 225 | +/// Expected skeleton: |
| 226 | +/// |
| 227 | +/// B |
| 228 | +/// / \ |
| 229 | +/// B E |
| 230 | +/// / \ \ |
| 231 | +/// B E * |
| 232 | +/// / \ \ \ |
| 233 | +/// NZ 9 11 15 |
| 234 | +
|
| 235 | +#[case::simple_tree_of_height_3( |
| 236 | + crate::db::create_original_skeleton_tests::case_helpers::simple_tree_of_height_3() |
| 237 | +)] |
| 238 | +/// Old tree structure: |
| 239 | +/// |
| 240 | +/// 29 |
| 241 | +/// / \ |
| 242 | +/// 13 16 |
| 243 | +/// / / \ |
| 244 | +/// 12 5 11 |
| 245 | +/// / \ \ / \ |
| 246 | +/// 10 2 3 4 7 |
| 247 | +/// |
| 248 | +/// Modified leaves indices: [8, 11, 13] |
| 249 | +/// |
| 250 | +/// Expected skeleton: |
| 251 | +/// |
| 252 | +/// B |
| 253 | +/// / \ |
| 254 | +/// E B |
| 255 | +/// / / \ |
| 256 | +/// B E E |
| 257 | +/// / \ \ \ |
| 258 | +/// NZ 2 NZ NZ |
| 259 | +#[case::another_simple_tree_of_height_3( |
| 260 | + crate::db::create_original_skeleton_tests::case_helpers::another_simple_tree_of_height_3() |
| 261 | +)] |
| 262 | +/// Old tree structure: |
| 263 | +/// |
| 264 | +/// 116 |
| 265 | +/// / \ |
| 266 | +/// 26 90 |
| 267 | +/// / / \ |
| 268 | +/// / 25 65 |
| 269 | +/// / \ / \ |
| 270 | +/// 24 \ 6 59 |
| 271 | +/// / \ \ / / \ |
| 272 | +/// 11 13 20 5 19 40 |
| 273 | +/// |
| 274 | +/// Modified leaves indices: [18, 25, 29, 30] |
| 275 | +/// |
| 276 | +/// Expected skeleton: |
| 277 | +/// |
| 278 | +/// B |
| 279 | +/// / \ |
| 280 | +/// E B |
| 281 | +/// / / \ |
| 282 | +/// / E B |
| 283 | +/// / \ / \ |
| 284 | +/// 24 \ E B |
| 285 | +/// \ / \ |
| 286 | +/// 20 5 40 |
| 287 | +#[case::tree_of_height_4_with_long_edge( |
| 288 | + crate::db::create_original_skeleton_tests::case_helpers::tree_of_height_4_with_long_edge() |
| 289 | +)] |
| 290 | +pub fn create_tree_cases( |
| 291 | + #[case] mut case: case_helpers::CreateTreeCase, |
| 292 | + #[values(true, false)] compare_modified_leaves: bool, |
| 293 | +) { |
| 294 | +} |
0 commit comments