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::{LeafLookup, LeafLookupError, LeafUpdate, SparseTrie, SparseTrieUpdates};
12use alloc::{borrow::Cow, boxed::Box, collections::VecDeque, vec::Vec};
13use alloy_primitives::{
14 keccak256,
15 map::{B256Map, HashMap, HashSet},
16 B256,
17};
18use alloy_trie::{BranchNodeCompact, TrieMask};
19use core::{cmp::Reverse, mem};
20use reth_execution_errors::SparseTrieResult;
21use reth_trie_common::{
22 BranchNodeMasks, BranchNodeRef, ExtensionNodeRef, LeafNodeRef, Nibbles, ProofTrieNodeV2,
23 RlpNode, TrieNodeV2, EMPTY_ROOT_HASH,
24};
25use slotmap::{DefaultKey, SlotMap};
26use smallvec::SmallVec;
27use tracing::{instrument, trace};
28
29#[cfg(feature = "trie-debug")]
30use crate::debug_recorder::{LeafUpdateRecord, ProofTrieNodeRecord, RecordedOp, TrieDebugRecorder};
31
32type Index = DefaultKey;
34type NodeArena = SlotMap<Index, ArenaSparseNode>;
36
37const TRACE_TARGET: &str = "trie::arena";
38
39const UPPER_TRIE_MAX_DEPTH: usize = 2;
42
43fn prefix_range(
49 sorted_keys: &[Nibbles],
50 start: usize,
51 prefix: &Nibbles,
52) -> core::ops::Range<usize> {
53 let begin = start + sorted_keys[start..].partition_point(|p| p < prefix);
55 let mut end = begin;
57 while end < sorted_keys.len() && sorted_keys[end].starts_with(prefix) {
58 end += 1;
59 }
60 begin..end
61}
62
63const fn slotmap_slot_size<T>() -> usize {
66 let union_size = if core::mem::size_of::<T>() > 4 { core::mem::size_of::<T>() } else { 4 };
70 let raw = union_size + 4;
71 let align = if core::mem::align_of::<T>() > 4 { core::mem::align_of::<T>() } else { 4 };
72 (raw + align - 1) & !(align - 1)
73}
74
75fn compact_arena(arena: &mut NodeArena, root: &mut Index) {
79 let mut new_arena = SlotMap::with_capacity(arena.len());
80 let mut queue = VecDeque::new();
81
82 let root_node = arena.remove(*root).expect("root exists");
83 let new_root = new_arena.insert(root_node);
84 queue.push_back(new_root);
85
86 while let Some(new_idx) = queue.pop_front() {
87 let old_children: SmallVec<[(usize, Index); 16]> = match &new_arena[new_idx] {
92 ArenaSparseNode::Branch(b) => b
93 .children
94 .iter()
95 .enumerate()
96 .filter_map(|(i, c)| match c {
97 ArenaSparseNodeBranchChild::Revealed(old_idx) => Some((i, *old_idx)),
98 _ => None,
99 })
100 .collect(),
101 _ => continue,
102 };
103
104 for (child_pos, old_child_idx) in old_children {
105 let child_node = arena.remove(old_child_idx).expect("child exists");
106 let new_child_idx = new_arena.insert(child_node);
107 let ArenaSparseNode::Branch(b) = &mut new_arena[new_idx] else { unreachable!() };
108 b.children[child_pos] = ArenaSparseNodeBranchChild::Revealed(new_child_idx);
109 queue.push_back(new_child_idx);
110 }
111 }
112
113 debug_assert!(
114 arena.is_empty(),
115 "compact_arena: {} orphaned nodes remaining after BFS drain",
116 arena.len(),
117 );
118
119 *arena = new_arena;
120 *root = new_root;
121}
122
123#[derive(Debug, Default, Clone)]
125struct ArenaTrieBuffers {
126 cursor: ArenaCursor,
128 updates: Option<SparseTrieUpdates>,
131 rlp_buf: Vec<u8>,
133 rlp_node_buf: Vec<RlpNode>,
135}
136
137impl ArenaTrieBuffers {
138 fn clear(&mut self) {
139 if let Some(updates) = self.updates.as_mut() {
140 updates.clear();
141 }
142 self.rlp_buf.clear();
143 self.rlp_node_buf.clear();
144 }
145}
146
147#[derive(Debug, Clone)]
151struct ArenaSparseSubtrie {
152 arena: NodeArena,
154 root: Index,
156 path: Nibbles,
158 buffers: ArenaTrieBuffers,
160 required_proofs: Vec<(usize, ArenaRequiredProof)>,
164 num_leaves: u64,
166 num_dirty_leaves: u64,
168 cached_memory_size: usize,
171}
172
173impl ArenaSparseSubtrie {
174 fn new(record_updates: bool) -> Box<Self> {
178 let mut arena = SlotMap::new();
179 let root = arena.insert(ArenaSparseNode::EmptyRoot);
180 let buffers = ArenaTrieBuffers {
181 updates: record_updates.then(SparseTrieUpdates::default),
182 ..Default::default()
183 };
184 Box::new(Self {
185 arena,
186 root,
187 path: Nibbles::default(),
188 buffers,
189 required_proofs: Vec::new(),
190 num_leaves: 0,
191 num_dirty_leaves: 0,
192 cached_memory_size: 0,
193 })
194 }
195
196 const fn memory_size(&self) -> usize {
199 self.cached_memory_size
200 }
201
202 #[cfg(debug_assertions)]
204 fn debug_assert_counters(&self) {
205 let (actual_leaves, actual_dirty) =
206 ArenaParallelSparseTrie::count_leaves_and_dirty(&self.arena, self.root);
207 debug_assert_eq!(
208 self.num_leaves, actual_leaves,
209 "subtrie {:?} num_leaves mismatch: stored {} vs actual {}",
210 self.path, self.num_leaves, actual_leaves,
211 );
212 debug_assert_eq!(
213 self.num_dirty_leaves, actual_dirty,
214 "subtrie {:?} num_dirty_leaves mismatch: stored {} vs actual {}",
215 self.path, self.num_dirty_leaves, actual_dirty,
216 );
217 }
218
219 fn prune(&mut self, retained_leaves: &[Nibbles]) -> usize {
229 if !matches!(&self.arena[self.root], ArenaSparseNode::Branch(_)) {
231 return 0;
232 }
233
234 debug_assert!(
235 retained_leaves.windows(2).all(|w| w[0] <= w[1]),
236 "retained_leaves must be sorted"
237 );
238 debug_assert_eq!(self.num_dirty_leaves, 0, "prune must run after hashing");
239
240 let old_count = self.arena.len();
241 let mut new_arena = SlotMap::with_capacity(retained_leaves.len() * 2);
245 let mut queue: VecDeque<(Index, Nibbles)> = VecDeque::new();
247 let mut new_num_leaves = 0u64;
248 let mut new_nodes_heap_size = 0usize;
249
250 let root_node = self.arena.remove(self.root).expect("root exists");
252 let new_root = new_arena.insert(root_node);
253 queue.push_back((new_root, self.path));
254
255 while let Some((new_idx, node_path)) = queue.pop_front() {
256 new_nodes_heap_size += new_arena[new_idx].extra_heap_bytes();
257
258 let ArenaSparseNode::Branch(b) = &new_arena[new_idx] else {
259 if matches!(&new_arena[new_idx], ArenaSparseNode::Leaf { .. }) {
260 new_num_leaves += 1;
261 }
262 continue;
263 };
264
265 let mut branch_logical_path = node_path;
267 branch_logical_path.extend(&b.short_key);
268
269 let children: SmallVec<[(usize, u8, Index); 16]> = BranchChildIter::new(b.state_mask)
271 .filter_map(|(dense_idx, nibble)| match &b.children[dense_idx] {
272 ArenaSparseNodeBranchChild::Revealed(old_idx) => {
273 Some((dense_idx.get(), nibble, *old_idx))
274 }
275 _ => None,
276 })
277 .collect();
278
279 for (child_pos, nibble, old_child_idx) in children {
280 let mut child_path = branch_logical_path;
282 child_path.push(nibble);
283
284 let child_short_key = match &self.arena[old_child_idx] {
286 ArenaSparseNode::Branch(b) => &b.short_key,
287 ArenaSparseNode::Leaf { key, .. } => key,
288 other => unreachable!("subtrie prune: unexpected child node kind: {other:?}"),
289 };
290 let mut child_prefix = child_path;
291 child_prefix.extend(child_short_key);
292
293 if has_prefix(retained_leaves, &child_prefix) {
294 let child_node = self.arena.remove(old_child_idx).expect("child exists");
296 let new_child_idx = new_arena.insert(child_node);
297 let ArenaSparseNode::Branch(b) = &mut new_arena[new_idx] else {
298 unreachable!()
299 };
300 b.children[child_pos] = ArenaSparseNodeBranchChild::Revealed(new_child_idx);
301 queue.push_back((new_child_idx, child_path));
302 } else {
303 let rlp_node = self.arena[old_child_idx]
305 .state_ref()
306 .expect("child must have state")
307 .cached_rlp_node()
308 .cloned()
309 .expect("pruned child must have cached RLP (prune runs after hashing)");
310 let ArenaSparseNode::Branch(b) = &mut new_arena[new_idx] else {
311 unreachable!()
312 };
313 b.children[child_pos] = ArenaSparseNodeBranchChild::Blinded(rlp_node);
314 }
315 }
316 }
317
318 let pruned = old_count - new_arena.len();
319 self.num_leaves = new_num_leaves;
320 self.num_dirty_leaves = 0;
321 self.arena = new_arena;
322 self.root = new_root;
323 self.cached_memory_size =
324 self.arena.capacity() * slotmap_slot_size::<ArenaSparseNode>() + new_nodes_heap_size;
325
326 #[cfg(debug_assertions)]
327 self.debug_assert_counters();
328 return pruned;
329
330 fn has_prefix(sorted_keys: &[Nibbles], prefix: &Nibbles) -> bool {
332 let idx = sorted_keys.binary_search(prefix).unwrap_or_else(|i| i);
333 sorted_keys.get(idx).is_some_and(|p| p.starts_with(prefix))
334 }
335 }
336
337 #[instrument(
345 level = "trace",
346 target = TRACE_TARGET,
347 skip_all,
348 fields(
349 subtrie = ?self.path,
350 num_updates = sorted_updates.len(),
351 ),
352 )]
353 fn update_leaves(&mut self, sorted_updates: &[(B256, Nibbles, LeafUpdate)]) {
354 if sorted_updates.is_empty() {
355 return;
356 }
357 trace!(target: TRACE_TARGET, "Subtrie update_leaves");
358
359 debug_assert!(
360 !matches!(self.arena[self.root], ArenaSparseNode::EmptyRoot),
361 "subtrie root must not be EmptyRoot at start of update_leaves"
362 );
363
364 self.buffers.cursor.reset(&self.arena, self.root, self.path);
365
366 for (idx, &(key, ref full_path, ref update)) in sorted_updates.iter().enumerate() {
367 let find_result = self.buffers.cursor.seek(&mut self.arena, full_path);
368
369 if matches!(find_result, SeekResult::Blinded) {
371 let logical_len = self.buffers.cursor.head_logical_branch_path_len(&self.arena);
372 self.required_proofs.push((
373 idx,
374 ArenaRequiredProof { key, min_len: (logical_len as u8 + 1).min(64) },
375 ));
376 continue;
377 }
378
379 match update {
380 LeafUpdate::Changed(value) if !value.is_empty() => {
381 let (_result, deltas) = ArenaParallelSparseTrie::upsert_leaf(
383 &mut self.arena,
384 &mut self.buffers.cursor,
385 &mut self.root,
386 full_path,
387 value,
388 find_result,
389 );
390 self.num_leaves = (self.num_leaves as i64 + deltas.num_leaves_delta) as u64;
391 self.num_dirty_leaves =
392 (self.num_dirty_leaves as i64 + deltas.num_dirty_leaves_delta) as u64;
393 }
394 LeafUpdate::Changed(_) => {
395 let (result, deltas) = ArenaParallelSparseTrie::remove_leaf(
396 &mut self.arena,
397 &mut self.buffers.cursor,
398 &mut self.root,
399 key,
400 full_path,
401 find_result,
402 &mut self.buffers.updates,
403 );
404 self.num_leaves = (self.num_leaves as i64 + deltas.num_leaves_delta) as u64;
405 self.num_dirty_leaves =
406 (self.num_dirty_leaves as i64 + deltas.num_dirty_leaves_delta) as u64;
407
408 if let RemoveLeafResult::NeedsProof { key, proof_key, min_len } = result {
409 self.required_proofs
410 .push((idx, ArenaRequiredProof { key: proof_key, min_len }));
411 self.required_proofs.push((idx, ArenaRequiredProof { key, min_len }));
412 }
413 }
414 LeafUpdate::Touched => {}
415 }
416 }
417
418 self.buffers.cursor.drain(&mut self.arena);
420
421 #[cfg(debug_assertions)]
422 self.debug_assert_counters();
423 }
424
425 fn reveal_nodes(&mut self, nodes: &mut [ProofTrieNodeV2]) -> SparseTrieResult<()> {
428 if nodes.is_empty() {
429 return Ok(());
430 }
431 trace!(target: TRACE_TARGET, path = ?self.path, num_nodes = nodes.len(), "Subtrie reveal_nodes");
432
433 debug_assert!(
434 !matches!(self.arena[self.root], ArenaSparseNode::EmptyRoot),
435 "subtrie root must not be EmptyRoot in reveal_nodes"
436 );
437
438 self.buffers.cursor.reset(&self.arena, self.root, self.path);
439
440 for node in nodes.iter_mut() {
441 let find_result = self.buffers.cursor.seek(&mut self.arena, &node.path);
442 if ArenaParallelSparseTrie::reveal_node(
443 &mut self.arena,
444 &self.buffers.cursor,
445 node,
446 find_result,
447 )
448 .is_some_and(|child_idx| matches!(self.arena[child_idx], ArenaSparseNode::Leaf { .. }))
449 {
450 self.num_leaves += 1;
451 }
452 }
453
454 self.buffers.cursor.drain(&mut self.arena);
456
457 #[cfg(debug_assertions)]
458 self.debug_assert_counters();
459
460 Ok(())
461 }
462
463 fn update_cached_rlp(&mut self) {
468 ArenaParallelSparseTrie::update_cached_rlp(
469 &mut self.arena,
470 self.root,
471 &mut self.buffers.cursor,
472 &mut self.buffers.rlp_buf,
473 &mut self.buffers.rlp_node_buf,
474 self.path,
475 &mut self.buffers.updates,
476 );
477 self.num_dirty_leaves = 0;
478 #[cfg(debug_assertions)]
479 self.debug_assert_counters();
480 }
481}
482
483#[derive(Debug, Default)]
487struct SubtrieCounterDeltas {
488 num_leaves_delta: i64,
489 num_dirty_leaves_delta: i64,
490}
491
492#[derive(Debug)]
495enum UpsertLeafResult {
496 Updated,
498 NewLeaf,
500 NewChild,
503}
504
505#[derive(Debug)]
508enum RemoveLeafResult {
509 Removed,
511 NotFound,
513 NeedsProof { key: B256, proof_key: B256, min_len: u8 },
516}
517
518#[derive(Debug, Clone)]
520struct ArenaRequiredProof {
521 key: B256,
523 min_len: u8,
525}
526
527#[derive(Debug, Clone, Copy, PartialEq, Eq)]
532pub struct ArenaParallelismThresholds {
533 pub min_dirty_leaves: u64,
537 pub min_revealed_nodes: usize,
541 pub min_updates: usize,
545 pub min_leaves_for_prune: u64,
549}
550
551impl Default for ArenaParallelismThresholds {
552 fn default() -> Self {
553 Self {
554 min_dirty_leaves: 64,
555 min_revealed_nodes: 16,
556 min_updates: 128,
557 min_leaves_for_prune: 128,
558 }
559 }
560}
561
562#[derive(Debug, Clone)]
619pub struct ArenaParallelSparseTrie {
620 upper_arena: NodeArena,
622 root: Index,
624 updates: Option<SparseTrieUpdates>,
626 buffers: ArenaTrieBuffers,
628 parallelism_thresholds: ArenaParallelismThresholds,
630 #[cfg(feature = "trie-debug")]
632 debug_recorder: TrieDebugRecorder,
633}
634
635impl ArenaParallelSparseTrie {
636 pub const fn with_parallelism_thresholds(
638 mut self,
639 thresholds: ArenaParallelismThresholds,
640 ) -> Self {
641 self.parallelism_thresholds = thresholds;
642 self
643 }
644
645 #[cfg(feature = "trie-debug")]
651 fn record_initial_state(&mut self) {
652 use crate::debug_recorder::{NodeStateRecord, TrieNodeRecord};
653 use alloy_primitives::hex;
654 use alloy_trie::nodes::{BranchNode, TrieNode};
655
656 fn state_to_record(state: &ArenaSparseNodeState) -> NodeStateRecord {
657 match state {
658 ArenaSparseNodeState::Revealed => NodeStateRecord::Revealed,
659 ArenaSparseNodeState::Cached { rlp_node } => {
660 NodeStateRecord::Cached { rlp_node: hex::encode(rlp_node.as_ref()) }
661 }
662 ArenaSparseNodeState::Dirty => NodeStateRecord::Dirty,
663 }
664 }
665
666 fn node_to_record(
670 arena: &NodeArena,
671 idx: Index,
672 path: Nibbles,
673 ) -> Option<ProofTrieNodeRecord> {
674 match &arena[idx] {
675 ArenaSparseNode::EmptyRoot => Some(ProofTrieNodeRecord {
676 path,
677 node: TrieNodeRecord(TrieNode::EmptyRoot),
678 masks: None,
679 short_key: None,
680 state: None,
681 }),
682 ArenaSparseNode::Branch(b) => {
683 let stack = b
684 .children
685 .iter()
686 .map(|child| match child {
687 ArenaSparseNodeBranchChild::Blinded(rlp) => rlp.clone(),
688 ArenaSparseNodeBranchChild::Revealed(child_idx) => {
689 arena[*child_idx]
691 .state_ref()
692 .and_then(|s| s.cached_rlp_node())
693 .cloned()
694 .unwrap_or_default()
695 }
696 })
697 .collect();
698 Some(ProofTrieNodeRecord {
699 path,
700 node: TrieNodeRecord(TrieNode::Branch(BranchNode::new(
701 stack,
702 b.state_mask,
703 ))),
704 masks: Some((
705 b.branch_masks.hash_mask.get(),
706 b.branch_masks.tree_mask.get(),
707 )),
708 short_key: (!b.short_key.is_empty()).then_some(b.short_key),
709 state: Some(state_to_record(&b.state)),
710 })
711 }
712 ArenaSparseNode::Leaf { key, value, state, .. } => Some(ProofTrieNodeRecord {
713 path,
714 node: TrieNodeRecord(TrieNode::Leaf(alloy_trie::nodes::LeafNode::new(
715 *key,
716 value.clone(),
717 ))),
718 masks: None,
719 short_key: None,
720 state: Some(state_to_record(state)),
721 }),
722 ArenaSparseNode::Subtrie(_) | ArenaSparseNode::TakenSubtrie => None,
723 }
724 }
725
726 fn collect_records(
728 arena: &mut NodeArena,
729 root: Index,
730 root_path: Nibbles,
731 cursor: &mut ArenaCursor,
732 result: &mut Vec<ProofTrieNodeRecord>,
733 ) {
734 cursor.reset(arena, root, root_path);
735
736 if let Some(record) = node_to_record(arena, root, root_path) {
738 result.push(record);
739 }
740
741 loop {
742 match cursor.next(arena, |_, node| {
743 matches!(node, ArenaSparseNode::Branch(_) | ArenaSparseNode::Leaf { .. })
744 }) {
745 NextResult::Done => break,
746 NextResult::Branch | NextResult::NonBranch => {
747 let head = cursor.head().expect("cursor is non-empty");
748 if let Some(record) = node_to_record(arena, head.index, head.path) {
749 result.push(record);
750 }
751 }
752 }
753 }
754 }
755
756 let mut nodes = Vec::new();
757
758 collect_records(
760 &mut self.upper_arena,
761 self.root,
762 Nibbles::default(),
763 &mut self.buffers.cursor,
764 &mut nodes,
765 );
766
767 for (_, node) in &mut self.upper_arena {
769 if let ArenaSparseNode::Subtrie(subtrie) = node {
770 collect_records(
771 &mut subtrie.arena,
772 subtrie.root,
773 subtrie.path,
774 &mut self.buffers.cursor,
775 &mut nodes,
776 );
777 }
778 }
779
780 self.debug_recorder.reset();
782 self.debug_recorder.record(RecordedOp::Prune);
783
784 if let Some(root_record) = nodes.first() {
786 self.debug_recorder.record(RecordedOp::SetRoot { node: root_record.clone() });
787 }
788 if nodes.len() > 1 {
789 self.debug_recorder.record(RecordedOp::RevealNodes { nodes: nodes[1..].to_vec() });
790 }
791 }
792
793 const fn should_be_subtrie(path_len: usize) -> bool {
796 path_len == UPPER_TRIE_MAX_DEPTH
797 }
798
799 fn maybe_wrap_in_subtrie(&mut self, child_idx: Index, child_path: &Nibbles) {
804 if !Self::should_be_subtrie(child_path.len()) {
805 return;
806 }
807
808 if !matches!(
810 self.upper_arena[child_idx],
811 ArenaSparseNode::Branch(_) | ArenaSparseNode::Leaf { .. }
812 ) {
813 return;
814 }
815
816 trace!(target: TRACE_TARGET, ?child_path, "Wrapping child into subtrie");
817 let mut subtrie = ArenaSparseSubtrie::new(self.updates.is_some());
818 subtrie.path = *child_path;
819 let mut root_node =
820 mem::replace(&mut self.upper_arena[child_idx], ArenaSparseNode::TakenSubtrie);
821
822 if let ArenaSparseNode::Branch(b) = &mut root_node {
824 for child in &mut b.children {
825 if let ArenaSparseNodeBranchChild::Revealed(idx) = child {
826 *idx =
827 Self::migrate_nodes(&mut subtrie.arena, &mut self.upper_arena, *idx, None);
828 }
829 }
830 }
831
832 subtrie.arena[subtrie.root] = root_node;
833 let (leaves, dirty) = Self::count_leaves_and_dirty(&subtrie.arena, subtrie.root);
834 subtrie.num_leaves = leaves;
835 subtrie.num_dirty_leaves = dirty;
836 #[cfg(debug_assertions)]
837 subtrie.debug_assert_counters();
838 self.upper_arena[child_idx] = ArenaSparseNode::Subtrie(subtrie);
839 }
840
841 fn maybe_wrap_branch_children(&mut self, cursor: &ArenaCursor) {
846 let head = cursor.head().expect("cursor is non-empty");
847 let head_idx = head.index;
848 let head_path = head.path;
849
850 let ArenaSparseNode::Branch(b) = &self.upper_arena[head_idx] else { return };
851 let short_key = b.short_key;
852 let children: SmallVec<[_; 4]> = b
853 .child_iter()
854 .filter_map(|(nibble, child)| match child {
855 ArenaSparseNodeBranchChild::Revealed(idx) => Some((nibble, *idx)),
856 ArenaSparseNodeBranchChild::Blinded(_) => None,
857 })
858 .collect();
859
860 for (nibble, child_idx) in children {
861 let mut child_path = head_path;
862 child_path.extend(&short_key);
863 child_path.push_unchecked(nibble);
864 self.maybe_wrap_in_subtrie(child_idx, &child_path);
865 }
866 }
867
868 #[instrument(
877 level = "trace",
878 target = TRACE_TARGET,
879 skip_all,
880 fields(subtrie_path = ?cursor.head().expect("cursor is non-empty").path),
881 )]
882 fn maybe_unwrap_subtrie(&mut self, cursor: &mut ArenaCursor) {
883 let subtrie_idx = cursor.head().expect("cursor is non-empty").index;
884
885 let ArenaSparseNode::Subtrie(subtrie) = &self.upper_arena[subtrie_idx] else {
886 return;
887 };
888
889 if !matches!(subtrie.arena[subtrie.root], ArenaSparseNode::EmptyRoot) {
890 return;
891 }
892
893 let child_nibble = cursor
894 .head()
895 .expect("cursor is non-empty")
896 .path
897 .last()
898 .expect("subtrie path must have at least one nibble");
899 let parent_idx = cursor.parent().expect("cursor has parent").index;
900
901 cursor.pop(&mut self.upper_arena);
904
905 self.recycle_subtrie_from_idx(subtrie_idx);
906
907 trace!(target: TRACE_TARGET, "Unwrapping empty subtrie, removing child slot");
908 let parent_branch = self.upper_arena[parent_idx].branch_mut();
909 let child_idx = BranchChildIdx::new(parent_branch.state_mask, child_nibble)
910 .expect("child nibble not found in parent state_mask");
911
912 parent_branch.children.remove(child_idx.get());
913 parent_branch.unset_child_bit(child_nibble);
914 parent_branch.state = parent_branch.state.to_dirty();
916
917 self.maybe_collapse_or_remove_branch(cursor);
918 }
919
920 fn recycle_subtrie(&mut self, node: ArenaSparseNode) {
926 let ArenaSparseNode::Subtrie(mut subtrie) = node else {
927 unreachable!("recycle_subtrie called on non-Subtrie node")
928 };
929 Self::merge_subtrie_updates(&mut self.buffers.updates, &mut subtrie.buffers.updates);
930 }
931
932 fn recycle_subtrie_from_idx(&mut self, idx: Index) {
934 let node = self.upper_arena.remove(idx).expect("subtrie exists in arena");
935 self.recycle_subtrie(node);
936 }
937
938 fn maybe_collapse_or_remove_branch(&mut self, cursor: &mut ArenaCursor) {
949 loop {
950 let branch_idx = cursor.head().expect("cursor is non-empty").index;
951
952 let count = {
955 let ArenaSparseNode::Branch(b) = &self.upper_arena[branch_idx] else {
956 return;
957 };
958 b.state_mask.count_bits()
959 };
960
961 if count >= 2 {
962 return;
963 }
964
965 if count == 0 {
966 if branch_idx == self.root {
967 self.upper_arena[branch_idx] = ArenaSparseNode::EmptyRoot;
968 return;
969 }
970 let branch_nibble = cursor
972 .head()
973 .expect("cursor is non-empty")
974 .path
975 .last()
976 .expect("non-root branch");
977 cursor.pop(&mut self.upper_arena);
978 self.upper_arena.remove(branch_idx);
979 let parent_idx = cursor.head().expect("cursor is non-empty").index;
980 let parent_branch = self.upper_arena[parent_idx].branch_mut();
981 let child_idx = BranchChildIdx::new(parent_branch.state_mask, branch_nibble)
982 .expect("child nibble not found in parent state_mask");
983 parent_branch.children.remove(child_idx.get());
984 parent_branch.unset_child_bit(branch_nibble);
985 parent_branch.state = parent_branch.state.to_dirty();
986 continue; }
988
989 let (remaining_nibble, remaining_child_idx) = {
991 let b = self.upper_arena[branch_idx].branch_ref();
992 let nibble = b.state_mask.iter().next().expect("branch has at least one child");
993 let child_idx = match &b.children[0] {
994 ArenaSparseNodeBranchChild::Revealed(idx) => Some(*idx),
995 ArenaSparseNodeBranchChild::Blinded(_) => None,
996 };
997 (nibble, child_idx)
998 };
999
1000 let Some(child_idx) = remaining_child_idx else {
1001 debug_assert!(false, "single remaining child is blinded — should have been caught by check_subtrie_collapse_needs_proof");
1002 return;
1003 };
1004
1005 if matches!(self.upper_arena[child_idx], ArenaSparseNode::TakenSubtrie) {
1006 return;
1009 }
1010
1011 let is_empty_subtrie = matches!(
1013 &self.upper_arena[child_idx],
1014 ArenaSparseNode::Subtrie(s) if matches!(s.arena[s.root], ArenaSparseNode::EmptyRoot)
1015 );
1016
1017 if is_empty_subtrie {
1018 self.recycle_subtrie_from_idx(child_idx);
1019 let branch = self.upper_arena[branch_idx].branch_mut();
1020 branch.children.remove(0);
1021 branch.unset_child_bit(remaining_nibble);
1022 branch.state = branch.state.to_dirty();
1023 continue; }
1025
1026 Self::collapse_branch(
1028 &mut self.upper_arena,
1029 cursor,
1030 &mut self.root,
1031 &mut self.buffers.updates,
1032 );
1033
1034 let child_idx = cursor.head().expect("cursor is non-empty").index;
1039 if let ArenaSparseNode::Subtrie(_) = &self.upper_arena[child_idx] {
1040 let ArenaSparseNode::Subtrie(mut subtrie) =
1041 mem::replace(&mut self.upper_arena[child_idx], ArenaSparseNode::TakenSubtrie)
1042 else {
1043 unreachable!()
1044 };
1045 Self::migrate_nodes(
1046 &mut self.upper_arena,
1047 &mut subtrie.arena,
1048 subtrie.root,
1049 Some(child_idx),
1050 );
1051 Self::merge_subtrie_updates(
1052 &mut self.buffers.updates,
1053 &mut subtrie.buffers.updates,
1054 );
1055
1056 self.maybe_wrap_branch_children(cursor);
1060 }
1061 return;
1062 }
1063 }
1064
1065 fn merge_subtrie_updates(
1072 dst: &mut Option<SparseTrieUpdates>,
1073 src: &mut Option<SparseTrieUpdates>,
1074 ) {
1075 if let Some(dst_updates) = dst.as_mut() {
1076 let src_updates = src.as_mut().expect("updates are enabled");
1077 debug_assert!(!src_updates.wiped, "subtrie updates should never have wiped=true");
1078
1079 for path in src_updates.updated_nodes.keys() {
1081 dst_updates.removed_nodes.remove(path);
1082 }
1083 dst_updates.updated_nodes.extend(src_updates.updated_nodes.drain());
1084
1085 for path in &src_updates.removed_nodes {
1087 dst_updates.updated_nodes.remove(path);
1088 }
1089 dst_updates.removed_nodes.extend(src_updates.removed_nodes.drain());
1090 }
1091 }
1092
1093 fn nibbles_to_padded_b256(path: &Nibbles) -> B256 {
1095 let mut bytes = [0u8; 32];
1096 path.pack_to(&mut bytes);
1097 B256::from(bytes)
1098 }
1099
1100 fn get_branch_masks(arena: &NodeArena, branch: &ArenaSparseNodeBranch) -> BranchNodeMasks {
1102 let mut masks = BranchNodeMasks::default();
1103
1104 for (nibble, child) in branch.child_iter() {
1105 let (hash_bit, tree_bit) = match child {
1106 ArenaSparseNodeBranchChild::Blinded(_) => (
1107 branch.branch_masks.hash_mask.is_bit_set(nibble),
1108 branch.branch_masks.tree_mask.is_bit_set(nibble),
1109 ),
1110 ArenaSparseNodeBranchChild::Revealed(child_idx) => {
1111 let child = &arena[*child_idx];
1112 (child.hash_mask_bit(), child.tree_mask_bit())
1113 }
1114 };
1115
1116 masks.set_child_bits(nibble, hash_bit, tree_bit);
1117 }
1118
1119 masks
1120 }
1121
1122 #[instrument(level = "trace", target = TRACE_TARGET, skip_all, fields(base_path = ?base_path), ret)]
1135 fn update_cached_rlp(
1136 arena: &mut NodeArena,
1137 root: Index,
1138 cursor: &mut ArenaCursor,
1139 rlp_buf: &mut Vec<u8>,
1140 rlp_node_buf: &mut Vec<RlpNode>,
1141 base_path: Nibbles,
1142 updates: &mut Option<SparseTrieUpdates>,
1143 ) -> RlpNode {
1144 rlp_node_buf.clear();
1145
1146 match &arena[root] {
1150 ArenaSparseNode::EmptyRoot => return RlpNode::word_rlp(&EMPTY_ROOT_HASH),
1151 ArenaSparseNode::Leaf { .. } => {
1152 Self::encode_leaf(arena, root, rlp_buf, rlp_node_buf);
1153 return rlp_node_buf.pop().expect("encode_leaf must push an RlpNode");
1154 }
1155 ArenaSparseNode::Branch(b) => {
1156 if let ArenaSparseNodeState::Cached { rlp_node, .. } = &b.state {
1157 return rlp_node.clone();
1158 }
1159 }
1160 ArenaSparseNode::Subtrie(_) | ArenaSparseNode::TakenSubtrie => {
1161 unreachable!("Subtrie/TakenSubtrie should not appear inside a subtrie's own arena");
1162 }
1163 }
1164
1165 cursor.reset(arena, root, base_path);
1166
1167 loop {
1171 let result = cursor.next(&mut *arena, |_, node| {
1172 matches!(
1173 node,
1174 ArenaSparseNode::Branch(b) if matches!(b.state, ArenaSparseNodeState::Dirty)
1175 )
1176 });
1177
1178 match result {
1179 NextResult::Done => break,
1180 NextResult::NonBranch => {
1181 unreachable!("should_descend only returns true for dirty branches")
1182 }
1183 NextResult::Branch => {}
1184 };
1185
1186 let head = cursor.head().expect("cursor is non-empty");
1187 let head_idx = head.index;
1188 let head_path = head.path;
1189
1190 trace!(
1194 target: TRACE_TARGET,
1195 branch_path = ?head_path,
1196 branch_short_key = ?arena[head_idx].short_key().expect("head is a branch"),
1197 state_mask = ?arena[head_idx].branch_ref().state_mask,
1198 "Calculating branch RlpNode",
1199 );
1200
1201 rlp_node_buf.clear();
1202 let state_mask = arena[head_idx].branch_ref().state_mask;
1203 for (child_idx, _nibble) in BranchChildIter::new(state_mask) {
1204 match &arena[head_idx].branch_ref().children[child_idx] {
1205 ArenaSparseNodeBranchChild::Blinded(rlp_node) => {
1206 rlp_node_buf.push(rlp_node.clone());
1207 }
1208 ArenaSparseNodeBranchChild::Revealed(child_idx) => {
1209 let child_idx = *child_idx;
1210 match &arena[child_idx] {
1211 ArenaSparseNode::Leaf { .. } => {
1212 Self::encode_leaf(arena, child_idx, rlp_buf, rlp_node_buf);
1213 }
1214 ArenaSparseNode::Branch(child_b) => {
1215 let ArenaSparseNodeState::Cached { rlp_node, .. } = &child_b.state
1216 else {
1217 panic!("child branch must be cached after DFS");
1218 };
1219 rlp_node_buf.push(rlp_node.clone());
1220 }
1221 ArenaSparseNode::Subtrie(subtrie) => {
1222 let subtrie_root = &subtrie.arena[subtrie.root];
1223 match subtrie_root {
1224 ArenaSparseNode::Branch(ArenaSparseNodeBranch {
1225 state: ArenaSparseNodeState::Cached { rlp_node, .. },
1226 ..
1227 }) |
1228 ArenaSparseNode::Leaf {
1229 state: ArenaSparseNodeState::Cached { rlp_node, .. },
1230 ..
1231 } => {
1232 rlp_node_buf.push(rlp_node.clone());
1233 }
1234 _ => panic!("subtrie root must be a cached Branch or Leaf"),
1235 }
1236 }
1237 ArenaSparseNode::TakenSubtrie | ArenaSparseNode::EmptyRoot => {
1238 unreachable!("Unexpected child {:?}", arena[child_idx]);
1239 }
1240 }
1241 }
1242 }
1243 }
1244
1245 let b = arena[head_idx].branch_ref();
1247 let short_key = b.short_key;
1248 let state_mask = b.state_mask;
1249 let prev_branch_masks = b.branch_masks;
1250 let new_branch_masks = Self::get_branch_masks(arena, b);
1251 let was_dirty = matches!(b.state, ArenaSparseNodeState::Dirty);
1252
1253 rlp_buf.clear();
1254 let rlp_node = BranchNodeRef::new(rlp_node_buf, state_mask).rlp(rlp_buf);
1255
1256 let rlp_node = if short_key.is_empty() {
1257 rlp_node
1258 } else {
1259 rlp_buf.clear();
1260 ExtensionNodeRef::new(&short_key, &rlp_node).rlp(rlp_buf)
1261 };
1262
1263 trace!(
1264 target: TRACE_TARGET,
1265 path = ?head_path,
1266 short_key = ?arena[head_idx].short_key(),
1267 children = ?state_mask.iter().zip(rlp_node_buf.iter()).collect::<Vec<_>>(),
1268 rlp_node = ?rlp_node,
1269 "Calculated branch RlpNode",
1270 );
1271
1272 let branch = arena[head_idx].branch_mut();
1273 branch.state = ArenaSparseNodeState::Cached { rlp_node: rlp_node.clone() };
1274 branch.branch_masks = new_branch_masks;
1275
1276 if let Some(trie_updates) = updates.as_mut().filter(|_| was_dirty) {
1279 let mut logical_path = head_path;
1280 logical_path.extend(&short_key);
1281
1282 if !logical_path.is_empty() {
1283 if !prev_branch_masks.is_empty() && new_branch_masks.is_empty() {
1284 trie_updates.updated_nodes.remove(&logical_path);
1285 trie_updates.removed_nodes.insert(logical_path);
1286 } else if !new_branch_masks.is_empty() {
1287 let compact = arena[head_idx].branch_ref().branch_node_compact(arena);
1288 trie_updates.updated_nodes.insert(logical_path, compact);
1289 trie_updates.removed_nodes.remove(&logical_path);
1290 }
1291 }
1292 }
1293 }
1294
1295 let ArenaSparseNodeState::Cached { rlp_node, .. } = &arena[root].branch_ref().state else {
1296 panic!("root must be cached after update_cached_rlp");
1297 };
1298 rlp_node.clone()
1299 }
1300
1301 fn get_leaf_value_in_arena<'a>(
1304 arena: &'a NodeArena,
1305 mut current: Index,
1306 full_path: &Nibbles,
1307 mut path_offset: usize,
1308 ) -> Option<&'a Vec<u8>> {
1309 loop {
1310 match &arena[current] {
1311 ArenaSparseNode::EmptyRoot | ArenaSparseNode::TakenSubtrie => return None,
1312 ArenaSparseNode::Leaf { key, value, .. } => {
1313 let remaining = full_path.slice(path_offset..);
1314 return (remaining == *key).then_some(value);
1315 }
1316 ArenaSparseNode::Branch(b) => {
1317 let short_key = &b.short_key;
1318 let logical_end = path_offset + short_key.len();
1319 if full_path.len() <= logical_end ||
1320 full_path.slice(path_offset..logical_end) != *short_key
1321 {
1322 return None;
1323 }
1324
1325 let child_nibble = full_path.get_unchecked(logical_end);
1326 let child_idx = BranchChildIdx::new(b.state_mask, child_nibble)?;
1327 match &b.children[child_idx] {
1328 ArenaSparseNodeBranchChild::Blinded(_) => return None,
1329 ArenaSparseNodeBranchChild::Revealed(child_idx) => {
1330 current = *child_idx;
1331 path_offset = logical_end + 1;
1332 }
1333 }
1334 }
1335 ArenaSparseNode::Subtrie(subtrie) => {
1336 return Self::get_leaf_value_in_arena(
1337 &subtrie.arena,
1338 subtrie.root,
1339 full_path,
1340 path_offset,
1341 );
1342 }
1343 }
1344 }
1345 }
1346
1347 fn find_leaf_in_arena(
1351 arena: &NodeArena,
1352 mut current: Index,
1353 full_path: &Nibbles,
1354 mut path_offset: usize,
1355 expected_value: Option<&Vec<u8>>,
1356 ) -> Result<LeafLookup, LeafLookupError> {
1357 loop {
1358 match &arena[current] {
1359 ArenaSparseNode::EmptyRoot | ArenaSparseNode::TakenSubtrie => {
1360 return Ok(LeafLookup::NonExistent);
1361 }
1362 ArenaSparseNode::Leaf { key, value, .. } => {
1363 let remaining = full_path.slice(path_offset..);
1364 if remaining != *key {
1365 return Ok(LeafLookup::NonExistent);
1366 }
1367 if let Some(expected) = expected_value &&
1368 *expected != *value
1369 {
1370 return Err(LeafLookupError::ValueMismatch {
1371 path: *full_path,
1372 expected: Some(expected.clone()),
1373 actual: value.clone(),
1374 });
1375 }
1376 return Ok(LeafLookup::Exists);
1377 }
1378 ArenaSparseNode::Branch(b) => {
1379 let short_key = &b.short_key;
1380 let logical_end = path_offset + short_key.len();
1381
1382 if full_path.len() <= logical_end {
1383 return Ok(LeafLookup::NonExistent);
1384 }
1385
1386 if full_path.slice(path_offset..logical_end) != *short_key {
1387 return Ok(LeafLookup::NonExistent);
1388 }
1389
1390 let child_nibble = full_path.get_unchecked(logical_end);
1391 let Some(child_idx) = BranchChildIdx::new(b.state_mask, child_nibble) else {
1392 return Ok(LeafLookup::NonExistent);
1393 };
1394
1395 match &b.children[child_idx] {
1396 ArenaSparseNodeBranchChild::Blinded(rlp_node) => {
1397 let hash = rlp_node
1398 .as_hash()
1399 .unwrap_or_else(|| keccak256(rlp_node.as_slice()));
1400 let mut blinded_path = full_path.slice(..logical_end);
1401 blinded_path.push_unchecked(child_nibble);
1402 return Err(LeafLookupError::BlindedNode { path: blinded_path, hash });
1403 }
1404 ArenaSparseNodeBranchChild::Revealed(child_idx) => {
1405 current = *child_idx;
1406 path_offset = logical_end + 1;
1407 }
1408 }
1409 }
1410 ArenaSparseNode::Subtrie(subtrie) => {
1411 return Self::find_leaf_in_arena(
1412 &subtrie.arena,
1413 subtrie.root,
1414 full_path,
1415 path_offset,
1416 expected_value,
1417 );
1418 }
1419 }
1420 }
1421 }
1422
1423 fn encode_leaf(
1426 arena: &mut NodeArena,
1427 idx: Index,
1428 rlp_buf: &mut Vec<u8>,
1429 rlp_node_buf: &mut Vec<RlpNode>,
1430 ) {
1431 let (key, value, state) = match &arena[idx] {
1432 ArenaSparseNode::Leaf { key, value, state } => (key, value, state),
1433 _ => unreachable!("encode_leaf called on non-Leaf node"),
1434 };
1435
1436 if let ArenaSparseNodeState::Cached { rlp_node, .. } = state {
1437 rlp_node_buf.push(rlp_node.clone());
1438 return;
1439 }
1440
1441 rlp_buf.clear();
1442 let rlp_node = LeafNodeRef { key, value }.rlp(rlp_buf);
1443
1444 *arena[idx].state_mut() = ArenaSparseNodeState::Cached { rlp_node: rlp_node.clone() };
1445 rlp_node_buf.push(rlp_node);
1446 }
1447
1448 fn split_and_insert_leaf(
1462 arena: &mut NodeArena,
1463 cursor: &mut ArenaCursor,
1464 root: &mut Index,
1465 new_leaf_path: Nibbles,
1466 value: &[u8],
1467 ) -> bool {
1468 let old_child_entry = cursor.head().expect("cursor must have head");
1469 let old_child_idx = old_child_entry.index;
1470 let old_child_short_key = arena[old_child_idx].short_key().expect("top of stack is a leaf");
1471 let diverge_len = new_leaf_path.common_prefix_length(old_child_short_key);
1472
1473 trace!(
1474 target: TRACE_TARGET,
1475 path = ?old_child_entry.path,
1476 ?new_leaf_path,
1477 ?old_child_short_key,
1478 diverge_len,
1479 "Splitting node and inserting new leaf",
1480 );
1481
1482 let old_child_nibble = old_child_short_key.get_unchecked(diverge_len);
1483 let old_child_suffix = old_child_short_key.slice(diverge_len + 1..);
1484
1485 let newly_dirtied_existing = match &mut arena[old_child_idx] {
1488 ArenaSparseNode::Leaf { key, state, .. } => {
1489 *key = old_child_suffix;
1490 let was_clean = !matches!(state, ArenaSparseNodeState::Dirty);
1491 *state = ArenaSparseNodeState::Dirty;
1492 was_clean
1493 }
1494 ArenaSparseNode::Branch(b) => {
1495 b.short_key = old_child_suffix;
1496 b.state = b.state.to_dirty();
1497 false
1499 }
1500 _ => unreachable!("split_and_insert_leaf called on non-Leaf/Branch node"),
1501 };
1502
1503 let short_key = new_leaf_path.slice(..diverge_len);
1504 let new_leaf_nibble = new_leaf_path.get_unchecked(diverge_len);
1505 debug_assert_ne!(old_child_nibble, new_leaf_nibble);
1506
1507 let new_leaf_idx = arena.insert(ArenaSparseNode::Leaf {
1508 state: ArenaSparseNodeState::Dirty,
1509 key: new_leaf_path.slice(diverge_len + 1..),
1510 value: value.to_vec(),
1511 });
1512
1513 let (first_nibble, first_child, second_nibble, second_child) =
1514 if old_child_nibble < new_leaf_nibble {
1515 (old_child_nibble, old_child_idx, new_leaf_nibble, new_leaf_idx)
1516 } else {
1517 (new_leaf_nibble, new_leaf_idx, old_child_nibble, old_child_idx)
1518 };
1519
1520 let state_mask = TrieMask::from(1u16 << first_nibble | 1u16 << second_nibble);
1521 let mut children = SmallVec::with_capacity(2);
1522 children.push(ArenaSparseNodeBranchChild::Revealed(first_child));
1523 children.push(ArenaSparseNodeBranchChild::Revealed(second_child));
1524
1525 let new_branch_idx = arena.insert(ArenaSparseNode::Branch(ArenaSparseNodeBranch {
1526 state: ArenaSparseNodeState::Dirty,
1527 children,
1528 state_mask,
1529 short_key,
1530 branch_masks: BranchNodeMasks::default(),
1531 }));
1532
1533 cursor.replace_head_index(arena, root, new_branch_idx);
1534 newly_dirtied_existing
1535 }
1536
1537 #[instrument(level = "trace", target = TRACE_TARGET, skip_all, fields(full_path = ?full_path))]
1552 fn upsert_leaf(
1553 arena: &mut NodeArena,
1554 cursor: &mut ArenaCursor,
1555 root: &mut Index,
1556 full_path: &Nibbles,
1557 value: &[u8],
1558 find_result: SeekResult,
1559 ) -> (UpsertLeafResult, SubtrieCounterDeltas) {
1560 trace!(target: TRACE_TARGET, ?find_result, "Upserting leaf");
1561 let head = cursor.head().expect("cursor is non-empty");
1562
1563 match find_result {
1564 SeekResult::Blinded => {
1565 unreachable!("Blinded case must be handled by caller")
1566 }
1567 SeekResult::EmptyRoot => {
1568 let head_idx = head.index;
1569 let head_path = head.path;
1570 arena[head_idx] = ArenaSparseNode::Leaf {
1571 state: ArenaSparseNodeState::Dirty,
1572 key: full_path.slice(head_path.len()..),
1573 value: value.to_vec(),
1574 };
1575 (
1576 UpsertLeafResult::NewLeaf,
1577 SubtrieCounterDeltas { num_leaves_delta: 1, num_dirty_leaves_delta: 1 },
1578 )
1579 }
1580 SeekResult::RevealedLeaf => {
1581 let head_idx = head.index;
1583 let was_clean =
1584 if let ArenaSparseNode::Leaf { value: v, state, .. } = &mut arena[head_idx] {
1585 v.clear();
1586 v.extend_from_slice(value);
1587 let was_clean = !matches!(state, ArenaSparseNodeState::Dirty);
1588 *state = ArenaSparseNodeState::Dirty;
1589 was_clean
1590 } else {
1591 unreachable!("RevealedLeaf but cursor head is not a leaf")
1592 };
1593 (
1594 UpsertLeafResult::Updated,
1595 SubtrieCounterDeltas {
1596 num_leaves_delta: 0,
1597 num_dirty_leaves_delta: was_clean as i64,
1598 },
1599 )
1600 }
1601 SeekResult::Diverged => {
1602 let head_path = head.path;
1603 let full_path_from_head = full_path.slice(head_path.len()..);
1604
1605 let split_dirtied_existing =
1606 Self::split_and_insert_leaf(arena, cursor, root, full_path_from_head, value);
1607
1608 let result = if cursor.depth() >= 1 {
1609 UpsertLeafResult::NewChild
1610 } else {
1611 UpsertLeafResult::NewLeaf
1612 };
1613 (
1614 result,
1615 SubtrieCounterDeltas {
1616 num_leaves_delta: 1,
1617 num_dirty_leaves_delta: 1 + split_dirtied_existing as i64,
1618 },
1619 )
1620 }
1621 SeekResult::NoChild { child_nibble } => {
1622 let head_idx = head.index;
1623
1624 let head_branch_logical_path = cursor.head_logical_branch_path(arena);
1625 let leaf_key = full_path.slice(head_branch_logical_path.len() + 1..);
1626 let new_leaf = arena.insert(ArenaSparseNode::Leaf {
1627 state: ArenaSparseNodeState::Dirty,
1628 key: leaf_key,
1629 value: value.to_vec(),
1630 });
1631
1632 let branch = arena[head_idx].branch_mut();
1633 branch.set_child(child_nibble, ArenaSparseNodeBranchChild::Revealed(new_leaf));
1634
1635 cursor.seek(arena, full_path);
1637
1638 (
1639 UpsertLeafResult::NewChild,
1640 SubtrieCounterDeltas { num_leaves_delta: 1, num_dirty_leaves_delta: 1 },
1641 )
1642 }
1643 SeekResult::RevealedSubtrie => {
1644 unreachable!("RevealedSubtrie must be handled by caller")
1645 }
1646 }
1647 }
1648
1649 fn remove_leaf(
1665 arena: &mut NodeArena,
1666 cursor: &mut ArenaCursor,
1667 root: &mut Index,
1668 key: B256,
1669 full_path: &Nibbles,
1670 find_result: SeekResult,
1671 updates: &mut Option<SparseTrieUpdates>,
1672 ) -> (RemoveLeafResult, SubtrieCounterDeltas) {
1673 match find_result {
1674 SeekResult::Blinded | SeekResult::RevealedSubtrie => {
1675 unreachable!("Blinded/RevealedSubtrie must be handled by caller")
1676 }
1677 SeekResult::EmptyRoot | SeekResult::Diverged | SeekResult::NoChild { .. } => {
1678 (RemoveLeafResult::NotFound, SubtrieCounterDeltas::default())
1679 }
1680 SeekResult::RevealedLeaf => {
1681 let head = cursor.head().expect("cursor is non-empty");
1683 let head_idx = head.index;
1684 let head_path = head.path;
1685
1686 trace!(
1687 target: TRACE_TARGET,
1688 path = ?head_path,
1689 ?full_path,
1690 "Removing leaf",
1691 );
1692
1693 if let Some(parent_entry) = cursor.parent() {
1696 let parent_idx = parent_entry.index;
1697 let child_nibble = head_path.last().expect("non-root leaf");
1698 let parent_branch = arena[parent_idx].branch_ref();
1699
1700 if parent_branch.state_mask.count_bits() == 2 &&
1701 parent_branch.sibling_child(child_nibble).is_blinded()
1702 {
1703 let sibling_nibble = parent_branch
1704 .state_mask
1705 .iter()
1706 .find(|&n| n != child_nibble)
1707 .expect("branch has two children");
1708 let mut sibling_path = cursor.parent_logical_branch_path(arena);
1709 sibling_path.push_unchecked(sibling_nibble);
1710 trace!(target: TRACE_TARGET, ?full_path, ?sibling_path, "Removal would collapse branch onto blinded sibling, requesting proof");
1711 return (
1712 RemoveLeafResult::NeedsProof {
1713 key,
1714 proof_key: Self::nibbles_to_padded_b256(&sibling_path),
1715 min_len: (sibling_path.len() as u8).min(64),
1716 },
1717 SubtrieCounterDeltas::default(),
1718 );
1719 }
1720 }
1721
1722 let removed_was_dirty =
1724 matches!(arena[head_idx].state_ref(), Some(ArenaSparseNodeState::Dirty));
1725
1726 if cursor.depth() == 0 {
1727 arena.remove(head_idx);
1730 *root = arena.insert(ArenaSparseNode::EmptyRoot);
1731 cursor.reset(arena, *root, head_path);
1732 return (
1733 RemoveLeafResult::Removed,
1734 SubtrieCounterDeltas {
1735 num_leaves_delta: -1,
1736 num_dirty_leaves_delta: -(removed_was_dirty as i64),
1737 },
1738 );
1739 }
1740
1741 cursor.pop(arena);
1743
1744 let parent_entry = cursor.head().expect("cursor is non-empty");
1746 let parent_idx = parent_entry.index;
1747 let child_nibble = head_path.last().expect("non-root leaf");
1748
1749 arena.remove(head_idx);
1751 let parent_branch = arena[parent_idx].branch_mut();
1752 parent_branch.remove_child(child_nibble);
1753
1754 let collapse_dirtied_leaf = if parent_branch.state_mask.count_bits() == 1 {
1757 Self::collapse_branch(arena, cursor, root, updates)
1758 } else {
1759 false
1760 };
1761 (
1762 RemoveLeafResult::Removed,
1763 SubtrieCounterDeltas {
1764 num_leaves_delta: -1,
1765 num_dirty_leaves_delta: (collapse_dirtied_leaf as i64) -
1766 (removed_was_dirty as i64),
1767 },
1768 )
1769 }
1770 }
1771 }
1772
1773 fn check_subtrie_collapse_needs_proof(
1779 arena: &NodeArena,
1780 cursor: &ArenaCursor,
1781 subtrie_updates: &[(B256, Nibbles, LeafUpdate)],
1782 ) -> Option<ArenaRequiredProof> {
1783 let num_removals = subtrie_updates
1784 .iter()
1785 .filter(|(_, _, u)| matches!(u, LeafUpdate::Changed(v) if v.is_empty()))
1786 .count() as u64;
1787
1788 let num_changed =
1796 subtrie_updates.iter().filter(|(_, _, u)| matches!(u, LeafUpdate::Changed(_))).count()
1797 as u64;
1798
1799 if num_removals == 0 || num_removals != num_changed {
1800 return None;
1801 }
1802
1803 let subtrie_entry = cursor.head()?;
1805 let subtrie_num_leaves = match &arena[subtrie_entry.index] {
1806 ArenaSparseNode::Subtrie(s) => s.num_leaves,
1807 _ => return None,
1808 };
1809 if num_removals < subtrie_num_leaves {
1810 return None;
1811 }
1812
1813 let child_nibble =
1814 subtrie_entry.path.last().expect("subtrie path must have at least one nibble");
1815
1816 let parent_entry = cursor.parent()?;
1817 let parent_branch = arena[parent_entry.index].branch_ref();
1818 if parent_branch.state_mask.count_bits() != 2 {
1819 return None;
1820 }
1821
1822 if !parent_branch.sibling_child(child_nibble).is_blinded() {
1823 return None;
1824 }
1825
1826 let sibling_nibble = parent_branch
1827 .state_mask
1828 .iter()
1829 .find(|&n| n != child_nibble)
1830 .expect("branch has two children");
1831 let mut sibling_path = cursor.parent_logical_branch_path(arena);
1832 sibling_path.push_unchecked(sibling_nibble);
1833
1834 Some(ArenaRequiredProof {
1835 key: Self::nibbles_to_padded_b256(&sibling_path),
1836 min_len: (sibling_path.len() as u8).min(64),
1837 })
1838 }
1839
1840 fn collapse_branch(
1851 arena: &mut NodeArena,
1852 cursor: &mut ArenaCursor,
1853 root: &mut Index,
1854 updates: &mut Option<SparseTrieUpdates>,
1855 ) -> bool {
1856 let branch_entry = cursor.head().expect("cursor is non-empty");
1857 let branch_idx = branch_entry.index;
1858 let branch = arena[branch_idx].branch_ref();
1859 let remaining_nibble =
1860 branch.state_mask.iter().next().expect("branch has at least one child");
1861 let branch_short_key = branch.short_key;
1862
1863 debug_assert_eq!(
1864 branch.state_mask.count_bits(),
1865 1,
1866 "collapse_branch requires exactly 1 child"
1867 );
1868 debug_assert!(
1869 !branch.children[0].is_blinded(),
1870 "collapse_branch called with a blinded remaining child"
1871 );
1872
1873 trace!(
1874 target: TRACE_TARGET,
1875 path = ?branch_entry.path,
1876 short_key = ?branch_short_key,
1877 branch_masks = ?branch.branch_masks,
1878 ?remaining_nibble,
1879 "Collapsing single-child branch",
1880 );
1881
1882 if let Some(trie_updates) = updates.as_mut() &&
1885 !branch.branch_masks.is_empty()
1886 {
1887 let logical_path = cursor.head_logical_branch_path(arena);
1888 if !logical_path.is_empty() {
1889 trie_updates.updated_nodes.remove(&logical_path);
1890 trie_updates.removed_nodes.insert(logical_path);
1891 }
1892 }
1893
1894 let mut prefix = branch_short_key;
1896 prefix.push_unchecked(remaining_nibble);
1897
1898 let ArenaSparseNodeBranchChild::Revealed(child_idx) = branch.children[0] else {
1899 unreachable!()
1900 };
1901
1902 let newly_dirtied_leaf = match &mut arena[child_idx] {
1905 ArenaSparseNode::Leaf { key, state, .. } => {
1906 let mut new_key = prefix;
1907 new_key.extend(key);
1908 *key = new_key;
1909 let was_clean = !matches!(state, ArenaSparseNodeState::Dirty);
1910 *state = ArenaSparseNodeState::Dirty;
1911 was_clean
1912 }
1913 ArenaSparseNode::Branch(b) => {
1914 let mut new_short_key = prefix;
1915 new_short_key.extend(&b.short_key);
1916 b.short_key = new_short_key;
1917 b.state = b.state.to_dirty();
1918 false
1919 }
1920 ArenaSparseNode::Subtrie(subtrie) => {
1921 subtrie.path = branch_entry.path;
1922 match &mut subtrie.arena[subtrie.root] {
1923 ArenaSparseNode::Branch(b) => {
1924 let mut new_short_key = prefix;
1925 new_short_key.extend(&b.short_key);
1926 b.short_key = new_short_key;
1927 b.state = b.state.to_dirty();
1928 }
1929 ArenaSparseNode::Leaf { key, state, .. } => {
1930 let mut new_key = prefix;
1931 new_key.extend(key);
1932 *key = new_key;
1933 let was_clean = !matches!(state, ArenaSparseNodeState::Dirty);
1934 *state = ArenaSparseNodeState::Dirty;
1935 if was_clean {
1936 subtrie.num_dirty_leaves += 1;
1937 }
1938 }
1939 _ => {
1940 unreachable!("subtrie root must be a Branch or Leaf during collapse_branch")
1941 }
1942 }
1943 false
1944 }
1945 _ => unreachable!("remaining child must be Leaf, Branch, or Subtrie"),
1946 };
1947
1948 cursor.replace_head_index(arena, root, child_idx);
1950
1951 arena.remove(branch_idx);
1953 newly_dirtied_leaf
1954 }
1955
1956 fn count_leaves_and_dirty(arena: &NodeArena, idx: Index) -> (u64, u64) {
1958 match &arena[idx] {
1959 ArenaSparseNode::Leaf { state, .. } => {
1960 let dirty = matches!(state, ArenaSparseNodeState::Dirty) as u64;
1961 (1, dirty)
1962 }
1963 ArenaSparseNode::Branch(b) => {
1964 let mut leaves = 0u64;
1965 let mut dirty = 0u64;
1966 for c in &b.children {
1967 if let ArenaSparseNodeBranchChild::Revealed(child_idx) = c {
1968 let (l, d) = Self::count_leaves_and_dirty(arena, *child_idx);
1969 leaves += l;
1970 dirty += d;
1971 }
1972 }
1973 (leaves, dirty)
1974 }
1975 _ => (0, 0),
1976 }
1977 }
1978
1979 #[instrument(level = "trace", target = TRACE_TARGET, skip_all)]
1985 #[cfg(debug_assertions)]
1986 fn debug_assert_subtrie_structure(&mut self) {
1987 let mut cursor = mem::take(&mut self.buffers.cursor);
1988 cursor.reset(&self.upper_arena, self.root, Nibbles::default());
1989
1990 loop {
1991 let result = cursor.next(&mut self.upper_arena, |_, _| true);
1992 match result {
1993 NextResult::Done => break,
1994 NextResult::NonBranch | NextResult::Branch => {
1995 let head = cursor.head().expect("cursor is non-empty");
1996 let path_len = head.path.len();
1997 let node = &self.upper_arena[head.index];
1998
1999 if Self::should_be_subtrie(path_len) {
2000 debug_assert!(
2001 matches!(
2002 node,
2003 ArenaSparseNode::Subtrie(_) | ArenaSparseNode::TakenSubtrie
2004 ),
2005 "node at path_len={path_len} should be a Subtrie but is {node:?}",
2006 );
2007 } else {
2008 debug_assert!(
2009 !matches!(node, ArenaSparseNode::Subtrie(_)),
2010 "node at path_len={path_len} should NOT be a Subtrie but is",
2011 );
2012 }
2013 }
2014 }
2015 }
2016
2017 self.buffers.cursor = cursor;
2018 }
2019
2020 fn migrate_nodes(
2028 dst: &mut NodeArena,
2029 src: &mut NodeArena,
2030 src_idx: Index,
2031 dst_slot: Option<Index>,
2032 ) -> Index {
2033 let mut node = src.remove(src_idx).expect("node exists in source arena");
2034
2035 if let ArenaSparseNode::Branch(b) = &mut node {
2037 for child in &mut b.children {
2038 if let ArenaSparseNodeBranchChild::Revealed(child_idx) = child {
2039 *child_idx = Self::migrate_nodes(dst, src, *child_idx, None);
2040 }
2041 }
2042 }
2043
2044 if let Some(slot) = dst_slot {
2045 dst[slot] = node;
2046 slot
2047 } else {
2048 dst.insert(node)
2049 }
2050 }
2051
2052 fn remove_pruned_node(
2056 arena: &mut NodeArena,
2057 cursor: &ArenaCursor,
2058 idx: Index,
2059 nibble: Option<u8>,
2060 ) -> ArenaSparseNode {
2061 trace!(target: TRACE_TARGET, path = ?cursor.head().unwrap().path, "pruning node");
2062 let node = arena.remove(idx).expect("node must exist to be pruned");
2063 let rlp_node = node.state_ref().and_then(|s| s.cached_rlp_node()).cloned();
2064
2065 if let Some(rlp_node) = rlp_node {
2066 let parent_idx = cursor.parent().expect("pruned child has parent").index;
2067 let child_nibble = nibble.expect("non-root child");
2068 let parent_branch = arena[parent_idx].branch_mut();
2069 let child_idx = BranchChildIdx::new(parent_branch.state_mask, child_nibble)
2070 .expect("child nibble not found in parent state_mask");
2071 parent_branch.children[child_idx] = ArenaSparseNodeBranchChild::Blinded(rlp_node);
2072 }
2073
2074 node
2075 }
2076
2077 #[instrument(level = "trace", target = TRACE_TARGET, skip_all)]
2086 fn reveal_node(
2087 arena: &mut NodeArena,
2088 cursor: &ArenaCursor,
2089 node: &mut ProofTrieNodeV2,
2090 find_result: SeekResult,
2091 ) -> Option<Index> {
2092 let SeekResult::Blinded = find_result else {
2093 return None;
2095 };
2096
2097 let head = cursor.head().expect("cursor is non-empty");
2098 let head_idx = head.index;
2099 let head_branch_logical_path = cursor.head_logical_branch_path(arena);
2100
2101 debug_assert_eq!(
2102 node.path.len(),
2103 head_branch_logical_path.len() + 1,
2104 "proof node path {:?} is not a direct child of branch at {:?} (expected depth {})",
2105 node.path,
2106 head_branch_logical_path,
2107 head_branch_logical_path.len() + 1,
2108 );
2109
2110 let child_nibble = node.path.get_unchecked(head_branch_logical_path.len());
2111 let head_branch = arena[head_idx].branch_ref();
2112 let dense_child_idx = BranchChildIdx::new(head_branch.state_mask, child_nibble)
2113 .expect("Blinded result but child nibble not in state_mask");
2114
2115 let cached_rlp = match &head_branch.children[dense_child_idx] {
2116 ArenaSparseNodeBranchChild::Blinded(rlp) => rlp.clone(),
2117 ArenaSparseNodeBranchChild::Revealed(_) => return None,
2118 };
2119
2120 trace!(
2121 target: TRACE_TARGET,
2122 path = ?node.path,
2123 rlp_node = ?cached_rlp,
2124 "Revealing node",
2125 );
2126
2127 let proof_node = mem::replace(node, ProofTrieNodeV2::empty());
2128 let mut arena_node = ArenaSparseNode::from_proof_node(proof_node);
2129
2130 let state = arena_node.state_mut();
2131 *state = ArenaSparseNodeState::Cached { rlp_node: cached_rlp };
2132
2133 let child_idx = arena.insert(arena_node);
2134 arena[head_idx].branch_mut().children[dense_child_idx] =
2135 ArenaSparseNodeBranchChild::Revealed(child_idx);
2136
2137 Some(child_idx)
2138 }
2139
2140 #[cfg(debug_assertions)]
2141 fn collect_reachable_nodes(arena: &NodeArena, idx: Index, reachable: &mut HashSet<Index>) {
2142 if !reachable.insert(idx) {
2143 return;
2144 }
2145 if let ArenaSparseNode::Branch(b) = &arena[idx] {
2146 for child in &b.children {
2147 if let ArenaSparseNodeBranchChild::Revealed(child_idx) = child {
2148 Self::collect_reachable_nodes(arena, *child_idx, reachable);
2149 }
2150 }
2151 }
2152 }
2153
2154 #[cfg(debug_assertions)]
2155 fn assert_no_orphaned_nodes(arena: &NodeArena, root: Index, label: &str) {
2156 let mut reachable = HashSet::default();
2157 Self::collect_reachable_nodes(arena, root, &mut reachable);
2158 let all_indices: HashSet<Index> = arena.iter().map(|(idx, _)| idx).collect();
2159 let orphaned: Vec<_> = all_indices.difference(&reachable).collect();
2160 debug_assert!(
2161 orphaned.is_empty(),
2162 "{label} has {} orphaned node(s): {orphaned:?}",
2163 orphaned.len(),
2164 );
2165 }
2166}
2167
2168#[cfg(debug_assertions)]
2169impl Drop for ArenaParallelSparseTrie {
2170 fn drop(&mut self) {
2171 Self::assert_no_orphaned_nodes(&self.upper_arena, self.root, "upper arena");
2172
2173 for (_, node) in &self.upper_arena {
2174 if let Some(subtrie) = node.as_subtrie() {
2175 Self::assert_no_orphaned_nodes(
2176 &subtrie.arena,
2177 subtrie.root,
2178 &alloc::format!("subtrie {:?}", subtrie.path),
2179 );
2180 }
2181 }
2182 }
2183}
2184
2185impl Default for ArenaParallelSparseTrie {
2186 fn default() -> Self {
2187 let mut upper_arena = SlotMap::new();
2188 let root = upper_arena.insert(ArenaSparseNode::EmptyRoot);
2189 Self {
2190 upper_arena,
2191 root,
2192 updates: None,
2193 buffers: ArenaTrieBuffers::default(),
2194 parallelism_thresholds: ArenaParallelismThresholds::default(),
2195 #[cfg(feature = "trie-debug")]
2196 debug_recorder: Default::default(),
2197 }
2198 }
2199}
2200
2201impl ArenaParallelSparseTrie {
2202 fn update_upper_subtrie(&mut self, head_idx: Index) {
2204 let ArenaSparseNode::Subtrie(subtrie) = &mut self.upper_arena[head_idx] else {
2205 unreachable!()
2206 };
2207
2208 if !subtrie.arena[subtrie.root].is_cached() {
2209 subtrie.update_cached_rlp();
2210 }
2211
2212 Self::merge_subtrie_updates(&mut self.buffers.updates, &mut subtrie.buffers.updates);
2213 }
2214}
2215
2216impl SparseTrie for ArenaParallelSparseTrie {
2217 #[instrument(level = "trace", target = TRACE_TARGET, skip_all)]
2218 fn set_root(
2219 &mut self,
2220 root: TrieNodeV2,
2221 masks: Option<BranchNodeMasks>,
2222 retain_updates: bool,
2223 ) -> SparseTrieResult<()> {
2224 #[cfg(feature = "trie-debug")]
2225 self.debug_recorder.record(RecordedOp::SetRoot {
2226 node: ProofTrieNodeRecord::from_proof_trie_node_v2(&ProofTrieNodeV2 {
2227 path: Nibbles::default(),
2228 node: root.clone(),
2229 masks,
2230 }),
2231 });
2232
2233 debug_assert!(
2234 matches!(self.upper_arena[self.root], ArenaSparseNode::EmptyRoot),
2235 "set_root called on a trie that already has revealed nodes"
2236 );
2237
2238 self.set_updates(retain_updates);
2239
2240 match root {
2241 TrieNodeV2::EmptyRoot => {
2242 trace!(target: TRACE_TARGET, "Setting empty root");
2243 }
2244 TrieNodeV2::Leaf(leaf) => {
2245 trace!(target: TRACE_TARGET, key = ?leaf.key, "Setting leaf root");
2246 self.upper_arena[self.root] = ArenaSparseNode::Leaf {
2247 state: ArenaSparseNodeState::Revealed,
2248 key: leaf.key,
2249 value: leaf.value,
2250 };
2251 }
2252 TrieNodeV2::Branch(branch) => {
2253 trace!(target: TRACE_TARGET, state_mask = ?branch.state_mask, num_children = branch.state_mask.count_bits(), "Setting branch root");
2254 let mut children = SmallVec::with_capacity(branch.state_mask.count_bits() as usize);
2255 for (stack_ptr, _nibble) in branch.state_mask.iter().enumerate() {
2256 children
2257 .push(ArenaSparseNodeBranchChild::Blinded(branch.stack[stack_ptr].clone()));
2258 }
2259
2260 self.upper_arena[self.root] = ArenaSparseNode::Branch(ArenaSparseNodeBranch {
2261 state: ArenaSparseNodeState::Revealed,
2262 children,
2263 state_mask: branch.state_mask,
2264 short_key: branch.key,
2265 branch_masks: masks.unwrap_or_default(),
2266 });
2267 }
2268 TrieNodeV2::Extension(_) => {
2269 panic!("set_root does not support Extension nodes; extensions are represented as branches with a short_key")
2270 }
2271 }
2272
2273 Ok(())
2274 }
2275
2276 fn set_updates(&mut self, retain_updates: bool) {
2277 self.updates = retain_updates.then(Default::default);
2278 if retain_updates {
2279 self.buffers.updates.get_or_insert_with(SparseTrieUpdates::default).clear();
2280 } else {
2281 self.buffers.updates = None;
2282 }
2283 }
2284
2285 fn reserve_nodes(&mut self, additional: usize) {
2286 self.upper_arena.reserve(additional);
2287 }
2288
2289 #[instrument(level = "trace", target = TRACE_TARGET, skip_all, fields(num_nodes = nodes.len()))]
2290 fn reveal_nodes(&mut self, nodes: &mut [ProofTrieNodeV2]) -> SparseTrieResult<()> {
2291 if nodes.is_empty() {
2292 return Ok(());
2293 }
2294
2295 #[cfg(feature = "trie-debug")]
2296 self.debug_recorder.record(RecordedOp::RevealNodes {
2297 nodes: nodes.iter().map(ProofTrieNodeRecord::from_proof_trie_node_v2).collect(),
2298 });
2299
2300 if matches!(self.upper_arena[self.root], ArenaSparseNode::EmptyRoot) {
2301 trace!(target: TRACE_TARGET, "Skipping reveal_nodes on empty root");
2302 return Ok(());
2303 }
2304
2305 nodes.sort_unstable_by_key(|n| n.path);
2307
2308 let threshold = self.parallelism_thresholds.min_revealed_nodes;
2309
2310 let mut cursor = mem::take(&mut self.buffers.cursor);
2312 cursor.reset(&self.upper_arena, self.root, Nibbles::default());
2313
2314 let mut node_idx = if nodes[0].path.is_empty() { 1 } else { 0 };
2316
2317 let mut taken: Vec<(Index, Box<ArenaSparseSubtrie>, Vec<ProofTrieNodeV2>)> = Vec::new();
2321
2322 while node_idx < nodes.len() {
2323 let find_result = cursor.seek(&mut self.upper_arena, &nodes[node_idx].path);
2324
2325 match find_result {
2326 SeekResult::RevealedLeaf => {
2327 trace!(target: TRACE_TARGET, path = ?nodes[node_idx].path, "Skipping reveal: leaf head");
2328 node_idx += 1;
2329 }
2330 SeekResult::Blinded => {
2331 let child_path = nodes[node_idx].path;
2333 let child_idx = Self::reveal_node(
2334 &mut self.upper_arena,
2335 &cursor,
2336 &mut nodes[node_idx],
2337 SeekResult::Blinded,
2338 );
2339 node_idx += 1;
2340
2341 if let Some(child_idx) = child_idx {
2342 self.maybe_wrap_in_subtrie(child_idx, &child_path);
2343 }
2344 }
2345 SeekResult::RevealedSubtrie => {
2346 let subtrie_entry = cursor.head().expect("cursor is non-empty");
2347 let child_idx = subtrie_entry.index;
2348 let prefix = subtrie_entry.path;
2349
2350 let subtrie_start = node_idx;
2351 while node_idx < nodes.len() && nodes[node_idx].path.starts_with(&prefix) {
2352 node_idx += 1;
2353 }
2354 let num_subtrie_nodes = node_idx - subtrie_start;
2355
2356 if num_subtrie_nodes >= threshold {
2357 trace!(target: TRACE_TARGET, ?prefix, num_subtrie_nodes, "Taking subtrie for parallel reveal");
2359 let ArenaSparseNode::Subtrie(subtrie) = mem::replace(
2360 &mut self.upper_arena[child_idx],
2361 ArenaSparseNode::TakenSubtrie,
2362 ) else {
2363 unreachable!("RevealedSubtrie must point to a Subtrie node")
2364 };
2365 let node_vec: Vec<ProofTrieNodeV2> = (subtrie_start..node_idx)
2366 .map(|i| mem::replace(&mut nodes[i], ProofTrieNodeV2::empty()))
2367 .collect();
2368 taken.push((child_idx, subtrie, node_vec));
2369 } else {
2370 trace!(target: TRACE_TARGET, ?prefix, num_subtrie_nodes, "Revealing subtrie inline");
2372 let ArenaSparseNode::Subtrie(subtrie) = &mut self.upper_arena[child_idx]
2373 else {
2374 unreachable!("RevealedSubtrie must point to a Subtrie node")
2375 };
2376 let mut subtrie_nodes: Vec<ProofTrieNodeV2> = (subtrie_start..node_idx)
2377 .map(|i| mem::replace(&mut nodes[i], ProofTrieNodeV2::empty()))
2378 .collect();
2379 subtrie.reveal_nodes(&mut subtrie_nodes)?;
2380 }
2381 }
2382 _ => {
2383 trace!(target: TRACE_TARGET, path = ?nodes[node_idx].path, ?find_result, "Skipping reveal: no blinded child");
2384 node_idx += 1;
2385 }
2386 }
2387 }
2388
2389 cursor.drain(&mut self.upper_arena);
2391 self.buffers.cursor = cursor;
2392
2393 if taken.is_empty() {
2394 return Ok(());
2395 }
2396
2397 if taken.len() == 1 {
2399 let (_, subtrie, node_vec) = &mut taken[0];
2400 subtrie.reveal_nodes(node_vec)?;
2401 } else {
2402 use rayon::iter::{IntoParallelRefMutIterator, ParallelIterator};
2403
2404 let results: Vec<SparseTrieResult<()>> = taken
2405 .par_iter_mut()
2406 .map(|(_, subtrie, node_vec)| subtrie.reveal_nodes(node_vec))
2407 .collect();
2408
2409 if let Some(err) = results.into_iter().find(|r| r.is_err()) {
2410 for (idx, subtrie, _) in taken {
2412 self.upper_arena[idx] = ArenaSparseNode::Subtrie(subtrie);
2413 }
2414 return err;
2415 }
2416 }
2417
2418 for (idx, subtrie, _) in taken {
2420 self.upper_arena[idx] = ArenaSparseNode::Subtrie(subtrie);
2421 }
2422
2423 #[cfg(debug_assertions)]
2424 self.debug_assert_subtrie_structure();
2425
2426 Ok(())
2427 }
2428
2429 #[instrument(level = "trace", target = TRACE_TARGET, skip_all, ret)]
2430 fn root(&mut self) -> B256 {
2431 #[cfg(feature = "trie-debug")]
2432 self.debug_recorder.record(RecordedOp::Root);
2433
2434 self.update_subtrie_hashes();
2435
2436 Self::merge_subtrie_updates(&mut self.updates, &mut self.buffers.updates);
2439
2440 let rlp_node = Self::update_cached_rlp(
2441 &mut self.upper_arena,
2442 self.root,
2443 &mut self.buffers.cursor,
2444 &mut self.buffers.rlp_buf,
2445 &mut self.buffers.rlp_node_buf,
2446 Nibbles::default(),
2447 &mut self.updates,
2448 );
2449
2450 rlp_node.as_hash().expect("root RlpNode must be a hash")
2451 }
2452
2453 fn is_root_cached(&self) -> bool {
2454 self.upper_arena[self.root].is_cached()
2455 }
2456
2457 #[instrument(level = "trace", target = TRACE_TARGET, skip_all)]
2458 fn update_subtrie_hashes(&mut self) {
2459 #[cfg(feature = "trie-debug")]
2460 self.debug_recorder.record(RecordedOp::UpdateSubtrieHashes);
2461
2462 trace!(target: TRACE_TARGET, "Updating subtrie hashes");
2463
2464 if !matches!(&self.upper_arena[self.root], ArenaSparseNode::Branch(_)) {
2466 return;
2467 }
2468
2469 let mut total_dirty_leaves: u64 = 0;
2472 let mut taken: Vec<(Index, Box<ArenaSparseSubtrie>)> = Vec::new();
2473 for (idx, node) in &mut self.upper_arena {
2474 let ArenaSparseNode::Subtrie(s) = node else { continue };
2475 if s.num_dirty_leaves == 0 {
2476 continue;
2477 }
2478 total_dirty_leaves += s.num_dirty_leaves;
2479 let ArenaSparseNode::Subtrie(subtrie) =
2480 mem::replace(node, ArenaSparseNode::TakenSubtrie)
2481 else {
2482 unreachable!()
2483 };
2484 taken.push((idx, subtrie));
2485 }
2486
2487 if !taken.is_empty() {
2490 if taken.len() == 1 || total_dirty_leaves < self.parallelism_thresholds.min_dirty_leaves
2491 {
2492 for (_, subtrie) in &mut taken {
2493 subtrie.update_cached_rlp();
2494 }
2495 } else {
2496 use rayon::iter::{IntoParallelIterator, ParallelIterator};
2497
2498 taken = taken
2499 .into_par_iter()
2500 .map(|(idx, mut subtrie)| {
2501 subtrie.update_cached_rlp();
2502 (idx, subtrie)
2503 })
2504 .collect();
2505 }
2506 }
2507
2508 if taken.is_empty() && self.upper_arena[self.root].is_cached() {
2511 return;
2512 }
2513
2514 taken.sort_unstable_by_key(|(_, b)| Reverse(b.path));
2518
2519 self.buffers.cursor.reset(&self.upper_arena, self.root, Nibbles::default());
2520
2521 loop {
2522 let result = self.buffers.cursor.next(&mut self.upper_arena, |_, child| match child {
2523 ArenaSparseNode::Branch(_) | ArenaSparseNode::Subtrie(_) => !child.is_cached(),
2524 ArenaSparseNode::TakenSubtrie => true,
2525 _ => false,
2526 });
2527
2528 match result {
2529 NextResult::Done => break,
2530 NextResult::Branch => continue,
2531 NextResult::NonBranch => {}
2532 }
2533
2534 let head_idx = self.buffers.cursor.head().expect("cursor is non-empty").index;
2536
2537 if matches!(&self.upper_arena[head_idx], ArenaSparseNode::TakenSubtrie) {
2538 let (_, subtrie) = taken.pop().expect("taken subtries must not be exhausted");
2539 debug_assert_eq!(
2540 subtrie.path,
2541 self.buffers.cursor.head().expect("cursor is non-empty").path,
2542 "taken subtrie path mismatch",
2543 );
2544 self.upper_arena[head_idx] = ArenaSparseNode::Subtrie(subtrie);
2545 }
2546
2547 self.update_upper_subtrie(head_idx);
2548 }
2549 }
2550
2551 fn get_leaf_value(&self, full_path: &Nibbles) -> Option<&Vec<u8>> {
2552 Self::get_leaf_value_in_arena(&self.upper_arena, self.root, full_path, 0)
2553 }
2554
2555 fn find_leaf(
2556 &self,
2557 full_path: &Nibbles,
2558 expected_value: Option<&Vec<u8>>,
2559 ) -> Result<LeafLookup, LeafLookupError> {
2560 Self::find_leaf_in_arena(&self.upper_arena, self.root, full_path, 0, expected_value)
2561 }
2562
2563 fn updates_ref(&self) -> Cow<'_, SparseTrieUpdates> {
2564 self.updates.as_ref().map_or(Cow::Owned(SparseTrieUpdates::default()), Cow::Borrowed)
2565 }
2566
2567 fn take_updates(&mut self) -> SparseTrieUpdates {
2568 match self.updates.take() {
2569 Some(updates) => {
2570 self.updates = Some(SparseTrieUpdates::with_capacity(
2571 updates.updated_nodes.len(),
2572 updates.removed_nodes.len(),
2573 ));
2574 updates
2575 }
2576 None => SparseTrieUpdates::default(),
2577 }
2578 }
2579
2580 #[instrument(level = "trace", target = TRACE_TARGET, skip_all)]
2581 fn wipe(&mut self) {
2582 trace!(target: TRACE_TARGET, "Wiping arena trie");
2583 self.clear();
2584 self.updates = self.updates.is_some().then(SparseTrieUpdates::wiped);
2585 }
2586
2587 #[instrument(level = "trace", target = TRACE_TARGET, skip_all)]
2588 fn clear(&mut self) {
2589 #[cfg(feature = "trie-debug")]
2590 self.debug_recorder.reset();
2591
2592 self.upper_arena = SlotMap::new();
2593 self.root = self.upper_arena.insert(ArenaSparseNode::EmptyRoot);
2594 if let Some(updates) = self.updates.as_mut() {
2595 updates.clear()
2596 }
2597 self.buffers.clear();
2598 }
2599
2600 fn shrink_nodes_to(&mut self, _size: usize) {
2601 }
2603
2604 fn shrink_values_to(&mut self, _size: usize) {
2605 }
2607
2608 fn size_hint(&self) -> usize {
2609 self.upper_arena
2610 .iter()
2611 .map(|(_, node)| match node {
2612 ArenaSparseNode::Subtrie(s) => s.num_leaves as usize,
2613 ArenaSparseNode::Leaf { .. } => 1,
2614 _ => 0,
2615 })
2616 .sum()
2617 }
2618
2619 fn memory_size(&self) -> usize {
2620 let slot_size = slotmap_slot_size::<ArenaSparseNode>();
2621
2622 let upper = self.upper_arena.capacity() * slot_size;
2624
2625 let subtrie_size: usize = self
2627 .upper_arena
2628 .iter()
2629 .filter_map(|(_, node)| match node {
2630 ArenaSparseNode::Subtrie(s) => Some(s.memory_size()),
2631 _ => None,
2632 })
2633 .sum();
2634
2635 let buffer_size = self.buffers.rlp_buf.capacity() +
2637 self.buffers.rlp_node_buf.capacity() * core::mem::size_of::<RlpNode>();
2638
2639 upper + subtrie_size + buffer_size
2640 }
2641
2642 #[instrument(
2643 level = "trace",
2644 target = TRACE_TARGET,
2645 skip_all,
2646 fields(num_retained_leaves = retained_leaves.len()),
2647 )]
2648 fn prune(&mut self, retained_leaves: &[Nibbles]) -> usize {
2649 if !matches!(&self.upper_arena[self.root], ArenaSparseNode::Branch(_)) {
2651 return 0;
2652 }
2653
2654 let mut retained_leaves = retained_leaves.to_vec();
2655 retained_leaves.sort_unstable();
2656
2657 let threshold = self.parallelism_thresholds.min_leaves_for_prune;
2658
2659 let mut cursor = mem::take(&mut self.buffers.cursor);
2660 cursor.reset(&self.upper_arena, self.root, Nibbles::default());
2661
2662 let mut taken: Vec<(Index, Box<ArenaSparseSubtrie>, core::ops::Range<usize>)> = Vec::new();
2664
2665 let mut pruned = 0;
2666 let mut retained_idx = 0;
2667
2668 loop {
2669 let result = cursor.next(&mut self.upper_arena, |_, child| {
2670 matches!(
2671 child,
2672 ArenaSparseNode::Branch(_) |
2673 ArenaSparseNode::Subtrie(_) |
2674 ArenaSparseNode::Leaf { .. }
2675 )
2676 });
2677
2678 match result {
2679 NextResult::Done => break,
2680 NextResult::NonBranch | NextResult::Branch => {}
2681 }
2682
2683 let head = cursor.head().expect("cursor is non-empty");
2684 let head_idx = head.index;
2685 let head_path = head.path;
2686
2687 match &self.upper_arena[head_idx] {
2688 ArenaSparseNode::Branch(_) | ArenaSparseNode::Leaf { .. } => {
2689 if cursor.depth() == 0 {
2691 continue;
2692 }
2693
2694 let short_key =
2695 self.upper_arena[head_idx].short_key().expect("must be branch or leaf");
2696 let mut node_prefix = head_path;
2697 node_prefix.extend(short_key);
2698
2699 let range = prefix_range(&retained_leaves, 0, &node_prefix);
2700 if !range.is_empty() {
2701 continue;
2702 }
2703
2704 Self::remove_pruned_node(
2705 &mut self.upper_arena,
2706 &cursor,
2707 head_idx,
2708 head_path.last(),
2709 );
2710 pruned += 1;
2711 }
2712 ArenaSparseNode::Subtrie(_) => {
2713 let subtrie_range = prefix_range(&retained_leaves, retained_idx, &head_path);
2714 retained_idx = subtrie_range.end;
2715
2716 if subtrie_range.is_empty() {
2717 let removed = Self::remove_pruned_node(
2718 &mut self.upper_arena,
2719 &cursor,
2720 head_idx,
2721 head_path.last(),
2722 );
2723 let ArenaSparseNode::Subtrie(s) = &removed else { unreachable!() };
2724 pruned += s.num_leaves as usize;
2725 self.recycle_subtrie(removed);
2726 continue;
2727 }
2728
2729 let ArenaSparseNode::Subtrie(subtrie) = &self.upper_arena[head_idx] else {
2730 unreachable!()
2731 };
2732 if subtrie.num_leaves >= threshold {
2733 let ArenaSparseNode::Subtrie(subtrie) = mem::replace(
2734 &mut self.upper_arena[head_idx],
2735 ArenaSparseNode::TakenSubtrie,
2736 ) else {
2737 unreachable!()
2738 };
2739 taken.push((head_idx, subtrie, subtrie_range));
2740 } else {
2741 let ArenaSparseNode::Subtrie(subtrie) = &mut self.upper_arena[head_idx]
2742 else {
2743 unreachable!()
2744 };
2745 pruned += subtrie.prune(&retained_leaves[subtrie_range]);
2746 }
2747 }
2748 _ => unreachable!("NonBranch in prune walk must be Subtrie, Leaf, or Branch"),
2749 }
2750 }
2751
2752 self.buffers.cursor = cursor;
2753
2754 if !taken.is_empty() {
2755 if taken.len() == 1 {
2757 let (_, ref mut subtrie, ref range) = taken[0];
2758 pruned += subtrie.prune(&retained_leaves[range.clone()]);
2759 } else {
2760 use rayon::iter::{IntoParallelRefMutIterator, ParallelIterator};
2761
2762 pruned += taken
2763 .par_iter_mut()
2764 .map(|(_, subtrie, range)| subtrie.prune(&retained_leaves[range.clone()]))
2765 .sum::<usize>();
2766 }
2767
2768 for (child_idx, subtrie, _) in taken {
2770 self.upper_arena[child_idx] = ArenaSparseNode::Subtrie(subtrie);
2771 }
2772 }
2773
2774 if pruned > 0 {
2775 compact_arena(&mut self.upper_arena, &mut self.root);
2776 }
2777
2778 #[cfg(feature = "trie-debug")]
2779 self.record_initial_state();
2780
2781 pruned
2782 }
2783
2784 #[instrument(
2785 level = "trace",
2786 target = TRACE_TARGET,
2787 skip_all,
2788 fields(num_updates = updates.len()),
2789 )]
2790 fn update_leaves(
2791 &mut self,
2792 updates: &mut B256Map<LeafUpdate>,
2793 mut proof_required_fn: impl FnMut(B256, u8),
2794 ) -> SparseTrieResult<()> {
2795 if updates.is_empty() {
2796 return Ok(());
2797 }
2798
2799 #[cfg(feature = "trie-debug")]
2800 let recorded_updates: Vec<_> =
2801 updates.iter().map(|(k, v)| (*k, LeafUpdateRecord::from(v))).collect();
2802 #[cfg(feature = "trie-debug")]
2803 let mut recorded_proof_targets: Vec<(B256, u8)> = Vec::new();
2804
2805 let mut sorted: Vec<_> =
2807 updates.drain().map(|(key, update)| (key, Nibbles::unpack(key), update)).collect();
2808 sorted.sort_unstable_by_key(|entry| entry.1);
2809
2810 let threshold = self.parallelism_thresholds.min_updates;
2811
2812 let mut cursor = mem::take(&mut self.buffers.cursor);
2813 cursor.reset(&self.upper_arena, self.root, Nibbles::default());
2814
2815 let mut taken: Vec<(Index, Box<ArenaSparseSubtrie>, core::ops::Range<usize>)> = Vec::new();
2817
2818 let mut update_idx = 0;
2819 while update_idx < sorted.len() {
2820 let (key, ref full_path, ref update) = sorted[update_idx];
2821
2822 let find_result = cursor.seek(&mut self.upper_arena, full_path);
2823
2824 match find_result {
2825 SeekResult::Blinded => {
2827 let logical_len = cursor.head_logical_branch_path_len(&self.upper_arena);
2828 let min_len = (logical_len as u8 + 1).min(64);
2829 trace!(target: TRACE_TARGET, ?key, min_len, "Update hit blinded node, requesting proof");
2830 proof_required_fn(key, min_len);
2831 #[cfg(feature = "trie-debug")]
2832 recorded_proof_targets.push((key, min_len));
2833 updates.insert(key, update.clone());
2834 }
2835 SeekResult::RevealedSubtrie => {
2837 let subtrie_entry = cursor.head().expect("cursor is non-empty");
2838 let child_idx = subtrie_entry.index;
2839 let subtrie_root_path = subtrie_entry.path;
2840
2841 let subtrie_start = update_idx;
2842 while update_idx < sorted.len() &&
2843 sorted[update_idx].1.starts_with(&subtrie_root_path)
2844 {
2845 update_idx += 1;
2846 }
2847
2848 let subtrie_updates = &sorted[subtrie_start..update_idx];
2849
2850 if let Some(proof) = Self::check_subtrie_collapse_needs_proof(
2854 &self.upper_arena,
2855 &cursor,
2856 subtrie_updates,
2857 ) {
2858 trace!(target: TRACE_TARGET, proof_key = ?proof.key, proof_min_len = proof.min_len, "Subtrie collapse would need blinded sibling, requesting proof");
2859 proof_required_fn(proof.key, proof.min_len);
2860 #[cfg(feature = "trie-debug")]
2861 recorded_proof_targets.push((proof.key, proof.min_len));
2862 for &(key, _, ref update) in subtrie_updates {
2863 updates.insert(key, update.clone());
2864 }
2865 continue;
2867 }
2868
2869 let num_subtrie_updates = update_idx - subtrie_start;
2870
2871 let all_removals = subtrie_updates
2875 .iter()
2876 .filter(|(_, _, u)| matches!(u, LeafUpdate::Changed(_)))
2880 .all(|(_, _, u)| matches!(u, LeafUpdate::Changed(v) if v.is_empty()));
2881 let subtrie_num_leaves = match &self.upper_arena[child_idx] {
2882 ArenaSparseNode::Subtrie(s) => s.num_leaves,
2883 _ => 0,
2884 };
2885 let might_empty_subtrie =
2886 all_removals && num_subtrie_updates as u64 >= subtrie_num_leaves;
2887
2888 if num_subtrie_updates >= threshold && !might_empty_subtrie {
2889 trace!(target: TRACE_TARGET, ?subtrie_root_path, num_subtrie_updates, "Taking subtrie for parallel update");
2891 let ArenaSparseNode::Subtrie(subtrie) = mem::replace(
2892 &mut self.upper_arena[child_idx],
2893 ArenaSparseNode::TakenSubtrie,
2894 ) else {
2895 unreachable!()
2896 };
2897 taken.push((child_idx, subtrie, subtrie_start..update_idx));
2898 } else {
2899 trace!(target: TRACE_TARGET, ?subtrie_root_path, num_subtrie_updates, "Updating subtrie inline");
2901 let ArenaSparseNode::Subtrie(subtrie) = &mut self.upper_arena[child_idx]
2902 else {
2903 unreachable!()
2904 };
2905
2906 subtrie.update_leaves(subtrie_updates);
2907
2908 for (target_idx, proof) in subtrie.required_proofs.drain(..) {
2909 proof_required_fn(proof.key, proof.min_len);
2910 #[cfg(feature = "trie-debug")]
2911 recorded_proof_targets.push((proof.key, proof.min_len));
2912 let (key, _, ref update) = subtrie_updates[target_idx];
2913 updates.insert(key, update.clone());
2914 }
2915
2916 self.maybe_unwrap_subtrie(&mut cursor);
2918 }
2919
2920 continue;
2922 }
2923 find_result @ (SeekResult::EmptyRoot |
2925 SeekResult::RevealedLeaf |
2926 SeekResult::Diverged |
2927 SeekResult::NoChild { .. }) => match update {
2928 LeafUpdate::Changed(v) if !v.is_empty() => {
2929 let (result, _deltas) = Self::upsert_leaf(
2930 &mut self.upper_arena,
2931 &mut cursor,
2932 &mut self.root,
2933 full_path,
2934 v,
2935 find_result,
2936 );
2937 match result {
2938 UpsertLeafResult::NewChild => {
2939 let head = cursor.head().expect("cursor is non-empty");
2940 if Self::should_be_subtrie(head.path.len()) {
2941 self.maybe_wrap_in_subtrie(head.index, &head.path);
2944 } else {
2945 self.maybe_wrap_branch_children(&cursor);
2949 }
2950 }
2951 UpsertLeafResult::NewLeaf => {
2952 self.maybe_wrap_branch_children(&cursor);
2955 }
2956 UpsertLeafResult::Updated => {}
2957 }
2958 }
2959 LeafUpdate::Changed(_) => {
2960 let (result, _deltas) = Self::remove_leaf(
2961 &mut self.upper_arena,
2962 &mut cursor,
2963 &mut self.root,
2964 key,
2965 full_path,
2966 find_result,
2967 &mut self.buffers.updates,
2968 );
2969 match result {
2970 RemoveLeafResult::NeedsProof { key, proof_key, min_len } => {
2971 proof_required_fn(proof_key, min_len);
2972 #[cfg(feature = "trie-debug")]
2973 recorded_proof_targets.push((proof_key, min_len));
2974 let update =
2975 mem::replace(&mut sorted[update_idx].2, LeafUpdate::Touched);
2976 updates.insert(key, update);
2977 }
2978 RemoveLeafResult::Removed => {
2979 self.maybe_collapse_or_remove_branch(&mut cursor);
2988 let head =
2989 cursor.head().expect("cursor always has root after collapse");
2990 self.maybe_wrap_in_subtrie(head.index, &head.path);
2991 }
2992 RemoveLeafResult::NotFound => {}
2993 }
2994 }
2995 LeafUpdate::Touched => {}
2996 },
2997 }
2998
2999 update_idx += 1;
3000 }
3001
3002 cursor.drain(&mut self.upper_arena);
3004 self.buffers.cursor = cursor;
3005
3006 if taken.is_empty() {
3007 #[cfg(debug_assertions)]
3008 self.debug_assert_subtrie_structure();
3009
3010 #[cfg(feature = "trie-debug")]
3011 self.debug_recorder.record(RecordedOp::UpdateLeaves {
3012 updates: recorded_updates,
3013 proof_targets: recorded_proof_targets,
3014 });
3015
3016 return Ok(());
3017 }
3018
3019 if taken.len() == 1 {
3021 let (_, ref mut subtrie, ref range) = taken[0];
3022 subtrie.update_leaves(&sorted[range.clone()]);
3023 } else {
3024 use rayon::iter::{IntoParallelRefMutIterator, ParallelIterator};
3025
3026 taken.par_iter_mut().for_each(|(_, subtrie, range)| {
3027 subtrie.update_leaves(&sorted[range.clone()]);
3028 });
3029 }
3030
3031 let taken_paths: Vec<Nibbles> = taken.iter().map(|(_, s, _)| s.path).collect();
3034 for (child_idx, mut subtrie, range) in taken {
3035 let subtrie_updates = &sorted[range];
3036 for (target_idx, proof) in subtrie.required_proofs.drain(..) {
3037 proof_required_fn(proof.key, proof.min_len);
3038 #[cfg(feature = "trie-debug")]
3039 recorded_proof_targets.push((proof.key, proof.min_len));
3040 let (key, _, ref update) = subtrie_updates[target_idx];
3041 updates.insert(key, update.clone());
3042 }
3043
3044 self.upper_arena[child_idx] = ArenaSparseNode::Subtrie(subtrie);
3046 }
3047
3048 {
3054 let mut cursor = mem::take(&mut self.buffers.cursor);
3055 cursor.reset(&self.upper_arena, self.root, Nibbles::default());
3056
3057 for path in &taken_paths {
3058 let find_result = cursor.seek(&mut self.upper_arena, path);
3059 match find_result {
3060 SeekResult::RevealedSubtrie => {
3061 debug_assert!(
3062 {
3063 let head_idx = cursor.head().expect("cursor is non-empty").index;
3064 !matches!(
3065 &self.upper_arena[head_idx],
3066 ArenaSparseNode::Subtrie(s) if matches!(s.arena[s.root], ArenaSparseNode::EmptyRoot)
3067 )
3068 },
3069 "taken subtrie became EmptyRoot — should have been forced inline"
3070 );
3071
3072 cursor.pop(&mut self.upper_arena);
3073
3074 self.maybe_collapse_or_remove_branch(&mut cursor);
3078 }
3079 _ => {
3080 }
3083 }
3084 }
3085
3086 cursor.drain(&mut self.upper_arena);
3087 self.buffers.cursor = cursor;
3088 }
3089
3090 #[cfg(debug_assertions)]
3091 self.debug_assert_subtrie_structure();
3092
3093 #[cfg(feature = "trie-debug")]
3094 self.debug_recorder.record(RecordedOp::UpdateLeaves {
3095 updates: recorded_updates,
3096 proof_targets: recorded_proof_targets,
3097 });
3098
3099 Ok(())
3100 }
3101
3102 #[cfg(feature = "trie-debug")]
3103 fn take_debug_recorder(&mut self) -> TrieDebugRecorder {
3104 core::mem::take(&mut self.debug_recorder)
3105 }
3106
3107 fn commit_updates(
3108 &mut self,
3109 _updated: &HashMap<Nibbles, BranchNodeCompact>,
3110 _removed: &HashSet<Nibbles>,
3111 ) {
3112 }
3114}
3115
3116#[cfg(test)]
3117mod tests {
3118 use super::TRACE_TARGET;
3119 use crate::{ArenaParallelSparseTrie, ArenaParallelismThresholds, LeafUpdate, SparseTrie};
3120 use alloy_primitives::{map::B256Map, B256, U256};
3121 use rand::{seq::SliceRandom, Rng, SeedableRng};
3122 use reth_trie::test_utils::TrieTestHarness;
3123 use reth_trie_common::{Nibbles, ProofV2Target};
3124 use std::collections::BTreeMap;
3125 use tracing::{info, trace};
3126
3127 struct ArenaTrieTestHarness {
3132 inner: TrieTestHarness,
3134 }
3135
3136 impl std::ops::Deref for ArenaTrieTestHarness {
3137 type Target = TrieTestHarness;
3138 fn deref(&self) -> &Self::Target {
3139 &self.inner
3140 }
3141 }
3142
3143 impl std::ops::DerefMut for ArenaTrieTestHarness {
3144 fn deref_mut(&mut self) -> &mut Self::Target {
3145 &mut self.inner
3146 }
3147 }
3148
3149 impl ArenaTrieTestHarness {
3150 fn new(storage: BTreeMap<B256, U256>) -> Self {
3152 Self { inner: TrieTestHarness::new(storage) }
3153 }
3154
3155 fn assert_changes(
3159 &self,
3160 apst: &mut ArenaParallelSparseTrie,
3161 changes: BTreeMap<B256, U256>,
3162 ) {
3163 let (expected_root, mut expected_trie_updates) = if changes.is_empty() {
3165 (self.original_root(), Default::default())
3166 } else {
3167 self.get_root_with_updates(&changes)
3168 };
3169
3170 self.minimize_trie_updates(&mut expected_trie_updates);
3171
3172 let mut leaf_updates: B256Map<LeafUpdate> = changes
3175 .iter()
3176 .map(|(&slot, &value)| {
3177 let rlp_value = if value == U256::ZERO {
3178 Vec::new()
3179 } else {
3180 alloy_rlp::encode_fixed_size(&value).to_vec()
3181 };
3182 (slot, LeafUpdate::Changed(rlp_value))
3183 })
3184 .collect();
3185
3186 loop {
3189 let mut targets: Vec<ProofV2Target> = Vec::new();
3190 apst.update_leaves(&mut leaf_updates, |key, min_len| {
3191 targets.push(ProofV2Target::new(key).with_min_len(min_len));
3192 })
3193 .expect("update_leaves should succeed");
3194
3195 if targets.is_empty() {
3196 break;
3197 }
3198
3199 let (mut proof_nodes, _) = self.proof_v2(&mut targets);
3200 apst.reveal_nodes(&mut proof_nodes).expect("reveal_nodes should succeed");
3201 }
3202
3203 let actual_root = apst.root();
3205 let mut actual_updates = apst.take_updates();
3206
3207 actual_updates.updated_nodes.retain(|path, node| {
3210 self.storage_trie_updates().storage_nodes.get(path) != Some(node)
3211 });
3212 actual_updates
3213 .removed_nodes
3214 .retain(|path| self.storage_trie_updates().storage_nodes.contains_key(path));
3215
3216 pretty_assertions::assert_eq!(
3217 expected_trie_updates.storage_nodes.into_iter().collect::<Vec<_>>().sort(),
3218 actual_updates.updated_nodes.into_iter().collect::<Vec<_>>().sort(),
3219 "updated nodes mismatch"
3220 );
3221 pretty_assertions::assert_eq!(
3222 expected_trie_updates.removed_nodes.into_iter().collect::<Vec<_>>().sort(),
3223 actual_updates.removed_nodes.into_iter().collect::<Vec<_>>().sort(),
3224 "removed nodes mismatch"
3225 );
3226 assert_eq!(expected_root, actual_root, "storage root mismatch");
3227 }
3228 }
3229
3230 use proptest::prelude::*;
3231 use proptest_arbitrary_interop::arb;
3232
3233 fn build_changeset(
3240 base: &BTreeMap<B256, U256>,
3241 new_keys: BTreeMap<B256, U256>,
3242 overlap_pct: f64,
3243 delete_pct: f64,
3244 rng: &mut rand::rngs::StdRng,
3245 ) -> BTreeMap<B256, U256> {
3246 let num_overlap = (base.len() as f64 * overlap_pct) as usize;
3247 let num_delete = (num_overlap as f64 * delete_pct) as usize;
3248
3249 let mut all_keys: Vec<B256> = base.keys().copied().collect();
3250 all_keys.shuffle(rng);
3251 let overlap_keys = &all_keys[..num_overlap];
3252
3253 let mut changeset = new_keys;
3254 for (i, &key) in overlap_keys.iter().enumerate() {
3255 let value =
3256 if i < num_delete { U256::ZERO } else { U256::from(rng.random::<u64>() | 1) };
3257 changeset.entry(key).or_insert(value);
3258 }
3259 changeset
3260 }
3261
3262 proptest! {
3263 #![proptest_config(ProptestConfig::with_cases(1000))]
3264 #[test]
3265 fn arena_trie_proptest(
3266 initial in proptest::collection::btree_map(arb::<B256>(), arb::<U256>(), 0..=100usize),
3267 changeset1_new_keys in proptest::collection::btree_map(arb::<B256>(), arb::<U256>(), 0..=30usize),
3268 changeset2_new_keys in proptest::collection::btree_map(arb::<B256>(), arb::<U256>(), 0..=30usize),
3269 overlap_pct in 0.0..=0.5f64,
3270 delete_pct in 0.0..=0.33f64, shuffle_seed in arb::<u64>(),
3272 ) {
3273 reth_tracing::init_test_tracing();
3274 info!(target: TRACE_TARGET, ?shuffle_seed, "PROPTEST START");
3275
3276 let initial: BTreeMap<B256, U256> = initial.into_iter()
3278 .filter(|(_, v)| *v != U256::ZERO)
3279 .collect();
3280
3281 let mut rng = rand::rngs::StdRng::seed_from_u64(shuffle_seed);
3282
3283 let changeset1 = build_changeset(&initial, changeset1_new_keys, overlap_pct, delete_pct, &mut rng);
3284 for (i, (k, v)) in changeset1.iter().enumerate() {
3285 trace!(target: TRACE_TARGET, ?i, ?k, ?v, "Changeset 1 entry");
3286 }
3287
3288 let mut harness = ArenaTrieTestHarness::new(initial);
3289
3290 let root_node = harness.root_node();
3292 let mut apst = ArenaParallelSparseTrie::default().with_parallelism_thresholds(
3293 ArenaParallelismThresholds {
3294 min_dirty_leaves: 3,
3295 min_revealed_nodes: 3,
3296 min_updates: 3,
3297 min_leaves_for_prune: 3,
3298 },
3299 );
3300 apst.set_root(root_node.node, root_node.masks, true).expect("set_root should succeed");
3301
3302 harness.assert_changes(&mut apst, changeset1.clone());
3303
3304 harness.apply_changeset(changeset1);
3306
3307 let mut all_storage_keys: Vec<Nibbles> = harness.storage().keys()
3309 .map(|k| Nibbles::unpack(*k))
3310 .collect();
3311 all_storage_keys.shuffle(&mut rng);
3312 let num_retain = if all_storage_keys.is_empty() { 0 } else {
3313 rng.random_range(0..=all_storage_keys.len())
3314 };
3315 let retained: Vec<Nibbles> = all_storage_keys[..num_retain].to_vec();
3316 apst.prune(&retained);
3317
3318 let changeset2 = build_changeset(harness.storage(), changeset2_new_keys, overlap_pct, delete_pct, &mut rng);
3319 for (i, (k, v)) in changeset2.iter().enumerate() {
3320 trace!(target: TRACE_TARGET, ?i, ?k, ?v, "Changeset 2 entry");
3321 }
3322
3323 harness.assert_changes(&mut apst, changeset2);
3324 }
3325 }
3326}