mirror of
https://github.com/paradigmxyz/reth.git
synced 2026-01-10 07:48:19 -05:00
This commit is contained in:
@@ -798,71 +798,19 @@ impl<N: NodePrimitives> ExecutedBlock<N> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Trie updates that result from calculating the state root for the block.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum ExecutedTrieUpdates {
|
||||
/// Trie updates present. State root was calculated, and the trie updates can be applied to the
|
||||
/// database.
|
||||
Present(Arc<TrieUpdates>),
|
||||
/// Trie updates missing. State root was calculated, but the trie updates cannot be applied to
|
||||
/// the current database state. To apply the updates, the state root must be recalculated, and
|
||||
/// new trie updates must be generated.
|
||||
///
|
||||
/// This can happen when processing fork chain blocks that are building on top of the
|
||||
/// historical database state. Since we don't store the historical trie state, we cannot
|
||||
/// generate the trie updates for it.
|
||||
Missing,
|
||||
}
|
||||
|
||||
impl ExecutedTrieUpdates {
|
||||
/// Creates a [`ExecutedTrieUpdates`] with present but empty trie updates.
|
||||
pub fn empty() -> Self {
|
||||
Self::Present(Arc::default())
|
||||
}
|
||||
|
||||
/// Sets the trie updates to the provided value as present.
|
||||
pub fn set_present(&mut self, updates: Arc<TrieUpdates>) {
|
||||
*self = Self::Present(updates);
|
||||
}
|
||||
|
||||
/// Takes the present trie updates, leaving the state as missing.
|
||||
pub fn take_present(&mut self) -> Option<Arc<TrieUpdates>> {
|
||||
match self {
|
||||
Self::Present(updates) => {
|
||||
let updates = core::mem::take(updates);
|
||||
*self = Self::Missing;
|
||||
Some(updates)
|
||||
}
|
||||
Self::Missing => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a reference to the trie updates if present.
|
||||
#[allow(clippy::missing_const_for_fn)]
|
||||
pub fn as_ref(&self) -> Option<&TrieUpdates> {
|
||||
match self {
|
||||
Self::Present(updates) => Some(updates),
|
||||
Self::Missing => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the trie updates are present.
|
||||
pub const fn is_present(&self) -> bool {
|
||||
matches!(self, Self::Present(_))
|
||||
}
|
||||
|
||||
/// Returns `true` if the trie updates are missing.
|
||||
pub const fn is_missing(&self) -> bool {
|
||||
matches!(self, Self::Missing)
|
||||
}
|
||||
}
|
||||
|
||||
/// An [`ExecutedBlock`] with its [`TrieUpdates`].
|
||||
///
|
||||
/// We store it as separate type because [`TrieUpdates`] are only available for blocks stored in
|
||||
/// memory and can't be obtained for canonical persisted blocks.
|
||||
#[derive(
|
||||
Clone, Debug, PartialEq, Eq, derive_more::Deref, derive_more::DerefMut, derive_more::Into,
|
||||
Clone,
|
||||
Debug,
|
||||
PartialEq,
|
||||
Eq,
|
||||
Default,
|
||||
derive_more::Deref,
|
||||
derive_more::DerefMut,
|
||||
derive_more::Into,
|
||||
)]
|
||||
pub struct ExecutedBlockWithTrieUpdates<N: NodePrimitives = EthPrimitives> {
|
||||
/// Inner [`ExecutedBlock`].
|
||||
@@ -870,11 +818,8 @@ pub struct ExecutedBlockWithTrieUpdates<N: NodePrimitives = EthPrimitives> {
|
||||
#[deref_mut]
|
||||
#[into]
|
||||
pub block: ExecutedBlock<N>,
|
||||
/// Trie updates that result from calculating the state root for the block.
|
||||
///
|
||||
/// If [`ExecutedTrieUpdates::Missing`], the trie updates should be computed when persisting
|
||||
/// the block **on top of the canonical parent**.
|
||||
pub trie: ExecutedTrieUpdates,
|
||||
/// Trie updates that result of applying the block.
|
||||
pub trie: Arc<TrieUpdates>,
|
||||
}
|
||||
|
||||
impl<N: NodePrimitives> ExecutedBlockWithTrieUpdates<N> {
|
||||
@@ -883,15 +828,15 @@ impl<N: NodePrimitives> ExecutedBlockWithTrieUpdates<N> {
|
||||
recovered_block: Arc<RecoveredBlock<N::Block>>,
|
||||
execution_output: Arc<ExecutionOutcome<N::Receipt>>,
|
||||
hashed_state: Arc<HashedPostState>,
|
||||
trie: ExecutedTrieUpdates,
|
||||
trie: Arc<TrieUpdates>,
|
||||
) -> Self {
|
||||
Self { block: ExecutedBlock { recovered_block, execution_output, hashed_state }, trie }
|
||||
}
|
||||
|
||||
/// Returns a reference to the trie updates for the block, if present.
|
||||
/// Returns a reference to the trie updates for the block
|
||||
#[inline]
|
||||
pub fn trie_updates(&self) -> Option<&TrieUpdates> {
|
||||
self.trie.as_ref()
|
||||
pub fn trie_updates(&self) -> &TrieUpdates {
|
||||
&self.trie
|
||||
}
|
||||
|
||||
/// Converts the value into [`SealedBlock`].
|
||||
|
||||
@@ -26,7 +26,7 @@ pub struct MemoryOverlayStateProviderRef<
|
||||
/// The collection of executed parent blocks. Expected order is newest to oldest.
|
||||
pub(crate) in_memory: Vec<ExecutedBlockWithTrieUpdates<N>>,
|
||||
/// Lazy-loaded in-memory trie data.
|
||||
pub(crate) trie_input: OnceLock<TrieInput>,
|
||||
pub(crate) trie_state: OnceLock<MemoryOverlayTrieState>,
|
||||
}
|
||||
|
||||
/// A state provider that stores references to in-memory blocks along with their state as well as
|
||||
@@ -45,7 +45,7 @@ impl<'a, N: NodePrimitives> MemoryOverlayStateProviderRef<'a, N> {
|
||||
historical: Box<dyn StateProvider + 'a>,
|
||||
in_memory: Vec<ExecutedBlockWithTrieUpdates<N>>,
|
||||
) -> Self {
|
||||
Self { historical, in_memory, trie_input: OnceLock::new() }
|
||||
Self { historical, in_memory, trie_state: OnceLock::new() }
|
||||
}
|
||||
|
||||
/// Turn this state provider into a state provider
|
||||
@@ -54,14 +54,14 @@ impl<'a, N: NodePrimitives> MemoryOverlayStateProviderRef<'a, N> {
|
||||
}
|
||||
|
||||
/// Return lazy-loaded trie state aggregated from in-memory blocks.
|
||||
fn trie_input(&self) -> &TrieInput {
|
||||
self.trie_input.get_or_init(|| {
|
||||
TrieInput::from_blocks(
|
||||
self.in_memory
|
||||
.iter()
|
||||
.rev()
|
||||
.map(|block| (block.hashed_state.as_ref(), block.trie.as_ref())),
|
||||
)
|
||||
fn trie_state(&self) -> &MemoryOverlayTrieState {
|
||||
self.trie_state.get_or_init(|| {
|
||||
let mut trie_state = MemoryOverlayTrieState::default();
|
||||
for block in self.in_memory.iter().rev() {
|
||||
trie_state.state.extend_ref(block.hashed_state.as_ref());
|
||||
trie_state.nodes.extend_ref(block.trie.as_ref());
|
||||
}
|
||||
trie_state
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -117,7 +117,8 @@ impl<N: NodePrimitives> StateRootProvider for MemoryOverlayStateProviderRef<'_,
|
||||
}
|
||||
|
||||
fn state_root_from_nodes(&self, mut input: TrieInput) -> ProviderResult<B256> {
|
||||
input.prepend_self(self.trie_input().clone());
|
||||
let MemoryOverlayTrieState { nodes, state } = self.trie_state().clone();
|
||||
input.prepend_cached(nodes, state);
|
||||
self.historical.state_root_from_nodes(input)
|
||||
}
|
||||
|
||||
@@ -132,7 +133,8 @@ impl<N: NodePrimitives> StateRootProvider for MemoryOverlayStateProviderRef<'_,
|
||||
&self,
|
||||
mut input: TrieInput,
|
||||
) -> ProviderResult<(B256, TrieUpdates)> {
|
||||
input.prepend_self(self.trie_input().clone());
|
||||
let MemoryOverlayTrieState { nodes, state } = self.trie_state().clone();
|
||||
input.prepend_cached(nodes, state);
|
||||
self.historical.state_root_from_nodes_with_updates(input)
|
||||
}
|
||||
}
|
||||
@@ -140,7 +142,7 @@ impl<N: NodePrimitives> StateRootProvider for MemoryOverlayStateProviderRef<'_,
|
||||
impl<N: NodePrimitives> StorageRootProvider for MemoryOverlayStateProviderRef<'_, N> {
|
||||
// TODO: Currently this does not reuse available in-memory trie nodes.
|
||||
fn storage_root(&self, address: Address, storage: HashedStorage) -> ProviderResult<B256> {
|
||||
let state = &self.trie_input().state;
|
||||
let state = &self.trie_state().state;
|
||||
let mut hashed_storage =
|
||||
state.storages.get(&keccak256(address)).cloned().unwrap_or_default();
|
||||
hashed_storage.extend(&storage);
|
||||
@@ -154,7 +156,7 @@ impl<N: NodePrimitives> StorageRootProvider for MemoryOverlayStateProviderRef<'_
|
||||
slot: B256,
|
||||
storage: HashedStorage,
|
||||
) -> ProviderResult<reth_trie::StorageProof> {
|
||||
let state = &self.trie_input().state;
|
||||
let state = &self.trie_state().state;
|
||||
let mut hashed_storage =
|
||||
state.storages.get(&keccak256(address)).cloned().unwrap_or_default();
|
||||
hashed_storage.extend(&storage);
|
||||
@@ -168,7 +170,7 @@ impl<N: NodePrimitives> StorageRootProvider for MemoryOverlayStateProviderRef<'_
|
||||
slots: &[B256],
|
||||
storage: HashedStorage,
|
||||
) -> ProviderResult<StorageMultiProof> {
|
||||
let state = &self.trie_input().state;
|
||||
let state = &self.trie_state().state;
|
||||
let mut hashed_storage =
|
||||
state.storages.get(&keccak256(address)).cloned().unwrap_or_default();
|
||||
hashed_storage.extend(&storage);
|
||||
@@ -183,7 +185,8 @@ impl<N: NodePrimitives> StateProofProvider for MemoryOverlayStateProviderRef<'_,
|
||||
address: Address,
|
||||
slots: &[B256],
|
||||
) -> ProviderResult<AccountProof> {
|
||||
input.prepend_self(self.trie_input().clone());
|
||||
let MemoryOverlayTrieState { nodes, state } = self.trie_state().clone();
|
||||
input.prepend_cached(nodes, state);
|
||||
self.historical.proof(input, address, slots)
|
||||
}
|
||||
|
||||
@@ -192,12 +195,14 @@ impl<N: NodePrimitives> StateProofProvider for MemoryOverlayStateProviderRef<'_,
|
||||
mut input: TrieInput,
|
||||
targets: MultiProofTargets,
|
||||
) -> ProviderResult<MultiProof> {
|
||||
input.prepend_self(self.trie_input().clone());
|
||||
let MemoryOverlayTrieState { nodes, state } = self.trie_state().clone();
|
||||
input.prepend_cached(nodes, state);
|
||||
self.historical.multiproof(input, targets)
|
||||
}
|
||||
|
||||
fn witness(&self, mut input: TrieInput, target: HashedPostState) -> ProviderResult<Vec<Bytes>> {
|
||||
input.prepend_self(self.trie_input().clone());
|
||||
let MemoryOverlayTrieState { nodes, state } = self.trie_state().clone();
|
||||
input.prepend_cached(nodes, state);
|
||||
self.historical.witness(input, target)
|
||||
}
|
||||
}
|
||||
@@ -233,3 +238,12 @@ impl<N: NodePrimitives> StateProvider for MemoryOverlayStateProviderRef<'_, N> {
|
||||
self.historical.bytecode_by_hash(code_hash)
|
||||
}
|
||||
}
|
||||
|
||||
/// The collection of data necessary for trie-related operations for [`MemoryOverlayStateProvider`].
|
||||
#[derive(Clone, Default, Debug)]
|
||||
pub(crate) struct MemoryOverlayTrieState {
|
||||
/// The collection of aggregated in-memory trie updates.
|
||||
pub(crate) nodes: TrieUpdates,
|
||||
/// The collection of hashed state from in-memory blocks.
|
||||
pub(crate) state: HashedPostState,
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
in_memory::ExecutedBlockWithTrieUpdates, CanonStateNotification, CanonStateNotifications,
|
||||
CanonStateSubscriptions, ExecutedTrieUpdates,
|
||||
CanonStateSubscriptions,
|
||||
};
|
||||
use alloy_consensus::{
|
||||
Header, SignableTransaction, Transaction as _, TxEip1559, TxReceipt, EMPTY_ROOT_HASH,
|
||||
@@ -25,7 +25,7 @@ use reth_primitives_traits::{
|
||||
SignedTransaction,
|
||||
};
|
||||
use reth_storage_api::NodePrimitivesProvider;
|
||||
use reth_trie::{root::state_root_unhashed, HashedPostState};
|
||||
use reth_trie::{root::state_root_unhashed, updates::TrieUpdates, HashedPostState};
|
||||
use revm_database::BundleState;
|
||||
use revm_state::AccountInfo;
|
||||
use std::{
|
||||
@@ -222,7 +222,7 @@ impl<N: NodePrimitives> TestBlockBuilder<N> {
|
||||
vec![Requests::default()],
|
||||
)),
|
||||
Arc::new(HashedPostState::default()),
|
||||
ExecutedTrieUpdates::empty(),
|
||||
Arc::new(TrieUpdates::default()),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
//! Internal errors for the tree module.
|
||||
|
||||
use alloy_consensus::BlockHeader;
|
||||
use alloy_primitives::B256;
|
||||
use reth_consensus::ConsensusError;
|
||||
use reth_errors::{BlockExecutionError, BlockValidationError, ProviderError};
|
||||
use reth_evm::execute::InternalBlockExecutionError;
|
||||
@@ -18,20 +17,6 @@ pub enum AdvancePersistenceError {
|
||||
/// A provider error
|
||||
#[error(transparent)]
|
||||
Provider(#[from] ProviderError),
|
||||
/// Missing ancestor.
|
||||
///
|
||||
/// This error occurs when we need to compute the state root for a block with missing trie
|
||||
/// updates, but the ancestor block is not available. State root computation requires the state
|
||||
/// from the parent block as a starting point.
|
||||
///
|
||||
/// A block may be missing the trie updates when it's a fork chain block building on top of the
|
||||
/// historical database state. Since we don't store the historical trie state, we cannot
|
||||
/// generate the trie updates for it until the moment when database is unwound to the canonical
|
||||
/// chain.
|
||||
///
|
||||
/// Also see [`reth_chain_state::ExecutedTrieUpdates::Missing`].
|
||||
#[error("Missing ancestor with hash {0}")]
|
||||
MissingAncestor(B256),
|
||||
}
|
||||
|
||||
#[derive(thiserror::Error)]
|
||||
|
||||
@@ -20,7 +20,7 @@ use payload_processor::sparse_trie::StateRootComputeOutcome;
|
||||
use persistence_state::CurrentPersistenceAction;
|
||||
use precompile_cache::{CachedPrecompile, PrecompileCacheMap};
|
||||
use reth_chain_state::{
|
||||
CanonicalInMemoryState, ExecutedBlock, ExecutedBlockWithTrieUpdates, ExecutedTrieUpdates,
|
||||
CanonicalInMemoryState, ExecutedBlock, ExecutedBlockWithTrieUpdates,
|
||||
MemoryOverlayStateProvider, NewCanonicalChain,
|
||||
};
|
||||
use reth_consensus::{Consensus, FullConsensus};
|
||||
@@ -49,7 +49,6 @@ use reth_trie_db::{DatabaseHashedPostState, StateCommitment};
|
||||
use reth_trie_parallel::root::{ParallelStateRoot, ParallelStateRootError};
|
||||
use state::TreeState;
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
fmt::Debug,
|
||||
sync::{
|
||||
mpsc::{Receiver, RecvError, RecvTimeoutError, Sender},
|
||||
@@ -728,16 +727,11 @@ where
|
||||
/// extension of the canonical chain.
|
||||
/// * walking back from the current head to verify that the target hash is not already part of
|
||||
/// the canonical chain.
|
||||
///
|
||||
/// The header is required as an arg, because we might be checking that the header is a fork
|
||||
/// block before it's in the tree state and before it's in the database.
|
||||
fn is_fork(&self, target_header: &SealedHeader<N::BlockHeader>) -> ProviderResult<bool> {
|
||||
let target_hash = target_header.hash();
|
||||
fn is_fork(&self, target_hash: B256) -> ProviderResult<bool> {
|
||||
// verify that the given hash is not part of an extension of the canon chain.
|
||||
let canonical_head = self.state.tree_state.canonical_head();
|
||||
let mut current_hash;
|
||||
let mut current_block = Cow::Borrowed(target_header);
|
||||
loop {
|
||||
let mut current_hash = target_hash;
|
||||
while let Some(current_block) = self.sealed_header_by_hash(current_hash)? {
|
||||
if current_block.hash() == canonical_head.hash {
|
||||
return Ok(false)
|
||||
}
|
||||
@@ -746,9 +740,6 @@ where
|
||||
break
|
||||
}
|
||||
current_hash = current_block.parent_hash();
|
||||
|
||||
let Some(next_block) = self.sealed_header_by_hash(current_hash)? else { break };
|
||||
current_block = Cow::Owned(next_block);
|
||||
}
|
||||
|
||||
// verify that the given hash is not already part of canonical chain stored in memory
|
||||
@@ -764,26 +755,6 @@ where
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
/// Check if the given block has any ancestors with missing trie updates.
|
||||
fn has_ancestors_with_missing_trie_updates(
|
||||
&self,
|
||||
target_header: &SealedHeader<N::BlockHeader>,
|
||||
) -> bool {
|
||||
// Walk back through the chain starting from the parent of the target block
|
||||
let mut current_hash = target_header.parent_hash();
|
||||
while let Some(block) = self.state.tree_state.blocks_by_hash.get(¤t_hash) {
|
||||
// Check if this block is missing trie updates
|
||||
if block.trie.is_missing() {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Move to the parent block
|
||||
current_hash = block.recovered_block().parent_hash();
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
/// Returns the persisting kind for the input block.
|
||||
fn persisting_kind_for(&self, block: &N::BlockHeader) -> PersistingKind {
|
||||
// Check that we're currently persisting.
|
||||
@@ -1050,7 +1021,7 @@ where
|
||||
if let Some(new_tip_num) = self.find_disk_reorg()? {
|
||||
self.remove_blocks(new_tip_num)
|
||||
} else if self.should_persist() {
|
||||
let blocks_to_persist = self.get_canonical_blocks_to_persist()?;
|
||||
let blocks_to_persist = self.get_canonical_blocks_to_persist();
|
||||
self.persist_blocks(blocks_to_persist);
|
||||
}
|
||||
}
|
||||
@@ -1377,20 +1348,9 @@ where
|
||||
}
|
||||
|
||||
/// Returns a batch of consecutive canonical blocks to persist in the range
|
||||
/// `(last_persisted_number .. canonical_head - threshold]`. The expected
|
||||
/// `(last_persisted_number .. canonical_head - threshold]` . The expected
|
||||
/// order is oldest -> newest.
|
||||
///
|
||||
/// For those blocks that didn't have the trie updates calculated, runs the state root
|
||||
/// calculation, and saves the trie updates.
|
||||
///
|
||||
/// Returns an error if the state root calculation fails.
|
||||
fn get_canonical_blocks_to_persist(
|
||||
&mut self,
|
||||
) -> Result<Vec<ExecutedBlockWithTrieUpdates<N>>, AdvancePersistenceError> {
|
||||
// We will calculate the state root using the database, so we need to be sure there are no
|
||||
// changes
|
||||
debug_assert!(!self.persistence_state.in_progress());
|
||||
|
||||
fn get_canonical_blocks_to_persist(&self) -> Vec<ExecutedBlockWithTrieUpdates<N>> {
|
||||
let mut blocks_to_persist = Vec::new();
|
||||
let mut current_hash = self.state.tree_state.canonical_block_hash();
|
||||
let last_persisted_number = self.persistence_state.last_persisted_block.number;
|
||||
@@ -1413,51 +1373,10 @@ where
|
||||
current_hash = block.recovered_block().parent_hash();
|
||||
}
|
||||
|
||||
// Reverse the order so that the oldest block comes first
|
||||
// reverse the order so that the oldest block comes first
|
||||
blocks_to_persist.reverse();
|
||||
|
||||
// Calculate missing trie updates
|
||||
for block in &mut blocks_to_persist {
|
||||
if block.trie.is_present() {
|
||||
continue
|
||||
}
|
||||
|
||||
debug!(
|
||||
target: "engine::tree",
|
||||
block = ?block.recovered_block().num_hash(),
|
||||
"Calculating trie updates before persisting"
|
||||
);
|
||||
|
||||
let provider = self
|
||||
.state_provider_builder(block.recovered_block().parent_hash())?
|
||||
.ok_or(AdvancePersistenceError::MissingAncestor(
|
||||
block.recovered_block().parent_hash(),
|
||||
))?
|
||||
.build()?;
|
||||
|
||||
let mut trie_input = self.compute_trie_input(
|
||||
self.persisting_kind_for(block.recovered_block().header()),
|
||||
self.provider.database_provider_ro()?,
|
||||
block.recovered_block().parent_hash(),
|
||||
)?;
|
||||
// Extend with block we are generating trie updates for.
|
||||
trie_input.append_ref(block.hashed_state());
|
||||
let (_root, updates) = provider.state_root_from_nodes_with_updates(trie_input)?;
|
||||
debug_assert_eq!(_root, block.recovered_block().state_root());
|
||||
|
||||
// Update trie updates in both tree state and blocks to persist that we return
|
||||
let trie_updates = Arc::new(updates);
|
||||
let tree_state_block = self
|
||||
.state
|
||||
.tree_state
|
||||
.blocks_by_hash
|
||||
.get_mut(&block.recovered_block().hash())
|
||||
.expect("blocks to persist are constructed from tree state blocks");
|
||||
tree_state_block.trie.set_present(trie_updates.clone());
|
||||
block.trie.set_present(trie_updates);
|
||||
}
|
||||
|
||||
Ok(blocks_to_persist)
|
||||
blocks_to_persist
|
||||
}
|
||||
|
||||
/// This clears the blocks from the in-memory tree state that have been persisted to the
|
||||
@@ -1909,10 +1828,7 @@ where
|
||||
.persisted_trie_updates
|
||||
.get(&block.recovered_block.hash())
|
||||
.cloned()?;
|
||||
Some(ExecutedBlockWithTrieUpdates {
|
||||
block: block.clone(),
|
||||
trie: ExecutedTrieUpdates::Present(trie),
|
||||
})
|
||||
Some(ExecutedBlockWithTrieUpdates { block: block.clone(), trie })
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
self.reinsert_reorged_blocks(old);
|
||||
@@ -2175,7 +2091,7 @@ where
|
||||
let trie_input_start = Instant::now();
|
||||
let res = self.compute_trie_input(
|
||||
persisting_kind,
|
||||
ensure_ok!(consistent_view.provider_ro()),
|
||||
consistent_view.clone(),
|
||||
block.header().parent_hash(),
|
||||
);
|
||||
let trie_input = match res {
|
||||
@@ -2325,25 +2241,13 @@ where
|
||||
// terminate prewarming task with good state output
|
||||
handle.terminate_caching(Some(output.state.clone()));
|
||||
|
||||
let is_fork = ensure_ok!(self.is_fork(block.sealed_header()));
|
||||
let missing_trie_updates =
|
||||
self.has_ancestors_with_missing_trie_updates(block.sealed_header());
|
||||
// If the block is a fork or has ancestors with missing trie updates, we don't save the trie
|
||||
// updates, because they may be incorrect. Instead, they will be recomputed on persistence.
|
||||
let save_trie_updates = !(is_fork || missing_trie_updates);
|
||||
|
||||
let trie_updates = if save_trie_updates {
|
||||
ExecutedTrieUpdates::Present(Arc::new(trie_output))
|
||||
} else {
|
||||
ExecutedTrieUpdates::Missing
|
||||
};
|
||||
let executed: ExecutedBlockWithTrieUpdates<N> = ExecutedBlockWithTrieUpdates {
|
||||
block: ExecutedBlock {
|
||||
recovered_block: Arc::new(block),
|
||||
execution_output: Arc::new(ExecutionOutcome::from((output, block_num_hash.number))),
|
||||
hashed_state: Arc::new(hashed_state),
|
||||
},
|
||||
trie: trie_updates,
|
||||
trie: Arc::new(trie_output),
|
||||
};
|
||||
|
||||
// if the parent is the canonical head, we can insert the block as the pending block
|
||||
@@ -2358,6 +2262,10 @@ where
|
||||
|
||||
// emit insert event
|
||||
let elapsed = start.elapsed();
|
||||
let is_fork = match self.is_fork(block_num_hash.hash) {
|
||||
Ok(val) => val,
|
||||
Err(e) => return Err((e.into(), executed.block.recovered_block().clone())),
|
||||
};
|
||||
let engine_event = if is_fork {
|
||||
BeaconConsensusEngineEvent::ForkBlockAdded(executed, elapsed)
|
||||
} else {
|
||||
@@ -2423,7 +2331,7 @@ where
|
||||
let consistent_view = ConsistentDbView::new_with_latest_tip(self.provider.clone())?;
|
||||
|
||||
let mut input =
|
||||
self.compute_trie_input(persisting_kind, consistent_view.provider_ro()?, parent_hash)?;
|
||||
self.compute_trie_input(persisting_kind, consistent_view.clone(), parent_hash)?;
|
||||
// Extend with block we are validating root for.
|
||||
input.append_ref(hashed_state);
|
||||
|
||||
@@ -2445,14 +2353,15 @@ where
|
||||
/// block.
|
||||
/// 3. Once in-memory blocks are collected and optionally filtered, we compute the
|
||||
/// [`HashedPostState`] from them.
|
||||
fn compute_trie_input<TP: DBProvider + BlockNumReader>(
|
||||
fn compute_trie_input(
|
||||
&self,
|
||||
persisting_kind: PersistingKind,
|
||||
provider: TP,
|
||||
consistent_view: ConsistentDbView<P>,
|
||||
parent_hash: B256,
|
||||
) -> ProviderResult<TrieInput> {
|
||||
) -> Result<TrieInput, ParallelStateRootError> {
|
||||
let mut input = TrieInput::default();
|
||||
|
||||
let provider = consistent_view.provider_ro()?;
|
||||
let best_block_number = provider.best_block_number()?;
|
||||
|
||||
let (mut historical, mut blocks) = self
|
||||
@@ -2523,9 +2432,9 @@ where
|
||||
input.append(revert_state);
|
||||
|
||||
// Extend with contents of parent in-memory blocks.
|
||||
input.extend_with_blocks(
|
||||
blocks.iter().rev().map(|block| (block.hashed_state(), block.trie_updates())),
|
||||
);
|
||||
for block in blocks.iter().rev() {
|
||||
input.append_cached_ref(block.trie_updates(), block.hashed_state())
|
||||
}
|
||||
|
||||
Ok(input)
|
||||
}
|
||||
@@ -2893,7 +2802,7 @@ mod tests {
|
||||
use reth_node_ethereum::EthereumEngineValidator;
|
||||
use reth_primitives_traits::Block as _;
|
||||
use reth_provider::test_utils::MockEthProvider;
|
||||
use reth_trie::HashedPostState;
|
||||
use reth_trie::{updates::TrieUpdates, HashedPostState};
|
||||
use std::{
|
||||
collections::BTreeMap,
|
||||
str::FromStr,
|
||||
@@ -3673,7 +3582,7 @@ mod tests {
|
||||
execution_output: Arc::new(ExecutionOutcome::default()),
|
||||
hashed_state: Arc::new(HashedPostState::default()),
|
||||
},
|
||||
trie: ExecutedTrieUpdates::empty(),
|
||||
trie: Arc::new(TrieUpdates::default()),
|
||||
});
|
||||
}
|
||||
test_harness.tree.state.tree_state.set_canonical_head(chain_a.last().unwrap().num_hash());
|
||||
@@ -3685,7 +3594,7 @@ mod tests {
|
||||
execution_output: Arc::new(ExecutionOutcome::default()),
|
||||
hashed_state: Arc::new(HashedPostState::default()),
|
||||
},
|
||||
trie: ExecutedTrieUpdates::empty(),
|
||||
trie: Arc::new(TrieUpdates::default()),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -3735,7 +3644,7 @@ mod tests {
|
||||
.with_persistence_threshold(persistence_threshold)
|
||||
.with_memory_block_buffer_target(memory_block_buffer_target);
|
||||
|
||||
let blocks_to_persist = test_harness.tree.get_canonical_blocks_to_persist().unwrap();
|
||||
let blocks_to_persist = test_harness.tree.get_canonical_blocks_to_persist();
|
||||
|
||||
let expected_blocks_to_persist_length: usize =
|
||||
(canonical_head_number - memory_block_buffer_target - last_persisted_block_number)
|
||||
@@ -3756,7 +3665,7 @@ mod tests {
|
||||
|
||||
assert!(test_harness.tree.state.tree_state.block_by_hash(fork_block_hash).is_some());
|
||||
|
||||
let blocks_to_persist = test_harness.tree.get_canonical_blocks_to_persist().unwrap();
|
||||
let blocks_to_persist = test_harness.tree.get_canonical_blocks_to_persist();
|
||||
assert_eq!(blocks_to_persist.len(), expected_blocks_to_persist_length);
|
||||
|
||||
// check that the fork block is not included in the blocks to persist
|
||||
@@ -4141,7 +4050,7 @@ mod tests {
|
||||
test_harness.check_canon_head(chain_b_tip_hash);
|
||||
|
||||
// verify that chain A is now considered a fork
|
||||
assert!(test_harness.tree.is_fork(chain_a.last().unwrap().sealed_header()).unwrap());
|
||||
assert!(test_harness.tree.is_fork(chain_a.last().unwrap().hash()).unwrap());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
|
||||
@@ -210,20 +210,13 @@ impl<N: NodePrimitives> TreeState<N> {
|
||||
while let Some(executed) = self.blocks_by_hash.get(¤t_block) {
|
||||
current_block = executed.recovered_block().parent_hash();
|
||||
if executed.recovered_block().number() <= upper_bound {
|
||||
let num_hash = executed.recovered_block().num_hash();
|
||||
debug!(target: "engine::tree", ?num_hash, "Attempting to remove block walking back from the head");
|
||||
if let Some((mut removed, _)) =
|
||||
self.remove_by_hash(executed.recovered_block().hash())
|
||||
{
|
||||
debug!(target: "engine::tree", ?num_hash, "Removed block walking back from the head");
|
||||
debug!(target: "engine::tree", num_hash=?executed.recovered_block().num_hash(), "Attempting to remove block walking back from the head");
|
||||
if let Some((removed, _)) = self.remove_by_hash(executed.recovered_block().hash()) {
|
||||
debug!(target: "engine::tree", num_hash=?removed.recovered_block().num_hash(), "Removed block walking back from the head");
|
||||
// finally, move the trie updates
|
||||
let Some(trie_updates) = removed.trie.take_present() else {
|
||||
debug!(target: "engine::tree", ?num_hash, "No trie updates found for persisted block");
|
||||
continue;
|
||||
};
|
||||
self.persisted_trie_updates.insert(
|
||||
removed.recovered_block().hash(),
|
||||
(removed.recovered_block().number(), trie_updates),
|
||||
(removed.recovered_block().number(), removed.trie),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ use alloy_rpc_types_debug::ExecutionWitness;
|
||||
use alloy_rpc_types_engine::PayloadId;
|
||||
use op_alloy_rpc_types_engine::OpPayloadAttributes;
|
||||
use reth_basic_payload_builder::*;
|
||||
use reth_chain_state::{ExecutedBlock, ExecutedBlockWithTrieUpdates, ExecutedTrieUpdates};
|
||||
use reth_chain_state::{ExecutedBlock, ExecutedBlockWithTrieUpdates};
|
||||
use reth_chainspec::{ChainSpecProvider, EthChainSpec};
|
||||
use reth_evm::{
|
||||
execute::{
|
||||
@@ -341,7 +341,7 @@ impl<Txs> OpBuilder<'_, Txs> {
|
||||
execution_output: Arc::new(execution_outcome),
|
||||
hashed_state: Arc::new(hashed_state),
|
||||
},
|
||||
trie: ExecutedTrieUpdates::Present(Arc::new(trie_updates)),
|
||||
trie: Arc::new(trie_updates),
|
||||
};
|
||||
|
||||
let no_tx_pool = ctx.attributes().no_tx_pool;
|
||||
|
||||
@@ -11,9 +11,7 @@
|
||||
use alloy_consensus::BlockHeader as _;
|
||||
use alloy_primitives::{Bytes, B256};
|
||||
use parking_lot::Mutex;
|
||||
use reth_chain_state::{
|
||||
ExecutedBlock, ExecutedBlockWithTrieUpdates, ExecutedTrieUpdates, MemoryOverlayStateProvider,
|
||||
};
|
||||
use reth_chain_state::{ExecutedBlock, ExecutedBlockWithTrieUpdates, MemoryOverlayStateProvider};
|
||||
use reth_errors::{ProviderError, ProviderResult};
|
||||
use reth_ethereum_primitives::{Block, BlockBody, EthPrimitives};
|
||||
use reth_evm::{execute::Executor, ConfigureEvm};
|
||||
@@ -130,7 +128,7 @@ where
|
||||
recovered_block: invalid,
|
||||
..Default::default()
|
||||
},
|
||||
trie: ExecutedTrieUpdates::empty(),
|
||||
..Default::default()
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -166,7 +164,7 @@ where
|
||||
let witness_state_provider = self.provider.state_by_block_hash(ancestor_hash)?;
|
||||
let mut trie_input = TrieInput::default();
|
||||
for block in executed_ancestors.into_iter().rev() {
|
||||
trie_input.append_cached_ref(block.trie.as_ref().unwrap(), &block.hashed_state);
|
||||
trie_input.append_cached_ref(&block.trie, &block.hashed_state);
|
||||
}
|
||||
let mut hashed_state = db.into_state();
|
||||
hashed_state.extend(record.hashed_state);
|
||||
|
||||
@@ -134,9 +134,6 @@ pub enum ProviderError {
|
||||
/// Received invalid output from configured storage implementation.
|
||||
#[error("received invalid output from storage")]
|
||||
InvalidStorageOutput,
|
||||
/// Missing trie updates.
|
||||
#[error("missing trie updates for block {0}")]
|
||||
MissingTrieUpdates(B256),
|
||||
/// Any other error type wrapped into a cloneable [`AnyError`].
|
||||
#[error(transparent)]
|
||||
Other(#[from] AnyError),
|
||||
|
||||
@@ -785,8 +785,7 @@ mod tests {
|
||||
use rand::Rng;
|
||||
use reth_chain_state::{
|
||||
test_utils::TestBlockBuilder, CanonStateNotification, CanonStateSubscriptions,
|
||||
CanonicalInMemoryState, ExecutedBlock, ExecutedBlockWithTrieUpdates, ExecutedTrieUpdates,
|
||||
NewCanonicalChain,
|
||||
CanonicalInMemoryState, ExecutedBlock, ExecutedBlockWithTrieUpdates, NewCanonicalChain,
|
||||
};
|
||||
use reth_chainspec::{
|
||||
ChainSpec, ChainSpecBuilder, ChainSpecProvider, EthereumHardfork, MAINNET,
|
||||
@@ -937,7 +936,7 @@ mod tests {
|
||||
Arc::new(RecoveredBlock::new_sealed(block.clone(), senders)),
|
||||
execution_outcome.into(),
|
||||
Default::default(),
|
||||
ExecutedTrieUpdates::empty(),
|
||||
Default::default(),
|
||||
)
|
||||
})
|
||||
.collect(),
|
||||
@@ -1069,7 +1068,7 @@ mod tests {
|
||||
)),
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
ExecutedTrieUpdates::empty(),
|
||||
Default::default(),
|
||||
)],
|
||||
};
|
||||
provider.canonical_in_memory_state.update_chain(chain);
|
||||
@@ -1107,7 +1106,7 @@ mod tests {
|
||||
execution_output: Default::default(),
|
||||
hashed_state: Default::default(),
|
||||
},
|
||||
trie: ExecutedTrieUpdates::empty(),
|
||||
trie: Default::default(),
|
||||
});
|
||||
|
||||
// Now the last block should be found in memory
|
||||
@@ -1165,7 +1164,7 @@ mod tests {
|
||||
)),
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
ExecutedTrieUpdates::empty(),
|
||||
Default::default(),
|
||||
)],
|
||||
};
|
||||
provider.canonical_in_memory_state.update_chain(chain);
|
||||
@@ -1221,7 +1220,7 @@ mod tests {
|
||||
execution_output: Default::default(),
|
||||
hashed_state: Default::default(),
|
||||
},
|
||||
trie: ExecutedTrieUpdates::empty(),
|
||||
trie: Default::default(),
|
||||
});
|
||||
|
||||
// Assertions related to the pending block
|
||||
@@ -1301,7 +1300,7 @@ mod tests {
|
||||
)),
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
ExecutedTrieUpdates::empty(),
|
||||
Default::default(),
|
||||
)],
|
||||
};
|
||||
provider.canonical_in_memory_state.update_chain(chain);
|
||||
@@ -1875,7 +1874,7 @@ mod tests {
|
||||
..Default::default()
|
||||
}),
|
||||
Default::default(),
|
||||
ExecutedTrieUpdates::empty(),
|
||||
Default::default(),
|
||||
)
|
||||
})
|
||||
.unwrap()],
|
||||
@@ -2004,7 +2003,7 @@ mod tests {
|
||||
execution_output: Default::default(),
|
||||
hashed_state: Default::default(),
|
||||
},
|
||||
trie: ExecutedTrieUpdates::empty(),
|
||||
trie: Default::default(),
|
||||
},
|
||||
);
|
||||
|
||||
@@ -2101,7 +2100,7 @@ mod tests {
|
||||
execution_output: Default::default(),
|
||||
hashed_state: Default::default(),
|
||||
},
|
||||
trie: ExecutedTrieUpdates::empty(),
|
||||
trie: Default::default(),
|
||||
});
|
||||
|
||||
// Set the safe block in memory
|
||||
|
||||
@@ -1491,9 +1491,7 @@ mod tests {
|
||||
use alloy_primitives::B256;
|
||||
use itertools::Itertools;
|
||||
use rand::Rng;
|
||||
use reth_chain_state::{
|
||||
ExecutedBlock, ExecutedBlockWithTrieUpdates, ExecutedTrieUpdates, NewCanonicalChain,
|
||||
};
|
||||
use reth_chain_state::{ExecutedBlock, ExecutedBlockWithTrieUpdates, NewCanonicalChain};
|
||||
use reth_db_api::models::AccountBeforeTx;
|
||||
use reth_ethereum_primitives::Block;
|
||||
use reth_execution_types::ExecutionOutcome;
|
||||
@@ -1603,7 +1601,7 @@ mod tests {
|
||||
)),
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
ExecutedTrieUpdates::empty(),
|
||||
Default::default(),
|
||||
)],
|
||||
};
|
||||
consistent_provider.canonical_in_memory_state.update_chain(chain);
|
||||
@@ -1647,7 +1645,7 @@ mod tests {
|
||||
execution_output: Default::default(),
|
||||
hashed_state: Default::default(),
|
||||
},
|
||||
trie: ExecutedTrieUpdates::empty(),
|
||||
trie: Default::default(),
|
||||
});
|
||||
|
||||
// Now the last block should be found in memory
|
||||
@@ -1713,7 +1711,7 @@ mod tests {
|
||||
)),
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
ExecutedTrieUpdates::empty(),
|
||||
Default::default(),
|
||||
)],
|
||||
};
|
||||
consistent_provider.canonical_in_memory_state.update_chain(chain);
|
||||
@@ -1828,7 +1826,7 @@ mod tests {
|
||||
..Default::default()
|
||||
}),
|
||||
Default::default(),
|
||||
ExecutedTrieUpdates::empty(),
|
||||
Default::default(),
|
||||
)
|
||||
})
|
||||
.unwrap()],
|
||||
|
||||
@@ -6,7 +6,7 @@ use crate::{
|
||||
use alloy_consensus::BlockHeader;
|
||||
use reth_chain_state::{ExecutedBlock, ExecutedBlockWithTrieUpdates};
|
||||
use reth_db_api::transaction::{DbTx, DbTxMut};
|
||||
use reth_errors::{ProviderError, ProviderResult};
|
||||
use reth_errors::ProviderResult;
|
||||
use reth_primitives_traits::{NodePrimitives, SignedTransaction};
|
||||
use reth_static_file_types::StaticFileSegment;
|
||||
use reth_storage_api::{DBProvider, StageCheckpointWriter, TransactionsProviderExt};
|
||||
@@ -165,7 +165,6 @@ where
|
||||
trie,
|
||||
} in blocks
|
||||
{
|
||||
let block_hash = recovered_block.hash();
|
||||
self.database()
|
||||
.insert_block(Arc::unwrap_or_clone(recovered_block), StorageLocation::Both)?;
|
||||
|
||||
@@ -180,9 +179,7 @@ where
|
||||
// insert hashes and intermediate merkle nodes
|
||||
self.database()
|
||||
.write_hashed_state(&Arc::unwrap_or_clone(hashed_state).into_sorted())?;
|
||||
self.database().write_trie_updates(
|
||||
trie.as_ref().ok_or(ProviderError::MissingTrieUpdates(block_hash))?,
|
||||
)?;
|
||||
self.database().write_trie_updates(&trie)?;
|
||||
}
|
||||
|
||||
// update history indices
|
||||
|
||||
@@ -84,7 +84,10 @@ std = [
|
||||
"revm-database/std",
|
||||
"revm-state/std",
|
||||
]
|
||||
eip1186 = ["alloy-rpc-types-eth/serde", "dep:alloy-serde"]
|
||||
eip1186 = [
|
||||
"alloy-rpc-types-eth/serde",
|
||||
"dep:alloy-serde",
|
||||
]
|
||||
serde = [
|
||||
"dep:serde",
|
||||
"bytes?/serde",
|
||||
@@ -98,7 +101,10 @@ serde = [
|
||||
"revm-database/serde",
|
||||
"revm-state/serde",
|
||||
]
|
||||
reth-codec = ["dep:reth-codecs", "dep:bytes"]
|
||||
reth-codec = [
|
||||
"dep:reth-codecs",
|
||||
"dep:bytes",
|
||||
]
|
||||
serde-bincode-compat = [
|
||||
"serde",
|
||||
"reth-primitives-traits/serde-bincode-compat",
|
||||
|
||||
@@ -31,47 +31,6 @@ impl TrieInput {
|
||||
Self { nodes: TrieUpdates::default(), state, prefix_sets }
|
||||
}
|
||||
|
||||
/// Create new trie input from the provided blocks, from oldest to newest. See the documentation
|
||||
/// for [`Self::extend_with_blocks`] for details.
|
||||
pub fn from_blocks<'a>(
|
||||
blocks: impl IntoIterator<Item = (&'a HashedPostState, Option<&'a TrieUpdates>)>,
|
||||
) -> Self {
|
||||
let mut input = Self::default();
|
||||
input.extend_with_blocks(blocks);
|
||||
input
|
||||
}
|
||||
|
||||
/// Extend the trie input with the provided blocks, from oldest to newest.
|
||||
///
|
||||
/// When encountering the first block with missing trie updates, the trie input will be extended
|
||||
/// with prefix sets constructed from the state of this block and the state itself, **without**
|
||||
/// trie updates. Subsequent blocks will be appended in the same way, i.e. only prefix sets
|
||||
/// constructed from the state and the state itself.
|
||||
pub fn extend_with_blocks<'a>(
|
||||
&mut self,
|
||||
blocks: impl IntoIterator<Item = (&'a HashedPostState, Option<&'a TrieUpdates>)>,
|
||||
) {
|
||||
let mut extend_trie_updates = true;
|
||||
for (hashed_state, trie_updates) in blocks {
|
||||
if let Some(nodes) = trie_updates.as_ref().filter(|_| extend_trie_updates) {
|
||||
self.append_cached_ref(nodes, hashed_state);
|
||||
} else {
|
||||
self.append_ref(hashed_state);
|
||||
extend_trie_updates = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Prepend another trie input to the current one.
|
||||
pub fn prepend_self(&mut self, mut other: Self) {
|
||||
core::mem::swap(&mut self.nodes, &mut other.nodes);
|
||||
self.nodes.extend(other.nodes);
|
||||
core::mem::swap(&mut self.state, &mut other.state);
|
||||
self.state.extend(other.state);
|
||||
// No need to swap prefix sets, as they will be sorted and deduplicated.
|
||||
self.prefix_sets.extend(other.prefix_sets);
|
||||
}
|
||||
|
||||
/// Prepend state to the input and extend the prefix sets.
|
||||
pub fn prepend(&mut self, mut state: HashedPostState) {
|
||||
self.prefix_sets.extend(state.construct_prefix_sets());
|
||||
|
||||
@@ -13,7 +13,7 @@ use alloy_consensus::EMPTY_ROOT_HASH;
|
||||
use alloy_primitives::{keccak256, Address, B256};
|
||||
use alloy_rlp::{BufMut, Encodable};
|
||||
use reth_execution_errors::{StateRootError, StorageRootError};
|
||||
use tracing::{trace, trace_span};
|
||||
use tracing::trace;
|
||||
|
||||
#[cfg(feature = "metrics")]
|
||||
use crate::metrics::{StateRootMetrics, TrieRootMetrics};
|
||||
@@ -396,10 +396,7 @@ where
|
||||
self,
|
||||
retain_updates: bool,
|
||||
) -> Result<(B256, usize, StorageTrieUpdates), StorageRootError> {
|
||||
let span = trace_span!(target: "trie::storage_root", "Storage trie", hashed_address = ?self.hashed_address);
|
||||
let _enter = span.enter();
|
||||
|
||||
trace!(target: "trie::storage_root", "calculating storage root");
|
||||
trace!(target: "trie::storage_root", hashed_address = ?self.hashed_address, "calculating storage root");
|
||||
|
||||
let mut hashed_storage_cursor =
|
||||
self.hashed_cursor_factory.hashed_storage_cursor(self.hashed_address)?;
|
||||
|
||||
Reference in New Issue
Block a user