Skip to content

Commit 87a7999

Browse files
shekhirinlwedge99
authored andcommitted
refactor(engine): persistence logic (paradigmxyz#18318)
1 parent d40c3bb commit 87a7999

File tree

2 files changed

+95
-24
lines changed

2 files changed

+95
-24
lines changed

crates/engine/tree/src/tree/mod.rs

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1568,6 +1568,9 @@ where
15681568
/// `(last_persisted_number .. canonical_head - threshold]`. The expected
15691569
/// order is oldest -> newest.
15701570
///
1571+
/// If any blocks are missing trie updates, all blocks are persisted, not taking `threshold`
1572+
/// into account.
1573+
///
15711574
/// For those blocks that didn't have the trie updates calculated, runs the state root
15721575
/// calculation, and saves the trie updates.
15731576
///
@@ -1582,13 +1585,31 @@ where
15821585
let mut blocks_to_persist = Vec::new();
15831586
let mut current_hash = self.state.tree_state.canonical_block_hash();
15841587
let last_persisted_number = self.persistence_state.last_persisted_block.number;
1585-
15861588
let canonical_head_number = self.state.tree_state.canonical_block_number();
1589+
let all_blocks_have_trie_updates = self
1590+
.state
1591+
.tree_state
1592+
.blocks_by_hash
1593+
.values()
1594+
.all(|block| block.trie_updates().is_some());
15871595

1588-
let target_number =
1589-
canonical_head_number.saturating_sub(self.config.memory_block_buffer_target());
1596+
let target_number = if all_blocks_have_trie_updates {
1597+
// Persist only up to block buffer target if all blocks have trie updates
1598+
canonical_head_number.saturating_sub(self.config.memory_block_buffer_target())
1599+
} else {
1600+
// Persist all blocks if any block is missing trie updates
1601+
canonical_head_number
1602+
};
15901603

1591-
debug!(target: "engine::tree", ?last_persisted_number, ?canonical_head_number, ?target_number, ?current_hash, "Returning canonical blocks to persist");
1604+
debug!(
1605+
target: "engine::tree",
1606+
?current_hash,
1607+
?last_persisted_number,
1608+
?canonical_head_number,
1609+
?all_blocks_have_trie_updates,
1610+
?target_number,
1611+
"Returning canonical blocks to persist"
1612+
);
15921613
while let Some(block) = self.state.tree_state.blocks_by_hash.get(&current_hash) {
15931614
if block.recovered_block().number() <= last_persisted_number {
15941615
break;
@@ -2332,12 +2353,8 @@ where
23322353
Ok(is_fork) => is_fork,
23332354
};
23342355

2335-
let ctx = TreeCtx::new(
2336-
&mut self.state,
2337-
&self.persistence_state,
2338-
&self.canonical_in_memory_state,
2339-
is_fork,
2340-
);
2356+
let ctx =
2357+
TreeCtx::new(&mut self.state, &self.persistence_state, &self.canonical_in_memory_state);
23412358

23422359
let start = Instant::now();
23432360

crates/engine/tree/src/tree/payload_validator.rs

Lines changed: 68 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,9 @@ use reth_primitives_traits::{
3535
AlloyBlockHeader, BlockTy, GotExpected, NodePrimitives, RecoveredBlock, SealedHeader,
3636
};
3737
use 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
};
4242
use reth_revm::db::State;
4343
use 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

6462
impl<'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

Comments
 (0)