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::{keccak256, map::B256Map, B256};
14use alloy_trie::TrieMask;
15use core::{cmp::Reverse, mem};
16use reth_execution_errors::SparseTrieResult;
17use reth_trie_common::{
18 prefix_set::PrefixSetMut, BranchNodeMasks, BranchNodeRef, ExtensionNodeRef, LeafNodeRef,
19 Nibbles, ProofTrieNodeV2, RlpNode, TrieNodeV2, EMPTY_ROOT_HASH,
20};
21use slotmap::{DefaultKey, SlotMap};
22use smallvec::SmallVec;
23use tracing::{instrument, trace};
24
25#[cfg(feature = "trie-debug")]
26use crate::debug_recorder::{LeafUpdateRecord, ProofTrieNodeRecord, RecordedOp, TrieDebugRecorder};
27
28type Index = DefaultKey;
30type NodeArena = SlotMap<Index, ArenaSparseNode>;
32
33const TRACE_TARGET: &str = "trie::arena";
34
35const UPPER_TRIE_MAX_DEPTH: usize = 2;
38
39fn prefix_range(
45 sorted_keys: &[Nibbles],
46 start: usize,
47 prefix: &Nibbles,
48) -> core::ops::Range<usize> {
49 let begin = start + sorted_keys[start..].partition_point(|p| p < prefix);
51 let mut end = begin;
53 while end < sorted_keys.len() && sorted_keys[end].starts_with(prefix) {
54 end += 1;
55 }
56 begin..end
57}
58
59const fn slotmap_slot_size<T>() -> usize {
62 let union_size = if core::mem::size_of::<T>() > 4 { core::mem::size_of::<T>() } else { 4 };
66 let raw = union_size + 4;
67 let align = if core::mem::align_of::<T>() > 4 { core::mem::align_of::<T>() } else { 4 };
68 (raw + align - 1) & !(align - 1)
69}
70
71fn compact_arena(arena: &mut NodeArena, root: &mut Index) {
75 let mut new_arena = SlotMap::with_capacity(arena.len());
76 let mut queue = VecDeque::new();
77
78 let root_node = arena.remove(*root).expect("root exists");
79 let new_root = new_arena.insert(root_node);
80 queue.push_back(new_root);
81
82 while let Some(new_idx) = queue.pop_front() {
83 let old_children: SmallVec<[(usize, Index); 16]> = match &new_arena[new_idx] {
88 ArenaSparseNode::Branch(b) => b
89 .children
90 .iter()
91 .enumerate()
92 .filter_map(|(i, c)| match c {
93 ArenaSparseNodeBranchChild::Revealed(old_idx) => Some((i, *old_idx)),
94 _ => None,
95 })
96 .collect(),
97 _ => continue,
98 };
99
100 for (child_pos, old_child_idx) in old_children {
101 let child_node = arena.remove(old_child_idx).expect("child exists");
102 let new_child_idx = new_arena.insert(child_node);
103 let ArenaSparseNode::Branch(b) = &mut new_arena[new_idx] else { unreachable!() };
104 b.children[child_pos] = ArenaSparseNodeBranchChild::Revealed(new_child_idx);
105 queue.push_back(new_child_idx);
106 }
107 }
108
109 debug_assert!(
110 arena.is_empty(),
111 "compact_arena: {} orphaned nodes remaining after BFS drain",
112 arena.len(),
113 );
114
115 *arena = new_arena;
116 *root = new_root;
117}
118
119#[derive(Debug, Default, Clone)]
122struct ArenaTrieBuffers {
123 cursor: ArenaCursor,
125 updates: Option<SparseTrieUpdates>,
128 changed_paths: Option<PrefixSetMut>,
130 rlp_buf: Vec<u8>,
132 rlp_node_buf: Vec<RlpNode>,
134}
135
136impl ArenaTrieBuffers {
137 fn clear(&mut self) {
138 if let Some(updates) = self.updates.as_mut() {
139 updates.clear();
140 }
141 if let Some(changed_paths) = self.changed_paths.as_mut() {
142 changed_paths.clear();
143 }
144 self.rlp_buf.clear();
145 self.rlp_node_buf.clear();
146 }
147}
148
149#[derive(Debug, Clone)]
153struct ArenaSparseSubtrie {
154 arena: NodeArena,
156 root: Index,
158 path: Nibbles,
160 buffers: ArenaTrieBuffers,
162 required_proofs: Vec<(usize, ArenaRequiredProof)>,
166 num_leaves: u64,
168 num_dirty_leaves: u64,
170 cached_memory_size: usize,
173}
174
175impl ArenaSparseSubtrie {
176 fn new(record_updates: bool, record_changed_paths: bool) -> Box<Self> {
180 let mut arena = SlotMap::new();
181 let root = arena.insert(ArenaSparseNode::EmptyRoot);
182 let buffers = ArenaTrieBuffers {
183 updates: record_updates.then(SparseTrieUpdates::default),
184 changed_paths: record_changed_paths.then(PrefixSetMut::default),
185 ..Default::default()
186 };
187 Box::new(Self {
188 arena,
189 root,
190 path: Nibbles::default(),
191 buffers,
192 required_proofs: Vec::new(),
193 num_leaves: 0,
194 num_dirty_leaves: 0,
195 cached_memory_size: 0,
196 })
197 }
198
199 const fn memory_size(&self) -> usize {
202 self.cached_memory_size
203 }
204
205 #[cfg(debug_assertions)]
207 fn debug_assert_counters(&self) {
208 let (actual_leaves, actual_dirty) =
209 ArenaParallelSparseTrie::count_leaves_and_dirty(&self.arena, self.root);
210 debug_assert_eq!(
211 self.num_leaves, actual_leaves,
212 "subtrie {:?} num_leaves mismatch: stored {} vs actual {}",
213 self.path, self.num_leaves, actual_leaves,
214 );
215 debug_assert_eq!(
216 self.num_dirty_leaves, actual_dirty,
217 "subtrie {:?} num_dirty_leaves mismatch: stored {} vs actual {}",
218 self.path, self.num_dirty_leaves, actual_dirty,
219 );
220 }
221
222 fn prune<'a>(&mut self, retained_leaves: impl IntoIterator<Item = &'a Nibbles>) -> usize {
232 if !matches!(&self.arena[self.root], ArenaSparseNode::Branch(_)) {
234 return 0;
235 }
236
237 debug_assert_eq!(self.num_dirty_leaves, 0, "prune must run after hashing");
238
239 let retained_leaves = retained_leaves.into_iter();
240 let (retained_lower_bound, retained_upper_bound) = retained_leaves.size_hint();
241 let old_count = self.arena.len();
242 let mut new_arena =
246 SlotMap::with_capacity(retained_upper_bound.unwrap_or(retained_lower_bound) * 2);
247 let mut retained_leaves = retained_leaves.peekable();
248 let mut new_num_leaves = 0u64;
249 let mut new_nodes_heap_size = 0usize;
250
251 let root_node = self.arena.remove(self.root).expect("root exists");
253 let new_root = new_arena.insert(root_node);
254 let mut stack = Vec::new();
255 if let Some(frame) = prepare_retained_node(
256 &new_arena,
257 new_root,
258 self.path,
259 &mut new_num_leaves,
260 &mut new_nodes_heap_size,
261 ) {
262 stack.push(frame);
263 }
264
265 while let Some(frame) = stack.last_mut() {
266 let Some((child_pos, nibble, old_child_idx)) = frame.next_revealed_child(&new_arena)
267 else {
268 stack.pop();
269 continue;
270 };
271
272 let parent_new_idx = frame.new_idx;
274 let mut child_path = frame.branch_logical_path;
275 child_path.push(nibble);
276
277 while retained_leaves.peek().is_some_and(|retained| *retained < &child_path) {
278 retained_leaves.next();
279 }
280
281 if retained_leaves.peek().is_some_and(|retained| retained.starts_with(&child_path)) {
282 let child_node = self.arena.remove(old_child_idx).expect("child exists");
284 let new_child_idx = new_arena.insert(child_node);
285 let ArenaSparseNode::Branch(b) = &mut new_arena[parent_new_idx] else {
286 unreachable!()
287 };
288 b.children[child_pos] = ArenaSparseNodeBranchChild::Revealed(new_child_idx);
289 if let Some(frame) = prepare_retained_node(
290 &new_arena,
291 new_child_idx,
292 child_path,
293 &mut new_num_leaves,
294 &mut new_nodes_heap_size,
295 ) {
296 stack.push(frame);
297 }
298 } else {
299 let node = &self.arena[old_child_idx];
301 let rlp_node = node
302 .state_ref()
303 .expect("child must have state")
304 .cached_rlp_node()
305 .cloned()
306 .expect("pruned child must have cached RLP (prune runs after hashing)");
307 trace!(
308 target: TRACE_TARGET,
309 path = ?child_path,
310 variant = %AsRef::<str>::as_ref(node),
311 cached_rlp_node = ?rlp_node,
312 "pruning node",
313 );
314 let ArenaSparseNode::Branch(b) = &mut new_arena[parent_new_idx] else {
315 unreachable!()
316 };
317 b.children[child_pos] = ArenaSparseNodeBranchChild::Blinded(rlp_node);
318 }
319 }
320
321 let pruned = old_count - new_arena.len();
322 self.num_leaves = new_num_leaves;
323 self.num_dirty_leaves = 0;
324 self.arena = new_arena;
325 self.root = new_root;
326 self.cached_memory_size =
327 self.arena.capacity() * slotmap_slot_size::<ArenaSparseNode>() + new_nodes_heap_size;
328
329 #[cfg(debug_assertions)]
330 self.debug_assert_counters();
331 return pruned;
332
333 struct CopyFrame {
334 new_idx: Index,
335 branch_logical_path: Nibbles,
336 state_mask: TrieMask,
337 remaining_child_mask: TrieMask,
338 }
339
340 impl CopyFrame {
341 fn next_revealed_child(&mut self, new_arena: &NodeArena) -> Option<(usize, u8, Index)> {
342 let ArenaSparseNode::Branch(b) = &new_arena[self.new_idx] else { unreachable!() };
343
344 loop {
345 let nibble = self.remaining_child_mask.first_set_bit_index()?;
346 self.remaining_child_mask.unset_bit(nibble);
347 let child_idx = BranchChildIdx::new(self.state_mask, nibble)
348 .expect("remaining_child_mask must be a subset of state_mask");
349
350 if let ArenaSparseNodeBranchChild::Revealed(old_idx) = b.children[child_idx] {
351 return Some((child_idx.get(), nibble, old_idx))
352 }
353 }
354 }
355 }
356
357 fn prepare_retained_node(
360 new_arena: &NodeArena,
361 new_idx: Index,
362 node_path: Nibbles,
363 new_num_leaves: &mut u64,
364 new_nodes_heap_size: &mut usize,
365 ) -> Option<CopyFrame> {
366 *new_nodes_heap_size += new_arena[new_idx].extra_heap_bytes();
367
368 let ArenaSparseNode::Branch(b) = &new_arena[new_idx] else {
369 if matches!(&new_arena[new_idx], ArenaSparseNode::Leaf { .. }) {
370 *new_num_leaves += 1;
371 }
372 return None;
373 };
374
375 let mut branch_logical_path = node_path;
377 branch_logical_path.extend(&b.short_key);
378
379 Some(CopyFrame {
380 new_idx,
381 branch_logical_path,
382 state_mask: b.state_mask,
383 remaining_child_mask: b.state_mask,
384 })
385 }
386 }
387
388 #[instrument(
396 level = "trace",
397 target = TRACE_TARGET,
398 skip_all,
399 fields(
400 subtrie = ?self.path,
401 num_updates = sorted_updates.len(),
402 ),
403 )]
404 fn update_leaves(&mut self, sorted_updates: &[(B256, Nibbles, LeafUpdate)]) {
405 if sorted_updates.is_empty() {
406 return;
407 }
408 trace!(target: TRACE_TARGET, "Subtrie update_leaves");
409
410 debug_assert!(
411 !matches!(self.arena[self.root], ArenaSparseNode::EmptyRoot),
412 "subtrie root must not be EmptyRoot at start of update_leaves"
413 );
414
415 self.buffers.cursor.reset(&self.arena, self.root, self.path);
416
417 for (idx, &(key, ref full_path, ref update)) in sorted_updates.iter().enumerate() {
418 let find_result = self.buffers.cursor.seek(&mut self.arena, full_path);
419
420 if matches!(find_result, SeekResult::Blinded) {
422 let logical_len = self.buffers.cursor.head_logical_branch_path_len(&self.arena);
423 self.required_proofs.push((
424 idx,
425 ArenaRequiredProof { key, min_len: (logical_len as u8 + 1).min(64) },
426 ));
427 continue;
428 }
429
430 match update {
431 LeafUpdate::Changed(value) if !value.is_empty() => {
432 let (_result, deltas) = ArenaParallelSparseTrie::upsert_leaf(
434 &mut self.arena,
435 &mut self.buffers.cursor,
436 &mut self.root,
437 full_path,
438 value,
439 find_result,
440 );
441 self.num_leaves = (self.num_leaves as i64 + deltas.num_leaves_delta) as u64;
442 self.num_dirty_leaves =
443 (self.num_dirty_leaves as i64 + deltas.num_dirty_leaves_delta) as u64;
444 }
445 LeafUpdate::Changed(_) => {
446 let (result, deltas) = ArenaParallelSparseTrie::remove_leaf(
447 &mut self.arena,
448 &mut self.buffers.cursor,
449 &mut self.root,
450 key,
451 full_path,
452 find_result,
453 &mut self.buffers.updates,
454 );
455 self.num_leaves = (self.num_leaves as i64 + deltas.num_leaves_delta) as u64;
456 self.num_dirty_leaves =
457 (self.num_dirty_leaves as i64 + deltas.num_dirty_leaves_delta) as u64;
458
459 if let RemoveLeafResult::NeedsProof { key, proof_key, min_len } = result {
460 self.required_proofs
461 .push((idx, ArenaRequiredProof { key: proof_key, min_len }));
462 self.required_proofs.push((idx, ArenaRequiredProof { key, min_len }));
463 }
464 }
465 LeafUpdate::Touched => {}
466 }
467 }
468
469 self.buffers.cursor.drain(&mut self.arena);
471
472 #[cfg(debug_assertions)]
473 self.debug_assert_counters();
474 }
475
476 fn reveal_nodes(&mut self, nodes: &mut [ProofTrieNodeV2]) -> SparseTrieResult<()> {
479 if nodes.is_empty() {
480 return Ok(());
481 }
482 trace!(target: TRACE_TARGET, path = ?self.path, num_nodes = nodes.len(), "Subtrie reveal_nodes");
483
484 debug_assert!(
485 !matches!(self.arena[self.root], ArenaSparseNode::EmptyRoot),
486 "subtrie root must not be EmptyRoot in reveal_nodes"
487 );
488
489 self.buffers.cursor.reset(&self.arena, self.root, self.path);
490
491 for node in nodes.iter_mut() {
492 let find_result = self.buffers.cursor.seek(&mut self.arena, &node.path);
493 if ArenaParallelSparseTrie::reveal_node(
494 &mut self.arena,
495 &self.buffers.cursor,
496 node,
497 find_result,
498 )
499 .is_some_and(|child_idx| matches!(self.arena[child_idx], ArenaSparseNode::Leaf { .. }))
500 {
501 self.num_leaves += 1;
502 }
503 }
504
505 self.buffers.cursor.drain(&mut self.arena);
507
508 #[cfg(debug_assertions)]
509 self.debug_assert_counters();
510
511 Ok(())
512 }
513
514 fn update_cached_rlp(&mut self) {
519 ArenaParallelSparseTrie::update_cached_rlp(
520 &mut self.arena,
521 self.root,
522 self.path,
523 &mut self.buffers,
524 );
525 self.num_dirty_leaves = 0;
526 #[cfg(debug_assertions)]
527 self.debug_assert_counters();
528 }
529}
530
531#[derive(Debug, Default)]
535struct SubtrieCounterDeltas {
536 num_leaves_delta: i64,
537 num_dirty_leaves_delta: i64,
538}
539
540#[derive(Debug)]
543enum UpsertLeafResult {
544 Updated,
546 NewLeaf,
548 NewChild,
551}
552
553#[derive(Debug)]
556enum RemoveLeafResult {
557 Removed,
559 NotFound,
561 NeedsProof { key: B256, proof_key: B256, min_len: u8 },
564}
565
566#[derive(Debug, Clone)]
568struct ArenaRequiredProof {
569 key: B256,
571 min_len: u8,
573}
574
575#[derive(Debug, Clone, Copy, PartialEq, Eq)]
580pub struct ArenaParallelismThresholds {
581 pub min_dirty_leaves: u64,
585 pub min_revealed_nodes: usize,
589 pub min_updates: usize,
593 pub min_leaves_for_prune: u64,
597}
598
599impl Default for ArenaParallelismThresholds {
600 fn default() -> Self {
601 Self {
602 min_dirty_leaves: 64,
603 min_revealed_nodes: 16,
604 min_updates: 128,
605 min_leaves_for_prune: 128,
606 }
607 }
608}
609
610#[derive(Debug, Clone)]
667pub struct ArenaParallelSparseTrie {
668 upper_arena: NodeArena,
670 root: Index,
672 buffers: ArenaTrieBuffers,
674 parallelism_thresholds: ArenaParallelismThresholds,
676 #[cfg(feature = "trie-debug")]
678 debug_recorder: TrieDebugRecorder,
679}
680
681impl ArenaParallelSparseTrie {
682 pub const fn with_parallelism_thresholds(
684 mut self,
685 thresholds: ArenaParallelismThresholds,
686 ) -> Self {
687 self.parallelism_thresholds = thresholds;
688 self
689 }
690
691 pub fn set_changed_paths(&mut self, retain_changed_paths: bool) {
693 if retain_changed_paths {
694 self.buffers.changed_paths.get_or_insert_with(PrefixSetMut::default).clear();
695 } else {
696 self.buffers.changed_paths = None;
697 }
698
699 for (_, node) in &mut self.upper_arena {
700 let ArenaSparseNode::Subtrie(subtrie) = node else {
701 continue;
702 };
703 if retain_changed_paths {
704 subtrie.buffers.changed_paths.get_or_insert_with(PrefixSetMut::default).clear();
705 } else {
706 subtrie.buffers.changed_paths = None;
707 }
708 }
709 }
710
711 pub fn with_changed_paths(mut self, retain_changed_paths: bool) -> Self {
713 self.set_changed_paths(retain_changed_paths);
714 self
715 }
716
717 pub fn take_changed_paths(&mut self) -> PrefixSetMut {
719 match self.buffers.changed_paths.take() {
720 Some(changed_paths) => {
721 self.buffers.changed_paths = Some(PrefixSetMut::with_capacity(changed_paths.len()));
722 changed_paths
723 }
724 None => PrefixSetMut::default(),
725 }
726 }
727
728 #[cfg(feature = "trie-debug")]
734 fn record_initial_state(&mut self) {
735 use crate::debug_recorder::{NodeStateRecord, TrieNodeRecord};
736 use alloy_primitives::hex;
737 use alloy_trie::nodes::{BranchNode, TrieNode};
738
739 fn state_to_record(state: &ArenaSparseNodeState) -> NodeStateRecord {
740 match state {
741 ArenaSparseNodeState::Revealed => NodeStateRecord::Revealed,
742 ArenaSparseNodeState::Cached { rlp_node, .. } => {
743 NodeStateRecord::Cached { rlp_node: hex::encode(rlp_node.as_ref()) }
744 }
745 ArenaSparseNodeState::Dirty => NodeStateRecord::Dirty,
746 }
747 }
748
749 fn node_to_record(
753 arena: &NodeArena,
754 idx: Index,
755 path: Nibbles,
756 ) -> Option<ProofTrieNodeRecord> {
757 match &arena[idx] {
758 ArenaSparseNode::EmptyRoot => Some(ProofTrieNodeRecord {
759 path,
760 node: TrieNodeRecord(TrieNode::EmptyRoot),
761 masks: None,
762 short_key: None,
763 state: None,
764 }),
765 ArenaSparseNode::Branch(b) => {
766 let stack = b
767 .children
768 .iter()
769 .map(|child| match child {
770 ArenaSparseNodeBranchChild::Blinded(rlp) => rlp.clone(),
771 ArenaSparseNodeBranchChild::Revealed(child_idx) => {
772 arena[*child_idx]
774 .state_ref()
775 .and_then(|s| s.cached_rlp_node())
776 .cloned()
777 .unwrap_or_default()
778 }
779 })
780 .collect();
781 Some(ProofTrieNodeRecord {
782 path,
783 node: TrieNodeRecord(TrieNode::Branch(BranchNode::new(
784 stack,
785 b.state_mask,
786 ))),
787 masks: Some((
788 b.branch_masks.hash_mask.get(),
789 b.branch_masks.tree_mask.get(),
790 )),
791 short_key: (!b.short_key.is_empty()).then_some(b.short_key),
792 state: Some(state_to_record(&b.state)),
793 })
794 }
795 ArenaSparseNode::Leaf { key, value, state, .. } => Some(ProofTrieNodeRecord {
796 path,
797 node: TrieNodeRecord(TrieNode::Leaf(alloy_trie::nodes::LeafNode::new(
798 *key,
799 value.clone(),
800 ))),
801 masks: None,
802 short_key: None,
803 state: Some(state_to_record(state)),
804 }),
805 ArenaSparseNode::Subtrie(_) | ArenaSparseNode::TakenSubtrie => None,
806 }
807 }
808
809 fn collect_records(
811 arena: &mut NodeArena,
812 root: Index,
813 root_path: Nibbles,
814 cursor: &mut ArenaCursor,
815 result: &mut Vec<ProofTrieNodeRecord>,
816 ) {
817 cursor.reset(arena, root, root_path);
818
819 if let Some(record) = node_to_record(arena, root, root_path) {
821 result.push(record);
822 }
823
824 loop {
825 match cursor.next(arena, |_, node| {
826 matches!(node, ArenaSparseNode::Branch(_) | ArenaSparseNode::Leaf { .. })
827 }) {
828 NextResult::Done => break,
829 NextResult::Branch | NextResult::NonBranch => {
830 let head = cursor.head().expect("cursor is non-empty");
831 if let Some(record) = node_to_record(arena, head.index, head.path) {
832 result.push(record);
833 }
834 }
835 }
836 }
837 }
838
839 let mut nodes = Vec::new();
840
841 collect_records(
843 &mut self.upper_arena,
844 self.root,
845 Nibbles::default(),
846 &mut self.buffers.cursor,
847 &mut nodes,
848 );
849
850 for (_, node) in &mut self.upper_arena {
852 if let ArenaSparseNode::Subtrie(subtrie) = node {
853 collect_records(
854 &mut subtrie.arena,
855 subtrie.root,
856 subtrie.path,
857 &mut self.buffers.cursor,
858 &mut nodes,
859 );
860 }
861 }
862
863 self.debug_recorder.reset();
865 self.debug_recorder.record(RecordedOp::Prune);
866
867 if let Some(root_record) = nodes.first() {
869 self.debug_recorder.record(RecordedOp::SetRoot { node: root_record.clone() });
870 }
871 if nodes.len() > 1 {
872 self.debug_recorder.record(RecordedOp::RevealNodes { nodes: nodes[1..].to_vec() });
873 }
874 }
875
876 const fn should_be_subtrie(path_len: usize) -> bool {
879 path_len == UPPER_TRIE_MAX_DEPTH
880 }
881
882 fn maybe_wrap_in_subtrie(&mut self, child_idx: Index, child_path: &Nibbles) {
887 if !Self::should_be_subtrie(child_path.len()) {
888 return;
889 }
890
891 if !matches!(
893 self.upper_arena[child_idx],
894 ArenaSparseNode::Branch(_) | ArenaSparseNode::Leaf { .. }
895 ) {
896 return;
897 }
898
899 trace!(target: TRACE_TARGET, ?child_path, "Wrapping child into subtrie");
900 let mut subtrie = ArenaSparseSubtrie::new(
901 self.buffers.updates.is_some(),
902 self.buffers.changed_paths.is_some(),
903 );
904 subtrie.path = *child_path;
905 let mut root_node =
906 mem::replace(&mut self.upper_arena[child_idx], ArenaSparseNode::TakenSubtrie);
907
908 if let ArenaSparseNode::Branch(b) = &mut root_node {
910 for child in &mut b.children {
911 if let ArenaSparseNodeBranchChild::Revealed(idx) = child {
912 *idx =
913 Self::migrate_nodes(&mut subtrie.arena, &mut self.upper_arena, *idx, None);
914 }
915 }
916 }
917
918 subtrie.arena[subtrie.root] = root_node;
919 let (leaves, dirty) = Self::count_leaves_and_dirty(&subtrie.arena, subtrie.root);
920 subtrie.num_leaves = leaves;
921 subtrie.num_dirty_leaves = dirty;
922 #[cfg(debug_assertions)]
923 subtrie.debug_assert_counters();
924 self.upper_arena[child_idx] = ArenaSparseNode::Subtrie(subtrie);
925 }
926
927 fn maybe_wrap_branch_children(&mut self, cursor: &ArenaCursor) {
932 let head = cursor.head().expect("cursor is non-empty");
933 let head_idx = head.index;
934 let head_path = head.path;
935
936 let ArenaSparseNode::Branch(b) = &self.upper_arena[head_idx] else { return };
937 let short_key = b.short_key;
938 let children: SmallVec<[_; 4]> = b
939 .child_iter()
940 .filter_map(|(nibble, child)| match child {
941 ArenaSparseNodeBranchChild::Revealed(idx) => Some((nibble, *idx)),
942 ArenaSparseNodeBranchChild::Blinded(_) => None,
943 })
944 .collect();
945
946 for (nibble, child_idx) in children {
947 let mut child_path = head_path;
948 child_path.extend(&short_key);
949 child_path.push_unchecked(nibble);
950 self.maybe_wrap_in_subtrie(child_idx, &child_path);
951 }
952 }
953
954 #[instrument(
963 level = "trace",
964 target = TRACE_TARGET,
965 skip_all,
966 fields(subtrie_path = ?cursor.head().expect("cursor is non-empty").path),
967 )]
968 fn maybe_unwrap_subtrie(&mut self, cursor: &mut ArenaCursor) {
969 let subtrie_idx = cursor.head().expect("cursor is non-empty").index;
970
971 let ArenaSparseNode::Subtrie(subtrie) = &self.upper_arena[subtrie_idx] else {
972 return;
973 };
974
975 if !matches!(subtrie.arena[subtrie.root], ArenaSparseNode::EmptyRoot) {
976 return;
977 }
978
979 let child_nibble = cursor
980 .head()
981 .expect("cursor is non-empty")
982 .path
983 .last()
984 .expect("subtrie path must have at least one nibble");
985 let parent_idx = cursor.parent().expect("cursor has parent").index;
986
987 cursor.pop(&mut self.upper_arena);
990
991 self.recycle_subtrie_from_idx(subtrie_idx);
992
993 trace!(target: TRACE_TARGET, "Unwrapping empty subtrie, removing child slot");
994 let parent_branch = self.upper_arena[parent_idx].branch_mut();
995 let child_idx = BranchChildIdx::new(parent_branch.state_mask, child_nibble)
996 .expect("child nibble not found in parent state_mask");
997
998 parent_branch.children.remove(child_idx.get());
999 parent_branch.unset_child_bit(child_nibble);
1000 parent_branch.state = parent_branch.state.to_dirty();
1002
1003 self.maybe_collapse_or_remove_branch(cursor);
1004 }
1005
1006 fn recycle_subtrie(&mut self, node: ArenaSparseNode) {
1012 let ArenaSparseNode::Subtrie(mut subtrie) = node else {
1013 unreachable!("recycle_subtrie called on non-Subtrie node")
1014 };
1015 Self::merge_subtrie_updates(&mut self.buffers.updates, &mut subtrie.buffers.updates);
1016 Self::merge_subtrie_changed_paths(
1017 &mut self.buffers.changed_paths,
1018 &mut subtrie.buffers.changed_paths,
1019 );
1020 }
1021
1022 fn recycle_subtrie_from_idx(&mut self, idx: Index) {
1024 let node = self.upper_arena.remove(idx).expect("subtrie exists in arena");
1025 self.recycle_subtrie(node);
1026 }
1027
1028 fn maybe_collapse_or_remove_branch(&mut self, cursor: &mut ArenaCursor) {
1039 loop {
1040 let branch_entry = cursor.head().expect("cursor is non-empty");
1041 let branch_idx = branch_entry.index;
1042 let branch_path = branch_entry.path;
1043
1044 let count = {
1047 let ArenaSparseNode::Branch(b) = &self.upper_arena[branch_idx] else {
1048 return;
1049 };
1050 b.state_mask.count_bits()
1051 };
1052
1053 if count >= 2 {
1054 return;
1055 }
1056
1057 if count == 0 {
1058 if branch_idx == self.root {
1059 self.upper_arena[branch_idx] = ArenaSparseNode::EmptyRoot;
1060 return;
1061 }
1062 let branch_nibble = branch_path.last().expect("non-root branch");
1064 cursor.pop(&mut self.upper_arena);
1065 self.upper_arena.remove(branch_idx);
1066 let parent_idx = cursor.head().expect("cursor is non-empty").index;
1067 let parent_branch = self.upper_arena[parent_idx].branch_mut();
1068 let child_idx = BranchChildIdx::new(parent_branch.state_mask, branch_nibble)
1069 .expect("child nibble not found in parent state_mask");
1070 parent_branch.children.remove(child_idx.get());
1071 parent_branch.unset_child_bit(branch_nibble);
1072 parent_branch.state = parent_branch.state.to_dirty();
1073 continue; }
1075
1076 let (remaining_nibble, remaining_child_idx) = {
1078 let b = self.upper_arena[branch_idx].branch_ref();
1079 let nibble = b.state_mask.iter().next().expect("branch has at least one child");
1080 let child_idx = match &b.children[0] {
1081 ArenaSparseNodeBranchChild::Revealed(idx) => Some(*idx),
1082 ArenaSparseNodeBranchChild::Blinded(_) => None,
1083 };
1084 (nibble, child_idx)
1085 };
1086
1087 let Some(child_idx) = remaining_child_idx else {
1088 debug_assert!(false, "single remaining child is blinded — should have been caught by check_subtrie_collapse_needs_proof");
1089 return;
1090 };
1091
1092 if matches!(self.upper_arena[child_idx], ArenaSparseNode::TakenSubtrie) {
1093 return;
1096 }
1097
1098 let is_empty_subtrie = matches!(
1100 &self.upper_arena[child_idx],
1101 ArenaSparseNode::Subtrie(s) if matches!(s.arena[s.root], ArenaSparseNode::EmptyRoot)
1102 );
1103
1104 if is_empty_subtrie {
1105 self.recycle_subtrie_from_idx(child_idx);
1106 let branch = self.upper_arena[branch_idx].branch_mut();
1107 branch.children.remove(0);
1108 branch.unset_child_bit(remaining_nibble);
1109 branch.state = branch.state.to_dirty();
1110 continue; }
1112
1113 Self::collapse_branch(
1115 &mut self.upper_arena,
1116 cursor,
1117 &mut self.root,
1118 &mut self.buffers.updates,
1119 );
1120
1121 let child_idx = cursor.head().expect("cursor is non-empty").index;
1126 if let ArenaSparseNode::Subtrie(_) = &self.upper_arena[child_idx] {
1127 let ArenaSparseNode::Subtrie(mut subtrie) =
1128 mem::replace(&mut self.upper_arena[child_idx], ArenaSparseNode::TakenSubtrie)
1129 else {
1130 unreachable!()
1131 };
1132 Self::migrate_nodes(
1133 &mut self.upper_arena,
1134 &mut subtrie.arena,
1135 subtrie.root,
1136 Some(child_idx),
1137 );
1138 Self::merge_subtrie_updates(
1139 &mut self.buffers.updates,
1140 &mut subtrie.buffers.updates,
1141 );
1142 Self::merge_subtrie_changed_paths(
1143 &mut self.buffers.changed_paths,
1144 &mut subtrie.buffers.changed_paths,
1145 );
1146
1147 self.maybe_wrap_branch_children(cursor);
1151 }
1152 return;
1153 }
1154 }
1155
1156 fn merge_subtrie_updates(
1163 dst: &mut Option<SparseTrieUpdates>,
1164 src: &mut Option<SparseTrieUpdates>,
1165 ) {
1166 if let Some(dst_updates) = dst.as_mut() {
1167 let src_updates = src.as_mut().expect("updates are enabled");
1168 debug_assert!(!src_updates.wiped, "subtrie updates should never have wiped=true");
1169
1170 for path in src_updates.updated_nodes.keys() {
1172 dst_updates.removed_nodes.remove(path);
1173 }
1174 dst_updates.updated_nodes.extend(src_updates.updated_nodes.drain());
1175
1176 for path in &src_updates.removed_nodes {
1178 dst_updates.updated_nodes.remove(path);
1179 }
1180 dst_updates.removed_nodes.extend(src_updates.removed_nodes.drain());
1181 }
1182 }
1183
1184 fn merge_subtrie_changed_paths(dst: &mut Option<PrefixSetMut>, src: &mut Option<PrefixSetMut>) {
1187 if let Some(dst_changed_paths) = dst.as_mut() {
1188 let src_changed_paths = src.as_mut().expect("changed path tracking is enabled");
1189 dst_changed_paths.append(src_changed_paths);
1190 }
1191 }
1192
1193 fn nibbles_to_padded_b256(path: &Nibbles) -> B256 {
1195 let mut bytes = [0u8; 32];
1196 path.pack_to(&mut bytes);
1197 B256::from(bytes)
1198 }
1199
1200 fn get_branch_masks(arena: &NodeArena, branch: &ArenaSparseNodeBranch) -> BranchNodeMasks {
1202 let mut masks = BranchNodeMasks::default();
1203
1204 for (nibble, child) in branch.child_iter() {
1205 let (hash_bit, tree_bit) = match child {
1206 ArenaSparseNodeBranchChild::Blinded(_) => (
1207 branch.branch_masks.hash_mask.is_bit_set(nibble),
1208 branch.branch_masks.tree_mask.is_bit_set(nibble),
1209 ),
1210 ArenaSparseNodeBranchChild::Revealed(child_idx) => {
1211 let child = &arena[*child_idx];
1212 (child.hash_mask_bit(), child.tree_mask_bit())
1213 }
1214 };
1215
1216 masks.set_child_bits(nibble, hash_bit, tree_bit);
1217 }
1218
1219 masks
1220 }
1221
1222 #[instrument(level = "trace", target = TRACE_TARGET, skip_all, fields(base_path = ?base_path), ret)]
1235 fn update_cached_rlp(
1236 arena: &mut NodeArena,
1237 root: Index,
1238 base_path: Nibbles,
1239 buffers: &mut ArenaTrieBuffers,
1240 ) -> RlpNode {
1241 let cursor = &mut buffers.cursor;
1242 let rlp_buf = &mut buffers.rlp_buf;
1243 let rlp_node_buf = &mut buffers.rlp_node_buf;
1244 let updates = &mut buffers.updates;
1245 let changed_paths = &mut buffers.changed_paths;
1246
1247 rlp_node_buf.clear();
1248
1249 match &arena[root] {
1253 ArenaSparseNode::EmptyRoot => return RlpNode::word_rlp(&EMPTY_ROOT_HASH),
1254 ArenaSparseNode::Leaf { .. } => {
1255 Self::encode_leaf(arena, root, rlp_buf, rlp_node_buf);
1256 let was_dirty = arena[root].state_mut().take_cached_was_dirty();
1257 if was_dirty && let Some(changed_paths) = changed_paths.as_mut() {
1258 changed_paths.insert(base_path);
1259 }
1260 return rlp_node_buf.pop().expect("encode_leaf must push an RlpNode");
1261 }
1262 ArenaSparseNode::Branch(b) => {
1263 if let ArenaSparseNodeState::Cached { rlp_node, .. } = &b.state {
1264 let rlp_node = rlp_node.clone();
1265 arena[root].state_mut().take_cached_was_dirty();
1266 return rlp_node;
1267 }
1268 }
1269 ArenaSparseNode::Subtrie(_) | ArenaSparseNode::TakenSubtrie => {
1270 unreachable!("Subtrie/TakenSubtrie should not appear inside a subtrie's own arena");
1271 }
1272 }
1273
1274 cursor.reset(arena, root, base_path);
1275
1276 loop {
1280 let result = cursor.next(&mut *arena, |_, node| {
1281 matches!(
1282 node,
1283 ArenaSparseNode::Branch(b) if matches!(b.state, ArenaSparseNodeState::Dirty)
1284 )
1285 });
1286
1287 match result {
1288 NextResult::Done => break,
1289 NextResult::NonBranch => {
1290 unreachable!("should_descend only returns true for dirty branches")
1291 }
1292 NextResult::Branch => {}
1293 };
1294
1295 let head = cursor.head().expect("cursor is non-empty");
1296 let head_idx = head.index;
1297 let head_path = head.path;
1298
1299 trace!(
1303 target: TRACE_TARGET,
1304 branch_path = ?head_path,
1305 branch_short_key = ?arena[head_idx].short_key().expect("head is a branch"),
1306 state_mask = ?arena[head_idx].branch_ref().state_mask,
1307 "Calculating branch RlpNode",
1308 );
1309
1310 rlp_node_buf.clear();
1311 let state_mask = arena[head_idx].branch_ref().state_mask;
1312 let branch_logical_path = {
1313 let branch = arena[head_idx].branch_ref();
1314 let mut path = head_path;
1315 path.extend(&branch.short_key);
1316 path
1317 };
1318 let mut child_subtree_emitted_changed_path = false;
1319 for (child_idx, nibble) in BranchChildIter::new(state_mask) {
1320 match &arena[head_idx].branch_ref().children[child_idx] {
1321 ArenaSparseNodeBranchChild::Blinded(rlp_node) => {
1322 rlp_node_buf.push(rlp_node.clone());
1323 }
1324 ArenaSparseNodeBranchChild::Revealed(child_idx) => {
1325 let child_idx = *child_idx;
1326 match &arena[child_idx] {
1327 ArenaSparseNode::Leaf { .. } => {
1328 Self::encode_leaf(arena, child_idx, rlp_buf, rlp_node_buf);
1329 if arena[child_idx].state_mut().take_cached_was_dirty() {
1330 child_subtree_emitted_changed_path = true;
1331 if let Some(changed_paths) = changed_paths.as_mut() {
1332 let mut child_path = branch_logical_path;
1333 child_path.push(nibble);
1334 changed_paths.insert(child_path);
1335 }
1336 }
1337 }
1338 ArenaSparseNode::Branch(child_b) => {
1339 let ArenaSparseNodeState::Cached { rlp_node, .. } = &child_b.state
1340 else {
1341 panic!("child branch must be cached after DFS");
1342 };
1343 let rlp_node = rlp_node.clone();
1344 if arena[child_idx].state_mut().take_cached_was_dirty() {
1345 child_subtree_emitted_changed_path = true;
1346 }
1347 rlp_node_buf.push(rlp_node);
1348 }
1349 ArenaSparseNode::Subtrie(subtrie) => {
1350 let subtrie_root = &subtrie.arena[subtrie.root];
1351 match subtrie_root {
1352 ArenaSparseNode::Branch(ArenaSparseNodeBranch {
1353 state: ArenaSparseNodeState::Cached { rlp_node, .. },
1354 ..
1355 }) |
1356 ArenaSparseNode::Leaf {
1357 state: ArenaSparseNodeState::Cached { rlp_node, .. },
1358 ..
1359 } => {
1360 rlp_node_buf.push(rlp_node.clone());
1361 }
1362 _ => panic!("subtrie root must be a cached Branch or Leaf"),
1363 }
1364 }
1365 ArenaSparseNode::TakenSubtrie | ArenaSparseNode::EmptyRoot => {
1366 unreachable!("Unexpected child {:?}", arena[child_idx]);
1367 }
1368 }
1369 }
1370 }
1371 }
1372
1373 let b = arena[head_idx].branch_ref();
1375 let short_key = b.short_key;
1376 let state_mask = b.state_mask;
1377 let prev_branch_masks = b.branch_masks;
1378 let new_branch_masks = Self::get_branch_masks(arena, b);
1379 let was_dirty = matches!(b.state, ArenaSparseNodeState::Dirty);
1380
1381 rlp_buf.clear();
1382 let rlp_node = BranchNodeRef::new(rlp_node_buf, state_mask).rlp(rlp_buf);
1383
1384 let rlp_node = if short_key.is_empty() {
1385 rlp_node
1386 } else {
1387 rlp_buf.clear();
1388 ExtensionNodeRef::new(&short_key, &rlp_node).rlp(rlp_buf)
1389 };
1390
1391 trace!(
1392 target: TRACE_TARGET,
1393 path = ?head_path,
1394 short_key = ?arena[head_idx].short_key(),
1395 children = ?state_mask.iter().zip(rlp_node_buf.iter()).collect::<Vec<_>>(),
1396 rlp_node = ?rlp_node,
1397 "Calculated branch RlpNode",
1398 );
1399
1400 let branch = arena[head_idx].branch_mut();
1401 branch.state = ArenaSparseNodeState::Cached { rlp_node: rlp_node.clone(), was_dirty };
1402 branch.branch_masks = new_branch_masks;
1403
1404 if was_dirty &&
1405 !child_subtree_emitted_changed_path &&
1406 let Some(changed_paths) = changed_paths.as_mut()
1407 {
1408 changed_paths.insert(head_path);
1409 }
1410
1411 if let Some(trie_updates) = updates.as_mut().filter(|_| was_dirty) {
1414 let mut logical_path = head_path;
1415 logical_path.extend(&short_key);
1416
1417 if !logical_path.is_empty() {
1418 if !prev_branch_masks.is_empty() && new_branch_masks.is_empty() {
1419 trie_updates.updated_nodes.remove(&logical_path);
1420 trie_updates.removed_nodes.insert(logical_path);
1421 } else if !new_branch_masks.is_empty() {
1422 let compact = arena[head_idx].branch_ref().branch_node_compact(arena);
1423 trie_updates.updated_nodes.insert(logical_path, compact);
1424 trie_updates.removed_nodes.remove(&logical_path);
1425 }
1426 }
1427 }
1428 }
1429
1430 let ArenaSparseNodeState::Cached { rlp_node, .. } = &arena[root].branch_ref().state else {
1431 panic!("root must be cached after update_cached_rlp");
1432 };
1433 let rlp_node = rlp_node.clone();
1434 arena[root].state_mut().take_cached_was_dirty();
1436 rlp_node
1437 }
1438
1439 fn get_leaf_value_in_arena<'a>(
1442 arena: &'a NodeArena,
1443 mut current: Index,
1444 full_path: &Nibbles,
1445 mut path_offset: usize,
1446 ) -> Option<&'a Vec<u8>> {
1447 loop {
1448 match &arena[current] {
1449 ArenaSparseNode::EmptyRoot | ArenaSparseNode::TakenSubtrie => return None,
1450 ArenaSparseNode::Leaf { key, value, .. } => {
1451 let remaining = full_path.slice(path_offset..);
1452 return (remaining == *key).then_some(value);
1453 }
1454 ArenaSparseNode::Branch(b) => {
1455 let short_key = &b.short_key;
1456 let logical_end = path_offset + short_key.len();
1457 if full_path.len() <= logical_end ||
1458 full_path.slice(path_offset..logical_end) != *short_key
1459 {
1460 return None;
1461 }
1462
1463 let child_nibble = full_path.get_unchecked(logical_end);
1464 let child_idx = BranchChildIdx::new(b.state_mask, child_nibble)?;
1465 match &b.children[child_idx] {
1466 ArenaSparseNodeBranchChild::Blinded(_) => return None,
1467 ArenaSparseNodeBranchChild::Revealed(child_idx) => {
1468 current = *child_idx;
1469 path_offset = logical_end + 1;
1470 }
1471 }
1472 }
1473 ArenaSparseNode::Subtrie(subtrie) => {
1474 return Self::get_leaf_value_in_arena(
1475 &subtrie.arena,
1476 subtrie.root,
1477 full_path,
1478 path_offset,
1479 );
1480 }
1481 }
1482 }
1483 }
1484
1485 fn find_leaf_in_arena(
1489 arena: &NodeArena,
1490 mut current: Index,
1491 full_path: &Nibbles,
1492 mut path_offset: usize,
1493 expected_value: Option<&Vec<u8>>,
1494 ) -> Result<LeafLookup, LeafLookupError> {
1495 loop {
1496 match &arena[current] {
1497 ArenaSparseNode::EmptyRoot | ArenaSparseNode::TakenSubtrie => {
1498 return Ok(LeafLookup::NonExistent);
1499 }
1500 ArenaSparseNode::Leaf { key, value, .. } => {
1501 let remaining = full_path.slice(path_offset..);
1502 if remaining != *key {
1503 return Ok(LeafLookup::NonExistent);
1504 }
1505 if let Some(expected) = expected_value &&
1506 *expected != *value
1507 {
1508 return Err(LeafLookupError::ValueMismatch {
1509 path: *full_path,
1510 expected: Some(expected.clone()),
1511 actual: value.clone(),
1512 });
1513 }
1514 return Ok(LeafLookup::Exists);
1515 }
1516 ArenaSparseNode::Branch(b) => {
1517 let short_key = &b.short_key;
1518 let logical_end = path_offset + short_key.len();
1519
1520 if full_path.len() <= logical_end {
1521 return Ok(LeafLookup::NonExistent);
1522 }
1523
1524 if full_path.slice(path_offset..logical_end) != *short_key {
1525 return Ok(LeafLookup::NonExistent);
1526 }
1527
1528 let child_nibble = full_path.get_unchecked(logical_end);
1529 let Some(child_idx) = BranchChildIdx::new(b.state_mask, child_nibble) else {
1530 return Ok(LeafLookup::NonExistent);
1531 };
1532
1533 match &b.children[child_idx] {
1534 ArenaSparseNodeBranchChild::Blinded(rlp_node) => {
1535 let hash = rlp_node
1536 .as_hash()
1537 .unwrap_or_else(|| keccak256(rlp_node.as_slice()));
1538 let mut blinded_path = full_path.slice(..logical_end);
1539 blinded_path.push_unchecked(child_nibble);
1540 return Err(LeafLookupError::BlindedNode { path: blinded_path, hash });
1541 }
1542 ArenaSparseNodeBranchChild::Revealed(child_idx) => {
1543 current = *child_idx;
1544 path_offset = logical_end + 1;
1545 }
1546 }
1547 }
1548 ArenaSparseNode::Subtrie(subtrie) => {
1549 return Self::find_leaf_in_arena(
1550 &subtrie.arena,
1551 subtrie.root,
1552 full_path,
1553 path_offset,
1554 expected_value,
1555 );
1556 }
1557 }
1558 }
1559 }
1560
1561 fn encode_leaf(
1565 arena: &mut NodeArena,
1566 idx: Index,
1567 rlp_buf: &mut Vec<u8>,
1568 rlp_node_buf: &mut Vec<RlpNode>,
1569 ) {
1570 let (key, value, state) = match &arena[idx] {
1571 ArenaSparseNode::Leaf { key, value, state } => (key, value, state),
1572 _ => unreachable!("encode_leaf called on non-Leaf node"),
1573 };
1574
1575 if let ArenaSparseNodeState::Cached { rlp_node, .. } = state {
1576 rlp_node_buf.push(rlp_node.clone());
1577 return;
1578 }
1579
1580 let was_dirty = matches!(state, ArenaSparseNodeState::Dirty);
1581
1582 rlp_buf.clear();
1583 let rlp_node = LeafNodeRef { key, value }.rlp(rlp_buf);
1584
1585 *arena[idx].state_mut() =
1586 ArenaSparseNodeState::Cached { rlp_node: rlp_node.clone(), was_dirty };
1587 rlp_node_buf.push(rlp_node);
1588 }
1589
1590 fn split_and_insert_leaf(
1604 arena: &mut NodeArena,
1605 cursor: &mut ArenaCursor,
1606 root: &mut Index,
1607 new_leaf_path: Nibbles,
1608 value: &[u8],
1609 ) -> bool {
1610 let old_child_entry = cursor.head().expect("cursor must have head");
1611 let old_child_idx = old_child_entry.index;
1612 let old_child_short_key = arena[old_child_idx].short_key().expect("top of stack is a leaf");
1613 let diverge_len = new_leaf_path.common_prefix_length(old_child_short_key);
1614
1615 trace!(
1616 target: TRACE_TARGET,
1617 path = ?old_child_entry.path,
1618 ?new_leaf_path,
1619 ?old_child_short_key,
1620 diverge_len,
1621 "Splitting node and inserting new leaf",
1622 );
1623
1624 let old_child_nibble = old_child_short_key.get_unchecked(diverge_len);
1625 let old_child_suffix = old_child_short_key.slice(diverge_len + 1..);
1626
1627 let newly_dirtied_existing = match &mut arena[old_child_idx] {
1630 ArenaSparseNode::Leaf { key, state, .. } => {
1631 *key = old_child_suffix;
1632 let was_clean = !matches!(state, ArenaSparseNodeState::Dirty);
1633 *state = ArenaSparseNodeState::Dirty;
1634 was_clean
1635 }
1636 ArenaSparseNode::Branch(b) => {
1637 b.short_key = old_child_suffix;
1638 b.state = b.state.to_dirty();
1639 false
1641 }
1642 _ => unreachable!("split_and_insert_leaf called on non-Leaf/Branch node"),
1643 };
1644
1645 let short_key = new_leaf_path.slice(..diverge_len);
1646 let new_leaf_nibble = new_leaf_path.get_unchecked(diverge_len);
1647 debug_assert_ne!(old_child_nibble, new_leaf_nibble);
1648
1649 let new_leaf_idx = arena.insert(ArenaSparseNode::Leaf {
1650 state: ArenaSparseNodeState::Dirty,
1651 key: new_leaf_path.slice(diverge_len + 1..),
1652 value: value.to_vec(),
1653 });
1654
1655 let (first_nibble, first_child, second_nibble, second_child) =
1656 if old_child_nibble < new_leaf_nibble {
1657 (old_child_nibble, old_child_idx, new_leaf_nibble, new_leaf_idx)
1658 } else {
1659 (new_leaf_nibble, new_leaf_idx, old_child_nibble, old_child_idx)
1660 };
1661
1662 let state_mask = TrieMask::from(1u16 << first_nibble | 1u16 << second_nibble);
1663 let mut children = SmallVec::with_capacity(2);
1664 children.push(ArenaSparseNodeBranchChild::Revealed(first_child));
1665 children.push(ArenaSparseNodeBranchChild::Revealed(second_child));
1666
1667 let new_branch_idx = arena.insert(ArenaSparseNode::Branch(ArenaSparseNodeBranch {
1668 state: ArenaSparseNodeState::Dirty,
1669 children,
1670 state_mask,
1671 short_key,
1672 branch_masks: BranchNodeMasks::default(),
1673 }));
1674
1675 cursor.replace_head_index(arena, root, new_branch_idx);
1676 newly_dirtied_existing
1677 }
1678
1679 #[instrument(level = "trace", target = TRACE_TARGET, skip_all, fields(full_path = ?full_path))]
1694 fn upsert_leaf(
1695 arena: &mut NodeArena,
1696 cursor: &mut ArenaCursor,
1697 root: &mut Index,
1698 full_path: &Nibbles,
1699 value: &[u8],
1700 find_result: SeekResult,
1701 ) -> (UpsertLeafResult, SubtrieCounterDeltas) {
1702 trace!(target: TRACE_TARGET, ?find_result, "Upserting leaf");
1703 let head = cursor.head().expect("cursor is non-empty");
1704
1705 match find_result {
1706 SeekResult::Blinded => {
1707 unreachable!("Blinded case must be handled by caller")
1708 }
1709 SeekResult::EmptyRoot => {
1710 let head_idx = head.index;
1711 let head_path = head.path;
1712 arena[head_idx] = ArenaSparseNode::Leaf {
1713 state: ArenaSparseNodeState::Dirty,
1714 key: full_path.slice(head_path.len()..),
1715 value: value.to_vec(),
1716 };
1717 (
1718 UpsertLeafResult::NewLeaf,
1719 SubtrieCounterDeltas { num_leaves_delta: 1, num_dirty_leaves_delta: 1 },
1720 )
1721 }
1722 SeekResult::RevealedLeaf => {
1723 let head_idx = head.index;
1725 let was_clean =
1726 if let ArenaSparseNode::Leaf { value: v, state, .. } = &mut arena[head_idx] {
1727 v.clear();
1728 v.extend_from_slice(value);
1729 let was_clean = !matches!(state, ArenaSparseNodeState::Dirty);
1730 *state = ArenaSparseNodeState::Dirty;
1731 was_clean
1732 } else {
1733 unreachable!("RevealedLeaf but cursor head is not a leaf")
1734 };
1735 (
1736 UpsertLeafResult::Updated,
1737 SubtrieCounterDeltas {
1738 num_leaves_delta: 0,
1739 num_dirty_leaves_delta: was_clean as i64,
1740 },
1741 )
1742 }
1743 SeekResult::Diverged => {
1744 let head_path = head.path;
1745 let full_path_from_head = full_path.slice(head_path.len()..);
1746
1747 let split_dirtied_existing =
1748 Self::split_and_insert_leaf(arena, cursor, root, full_path_from_head, value);
1749
1750 let result = if cursor.depth() >= 1 {
1751 UpsertLeafResult::NewChild
1752 } else {
1753 UpsertLeafResult::NewLeaf
1754 };
1755 (
1756 result,
1757 SubtrieCounterDeltas {
1758 num_leaves_delta: 1,
1759 num_dirty_leaves_delta: 1 + split_dirtied_existing as i64,
1760 },
1761 )
1762 }
1763 SeekResult::NoChild { child_nibble } => {
1764 let head_idx = head.index;
1765
1766 let head_branch_logical_path = cursor.head_logical_branch_path(arena);
1767 let leaf_key = full_path.slice(head_branch_logical_path.len() + 1..);
1768 let new_leaf = arena.insert(ArenaSparseNode::Leaf {
1769 state: ArenaSparseNodeState::Dirty,
1770 key: leaf_key,
1771 value: value.to_vec(),
1772 });
1773
1774 let branch = arena[head_idx].branch_mut();
1775 branch.set_child(child_nibble, ArenaSparseNodeBranchChild::Revealed(new_leaf));
1776
1777 cursor.seek(arena, full_path);
1779
1780 (
1781 UpsertLeafResult::NewChild,
1782 SubtrieCounterDeltas { num_leaves_delta: 1, num_dirty_leaves_delta: 1 },
1783 )
1784 }
1785 SeekResult::RevealedSubtrie => {
1786 unreachable!("RevealedSubtrie must be handled by caller")
1787 }
1788 }
1789 }
1790
1791 fn remove_leaf(
1807 arena: &mut NodeArena,
1808 cursor: &mut ArenaCursor,
1809 root: &mut Index,
1810 key: B256,
1811 full_path: &Nibbles,
1812 find_result: SeekResult,
1813 updates: &mut Option<SparseTrieUpdates>,
1814 ) -> (RemoveLeafResult, SubtrieCounterDeltas) {
1815 match find_result {
1816 SeekResult::Blinded | SeekResult::RevealedSubtrie => {
1817 unreachable!("Blinded/RevealedSubtrie must be handled by caller")
1818 }
1819 SeekResult::EmptyRoot | SeekResult::Diverged | SeekResult::NoChild { .. } => {
1820 (RemoveLeafResult::NotFound, SubtrieCounterDeltas::default())
1821 }
1822 SeekResult::RevealedLeaf => {
1823 let head = cursor.head().expect("cursor is non-empty");
1825 let head_idx = head.index;
1826 let head_path = head.path;
1827
1828 trace!(
1829 target: TRACE_TARGET,
1830 path = ?head_path,
1831 ?full_path,
1832 "Removing leaf",
1833 );
1834
1835 if let Some(parent_entry) = cursor.parent() {
1838 let parent_idx = parent_entry.index;
1839 let child_nibble = head_path.last().expect("non-root leaf");
1840 let parent_branch = arena[parent_idx].branch_ref();
1841
1842 if parent_branch.state_mask.count_bits() == 2 &&
1843 parent_branch.sibling_child(child_nibble).is_blinded()
1844 {
1845 let sibling_nibble = parent_branch
1846 .state_mask
1847 .iter()
1848 .find(|&n| n != child_nibble)
1849 .expect("branch has two children");
1850 let mut sibling_path = cursor.parent_logical_branch_path(arena);
1851 sibling_path.push_unchecked(sibling_nibble);
1852 trace!(target: TRACE_TARGET, ?full_path, ?sibling_path, "Removal would collapse branch onto blinded sibling, requesting proof");
1853 return (
1854 RemoveLeafResult::NeedsProof {
1855 key,
1856 proof_key: Self::nibbles_to_padded_b256(&sibling_path),
1857 min_len: (sibling_path.len() as u8).min(64),
1858 },
1859 SubtrieCounterDeltas::default(),
1860 );
1861 }
1862 }
1863
1864 let removed_was_dirty =
1866 matches!(arena[head_idx].state_ref(), Some(ArenaSparseNodeState::Dirty));
1867
1868 if cursor.depth() == 0 {
1869 arena.remove(head_idx);
1872 *root = arena.insert(ArenaSparseNode::EmptyRoot);
1873 cursor.reset(arena, *root, head_path);
1874 return (
1875 RemoveLeafResult::Removed,
1876 SubtrieCounterDeltas {
1877 num_leaves_delta: -1,
1878 num_dirty_leaves_delta: -(removed_was_dirty as i64),
1879 },
1880 );
1881 }
1882
1883 cursor.pop(arena);
1885
1886 let parent_entry = cursor.head().expect("cursor is non-empty");
1888 let parent_idx = parent_entry.index;
1889 let child_nibble = head_path.last().expect("non-root leaf");
1890
1891 arena.remove(head_idx);
1893 let parent_branch = arena[parent_idx].branch_mut();
1894 parent_branch.remove_child(child_nibble);
1895
1896 let collapse_dirtied_leaf = if parent_branch.state_mask.count_bits() == 1 {
1899 Self::collapse_branch(arena, cursor, root, updates)
1900 } else {
1901 false
1902 };
1903 (
1904 RemoveLeafResult::Removed,
1905 SubtrieCounterDeltas {
1906 num_leaves_delta: -1,
1907 num_dirty_leaves_delta: (collapse_dirtied_leaf as i64) -
1908 (removed_was_dirty as i64),
1909 },
1910 )
1911 }
1912 }
1913 }
1914
1915 fn check_subtrie_collapse_needs_proof(
1921 arena: &NodeArena,
1922 cursor: &ArenaCursor,
1923 subtrie_updates: &[(B256, Nibbles, LeafUpdate)],
1924 ) -> Option<ArenaRequiredProof> {
1925 let num_removals = subtrie_updates
1926 .iter()
1927 .filter(|(_, _, u)| matches!(u, LeafUpdate::Changed(v) if v.is_empty()))
1928 .count() as u64;
1929
1930 let num_changed =
1938 subtrie_updates.iter().filter(|(_, _, u)| matches!(u, LeafUpdate::Changed(_))).count()
1939 as u64;
1940
1941 if num_removals == 0 || num_removals != num_changed {
1942 return None;
1943 }
1944
1945 let subtrie_entry = cursor.head()?;
1947 let subtrie_num_leaves = match &arena[subtrie_entry.index] {
1948 ArenaSparseNode::Subtrie(s) => s.num_leaves,
1949 _ => return None,
1950 };
1951 if num_removals < subtrie_num_leaves {
1952 return None;
1953 }
1954
1955 let child_nibble =
1956 subtrie_entry.path.last().expect("subtrie path must have at least one nibble");
1957
1958 let parent_entry = cursor.parent()?;
1959 let parent_branch = arena[parent_entry.index].branch_ref();
1960 if parent_branch.state_mask.count_bits() != 2 {
1961 return None;
1962 }
1963
1964 if !parent_branch.sibling_child(child_nibble).is_blinded() {
1965 return None;
1966 }
1967
1968 let sibling_nibble = parent_branch
1969 .state_mask
1970 .iter()
1971 .find(|&n| n != child_nibble)
1972 .expect("branch has two children");
1973 let mut sibling_path = cursor.parent_logical_branch_path(arena);
1974 sibling_path.push_unchecked(sibling_nibble);
1975
1976 Some(ArenaRequiredProof {
1977 key: Self::nibbles_to_padded_b256(&sibling_path),
1978 min_len: (sibling_path.len() as u8).min(64),
1979 })
1980 }
1981
1982 fn collapse_branch(
1993 arena: &mut NodeArena,
1994 cursor: &mut ArenaCursor,
1995 root: &mut Index,
1996 updates: &mut Option<SparseTrieUpdates>,
1997 ) -> bool {
1998 let branch_entry = cursor.head().expect("cursor is non-empty");
1999 let branch_idx = branch_entry.index;
2000 let branch = arena[branch_idx].branch_ref();
2001 let remaining_nibble =
2002 branch.state_mask.iter().next().expect("branch has at least one child");
2003 let branch_short_key = branch.short_key;
2004
2005 debug_assert_eq!(
2006 branch.state_mask.count_bits(),
2007 1,
2008 "collapse_branch requires exactly 1 child"
2009 );
2010 debug_assert!(
2011 !branch.children[0].is_blinded(),
2012 "collapse_branch called with a blinded remaining child"
2013 );
2014
2015 trace!(
2016 target: TRACE_TARGET,
2017 path = ?branch_entry.path,
2018 short_key = ?branch_short_key,
2019 branch_masks = ?branch.branch_masks,
2020 ?remaining_nibble,
2021 "Collapsing single-child branch",
2022 );
2023
2024 if let Some(trie_updates) = updates.as_mut() &&
2027 !branch.branch_masks.is_empty()
2028 {
2029 let logical_path = cursor.head_logical_branch_path(arena);
2030 if !logical_path.is_empty() {
2031 trie_updates.updated_nodes.remove(&logical_path);
2032 trie_updates.removed_nodes.insert(logical_path);
2033 }
2034 }
2035
2036 let mut prefix = branch_short_key;
2038 prefix.push_unchecked(remaining_nibble);
2039
2040 let ArenaSparseNodeBranchChild::Revealed(child_idx) = branch.children[0] else {
2041 unreachable!()
2042 };
2043
2044 let newly_dirtied_leaf = match &mut arena[child_idx] {
2047 ArenaSparseNode::Leaf { key, state, .. } => {
2048 let mut new_key = prefix;
2049 new_key.extend(key);
2050 *key = new_key;
2051 let was_clean = !matches!(state, ArenaSparseNodeState::Dirty);
2052 *state = ArenaSparseNodeState::Dirty;
2053 was_clean
2054 }
2055 ArenaSparseNode::Branch(b) => {
2056 let mut new_short_key = prefix;
2057 new_short_key.extend(&b.short_key);
2058 b.short_key = new_short_key;
2059 b.state = b.state.to_dirty();
2060 false
2061 }
2062 ArenaSparseNode::Subtrie(subtrie) => {
2063 subtrie.path = branch_entry.path;
2064 match &mut subtrie.arena[subtrie.root] {
2065 ArenaSparseNode::Branch(b) => {
2066 let mut new_short_key = prefix;
2067 new_short_key.extend(&b.short_key);
2068 b.short_key = new_short_key;
2069 b.state = b.state.to_dirty();
2070 }
2071 ArenaSparseNode::Leaf { key, state, .. } => {
2072 let mut new_key = prefix;
2073 new_key.extend(key);
2074 *key = new_key;
2075 let was_clean = !matches!(state, ArenaSparseNodeState::Dirty);
2076 *state = ArenaSparseNodeState::Dirty;
2077 if was_clean {
2078 subtrie.num_dirty_leaves += 1;
2079 }
2080 }
2081 _ => {
2082 unreachable!("subtrie root must be a Branch or Leaf during collapse_branch")
2083 }
2084 }
2085 false
2086 }
2087 _ => unreachable!("remaining child must be Leaf, Branch, or Subtrie"),
2088 };
2089
2090 cursor.replace_head_index(arena, root, child_idx);
2092
2093 arena.remove(branch_idx);
2095 newly_dirtied_leaf
2096 }
2097
2098 fn count_leaves_and_dirty(arena: &NodeArena, idx: Index) -> (u64, u64) {
2100 match &arena[idx] {
2101 ArenaSparseNode::Leaf { state, .. } => {
2102 let dirty = matches!(state, ArenaSparseNodeState::Dirty) as u64;
2103 (1, dirty)
2104 }
2105 ArenaSparseNode::Branch(b) => {
2106 let mut leaves = 0u64;
2107 let mut dirty = 0u64;
2108 for c in &b.children {
2109 if let ArenaSparseNodeBranchChild::Revealed(child_idx) = c {
2110 let (l, d) = Self::count_leaves_and_dirty(arena, *child_idx);
2111 leaves += l;
2112 dirty += d;
2113 }
2114 }
2115 (leaves, dirty)
2116 }
2117 _ => (0, 0),
2118 }
2119 }
2120
2121 #[instrument(level = "trace", target = TRACE_TARGET, skip_all)]
2127 #[cfg(debug_assertions)]
2128 fn debug_assert_subtrie_structure(&mut self) {
2129 let mut cursor = mem::take(&mut self.buffers.cursor);
2130 cursor.reset(&self.upper_arena, self.root, Nibbles::default());
2131
2132 loop {
2133 let result = cursor.next(&mut self.upper_arena, |_, _| true);
2134 match result {
2135 NextResult::Done => break,
2136 NextResult::NonBranch | NextResult::Branch => {
2137 let head = cursor.head().expect("cursor is non-empty");
2138 let path_len = head.path.len();
2139 let node = &self.upper_arena[head.index];
2140
2141 if Self::should_be_subtrie(path_len) {
2142 debug_assert!(
2143 matches!(
2144 node,
2145 ArenaSparseNode::Subtrie(_) | ArenaSparseNode::TakenSubtrie
2146 ),
2147 "node at path_len={path_len} should be a Subtrie but is {node:?}",
2148 );
2149 } else {
2150 debug_assert!(
2151 !matches!(node, ArenaSparseNode::Subtrie(_)),
2152 "node at path_len={path_len} should NOT be a Subtrie but is",
2153 );
2154 }
2155 }
2156 }
2157 }
2158
2159 self.buffers.cursor = cursor;
2160 }
2161
2162 fn migrate_nodes(
2170 dst: &mut NodeArena,
2171 src: &mut NodeArena,
2172 src_idx: Index,
2173 dst_slot: Option<Index>,
2174 ) -> Index {
2175 let mut node = src.remove(src_idx).expect("node exists in source arena");
2176
2177 if let ArenaSparseNode::Branch(b) = &mut node {
2179 for child in &mut b.children {
2180 if let ArenaSparseNodeBranchChild::Revealed(child_idx) = child {
2181 *child_idx = Self::migrate_nodes(dst, src, *child_idx, None);
2182 }
2183 }
2184 }
2185
2186 if let Some(slot) = dst_slot {
2187 dst[slot] = node;
2188 slot
2189 } else {
2190 dst.insert(node)
2191 }
2192 }
2193
2194 fn remove_pruned_node(
2198 arena: &mut NodeArena,
2199 cursor: &ArenaCursor,
2200 idx: Index,
2201 nibble: Option<u8>,
2202 ) -> ArenaSparseNode {
2203 let path = cursor.head().expect("cursor is non-empty").path;
2204 let node = arena.remove(idx).expect("node must exist to be pruned");
2205 let rlp_node = node.state_ref().and_then(|s| s.cached_rlp_node()).cloned();
2206 trace!(
2207 target: TRACE_TARGET,
2208 ?path,
2209 variant = %AsRef::<str>::as_ref(&node),
2210 cached_rlp_node = ?rlp_node,
2211 "pruning node",
2212 );
2213
2214 if let Some(rlp_node) = rlp_node {
2215 let parent_idx = cursor.parent().expect("pruned child has parent").index;
2216 let child_nibble = nibble.expect("non-root child");
2217 let parent_branch = arena[parent_idx].branch_mut();
2218 let child_idx = BranchChildIdx::new(parent_branch.state_mask, child_nibble)
2219 .expect("child nibble not found in parent state_mask");
2220 parent_branch.children[child_idx] = ArenaSparseNodeBranchChild::Blinded(rlp_node);
2221 }
2222
2223 node
2224 }
2225
2226 #[instrument(level = "trace", target = TRACE_TARGET, skip_all)]
2235 fn reveal_node(
2236 arena: &mut NodeArena,
2237 cursor: &ArenaCursor,
2238 node: &mut ProofTrieNodeV2,
2239 find_result: SeekResult,
2240 ) -> Option<Index> {
2241 let SeekResult::Blinded = find_result else {
2242 return None;
2244 };
2245
2246 let head = cursor.head().expect("cursor is non-empty");
2247 let head_idx = head.index;
2248 let head_branch_logical_path = cursor.head_logical_branch_path(arena);
2249
2250 debug_assert_eq!(
2251 node.path.len(),
2252 head_branch_logical_path.len() + 1,
2253 "proof node path {:?} is not a direct child of branch at {:?} (expected depth {})",
2254 node.path,
2255 head_branch_logical_path,
2256 head_branch_logical_path.len() + 1,
2257 );
2258
2259 let child_nibble = node.path.get_unchecked(head_branch_logical_path.len());
2260 let head_branch = arena[head_idx].branch_ref();
2261 let dense_child_idx = BranchChildIdx::new(head_branch.state_mask, child_nibble)
2262 .expect("Blinded result but child nibble not in state_mask");
2263
2264 let cached_rlp = match &head_branch.children[dense_child_idx] {
2265 ArenaSparseNodeBranchChild::Blinded(rlp) => rlp.clone(),
2266 ArenaSparseNodeBranchChild::Revealed(_) => return None,
2267 };
2268
2269 trace!(
2270 target: TRACE_TARGET,
2271 path = ?node.path,
2272 rlp_node = ?cached_rlp,
2273 "Revealing node",
2274 );
2275
2276 let proof_node = mem::replace(node, ProofTrieNodeV2::empty());
2277 let mut arena_node = ArenaSparseNode::from_proof_node(proof_node);
2278
2279 let state = arena_node.state_mut();
2280 *state = ArenaSparseNodeState::Cached { rlp_node: cached_rlp, was_dirty: false };
2281
2282 let child_idx = arena.insert(arena_node);
2283 arena[head_idx].branch_mut().children[dense_child_idx] =
2284 ArenaSparseNodeBranchChild::Revealed(child_idx);
2285
2286 Some(child_idx)
2287 }
2288
2289 #[cfg(debug_assertions)]
2290 fn collect_reachable_nodes(
2291 arena: &NodeArena,
2292 idx: Index,
2293 reachable: &mut alloy_primitives::map::HashSet<Index>,
2294 ) {
2295 if !reachable.insert(idx) {
2296 return;
2297 }
2298 if let ArenaSparseNode::Branch(b) = &arena[idx] {
2299 for child in &b.children {
2300 if let ArenaSparseNodeBranchChild::Revealed(child_idx) = child {
2301 Self::collect_reachable_nodes(arena, *child_idx, reachable);
2302 }
2303 }
2304 }
2305 }
2306
2307 #[cfg(debug_assertions)]
2308 fn assert_no_orphaned_nodes(arena: &NodeArena, root: Index, label: &str) {
2309 let mut reachable = alloy_primitives::map::HashSet::default();
2310 Self::collect_reachable_nodes(arena, root, &mut reachable);
2311 let all_indices: alloy_primitives::map::HashSet<Index> =
2312 arena.iter().map(|(idx, _)| idx).collect();
2313 let orphaned: Vec<_> = all_indices.difference(&reachable).collect();
2314 debug_assert!(
2315 orphaned.is_empty(),
2316 "{label} has {} orphaned node(s): {orphaned:?}",
2317 orphaned.len(),
2318 );
2319 }
2320}
2321
2322#[cfg(debug_assertions)]
2323impl Drop for ArenaParallelSparseTrie {
2324 fn drop(&mut self) {
2325 Self::assert_no_orphaned_nodes(&self.upper_arena, self.root, "upper arena");
2326
2327 for (_, node) in &self.upper_arena {
2328 if let Some(subtrie) = node.as_subtrie() {
2329 Self::assert_no_orphaned_nodes(
2330 &subtrie.arena,
2331 subtrie.root,
2332 &alloc::format!("subtrie {:?}", subtrie.path),
2333 );
2334 }
2335 }
2336 }
2337}
2338
2339impl Default for ArenaParallelSparseTrie {
2340 fn default() -> Self {
2341 let mut upper_arena = SlotMap::new();
2342 let root = upper_arena.insert(ArenaSparseNode::EmptyRoot);
2343 Self {
2344 upper_arena,
2345 root,
2346 buffers: ArenaTrieBuffers::default(),
2347 parallelism_thresholds: ArenaParallelismThresholds::default(),
2348 #[cfg(feature = "trie-debug")]
2349 debug_recorder: Default::default(),
2350 }
2351 }
2352}
2353
2354impl ArenaParallelSparseTrie {
2355 fn update_upper_subtrie(&mut self, head_idx: Index) {
2357 let ArenaSparseNode::Subtrie(subtrie) = &mut self.upper_arena[head_idx] else {
2358 unreachable!()
2359 };
2360
2361 if !subtrie.arena[subtrie.root].is_cached() {
2362 subtrie.update_cached_rlp();
2363 }
2364
2365 Self::merge_subtrie_updates(&mut self.buffers.updates, &mut subtrie.buffers.updates);
2366 Self::merge_subtrie_changed_paths(
2367 &mut self.buffers.changed_paths,
2368 &mut subtrie.buffers.changed_paths,
2369 );
2370 }
2371}
2372
2373impl SparseTrie for ArenaParallelSparseTrie {
2374 #[instrument(level = "trace", target = TRACE_TARGET, skip_all)]
2375 fn set_root(
2376 &mut self,
2377 root: TrieNodeV2,
2378 masks: Option<BranchNodeMasks>,
2379 retain_updates: bool,
2380 ) -> SparseTrieResult<()> {
2381 #[cfg(feature = "trie-debug")]
2382 self.debug_recorder.record(RecordedOp::SetRoot {
2383 node: ProofTrieNodeRecord::from_proof_trie_node_v2(&ProofTrieNodeV2 {
2384 path: Nibbles::default(),
2385 node: root.clone(),
2386 masks,
2387 }),
2388 });
2389
2390 debug_assert!(
2391 matches!(self.upper_arena[self.root], ArenaSparseNode::EmptyRoot),
2392 "set_root called on a trie that already has revealed nodes"
2393 );
2394
2395 self.set_updates(retain_updates);
2396
2397 match root {
2398 TrieNodeV2::EmptyRoot => {
2399 trace!(target: TRACE_TARGET, "Setting empty root");
2400 }
2401 TrieNodeV2::Leaf(leaf) => {
2402 trace!(target: TRACE_TARGET, key = ?leaf.key, "Setting leaf root");
2403 self.upper_arena[self.root] = ArenaSparseNode::Leaf {
2404 state: ArenaSparseNodeState::Revealed,
2405 key: leaf.key,
2406 value: leaf.value,
2407 };
2408 }
2409 TrieNodeV2::Branch(branch) => {
2410 trace!(target: TRACE_TARGET, state_mask = ?branch.state_mask, num_children = branch.state_mask.count_bits(), "Setting branch root");
2411 let mut children = SmallVec::with_capacity(branch.state_mask.count_bits() as usize);
2412 for (stack_ptr, _nibble) in branch.state_mask.iter().enumerate() {
2413 children
2414 .push(ArenaSparseNodeBranchChild::Blinded(branch.stack[stack_ptr].clone()));
2415 }
2416
2417 self.upper_arena[self.root] = ArenaSparseNode::Branch(ArenaSparseNodeBranch {
2418 state: ArenaSparseNodeState::Revealed,
2419 children,
2420 state_mask: branch.state_mask,
2421 short_key: branch.key,
2422 branch_masks: masks.unwrap_or_default(),
2423 });
2424 }
2425 TrieNodeV2::Extension(_) => {
2426 panic!("set_root does not support Extension nodes; extensions are represented as branches with a short_key")
2427 }
2428 }
2429
2430 Ok(())
2431 }
2432
2433 fn set_updates(&mut self, retain_updates: bool) {
2434 if retain_updates {
2435 self.buffers.updates.get_or_insert_with(SparseTrieUpdates::default).clear();
2436 } else {
2437 self.buffers.updates = None;
2438 }
2439 }
2440
2441 fn set_changed_paths(&mut self, retain_changed_paths: bool) {
2442 Self::set_changed_paths(self, retain_changed_paths);
2443 }
2444
2445 #[instrument(level = "trace", target = TRACE_TARGET, skip_all, fields(num_nodes = nodes.len()))]
2446 fn reveal_nodes(&mut self, nodes: &mut [ProofTrieNodeV2]) -> SparseTrieResult<()> {
2447 if nodes.is_empty() {
2448 return Ok(());
2449 }
2450
2451 #[cfg(feature = "trie-debug")]
2452 self.debug_recorder.record(RecordedOp::RevealNodes {
2453 nodes: nodes.iter().map(ProofTrieNodeRecord::from_proof_trie_node_v2).collect(),
2454 });
2455
2456 if matches!(self.upper_arena[self.root], ArenaSparseNode::EmptyRoot) {
2457 trace!(target: TRACE_TARGET, "Skipping reveal_nodes on empty root");
2458 return Ok(());
2459 }
2460
2461 nodes.sort_unstable_by_key(|n| n.path);
2463
2464 let threshold = self.parallelism_thresholds.min_revealed_nodes;
2465
2466 let mut cursor = mem::take(&mut self.buffers.cursor);
2468 cursor.reset(&self.upper_arena, self.root, Nibbles::default());
2469
2470 let mut node_idx = if nodes[0].path.is_empty() { 1 } else { 0 };
2472
2473 let mut taken: Vec<(Index, Box<ArenaSparseSubtrie>, Vec<ProofTrieNodeV2>)> = Vec::new();
2477
2478 while node_idx < nodes.len() {
2479 let find_result = cursor.seek(&mut self.upper_arena, &nodes[node_idx].path);
2480
2481 match find_result {
2482 SeekResult::RevealedLeaf => {
2483 trace!(target: TRACE_TARGET, path = ?nodes[node_idx].path, "Skipping reveal: leaf head");
2484 node_idx += 1;
2485 }
2486 SeekResult::Blinded => {
2487 let child_path = nodes[node_idx].path;
2489 let child_idx = Self::reveal_node(
2490 &mut self.upper_arena,
2491 &cursor,
2492 &mut nodes[node_idx],
2493 SeekResult::Blinded,
2494 );
2495 node_idx += 1;
2496
2497 if let Some(child_idx) = child_idx {
2498 self.maybe_wrap_in_subtrie(child_idx, &child_path);
2499 }
2500 }
2501 SeekResult::RevealedSubtrie => {
2502 let subtrie_entry = cursor.head().expect("cursor is non-empty");
2503 let child_idx = subtrie_entry.index;
2504 let prefix = subtrie_entry.path;
2505
2506 let subtrie_start = node_idx;
2507 while node_idx < nodes.len() && nodes[node_idx].path.starts_with(&prefix) {
2508 node_idx += 1;
2509 }
2510 let num_subtrie_nodes = node_idx - subtrie_start;
2511
2512 if num_subtrie_nodes >= threshold {
2513 trace!(target: TRACE_TARGET, ?prefix, num_subtrie_nodes, "Taking subtrie for parallel reveal");
2515 let ArenaSparseNode::Subtrie(subtrie) = mem::replace(
2516 &mut self.upper_arena[child_idx],
2517 ArenaSparseNode::TakenSubtrie,
2518 ) else {
2519 unreachable!("RevealedSubtrie must point to a Subtrie node")
2520 };
2521 let node_vec: Vec<ProofTrieNodeV2> = (subtrie_start..node_idx)
2522 .map(|i| mem::replace(&mut nodes[i], ProofTrieNodeV2::empty()))
2523 .collect();
2524 taken.push((child_idx, subtrie, node_vec));
2525 } else {
2526 trace!(target: TRACE_TARGET, ?prefix, num_subtrie_nodes, "Revealing subtrie inline");
2528 let ArenaSparseNode::Subtrie(subtrie) = &mut self.upper_arena[child_idx]
2529 else {
2530 unreachable!("RevealedSubtrie must point to a Subtrie node")
2531 };
2532 let mut subtrie_nodes: Vec<ProofTrieNodeV2> = (subtrie_start..node_idx)
2533 .map(|i| mem::replace(&mut nodes[i], ProofTrieNodeV2::empty()))
2534 .collect();
2535 subtrie.reveal_nodes(&mut subtrie_nodes)?;
2536 }
2537 }
2538 _ => {
2539 trace!(target: TRACE_TARGET, path = ?nodes[node_idx].path, ?find_result, "Skipping reveal: no blinded child");
2540 node_idx += 1;
2541 }
2542 }
2543 }
2544
2545 cursor.drain(&mut self.upper_arena);
2547 self.buffers.cursor = cursor;
2548
2549 if taken.is_empty() {
2550 return Ok(());
2551 }
2552
2553 if taken.len() == 1 {
2555 let (_, subtrie, node_vec) = &mut taken[0];
2556 subtrie.reveal_nodes(node_vec)?;
2557 } else {
2558 use rayon::iter::{IntoParallelRefMutIterator, ParallelIterator};
2559
2560 let parent_span = tracing::Span::current();
2561 let results: Vec<SparseTrieResult<()>> = taken
2562 .par_iter_mut()
2563 .map(|(_, subtrie, node_vec)| {
2564 let _guard = parent_span.enter();
2565 subtrie.reveal_nodes(node_vec)
2566 })
2567 .collect();
2568
2569 if let Some(err) = results.into_iter().find(|r| r.is_err()) {
2570 for (idx, subtrie, _) in taken {
2572 self.upper_arena[idx] = ArenaSparseNode::Subtrie(subtrie);
2573 }
2574 return err;
2575 }
2576 }
2577
2578 for (idx, subtrie, _) in taken {
2580 self.upper_arena[idx] = ArenaSparseNode::Subtrie(subtrie);
2581 }
2582
2583 #[cfg(debug_assertions)]
2584 self.debug_assert_subtrie_structure();
2585
2586 Ok(())
2587 }
2588
2589 #[instrument(level = "trace", target = TRACE_TARGET, skip_all, ret)]
2590 fn root(&mut self) -> B256 {
2591 #[cfg(feature = "trie-debug")]
2592 self.debug_recorder.record(RecordedOp::Root);
2593
2594 self.update_subtrie_hashes();
2595
2596 let rlp_node = Self::update_cached_rlp(
2597 &mut self.upper_arena,
2598 self.root,
2599 Nibbles::default(),
2600 &mut self.buffers,
2601 );
2602
2603 rlp_node.as_hash().expect("root RlpNode must be a hash")
2604 }
2605
2606 fn is_root_cached(&self) -> bool {
2607 self.upper_arena[self.root].is_cached()
2608 }
2609
2610 #[instrument(level = "trace", target = TRACE_TARGET, skip_all)]
2611 fn update_subtrie_hashes(&mut self) {
2612 #[cfg(feature = "trie-debug")]
2613 self.debug_recorder.record(RecordedOp::UpdateSubtrieHashes);
2614
2615 trace!(target: TRACE_TARGET, "Updating subtrie hashes");
2616
2617 if !matches!(&self.upper_arena[self.root], ArenaSparseNode::Branch(_)) {
2619 return;
2620 }
2621
2622 let mut total_dirty_leaves: u64 = 0;
2624 let mut taken: Vec<(Index, Box<ArenaSparseSubtrie>)> = Vec::new();
2625 for (idx, node) in &mut self.upper_arena {
2626 let ArenaSparseNode::Subtrie(s) = node else { continue };
2627 if s.num_dirty_leaves == 0 {
2628 continue;
2629 }
2630 total_dirty_leaves += s.num_dirty_leaves;
2631 let ArenaSparseNode::Subtrie(subtrie) =
2632 mem::replace(node, ArenaSparseNode::TakenSubtrie)
2633 else {
2634 unreachable!()
2635 };
2636 taken.push((idx, subtrie));
2637 }
2638
2639 if !taken.is_empty() {
2641 if taken.len() == 1 || total_dirty_leaves < self.parallelism_thresholds.min_dirty_leaves
2642 {
2643 for (_, subtrie) in &mut taken {
2644 subtrie.update_cached_rlp();
2645 }
2646 } else {
2647 use rayon::iter::{IntoParallelIterator, ParallelIterator};
2648
2649 let parent_span = tracing::Span::current();
2650 taken = taken
2651 .into_par_iter()
2652 .map(|(idx, mut subtrie)| {
2653 let _guard = parent_span.enter();
2654 subtrie.update_cached_rlp();
2655 (idx, subtrie)
2656 })
2657 .collect();
2658 }
2659 }
2660
2661 if taken.is_empty() && self.upper_arena[self.root].is_cached() {
2664 return;
2665 }
2666
2667 taken.sort_unstable_by_key(|(_, b)| Reverse(b.path));
2671
2672 self.buffers.cursor.reset(&self.upper_arena, self.root, Nibbles::default());
2673
2674 loop {
2675 let result = self.buffers.cursor.next(&mut self.upper_arena, |_, child| match child {
2676 ArenaSparseNode::Branch(_) | ArenaSparseNode::Subtrie(_) => !child.is_cached(),
2677 ArenaSparseNode::TakenSubtrie => true,
2678 _ => false,
2679 });
2680
2681 match result {
2682 NextResult::Done => break,
2683 NextResult::Branch => continue,
2684 NextResult::NonBranch => {}
2685 }
2686
2687 let head_idx = self.buffers.cursor.head().expect("cursor is non-empty").index;
2689
2690 if matches!(&self.upper_arena[head_idx], ArenaSparseNode::TakenSubtrie) {
2691 let (_, subtrie) = taken.pop().expect("taken subtries must not be exhausted");
2692 debug_assert_eq!(
2693 subtrie.path,
2694 self.buffers.cursor.head().expect("cursor is non-empty").path,
2695 "taken subtrie path mismatch",
2696 );
2697 self.upper_arena[head_idx] = ArenaSparseNode::Subtrie(subtrie);
2698 }
2699
2700 self.update_upper_subtrie(head_idx);
2701 }
2702 }
2703
2704 fn get_leaf_value(&self, full_path: &Nibbles) -> Option<&Vec<u8>> {
2705 Self::get_leaf_value_in_arena(&self.upper_arena, self.root, full_path, 0)
2706 }
2707
2708 fn find_leaf(
2709 &self,
2710 full_path: &Nibbles,
2711 expected_value: Option<&Vec<u8>>,
2712 ) -> Result<LeafLookup, LeafLookupError> {
2713 Self::find_leaf_in_arena(&self.upper_arena, self.root, full_path, 0, expected_value)
2714 }
2715
2716 fn updates_ref(&self) -> Cow<'_, SparseTrieUpdates> {
2717 self.buffers
2718 .updates
2719 .as_ref()
2720 .map_or(Cow::Owned(SparseTrieUpdates::default()), Cow::Borrowed)
2721 }
2722
2723 fn take_updates(&mut self) -> SparseTrieUpdates {
2724 match self.buffers.updates.take() {
2725 Some(updates) => {
2726 self.buffers.updates = Some(SparseTrieUpdates::with_capacity(
2727 updates.updated_nodes.len(),
2728 updates.removed_nodes.len(),
2729 ));
2730 updates
2731 }
2732 None => SparseTrieUpdates::default(),
2733 }
2734 }
2735
2736 fn take_changed_paths(&mut self) -> PrefixSetMut {
2737 Self::take_changed_paths(self)
2738 }
2739
2740 #[instrument(level = "trace", target = TRACE_TARGET, skip_all)]
2741 fn wipe(&mut self) {
2742 trace!(target: TRACE_TARGET, "Wiping arena trie");
2743 self.clear();
2744 self.buffers.updates = self.buffers.updates.is_some().then(SparseTrieUpdates::wiped);
2745 }
2746
2747 #[instrument(level = "trace", target = TRACE_TARGET, skip_all)]
2748 fn clear(&mut self) {
2749 #[cfg(feature = "trie-debug")]
2750 self.debug_recorder.reset();
2751
2752 self.upper_arena = SlotMap::new();
2753 self.root = self.upper_arena.insert(ArenaSparseNode::EmptyRoot);
2754 self.buffers.clear();
2755 }
2756
2757 fn size_hint(&self) -> usize {
2758 self.upper_arena
2759 .iter()
2760 .map(|(_, node)| match node {
2761 ArenaSparseNode::Subtrie(s) => s.num_leaves as usize,
2762 ArenaSparseNode::Leaf { .. } => 1,
2763 _ => 0,
2764 })
2765 .sum()
2766 }
2767
2768 fn memory_size(&self) -> usize {
2769 let slot_size = slotmap_slot_size::<ArenaSparseNode>();
2770
2771 let upper = self.upper_arena.capacity() * slot_size;
2773
2774 let subtrie_size: usize = self
2776 .upper_arena
2777 .iter()
2778 .filter_map(|(_, node)| match node {
2779 ArenaSparseNode::Subtrie(s) => Some(s.memory_size()),
2780 _ => None,
2781 })
2782 .sum();
2783
2784 let buffer_size = self.buffers.rlp_buf.capacity() +
2786 self.buffers.rlp_node_buf.capacity() * core::mem::size_of::<RlpNode>();
2787
2788 upper + subtrie_size + buffer_size
2789 }
2790
2791 #[instrument(
2792 level = "trace",
2793 target = TRACE_TARGET,
2794 skip_all,
2795 fields(num_retained_leaves = retained_leaves.len()),
2796 )]
2797 fn prune(&mut self, retained_leaves: &[Nibbles]) -> usize {
2798 if !matches!(&self.upper_arena[self.root], ArenaSparseNode::Branch(_)) {
2800 return 0;
2801 }
2802
2803 debug_assert!(
2804 retained_leaves.windows(2).all(|w| w[0] <= w[1]),
2805 "retained_leaves must be sorted"
2806 );
2807
2808 let threshold = self.parallelism_thresholds.min_leaves_for_prune;
2809
2810 let mut cursor = mem::take(&mut self.buffers.cursor);
2811 cursor.reset(&self.upper_arena, self.root, Nibbles::default());
2812
2813 let mut taken: Vec<(Index, Box<ArenaSparseSubtrie>, core::ops::Range<usize>)> = Vec::new();
2815
2816 let mut pruned = 0;
2817 let mut retained_idx = 0;
2818
2819 loop {
2820 let result = cursor.next(&mut self.upper_arena, |_, child| {
2821 matches!(
2822 child,
2823 ArenaSparseNode::Branch(_) |
2824 ArenaSparseNode::Subtrie(_) |
2825 ArenaSparseNode::Leaf { .. }
2826 )
2827 });
2828
2829 match result {
2830 NextResult::Done => break,
2831 NextResult::NonBranch | NextResult::Branch => {}
2832 }
2833
2834 let head = cursor.head().expect("cursor is non-empty");
2835 let head_idx = head.index;
2836 let head_path = head.path;
2837
2838 match &self.upper_arena[head_idx] {
2839 ArenaSparseNode::Branch(_) | ArenaSparseNode::Leaf { .. } => {
2840 if cursor.depth() == 0 {
2842 continue;
2843 }
2844
2845 let range = prefix_range(retained_leaves, 0, &head_path);
2846 if !range.is_empty() {
2847 continue;
2848 }
2849
2850 Self::remove_pruned_node(
2851 &mut self.upper_arena,
2852 &cursor,
2853 head_idx,
2854 head_path.last(),
2855 );
2856 pruned += 1;
2857 }
2858 ArenaSparseNode::Subtrie(_) => {
2859 let subtrie_range = prefix_range(retained_leaves, retained_idx, &head_path);
2860 retained_idx = subtrie_range.end;
2861
2862 if subtrie_range.is_empty() {
2863 let removed = Self::remove_pruned_node(
2864 &mut self.upper_arena,
2865 &cursor,
2866 head_idx,
2867 head_path.last(),
2868 );
2869 let ArenaSparseNode::Subtrie(s) = &removed else { unreachable!() };
2870 pruned += s.num_leaves as usize;
2871 self.recycle_subtrie(removed);
2872 continue;
2873 }
2874
2875 let ArenaSparseNode::Subtrie(subtrie) = &self.upper_arena[head_idx] else {
2876 unreachable!()
2877 };
2878 if subtrie.num_leaves >= threshold {
2879 let ArenaSparseNode::Subtrie(subtrie) = mem::replace(
2880 &mut self.upper_arena[head_idx],
2881 ArenaSparseNode::TakenSubtrie,
2882 ) else {
2883 unreachable!()
2884 };
2885 taken.push((head_idx, subtrie, subtrie_range));
2886 } else {
2887 let ArenaSparseNode::Subtrie(subtrie) = &mut self.upper_arena[head_idx]
2888 else {
2889 unreachable!()
2890 };
2891 pruned += subtrie.prune(retained_leaves[subtrie_range].iter());
2892 }
2893 }
2894 _ => unreachable!("NonBranch in prune walk must be Subtrie, Leaf, or Branch"),
2895 }
2896 }
2897
2898 self.buffers.cursor = cursor;
2899
2900 if !taken.is_empty() {
2901 if taken.len() == 1 {
2903 let (_, ref mut subtrie, ref range) = taken[0];
2904 pruned += subtrie.prune(retained_leaves[range.clone()].iter());
2905 } else {
2906 use rayon::iter::{IntoParallelRefMutIterator, ParallelIterator};
2907
2908 let parent_span = tracing::Span::current();
2909 pruned += taken
2910 .par_iter_mut()
2911 .map(|(_, subtrie, range)| {
2912 let _guard = parent_span.enter();
2913 let _span = tracing::trace_span!(
2914 target: TRACE_TARGET,
2915 "subtrie_prune",
2916 subtrie = ?subtrie.path,
2917 )
2918 .entered();
2919
2920 subtrie.prune(retained_leaves[range.clone()].iter())
2921 })
2922 .sum::<usize>();
2923 }
2924
2925 for (child_idx, subtrie, _) in taken {
2927 self.upper_arena[child_idx] = ArenaSparseNode::Subtrie(subtrie);
2928 }
2929 }
2930
2931 if pruned > 0 {
2932 compact_arena(&mut self.upper_arena, &mut self.root);
2933 }
2934
2935 #[cfg(feature = "trie-debug")]
2936 self.record_initial_state();
2937
2938 pruned
2939 }
2940
2941 #[instrument(
2942 level = "trace",
2943 target = TRACE_TARGET,
2944 skip_all,
2945 fields(num_updates = updates.len()),
2946 )]
2947 fn update_leaves(
2948 &mut self,
2949 updates: &mut B256Map<LeafUpdate>,
2950 mut proof_required_fn: impl FnMut(B256, u8),
2951 ) -> SparseTrieResult<()> {
2952 if updates.is_empty() {
2953 return Ok(());
2954 }
2955
2956 #[cfg(feature = "trie-debug")]
2957 let recorded_updates: Vec<_> =
2958 updates.iter().map(|(k, v)| (*k, LeafUpdateRecord::from(v))).collect();
2959 #[cfg(feature = "trie-debug")]
2960 let mut recorded_proof_targets: Vec<(B256, u8)> = Vec::new();
2961
2962 let mut sorted: Vec<_> =
2964 updates.drain().map(|(key, update)| (key, Nibbles::unpack(key), update)).collect();
2965 sorted.sort_unstable_by_key(|entry| entry.1);
2966
2967 let threshold = self.parallelism_thresholds.min_updates;
2968 let parallelize_distributed_updates = sorted.len() >= threshold.saturating_mul(4);
2969
2970 let mut cursor = mem::take(&mut self.buffers.cursor);
2971 cursor.reset(&self.upper_arena, self.root, Nibbles::default());
2972
2973 let mut taken: Vec<(Index, Box<ArenaSparseSubtrie>, core::ops::Range<usize>)> = Vec::new();
2975
2976 let mut update_idx = 0;
2977 while update_idx < sorted.len() {
2978 let (key, ref full_path, ref update) = sorted[update_idx];
2979
2980 let find_result = cursor.seek(&mut self.upper_arena, full_path);
2981
2982 match find_result {
2983 SeekResult::Blinded => {
2985 let logical_len = cursor.head_logical_branch_path_len(&self.upper_arena);
2986 let min_len = (logical_len as u8 + 1).min(64);
2987 trace!(target: TRACE_TARGET, ?key, min_len, "Update hit blinded node, requesting proof");
2988 proof_required_fn(key, min_len);
2989 #[cfg(feature = "trie-debug")]
2990 recorded_proof_targets.push((key, min_len));
2991 updates.insert(key, update.clone());
2992 }
2993 SeekResult::RevealedSubtrie => {
2995 let subtrie_entry = cursor.head().expect("cursor is non-empty");
2996 let child_idx = subtrie_entry.index;
2997 let subtrie_root_path = subtrie_entry.path;
2998
2999 let subtrie_start = update_idx;
3000 while update_idx < sorted.len() &&
3001 sorted[update_idx].1.starts_with(&subtrie_root_path)
3002 {
3003 update_idx += 1;
3004 }
3005
3006 let subtrie_updates = &sorted[subtrie_start..update_idx];
3007
3008 if let Some(proof) = Self::check_subtrie_collapse_needs_proof(
3012 &self.upper_arena,
3013 &cursor,
3014 subtrie_updates,
3015 ) {
3016 trace!(target: TRACE_TARGET, proof_key = ?proof.key, proof_min_len = proof.min_len, "Subtrie collapse would need blinded sibling, requesting proof");
3017 proof_required_fn(proof.key, proof.min_len);
3018 #[cfg(feature = "trie-debug")]
3019 recorded_proof_targets.push((proof.key, proof.min_len));
3020 for &(key, _, ref update) in subtrie_updates {
3021 updates.insert(key, update.clone());
3022 }
3023 continue;
3025 }
3026
3027 let num_subtrie_updates = update_idx - subtrie_start;
3028
3029 let all_removals = subtrie_updates
3033 .iter()
3034 .filter(|(_, _, u)| matches!(u, LeafUpdate::Changed(_)))
3038 .all(|(_, _, u)| matches!(u, LeafUpdate::Changed(v) if v.is_empty()));
3039 let subtrie_num_leaves = match &self.upper_arena[child_idx] {
3040 ArenaSparseNode::Subtrie(s) => s.num_leaves,
3041 _ => 0,
3042 };
3043 let might_empty_subtrie =
3044 all_removals && num_subtrie_updates as u64 >= subtrie_num_leaves;
3045
3046 if (num_subtrie_updates >= threshold || parallelize_distributed_updates) &&
3047 !might_empty_subtrie
3048 {
3049 trace!(target: TRACE_TARGET, ?subtrie_root_path, num_subtrie_updates, "Taking subtrie for parallel update");
3051 let ArenaSparseNode::Subtrie(subtrie) = mem::replace(
3052 &mut self.upper_arena[child_idx],
3053 ArenaSparseNode::TakenSubtrie,
3054 ) else {
3055 unreachable!()
3056 };
3057 taken.push((child_idx, subtrie, subtrie_start..update_idx));
3058 } else {
3059 trace!(target: TRACE_TARGET, ?subtrie_root_path, num_subtrie_updates, "Updating subtrie inline");
3061 let ArenaSparseNode::Subtrie(subtrie) = &mut self.upper_arena[child_idx]
3062 else {
3063 unreachable!()
3064 };
3065
3066 subtrie.update_leaves(subtrie_updates);
3067
3068 for (target_idx, proof) in subtrie.required_proofs.drain(..) {
3069 proof_required_fn(proof.key, proof.min_len);
3070 #[cfg(feature = "trie-debug")]
3071 recorded_proof_targets.push((proof.key, proof.min_len));
3072 let (key, _, ref update) = subtrie_updates[target_idx];
3073 updates.insert(key, update.clone());
3074 }
3075
3076 self.maybe_unwrap_subtrie(&mut cursor);
3078 }
3079
3080 continue;
3082 }
3083 find_result @ (SeekResult::EmptyRoot |
3085 SeekResult::RevealedLeaf |
3086 SeekResult::Diverged |
3087 SeekResult::NoChild { .. }) => match update {
3088 LeafUpdate::Changed(v) if !v.is_empty() => {
3089 let (result, _deltas) = Self::upsert_leaf(
3090 &mut self.upper_arena,
3091 &mut cursor,
3092 &mut self.root,
3093 full_path,
3094 v,
3095 find_result,
3096 );
3097 match result {
3098 UpsertLeafResult::NewChild => {
3099 let head = cursor.head().expect("cursor is non-empty");
3100 if Self::should_be_subtrie(head.path.len()) {
3101 self.maybe_wrap_in_subtrie(head.index, &head.path);
3104 } else {
3105 self.maybe_wrap_branch_children(&cursor);
3109 }
3110 }
3111 UpsertLeafResult::NewLeaf => {
3112 self.maybe_wrap_branch_children(&cursor);
3115 }
3116 UpsertLeafResult::Updated => {}
3117 }
3118 }
3119 LeafUpdate::Changed(_) => {
3120 let (result, _deltas) = Self::remove_leaf(
3121 &mut self.upper_arena,
3122 &mut cursor,
3123 &mut self.root,
3124 key,
3125 full_path,
3126 find_result,
3127 &mut self.buffers.updates,
3128 );
3129 match result {
3130 RemoveLeafResult::NeedsProof { key, proof_key, min_len } => {
3131 proof_required_fn(proof_key, min_len);
3132 #[cfg(feature = "trie-debug")]
3133 recorded_proof_targets.push((proof_key, min_len));
3134 let update =
3135 mem::replace(&mut sorted[update_idx].2, LeafUpdate::Touched);
3136 updates.insert(key, update);
3137 }
3138 RemoveLeafResult::Removed => {
3139 self.maybe_collapse_or_remove_branch(&mut cursor);
3148 let head =
3149 cursor.head().expect("cursor always has root after collapse");
3150 self.maybe_wrap_in_subtrie(head.index, &head.path);
3151 }
3152 RemoveLeafResult::NotFound => {}
3153 }
3154 }
3155 LeafUpdate::Touched => {}
3156 },
3157 }
3158
3159 update_idx += 1;
3160 }
3161
3162 cursor.drain(&mut self.upper_arena);
3164 self.buffers.cursor = cursor;
3165
3166 if taken.is_empty() {
3167 #[cfg(debug_assertions)]
3168 self.debug_assert_subtrie_structure();
3169
3170 #[cfg(feature = "trie-debug")]
3171 self.debug_recorder.record(RecordedOp::UpdateLeaves {
3172 updates: recorded_updates,
3173 proof_targets: recorded_proof_targets,
3174 });
3175
3176 return Ok(());
3177 }
3178
3179 if taken.len() == 1 {
3181 let (_, ref mut subtrie, ref range) = taken[0];
3182 subtrie.update_leaves(&sorted[range.clone()]);
3183 } else {
3184 use rayon::iter::{IntoParallelRefMutIterator, ParallelIterator};
3185
3186 let parent_span = tracing::Span::current();
3187 taken.par_iter_mut().for_each(|(_, subtrie, range)| {
3188 let _guard = parent_span.enter();
3189 subtrie.update_leaves(&sorted[range.clone()]);
3190 });
3191 }
3192
3193 let taken_paths: Vec<Nibbles> = taken.iter().map(|(_, s, _)| s.path).collect();
3196 for (child_idx, mut subtrie, range) in taken {
3197 let subtrie_updates = &sorted[range];
3198 for (target_idx, proof) in subtrie.required_proofs.drain(..) {
3199 proof_required_fn(proof.key, proof.min_len);
3200 #[cfg(feature = "trie-debug")]
3201 recorded_proof_targets.push((proof.key, proof.min_len));
3202 let (key, _, ref update) = subtrie_updates[target_idx];
3203 updates.insert(key, update.clone());
3204 }
3205
3206 self.upper_arena[child_idx] = ArenaSparseNode::Subtrie(subtrie);
3208 }
3209
3210 {
3216 let mut cursor = mem::take(&mut self.buffers.cursor);
3217 cursor.reset(&self.upper_arena, self.root, Nibbles::default());
3218
3219 for path in &taken_paths {
3220 let find_result = cursor.seek(&mut self.upper_arena, path);
3221 match find_result {
3222 SeekResult::RevealedSubtrie => {
3223 debug_assert!(
3224 {
3225 let head_idx = cursor.head().expect("cursor is non-empty").index;
3226 !matches!(
3227 &self.upper_arena[head_idx],
3228 ArenaSparseNode::Subtrie(s) if matches!(s.arena[s.root], ArenaSparseNode::EmptyRoot)
3229 )
3230 },
3231 "taken subtrie became EmptyRoot — should have been forced inline"
3232 );
3233
3234 cursor.pop(&mut self.upper_arena);
3235
3236 self.maybe_collapse_or_remove_branch(&mut cursor);
3240 }
3241 _ => {
3242 }
3245 }
3246 }
3247
3248 cursor.drain(&mut self.upper_arena);
3249 self.buffers.cursor = cursor;
3250 }
3251
3252 #[cfg(debug_assertions)]
3253 self.debug_assert_subtrie_structure();
3254
3255 #[cfg(feature = "trie-debug")]
3256 self.debug_recorder.record(RecordedOp::UpdateLeaves {
3257 updates: recorded_updates,
3258 proof_targets: recorded_proof_targets,
3259 });
3260
3261 Ok(())
3262 }
3263
3264 #[cfg(feature = "trie-debug")]
3265 fn take_debug_recorder(&mut self) -> TrieDebugRecorder {
3266 core::mem::take(&mut self.debug_recorder)
3267 }
3268}
3269
3270#[cfg(test)]
3271mod tests {
3272 use super::TRACE_TARGET;
3273 use crate::{ArenaParallelSparseTrie, ArenaParallelismThresholds, LeafUpdate, SparseTrie};
3274 use alloy_primitives::{map::B256Map, B256, U256};
3275 use rand::{seq::SliceRandom, Rng, SeedableRng};
3276 use reth_trie::test_utils::TrieTestHarness;
3277 use reth_trie_common::{Nibbles, ProofV2Target};
3278 use std::collections::BTreeMap;
3279 use tracing::{info, trace};
3280
3281 struct ArenaTrieTestHarness {
3286 inner: TrieTestHarness,
3288 }
3289
3290 impl std::ops::Deref for ArenaTrieTestHarness {
3291 type Target = TrieTestHarness;
3292 fn deref(&self) -> &Self::Target {
3293 &self.inner
3294 }
3295 }
3296
3297 impl std::ops::DerefMut for ArenaTrieTestHarness {
3298 fn deref_mut(&mut self) -> &mut Self::Target {
3299 &mut self.inner
3300 }
3301 }
3302
3303 impl ArenaTrieTestHarness {
3304 fn new(storage: BTreeMap<B256, U256>) -> Self {
3306 Self { inner: TrieTestHarness::new(storage) }
3307 }
3308
3309 fn assert_changes(
3313 &self,
3314 apst: &mut ArenaParallelSparseTrie,
3315 changes: BTreeMap<B256, U256>,
3316 ) {
3317 let (expected_root, mut expected_trie_updates) = if changes.is_empty() {
3319 (self.original_root(), Default::default())
3320 } else {
3321 self.get_root_with_updates(&changes)
3322 };
3323
3324 self.minimize_trie_updates(&mut expected_trie_updates);
3325
3326 let mut leaf_updates: B256Map<LeafUpdate> = changes
3329 .iter()
3330 .map(|(&slot, &value)| {
3331 let rlp_value = if value == U256::ZERO {
3332 Vec::new()
3333 } else {
3334 alloy_rlp::encode_fixed_size(&value).to_vec()
3335 };
3336 (slot, LeafUpdate::Changed(rlp_value))
3337 })
3338 .collect();
3339
3340 loop {
3343 let mut targets: Vec<ProofV2Target> = Vec::new();
3344 apst.update_leaves(&mut leaf_updates, |key, min_len| {
3345 targets.push(ProofV2Target::new(key).with_min_len(min_len));
3346 })
3347 .expect("update_leaves should succeed");
3348
3349 if targets.is_empty() {
3350 break;
3351 }
3352
3353 let (mut proof_nodes, _) = self.proof_v2(&mut targets);
3354 apst.reveal_nodes(&mut proof_nodes).expect("reveal_nodes should succeed");
3355 }
3356
3357 let actual_root = apst.root();
3359 let mut actual_updates = apst.take_updates();
3360
3361 actual_updates.updated_nodes.retain(|path, node| {
3364 self.storage_trie_updates().storage_nodes.get(path) != Some(node)
3365 });
3366 actual_updates
3367 .removed_nodes
3368 .retain(|path| self.storage_trie_updates().storage_nodes.contains_key(path));
3369
3370 pretty_assertions::assert_eq!(
3371 expected_trie_updates.storage_nodes.into_iter().collect::<Vec<_>>().sort(),
3372 actual_updates.updated_nodes.into_iter().collect::<Vec<_>>().sort(),
3373 "updated nodes mismatch"
3374 );
3375 pretty_assertions::assert_eq!(
3376 expected_trie_updates.removed_nodes.into_iter().collect::<Vec<_>>().sort(),
3377 actual_updates.removed_nodes.into_iter().collect::<Vec<_>>().sort(),
3378 "removed nodes mismatch"
3379 );
3380 assert_eq!(expected_root, actual_root, "storage root mismatch");
3381 }
3382 }
3383
3384 use proptest::prelude::*;
3385 use proptest_arbitrary_interop::arb;
3386
3387 fn build_changeset(
3394 base: &BTreeMap<B256, U256>,
3395 new_keys: BTreeMap<B256, U256>,
3396 overlap_pct: f64,
3397 delete_pct: f64,
3398 rng: &mut rand::rngs::StdRng,
3399 ) -> BTreeMap<B256, U256> {
3400 let num_overlap = (base.len() as f64 * overlap_pct) as usize;
3401 let num_delete = (num_overlap as f64 * delete_pct) as usize;
3402
3403 let mut all_keys: Vec<B256> = base.keys().copied().collect();
3404 all_keys.shuffle(rng);
3405 let overlap_keys = &all_keys[..num_overlap];
3406
3407 let mut changeset = new_keys;
3408 for (i, &key) in overlap_keys.iter().enumerate() {
3409 let value =
3410 if i < num_delete { U256::ZERO } else { U256::from(rng.random::<u64>() | 1) };
3411 changeset.entry(key).or_insert(value);
3412 }
3413 changeset
3414 }
3415
3416 proptest! {
3417 #![proptest_config(ProptestConfig::with_cases(1000))]
3418 #[test]
3419 fn arena_trie_proptest(
3420 initial in proptest::collection::btree_map(arb::<B256>(), arb::<U256>(), 0..=100usize),
3421 changeset1_new_keys in proptest::collection::btree_map(arb::<B256>(), arb::<U256>(), 0..=30usize),
3422 changeset2_new_keys in proptest::collection::btree_map(arb::<B256>(), arb::<U256>(), 0..=30usize),
3423 overlap_pct in 0.0..=0.5f64,
3424 delete_pct in 0.0..=0.33f64, shuffle_seed in arb::<u64>(),
3426 ) {
3427 reth_tracing::init_test_tracing();
3428 info!(target: TRACE_TARGET, ?shuffle_seed, "PROPTEST START");
3429
3430 let initial: BTreeMap<B256, U256> = initial.into_iter()
3432 .filter(|(_, v)| *v != U256::ZERO)
3433 .collect();
3434
3435 let mut rng = rand::rngs::StdRng::seed_from_u64(shuffle_seed);
3436
3437 let changeset1 = build_changeset(&initial, changeset1_new_keys, overlap_pct, delete_pct, &mut rng);
3438 for (i, (k, v)) in changeset1.iter().enumerate() {
3439 trace!(target: TRACE_TARGET, ?i, ?k, ?v, "Changeset 1 entry");
3440 }
3441
3442 let mut harness = ArenaTrieTestHarness::new(initial);
3443
3444 let root_node = harness.root_node();
3446 let mut apst = ArenaParallelSparseTrie::default().with_parallelism_thresholds(
3447 ArenaParallelismThresholds {
3448 min_dirty_leaves: 3,
3449 min_revealed_nodes: 3,
3450 min_updates: 3,
3451 min_leaves_for_prune: 3,
3452 },
3453 );
3454 apst.set_root(root_node.node, root_node.masks, true).expect("set_root should succeed");
3455
3456 harness.assert_changes(&mut apst, changeset1.clone());
3457
3458 harness.apply_changeset(changeset1);
3460
3461 let mut all_storage_keys: Vec<Nibbles> = harness.storage().keys()
3463 .map(|k| Nibbles::unpack(*k))
3464 .collect();
3465 all_storage_keys.shuffle(&mut rng);
3466 let num_retain = if all_storage_keys.is_empty() { 0 } else {
3467 rng.random_range(0..=all_storage_keys.len())
3468 };
3469 let mut retained: Vec<Nibbles> = all_storage_keys[..num_retain].to_vec();
3470 retained.sort_unstable();
3471 apst.prune(&retained);
3472
3473 let changeset2 = build_changeset(harness.storage(), changeset2_new_keys, overlap_pct, delete_pct, &mut rng);
3474 for (i, (k, v)) in changeset2.iter().enumerate() {
3475 trace!(target: TRACE_TARGET, ?i, ?k, ?v, "Changeset 2 entry");
3476 }
3477
3478 harness.assert_changes(&mut apst, changeset2);
3479 }
3480 }
3481}