1use self::{
4 compare::SingleVerifier,
5 pipeline::{StoragePipeline, WorkerContext},
6};
7use crate::{
8 hashed_cursor::{HashedCursor, HashedStorageCursor},
9 trie_cursor::{
10 depth_first::{self, DepthFirstTrieIterator},
11 TrieCursor, TrieStorageCursor,
12 },
13 Nibbles,
14};
15use alloy_primitives::{B256, U256};
16use alloy_rlp::Encodable;
17use alloy_trie::BranchNodeCompact;
18use crossbeam_channel::{Receiver, Sender};
19use reth_execution_errors::StateRootError;
20use reth_primitives_traits::Account;
21use reth_storage_errors::db::DatabaseError;
22use std::{
23 sync::Arc,
24 thread::{self, JoinHandle},
25};
26
27mod compare;
28mod pipeline;
29mod storage;
30pub use storage::{StorageJob, StorageWorker};
31
32#[derive(Debug)]
44pub struct Verifier<T: TrieCursor, H> {
45 events: Receiver<Result<Output, StateRootError>>,
46 cancel: Option<Sender<()>>,
48 background: Option<JoinHandle<AccountVerifier<T, H>>>,
50}
51
52const FINDINGS_CAPACITY: usize = 1024;
53
54const ACCOUNT_UPDATE_THRESHOLD: usize = 1024;
55
56pub const DEFAULT_STORAGE_LOOKAHEAD: usize = 1_000_000;
58
59impl<T, H> Verifier<T, H>
60where
61 T: TrieCursor + Send + 'static,
62 H: HashedCursor<Value = Account> + Send + 'static,
63{
64 pub fn new<L, W, C, TX>(
91 trie: T,
92 accounts: H,
93 storage_addresses: L,
94 storage_workers: Vec<StorageWorker<W, C, TX>>,
95 storage_lookahead: usize,
96 ) -> Result<Self, DatabaseError>
97 where
98 L: Iterator<Item = Result<StorageJob, DatabaseError>> + Send + 'static,
99 W: TrieStorageCursor + Send + 'static,
100 C: HashedStorageCursor<Value = U256> + Send + 'static,
101 TX: Send + 'static,
102 {
103 let account_verifier = SingleVerifier::new(None, trie)?;
104 assert!(!storage_workers.is_empty(), "verification requires storage workers");
105 assert!(storage_lookahead > 0, "storage lookahead must be positive");
106 let (events_tx, events) = crossbeam_channel::bounded(FINDINGS_CAPACITY);
107 let (cancel, cancel_rx) = crossbeam_channel::bounded(0);
108 let context = WorkerContext::new(events_tx, cancel_rx);
109 let mut account = AccountVerifier {
110 accounts,
111 account_verifier,
112 hash_builder: crate::HashBuilder::default().with_updates(true),
113 account_rlp: Vec::with_capacity(crate::TRIE_ACCOUNT_RLP_MAX_SIZE),
114 };
115 let span = tracing::Span::current();
116 let background = thread::Builder::new()
117 .name("trie-accounts".into())
118 .spawn(move || {
119 let _entered = span.enter();
120 match StoragePipeline::spawn(
121 storage_addresses,
122 storage_workers,
123 context.clone(),
124 storage_lookahead,
125 ) {
126 Ok(mut pipeline) => {
127 context.run(|| account.run(&mut pipeline, &context));
130 pipeline.finish();
131 }
132 Err(error) => context.run(|| Err(error.into())),
133 }
134 account
135 })
136 .map_err(|error| DatabaseError::Custom(Arc::new(error)))?;
137 Ok(Self { events, cancel: Some(cancel), background: Some(background) })
138 }
139}
140
141impl<T: TrieCursor, H> Verifier<T, H> {
142 fn stop(&mut self) {
143 self.cancel.take();
144 if let Some(background) = self.background.take() &&
145 let Err(panic) = background.join() &&
146 !thread::panicking()
147 {
148 std::panic::resume_unwind(panic);
149 }
150 }
151}
152
153impl<T: TrieCursor, H> Iterator for Verifier<T, H> {
154 type Item = Result<Output, StateRootError>;
155
156 fn next(&mut self) -> Option<Self::Item> {
157 self.cancel.as_ref()?;
158 match self.events.recv() {
159 Ok(Ok(output)) => Some(Ok(output)),
160 Ok(Err(error)) => {
161 self.stop();
162 Some(Err(error))
163 }
164 Err(_) => {
165 self.stop();
166 None
167 }
168 }
169 }
170}
171
172impl<T: TrieCursor, H> Drop for Verifier<T, H> {
173 fn drop(&mut self) {
174 self.stop();
175 }
176}
177
178#[derive(Debug)]
180struct AccountVerifier<T: TrieCursor, H> {
181 accounts: H,
182 account_verifier: SingleVerifier<DepthFirstTrieIterator<T>>,
183 hash_builder: crate::HashBuilder,
184 account_rlp: Vec<u8>,
185}
186
187impl<T, H> AccountVerifier<T, H>
188where
189 T: TrieCursor,
190 H: HashedCursor<Value = Account>,
191{
192 fn run(
193 &mut self,
194 pipeline: &mut StoragePipeline,
195 context: &WorkerContext,
196 ) -> Result<(), StateRootError> {
197 let mut next_account = self.accounts.seek(B256::ZERO)?;
198 loop {
199 if context.is_cancelled() {
200 return Ok(());
201 }
202 let next_root = pipeline.next_root(next_account.as_ref().map(|(address, _)| *address));
204 if context.is_cancelled() {
205 return Ok(());
206 }
207 let (address, root) = match (next_account.as_ref(), next_root) {
208 (_, Some(storage)) => storage,
209 (Some((address, _)), None) => (*address, alloy_consensus::EMPTY_ROOT_HASH),
210 (None, None) => break,
211 };
212 let account = match next_account.take() {
213 Some((key, account)) if key == address => {
214 next_account = self.accounts.next()?;
215 account
216 }
217 next => {
218 next_account = next;
219 continue;
222 }
223 };
224 self.account_rlp.clear();
225 account.into_trie_account(root).encode(&mut self.account_rlp);
226 self.hash_builder.add_leaf(Nibbles::unpack(address), &self.account_rlp);
227 if self.hash_builder.updates_len() >= ACCOUNT_UPDATE_THRESHOLD &&
228 !self.verify_account_updates(context)?
229 {
230 return Ok(());
231 }
232 }
233 self.hash_builder.root();
235 if !self.verify_account_updates(context)? {
236 return Ok(());
237 }
238 self.account_verifier.finalize(&mut |output| context.report_one(output))?;
239 Ok(())
240 }
241
242 fn verify_account_updates(&mut self, context: &WorkerContext) -> Result<bool, StateRootError> {
243 let (builder, updates) = std::mem::take(&mut self.hash_builder).split();
244 self.hash_builder = builder.with_updates(true);
245 let mut nodes =
249 updates.into_iter().filter(|(path, _)| !path.is_empty()).collect::<Vec<_>>();
250 nodes.sort_unstable_by(|a, b| depth_first::cmp(&a.0, &b.0));
251 for (path, node) in nodes {
252 if !self.account_verifier.next(&mut |output| context.report_one(output), path, node)? ||
253 !context.report_one(Output::Progress(path))
254 {
255 return Ok(false);
256 }
257 }
258 Ok(true)
259 }
260}
261
262#[derive(Debug, Clone, PartialEq, Eq)]
266pub enum Output {
267 AccountExtra(Nibbles, BranchNodeCompact),
269 StorageExtra(B256, Nibbles, BranchNodeCompact),
271 AccountWrong {
273 path: Nibbles,
275 expected: BranchNodeCompact,
277 found: BranchNodeCompact,
279 },
280 StorageWrong {
282 account: B256,
284 path: Nibbles,
286 expected: BranchNodeCompact,
288 found: BranchNodeCompact,
290 },
291 AccountMissing(Nibbles, BranchNodeCompact),
293 StorageMissing(B256, Nibbles, BranchNodeCompact),
295 Progress(Nibbles),
297}
298
299pub fn default_storage_workers() -> usize {
302 thread::available_parallelism().map_or(1, usize::from)
303}