@@ -35,9 +35,9 @@ use reth_primitives_traits::{
3535 AlloyBlockHeader , BlockTy , GotExpected , NodePrimitives , RecoveredBlock , SealedHeader ,
3636} ;
3737use reth_provider:: {
38- BlockExecutionOutput , BlockNumReader , BlockReader , DBProvider , DatabaseProviderFactory ,
39- ExecutionOutcome , HashedPostStateProvider , ProviderError , StateProvider , StateProviderFactory ,
40- StateReader , StateRootProvider ,
38+ BlockExecutionOutput , BlockHashReader , BlockNumReader , BlockReader , DBProvider ,
39+ DatabaseProviderFactory , ExecutionOutcome , HashedPostStateProvider , HeaderProvider ,
40+ ProviderError , StateProvider , StateProviderFactory , StateReader , StateRootProvider ,
4141} ;
4242use reth_revm:: db:: State ;
4343use reth_trie:: { updates:: TrieUpdates , HashedPostState , KeccakKeyHasher , TrieInput } ;
@@ -57,8 +57,6 @@ pub struct TreeCtx<'a, N: NodePrimitives> {
5757 persistence : & ' a PersistenceState ,
5858 /// Reference to the canonical in-memory state
5959 canonical_in_memory_state : & ' a CanonicalInMemoryState < N > ,
60- /// Whether the currently validated block is on a fork chain.
61- is_fork : bool ,
6260}
6361
6462impl < ' a , N : NodePrimitives > std:: fmt:: Debug for TreeCtx < ' a , N > {
@@ -77,9 +75,8 @@ impl<'a, N: NodePrimitives> TreeCtx<'a, N> {
7775 state : & ' a mut EngineApiTreeState < N > ,
7876 persistence : & ' a PersistenceState ,
7977 canonical_in_memory_state : & ' a CanonicalInMemoryState < N > ,
80- is_fork : bool ,
8178 ) -> Self {
82- Self { state, persistence, canonical_in_memory_state, is_fork }
79+ Self { state, persistence, canonical_in_memory_state }
8380 }
8481
8582 /// Returns a reference to the engine tree state
@@ -102,11 +99,6 @@ impl<'a, N: NodePrimitives> TreeCtx<'a, N> {
10299 self . canonical_in_memory_state
103100 }
104101
105- /// Returns whether the currently validated block is on a fork chain.
106- pub const fn is_fork ( & self ) -> bool {
107- self . is_fork
108- }
109-
110102 /// Determines the persisting kind for the given block based on persistence info.
111103 ///
112104 /// Based on the given header it returns whether any conflicting persistence operation is
@@ -588,9 +580,26 @@ where
588580 // terminate prewarming task with good state output
589581 handle. terminate_caching ( Some ( output. state . clone ( ) ) ) ;
590582
591- // If the block is a fork, we don't save the trie updates, because they may be incorrect.
583+ // If the block doesn't connect to the database tip, we don't save its trie updates, because
584+ // they may be incorrect as they were calculated on top of the forked block.
585+ //
586+ // We also only save trie updates if all ancestors have trie updates, because otherwise the
587+ // trie updates may be incorrect.
588+ //
592589 // Instead, they will be recomputed on persistence.
593- let trie_updates = if ctx. is_fork ( ) {
590+ let connects_to_last_persisted =
591+ ensure_ok ! ( self . block_connects_to_last_persisted( ctx, & block) ) ;
592+ let should_discard_trie_updates =
593+ !connects_to_last_persisted || has_ancestors_with_missing_trie_updates;
594+ debug ! (
595+ target: "engine::tree" ,
596+ block = ?block_num_hash,
597+ connects_to_last_persisted,
598+ has_ancestors_with_missing_trie_updates,
599+ should_discard_trie_updates,
600+ "Checking if should discard trie updates"
601+ ) ;
602+ let trie_updates = if should_discard_trie_updates {
594603 ExecutedTrieUpdates :: Missing
595604 } else {
596605 ExecutedTrieUpdates :: Present ( Arc :: new ( trie_output) )
@@ -729,6 +738,51 @@ where
729738 ParallelStateRoot :: new ( consistent_view, input) . incremental_root_with_updates ( )
730739 }
731740
741+ /// Checks if the given block connects to the last persisted block, i.e. if the last persisted
742+ /// block is the ancestor of the given block.
743+ ///
744+ /// This checks the database for the actual last persisted block, not [`PersistenceState`].
745+ fn block_connects_to_last_persisted (
746+ & self ,
747+ ctx : TreeCtx < ' _ , N > ,
748+ block : & RecoveredBlock < N :: Block > ,
749+ ) -> ProviderResult < bool > {
750+ let provider = self . provider . database_provider_ro ( ) ?;
751+ let last_persisted_block = provider. last_block_number ( ) ?;
752+ let last_persisted_hash = provider
753+ . block_hash ( last_persisted_block) ?
754+ . ok_or ( ProviderError :: HeaderNotFound ( last_persisted_block. into ( ) ) ) ?;
755+ let last_persisted = NumHash :: new ( last_persisted_block, last_persisted_hash) ;
756+
757+ let parent_num_hash = |hash : B256 | -> ProviderResult < NumHash > {
758+ let parent_num_hash =
759+ if let Some ( header) = ctx. state ( ) . tree_state . sealed_header_by_hash ( & hash) {
760+ Some ( header. parent_num_hash ( ) )
761+ } else {
762+ provider. sealed_header_by_hash ( hash) ?. map ( |header| header. parent_num_hash ( ) )
763+ } ;
764+
765+ parent_num_hash. ok_or ( ProviderError :: BlockHashNotFound ( hash) )
766+ } ;
767+
768+ let mut parent_block = block. parent_num_hash ( ) ;
769+ while parent_block. number > last_persisted. number {
770+ parent_block = parent_num_hash ( parent_block. hash ) ?;
771+ }
772+
773+ let connects = parent_block == last_persisted;
774+
775+ debug ! (
776+ target: "engine::tree" ,
777+ num_hash = ?block. num_hash( ) ,
778+ ?last_persisted,
779+ ?parent_block,
780+ "Checking if block connects to last persisted block"
781+ ) ;
782+
783+ Ok ( connects)
784+ }
785+
732786 /// Check if the given block has any ancestors with missing trie updates.
733787 fn has_ancestors_with_missing_trie_updates (
734788 & self ,
0 commit comments