Skip to content

Commit 6c85597

Browse files
authored
revert: fix(engine): recompute trie updates for forked blocks (#16500) (#16565)
1 parent 91d8ee2 commit 6c85597

File tree

15 files changed

+115
-318
lines changed

15 files changed

+115
-318
lines changed

crates/chain-state/src/in_memory.rs

Lines changed: 14 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -798,83 +798,28 @@ impl<N: NodePrimitives> ExecutedBlock<N> {
798798
}
799799
}
800800

801-
/// Trie updates that result from calculating the state root for the block.
802-
#[derive(Debug, Clone, PartialEq, Eq)]
803-
pub enum ExecutedTrieUpdates {
804-
/// Trie updates present. State root was calculated, and the trie updates can be applied to the
805-
/// database.
806-
Present(Arc<TrieUpdates>),
807-
/// Trie updates missing. State root was calculated, but the trie updates cannot be applied to
808-
/// the current database state. To apply the updates, the state root must be recalculated, and
809-
/// new trie updates must be generated.
810-
///
811-
/// This can happen when processing fork chain blocks that are building on top of the
812-
/// historical database state. Since we don't store the historical trie state, we cannot
813-
/// generate the trie updates for it.
814-
Missing,
815-
}
816-
817-
impl ExecutedTrieUpdates {
818-
/// Creates a [`ExecutedTrieUpdates`] with present but empty trie updates.
819-
pub fn empty() -> Self {
820-
Self::Present(Arc::default())
821-
}
822-
823-
/// Sets the trie updates to the provided value as present.
824-
pub fn set_present(&mut self, updates: Arc<TrieUpdates>) {
825-
*self = Self::Present(updates);
826-
}
827-
828-
/// Takes the present trie updates, leaving the state as missing.
829-
pub fn take_present(&mut self) -> Option<Arc<TrieUpdates>> {
830-
match self {
831-
Self::Present(updates) => {
832-
let updates = core::mem::take(updates);
833-
*self = Self::Missing;
834-
Some(updates)
835-
}
836-
Self::Missing => None,
837-
}
838-
}
839-
840-
/// Returns a reference to the trie updates if present.
841-
#[allow(clippy::missing_const_for_fn)]
842-
pub fn as_ref(&self) -> Option<&TrieUpdates> {
843-
match self {
844-
Self::Present(updates) => Some(updates),
845-
Self::Missing => None,
846-
}
847-
}
848-
849-
/// Returns `true` if the trie updates are present.
850-
pub const fn is_present(&self) -> bool {
851-
matches!(self, Self::Present(_))
852-
}
853-
854-
/// Returns `true` if the trie updates are missing.
855-
pub const fn is_missing(&self) -> bool {
856-
matches!(self, Self::Missing)
857-
}
858-
}
859-
860801
/// An [`ExecutedBlock`] with its [`TrieUpdates`].
861802
///
862803
/// We store it as separate type because [`TrieUpdates`] are only available for blocks stored in
863804
/// memory and can't be obtained for canonical persisted blocks.
864805
#[derive(
865-
Clone, Debug, PartialEq, Eq, derive_more::Deref, derive_more::DerefMut, derive_more::Into,
806+
Clone,
807+
Debug,
808+
PartialEq,
809+
Eq,
810+
Default,
811+
derive_more::Deref,
812+
derive_more::DerefMut,
813+
derive_more::Into,
866814
)]
867815
pub struct ExecutedBlockWithTrieUpdates<N: NodePrimitives = EthPrimitives> {
868816
/// Inner [`ExecutedBlock`].
869817
#[deref]
870818
#[deref_mut]
871819
#[into]
872820
pub block: ExecutedBlock<N>,
873-
/// Trie updates that result from calculating the state root for the block.
874-
///
875-
/// If [`ExecutedTrieUpdates::Missing`], the trie updates should be computed when persisting
876-
/// the block **on top of the canonical parent**.
877-
pub trie: ExecutedTrieUpdates,
821+
/// Trie updates that result of applying the block.
822+
pub trie: Arc<TrieUpdates>,
878823
}
879824

880825
impl<N: NodePrimitives> ExecutedBlockWithTrieUpdates<N> {
@@ -883,15 +828,15 @@ impl<N: NodePrimitives> ExecutedBlockWithTrieUpdates<N> {
883828
recovered_block: Arc<RecoveredBlock<N::Block>>,
884829
execution_output: Arc<ExecutionOutcome<N::Receipt>>,
885830
hashed_state: Arc<HashedPostState>,
886-
trie: ExecutedTrieUpdates,
831+
trie: Arc<TrieUpdates>,
887832
) -> Self {
888833
Self { block: ExecutedBlock { recovered_block, execution_output, hashed_state }, trie }
889834
}
890835

891-
/// Returns a reference to the trie updates for the block, if present.
836+
/// Returns a reference to the trie updates for the block
892837
#[inline]
893-
pub fn trie_updates(&self) -> Option<&TrieUpdates> {
894-
self.trie.as_ref()
838+
pub fn trie_updates(&self) -> &TrieUpdates {
839+
&self.trie
895840
}
896841

897842
/// Converts the value into [`SealedBlock`].

crates/chain-state/src/memory_overlay.rs

Lines changed: 32 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ pub struct MemoryOverlayStateProviderRef<
2626
/// The collection of executed parent blocks. Expected order is newest to oldest.
2727
pub(crate) in_memory: Vec<ExecutedBlockWithTrieUpdates<N>>,
2828
/// Lazy-loaded in-memory trie data.
29-
pub(crate) trie_input: OnceLock<TrieInput>,
29+
pub(crate) trie_state: OnceLock<MemoryOverlayTrieState>,
3030
}
3131

3232
/// 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> {
4545
historical: Box<dyn StateProvider + 'a>,
4646
in_memory: Vec<ExecutedBlockWithTrieUpdates<N>>,
4747
) -> Self {
48-
Self { historical, in_memory, trie_input: OnceLock::new() }
48+
Self { historical, in_memory, trie_state: OnceLock::new() }
4949
}
5050

5151
/// Turn this state provider into a state provider
@@ -54,14 +54,14 @@ impl<'a, N: NodePrimitives> MemoryOverlayStateProviderRef<'a, N> {
5454
}
5555

5656
/// Return lazy-loaded trie state aggregated from in-memory blocks.
57-
fn trie_input(&self) -> &TrieInput {
58-
self.trie_input.get_or_init(|| {
59-
TrieInput::from_blocks(
60-
self.in_memory
61-
.iter()
62-
.rev()
63-
.map(|block| (block.hashed_state.as_ref(), block.trie.as_ref())),
64-
)
57+
fn trie_state(&self) -> &MemoryOverlayTrieState {
58+
self.trie_state.get_or_init(|| {
59+
let mut trie_state = MemoryOverlayTrieState::default();
60+
for block in self.in_memory.iter().rev() {
61+
trie_state.state.extend_ref(block.hashed_state.as_ref());
62+
trie_state.nodes.extend_ref(block.trie.as_ref());
63+
}
64+
trie_state
6565
})
6666
}
6767
}
@@ -117,7 +117,8 @@ impl<N: NodePrimitives> StateRootProvider for MemoryOverlayStateProviderRef<'_,
117117
}
118118

119119
fn state_root_from_nodes(&self, mut input: TrieInput) -> ProviderResult<B256> {
120-
input.prepend_self(self.trie_input().clone());
120+
let MemoryOverlayTrieState { nodes, state } = self.trie_state().clone();
121+
input.prepend_cached(nodes, state);
121122
self.historical.state_root_from_nodes(input)
122123
}
123124

@@ -132,15 +133,16 @@ impl<N: NodePrimitives> StateRootProvider for MemoryOverlayStateProviderRef<'_,
132133
&self,
133134
mut input: TrieInput,
134135
) -> ProviderResult<(B256, TrieUpdates)> {
135-
input.prepend_self(self.trie_input().clone());
136+
let MemoryOverlayTrieState { nodes, state } = self.trie_state().clone();
137+
input.prepend_cached(nodes, state);
136138
self.historical.state_root_from_nodes_with_updates(input)
137139
}
138140
}
139141

140142
impl<N: NodePrimitives> StorageRootProvider for MemoryOverlayStateProviderRef<'_, N> {
141143
// TODO: Currently this does not reuse available in-memory trie nodes.
142144
fn storage_root(&self, address: Address, storage: HashedStorage) -> ProviderResult<B256> {
143-
let state = &self.trie_input().state;
145+
let state = &self.trie_state().state;
144146
let mut hashed_storage =
145147
state.storages.get(&keccak256(address)).cloned().unwrap_or_default();
146148
hashed_storage.extend(&storage);
@@ -154,7 +156,7 @@ impl<N: NodePrimitives> StorageRootProvider for MemoryOverlayStateProviderRef<'_
154156
slot: B256,
155157
storage: HashedStorage,
156158
) -> ProviderResult<reth_trie::StorageProof> {
157-
let state = &self.trie_input().state;
159+
let state = &self.trie_state().state;
158160
let mut hashed_storage =
159161
state.storages.get(&keccak256(address)).cloned().unwrap_or_default();
160162
hashed_storage.extend(&storage);
@@ -168,7 +170,7 @@ impl<N: NodePrimitives> StorageRootProvider for MemoryOverlayStateProviderRef<'_
168170
slots: &[B256],
169171
storage: HashedStorage,
170172
) -> ProviderResult<StorageMultiProof> {
171-
let state = &self.trie_input().state;
173+
let state = &self.trie_state().state;
172174
let mut hashed_storage =
173175
state.storages.get(&keccak256(address)).cloned().unwrap_or_default();
174176
hashed_storage.extend(&storage);
@@ -183,7 +185,8 @@ impl<N: NodePrimitives> StateProofProvider for MemoryOverlayStateProviderRef<'_,
183185
address: Address,
184186
slots: &[B256],
185187
) -> ProviderResult<AccountProof> {
186-
input.prepend_self(self.trie_input().clone());
188+
let MemoryOverlayTrieState { nodes, state } = self.trie_state().clone();
189+
input.prepend_cached(nodes, state);
187190
self.historical.proof(input, address, slots)
188191
}
189192

@@ -192,12 +195,14 @@ impl<N: NodePrimitives> StateProofProvider for MemoryOverlayStateProviderRef<'_,
192195
mut input: TrieInput,
193196
targets: MultiProofTargets,
194197
) -> ProviderResult<MultiProof> {
195-
input.prepend_self(self.trie_input().clone());
198+
let MemoryOverlayTrieState { nodes, state } = self.trie_state().clone();
199+
input.prepend_cached(nodes, state);
196200
self.historical.multiproof(input, targets)
197201
}
198202

199203
fn witness(&self, mut input: TrieInput, target: HashedPostState) -> ProviderResult<Vec<Bytes>> {
200-
input.prepend_self(self.trie_input().clone());
204+
let MemoryOverlayTrieState { nodes, state } = self.trie_state().clone();
205+
input.prepend_cached(nodes, state);
201206
self.historical.witness(input, target)
202207
}
203208
}
@@ -233,3 +238,12 @@ impl<N: NodePrimitives> StateProvider for MemoryOverlayStateProviderRef<'_, N> {
233238
self.historical.bytecode_by_hash(code_hash)
234239
}
235240
}
241+
242+
/// The collection of data necessary for trie-related operations for [`MemoryOverlayStateProvider`].
243+
#[derive(Clone, Default, Debug)]
244+
pub(crate) struct MemoryOverlayTrieState {
245+
/// The collection of aggregated in-memory trie updates.
246+
pub(crate) nodes: TrieUpdates,
247+
/// The collection of hashed state from in-memory blocks.
248+
pub(crate) state: HashedPostState,
249+
}

crates/chain-state/src/test_utils.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::{
22
in_memory::ExecutedBlockWithTrieUpdates, CanonStateNotification, CanonStateNotifications,
3-
CanonStateSubscriptions, ExecutedTrieUpdates,
3+
CanonStateSubscriptions,
44
};
55
use alloy_consensus::{
66
Header, SignableTransaction, Transaction as _, TxEip1559, TxReceipt, EMPTY_ROOT_HASH,
@@ -25,7 +25,7 @@ use reth_primitives_traits::{
2525
SignedTransaction,
2626
};
2727
use reth_storage_api::NodePrimitivesProvider;
28-
use reth_trie::{root::state_root_unhashed, HashedPostState};
28+
use reth_trie::{root::state_root_unhashed, updates::TrieUpdates, HashedPostState};
2929
use revm_database::BundleState;
3030
use revm_state::AccountInfo;
3131
use std::{
@@ -222,7 +222,7 @@ impl<N: NodePrimitives> TestBlockBuilder<N> {
222222
vec![Requests::default()],
223223
)),
224224
Arc::new(HashedPostState::default()),
225-
ExecutedTrieUpdates::empty(),
225+
Arc::new(TrieUpdates::default()),
226226
)
227227
}
228228

crates/engine/tree/src/tree/error.rs

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
//! Internal errors for the tree module.
22
33
use alloy_consensus::BlockHeader;
4-
use alloy_primitives::B256;
54
use reth_consensus::ConsensusError;
65
use reth_errors::{BlockExecutionError, BlockValidationError, ProviderError};
76
use reth_evm::execute::InternalBlockExecutionError;
@@ -18,20 +17,6 @@ pub enum AdvancePersistenceError {
1817
/// A provider error
1918
#[error(transparent)]
2019
Provider(#[from] ProviderError),
21-
/// Missing ancestor.
22-
///
23-
/// This error occurs when we need to compute the state root for a block with missing trie
24-
/// updates, but the ancestor block is not available. State root computation requires the state
25-
/// from the parent block as a starting point.
26-
///
27-
/// A block may be missing the trie updates when it's a fork chain block building on top of the
28-
/// historical database state. Since we don't store the historical trie state, we cannot
29-
/// generate the trie updates for it until the moment when database is unwound to the canonical
30-
/// chain.
31-
///
32-
/// Also see [`reth_chain_state::ExecutedTrieUpdates::Missing`].
33-
#[error("Missing ancestor with hash {0}")]
34-
MissingAncestor(B256),
3520
}
3621

3722
#[derive(thiserror::Error)]

0 commit comments

Comments
 (0)