Skip to main content

reth_stages/stages/
merkle.rs

1use alloy_consensus::{constants::KECCAK_EMPTY, 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
29// TODO: automate the process outlined below so the user can just send in a debugging package
30/// The error message that we include in invalid state root errors to tell users what information
31/// they should include in a bug report, since true state root errors can be impossible to debug
32/// with just basic logs.
33pub 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
47/// The default threshold (in number of blocks) for switching from incremental trie building
48/// of changes to whole rebuild.
49pub const MERKLE_STAGE_DEFAULT_REBUILD_THRESHOLD: u64 = 100_000;
50
51/// The default threshold (in number of blocks) to run the stage in incremental mode. The
52/// incremental mode will calculate the state root for a large range of blocks by calculating the
53/// new state root for this many blocks, in batches, repeating until we reach the desired block
54/// number.
55pub const MERKLE_STAGE_DEFAULT_INCREMENTAL_THRESHOLD: u64 = 7_000;
56
57/// The merkle hashing stage uses input from
58/// [`AccountHashingStage`][crate::stages::AccountHashingStage] and
59/// [`StorageHashingStage`][crate::stages::StorageHashingStage] to calculate intermediate hashes
60/// and state roots.
61///
62/// This stage should be run with the above two stages, otherwise it is a no-op.
63///
64/// This stage is split in two: one for calculating hashes and one for unwinding.
65///
66/// When run in execution, it's going to be executed AFTER the hashing stages, to generate
67/// the state root. When run in unwind mode, it's going to be executed BEFORE the hashing stages,
68/// so that it unwinds the intermediate hashes based on the unwound hashed state from the hashing
69/// stages. The order of these two variants is important. The unwind variant should be added to the
70/// pipeline before the execution variant.
71///
72/// An example pipeline to only hash state would be:
73///
74/// - [`MerkleStage::Unwind`]
75/// - [`AccountHashingStage`][crate::stages::AccountHashingStage]
76/// - [`StorageHashingStage`][crate::stages::StorageHashingStage]
77/// - [`MerkleStage::Execution`]
78#[derive(Debug, Clone)]
79pub enum MerkleStage {
80    /// The execution portion of the merkle stage.
81    Execution {
82        // TODO: make struct for holding incremental settings, for code reuse between `Execution`
83        // variant and `Both`
84        /// The threshold (in number of blocks) for switching from incremental trie building
85        /// of changes to whole rebuild.
86        rebuild_threshold: u64,
87        /// The threshold (in number of blocks) to run the stage in incremental mode. The
88        /// incremental mode will calculate the state root by calculating the new state root for
89        /// some number of blocks, repeating until we reach the desired block number.
90        incremental_threshold: u64,
91    },
92    /// The unwind portion of the merkle stage.
93    Unwind {
94        /// Whether every child of a changed branch path should be walked.
95        walk_all_changed_branch_children: bool,
96    },
97    /// Able to execute and unwind. Used for tests
98    #[cfg(any(test, feature = "test-utils"))]
99    Both {
100        /// The threshold (in number of blocks) for switching from incremental trie building
101        /// of changes to whole rebuild.
102        rebuild_threshold: u64,
103        /// The threshold (in number of blocks) to run the stage in incremental mode. The
104        /// incremental mode will calculate the state root by calculating the new state root for
105        /// some number of blocks, repeating until we reach the desired block number.
106        incremental_threshold: u64,
107    },
108}
109
110impl MerkleStage {
111    /// Stage default for the [`MerkleStage::Execution`].
112    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    /// Stage default for the [`MerkleStage::Unwind`].
120    pub const fn default_unwind() -> Self {
121        Self::new_unwind(false)
122    }
123
124    /// Create a new instance of [`MerkleStage::Unwind`].
125    pub const fn new_unwind(walk_all_changed_branch_children: bool) -> Self {
126        Self::Unwind { walk_all_changed_branch_children }
127    }
128
129    /// Create new instance of [`MerkleStage::Execution`].
130    pub const fn new_execution(rebuild_threshold: u64, incremental_threshold: u64) -> Self {
131        Self::Execution { rebuild_threshold, incremental_threshold }
132    }
133
134    /// Gets the hashing progress
135    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    /// Saves the hashing progress
151    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    /// Return the id of the stage
182    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    /// Execute the stage.
192    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
207        let range = input.next_block_range();
208        let (from_block, to_block) = range.clone().into_inner();
209        let current_block_number = input.checkpoint().block_number;
210
211        let target_block = provider
212            .header_by_number(to_block)?
213            .ok_or_else(|| ProviderError::HeaderNotFound(to_block.into()))?;
214        let target_block_root = target_block.state_root();
215
216        let (trie_root, entities_checkpoint) = if range.is_empty() {
217            (target_block_root, input.checkpoint().entities_stage_checkpoint().unwrap_or_default())
218        } else if to_block - from_block > threshold || from_block == 1 {
219            let mut checkpoint = self.get_execution_checkpoint(provider)?;
220
221            // if there are more blocks than threshold it is faster to rebuild the trie
222            let mut entities_checkpoint = if let Some(checkpoint) =
223                checkpoint.as_ref().filter(|c| c.target_block == to_block)
224            {
225                debug!(
226                    target: "sync::stages::merkle::exec",
227                    current = ?current_block_number,
228                    target = ?to_block,
229                    last_account_key = ?checkpoint.last_account_key,
230                    "Continuing inner merkle checkpoint"
231                );
232
233                input.checkpoint().entities_stage_checkpoint()
234            } else {
235                debug!(
236                    target: "sync::stages::merkle::exec",
237                    current = ?current_block_number,
238                    target = ?to_block,
239                    previous_checkpoint = ?checkpoint,
240                    "Rebuilding trie"
241                );
242                // Reset the checkpoint and clear trie tables
243                checkpoint = None;
244                self.save_execution_checkpoint(provider, None)?;
245                provider.tx_ref().clear::<tables::AccountsTrie>()?;
246                provider.tx_ref().clear::<tables::StoragesTrie>()?;
247
248                None
249            }
250            .unwrap_or(EntitiesCheckpoint {
251                processed: 0,
252                total: (provider.count_entries::<tables::HashedAccounts>()? +
253                    provider.count_entries::<tables::HashedStorages>()?)
254                    as u64,
255            });
256
257            let tx = provider.tx_ref();
258            let progress = reth_trie_db::with_adapter!(provider, |A| {
259                DbStateRoot::<_, A>::from_tx(tx)
260                    .with_intermediate_state(checkpoint.map(IntermediateStateRootState::from))
261                    .root_with_progress()
262            })
263            .map_err(|e| {
264                error!(target: "sync::stages::merkle", %e, ?current_block_number, ?to_block, "State root with progress failed! {INVALID_STATE_ROOT_ERROR_MESSAGE}");
265                StageError::Fatal(Box::new(e))
266            })?;
267            match progress {
268                StateRootProgress::Progress(state, hashed_entries_walked, updates) => {
269                    provider.write_trie_updates(updates)?;
270
271                    let mut checkpoint = MerkleCheckpoint::new(
272                        to_block,
273                        state.account_root_state.last_hashed_key,
274                        state
275                            .account_root_state
276                            .walker_stack
277                            .into_iter()
278                            .map(StoredSubNode::from)
279                            .collect(),
280                        state.account_root_state.hash_builder.into(),
281                    );
282
283                    // Save storage root state if present
284                    if let Some(storage_state) = state.storage_root_state {
285                        checkpoint.storage_root_checkpoint =
286                            Some(StorageRootMerkleCheckpoint::new(
287                                storage_state.state.last_hashed_key,
288                                storage_state
289                                    .state
290                                    .walker_stack
291                                    .into_iter()
292                                    .map(StoredSubNode::from)
293                                    .collect(),
294                                storage_state.state.hash_builder.into(),
295                                storage_state.account.nonce,
296                                storage_state.account.balance,
297                                storage_state.account.bytecode_hash.unwrap_or(KECCAK_EMPTY),
298                            ));
299                    }
300                    self.save_execution_checkpoint(provider, Some(checkpoint))?;
301
302                    entities_checkpoint.processed += hashed_entries_walked as u64;
303
304                    return Ok(ExecOutput {
305                        checkpoint: input
306                            .checkpoint()
307                            .with_entities_stage_checkpoint(entities_checkpoint),
308                        done: false,
309                    })
310                }
311                StateRootProgress::Complete(root, hashed_entries_walked, updates) => {
312                    provider.write_trie_updates(updates)?;
313
314                    entities_checkpoint.processed += hashed_entries_walked as u64;
315
316                    (root, entities_checkpoint)
317                }
318            }
319        } else {
320            debug!(target: "sync::stages::merkle::exec", current = ?current_block_number, target = ?to_block, "Updating trie in chunks");
321            let mut final_root = None;
322            for start_block in range.step_by(incremental_threshold as usize) {
323                let chunk_to = std::cmp::min(start_block + incremental_threshold - 1, to_block);
324                let chunk_range = start_block..=chunk_to;
325                debug!(
326                    target: "sync::stages::merkle::exec",
327                    current = ?current_block_number,
328                    target = ?to_block,
329                    incremental_threshold,
330                    chunk_range = ?chunk_range,
331                    "Processing chunk"
332                );
333                let (root, updates) = reth_trie_db::with_adapter!(provider, |A| {
334                    DbStateRoot::<_, A>::incremental_root_with_updates(provider, chunk_range)
335                })
336                .map_err(|e| {
337                    error!(target: "sync::stages::merkle", %e, ?current_block_number, ?to_block, "Incremental state root failed! {INVALID_STATE_ROOT_ERROR_MESSAGE}");
338                    StageError::Fatal(Box::new(e))
339                })?;
340                provider.write_trie_updates(updates)?;
341                final_root = Some(root);
342            }
343
344            // if we had no final root, we must have not looped above, which should not be possible
345            let final_root = final_root.ok_or(StageError::Fatal(
346                "Incremental merkle hashing did not produce a final root".into(),
347            ))?;
348
349            let total_hashed_entries = (provider.count_entries::<tables::HashedAccounts>()? +
350                provider.count_entries::<tables::HashedStorages>()?)
351                as u64;
352
353            let entities_checkpoint = EntitiesCheckpoint {
354                // This is fine because `range` doesn't have an upper bound, so in this `else`
355                // branch we're just hashing all remaining accounts and storage slots we have in the
356                // database.
357                processed: total_hashed_entries,
358                total: total_hashed_entries,
359            };
360            // Save the checkpoint
361            (final_root, entities_checkpoint)
362        };
363
364        // Reset the checkpoint
365        self.save_execution_checkpoint(provider, None)?;
366
367        validate_state_root(trie_root, SealedHeader::seal_slow(target_block), to_block)?;
368
369        Ok(ExecOutput {
370            checkpoint: StageCheckpoint::new(to_block)
371                .with_entities_stage_checkpoint(entities_checkpoint),
372            done: true,
373        })
374    }
375
376    /// Unwind the stage.
377    fn unwind(
378        &mut self,
379        provider: &Provider,
380        input: UnwindInput,
381    ) -> Result<UnwindOutput, StageError> {
382        let tx = provider.tx_ref();
383        let range = input.unwind_block_range();
384        if matches!(self, Self::Execution { .. }) {
385            info!(target: "sync::stages::merkle::unwind", "Stage is always skipped");
386            return Ok(UnwindOutput { checkpoint: StageCheckpoint::new(input.unwind_to) })
387        }
388        let walk_all_changed_branch_children = match self {
389            Self::Unwind { walk_all_changed_branch_children } => *walk_all_changed_branch_children,
390            #[cfg(any(test, feature = "test-utils"))]
391            Self::Both { .. } => false,
392            Self::Execution { .. } => unreachable!(),
393        };
394
395        let mut entities_checkpoint =
396            input.checkpoint.entities_stage_checkpoint().unwrap_or(EntitiesCheckpoint {
397                processed: 0,
398                total: (tx.entries::<tables::HashedAccounts>()? +
399                    tx.entries::<tables::HashedStorages>()?) as u64,
400            });
401
402        if input.unwind_to == 0 {
403            tx.clear::<tables::AccountsTrie>()?;
404            tx.clear::<tables::StoragesTrie>()?;
405
406            entities_checkpoint.processed = 0;
407
408            return Ok(UnwindOutput {
409                checkpoint: StageCheckpoint::new(input.unwind_to)
410                    .with_entities_stage_checkpoint(entities_checkpoint),
411            })
412        }
413
414        // Unwind trie only if there are transitions
415        if range.is_empty() {
416            info!(target: "sync::stages::merkle::unwind", "Nothing to unwind");
417        } else {
418            let (block_root, updates) = reth_trie_db::with_adapter!(provider, |A| {
419                DbStateRoot::<_, A>::incremental_root_calculator(provider, range).and_then(
420                    |calculator| {
421                        calculator
422                            .with_walk_all_changed_branch_children(walk_all_changed_branch_children)
423                            .root_with_updates()
424                    },
425                )
426            })
427            .map_err(|e| StageError::Fatal(Box::new(e)))?;
428
429            // Validate the calculated state root
430            let target = provider
431                .header_by_number(input.unwind_to)?
432                .ok_or_else(|| ProviderError::HeaderNotFound(input.unwind_to.into()))?;
433
434            validate_state_root(block_root, SealedHeader::seal_slow(target), input.unwind_to)?;
435
436            // Validation passed, apply unwind changes to the database.
437            provider.write_trie_updates(updates)?;
438
439            // Update entities checkpoint to reflect the unwind operation
440            // Since we're unwinding, we need to recalculate the total entities at the target block
441            let accounts = tx.entries::<tables::HashedAccounts>()?;
442            let storages = tx.entries::<tables::HashedStorages>()?;
443            let total = (accounts + storages) as u64;
444            entities_checkpoint.total = total;
445            entities_checkpoint.processed = total;
446        }
447
448        Ok(UnwindOutput {
449            checkpoint: StageCheckpoint::new(input.unwind_to)
450                .with_entities_stage_checkpoint(entities_checkpoint),
451        })
452    }
453}
454
455/// Check that the computed state root matches the root in the expected header.
456#[inline]
457fn validate_state_root<H: BlockHeader + Sealable + Debug>(
458    got: B256,
459    expected: SealedHeader<H>,
460    target_block: BlockNumber,
461) -> Result<(), StageError> {
462    if got == expected.state_root() {
463        Ok(())
464    } else {
465        error!(target: "sync::stages::merkle", ?target_block, ?got, ?expected, "Failed to verify block state root! {INVALID_STATE_ROOT_ERROR_MESSAGE}");
466        Err(StageError::Block {
467            error: BlockErrorKind::Validation(ConsensusError::BodyStateRootDiff(
468                GotExpected { got, expected: expected.state_root() }.into(),
469            )),
470            block: Box::new(expected.block_with_parent()),
471        })
472    }
473}
474
475#[cfg(test)]
476mod tests {
477    use super::*;
478    use crate::test_utils::{
479        stage_test_suite_ext, ExecuteStageTestRunner, StageTestRunner, StorageKind,
480        TestRunnerError, TestStageDB, UnwindStageTestRunner,
481    };
482    use alloy_primitives::{keccak256, U256};
483    use assert_matches::assert_matches;
484    use reth_db_api::cursor::{DbCursorRO, DbCursorRW, DbDupCursorRO};
485    use reth_primitives_traits::{SealedBlock, StorageEntry};
486    use reth_provider::{providers::StaticFileWriter, StaticFileProviderFactory};
487    use reth_stages_api::StageUnitCheckpoint;
488    use reth_static_file_types::StaticFileSegment;
489    use reth_testing_utils::generators::{
490        self, random_block, random_block_range, random_changeset_range,
491        random_contract_account_range, BlockParams, BlockRangeParams,
492    };
493    use reth_trie::test_utils::{state_root, state_root_prehashed};
494    use std::collections::BTreeMap;
495
496    stage_test_suite_ext!(MerkleTestRunner, merkle);
497
498    /// Execute from genesis so as to merkelize whole state
499    #[tokio::test]
500    async fn execute_clean_merkle() {
501        let (previous_stage, stage_progress) = (500, 0);
502
503        // Set up the runner
504        let mut runner = MerkleTestRunner::default();
505        // set low threshold so we hash the whole storage
506        let input = ExecInput {
507            target: Some(previous_stage),
508            checkpoint: Some(StageCheckpoint::new(stage_progress)),
509        };
510
511        runner.seed_execution(input).expect("failed to seed execution");
512
513        let rx = runner.execute(input);
514
515        // Assert the successful result
516        let result = rx.await.unwrap();
517        assert_matches!(
518            result,
519            Ok(ExecOutput {
520                checkpoint: StageCheckpoint {
521                    block_number,
522                    stage_checkpoint: Some(StageUnitCheckpoint::Entities(EntitiesCheckpoint {
523                        processed,
524                        total
525                    }))
526                },
527                done: true
528            }) if block_number == previous_stage && processed == total &&
529                total == (
530                    runner.db.count_entries::<tables::HashedAccounts>().unwrap() +
531                    runner.db.count_entries::<tables::HashedStorages>().unwrap()
532                ) as u64
533        );
534
535        // Validate the stage execution
536        assert!(runner.validate_execution(input, result.ok()).is_ok(), "execution validation");
537    }
538
539    /// Update small trie
540    #[tokio::test]
541    async fn execute_small_merkle() {
542        let (previous_stage, stage_progress) = (2, 1);
543
544        // Set up the runner
545        let mut runner = MerkleTestRunner::default();
546        let input = ExecInput {
547            target: Some(previous_stage),
548            checkpoint: Some(StageCheckpoint::new(stage_progress)),
549        };
550
551        runner.seed_execution(input).expect("failed to seed execution");
552
553        let rx = runner.execute(input);
554
555        // Assert the successful result
556        let result = rx.await.unwrap();
557        assert_matches!(
558            result,
559            Ok(ExecOutput {
560                checkpoint: StageCheckpoint {
561                    block_number,
562                    stage_checkpoint: Some(StageUnitCheckpoint::Entities(EntitiesCheckpoint {
563                        processed,
564                        total
565                    }))
566                },
567                done: true
568            }) if block_number == previous_stage && processed == total &&
569                total == (
570                    runner.db.count_entries::<tables::HashedAccounts>().unwrap() +
571                    runner.db.count_entries::<tables::HashedStorages>().unwrap()
572                ) as u64
573        );
574
575        // Validate the stage execution
576        assert!(runner.validate_execution(input, result.ok()).is_ok(), "execution validation");
577    }
578
579    #[tokio::test]
580    async fn execute_chunked_merkle() {
581        let (previous_stage, stage_progress) = (200, 100);
582        let clean_threshold = 100;
583        let incremental_threshold = 10;
584
585        // Set up the runner
586        let mut runner =
587            MerkleTestRunner { db: TestStageDB::default(), clean_threshold, incremental_threshold };
588
589        let input = ExecInput {
590            target: Some(previous_stage),
591            checkpoint: Some(StageCheckpoint::new(stage_progress)),
592        };
593
594        runner.seed_execution(input).expect("failed to seed execution");
595        let rx = runner.execute(input);
596
597        // Assert the successful result
598        let result = rx.await.unwrap();
599        assert_matches!(
600            result,
601            Ok(ExecOutput {
602                checkpoint: StageCheckpoint {
603                    block_number,
604                    stage_checkpoint: Some(StageUnitCheckpoint::Entities(EntitiesCheckpoint {
605                        processed,
606                        total
607                    }))
608                },
609                done: true
610            }) if block_number == previous_stage && processed == total &&
611                total == (
612                    runner.db.count_entries::<tables::HashedAccounts>().unwrap() +
613                    runner.db.count_entries::<tables::HashedStorages>().unwrap()
614                ) as u64
615        );
616
617        // Validate the stage execution
618        let provider = runner.db.factory.provider().unwrap();
619        let header = provider.header_by_number(previous_stage).unwrap().unwrap();
620        let expected_root = header.state_root;
621
622        let actual_root = runner
623            .db
624            .query_with_provider(|provider| {
625                Ok(reth_trie_db::with_adapter!(provider, |A| {
626                    DbStateRoot::<_, A>::incremental_root_with_updates(
627                        &provider,
628                        stage_progress + 1..=previous_stage,
629                    )
630                }))
631            })
632            .unwrap();
633
634        assert_eq!(
635            actual_root.unwrap().0,
636            expected_root,
637            "State root mismatch after chunked processing"
638        );
639    }
640
641    struct MerkleTestRunner {
642        db: TestStageDB,
643        clean_threshold: u64,
644        incremental_threshold: u64,
645    }
646
647    impl Default for MerkleTestRunner {
648        fn default() -> Self {
649            Self {
650                db: TestStageDB::default(),
651                clean_threshold: 10000,
652                incremental_threshold: 10000,
653            }
654        }
655    }
656
657    impl StageTestRunner for MerkleTestRunner {
658        type S = MerkleStage;
659
660        fn db(&self) -> &TestStageDB {
661            &self.db
662        }
663
664        fn stage(&self) -> Self::S {
665            Self::S::Both {
666                rebuild_threshold: self.clean_threshold,
667                incremental_threshold: self.incremental_threshold,
668            }
669        }
670    }
671
672    impl ExecuteStageTestRunner for MerkleTestRunner {
673        type Seed = Vec<SealedBlock<reth_ethereum_primitives::Block>>;
674
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, 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            // add block changeset from block 1.
741            self.db.insert_changesets(transitions, Some(start))?;
742            self.db.insert_accounts_and_storages(final_state)?;
743
744            // Calculate state root
745            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            // The execution is validated within the stage
789            Ok(())
790        }
791    }
792
793    impl UnwindStageTestRunner for MerkleTestRunner {
794        fn validate_unwind(&self, _input: UnwindInput) -> Result<(), TestRunnerError> {
795            // The unwind is validated within the stage
796            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}