1use alloy_consensus::BlockHeader;
2use alloy_primitives::{BlockNumber, Sealable, B256};
3use reth_codecs::Compact;
4use reth_consensus::ConsensusError;
5use reth_db_api::{
6 tables,
7 transaction::{DbTx, DbTxMut},
8};
9use reth_primitives_traits::{GotExpected, SealedHeader};
10use reth_provider::{
11 ChangeSetReader, DBProvider, HeaderProvider, ProviderError, StageCheckpointReader,
12 StageCheckpointWriter, StatsReader, StorageChangeSetReader, StorageSettingsCache, TrieWriter,
13};
14use reth_stages_api::{
15 BlockErrorKind, EntitiesCheckpoint, ExecInput, ExecOutput, MerkleCheckpoint, Stage,
16 StageCheckpoint, StageError, StageId, StorageRootMerkleCheckpoint, UnwindInput, UnwindOutput,
17};
18use reth_trie::{IntermediateStateRootState, StateRoot, StateRootProgress, StoredSubNode};
19use reth_trie_db::DatabaseStateRoot;
20
21use std::fmt::Debug;
22
23type DbStateRoot<'a, TX, A> = StateRoot<
24 reth_trie_db::DatabaseTrieCursorFactory<&'a TX, A>,
25 reth_trie_db::DatabaseHashedCursorFactory<&'a TX>,
26>;
27use tracing::*;
28
29pub const INVALID_STATE_ROOT_ERROR_MESSAGE: &str = r#"
34Invalid state root error on stage verification!
35This is an error that likely requires a report to the reth team with additional information.
36Please include the following information in your report:
37 * This error message
38 * The state root of the block that was rejected
39 * The output of `reth db stats --checksum` from the database that was being used. This will take a long time to run!
40 * 50-100 lines of logs before and after the first occurrence of the log message with the state root of the block that was rejected.
41 * The debug logs from __the same time period__. To find the default location for these logs, run:
42 `reth --help | grep -A 4 'log.file.directory'`
43
44Once you have this information, please submit a github issue at https://github.com/paradigmxyz/reth/issues/new
45"#;
46
47pub const MERKLE_STAGE_DEFAULT_REBUILD_THRESHOLD: u64 = 100_000;
50
51pub const MERKLE_STAGE_DEFAULT_INCREMENTAL_THRESHOLD: u64 = 7_000;
56
57#[derive(Debug, Clone)]
79pub enum MerkleStage {
80 Execution {
82 rebuild_threshold: u64,
87 incremental_threshold: u64,
91 },
92 Unwind {
94 walk_all_changed_branch_children: bool,
96 },
97 #[cfg(any(test, feature = "test-utils"))]
99 Both {
100 rebuild_threshold: u64,
103 incremental_threshold: u64,
107 },
108}
109
110impl MerkleStage {
111 pub const fn default_execution() -> Self {
113 Self::Execution {
114 rebuild_threshold: MERKLE_STAGE_DEFAULT_REBUILD_THRESHOLD,
115 incremental_threshold: MERKLE_STAGE_DEFAULT_INCREMENTAL_THRESHOLD,
116 }
117 }
118
119 pub const fn default_unwind() -> Self {
121 Self::new_unwind(false)
122 }
123
124 pub const fn new_unwind(walk_all_changed_branch_children: bool) -> Self {
126 Self::Unwind { walk_all_changed_branch_children }
127 }
128
129 pub const fn new_execution(rebuild_threshold: u64, incremental_threshold: u64) -> Self {
131 Self::Execution { rebuild_threshold, incremental_threshold }
132 }
133
134 pub fn get_execution_checkpoint(
136 &self,
137 provider: &impl StageCheckpointReader,
138 ) -> Result<Option<MerkleCheckpoint>, StageError> {
139 let buf =
140 provider.get_stage_checkpoint_progress(StageId::MerkleExecute)?.unwrap_or_default();
141
142 if buf.is_empty() {
143 return Ok(None)
144 }
145
146 let (checkpoint, _) = MerkleCheckpoint::from_compact(&buf, buf.len());
147 Ok(Some(checkpoint))
148 }
149
150 pub fn save_execution_checkpoint(
152 &self,
153 provider: &impl StageCheckpointWriter,
154 checkpoint: Option<MerkleCheckpoint>,
155 ) -> Result<(), StageError> {
156 let mut buf = vec![];
157 if let Some(checkpoint) = checkpoint {
158 debug!(
159 target: "sync::stages::merkle::exec",
160 last_account_key = ?checkpoint.last_account_key,
161 "Saving inner merkle checkpoint"
162 );
163 checkpoint.to_compact(&mut buf);
164 }
165 Ok(provider.save_stage_checkpoint_progress(StageId::MerkleExecute, buf)?)
166 }
167}
168
169impl<Provider> Stage<Provider> for MerkleStage
170where
171 Provider: DBProvider<Tx: DbTxMut>
172 + TrieWriter
173 + StatsReader
174 + HeaderProvider
175 + ChangeSetReader
176 + StorageChangeSetReader
177 + StorageSettingsCache
178 + StageCheckpointReader
179 + StageCheckpointWriter,
180{
181 fn id(&self) -> StageId {
183 match self {
184 Self::Execution { .. } => StageId::MerkleExecute,
185 Self::Unwind { .. } => StageId::MerkleUnwind,
186 #[cfg(any(test, feature = "test-utils"))]
187 Self::Both { .. } => StageId::Other("MerkleBoth"),
188 }
189 }
190
191 fn execute(&mut self, provider: &Provider, input: ExecInput) -> Result<ExecOutput, StageError> {
193 let (threshold, incremental_threshold) = match self {
194 Self::Unwind { .. } => {
195 info!(target: "sync::stages::merkle::unwind", "Stage is always skipped");
196 return Ok(ExecOutput::done(StageCheckpoint::new(input.target())))
197 }
198 Self::Execution { rebuild_threshold, incremental_threshold } => {
199 (*rebuild_threshold, *incremental_threshold)
200 }
201 #[cfg(any(test, feature = "test-utils"))]
202 Self::Both { rebuild_threshold, incremental_threshold } => {
203 (*rebuild_threshold, *incremental_threshold)
204 }
205 };
206 let incremental_threshold = incremental_threshold.max(1);
207
208 let range = input.next_block_range();
209 let (from_block, to_block) = range.clone().into_inner();
210 let current_block_number = input.checkpoint().block_number;
211
212 let target_block = provider
213 .header_by_number(to_block)?
214 .ok_or_else(|| ProviderError::HeaderNotFound(to_block.into()))?;
215 let target_block_root = target_block.state_root();
216
217 let (trie_root, entities_checkpoint) = if range.is_empty() {
218 (target_block_root, input.checkpoint().entities_stage_checkpoint().unwrap_or_default())
219 } else if to_block - from_block > threshold || from_block == 1 {
220 let mut checkpoint = self.get_execution_checkpoint(provider)?;
221
222 let mut entities_checkpoint = if let Some(checkpoint) =
224 checkpoint.as_ref().filter(|c| c.target_block == to_block)
225 {
226 debug!(
227 target: "sync::stages::merkle::exec",
228 current = ?current_block_number,
229 target = ?to_block,
230 last_account_key = ?checkpoint.last_account_key,
231 "Continuing inner merkle checkpoint"
232 );
233
234 input.checkpoint().entities_stage_checkpoint()
235 } else {
236 debug!(
237 target: "sync::stages::merkle::exec",
238 current = ?current_block_number,
239 target = ?to_block,
240 previous_checkpoint = ?checkpoint,
241 "Rebuilding trie"
242 );
243 checkpoint = None;
245 self.save_execution_checkpoint(provider, None)?;
246 provider.tx_ref().clear::<tables::AccountsTrie>()?;
247 provider.tx_ref().clear::<tables::StoragesTrie>()?;
248
249 None
250 }
251 .unwrap_or(EntitiesCheckpoint {
252 processed: 0,
253 total: (provider.count_entries::<tables::HashedAccounts>()? +
254 provider.count_entries::<tables::HashedStorages>()?)
255 as u64,
256 });
257
258 let tx = provider.tx_ref();
259 let progress = reth_trie_db::with_adapter!(provider, |A| {
260 DbStateRoot::<_, A>::from_tx(tx)
261 .with_intermediate_state(checkpoint.map(IntermediateStateRootState::from))
262 .root_with_progress()
263 })
264 .map_err(|e| {
265 error!(target: "sync::stages::merkle", %e, ?current_block_number, ?to_block, "State root with progress failed! {INVALID_STATE_ROOT_ERROR_MESSAGE}");
266 StageError::Fatal(Box::new(e))
267 })?;
268 match progress {
269 StateRootProgress::Progress(state, hashed_entries_walked, updates) => {
270 provider.write_trie_updates(updates)?;
271
272 let mut checkpoint = MerkleCheckpoint::new(
273 to_block,
274 state.account_root_state.last_hashed_key,
275 state
276 .account_root_state
277 .walker_stack
278 .into_iter()
279 .map(StoredSubNode::from)
280 .collect(),
281 state.account_root_state.hash_builder.into(),
282 );
283
284 if let Some(storage_state) = state.storage_root_state {
286 checkpoint.storage_root_checkpoint =
287 Some(StorageRootMerkleCheckpoint::new(
288 storage_state.state.last_hashed_key,
289 storage_state
290 .state
291 .walker_stack
292 .into_iter()
293 .map(StoredSubNode::from)
294 .collect(),
295 storage_state.state.hash_builder.into(),
296 storage_state.account,
297 ));
298 }
299 self.save_execution_checkpoint(provider, Some(checkpoint))?;
300
301 entities_checkpoint.processed += hashed_entries_walked as u64;
302
303 return Ok(ExecOutput::in_progress(
304 input.checkpoint().with_entities_stage_checkpoint(entities_checkpoint),
305 ))
306 }
307 StateRootProgress::Complete(root, hashed_entries_walked, updates) => {
308 provider.write_trie_updates(updates)?;
309
310 entities_checkpoint.processed += hashed_entries_walked as u64;
311
312 (root, entities_checkpoint)
313 }
314 }
315 } else {
316 debug!(target: "sync::stages::merkle::exec", current = ?current_block_number, target = ?to_block, "Updating trie in chunks");
317 let mut final_root = None;
318 for start_block in range.step_by(incremental_threshold as usize) {
319 let chunk_to = start_block.saturating_add(incremental_threshold - 1).min(to_block);
320 let chunk_range = start_block..=chunk_to;
321 debug!(
322 target: "sync::stages::merkle::exec",
323 current = ?current_block_number,
324 target = ?to_block,
325 incremental_threshold,
326 chunk_range = ?chunk_range,
327 "Processing chunk"
328 );
329 let (root, updates) = reth_trie_db::with_adapter!(provider, |A| {
330 DbStateRoot::<_, A>::incremental_root_with_updates(provider, chunk_range)
331 })
332 .map_err(|e| {
333 error!(target: "sync::stages::merkle", %e, ?current_block_number, ?to_block, "Incremental state root failed! {INVALID_STATE_ROOT_ERROR_MESSAGE}");
334 StageError::Fatal(Box::new(e))
335 })?;
336 provider.write_trie_updates(updates)?;
337 final_root = Some(root);
338 }
339
340 let final_root = final_root.ok_or(StageError::Fatal(
342 "Incremental merkle hashing did not produce a final root".into(),
343 ))?;
344
345 let total_hashed_entries = (provider.count_entries::<tables::HashedAccounts>()? +
346 provider.count_entries::<tables::HashedStorages>()?)
347 as u64;
348
349 let entities_checkpoint = EntitiesCheckpoint {
350 processed: total_hashed_entries,
354 total: total_hashed_entries,
355 };
356 (final_root, entities_checkpoint)
358 };
359
360 self.save_execution_checkpoint(provider, None)?;
362
363 validate_state_root(trie_root, SealedHeader::seal_slow(target_block), to_block)?;
364
365 Ok(ExecOutput::done(
366 StageCheckpoint::new(to_block).with_entities_stage_checkpoint(entities_checkpoint),
367 ))
368 }
369
370 fn unwind(
372 &mut self,
373 provider: &Provider,
374 input: UnwindInput,
375 ) -> Result<UnwindOutput, StageError> {
376 let tx = provider.tx_ref();
377 let range = input.unwind_block_range();
378 if matches!(self, Self::Execution { .. }) {
379 info!(target: "sync::stages::merkle::unwind", "Stage is always skipped");
380 return Ok(UnwindOutput { checkpoint: StageCheckpoint::new(input.unwind_to) })
381 }
382 let walk_all_changed_branch_children = match self {
383 Self::Unwind { walk_all_changed_branch_children } => *walk_all_changed_branch_children,
384 #[cfg(any(test, feature = "test-utils"))]
385 Self::Both { .. } => false,
386 Self::Execution { .. } => unreachable!(),
387 };
388
389 let mut entities_checkpoint =
390 input.checkpoint.entities_stage_checkpoint().unwrap_or(EntitiesCheckpoint {
391 processed: 0,
392 total: (tx.entries::<tables::HashedAccounts>()? +
393 tx.entries::<tables::HashedStorages>()?) as u64,
394 });
395
396 if input.unwind_to == 0 {
397 tx.clear::<tables::AccountsTrie>()?;
398 tx.clear::<tables::StoragesTrie>()?;
399
400 entities_checkpoint.processed = 0;
401
402 return Ok(UnwindOutput {
403 checkpoint: StageCheckpoint::new(input.unwind_to)
404 .with_entities_stage_checkpoint(entities_checkpoint),
405 })
406 }
407
408 if range.is_empty() {
410 info!(target: "sync::stages::merkle::unwind", "Nothing to unwind");
411 } else {
412 let (block_root, updates) = reth_trie_db::with_adapter!(provider, |A| {
413 DbStateRoot::<_, A>::incremental_root_calculator(provider, range).and_then(
414 |calculator| {
415 calculator
416 .with_walk_all_changed_branch_children(walk_all_changed_branch_children)
417 .root_with_updates()
418 },
419 )
420 })
421 .map_err(|e| StageError::Fatal(Box::new(e)))?;
422
423 let target = provider
425 .header_by_number(input.unwind_to)?
426 .ok_or_else(|| ProviderError::HeaderNotFound(input.unwind_to.into()))?;
427
428 validate_state_root(block_root, SealedHeader::seal_slow(target), input.unwind_to)?;
429
430 provider.write_trie_updates(updates)?;
432
433 let accounts = tx.entries::<tables::HashedAccounts>()?;
436 let storages = tx.entries::<tables::HashedStorages>()?;
437 let total = (accounts + storages) as u64;
438 entities_checkpoint.total = total;
439 entities_checkpoint.processed = total;
440 }
441
442 Ok(UnwindOutput {
443 checkpoint: StageCheckpoint::new(input.unwind_to)
444 .with_entities_stage_checkpoint(entities_checkpoint),
445 })
446 }
447}
448
449#[inline]
451fn validate_state_root<H: BlockHeader + Sealable + Debug>(
452 got: B256,
453 expected: SealedHeader<H>,
454 target_block: BlockNumber,
455) -> Result<(), StageError> {
456 if got == expected.state_root() {
457 Ok(())
458 } else {
459 error!(target: "sync::stages::merkle", ?target_block, ?got, ?expected, "Failed to verify block state root! {INVALID_STATE_ROOT_ERROR_MESSAGE}");
460 Err(StageError::Block {
461 error: BlockErrorKind::Validation(ConsensusError::BodyStateRootDiff(
462 GotExpected { got, expected: expected.state_root() }.into(),
463 )),
464 block: Box::new(expected.block_with_parent()),
465 })
466 }
467}
468
469#[cfg(test)]
470mod tests {
471 use super::*;
472 use crate::test_utils::{
473 stage_test_suite_ext, ExecuteStageTestRunner, StageTestRunner, StorageKind,
474 TestRunnerError, TestStageDB, UnwindStageTestRunner,
475 };
476 use alloy_primitives::{keccak256, U256};
477 use assert_matches::assert_matches;
478 use reth_db_api::cursor::{DbCursorRO, DbCursorRW, DbDupCursorRO};
479 use reth_primitives_traits::{SealedBlock, StorageEntry};
480 use reth_provider::{providers::StaticFileWriter, StaticFileProviderFactory};
481 use reth_stages_api::StageUnitCheckpoint;
482 use reth_static_file_types::StaticFileSegment;
483 use reth_testing_utils::generators::{
484 self, random_block, random_block_range, random_changeset_range,
485 random_contract_account_range, BlockParams, BlockRangeParams,
486 };
487 use reth_trie::test_utils::{state_root, state_root_prehashed};
488 use std::collections::BTreeMap;
489
490 stage_test_suite_ext!(MerkleTestRunner, merkle);
491
492 #[tokio::test]
494 async fn execute_clean_merkle() {
495 let (previous_stage, stage_progress) = (500, 0);
496
497 let mut runner = MerkleTestRunner::default();
499 let input = ExecInput {
501 target: Some(previous_stage),
502 checkpoint: Some(StageCheckpoint::new(stage_progress)),
503 };
504
505 runner.seed_execution(input).expect("failed to seed execution");
506
507 let rx = runner.execute(input);
508
509 let result = rx.await.unwrap();
511 assert_matches!(
512 result,
513 Ok(ExecOutput {
514 checkpoint: StageCheckpoint {
515 block_number,
516 stage_checkpoint: Some(StageUnitCheckpoint::Entities(EntitiesCheckpoint {
517 processed,
518 total
519 }))
520 },
521 done: true
522 }) if block_number == previous_stage && processed == total &&
523 total == (
524 runner.db.count_entries::<tables::HashedAccounts>().unwrap() +
525 runner.db.count_entries::<tables::HashedStorages>().unwrap()
526 ) as u64
527 );
528
529 assert!(runner.validate_execution(input, result.ok()).is_ok(), "execution validation");
531 }
532
533 #[tokio::test]
535 async fn execute_small_merkle() {
536 let (previous_stage, stage_progress) = (2, 1);
537
538 let mut runner = MerkleTestRunner::default();
540 let input = ExecInput {
541 target: Some(previous_stage),
542 checkpoint: Some(StageCheckpoint::new(stage_progress)),
543 };
544
545 runner.seed_execution(input).expect("failed to seed execution");
546
547 let rx = runner.execute(input);
548
549 let result = rx.await.unwrap();
551 assert_matches!(
552 result,
553 Ok(ExecOutput {
554 checkpoint: StageCheckpoint {
555 block_number,
556 stage_checkpoint: Some(StageUnitCheckpoint::Entities(EntitiesCheckpoint {
557 processed,
558 total
559 }))
560 },
561 done: true
562 }) if block_number == previous_stage && processed == total &&
563 total == (
564 runner.db.count_entries::<tables::HashedAccounts>().unwrap() +
565 runner.db.count_entries::<tables::HashedStorages>().unwrap()
566 ) as u64
567 );
568
569 assert!(runner.validate_execution(input, result.ok()).is_ok(), "execution validation");
571 }
572
573 #[tokio::test]
574 async fn execute_chunked_merkle() {
575 for incremental_threshold in [0, 10, u64::MAX] {
576 execute_chunked_merkle_with(incremental_threshold).await;
577 }
578 }
579
580 async fn execute_chunked_merkle_with(incremental_threshold: u64) {
581 let (previous_stage, stage_progress) = (200, 100);
582 let clean_threshold = 100;
583
584 let mut runner =
586 MerkleTestRunner { db: TestStageDB::default(), clean_threshold, incremental_threshold };
587
588 let input = ExecInput {
589 target: Some(previous_stage),
590 checkpoint: Some(StageCheckpoint::new(stage_progress)),
591 };
592
593 runner.seed_execution(input).expect("failed to seed execution");
594 let rx = runner.execute(input);
595
596 let result = rx.await.unwrap();
598 assert_matches!(
599 result,
600 Ok(ExecOutput {
601 checkpoint: StageCheckpoint {
602 block_number,
603 stage_checkpoint: Some(StageUnitCheckpoint::Entities(EntitiesCheckpoint {
604 processed,
605 total
606 }))
607 },
608 done: true
609 }) if block_number == previous_stage && processed == total &&
610 total == (
611 runner.db.count_entries::<tables::HashedAccounts>().unwrap() +
612 runner.db.count_entries::<tables::HashedStorages>().unwrap()
613 ) as u64
614 );
615
616 let provider = runner.db.factory.provider().unwrap();
618 let header = provider.header_by_number(previous_stage).unwrap().unwrap();
619 let expected_root = header.state_root;
620
621 let actual_root = runner
622 .db
623 .query_with_provider(|provider| {
624 Ok(reth_trie_db::with_adapter!(provider, |A| {
625 DbStateRoot::<_, A>::incremental_root_with_updates(
626 &provider,
627 stage_progress + 1..=previous_stage,
628 )
629 }))
630 })
631 .unwrap();
632
633 assert_eq!(
634 actual_root.unwrap().0,
635 expected_root,
636 "State root mismatch after chunked processing"
637 );
638 }
639
640 struct MerkleTestRunner {
641 db: TestStageDB,
642 clean_threshold: u64,
643 incremental_threshold: u64,
644 }
645
646 impl Default for MerkleTestRunner {
647 fn default() -> Self {
648 Self {
649 db: TestStageDB::default(),
650 clean_threshold: 10000,
651 incremental_threshold: 10000,
652 }
653 }
654 }
655
656 impl StageTestRunner for MerkleTestRunner {
657 type S = MerkleStage;
658
659 fn db(&self) -> &TestStageDB {
660 &self.db
661 }
662
663 fn stage(&self) -> Self::S {
664 Self::S::Both {
665 rebuild_threshold: self.clean_threshold,
666 incremental_threshold: self.incremental_threshold,
667 }
668 }
669 }
670
671 impl ExecuteStageTestRunner for MerkleTestRunner {
672 type Seed = Vec<SealedBlock<reth_ethereum_primitives::Block>>;
673
674 #[allow(clippy::clone_on_copy)]
675 fn seed_execution(&mut self, input: ExecInput) -> Result<Self::Seed, TestRunnerError> {
676 let stage_progress = input.checkpoint().block_number;
677 let start = stage_progress + 1;
678 let end = input.target();
679 let mut rng = generators::rng();
680
681 let mut preblocks = vec![];
682 if stage_progress > 0 {
683 preblocks.append(&mut random_block_range(
684 &mut rng,
685 0..=stage_progress - 1,
686 BlockRangeParams {
687 parent: Some(B256::ZERO),
688 tx_count: 0..1,
689 ..Default::default()
690 },
691 ));
692 self.db.insert_blocks(preblocks.iter(), StorageKind::Static)?;
693 }
694
695 let num_of_accounts = 31;
696 let accounts = random_contract_account_range(&mut rng, &mut (0..num_of_accounts))
697 .into_iter()
698 .collect::<BTreeMap<_, _>>();
699
700 self.db.insert_accounts_and_storages(
701 accounts.iter().map(|(addr, acc)| (*addr, (acc.clone(), std::iter::empty()))),
702 )?;
703
704 let (header, body) = random_block(
705 &mut rng,
706 stage_progress,
707 BlockParams { parent: preblocks.last().map(|b| b.hash()), ..Default::default() },
708 )
709 .split_sealed_header_body();
710 let mut header = header.unseal();
711
712 header.state_root = state_root(
713 accounts
714 .clone()
715 .into_iter()
716 .map(|(address, account)| (address, (account, std::iter::empty()))),
717 );
718 let sealed_head = SealedBlock::<reth_ethereum_primitives::Block>::from_sealed_parts(
719 SealedHeader::seal_slow(header),
720 body,
721 );
722
723 let head_hash = sealed_head.hash();
724 let mut blocks = vec![sealed_head];
725 blocks.extend(random_block_range(
726 &mut rng,
727 start..=end,
728 BlockRangeParams { parent: Some(head_hash), tx_count: 0..3, ..Default::default() },
729 ));
730 let last_block = blocks.last().cloned().unwrap();
731 self.db.insert_blocks(blocks.iter(), StorageKind::Static)?;
732
733 let (transitions, final_state) = random_changeset_range(
734 &mut rng,
735 blocks.iter(),
736 accounts.into_iter().map(|(addr, acc)| (addr, (acc, Vec::new()))),
737 0..3,
738 0..256,
739 );
740 self.db.insert_changesets(transitions, Some(start))?;
742 self.db.insert_accounts_and_storages(final_state)?;
743
744 let root = self.db.query(|tx| {
746 let mut accounts = BTreeMap::default();
747 let mut accounts_cursor = tx.cursor_read::<tables::HashedAccounts>()?;
748 let mut storage_cursor = tx.cursor_dup_read::<tables::HashedStorages>()?;
749 for entry in accounts_cursor.walk_range(..)? {
750 let (key, account) = entry?;
751 let mut storage_entries = Vec::new();
752 let mut entry = storage_cursor.seek_exact(key)?;
753 while let Some((_, storage)) = entry {
754 storage_entries.push(storage);
755 entry = storage_cursor.next_dup()?;
756 }
757 let storage = storage_entries
758 .into_iter()
759 .filter(|v| !v.value.is_zero())
760 .map(|v| (v.key, v.value))
761 .collect::<Vec<_>>();
762 accounts.insert(key, (account, storage));
763 }
764
765 Ok(state_root_prehashed(accounts))
766 })?;
767
768 let static_file_provider = self.db.factory.static_file_provider();
769 let mut writer =
770 static_file_provider.latest_writer(StaticFileSegment::Headers).unwrap();
771 let mut last_header = last_block.clone_sealed_header();
772 last_header.set_state_root(root);
773
774 let hash = last_header.hash_slow();
775 writer.prune_headers(1).unwrap();
776 writer.commit().unwrap();
777 writer.append_header(&last_header, &hash).unwrap();
778 writer.commit().unwrap();
779
780 Ok(blocks)
781 }
782
783 fn validate_execution(
784 &self,
785 _input: ExecInput,
786 _output: Option<ExecOutput>,
787 ) -> Result<(), TestRunnerError> {
788 Ok(())
790 }
791 }
792
793 impl UnwindStageTestRunner for MerkleTestRunner {
794 fn validate_unwind(&self, _input: UnwindInput) -> Result<(), TestRunnerError> {
795 Ok(())
797 }
798
799 fn before_unwind(&self, input: UnwindInput) -> Result<(), TestRunnerError> {
800 let target_block = input.unwind_to + 1;
801
802 self.db
803 .commit(|tx| {
804 let mut storage_changesets_cursor =
805 tx.cursor_dup_read::<tables::StorageChangeSets>().unwrap();
806 let mut storage_cursor =
807 tx.cursor_dup_write::<tables::HashedStorages>().unwrap();
808
809 let mut tree: BTreeMap<B256, BTreeMap<B256, U256>> = BTreeMap::new();
810
811 let mut rev_changeset_walker =
812 storage_changesets_cursor.walk_back(None).unwrap();
813 while let Some((bn_address, entry)) =
814 rev_changeset_walker.next().transpose().unwrap()
815 {
816 if bn_address.block_number() < target_block {
817 break
818 }
819
820 tree.entry(keccak256(bn_address.address()))
821 .or_default()
822 .insert(keccak256(entry.key), entry.value);
823 }
824 for (hashed_address, storage) in tree {
825 for (hashed_slot, value) in storage {
826 let storage_entry = storage_cursor
827 .seek_by_key_subkey(hashed_address, hashed_slot)
828 .unwrap();
829 if storage_entry.is_some_and(|v| v.key == hashed_slot) {
830 storage_cursor.delete_current().unwrap();
831 }
832
833 if !value.is_zero() {
834 let storage_entry = StorageEntry { key: hashed_slot, value };
835 storage_cursor.upsert(hashed_address, &storage_entry).unwrap();
836 }
837 }
838 }
839
840 let mut changeset_cursor =
841 tx.cursor_dup_write::<tables::AccountChangeSets>().unwrap();
842 let mut rev_changeset_walker = changeset_cursor.walk_back(None).unwrap();
843
844 while let Some((block_number, account_before_tx)) =
845 rev_changeset_walker.next().transpose().unwrap()
846 {
847 if block_number < target_block {
848 break
849 }
850
851 if let Some(acc) = account_before_tx.info {
852 tx.put::<tables::HashedAccounts>(
853 keccak256(account_before_tx.address),
854 acc,
855 )
856 .unwrap();
857 } else {
858 tx.delete::<tables::HashedAccounts>(
859 keccak256(account_before_tx.address),
860 None,
861 )
862 .unwrap();
863 }
864 }
865 Ok(())
866 })
867 .unwrap();
868 Ok(())
869 }
870 }
871}