reth_chain_state/
preserved_sparse_trie.rs1use alloy_primitives::{map::B256Map, B256};
4use reth_trie_sparse::SparseStateTrie;
5use std::{fmt, sync::mpsc::Receiver};
6use tracing::debug;
7
8pub struct PreservedSparseTrie {
10 trie: Receiver<SparseTrieWithStorageRoots>,
12 block_hash: B256,
17 anchor_hash: B256,
19}
20
21impl fmt::Debug for PreservedSparseTrie {
22 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
23 f.debug_struct("PreservedSparseTrie")
24 .field("block_hash", &self.block_hash)
25 .field("anchor_hash", &self.anchor_hash)
26 .finish_non_exhaustive()
27 }
28}
29
30impl PreservedSparseTrie {
31 pub const fn pending(
33 trie: Receiver<SparseTrieWithStorageRoots>,
34 block_hash: B256,
35 anchor_hash: B256,
36 ) -> Self {
37 Self { trie, block_hash, anchor_hash }
38 }
39
40 pub const fn block_hash(&self) -> B256 {
42 self.block_hash
43 }
44
45 pub const fn anchor_hash(&self) -> B256 {
47 self.anchor_hash
48 }
49
50 pub fn into_trie_for(
56 self,
57 parent_hash: B256,
58 ) -> Result<Option<SparseTrieWithStorageRoots>, PreservedSparseTrieError> {
59 if self.block_hash == parent_hash {
60 let trie = self.trie.recv().map_err(|_| PreservedSparseTrieError::ProducerDropped {
61 block_hash: self.block_hash,
62 })?;
63 debug!(
64 target: "engine::tree::payload_processor",
65 block_hash = %self.block_hash,
66 anchor_hash = %self.anchor_hash,
67 "Reusing anchored sparse trie for continuation payload"
68 );
69 Ok(Some(trie))
70 } else {
71 debug!(
72 target: "engine::tree::payload_processor",
73 block_hash = %self.block_hash,
74 anchor_hash = %self.anchor_hash,
75 %parent_hash,
76 "Dropping anchored sparse trie - parent hash mismatch"
77 );
78 Ok(None)
79 }
80 }
81}
82
83#[derive(Debug, Clone, Copy, PartialEq, Eq)]
85pub enum PreservedSparseTrieError {
86 ProducerDropped {
88 block_hash: B256,
90 },
91}
92
93impl fmt::Display for PreservedSparseTrieError {
94 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
95 match self {
96 Self::ProducerDropped { block_hash } => {
97 write!(f, "pending preserved sparse trie producer dropped for {block_hash}")
98 }
99 }
100 }
101}
102
103impl std::error::Error for PreservedSparseTrieError {}
104
105#[derive(Debug, Default)]
107pub struct SparseTrieWithStorageRoots {
108 pub trie: SparseStateTrie,
110 pub storage_roots: B256Map<B256>,
112}
113
114#[cfg(test)]
115mod tests {
116 use super::*;
117 use std::sync::mpsc;
118
119 #[test]
120 fn pending_trie_exposes_block_hash_before_completion() {
121 let block_hash = B256::with_last_byte(1);
122 let anchor_hash = B256::with_last_byte(2);
123 let (tx, rx) = mpsc::channel();
124 let preserved = PreservedSparseTrie::pending(rx, block_hash, anchor_hash);
125
126 assert_eq!(preserved.block_hash(), block_hash);
127 assert_eq!(preserved.anchor_hash(), anchor_hash);
128 tx.send(SparseTrieWithStorageRoots::default()).unwrap();
129 assert!(preserved.into_trie_for(block_hash).unwrap().is_some());
130 }
131
132 #[test]
133 fn pending_trie_with_mismatched_parent_does_not_wait() {
134 let block_hash = B256::with_last_byte(1);
135 let other_block_hash = B256::with_last_byte(2);
136 let anchor_hash = B256::with_last_byte(3);
137 let (tx, rx) = mpsc::channel();
138 let preserved = PreservedSparseTrie::pending(rx, block_hash, anchor_hash);
139
140 assert!(preserved.into_trie_for(other_block_hash).unwrap().is_none());
141 assert!(tx.send(SparseTrieWithStorageRoots::default()).is_err());
142 }
143}