1mod branch_child_idx;
2mod cursor;
3mod nodes;
4
5use branch_child_idx::{BranchChildIdx, BranchChildIter};
6use cursor::{ArenaCursor, NextResult, SeekResult};
7use nodes::{
8 ArenaSparseNode, ArenaSparseNodeBranch, ArenaSparseNodeBranchChild, ArenaSparseNodeState,
9};
10
11use crate::{
12 LeafLookup, LeafLookupError, LeafUpdate, SparseTrie, SparseTrieUpdates, TrieNodeEpoch,
13};
14use alloc::{borrow::Cow, boxed::Box, collections::VecDeque, vec::Vec};
15use alloy_primitives::{keccak256, map::B256Map, B256};
16use alloy_trie::TrieMask;
17use core::{cmp::Reverse, mem};
18use reth_execution_errors::SparseTrieResult;
19use reth_trie_common::{
20 BranchNodeMasks, BranchNodeRef, ExtensionNodeRef, LeafNodeRef, Nibbles, ProofTrieNodeV2,
21 ProofV2TargetParent, RlpNode, TrieNodeV2, EMPTY_ROOT_HASH,
22};
23use slotmap::{DefaultKey, SlotMap};
24use smallvec::SmallVec;
25use tracing::{instrument, trace};
26
27type Index = DefaultKey;
29type NodeArena = SlotMap<Index, ArenaSparseNode>;
31
32const TRACE_TARGET: &str = "trie::arena";
33
34const UPPER_TRIE_MAX_DEPTH: usize = 2;
37
38fn compact_arena(arena: &mut NodeArena, root: &mut Index) {
42 let mut new_arena = SlotMap::with_capacity(arena.len());
43 let mut queue = VecDeque::new();
44
45 let root_node = arena.remove(*root).expect("root exists");
46 let new_root = new_arena.insert(root_node);
47 queue.push_back(new_root);
48
49 while let Some(new_idx) = queue.pop_front() {
50 let old_children: SmallVec<[(usize, Index); 16]> = match &new_arena[new_idx] {
55 ArenaSparseNode::Branch(b) => b
56 .children
57 .iter()
58 .enumerate()
59 .filter_map(|(i, c)| match c {
60 ArenaSparseNodeBranchChild::Revealed(old_idx) => Some((i, *old_idx)),
61 _ => None,
62 })
63 .collect(),
64 _ => continue,
65 };
66
67 for (child_pos, old_child_idx) in old_children {
68 let child_node = arena.remove(old_child_idx).expect("child exists");
69 let new_child_idx = new_arena.insert(child_node);
70 let ArenaSparseNode::Branch(b) = &mut new_arena[new_idx] else { unreachable!() };
71 b.children[child_pos] = ArenaSparseNodeBranchChild::Revealed(new_child_idx);
72 queue.push_back(new_child_idx);
73 }
74 }
75
76 debug_assert!(
77 arena.is_empty(),
78 "compact_arena: {} orphaned nodes remaining after BFS drain",
79 arena.len(),
80 );
81
82 *arena = new_arena;
83 *root = new_root;
84}
85
86#[derive(Debug, Default, Clone)]
89struct ArenaTrieBuffers {
90 cursor: ArenaCursor,
92 updates: Option<SparseTrieUpdates>,
95 rlp_buf: Vec<u8>,
97 rlp_node_buf: Vec<RlpNode>,
99}
100
101impl ArenaTrieBuffers {
102 fn clear(&mut self) {
103 if let Some(updates) = self.updates.as_mut() {
104 updates.clear();
105 }
106 self.rlp_buf.clear();
107 self.rlp_node_buf.clear();
108 }
109}
110
111#[derive(Debug, Clone)]
115struct ArenaSparseSubtrie {
116 arena: NodeArena,
118 root: Index,
120 path: Nibbles,
122 buffers: ArenaTrieBuffers,
124 required_proofs: Vec<(usize, ArenaRequiredProof)>,
128 num_leaves: u64,
130 num_dirty_leaves: u64,
132}
133
134impl ArenaSparseSubtrie {
135 fn new(record_updates: bool) -> Box<Self> {
139 let mut arena = SlotMap::new();
140 let root =
141 arena.insert(ArenaSparseNode::EmptyRoot { state: ArenaSparseNodeState::Revealed });
142 let buffers = ArenaTrieBuffers {
143 updates: record_updates.then(SparseTrieUpdates::default),
144 ..Default::default()
145 };
146 Box::new(Self {
147 arena,
148 root,
149 path: Nibbles::default(),
150 buffers,
151 required_proofs: Vec::new(),
152 num_leaves: 0,
153 num_dirty_leaves: 0,
154 })
155 }
156
157 #[cfg(debug_assertions)]
159 fn debug_assert_counters(&self) {
160 let (actual_leaves, actual_dirty) =
161 ArenaParallelSparseTrie::count_leaves_and_dirty(&self.arena, self.root);
162 debug_assert_eq!(
163 self.num_leaves, actual_leaves,
164 "subtrie {:?} num_leaves mismatch: stored {} vs actual {}",
165 self.path, self.num_leaves, actual_leaves,
166 );
167 debug_assert_eq!(
168 self.num_dirty_leaves, actual_dirty,
169 "subtrie {:?} num_dirty_leaves mismatch: stored {} vs actual {}",
170 self.path, self.num_dirty_leaves, actual_dirty,
171 );
172 }
173
174 fn prune(&mut self, prune_before: TrieNodeEpoch) -> usize {
179 if !matches!(&self.arena[self.root], ArenaSparseNode::Branch(_)) {
181 return 0;
182 }
183
184 debug_assert_eq!(self.num_dirty_leaves, 0, "prune must run after hashing");
185
186 if prune_before == TrieNodeEpoch::UNMODIFIED {
187 return 0;
188 }
189
190 let old_count = self.arena.len();
191 let mut new_arena = SlotMap::new();
193 let mut new_num_leaves = 0u64;
194
195 let root_node = self.arena.remove(self.root).expect("root exists");
197 let new_root = new_arena.insert(root_node);
198 let mut stack = Vec::new();
199 if let Some(frame) =
200 prepare_retained_node(&new_arena, new_root, self.path, &mut new_num_leaves)
201 {
202 stack.push(frame);
203 }
204
205 while let Some(frame) = stack.last_mut() {
206 let Some((child_pos, nibble, old_child_idx)) = frame.next_revealed_child(&new_arena)
207 else {
208 stack.pop();
209 continue;
210 };
211
212 let parent_new_idx = frame.new_idx;
213 let mut child_path = frame.branch_logical_path;
214 child_path.push(nibble);
215
216 let child_epoch = self.arena[old_child_idx]
217 .state_ref()
218 .and_then(ArenaSparseNodeState::cached_epoch)
219 .expect("prune must run after hashing");
220
221 if child_epoch.should_prune(prune_before) {
222 let node = &self.arena[old_child_idx];
223 let rlp_node = node
224 .state_ref()
225 .and_then(ArenaSparseNodeState::cached_rlp_node)
226 .cloned()
227 .expect("prune must run after hashing");
228 trace!(
229 target: TRACE_TARGET,
230 path = ?child_path,
231 variant = %AsRef::<str>::as_ref(node),
232 cached_rlp_node = ?rlp_node,
233 "pruning node",
234 );
235 let ArenaSparseNode::Branch(b) = &mut new_arena[parent_new_idx] else {
236 unreachable!()
237 };
238 b.children[child_pos] = ArenaSparseNodeBranchChild::Blinded(rlp_node);
239 } else {
240 let child_node = self.arena.remove(old_child_idx).expect("child exists");
241 let new_child_idx = new_arena.insert(child_node);
242 if let Some(frame) = prepare_retained_node(
243 &new_arena,
244 new_child_idx,
245 child_path,
246 &mut new_num_leaves,
247 ) {
248 stack.push(frame);
249 }
250 let ArenaSparseNode::Branch(b) = &mut new_arena[parent_new_idx] else {
251 unreachable!()
252 };
253 b.children[child_pos] = ArenaSparseNodeBranchChild::Revealed(new_child_idx);
254 }
255 }
256
257 let pruned = old_count - new_arena.len();
258 self.num_leaves = new_num_leaves;
259 self.num_dirty_leaves = 0;
260 self.arena = new_arena;
261 self.root = new_root;
262
263 #[cfg(debug_assertions)]
264 self.debug_assert_counters();
265 return pruned;
266
267 struct CopyFrame {
268 new_idx: Index,
269 branch_logical_path: Nibbles,
270 state_mask: TrieMask,
271 remaining_child_mask: TrieMask,
272 }
273
274 impl CopyFrame {
275 fn next_revealed_child(&mut self, new_arena: &NodeArena) -> Option<(usize, u8, Index)> {
276 let ArenaSparseNode::Branch(b) = &new_arena[self.new_idx] else { unreachable!() };
277
278 loop {
279 let nibble = self.remaining_child_mask.first_set_bit_index()?;
280 self.remaining_child_mask.unset_bit(nibble);
281 let child_idx = BranchChildIdx::new(self.state_mask, nibble)
282 .expect("remaining_child_mask must be a subset of state_mask");
283
284 if let ArenaSparseNodeBranchChild::Revealed(old_idx) = b.children[child_idx] {
285 return Some((child_idx.get(), nibble, old_idx))
286 }
287 }
288 }
289 }
290
291 fn prepare_retained_node(
294 new_arena: &NodeArena,
295 new_idx: Index,
296 node_path: Nibbles,
297 new_num_leaves: &mut u64,
298 ) -> Option<CopyFrame> {
299 let ArenaSparseNode::Branch(b) = &new_arena[new_idx] else {
300 if matches!(&new_arena[new_idx], ArenaSparseNode::Leaf { .. }) {
301 *new_num_leaves += 1;
302 }
303 return None;
304 };
305
306 let mut branch_logical_path = node_path;
307 branch_logical_path.extend(&b.short_key);
308
309 Some(CopyFrame {
310 new_idx,
311 branch_logical_path,
312 state_mask: b.state_mask,
313 remaining_child_mask: b.state_mask,
314 })
315 }
316 }
317
318 #[instrument(
326 level = "trace",
327 target = TRACE_TARGET,
328 skip_all,
329 fields(
330 subtrie = ?self.path,
331 num_updates = sorted_updates.len(),
332 ),
333 )]
334 fn update_leaves(&mut self, sorted_updates: &[(B256, Nibbles, LeafUpdate)]) {
335 if sorted_updates.is_empty() {
336 return;
337 }
338 trace!(target: TRACE_TARGET, "Subtrie update_leaves");
339
340 debug_assert!(
341 !matches!(self.arena[self.root], ArenaSparseNode::EmptyRoot { .. }),
342 "subtrie root must not be EmptyRoot at start of update_leaves"
343 );
344
345 self.buffers.cursor.reset(&self.arena, self.root, self.path);
346
347 for (idx, &(key, ref full_path, ref update)) in sorted_updates.iter().enumerate() {
348 let find_result = self.buffers.cursor.seek(&mut self.arena, full_path);
349
350 if matches!(find_result, SeekResult::Blinded) {
352 let logical_len = self.buffers.cursor.head_logical_branch_path_len(&self.arena);
353 self.required_proofs.push((
354 idx,
355 ArenaRequiredProof { key, parent: ProofV2TargetParent::new(logical_len) },
356 ));
357 continue;
358 }
359
360 match update {
361 LeafUpdate::Changed(value) if !value.is_empty() => {
362 let (_result, deltas) = ArenaParallelSparseTrie::upsert_leaf(
364 &mut self.arena,
365 &mut self.buffers.cursor,
366 &mut self.root,
367 full_path,
368 value,
369 find_result,
370 );
371 self.num_leaves = (self.num_leaves as i64 + deltas.num_leaves_delta) as u64;
372 self.num_dirty_leaves =
373 (self.num_dirty_leaves as i64 + deltas.num_dirty_leaves_delta) as u64;
374 }
375 LeafUpdate::Changed(_) => {
376 let (result, deltas) = ArenaParallelSparseTrie::remove_leaf(
377 &mut self.arena,
378 &mut self.buffers.cursor,
379 &mut self.root,
380 key,
381 full_path,
382 find_result,
383 &mut self.buffers.updates,
384 );
385 self.num_leaves = (self.num_leaves as i64 + deltas.num_leaves_delta) as u64;
386 self.num_dirty_leaves =
387 (self.num_dirty_leaves as i64 + deltas.num_dirty_leaves_delta) as u64;
388
389 if let RemoveLeafResult::NeedsProof { key, proof_key, parent } = result {
390 self.required_proofs
391 .push((idx, ArenaRequiredProof { key: proof_key, parent }));
392 self.required_proofs.push((idx, ArenaRequiredProof { key, parent }));
393 }
394 }
395 LeafUpdate::Touched => {}
396 }
397 }
398
399 self.buffers.cursor.drain(&mut self.arena);
401
402 #[cfg(debug_assertions)]
403 self.debug_assert_counters();
404 }
405
406 fn reveal_nodes(&mut self, nodes: &mut [ProofTrieNodeV2]) -> SparseTrieResult<()> {
409 if nodes.is_empty() {
410 return Ok(());
411 }
412 trace!(target: TRACE_TARGET, path = ?self.path, num_nodes = nodes.len(), "Subtrie reveal_nodes");
413
414 debug_assert!(
415 !matches!(self.arena[self.root], ArenaSparseNode::EmptyRoot { .. }),
416 "subtrie root must not be EmptyRoot in reveal_nodes"
417 );
418
419 self.buffers.cursor.reset(&self.arena, self.root, self.path);
420
421 for node in nodes.iter_mut() {
422 let find_result = self.buffers.cursor.seek(&mut self.arena, &node.path);
423 if ArenaParallelSparseTrie::reveal_node(
424 &mut self.arena,
425 &self.buffers.cursor,
426 node,
427 find_result,
428 )
429 .is_some_and(|child_idx| matches!(self.arena[child_idx], ArenaSparseNode::Leaf { .. }))
430 {
431 self.num_leaves += 1;
432 }
433 }
434
435 self.buffers.cursor.drain(&mut self.arena);
437
438 #[cfg(debug_assertions)]
439 self.debug_assert_counters();
440
441 Ok(())
442 }
443
444 fn update_cached_rlp(&mut self, new_epoch: TrieNodeEpoch) {
449 ArenaParallelSparseTrie::update_cached_rlp(
450 &mut self.arena,
451 self.root,
452 self.path,
453 &mut self.buffers,
454 new_epoch,
455 );
456 self.num_dirty_leaves = 0;
457 #[cfg(debug_assertions)]
458 self.debug_assert_counters();
459 }
460}
461
462#[derive(Debug, Default)]
466struct SubtrieCounterDeltas {
467 num_leaves_delta: i64,
468 num_dirty_leaves_delta: i64,
469}
470
471#[derive(Debug)]
474enum UpsertLeafResult {
475 Updated,
477 NewLeaf,
479 NewChild,
482}
483
484#[derive(Debug)]
487enum RemoveLeafResult {
488 Removed,
490 NotFound,
492 NeedsProof { key: B256, proof_key: B256, parent: ProofV2TargetParent },
495}
496
497#[derive(Debug, Clone)]
499struct ArenaRequiredProof {
500 key: B256,
502 parent: ProofV2TargetParent,
504}
505
506#[derive(Debug, Clone, Copy, PartialEq, Eq)]
511pub struct ArenaParallelismThresholds {
512 pub min_dirty_leaves: u64,
516 pub min_revealed_nodes: usize,
520 pub min_updates: usize,
524 pub min_leaves_for_prune: u64,
528}
529
530impl Default for ArenaParallelismThresholds {
531 fn default() -> Self {
532 Self {
533 min_dirty_leaves: 64,
534 min_revealed_nodes: 16,
535 min_updates: 128,
536 min_leaves_for_prune: 128,
537 }
538 }
539}
540
541#[derive(Debug, Clone)]
598pub struct ArenaParallelSparseTrie {
599 upper_arena: NodeArena,
601 root: Index,
603 buffers: ArenaTrieBuffers,
605 parallelism_thresholds: ArenaParallelismThresholds,
607}
608
609impl ArenaParallelSparseTrie {
610 pub const fn with_parallelism_thresholds(
612 mut self,
613 thresholds: ArenaParallelismThresholds,
614 ) -> Self {
615 self.parallelism_thresholds = thresholds;
616 self
617 }
618
619 const fn should_be_subtrie(path_len: usize) -> bool {
622 path_len == UPPER_TRIE_MAX_DEPTH
623 }
624
625 fn maybe_wrap_in_subtrie(&mut self, child_idx: Index, child_path: &Nibbles) {
630 if !Self::should_be_subtrie(child_path.len()) {
631 return;
632 }
633
634 if !matches!(
636 self.upper_arena[child_idx],
637 ArenaSparseNode::Branch(_) | ArenaSparseNode::Leaf { .. }
638 ) {
639 return;
640 }
641
642 trace!(target: TRACE_TARGET, ?child_path, "Wrapping child into subtrie");
643 let mut subtrie = ArenaSparseSubtrie::new(self.buffers.updates.is_some());
644 subtrie.path = *child_path;
645 let mut root_node =
646 mem::replace(&mut self.upper_arena[child_idx], ArenaSparseNode::TakenSubtrie);
647
648 if let ArenaSparseNode::Branch(b) = &mut root_node {
650 for child in &mut b.children {
651 if let ArenaSparseNodeBranchChild::Revealed(idx) = child {
652 *idx =
653 Self::migrate_nodes(&mut subtrie.arena, &mut self.upper_arena, *idx, None);
654 }
655 }
656 }
657
658 subtrie.arena[subtrie.root] = root_node;
659 let (leaves, dirty) = Self::count_leaves_and_dirty(&subtrie.arena, subtrie.root);
660 subtrie.num_leaves = leaves;
661 subtrie.num_dirty_leaves = dirty;
662 #[cfg(debug_assertions)]
663 subtrie.debug_assert_counters();
664 self.upper_arena[child_idx] = ArenaSparseNode::Subtrie(subtrie);
665 }
666
667 fn maybe_wrap_branch_children(&mut self, cursor: &ArenaCursor) {
672 let head = cursor.head().expect("cursor is non-empty");
673 let head_idx = head.index;
674 let head_path = head.path;
675
676 let ArenaSparseNode::Branch(b) = &self.upper_arena[head_idx] else { return };
677 let short_key = b.short_key;
678 let children: SmallVec<[_; 4]> = b
679 .child_iter()
680 .filter_map(|(nibble, child)| match child {
681 ArenaSparseNodeBranchChild::Revealed(idx) => Some((nibble, *idx)),
682 ArenaSparseNodeBranchChild::Blinded(_) => None,
683 })
684 .collect();
685
686 for (nibble, child_idx) in children {
687 let mut child_path = head_path;
688 child_path.extend(&short_key);
689 child_path.push_unchecked(nibble);
690 self.maybe_wrap_in_subtrie(child_idx, &child_path);
691 }
692 }
693
694 #[instrument(
703 level = "trace",
704 target = TRACE_TARGET,
705 skip_all,
706 fields(subtrie_path = ?cursor.head().expect("cursor is non-empty").path),
707 )]
708 fn maybe_unwrap_subtrie(&mut self, cursor: &mut ArenaCursor) {
709 let subtrie_idx = cursor.head().expect("cursor is non-empty").index;
710
711 let ArenaSparseNode::Subtrie(subtrie) = &self.upper_arena[subtrie_idx] else {
712 return;
713 };
714
715 if !matches!(subtrie.arena[subtrie.root], ArenaSparseNode::EmptyRoot { .. }) {
716 return;
717 }
718
719 let child_nibble = cursor
720 .head()
721 .expect("cursor is non-empty")
722 .path
723 .last()
724 .expect("subtrie path must have at least one nibble");
725 let parent_idx = cursor.parent().expect("cursor has parent").index;
726
727 cursor.pop(&mut self.upper_arena);
730
731 self.recycle_subtrie_from_idx(subtrie_idx);
732
733 trace!(target: TRACE_TARGET, "Unwrapping empty subtrie, removing child slot");
734 let parent_branch = self.upper_arena[parent_idx].branch_mut();
735 let child_idx = BranchChildIdx::new(parent_branch.state_mask, child_nibble)
736 .expect("child nibble not found in parent state_mask");
737
738 parent_branch.children.remove(child_idx.get());
739 parent_branch.unset_child_bit(child_nibble);
740 parent_branch.state = parent_branch.state.to_dirty();
742
743 self.maybe_collapse_or_remove_branch(cursor);
744 }
745
746 fn recycle_subtrie(&mut self, node: ArenaSparseNode) {
752 let ArenaSparseNode::Subtrie(mut subtrie) = node else {
753 unreachable!("recycle_subtrie called on non-Subtrie node")
754 };
755 Self::merge_subtrie_updates(&mut self.buffers.updates, &mut subtrie.buffers.updates);
756 }
757
758 fn recycle_subtrie_from_idx(&mut self, idx: Index) {
760 let node = self.upper_arena.remove(idx).expect("subtrie exists in arena");
761 self.recycle_subtrie(node);
762 }
763
764 fn maybe_collapse_or_remove_branch(&mut self, cursor: &mut ArenaCursor) {
775 loop {
776 let branch_entry = cursor.head().expect("cursor is non-empty");
777 let branch_idx = branch_entry.index;
778 let branch_path = branch_entry.path;
779
780 let count = {
783 let ArenaSparseNode::Branch(b) = &self.upper_arena[branch_idx] else {
784 return;
785 };
786 b.state_mask.count_bits()
787 };
788
789 if count >= 2 {
790 return;
791 }
792
793 if count == 0 {
794 if branch_idx == self.root {
795 self.upper_arena[branch_idx] =
796 ArenaSparseNode::EmptyRoot { state: ArenaSparseNodeState::Dirty };
797 return;
798 }
799 let branch_nibble = branch_path.last().expect("non-root branch");
801 cursor.pop(&mut self.upper_arena);
802 self.upper_arena.remove(branch_idx);
803 let parent_idx = cursor.head().expect("cursor is non-empty").index;
804 let parent_branch = self.upper_arena[parent_idx].branch_mut();
805 let child_idx = BranchChildIdx::new(parent_branch.state_mask, branch_nibble)
806 .expect("child nibble not found in parent state_mask");
807 parent_branch.children.remove(child_idx.get());
808 parent_branch.unset_child_bit(branch_nibble);
809 parent_branch.state = parent_branch.state.to_dirty();
810 continue; }
812
813 let (remaining_nibble, remaining_child_idx) = {
815 let b = self.upper_arena[branch_idx].branch_ref();
816 let nibble = b.state_mask.iter().next().expect("branch has at least one child");
817 let child_idx = match &b.children[0] {
818 ArenaSparseNodeBranchChild::Revealed(idx) => Some(*idx),
819 ArenaSparseNodeBranchChild::Blinded(_) => None,
820 };
821 (nibble, child_idx)
822 };
823
824 let Some(child_idx) = remaining_child_idx else {
825 debug_assert!(false, "single remaining child is blinded — should have been caught by check_subtrie_collapse_needs_proof");
826 return;
827 };
828
829 if matches!(self.upper_arena[child_idx], ArenaSparseNode::TakenSubtrie) {
830 return;
833 }
834
835 let is_empty_subtrie = matches!(
837 &self.upper_arena[child_idx],
838 ArenaSparseNode::Subtrie(s) if matches!(s.arena[s.root], ArenaSparseNode::EmptyRoot { .. })
839 );
840
841 if is_empty_subtrie {
842 self.recycle_subtrie_from_idx(child_idx);
843 let branch = self.upper_arena[branch_idx].branch_mut();
844 branch.children.remove(0);
845 branch.unset_child_bit(remaining_nibble);
846 branch.state = branch.state.to_dirty();
847 continue; }
849
850 Self::collapse_branch(
852 &mut self.upper_arena,
853 cursor,
854 &mut self.root,
855 &mut self.buffers.updates,
856 );
857
858 let child_idx = cursor.head().expect("cursor is non-empty").index;
863 if let ArenaSparseNode::Subtrie(_) = &self.upper_arena[child_idx] {
864 let ArenaSparseNode::Subtrie(mut subtrie) =
865 mem::replace(&mut self.upper_arena[child_idx], ArenaSparseNode::TakenSubtrie)
866 else {
867 unreachable!()
868 };
869 Self::migrate_nodes(
870 &mut self.upper_arena,
871 &mut subtrie.arena,
872 subtrie.root,
873 Some(child_idx),
874 );
875 Self::merge_subtrie_updates(
876 &mut self.buffers.updates,
877 &mut subtrie.buffers.updates,
878 );
879
880 self.maybe_wrap_branch_children(cursor);
884 }
885 return;
886 }
887 }
888
889 fn merge_subtrie_updates(
896 dst: &mut Option<SparseTrieUpdates>,
897 src: &mut Option<SparseTrieUpdates>,
898 ) {
899 if let Some(dst_updates) = dst.as_mut() {
900 let src_updates = src.as_mut().expect("updates are enabled");
901
902 for path in src_updates.updated_nodes.keys() {
904 dst_updates.removed_nodes.remove(path);
905 }
906 dst_updates.updated_nodes.extend(src_updates.updated_nodes.drain());
907
908 for path in &src_updates.removed_nodes {
910 dst_updates.updated_nodes.remove(path);
911 }
912 dst_updates.removed_nodes.extend(src_updates.removed_nodes.drain());
913 }
914 }
915
916 fn nibbles_to_padded_b256(path: &Nibbles) -> B256 {
918 let mut bytes = [0u8; 32];
919 path.pack_to(&mut bytes);
920 B256::from(bytes)
921 }
922
923 fn get_branch_masks(arena: &NodeArena, branch: &ArenaSparseNodeBranch) -> BranchNodeMasks {
925 let mut masks = BranchNodeMasks::default();
926
927 for (nibble, child) in branch.child_iter() {
928 let (hash_bit, tree_bit) = match child {
929 ArenaSparseNodeBranchChild::Blinded(_) => (
930 branch.branch_masks.hash_mask.is_bit_set(nibble),
931 branch.branch_masks.tree_mask.is_bit_set(nibble),
932 ),
933 ArenaSparseNodeBranchChild::Revealed(child_idx) => {
934 let child = &arena[*child_idx];
935 (child.hash_mask_bit(), child.tree_mask_bit())
936 }
937 };
938
939 masks.set_child_bits(nibble, hash_bit, tree_bit);
940 }
941
942 masks
943 }
944
945 #[instrument(level = "trace", target = TRACE_TARGET, skip_all, fields(base_path = ?base_path), ret)]
958 fn update_cached_rlp(
959 arena: &mut NodeArena,
960 root: Index,
961 base_path: Nibbles,
962 buffers: &mut ArenaTrieBuffers,
963 new_epoch: TrieNodeEpoch,
964 ) -> RlpNode {
965 let cursor = &mut buffers.cursor;
966 let rlp_buf = &mut buffers.rlp_buf;
967 let rlp_node_buf = &mut buffers.rlp_node_buf;
968 let updates = &mut buffers.updates;
969
970 rlp_node_buf.clear();
971
972 match &arena[root] {
976 ArenaSparseNode::EmptyRoot { state } => {
977 let node_epoch = match state {
978 ArenaSparseNodeState::Cached { epoch, .. } => *epoch,
979 ArenaSparseNodeState::Revealed => TrieNodeEpoch::UNMODIFIED,
980 ArenaSparseNodeState::Dirty => new_epoch,
981 };
982 let rlp_node = RlpNode::word_rlp(&EMPTY_ROOT_HASH);
983 *arena[root].state_mut() =
984 ArenaSparseNodeState::Cached { rlp_node: rlp_node.clone(), epoch: node_epoch };
985 return rlp_node
986 }
987 ArenaSparseNode::Leaf { .. } => {
988 Self::encode_leaf(arena, root, rlp_buf, rlp_node_buf, new_epoch);
989 return rlp_node_buf.pop().expect("encode_leaf must push an RlpNode");
990 }
991 ArenaSparseNode::Branch(b) => {
992 if let ArenaSparseNodeState::Cached { rlp_node, .. } = &b.state {
993 let rlp_node = rlp_node.clone();
994 return rlp_node;
995 }
996 }
997 ArenaSparseNode::Subtrie(_) | ArenaSparseNode::TakenSubtrie => {
998 unreachable!("Subtrie/TakenSubtrie should not appear inside a subtrie's own arena");
999 }
1000 }
1001
1002 cursor.reset(arena, root, base_path);
1003
1004 loop {
1008 let result = cursor.next(&mut *arena, |_, node| {
1009 matches!(
1010 node,
1011 ArenaSparseNode::Branch(b) if matches!(b.state, ArenaSparseNodeState::Dirty)
1012 )
1013 });
1014
1015 match result {
1016 NextResult::Done => break,
1017 NextResult::NonBranch => {
1018 unreachable!("should_descend only returns true for dirty branches")
1019 }
1020 NextResult::Branch => {}
1021 };
1022
1023 let head = cursor.head().expect("cursor is non-empty");
1024 let head_idx = head.index;
1025 let head_path = head.path;
1026
1027 trace!(
1031 target: TRACE_TARGET,
1032 branch_path = ?head_path,
1033 branch_short_key = ?arena[head_idx].short_key().expect("head is a branch"),
1034 state_mask = ?arena[head_idx].branch_ref().state_mask,
1035 "Calculating branch RlpNode",
1036 );
1037
1038 rlp_node_buf.clear();
1039 let mut node_epoch = TrieNodeEpoch::UNMODIFIED;
1040 let state_mask = arena[head_idx].branch_ref().state_mask;
1041 for (child_idx, _nibble) in BranchChildIter::new(state_mask) {
1042 match &arena[head_idx].branch_ref().children[child_idx] {
1043 ArenaSparseNodeBranchChild::Blinded(rlp_node) => {
1044 rlp_node_buf.push(rlp_node.clone());
1045 }
1046 ArenaSparseNodeBranchChild::Revealed(child_idx) => {
1047 let child_idx = *child_idx;
1048 match &arena[child_idx] {
1049 ArenaSparseNode::Leaf { .. } => {
1050 Self::encode_leaf(
1051 arena,
1052 child_idx,
1053 rlp_buf,
1054 rlp_node_buf,
1055 new_epoch,
1056 );
1057 }
1058 ArenaSparseNode::Branch(child_b) => {
1059 let ArenaSparseNodeState::Cached { rlp_node, .. } = &child_b.state
1060 else {
1061 panic!("child branch must be cached after DFS");
1062 };
1063 let rlp_node = rlp_node.clone();
1064 rlp_node_buf.push(rlp_node);
1065 }
1066 ArenaSparseNode::Subtrie(subtrie) => {
1067 let subtrie_root = &subtrie.arena[subtrie.root];
1068 match subtrie_root {
1069 ArenaSparseNode::Branch(ArenaSparseNodeBranch {
1070 state: ArenaSparseNodeState::Cached { rlp_node, .. },
1071 ..
1072 }) |
1073 ArenaSparseNode::Leaf {
1074 state: ArenaSparseNodeState::Cached { rlp_node, .. },
1075 ..
1076 } => {
1077 rlp_node_buf.push(rlp_node.clone());
1078 }
1079 _ => panic!("subtrie root must be a cached Branch or Leaf"),
1080 }
1081 }
1082 ArenaSparseNode::TakenSubtrie | ArenaSparseNode::EmptyRoot { .. } => {
1083 unreachable!("Unexpected child {:?}", arena[child_idx]);
1084 }
1085 }
1086 let Some(ArenaSparseNodeState::Cached { epoch: child_epoch, .. }) =
1087 arena[child_idx].state_ref()
1088 else {
1089 panic!("revealed child must be cached after encoding");
1090 };
1091 node_epoch = node_epoch.max(*child_epoch);
1092 }
1093 }
1094 }
1095
1096 let b = arena[head_idx].branch_ref();
1098 let short_key = b.short_key;
1099 let state_mask = b.state_mask;
1100 let prev_branch_masks = b.branch_masks;
1101 let new_branch_masks = Self::get_branch_masks(arena, b);
1102 let was_dirty = matches!(b.state, ArenaSparseNodeState::Dirty);
1103 if was_dirty {
1104 node_epoch = node_epoch.max(new_epoch);
1105 }
1106
1107 rlp_buf.clear();
1108 let rlp_node = BranchNodeRef::new(rlp_node_buf, state_mask).rlp(rlp_buf);
1109
1110 let rlp_node = if short_key.is_empty() {
1111 rlp_node
1112 } else {
1113 rlp_buf.clear();
1114 ExtensionNodeRef::new(&short_key, &rlp_node).rlp(rlp_buf)
1115 };
1116
1117 trace!(
1118 target: TRACE_TARGET,
1119 path = ?head_path,
1120 short_key = ?arena[head_idx].short_key(),
1121 children = ?state_mask.iter().zip(rlp_node_buf.iter()).collect::<Vec<_>>(),
1122 rlp_node = ?rlp_node,
1123 "Calculated branch RlpNode",
1124 );
1125
1126 let branch = arena[head_idx].branch_mut();
1127 branch.state = ArenaSparseNodeState::Cached { rlp_node, epoch: node_epoch };
1128 branch.branch_masks = new_branch_masks;
1129
1130 if let Some(trie_updates) = updates.as_mut().filter(|_| was_dirty) {
1133 let mut logical_path = head_path;
1134 logical_path.extend(&short_key);
1135
1136 if !logical_path.is_empty() {
1137 if !prev_branch_masks.is_empty() && new_branch_masks.is_empty() {
1138 trie_updates.updated_nodes.remove(&logical_path);
1139 trie_updates.removed_nodes.insert(logical_path);
1140 } else if !new_branch_masks.is_empty() {
1141 let compact = arena[head_idx].branch_ref().branch_node_compact(arena);
1142 trie_updates.updated_nodes.insert(logical_path, compact);
1143 trie_updates.removed_nodes.remove(&logical_path);
1144 }
1145 }
1146 }
1147 }
1148
1149 let ArenaSparseNodeState::Cached { rlp_node, .. } = &arena[root].branch_ref().state else {
1150 panic!("root must be cached after update_cached_rlp");
1151 };
1152 rlp_node.clone()
1153 }
1154
1155 fn get_leaf_value_in_arena<'a>(
1158 arena: &'a NodeArena,
1159 mut current: Index,
1160 full_path: &Nibbles,
1161 mut path_offset: usize,
1162 ) -> Option<&'a Vec<u8>> {
1163 loop {
1164 match &arena[current] {
1165 ArenaSparseNode::EmptyRoot { .. } | ArenaSparseNode::TakenSubtrie => return None,
1166 ArenaSparseNode::Leaf { key, value, .. } => {
1167 let remaining = full_path.slice(path_offset..);
1168 return (remaining == *key).then_some(value);
1169 }
1170 ArenaSparseNode::Branch(b) => {
1171 let short_key = &b.short_key;
1172 let logical_end = path_offset + short_key.len();
1173 if full_path.len() <= logical_end ||
1174 full_path.slice(path_offset..logical_end) != *short_key
1175 {
1176 return None;
1177 }
1178
1179 let child_nibble = full_path.get_unchecked(logical_end);
1180 let child_idx = BranchChildIdx::new(b.state_mask, child_nibble)?;
1181 match &b.children[child_idx] {
1182 ArenaSparseNodeBranchChild::Blinded(_) => return None,
1183 ArenaSparseNodeBranchChild::Revealed(child_idx) => {
1184 current = *child_idx;
1185 path_offset = logical_end + 1;
1186 }
1187 }
1188 }
1189 ArenaSparseNode::Subtrie(subtrie) => {
1190 return Self::get_leaf_value_in_arena(
1191 &subtrie.arena,
1192 subtrie.root,
1193 full_path,
1194 path_offset,
1195 );
1196 }
1197 }
1198 }
1199 }
1200
1201 fn find_leaf_in_arena(
1205 arena: &NodeArena,
1206 mut current: Index,
1207 full_path: &Nibbles,
1208 mut path_offset: usize,
1209 expected_value: Option<&Vec<u8>>,
1210 ) -> Result<LeafLookup, LeafLookupError> {
1211 loop {
1212 match &arena[current] {
1213 ArenaSparseNode::EmptyRoot { .. } | ArenaSparseNode::TakenSubtrie => {
1214 return Ok(LeafLookup::NonExistent);
1215 }
1216 ArenaSparseNode::Leaf { key, value, .. } => {
1217 let remaining = full_path.slice(path_offset..);
1218 if remaining != *key {
1219 return Ok(LeafLookup::NonExistent);
1220 }
1221 if let Some(expected) = expected_value &&
1222 *expected != *value
1223 {
1224 return Err(LeafLookupError::ValueMismatch {
1225 path: *full_path,
1226 expected: Some(expected.clone()),
1227 actual: value.clone(),
1228 });
1229 }
1230 return Ok(LeafLookup::Exists);
1231 }
1232 ArenaSparseNode::Branch(b) => {
1233 let short_key = &b.short_key;
1234 let logical_end = path_offset + short_key.len();
1235
1236 if full_path.len() <= logical_end {
1237 return Ok(LeafLookup::NonExistent);
1238 }
1239
1240 if full_path.slice(path_offset..logical_end) != *short_key {
1241 return Ok(LeafLookup::NonExistent);
1242 }
1243
1244 let child_nibble = full_path.get_unchecked(logical_end);
1245 let Some(child_idx) = BranchChildIdx::new(b.state_mask, child_nibble) else {
1246 return Ok(LeafLookup::NonExistent);
1247 };
1248
1249 match &b.children[child_idx] {
1250 ArenaSparseNodeBranchChild::Blinded(rlp_node) => {
1251 let hash = rlp_node
1252 .as_hash()
1253 .unwrap_or_else(|| keccak256(rlp_node.as_slice()));
1254 let mut blinded_path = full_path.slice(..logical_end);
1255 blinded_path.push_unchecked(child_nibble);
1256 return Err(LeafLookupError::BlindedNode { path: blinded_path, hash });
1257 }
1258 ArenaSparseNodeBranchChild::Revealed(child_idx) => {
1259 current = *child_idx;
1260 path_offset = logical_end + 1;
1261 }
1262 }
1263 }
1264 ArenaSparseNode::Subtrie(subtrie) => {
1265 return Self::find_leaf_in_arena(
1266 &subtrie.arena,
1267 subtrie.root,
1268 full_path,
1269 path_offset,
1270 expected_value,
1271 );
1272 }
1273 }
1274 }
1275 }
1276
1277 fn encode_leaf(
1281 arena: &mut NodeArena,
1282 idx: Index,
1283 rlp_buf: &mut Vec<u8>,
1284 rlp_node_buf: &mut Vec<RlpNode>,
1285 new_epoch: TrieNodeEpoch,
1286 ) {
1287 let (key, value, state) = match &arena[idx] {
1288 ArenaSparseNode::Leaf { key, value, state } => (key, value, state),
1289 _ => unreachable!("encode_leaf called on non-Leaf node"),
1290 };
1291
1292 let epoch = match state {
1293 ArenaSparseNodeState::Cached { rlp_node, .. } => {
1294 rlp_node_buf.push(rlp_node.clone());
1295 return;
1296 }
1297 ArenaSparseNodeState::Revealed => TrieNodeEpoch::UNMODIFIED,
1298 ArenaSparseNodeState::Dirty => new_epoch,
1299 };
1300
1301 rlp_buf.clear();
1302 let rlp_node = LeafNodeRef { key, value }.rlp(rlp_buf);
1303
1304 *arena[idx].state_mut() =
1305 ArenaSparseNodeState::Cached { rlp_node: rlp_node.clone(), epoch };
1306 rlp_node_buf.push(rlp_node);
1307 }
1308
1309 fn split_and_insert_leaf(
1323 arena: &mut NodeArena,
1324 cursor: &mut ArenaCursor,
1325 root: &mut Index,
1326 new_leaf_path: Nibbles,
1327 value: &[u8],
1328 ) -> bool {
1329 let old_child_entry = cursor.head().expect("cursor must have head");
1330 let old_child_idx = old_child_entry.index;
1331 let old_child_short_key = arena[old_child_idx].short_key().expect("top of stack is a leaf");
1332 let diverge_len = new_leaf_path.common_prefix_length(old_child_short_key);
1333
1334 trace!(
1335 target: TRACE_TARGET,
1336 path = ?old_child_entry.path,
1337 ?new_leaf_path,
1338 ?old_child_short_key,
1339 diverge_len,
1340 "Splitting node and inserting new leaf",
1341 );
1342
1343 let old_child_nibble = old_child_short_key.get_unchecked(diverge_len);
1344 let old_child_suffix = old_child_short_key.slice(diverge_len + 1..);
1345
1346 let newly_dirtied_existing = match &mut arena[old_child_idx] {
1349 ArenaSparseNode::Leaf { key, state, .. } => {
1350 *key = old_child_suffix;
1351 let was_clean = !matches!(state, ArenaSparseNodeState::Dirty);
1352 *state = ArenaSparseNodeState::Dirty;
1353 was_clean
1354 }
1355 ArenaSparseNode::Branch(b) => {
1356 b.short_key = old_child_suffix;
1357 b.state = b.state.to_dirty();
1358 false
1360 }
1361 _ => unreachable!("split_and_insert_leaf called on non-Leaf/Branch node"),
1362 };
1363
1364 let short_key = new_leaf_path.slice(..diverge_len);
1365 let new_leaf_nibble = new_leaf_path.get_unchecked(diverge_len);
1366 debug_assert_ne!(old_child_nibble, new_leaf_nibble);
1367
1368 let new_leaf_idx = arena.insert(ArenaSparseNode::Leaf {
1369 state: ArenaSparseNodeState::Dirty,
1370 key: new_leaf_path.slice(diverge_len + 1..),
1371 value: value.to_vec(),
1372 });
1373
1374 let (first_nibble, first_child, second_nibble, second_child) =
1375 if old_child_nibble < new_leaf_nibble {
1376 (old_child_nibble, old_child_idx, new_leaf_nibble, new_leaf_idx)
1377 } else {
1378 (new_leaf_nibble, new_leaf_idx, old_child_nibble, old_child_idx)
1379 };
1380
1381 let state_mask = TrieMask::from(1u16 << first_nibble | 1u16 << second_nibble);
1382 let mut children = SmallVec::with_capacity(2);
1383 children.push(ArenaSparseNodeBranchChild::Revealed(first_child));
1384 children.push(ArenaSparseNodeBranchChild::Revealed(second_child));
1385
1386 let new_branch_idx = arena.insert(ArenaSparseNode::Branch(ArenaSparseNodeBranch {
1387 state: ArenaSparseNodeState::Dirty,
1388 children,
1389 state_mask,
1390 short_key,
1391 branch_masks: BranchNodeMasks::default(),
1392 }));
1393
1394 cursor.replace_head_index(arena, root, new_branch_idx);
1395 newly_dirtied_existing
1396 }
1397
1398 #[instrument(level = "trace", target = TRACE_TARGET, skip_all, fields(full_path = ?full_path))]
1413 fn upsert_leaf(
1414 arena: &mut NodeArena,
1415 cursor: &mut ArenaCursor,
1416 root: &mut Index,
1417 full_path: &Nibbles,
1418 value: &[u8],
1419 find_result: SeekResult,
1420 ) -> (UpsertLeafResult, SubtrieCounterDeltas) {
1421 trace!(target: TRACE_TARGET, ?find_result, "Upserting leaf");
1422 let head = cursor.head().expect("cursor is non-empty");
1423
1424 match find_result {
1425 SeekResult::Blinded => {
1426 unreachable!("Blinded case must be handled by caller")
1427 }
1428 SeekResult::EmptyRoot => {
1429 let head_idx = head.index;
1430 let head_path = head.path;
1431 arena[head_idx] = ArenaSparseNode::Leaf {
1432 state: ArenaSparseNodeState::Dirty,
1433 key: full_path.slice(head_path.len()..),
1434 value: value.to_vec(),
1435 };
1436 (
1437 UpsertLeafResult::NewLeaf,
1438 SubtrieCounterDeltas { num_leaves_delta: 1, num_dirty_leaves_delta: 1 },
1439 )
1440 }
1441 SeekResult::RevealedLeaf => {
1442 let head_idx = head.index;
1444 let was_clean =
1445 if let ArenaSparseNode::Leaf { value: v, state, .. } = &mut arena[head_idx] {
1446 v.clear();
1447 v.extend_from_slice(value);
1448 let was_clean = !matches!(state, ArenaSparseNodeState::Dirty);
1449 *state = ArenaSparseNodeState::Dirty;
1450 was_clean
1451 } else {
1452 unreachable!("RevealedLeaf but cursor head is not a leaf")
1453 };
1454 (
1455 UpsertLeafResult::Updated,
1456 SubtrieCounterDeltas {
1457 num_leaves_delta: 0,
1458 num_dirty_leaves_delta: was_clean as i64,
1459 },
1460 )
1461 }
1462 SeekResult::Diverged => {
1463 let head_path = head.path;
1464 let full_path_from_head = full_path.slice(head_path.len()..);
1465
1466 let split_dirtied_existing =
1467 Self::split_and_insert_leaf(arena, cursor, root, full_path_from_head, value);
1468
1469 let result = if cursor.depth() >= 1 {
1470 UpsertLeafResult::NewChild
1471 } else {
1472 UpsertLeafResult::NewLeaf
1473 };
1474 (
1475 result,
1476 SubtrieCounterDeltas {
1477 num_leaves_delta: 1,
1478 num_dirty_leaves_delta: 1 + split_dirtied_existing as i64,
1479 },
1480 )
1481 }
1482 SeekResult::NoChild { child_nibble } => {
1483 let head_idx = head.index;
1484
1485 let head_branch_logical_path = cursor.head_logical_branch_path(arena);
1486 let leaf_key = full_path.slice(head_branch_logical_path.len() + 1..);
1487 let new_leaf = arena.insert(ArenaSparseNode::Leaf {
1488 state: ArenaSparseNodeState::Dirty,
1489 key: leaf_key,
1490 value: value.to_vec(),
1491 });
1492
1493 let branch = arena[head_idx].branch_mut();
1494 branch.set_child(child_nibble, ArenaSparseNodeBranchChild::Revealed(new_leaf));
1495
1496 cursor.seek(arena, full_path);
1498
1499 (
1500 UpsertLeafResult::NewChild,
1501 SubtrieCounterDeltas { num_leaves_delta: 1, num_dirty_leaves_delta: 1 },
1502 )
1503 }
1504 SeekResult::RevealedSubtrie => {
1505 unreachable!("RevealedSubtrie must be handled by caller")
1506 }
1507 }
1508 }
1509
1510 fn remove_leaf(
1526 arena: &mut NodeArena,
1527 cursor: &mut ArenaCursor,
1528 root: &mut Index,
1529 key: B256,
1530 full_path: &Nibbles,
1531 find_result: SeekResult,
1532 updates: &mut Option<SparseTrieUpdates>,
1533 ) -> (RemoveLeafResult, SubtrieCounterDeltas) {
1534 match find_result {
1535 SeekResult::Blinded | SeekResult::RevealedSubtrie => {
1536 unreachable!("Blinded/RevealedSubtrie must be handled by caller")
1537 }
1538 SeekResult::EmptyRoot | SeekResult::Diverged | SeekResult::NoChild { .. } => {
1539 (RemoveLeafResult::NotFound, SubtrieCounterDeltas::default())
1540 }
1541 SeekResult::RevealedLeaf => {
1542 let head = cursor.head().expect("cursor is non-empty");
1544 let head_idx = head.index;
1545 let head_path = head.path;
1546
1547 trace!(
1548 target: TRACE_TARGET,
1549 path = ?head_path,
1550 ?full_path,
1551 "Removing leaf",
1552 );
1553
1554 if let Some(parent_entry) = cursor.parent() {
1557 let parent_idx = parent_entry.index;
1558 let child_nibble = head_path.last().expect("non-root leaf");
1559 let parent_branch = arena[parent_idx].branch_ref();
1560
1561 if parent_branch.state_mask.count_bits() == 2 &&
1562 parent_branch.sibling_child(child_nibble).is_blinded()
1563 {
1564 let sibling_nibble = parent_branch
1565 .state_mask
1566 .iter()
1567 .find(|&n| n != child_nibble)
1568 .expect("branch has two children");
1569 let mut sibling_path = cursor.parent_logical_branch_path(arena);
1570 sibling_path.push_unchecked(sibling_nibble);
1571 trace!(target: TRACE_TARGET, ?full_path, ?sibling_path, "Removal would collapse branch onto blinded sibling, requesting proof");
1572 return (
1573 RemoveLeafResult::NeedsProof {
1574 key,
1575 proof_key: Self::nibbles_to_padded_b256(&sibling_path),
1576 parent: ProofV2TargetParent::new(
1577 sibling_path
1578 .len()
1579 .checked_sub(1)
1580 .expect("sibling path has a child nibble"),
1581 ),
1582 },
1583 SubtrieCounterDeltas::default(),
1584 );
1585 }
1586 }
1587
1588 let removed_was_dirty =
1590 matches!(arena[head_idx].state_ref(), Some(ArenaSparseNodeState::Dirty));
1591
1592 if cursor.depth() == 0 {
1593 arena.remove(head_idx);
1596 *root = arena
1597 .insert(ArenaSparseNode::EmptyRoot { state: ArenaSparseNodeState::Dirty });
1598 cursor.reset(arena, *root, head_path);
1599 return (
1600 RemoveLeafResult::Removed,
1601 SubtrieCounterDeltas {
1602 num_leaves_delta: -1,
1603 num_dirty_leaves_delta: -(removed_was_dirty as i64),
1604 },
1605 );
1606 }
1607
1608 cursor.pop(arena);
1610
1611 let parent_entry = cursor.head().expect("cursor is non-empty");
1613 let parent_idx = parent_entry.index;
1614 let child_nibble = head_path.last().expect("non-root leaf");
1615
1616 arena.remove(head_idx);
1618 let parent_branch = arena[parent_idx].branch_mut();
1619 parent_branch.remove_child(child_nibble);
1620
1621 let collapse_dirtied_leaf = if parent_branch.state_mask.count_bits() == 1 {
1624 Self::collapse_branch(arena, cursor, root, updates)
1625 } else {
1626 false
1627 };
1628 (
1629 RemoveLeafResult::Removed,
1630 SubtrieCounterDeltas {
1631 num_leaves_delta: -1,
1632 num_dirty_leaves_delta: (collapse_dirtied_leaf as i64) -
1633 (removed_was_dirty as i64),
1634 },
1635 )
1636 }
1637 }
1638 }
1639
1640 fn check_subtrie_collapse_needs_proof(
1646 arena: &NodeArena,
1647 cursor: &ArenaCursor,
1648 subtrie_updates: &[(B256, Nibbles, LeafUpdate)],
1649 ) -> Option<ArenaRequiredProof> {
1650 let num_removals = subtrie_updates
1651 .iter()
1652 .filter(|(_, _, u)| matches!(u, LeafUpdate::Changed(v) if v.is_empty()))
1653 .count() as u64;
1654
1655 let num_changed =
1663 subtrie_updates.iter().filter(|(_, _, u)| matches!(u, LeafUpdate::Changed(_))).count()
1664 as u64;
1665
1666 if num_removals == 0 || num_removals != num_changed {
1667 return None;
1668 }
1669
1670 let subtrie_entry = cursor.head()?;
1672 let subtrie_num_leaves = match &arena[subtrie_entry.index] {
1673 ArenaSparseNode::Subtrie(s) => s.num_leaves,
1674 _ => return None,
1675 };
1676 if num_removals < subtrie_num_leaves {
1677 return None;
1678 }
1679
1680 let child_nibble =
1681 subtrie_entry.path.last().expect("subtrie path must have at least one nibble");
1682
1683 let parent_entry = cursor.parent()?;
1684 let parent_branch = arena[parent_entry.index].branch_ref();
1685 if parent_branch.state_mask.count_bits() != 2 {
1686 return None;
1687 }
1688
1689 if !parent_branch.sibling_child(child_nibble).is_blinded() {
1690 return None;
1691 }
1692
1693 let sibling_nibble = parent_branch
1694 .state_mask
1695 .iter()
1696 .find(|&n| n != child_nibble)
1697 .expect("branch has two children");
1698 let mut sibling_path = cursor.parent_logical_branch_path(arena);
1699 sibling_path.push_unchecked(sibling_nibble);
1700
1701 Some(ArenaRequiredProof {
1702 key: Self::nibbles_to_padded_b256(&sibling_path),
1703 parent: ProofV2TargetParent::new(
1704 sibling_path.len().checked_sub(1).expect("sibling path has a child nibble"),
1705 ),
1706 })
1707 }
1708
1709 fn collapse_branch(
1720 arena: &mut NodeArena,
1721 cursor: &mut ArenaCursor,
1722 root: &mut Index,
1723 updates: &mut Option<SparseTrieUpdates>,
1724 ) -> bool {
1725 let branch_entry = cursor.head().expect("cursor is non-empty");
1726 let branch_idx = branch_entry.index;
1727 let branch = arena[branch_idx].branch_ref();
1728 let remaining_nibble =
1729 branch.state_mask.iter().next().expect("branch has at least one child");
1730 let branch_short_key = branch.short_key;
1731
1732 debug_assert_eq!(
1733 branch.state_mask.count_bits(),
1734 1,
1735 "collapse_branch requires exactly 1 child"
1736 );
1737 debug_assert!(
1738 !branch.children[0].is_blinded(),
1739 "collapse_branch called with a blinded remaining child"
1740 );
1741
1742 trace!(
1743 target: TRACE_TARGET,
1744 path = ?branch_entry.path,
1745 short_key = ?branch_short_key,
1746 branch_masks = ?branch.branch_masks,
1747 ?remaining_nibble,
1748 "Collapsing single-child branch",
1749 );
1750
1751 if let Some(trie_updates) = updates.as_mut() &&
1754 !branch.branch_masks.is_empty()
1755 {
1756 let logical_path = cursor.head_logical_branch_path(arena);
1757 if !logical_path.is_empty() {
1758 trie_updates.updated_nodes.remove(&logical_path);
1759 trie_updates.removed_nodes.insert(logical_path);
1760 }
1761 }
1762
1763 let mut prefix = branch_short_key;
1765 prefix.push_unchecked(remaining_nibble);
1766
1767 let ArenaSparseNodeBranchChild::Revealed(child_idx) = branch.children[0] else {
1768 unreachable!()
1769 };
1770
1771 let newly_dirtied_leaf = match &mut arena[child_idx] {
1774 ArenaSparseNode::Leaf { key, state, .. } => {
1775 let mut new_key = prefix;
1776 new_key.extend(key);
1777 *key = new_key;
1778 let was_clean = !matches!(state, ArenaSparseNodeState::Dirty);
1779 *state = ArenaSparseNodeState::Dirty;
1780 was_clean
1781 }
1782 ArenaSparseNode::Branch(b) => {
1783 let mut new_short_key = prefix;
1784 new_short_key.extend(&b.short_key);
1785 b.short_key = new_short_key;
1786 b.state = b.state.to_dirty();
1787 false
1788 }
1789 ArenaSparseNode::Subtrie(subtrie) => {
1790 subtrie.path = branch_entry.path;
1791 match &mut subtrie.arena[subtrie.root] {
1792 ArenaSparseNode::Branch(b) => {
1793 let mut new_short_key = prefix;
1794 new_short_key.extend(&b.short_key);
1795 b.short_key = new_short_key;
1796 b.state = b.state.to_dirty();
1797 }
1798 ArenaSparseNode::Leaf { key, state, .. } => {
1799 let mut new_key = prefix;
1800 new_key.extend(key);
1801 *key = new_key;
1802 let was_clean = !matches!(state, ArenaSparseNodeState::Dirty);
1803 *state = ArenaSparseNodeState::Dirty;
1804 if was_clean {
1805 subtrie.num_dirty_leaves += 1;
1806 }
1807 }
1808 _ => {
1809 unreachable!("subtrie root must be a Branch or Leaf during collapse_branch")
1810 }
1811 }
1812 false
1813 }
1814 _ => unreachable!("remaining child must be Leaf, Branch, or Subtrie"),
1815 };
1816
1817 cursor.replace_head_index(arena, root, child_idx);
1819
1820 arena.remove(branch_idx);
1822 newly_dirtied_leaf
1823 }
1824
1825 fn count_leaves_and_dirty(arena: &NodeArena, idx: Index) -> (u64, u64) {
1827 match &arena[idx] {
1828 ArenaSparseNode::Leaf { state, .. } => {
1829 let dirty = matches!(state, ArenaSparseNodeState::Dirty) as u64;
1830 (1, dirty)
1831 }
1832 ArenaSparseNode::Branch(b) => {
1833 let mut leaves = 0u64;
1834 let mut dirty = 0u64;
1835 for c in &b.children {
1836 if let ArenaSparseNodeBranchChild::Revealed(child_idx) = c {
1837 let (l, d) = Self::count_leaves_and_dirty(arena, *child_idx);
1838 leaves += l;
1839 dirty += d;
1840 }
1841 }
1842 (leaves, dirty)
1843 }
1844 _ => (0, 0),
1845 }
1846 }
1847
1848 #[instrument(level = "trace", target = TRACE_TARGET, skip_all)]
1854 #[cfg(debug_assertions)]
1855 fn debug_assert_subtrie_structure(&mut self) {
1856 let mut cursor = mem::take(&mut self.buffers.cursor);
1857 cursor.reset(&self.upper_arena, self.root, Nibbles::default());
1858
1859 loop {
1860 let result = cursor.next(&mut self.upper_arena, |_, _| true);
1861 match result {
1862 NextResult::Done => break,
1863 NextResult::NonBranch | NextResult::Branch => {
1864 let head = cursor.head().expect("cursor is non-empty");
1865 let path_len = head.path.len();
1866 let node = &self.upper_arena[head.index];
1867
1868 if Self::should_be_subtrie(path_len) {
1869 debug_assert!(
1870 matches!(
1871 node,
1872 ArenaSparseNode::Subtrie(_) | ArenaSparseNode::TakenSubtrie
1873 ),
1874 "node at path_len={path_len} should be a Subtrie but is {node:?}",
1875 );
1876 } else {
1877 debug_assert!(
1878 !matches!(node, ArenaSparseNode::Subtrie(_)),
1879 "node at path_len={path_len} should NOT be a Subtrie but is",
1880 );
1881 }
1882 }
1883 }
1884 }
1885
1886 self.buffers.cursor = cursor;
1887 }
1888
1889 fn migrate_nodes(
1897 dst: &mut NodeArena,
1898 src: &mut NodeArena,
1899 src_idx: Index,
1900 dst_slot: Option<Index>,
1901 ) -> Index {
1902 let mut node = src.remove(src_idx).expect("node exists in source arena");
1903
1904 if let ArenaSparseNode::Branch(b) = &mut node {
1906 for child in &mut b.children {
1907 if let ArenaSparseNodeBranchChild::Revealed(child_idx) = child {
1908 *child_idx = Self::migrate_nodes(dst, src, *child_idx, None);
1909 }
1910 }
1911 }
1912
1913 if let Some(slot) = dst_slot {
1914 dst[slot] = node;
1915 slot
1916 } else {
1917 dst.insert(node)
1918 }
1919 }
1920
1921 fn remove_pruned_node(
1924 arena: &mut NodeArena,
1925 cursor: &ArenaCursor,
1926 idx: Index,
1927 nibble: Option<u8>,
1928 ) -> ArenaSparseNode {
1929 let path = cursor.head().expect("cursor is non-empty").path;
1930 let node = arena.remove(idx).expect("node must exist to be pruned");
1931 let rlp_node = node
1932 .state_ref()
1933 .and_then(ArenaSparseNodeState::cached_rlp_node)
1934 .cloned()
1935 .expect("prune must run after hashing");
1936 trace!(
1937 target: TRACE_TARGET,
1938 ?path,
1939 variant = %AsRef::<str>::as_ref(&node),
1940 cached_rlp_node = ?rlp_node,
1941 "pruning node",
1942 );
1943
1944 let parent_idx = cursor.parent().expect("pruned child has parent").index;
1945 let child_nibble = nibble.expect("non-root child");
1946 let parent_branch = arena[parent_idx].branch_mut();
1947 let child_idx = BranchChildIdx::new(parent_branch.state_mask, child_nibble)
1948 .expect("child nibble not found in parent state_mask");
1949 parent_branch.children[child_idx] = ArenaSparseNodeBranchChild::Blinded(rlp_node);
1950
1951 node
1952 }
1953
1954 #[instrument(level = "trace", target = TRACE_TARGET, skip_all)]
1963 fn reveal_node(
1964 arena: &mut NodeArena,
1965 cursor: &ArenaCursor,
1966 node: &mut ProofTrieNodeV2,
1967 find_result: SeekResult,
1968 ) -> Option<Index> {
1969 let SeekResult::Blinded = find_result else {
1970 return None;
1972 };
1973
1974 let head = cursor.head().expect("cursor is non-empty");
1975 let head_idx = head.index;
1976 let head_branch_logical_path = cursor.head_logical_branch_path(arena);
1977
1978 debug_assert_eq!(
1979 node.path.len(),
1980 head_branch_logical_path.len() + 1,
1981 "proof node path {:?} is not a direct child of branch at {:?} (expected depth {})",
1982 node.path,
1983 head_branch_logical_path,
1984 head_branch_logical_path.len() + 1,
1985 );
1986
1987 let child_nibble = node.path.get_unchecked(head_branch_logical_path.len());
1988 let head_branch = arena[head_idx].branch_ref();
1989 let dense_child_idx = BranchChildIdx::new(head_branch.state_mask, child_nibble)
1990 .expect("Blinded result but child nibble not in state_mask");
1991
1992 let cached_rlp = match &head_branch.children[dense_child_idx] {
1993 ArenaSparseNodeBranchChild::Blinded(rlp) => rlp.clone(),
1994 ArenaSparseNodeBranchChild::Revealed(_) => return None,
1995 };
1996
1997 trace!(
1998 target: TRACE_TARGET,
1999 path = ?node.path,
2000 rlp_node = ?cached_rlp,
2001 "Revealing node",
2002 );
2003
2004 let proof_node = mem::replace(node, ProofTrieNodeV2::empty());
2005 let mut arena_node = ArenaSparseNode::from_proof_node(proof_node);
2006
2007 let state = arena_node.state_mut();
2008 *state =
2009 ArenaSparseNodeState::Cached { rlp_node: cached_rlp, epoch: TrieNodeEpoch::UNMODIFIED };
2010
2011 let child_idx = arena.insert(arena_node);
2012 arena[head_idx].branch_mut().children[dense_child_idx] =
2013 ArenaSparseNodeBranchChild::Revealed(child_idx);
2014
2015 Some(child_idx)
2016 }
2017
2018 #[cfg(debug_assertions)]
2019 fn collect_reachable_nodes(
2020 arena: &NodeArena,
2021 idx: Index,
2022 reachable: &mut alloy_primitives::map::HashSet<Index>,
2023 ) {
2024 if !reachable.insert(idx) {
2025 return;
2026 }
2027 if let ArenaSparseNode::Branch(b) = &arena[idx] {
2028 for child in &b.children {
2029 if let ArenaSparseNodeBranchChild::Revealed(child_idx) = child {
2030 Self::collect_reachable_nodes(arena, *child_idx, reachable);
2031 }
2032 }
2033 }
2034 }
2035
2036 #[cfg(debug_assertions)]
2037 fn assert_no_orphaned_nodes(arena: &NodeArena, root: Index, label: &str) {
2038 let mut reachable = alloy_primitives::map::HashSet::default();
2039 Self::collect_reachable_nodes(arena, root, &mut reachable);
2040 let all_indices: alloy_primitives::map::HashSet<Index> =
2041 arena.iter().map(|(idx, _)| idx).collect();
2042 let orphaned: Vec<_> = all_indices.difference(&reachable).collect();
2043 debug_assert!(
2044 orphaned.is_empty(),
2045 "{label} has {} orphaned node(s): {orphaned:?}",
2046 orphaned.len(),
2047 );
2048 }
2049}
2050
2051#[cfg(debug_assertions)]
2052impl Drop for ArenaParallelSparseTrie {
2053 fn drop(&mut self) {
2054 Self::assert_no_orphaned_nodes(&self.upper_arena, self.root, "upper arena");
2055
2056 for (_, node) in &self.upper_arena {
2057 if let Some(subtrie) = node.as_subtrie() {
2058 Self::assert_no_orphaned_nodes(
2059 &subtrie.arena,
2060 subtrie.root,
2061 &alloc::format!("subtrie {:?}", subtrie.path),
2062 );
2063 }
2064 }
2065 }
2066}
2067
2068impl Default for ArenaParallelSparseTrie {
2069 fn default() -> Self {
2070 let mut upper_arena = SlotMap::new();
2071 let root = upper_arena
2072 .insert(ArenaSparseNode::EmptyRoot { state: ArenaSparseNodeState::Revealed });
2073 Self {
2074 upper_arena,
2075 root,
2076 buffers: ArenaTrieBuffers::default(),
2077 parallelism_thresholds: ArenaParallelismThresholds::default(),
2078 }
2079 }
2080}
2081
2082impl ArenaParallelSparseTrie {
2083 fn update_upper_subtrie(&mut self, head_idx: Index, new_epoch: TrieNodeEpoch) {
2085 let ArenaSparseNode::Subtrie(subtrie) = &mut self.upper_arena[head_idx] else {
2086 unreachable!()
2087 };
2088
2089 if !subtrie.arena[subtrie.root].is_cached() {
2090 subtrie.update_cached_rlp(new_epoch);
2091 }
2092
2093 Self::merge_subtrie_updates(&mut self.buffers.updates, &mut subtrie.buffers.updates);
2094 }
2095}
2096
2097impl SparseTrie for ArenaParallelSparseTrie {
2098 #[instrument(level = "trace", target = TRACE_TARGET, skip_all)]
2099 fn set_root(
2100 &mut self,
2101 root: TrieNodeV2,
2102 masks: Option<BranchNodeMasks>,
2103 retain_updates: bool,
2104 ) -> SparseTrieResult<()> {
2105 debug_assert!(
2106 matches!(self.upper_arena[self.root], ArenaSparseNode::EmptyRoot { .. }),
2107 "set_root called on a trie that already has revealed nodes"
2108 );
2109
2110 self.set_updates(retain_updates);
2111
2112 match root {
2113 TrieNodeV2::EmptyRoot => {
2114 trace!(target: TRACE_TARGET, "Setting empty root");
2115 self.upper_arena[self.root] =
2116 ArenaSparseNode::EmptyRoot { state: ArenaSparseNodeState::Revealed };
2117 }
2118 TrieNodeV2::Leaf(leaf) => {
2119 trace!(target: TRACE_TARGET, key = ?leaf.key, "Setting leaf root");
2120 self.upper_arena[self.root] = ArenaSparseNode::Leaf {
2121 state: ArenaSparseNodeState::Revealed,
2122 key: leaf.key,
2123 value: leaf.value,
2124 };
2125 }
2126 TrieNodeV2::Branch(branch) => {
2127 trace!(target: TRACE_TARGET, state_mask = ?branch.state_mask, num_children = branch.state_mask.count_bits(), "Setting branch root");
2128 let mut children = SmallVec::with_capacity(branch.state_mask.count_bits() as usize);
2129 for (stack_ptr, _nibble) in branch.state_mask.iter().enumerate() {
2130 children
2131 .push(ArenaSparseNodeBranchChild::Blinded(branch.stack[stack_ptr].clone()));
2132 }
2133
2134 self.upper_arena[self.root] = ArenaSparseNode::Branch(ArenaSparseNodeBranch {
2135 state: ArenaSparseNodeState::Revealed,
2136 children,
2137 state_mask: branch.state_mask,
2138 short_key: branch.key,
2139 branch_masks: masks.unwrap_or_default(),
2140 });
2141 }
2142 TrieNodeV2::Extension(_) => {
2143 panic!("set_root does not support Extension nodes; extensions are represented as branches with a short_key")
2144 }
2145 }
2146
2147 Ok(())
2148 }
2149
2150 fn set_updates(&mut self, retain_updates: bool) {
2151 if retain_updates {
2152 self.buffers.updates.get_or_insert_with(SparseTrieUpdates::default).clear();
2153 } else {
2154 self.buffers.updates = None;
2155 }
2156 }
2157
2158 #[instrument(level = "trace", target = TRACE_TARGET, skip_all, fields(num_nodes = nodes.len()))]
2159 fn reveal_nodes(&mut self, nodes: &mut [ProofTrieNodeV2]) -> SparseTrieResult<()> {
2160 if nodes.is_empty() {
2161 return Ok(());
2162 }
2163
2164 if matches!(self.upper_arena[self.root], ArenaSparseNode::EmptyRoot { .. }) {
2165 trace!(target: TRACE_TARGET, "Skipping reveal_nodes on empty root");
2166 return Ok(());
2167 }
2168
2169 nodes.sort_unstable_by_key(|n| n.path);
2171
2172 let threshold = self.parallelism_thresholds.min_revealed_nodes;
2173
2174 let mut cursor = mem::take(&mut self.buffers.cursor);
2176 cursor.reset(&self.upper_arena, self.root, Nibbles::default());
2177
2178 let mut node_idx = if nodes[0].path.is_empty() { 1 } else { 0 };
2180
2181 let mut taken: Vec<(Index, Box<ArenaSparseSubtrie>, Vec<ProofTrieNodeV2>)> = Vec::new();
2185
2186 while node_idx < nodes.len() {
2187 let find_result = cursor.seek(&mut self.upper_arena, &nodes[node_idx].path);
2188
2189 match find_result {
2190 SeekResult::RevealedLeaf => {
2191 trace!(target: TRACE_TARGET, path = ?nodes[node_idx].path, "Skipping reveal: leaf head");
2192 node_idx += 1;
2193 }
2194 SeekResult::Blinded => {
2195 let child_path = nodes[node_idx].path;
2197 let child_idx = Self::reveal_node(
2198 &mut self.upper_arena,
2199 &cursor,
2200 &mut nodes[node_idx],
2201 SeekResult::Blinded,
2202 );
2203 node_idx += 1;
2204
2205 if let Some(child_idx) = child_idx {
2206 self.maybe_wrap_in_subtrie(child_idx, &child_path);
2207 }
2208 }
2209 SeekResult::RevealedSubtrie => {
2210 let subtrie_entry = cursor.head().expect("cursor is non-empty");
2211 let child_idx = subtrie_entry.index;
2212 let prefix = subtrie_entry.path;
2213
2214 let subtrie_start = node_idx;
2215 while node_idx < nodes.len() && nodes[node_idx].path.starts_with(&prefix) {
2216 node_idx += 1;
2217 }
2218 let num_subtrie_nodes = node_idx - subtrie_start;
2219
2220 if num_subtrie_nodes >= threshold {
2221 trace!(target: TRACE_TARGET, ?prefix, num_subtrie_nodes, "Taking subtrie for parallel reveal");
2223 let ArenaSparseNode::Subtrie(subtrie) = mem::replace(
2224 &mut self.upper_arena[child_idx],
2225 ArenaSparseNode::TakenSubtrie,
2226 ) else {
2227 unreachable!("RevealedSubtrie must point to a Subtrie node")
2228 };
2229 let node_vec: Vec<ProofTrieNodeV2> = (subtrie_start..node_idx)
2230 .map(|i| mem::replace(&mut nodes[i], ProofTrieNodeV2::empty()))
2231 .collect();
2232 taken.push((child_idx, subtrie, node_vec));
2233 } else {
2234 trace!(target: TRACE_TARGET, ?prefix, num_subtrie_nodes, "Revealing subtrie inline");
2236 let ArenaSparseNode::Subtrie(subtrie) = &mut self.upper_arena[child_idx]
2237 else {
2238 unreachable!("RevealedSubtrie must point to a Subtrie node")
2239 };
2240 let mut subtrie_nodes: Vec<ProofTrieNodeV2> = (subtrie_start..node_idx)
2241 .map(|i| mem::replace(&mut nodes[i], ProofTrieNodeV2::empty()))
2242 .collect();
2243 subtrie.reveal_nodes(&mut subtrie_nodes)?;
2244 }
2245 }
2246 _ => {
2247 trace!(target: TRACE_TARGET, path = ?nodes[node_idx].path, ?find_result, "Skipping reveal: no blinded child");
2248 node_idx += 1;
2249 }
2250 }
2251 }
2252
2253 cursor.drain(&mut self.upper_arena);
2255 self.buffers.cursor = cursor;
2256
2257 if taken.is_empty() {
2258 return Ok(());
2259 }
2260
2261 if taken.len() == 1 {
2263 let (_, subtrie, node_vec) = &mut taken[0];
2264 subtrie.reveal_nodes(node_vec)?;
2265 } else {
2266 use rayon::iter::{IntoParallelRefMutIterator, ParallelIterator};
2267
2268 let parent_span = tracing::Span::current();
2269 let results: Vec<SparseTrieResult<()>> = taken
2270 .par_iter_mut()
2271 .map(|(_, subtrie, node_vec)| {
2272 let _guard = parent_span.enter();
2273 subtrie.reveal_nodes(node_vec)
2274 })
2275 .collect();
2276
2277 if let Some(err) = results.into_iter().find(|r| r.is_err()) {
2278 for (idx, subtrie, _) in taken {
2280 self.upper_arena[idx] = ArenaSparseNode::Subtrie(subtrie);
2281 }
2282 return err;
2283 }
2284 }
2285
2286 for (idx, subtrie, _) in taken {
2288 self.upper_arena[idx] = ArenaSparseNode::Subtrie(subtrie);
2289 }
2290
2291 #[cfg(debug_assertions)]
2292 self.debug_assert_subtrie_structure();
2293
2294 Ok(())
2295 }
2296
2297 #[instrument(level = "trace", target = TRACE_TARGET, skip_all, ret)]
2298 fn root(&mut self, new_epoch: TrieNodeEpoch) -> B256 {
2299 self.update_subtrie_hashes(new_epoch);
2300
2301 let rlp_node = Self::update_cached_rlp(
2302 &mut self.upper_arena,
2303 self.root,
2304 Nibbles::default(),
2305 &mut self.buffers,
2306 new_epoch,
2307 );
2308
2309 rlp_node.as_hash().expect("root RlpNode must be a hash")
2310 }
2311
2312 fn is_root_cached(&self) -> bool {
2313 self.upper_arena[self.root].is_cached()
2314 }
2315
2316 fn root_epoch(&self) -> Option<TrieNodeEpoch> {
2317 match self.upper_arena[self.root].state_ref()? {
2318 ArenaSparseNodeState::Revealed => Some(TrieNodeEpoch::UNMODIFIED),
2319 ArenaSparseNodeState::Cached { epoch, .. } => Some(*epoch),
2320 ArenaSparseNodeState::Dirty => None,
2321 }
2322 }
2323
2324 #[instrument(level = "trace", target = TRACE_TARGET, skip_all)]
2325 fn update_subtrie_hashes(&mut self, new_epoch: TrieNodeEpoch) {
2326 trace!(target: TRACE_TARGET, "Updating subtrie hashes");
2327
2328 if !matches!(&self.upper_arena[self.root], ArenaSparseNode::Branch(_)) {
2330 return;
2331 }
2332
2333 let mut total_dirty_leaves: u64 = 0;
2335 let mut taken: Vec<(Index, Box<ArenaSparseSubtrie>)> = Vec::new();
2336 for (idx, node) in &mut self.upper_arena {
2337 let ArenaSparseNode::Subtrie(s) = node else { continue };
2338 if s.num_dirty_leaves == 0 {
2339 continue;
2340 }
2341 total_dirty_leaves += s.num_dirty_leaves;
2342 let ArenaSparseNode::Subtrie(subtrie) =
2343 mem::replace(node, ArenaSparseNode::TakenSubtrie)
2344 else {
2345 unreachable!()
2346 };
2347 taken.push((idx, subtrie));
2348 }
2349
2350 if !taken.is_empty() {
2352 if taken.len() == 1 || total_dirty_leaves < self.parallelism_thresholds.min_dirty_leaves
2353 {
2354 for (_, subtrie) in &mut taken {
2355 subtrie.update_cached_rlp(new_epoch);
2356 }
2357 } else {
2358 use rayon::iter::{IntoParallelIterator, ParallelIterator};
2359
2360 let parent_span = tracing::Span::current();
2361 taken = taken
2362 .into_par_iter()
2363 .map(|(idx, mut subtrie)| {
2364 let _guard = parent_span.enter();
2365 subtrie.update_cached_rlp(new_epoch);
2366 (idx, subtrie)
2367 })
2368 .collect();
2369 }
2370 }
2371
2372 if taken.is_empty() && self.upper_arena[self.root].is_cached() {
2375 return;
2376 }
2377
2378 taken.sort_unstable_by_key(|(_, b)| Reverse(b.path));
2382
2383 self.buffers.cursor.reset(&self.upper_arena, self.root, Nibbles::default());
2384
2385 loop {
2386 let result = self.buffers.cursor.next(&mut self.upper_arena, |_, child| match child {
2387 ArenaSparseNode::Branch(_) | ArenaSparseNode::Subtrie(_) => !child.is_cached(),
2388 ArenaSparseNode::TakenSubtrie => true,
2389 _ => false,
2390 });
2391
2392 match result {
2393 NextResult::Done => break,
2394 NextResult::Branch => continue,
2395 NextResult::NonBranch => {}
2396 }
2397
2398 let head_idx = self.buffers.cursor.head().expect("cursor is non-empty").index;
2400
2401 if matches!(&self.upper_arena[head_idx], ArenaSparseNode::TakenSubtrie) {
2402 let (_, subtrie) = taken.pop().expect("taken subtries must not be exhausted");
2403 debug_assert_eq!(
2404 subtrie.path,
2405 self.buffers.cursor.head().expect("cursor is non-empty").path,
2406 "taken subtrie path mismatch",
2407 );
2408 self.upper_arena[head_idx] = ArenaSparseNode::Subtrie(subtrie);
2409 }
2410
2411 self.update_upper_subtrie(head_idx, new_epoch);
2412 }
2413 }
2414
2415 fn get_leaf_value(&self, full_path: &Nibbles) -> Option<&Vec<u8>> {
2416 Self::get_leaf_value_in_arena(&self.upper_arena, self.root, full_path, 0)
2417 }
2418
2419 fn find_leaf(
2420 &self,
2421 full_path: &Nibbles,
2422 expected_value: Option<&Vec<u8>>,
2423 ) -> Result<LeafLookup, LeafLookupError> {
2424 Self::find_leaf_in_arena(&self.upper_arena, self.root, full_path, 0, expected_value)
2425 }
2426
2427 fn updates_ref(&self) -> Cow<'_, SparseTrieUpdates> {
2428 self.buffers
2429 .updates
2430 .as_ref()
2431 .map_or(Cow::Owned(SparseTrieUpdates::default()), Cow::Borrowed)
2432 }
2433
2434 fn take_updates(&mut self) -> SparseTrieUpdates {
2435 match self.buffers.updates.take() {
2436 Some(updates) => {
2437 self.buffers.updates = Some(SparseTrieUpdates::with_capacity(
2438 updates.updated_nodes.len(),
2439 updates.removed_nodes.len(),
2440 ));
2441 updates
2442 }
2443 None => SparseTrieUpdates::default(),
2444 }
2445 }
2446
2447 #[instrument(level = "trace", target = TRACE_TARGET, skip_all)]
2448 fn clear(&mut self) {
2449 self.upper_arena = SlotMap::new();
2450 self.root = self
2451 .upper_arena
2452 .insert(ArenaSparseNode::EmptyRoot { state: ArenaSparseNodeState::Revealed });
2453 self.buffers.clear();
2454 }
2455
2456 #[instrument(
2457 level = "trace",
2458 target = TRACE_TARGET,
2459 skip_all,
2460 fields(prune_before = prune_before.get()),
2461 )]
2462 fn prune(&mut self, prune_before: TrieNodeEpoch) -> usize {
2463 assert!(self.root_epoch().is_some(), "prune cannot run on a dirty trie");
2464
2465 if !matches!(&self.upper_arena[self.root], ArenaSparseNode::Branch(_)) {
2467 return 0;
2468 }
2469
2470 let threshold = self.parallelism_thresholds.min_leaves_for_prune;
2471
2472 let mut cursor = mem::take(&mut self.buffers.cursor);
2473 cursor.reset(&self.upper_arena, self.root, Nibbles::default());
2474
2475 let mut taken: Vec<(Index, Box<ArenaSparseSubtrie>)> = Vec::new();
2477
2478 let mut pruned = 0;
2479
2480 loop {
2481 let result = cursor.next(&mut self.upper_arena, |_, child| {
2482 matches!(
2483 child,
2484 ArenaSparseNode::Branch(_) |
2485 ArenaSparseNode::Subtrie(_) |
2486 ArenaSparseNode::Leaf { .. }
2487 )
2488 });
2489
2490 if matches!(result, NextResult::Done) {
2491 break
2492 }
2493
2494 let head = cursor.head().expect("cursor is non-empty");
2495 let head_idx = head.index;
2496 let head_path = head.path;
2497
2498 match &self.upper_arena[head_idx] {
2499 ArenaSparseNode::Branch(_) | ArenaSparseNode::Leaf { .. } => {
2500 if cursor.depth() == 0 {
2502 continue;
2503 }
2504
2505 let node_epoch = self.upper_arena[head_idx]
2506 .state_ref()
2507 .and_then(ArenaSparseNodeState::cached_epoch)
2508 .expect("prune must run after hashing");
2509 if !node_epoch.should_prune(prune_before) {
2510 continue;
2511 }
2512
2513 Self::remove_pruned_node(
2514 &mut self.upper_arena,
2515 &cursor,
2516 head_idx,
2517 head_path.last(),
2518 );
2519 pruned += 1;
2520 }
2521 ArenaSparseNode::Subtrie(_) => {
2522 let root_epoch = self.upper_arena[head_idx]
2523 .state_ref()
2524 .and_then(ArenaSparseNodeState::cached_epoch)
2525 .expect("prune must run after hashing");
2526 if root_epoch.should_prune(prune_before) {
2527 let removed = Self::remove_pruned_node(
2528 &mut self.upper_arena,
2529 &cursor,
2530 head_idx,
2531 head_path.last(),
2532 );
2533 let ArenaSparseNode::Subtrie(s) = &removed else { unreachable!() };
2534 pruned += s.arena.len();
2535 self.recycle_subtrie(removed);
2536 continue;
2537 }
2538
2539 let ArenaSparseNode::Subtrie(subtrie) = &self.upper_arena[head_idx] else {
2540 unreachable!()
2541 };
2542 if subtrie.num_leaves >= threshold {
2543 let ArenaSparseNode::Subtrie(subtrie) = mem::replace(
2544 &mut self.upper_arena[head_idx],
2545 ArenaSparseNode::TakenSubtrie,
2546 ) else {
2547 unreachable!()
2548 };
2549 taken.push((head_idx, subtrie));
2550 } else {
2551 let ArenaSparseNode::Subtrie(subtrie) = &mut self.upper_arena[head_idx]
2552 else {
2553 unreachable!()
2554 };
2555 pruned += subtrie.prune(prune_before);
2556 }
2557 }
2558 _ => unreachable!("NonBranch in prune walk must be Subtrie, Leaf, or Branch"),
2559 }
2560 }
2561
2562 self.buffers.cursor = cursor;
2563
2564 if !taken.is_empty() {
2565 if taken.len() == 1 {
2567 let (_, ref mut subtrie) = taken[0];
2568 pruned += subtrie.prune(prune_before);
2569 } else {
2570 use rayon::iter::{IntoParallelRefMutIterator, ParallelIterator};
2571
2572 let parent_span = tracing::Span::current();
2573 pruned += taken
2574 .par_iter_mut()
2575 .map(|(_, subtrie)| {
2576 let _guard = parent_span.enter();
2577 let _span = tracing::trace_span!(
2578 target: TRACE_TARGET,
2579 "subtrie_prune",
2580 subtrie = ?subtrie.path,
2581 )
2582 .entered();
2583
2584 subtrie.prune(prune_before)
2585 })
2586 .sum::<usize>();
2587 }
2588
2589 for (child_idx, subtrie) in taken {
2591 self.upper_arena[child_idx] = ArenaSparseNode::Subtrie(subtrie);
2592 }
2593 }
2594
2595 if pruned > 0 {
2596 compact_arena(&mut self.upper_arena, &mut self.root);
2597 }
2598
2599 pruned
2600 }
2601
2602 #[instrument(
2603 level = "trace",
2604 target = TRACE_TARGET,
2605 skip_all,
2606 fields(num_updates = updates.len()),
2607 )]
2608 fn update_leaves(
2609 &mut self,
2610 updates: &mut B256Map<LeafUpdate>,
2611 mut proof_required_fn: impl FnMut(B256, ProofV2TargetParent),
2612 ) -> SparseTrieResult<()> {
2613 if updates.is_empty() {
2614 return Ok(());
2615 }
2616
2617 let mut sorted: Vec<_> =
2619 updates.drain().map(|(key, update)| (key, Nibbles::unpack(key), update)).collect();
2620 sorted.sort_unstable_by_key(|entry| entry.1);
2621
2622 let threshold = self.parallelism_thresholds.min_updates;
2623 let parallelize_distributed_updates = sorted.len() >= threshold.saturating_mul(4);
2624
2625 let mut cursor = mem::take(&mut self.buffers.cursor);
2626 cursor.reset(&self.upper_arena, self.root, Nibbles::default());
2627
2628 let mut taken: Vec<(Index, Box<ArenaSparseSubtrie>, core::ops::Range<usize>)> = Vec::new();
2630
2631 let mut update_idx = 0;
2632 while update_idx < sorted.len() {
2633 let (key, ref full_path, ref update) = sorted[update_idx];
2634
2635 let find_result = cursor.seek(&mut self.upper_arena, full_path);
2636
2637 match find_result {
2638 SeekResult::Blinded => {
2640 let logical_len = cursor.head_logical_branch_path_len(&self.upper_arena);
2641 let parent = ProofV2TargetParent::new(logical_len);
2642 trace!(target: TRACE_TARGET, ?key, ?parent, "Update hit blinded node, requesting proof");
2643 proof_required_fn(key, parent);
2644 updates.insert(key, update.clone());
2645 }
2646 SeekResult::RevealedSubtrie => {
2648 let subtrie_entry = cursor.head().expect("cursor is non-empty");
2649 let child_idx = subtrie_entry.index;
2650 let subtrie_root_path = subtrie_entry.path;
2651
2652 let subtrie_start = update_idx;
2653 while update_idx < sorted.len() &&
2654 sorted[update_idx].1.starts_with(&subtrie_root_path)
2655 {
2656 update_idx += 1;
2657 }
2658
2659 let subtrie_updates = &sorted[subtrie_start..update_idx];
2660
2661 if let Some(proof) = Self::check_subtrie_collapse_needs_proof(
2665 &self.upper_arena,
2666 &cursor,
2667 subtrie_updates,
2668 ) {
2669 trace!(target: TRACE_TARGET, proof_key = ?proof.key, proof_parent = ?proof.parent, "Subtrie collapse would need blinded sibling, requesting proof");
2670 proof_required_fn(proof.key, proof.parent);
2671 for &(key, _, ref update) in subtrie_updates {
2672 updates.insert(key, update.clone());
2673 }
2674 continue;
2676 }
2677
2678 let num_subtrie_updates = update_idx - subtrie_start;
2679
2680 let all_removals = subtrie_updates
2684 .iter()
2685 .filter(|(_, _, u)| matches!(u, LeafUpdate::Changed(_)))
2689 .all(|(_, _, u)| matches!(u, LeafUpdate::Changed(v) if v.is_empty()));
2690 let subtrie_num_leaves = match &self.upper_arena[child_idx] {
2691 ArenaSparseNode::Subtrie(s) => s.num_leaves,
2692 _ => 0,
2693 };
2694 let might_empty_subtrie =
2695 all_removals && num_subtrie_updates as u64 >= subtrie_num_leaves;
2696
2697 if (num_subtrie_updates >= threshold || parallelize_distributed_updates) &&
2698 !might_empty_subtrie
2699 {
2700 trace!(target: TRACE_TARGET, ?subtrie_root_path, num_subtrie_updates, "Taking subtrie for parallel update");
2702 let ArenaSparseNode::Subtrie(subtrie) = mem::replace(
2703 &mut self.upper_arena[child_idx],
2704 ArenaSparseNode::TakenSubtrie,
2705 ) else {
2706 unreachable!()
2707 };
2708 taken.push((child_idx, subtrie, subtrie_start..update_idx));
2709 } else {
2710 trace!(target: TRACE_TARGET, ?subtrie_root_path, num_subtrie_updates, "Updating subtrie inline");
2712 let ArenaSparseNode::Subtrie(subtrie) = &mut self.upper_arena[child_idx]
2713 else {
2714 unreachable!()
2715 };
2716
2717 subtrie.update_leaves(subtrie_updates);
2718
2719 for (target_idx, proof) in subtrie.required_proofs.drain(..) {
2720 proof_required_fn(proof.key, proof.parent);
2721 let (key, _, ref update) = subtrie_updates[target_idx];
2722 updates.insert(key, update.clone());
2723 }
2724
2725 self.maybe_unwrap_subtrie(&mut cursor);
2727 }
2728
2729 continue;
2731 }
2732 find_result @ (SeekResult::EmptyRoot |
2734 SeekResult::RevealedLeaf |
2735 SeekResult::Diverged |
2736 SeekResult::NoChild { .. }) => match update {
2737 LeafUpdate::Changed(v) if !v.is_empty() => {
2738 let (result, _deltas) = Self::upsert_leaf(
2739 &mut self.upper_arena,
2740 &mut cursor,
2741 &mut self.root,
2742 full_path,
2743 v,
2744 find_result,
2745 );
2746 match result {
2747 UpsertLeafResult::NewChild => {
2748 let head = cursor.head().expect("cursor is non-empty");
2749 if Self::should_be_subtrie(head.path.len()) {
2750 self.maybe_wrap_in_subtrie(head.index, &head.path);
2753 } else {
2754 self.maybe_wrap_branch_children(&cursor);
2758 }
2759 }
2760 UpsertLeafResult::NewLeaf => {
2761 self.maybe_wrap_branch_children(&cursor);
2764 }
2765 UpsertLeafResult::Updated => {}
2766 }
2767 }
2768 LeafUpdate::Changed(_) => {
2769 let (result, _deltas) = Self::remove_leaf(
2770 &mut self.upper_arena,
2771 &mut cursor,
2772 &mut self.root,
2773 key,
2774 full_path,
2775 find_result,
2776 &mut self.buffers.updates,
2777 );
2778 match result {
2779 RemoveLeafResult::NeedsProof { key, proof_key, parent } => {
2780 proof_required_fn(proof_key, parent);
2781 let update =
2782 mem::replace(&mut sorted[update_idx].2, LeafUpdate::Touched);
2783 updates.insert(key, update);
2784 }
2785 RemoveLeafResult::Removed => {
2786 self.maybe_collapse_or_remove_branch(&mut cursor);
2795 let head =
2796 cursor.head().expect("cursor always has root after collapse");
2797 self.maybe_wrap_in_subtrie(head.index, &head.path);
2798 }
2799 RemoveLeafResult::NotFound => {}
2800 }
2801 }
2802 LeafUpdate::Touched => {}
2803 },
2804 }
2805
2806 update_idx += 1;
2807 }
2808
2809 cursor.drain(&mut self.upper_arena);
2811 self.buffers.cursor = cursor;
2812
2813 if taken.is_empty() {
2814 #[cfg(debug_assertions)]
2815 self.debug_assert_subtrie_structure();
2816
2817 return Ok(());
2818 }
2819
2820 if taken.len() == 1 {
2822 let (_, ref mut subtrie, ref range) = taken[0];
2823 subtrie.update_leaves(&sorted[range.clone()]);
2824 } else {
2825 use rayon::iter::{IntoParallelRefMutIterator, ParallelIterator};
2826
2827 let parent_span = tracing::Span::current();
2828 taken.par_iter_mut().for_each(|(_, subtrie, range)| {
2829 let _guard = parent_span.enter();
2830 subtrie.update_leaves(&sorted[range.clone()]);
2831 });
2832 }
2833
2834 let taken_paths: Vec<Nibbles> = taken.iter().map(|(_, s, _)| s.path).collect();
2837 for (child_idx, mut subtrie, range) in taken {
2838 let subtrie_updates = &sorted[range];
2839 for (target_idx, proof) in subtrie.required_proofs.drain(..) {
2840 proof_required_fn(proof.key, proof.parent);
2841 let (key, _, ref update) = subtrie_updates[target_idx];
2842 updates.insert(key, update.clone());
2843 }
2844
2845 self.upper_arena[child_idx] = ArenaSparseNode::Subtrie(subtrie);
2847 }
2848
2849 {
2855 let mut cursor = mem::take(&mut self.buffers.cursor);
2856 cursor.reset(&self.upper_arena, self.root, Nibbles::default());
2857
2858 for path in &taken_paths {
2859 let find_result = cursor.seek(&mut self.upper_arena, path);
2860 match find_result {
2861 SeekResult::RevealedSubtrie => {
2862 debug_assert!(
2863 {
2864 let head_idx = cursor.head().expect("cursor is non-empty").index;
2865 !matches!(
2866 &self.upper_arena[head_idx],
2867 ArenaSparseNode::Subtrie(s) if matches!(s.arena[s.root], ArenaSparseNode::EmptyRoot { .. })
2868 )
2869 },
2870 "taken subtrie became EmptyRoot — should have been forced inline"
2871 );
2872
2873 cursor.pop(&mut self.upper_arena);
2874
2875 self.maybe_collapse_or_remove_branch(&mut cursor);
2879 }
2880 _ => {
2881 }
2884 }
2885 }
2886
2887 cursor.drain(&mut self.upper_arena);
2888 self.buffers.cursor = cursor;
2889 }
2890
2891 #[cfg(debug_assertions)]
2892 self.debug_assert_subtrie_structure();
2893
2894 Ok(())
2895 }
2896}
2897
2898#[cfg(test)]
2899mod tests {
2900 use super::TRACE_TARGET;
2901 use crate::{
2902 ArenaParallelSparseTrie, ArenaParallelismThresholds, LeafUpdate, SparseTrie, TrieNodeEpoch,
2903 };
2904 use alloy_primitives::{map::B256Map, B256, U256};
2905 use rand::{seq::SliceRandom, Rng, SeedableRng};
2906 use reth_trie::test_utils::TrieTestHarness;
2907 use reth_trie_common::ProofV2Target;
2908 use std::collections::BTreeMap;
2909 use tracing::{info, trace};
2910
2911 const fn epoch(value: u64) -> TrieNodeEpoch {
2912 TrieNodeEpoch::new(value)
2913 }
2914
2915 struct ArenaTrieTestHarness {
2920 inner: TrieTestHarness,
2922 }
2923
2924 impl std::ops::Deref for ArenaTrieTestHarness {
2925 type Target = TrieTestHarness;
2926 fn deref(&self) -> &Self::Target {
2927 &self.inner
2928 }
2929 }
2930
2931 impl std::ops::DerefMut for ArenaTrieTestHarness {
2932 fn deref_mut(&mut self) -> &mut Self::Target {
2933 &mut self.inner
2934 }
2935 }
2936
2937 impl ArenaTrieTestHarness {
2938 fn new(storage: BTreeMap<B256, U256>) -> Self {
2940 Self { inner: TrieTestHarness::new(storage) }
2941 }
2942
2943 fn assert_changes(
2947 &self,
2948 apst: &mut ArenaParallelSparseTrie,
2949 changes: BTreeMap<B256, U256>,
2950 ) {
2951 let (expected_root, mut expected_trie_updates) = if changes.is_empty() {
2953 (self.original_root(), Default::default())
2954 } else {
2955 self.get_root_with_updates(&changes)
2956 };
2957
2958 self.minimize_trie_updates(&mut expected_trie_updates);
2959
2960 let mut leaf_updates: B256Map<LeafUpdate> = changes
2963 .iter()
2964 .map(|(&slot, &value)| {
2965 let rlp_value = if value == U256::ZERO {
2966 Vec::new()
2967 } else {
2968 alloy_rlp::encode_fixed_size(&value).to_vec()
2969 };
2970 (slot, LeafUpdate::Changed(rlp_value))
2971 })
2972 .collect();
2973
2974 loop {
2977 let mut targets: Vec<ProofV2Target> = Vec::new();
2978 apst.update_leaves(&mut leaf_updates, |key, parent| {
2979 targets.push(ProofV2Target::new(key).with_parent(parent));
2980 })
2981 .expect("update_leaves should succeed");
2982
2983 if targets.is_empty() {
2984 break;
2985 }
2986
2987 let (mut proof_nodes, _) = self.proof_v2(&mut targets);
2988 apst.reveal_nodes(&mut proof_nodes).expect("reveal_nodes should succeed");
2989 }
2990
2991 let actual_root = apst.root(epoch(0));
2993 let mut actual_updates = apst.take_updates();
2994
2995 actual_updates.updated_nodes.retain(|path, node| {
2998 self.storage_trie_updates().storage_nodes.get(path) != Some(node)
2999 });
3000 actual_updates
3001 .removed_nodes
3002 .retain(|path| self.storage_trie_updates().storage_nodes.contains_key(path));
3003
3004 let mut expected_updated_nodes =
3005 expected_trie_updates.storage_nodes.into_iter().collect::<Vec<_>>();
3006 let mut actual_updated_nodes =
3007 actual_updates.updated_nodes.into_iter().collect::<Vec<_>>();
3008 expected_updated_nodes.sort();
3009 actual_updated_nodes.sort();
3010 pretty_assertions::assert_eq!(
3011 expected_updated_nodes,
3012 actual_updated_nodes,
3013 "updated nodes mismatch"
3014 );
3015
3016 let mut expected_removed_nodes =
3017 expected_trie_updates.removed_nodes.into_iter().collect::<Vec<_>>();
3018 let mut actual_removed_nodes =
3019 actual_updates.removed_nodes.into_iter().collect::<Vec<_>>();
3020 expected_removed_nodes.sort();
3021 actual_removed_nodes.sort();
3022 pretty_assertions::assert_eq!(
3023 expected_removed_nodes,
3024 actual_removed_nodes,
3025 "removed nodes mismatch"
3026 );
3027 assert_eq!(expected_root, actual_root, "storage root mismatch");
3028 }
3029 }
3030
3031 use proptest::prelude::*;
3032 use proptest_arbitrary_interop::arb;
3033
3034 fn build_changeset(
3041 base: &BTreeMap<B256, U256>,
3042 new_keys: BTreeMap<B256, U256>,
3043 overlap_pct: f64,
3044 delete_pct: f64,
3045 rng: &mut rand::rngs::StdRng,
3046 ) -> BTreeMap<B256, U256> {
3047 let num_overlap = (base.len() as f64 * overlap_pct) as usize;
3048 let num_delete = (num_overlap as f64 * delete_pct) as usize;
3049
3050 let mut all_keys: Vec<B256> = base.keys().copied().collect();
3051 all_keys.shuffle(rng);
3052 let overlap_keys = &all_keys[..num_overlap];
3053
3054 let mut changeset = new_keys;
3055 for (i, &key) in overlap_keys.iter().enumerate() {
3056 let value =
3057 if i < num_delete { U256::ZERO } else { U256::from(rng.random::<u64>() | 1) };
3058 changeset.entry(key).or_insert(value);
3059 }
3060 changeset
3061 }
3062
3063 proptest! {
3064 #![proptest_config(ProptestConfig::with_cases(1000))]
3065 #[test]
3066 fn arena_trie_proptest(
3067 initial in proptest::collection::btree_map(arb::<B256>(), arb::<U256>(), 0..=100usize),
3068 changeset1_new_keys in proptest::collection::btree_map(arb::<B256>(), arb::<U256>(), 0..=30usize),
3069 changeset2_new_keys in proptest::collection::btree_map(arb::<B256>(), arb::<U256>(), 0..=30usize),
3070 overlap_pct in 0.0..=0.5f64,
3071 delete_pct in 0.0..=0.33f64, shuffle_seed in arb::<u64>(),
3073 ) {
3074 reth_tracing::init_test_tracing();
3075 info!(target: TRACE_TARGET, ?shuffle_seed, "PROPTEST START");
3076
3077 let initial: BTreeMap<B256, U256> = initial.into_iter()
3079 .filter(|(_, v)| *v != U256::ZERO)
3080 .collect();
3081
3082 let mut rng = rand::rngs::StdRng::seed_from_u64(shuffle_seed);
3083
3084 let changeset1 = build_changeset(&initial, changeset1_new_keys, overlap_pct, delete_pct, &mut rng);
3085 for (i, (k, v)) in changeset1.iter().enumerate() {
3086 trace!(target: TRACE_TARGET, ?i, ?k, ?v, "Changeset 1 entry");
3087 }
3088
3089 let mut harness = ArenaTrieTestHarness::new(initial);
3090
3091 let root_node = harness.root_node();
3093 let mut apst = ArenaParallelSparseTrie::default().with_parallelism_thresholds(
3094 ArenaParallelismThresholds {
3095 min_dirty_leaves: 3,
3096 min_revealed_nodes: 3,
3097 min_updates: 3,
3098 min_leaves_for_prune: 3,
3099 },
3100 );
3101 apst.set_root(root_node.node, root_node.masks, true).expect("set_root should succeed");
3102
3103 harness.assert_changes(&mut apst, changeset1.clone());
3104
3105 harness.apply_changeset(changeset1);
3107
3108 apst.prune(epoch(1));
3111
3112 let changeset2 = build_changeset(harness.storage(), changeset2_new_keys, overlap_pct, delete_pct, &mut rng);
3113 for (i, (k, v)) in changeset2.iter().enumerate() {
3114 trace!(target: TRACE_TARGET, ?i, ?k, ?v, "Changeset 2 entry");
3115 }
3116
3117 harness.assert_changes(&mut apst, changeset2);
3118 }
3119 }
3120}