reth_chain_state/
preserved_sparse_trie.rs1use alloy_primitives::B256;
4use reth_trie_sparse::SparseStateTrie;
5use std::{
6 fmt,
7 sync::mpsc::{self, Receiver, Sender},
8};
9use tracing::debug;
10
11pub type SparseTrie = SparseStateTrie;
13
14pub struct PreservedSparseTrie {
16 trie: PreservedSparseTrieInner,
18 state_root: B256,
23 anchor_hash: B256,
25}
26
27impl fmt::Debug for PreservedSparseTrie {
28 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
29 f.debug_struct("PreservedSparseTrie")
30 .field("state_root", &self.state_root)
31 .field("anchor_hash", &self.anchor_hash)
32 .finish_non_exhaustive()
33 }
34}
35
36impl PreservedSparseTrie {
37 pub const fn anchored(trie: SparseTrie, state_root: B256, anchor_hash: B256) -> Self {
42 Self { trie: PreservedSparseTrieInner::Ready(trie), state_root, anchor_hash }
43 }
44
45 pub fn pending(state_root: B256, anchor_hash: B256) -> (Self, PreservedSparseTrieCompleter) {
47 let (tx, rx) = mpsc::channel();
48 (
49 Self { trie: PreservedSparseTrieInner::Pending(rx), state_root, anchor_hash },
50 PreservedSparseTrieCompleter { tx },
51 )
52 }
53
54 pub const fn state_root(&self) -> B256 {
56 self.state_root
57 }
58
59 pub const fn anchor_hash(&self) -> B256 {
61 self.anchor_hash
62 }
63
64 pub fn into_trie_for(
69 self,
70 parent_state_root: B256,
71 ) -> Result<Option<SparseTrie>, PreservedSparseTrieError> {
72 if self.state_root == parent_state_root {
73 let trie = match self.trie {
74 PreservedSparseTrieInner::Ready(trie) => trie,
75 PreservedSparseTrieInner::Pending(rx) => match rx.recv() {
76 Ok(trie) => trie,
77 Err(_) => {
78 return Err(PreservedSparseTrieError::ProducerDropped {
79 state_root: self.state_root,
80 })
81 }
82 },
83 };
84 debug!(
85 target: "engine::tree::payload_processor",
86 state_root = %self.state_root,
87 anchor_hash = %self.anchor_hash,
88 "Reusing anchored sparse trie for continuation payload"
89 );
90 Ok(Some(trie))
91 } else {
92 debug!(
93 target: "engine::tree::payload_processor",
94 anchor_root = %self.state_root,
95 anchor_hash = %self.anchor_hash,
96 %parent_state_root,
97 "Dropping anchored sparse trie - parent state root mismatch"
98 );
99 Ok(None)
100 }
101 }
102}
103
104#[derive(Debug, Clone, Copy, PartialEq, Eq)]
106pub enum PreservedSparseTrieError {
107 ProducerDropped {
109 state_root: B256,
111 },
112}
113
114impl fmt::Display for PreservedSparseTrieError {
115 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
116 match self {
117 Self::ProducerDropped { state_root } => {
118 write!(f, "pending preserved sparse trie producer dropped for {state_root}")
119 }
120 }
121 }
122}
123
124impl std::error::Error for PreservedSparseTrieError {}
125
126#[allow(clippy::large_enum_variant)]
127enum PreservedSparseTrieInner {
128 Ready(SparseTrie),
129 Pending(Receiver<SparseTrie>),
130}
131
132#[derive(Debug)]
134pub struct PreservedSparseTrieCompleter {
135 tx: Sender<SparseTrie>,
136}
137
138impl PreservedSparseTrieCompleter {
139 pub fn complete(self, trie: SparseTrie) -> Result<(), SparseTrie> {
141 self.tx.send(trie).map_err(|err| err.0)
142 }
143}
144
145#[cfg(test)]
146mod tests {
147 use super::*;
148
149 #[test]
150 fn pending_trie_exposes_state_root_before_completion() {
151 let state_root = B256::with_last_byte(1);
152 let anchor_hash = B256::with_last_byte(2);
153 let (preserved, completer) = PreservedSparseTrie::pending(state_root, anchor_hash);
154
155 assert_eq!(preserved.state_root(), state_root);
156 assert_eq!(preserved.anchor_hash(), anchor_hash);
157 completer.complete(SparseTrie::default()).unwrap();
158 assert!(preserved.into_trie_for(state_root).unwrap().is_some());
159 }
160
161 #[test]
162 fn pending_trie_with_mismatched_root_does_not_wait() {
163 let state_root = B256::with_last_byte(1);
164 let other_state_root = B256::with_last_byte(2);
165 let anchor_hash = B256::with_last_byte(3);
166 let (preserved, completer) = PreservedSparseTrie::pending(state_root, anchor_hash);
167
168 assert!(preserved.into_trie_for(other_state_root).unwrap().is_none());
169 assert!(completer.complete(SparseTrie::default()).is_err());
170 }
171}