mirror of
https://github.com/paradigmxyz/reth.git
synced 2026-04-30 03:01:58 -04:00
Compare commits
7 Commits
devnet4
...
yk/state_p
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
51cd4d1523 | ||
|
|
a6a7b6021c | ||
|
|
994ed1bbd8 | ||
|
|
29fdbdb7b3 | ||
|
|
3a82e17436 | ||
|
|
c5c9cd96d0 | ||
|
|
f84f6f02d9 |
@@ -86,7 +86,7 @@ where
|
||||
evm_config: C,
|
||||
) -> Self
|
||||
where
|
||||
V: EngineValidator<N::Payload>,
|
||||
V: EngineValidator<N::Payload, Provider = BlockchainProvider<N>>,
|
||||
C: ConfigureEvm<Primitives = N::Primitives> + 'static,
|
||||
{
|
||||
let engine_kind =
|
||||
|
||||
@@ -116,10 +116,10 @@ impl<N: NodePrimitives, P> StateProviderBuilder<N, P>
|
||||
where
|
||||
P: BlockReader + StateProviderFactory + StateReader + Clone,
|
||||
{
|
||||
/// Creates a new state provider from this builder.
|
||||
pub fn build(&self) -> ProviderResult<StateProviderBox> {
|
||||
/// Consumes the builder and creates a new state provider.
|
||||
pub fn build(self) -> ProviderResult<StateProviderBox> {
|
||||
let mut provider = self.provider_factory.state_by_block_hash(self.historical)?;
|
||||
if let Some(overlay) = self.overlay.clone() {
|
||||
if let Some(overlay) = self.overlay {
|
||||
provider = Box::new(MemoryOverlayStateProvider::new(provider, overlay))
|
||||
}
|
||||
Ok(provider)
|
||||
@@ -316,12 +316,14 @@ where
|
||||
+ HashedPostStateProvider
|
||||
+ TrieReader
|
||||
+ Clone
|
||||
+ Send
|
||||
+ Sync
|
||||
+ 'static,
|
||||
<P as DatabaseProviderFactory>::Provider:
|
||||
BlockReader<Block = N::Block, Header = N::BlockHeader>,
|
||||
C: ConfigureEvm<Primitives = N> + 'static,
|
||||
T: PayloadTypes<BuiltPayload: BuiltPayload<Primitives = N>>,
|
||||
V: EngineValidator<T>,
|
||||
V: EngineValidator<T, Provider = P>,
|
||||
{
|
||||
/// Creates a new [`EngineApiTreeHandler`].
|
||||
#[expect(clippy::too_many_arguments)]
|
||||
@@ -2475,7 +2477,7 @@ where
|
||||
&mut self,
|
||||
block_id: BlockWithParent,
|
||||
input: Input,
|
||||
execute: impl FnOnce(&mut V, Input, TreeCtx<'_, N>) -> Result<ExecutedBlock<N>, Err>,
|
||||
execute: impl FnOnce(&mut V, Input, TreeCtx<'_, N, P>) -> Result<ExecutedBlock<N>, Err>,
|
||||
convert_to_block: impl FnOnce(&mut Self, Input) -> Result<SealedBlock<N::Block>, Err>,
|
||||
) -> Result<InsertPayloadOk, Err>
|
||||
where
|
||||
@@ -2499,8 +2501,7 @@ where
|
||||
_ => {}
|
||||
};
|
||||
|
||||
// Ensure that the parent state is available.
|
||||
match self.state_provider_builder(block_id.parent) {
|
||||
let provider_builder = match self.state_provider_builder(block_id.parent) {
|
||||
Err(err) => {
|
||||
let block = convert_to_block(self, input)?;
|
||||
return Err(InsertBlockError::new(block, err.into()).into());
|
||||
@@ -2524,8 +2525,18 @@ where
|
||||
missing_ancestor,
|
||||
}))
|
||||
}
|
||||
Ok(Some(_)) => {}
|
||||
}
|
||||
Ok(Some(builder)) => builder,
|
||||
};
|
||||
|
||||
// Build the state provider. The builder is cloned because it's also needed for parallel
|
||||
// tasks.
|
||||
let state_provider = match provider_builder.clone().build() {
|
||||
Ok(provider) => provider,
|
||||
Err(err) => {
|
||||
let block = convert_to_block(self, input)?;
|
||||
return Err(InsertBlockError::new(block, err.into()).into());
|
||||
}
|
||||
};
|
||||
|
||||
// determine whether we are on a fork chain by comparing the block number with the
|
||||
// canonical head. This is a simple check that is sufficient for the event emission below.
|
||||
@@ -2533,7 +2544,12 @@ where
|
||||
// as this indicates there's already a canonical block at that height.
|
||||
let is_fork = block_id.block.number <= self.state.tree_state.current_canonical_head.number;
|
||||
|
||||
let ctx = TreeCtx::new(&mut self.state, &self.canonical_in_memory_state);
|
||||
let ctx = TreeCtx::with_precomputed(
|
||||
&mut self.state,
|
||||
&self.canonical_in_memory_state,
|
||||
state_provider,
|
||||
provider_builder,
|
||||
);
|
||||
|
||||
let start = Instant::now();
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ use reth_primitives_traits::{
|
||||
use reth_provider::{
|
||||
providers::OverlayStateProviderFactory, BlockExecutionOutput, BlockReader,
|
||||
DatabaseProviderFactory, DatabaseProviderROFactory, ExecutionOutcome, HashedPostStateProvider,
|
||||
ProviderError, PruneCheckpointReader, StageCheckpointReader, StateProvider,
|
||||
ProviderError, PruneCheckpointReader, StageCheckpointReader, StateProvider, StateProviderBox,
|
||||
StateProviderFactory, StateReader, TrieReader,
|
||||
};
|
||||
use reth_revm::db::State;
|
||||
@@ -55,30 +55,73 @@ use tracing::{debug, debug_span, error, info, instrument, trace, warn};
|
||||
/// Context providing access to tree state during validation.
|
||||
///
|
||||
/// This context is provided to the [`EngineValidator`] and includes the state of the tree's
|
||||
/// internals
|
||||
pub struct TreeCtx<'a, N: NodePrimitives> {
|
||||
/// internals.
|
||||
///
|
||||
/// The generic parameter `P` represents the provider type used for state lookups.
|
||||
pub struct TreeCtx<'a, N: NodePrimitives, P> {
|
||||
/// The engine API tree state
|
||||
state: &'a mut EngineApiTreeState<N>,
|
||||
/// Reference to the canonical in-memory state
|
||||
canonical_in_memory_state: &'a CanonicalInMemoryState<N>,
|
||||
/// Optional precomputed state provider and builder to avoid redundant lookups.
|
||||
/// This is set by [`crate::tree::EngineApiTreeHandler`] after validating parent state exists.
|
||||
pub precomputed: Option<StateProviderAndBuilder<N, P>>,
|
||||
}
|
||||
|
||||
impl<'a, N: NodePrimitives> std::fmt::Debug for TreeCtx<'a, N> {
|
||||
/// Precomputed state provider and builder for block validation.
|
||||
///
|
||||
/// Contains both the built state provider (for main execution) and the builder
|
||||
/// (for spawning parallel tasks that need their own providers).
|
||||
pub struct StateProviderAndBuilder<N: NodePrimitives, P> {
|
||||
/// The built state provider for main execution.
|
||||
pub provider: StateProviderBox,
|
||||
/// The builder for spawning parallel tasks.
|
||||
pub builder: StateProviderBuilder<N, P>,
|
||||
}
|
||||
|
||||
impl<N: NodePrimitives, P> std::fmt::Debug for StateProviderAndBuilder<N, P> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("TreeCtx")
|
||||
.field("state", &"EngineApiTreeState")
|
||||
.field("canonical_in_memory_state", &self.canonical_in_memory_state)
|
||||
f.debug_struct("StateProviderAndBuilder")
|
||||
.field("provider", &"StateProviderBox")
|
||||
.field("builder", &"StateProviderBuilder")
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, N: NodePrimitives> TreeCtx<'a, N> {
|
||||
/// Creates a new tree context
|
||||
impl<'a, N: NodePrimitives, P> std::fmt::Debug for TreeCtx<'a, N, P> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("TreeCtx")
|
||||
.field("state", &"EngineApiTreeState")
|
||||
.field("canonical_in_memory_state", &self.canonical_in_memory_state)
|
||||
.field("precomputed", &self.precomputed.as_ref().map(|_| "Some(...)"))
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, N: NodePrimitives, P> TreeCtx<'a, N, P> {
|
||||
/// Creates a new tree context.
|
||||
pub const fn new(
|
||||
state: &'a mut EngineApiTreeState<N>,
|
||||
canonical_in_memory_state: &'a CanonicalInMemoryState<N>,
|
||||
) -> Self {
|
||||
Self { state, canonical_in_memory_state }
|
||||
Self { state, canonical_in_memory_state, precomputed: None }
|
||||
}
|
||||
|
||||
/// Creates a new tree context with precomputed state provider and builder.
|
||||
///
|
||||
/// This is the preferred constructor when the provider and builder are available,
|
||||
/// as it avoids redundant state lookups in the validator.
|
||||
pub const fn with_precomputed(
|
||||
state: &'a mut EngineApiTreeState<N>,
|
||||
canonical_in_memory_state: &'a CanonicalInMemoryState<N>,
|
||||
provider: StateProviderBox,
|
||||
builder: StateProviderBuilder<N, P>,
|
||||
) -> Self {
|
||||
Self {
|
||||
state,
|
||||
canonical_in_memory_state,
|
||||
precomputed: Some(StateProviderAndBuilder { provider, builder }),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a reference to the engine tree state
|
||||
@@ -322,7 +365,7 @@ where
|
||||
pub fn validate_block_with_state<T: PayloadTypes<BuiltPayload: BuiltPayload<Primitives = N>>>(
|
||||
&mut self,
|
||||
input: BlockOrPayload<T>,
|
||||
mut ctx: TreeCtx<'_, N>,
|
||||
mut ctx: TreeCtx<'_, N, P>,
|
||||
) -> ValidationOutcome<N, InsertPayloadError<N::Block>>
|
||||
where
|
||||
V: PayloadValidator<T, Block = N::Block>,
|
||||
@@ -359,20 +402,29 @@ where
|
||||
let parent_hash = input.parent_hash();
|
||||
let block_num_hash = input.num_hash();
|
||||
|
||||
trace!(target: "engine::tree::payload_validator", "Fetching block state provider");
|
||||
trace!(target: "engine::tree::payload_validator", "Building state provider");
|
||||
let _enter =
|
||||
debug_span!(target: "engine::tree::payload_validator", "state provider").entered();
|
||||
let Some(provider_builder) =
|
||||
ensure_ok!(self.state_provider_builder(parent_hash, ctx.state()))
|
||||
else {
|
||||
// this is pre-validated in the tree
|
||||
return Err(InsertBlockError::new(
|
||||
self.convert_to_block(input)?,
|
||||
ProviderError::HeaderNotFound(parent_hash.into()).into(),
|
||||
)
|
||||
.into())
|
||||
|
||||
// Use precomputed state from TreeCtx (set by EngineApiTreeHandler) to avoid
|
||||
// redundant state lookups. Fall back to computing if not available (legacy callers).
|
||||
let (mut state_provider, provider_builder) = if let Some(precomputed) =
|
||||
ctx.precomputed.take()
|
||||
{
|
||||
(precomputed.provider, precomputed.builder)
|
||||
} else {
|
||||
// Legacy path: compute both builder and provider
|
||||
let Some(builder) = ensure_ok!(self.state_provider_builder(parent_hash, ctx.state()))
|
||||
else {
|
||||
return Err(InsertBlockError::new(
|
||||
self.convert_to_block(input)?,
|
||||
ProviderError::HeaderNotFound(parent_hash.into()).into(),
|
||||
)
|
||||
.into())
|
||||
};
|
||||
let provider = ensure_ok!(builder.clone().build());
|
||||
(provider, builder)
|
||||
};
|
||||
let mut state_provider = ensure_ok!(provider_builder.build());
|
||||
drop(_enter);
|
||||
|
||||
// Fetch parent block. This goes to memory most of the time unless the parent block is
|
||||
@@ -729,7 +781,7 @@ where
|
||||
block: &RecoveredBlock<N::Block>,
|
||||
parent_block: &SealedHeader<N::BlockHeader>,
|
||||
output: &BlockExecutionOutput<N::Receipt>,
|
||||
ctx: &mut TreeCtx<'_, N>,
|
||||
ctx: &mut TreeCtx<'_, N, P>,
|
||||
) -> Result<HashedPostState, InsertBlockErrorKind>
|
||||
where
|
||||
V: PayloadValidator<T, Block = N::Block>,
|
||||
@@ -1052,7 +1104,7 @@ where
|
||||
&self,
|
||||
block: RecoveredBlock<N::Block>,
|
||||
execution_outcome: Arc<ExecutionOutcome<N::Receipt>>,
|
||||
ctx: &TreeCtx<'_, N>,
|
||||
ctx: &TreeCtx<'_, N, P>,
|
||||
hashed_state: HashedPostState,
|
||||
trie_output: TrieUpdates,
|
||||
) -> ExecutedBlock<N> {
|
||||
@@ -1129,6 +1181,9 @@ pub trait EngineValidator<
|
||||
N: NodePrimitives = <<Types as PayloadTypes>::BuiltPayload as BuiltPayload>::Primitives,
|
||||
>: Send + Sync + 'static
|
||||
{
|
||||
/// The provider type used for state lookups.
|
||||
type Provider;
|
||||
|
||||
/// Validates the payload attributes with respect to the header.
|
||||
///
|
||||
/// By default, this enforces that the payload attributes timestamp is greater than the
|
||||
@@ -1161,14 +1216,14 @@ pub trait EngineValidator<
|
||||
fn validate_payload(
|
||||
&mut self,
|
||||
payload: Types::ExecutionData,
|
||||
ctx: TreeCtx<'_, N>,
|
||||
ctx: TreeCtx<'_, N, Self::Provider>,
|
||||
) -> ValidationOutcome<N>;
|
||||
|
||||
/// Validates a block downloaded from the network.
|
||||
fn validate_block(
|
||||
&mut self,
|
||||
block: SealedBlock<N::Block>,
|
||||
ctx: TreeCtx<'_, N>,
|
||||
ctx: TreeCtx<'_, N, Self::Provider>,
|
||||
) -> ValidationOutcome<N>;
|
||||
|
||||
/// Hook called after an executed block is inserted directly into the tree.
|
||||
@@ -1187,12 +1242,16 @@ where
|
||||
+ StateReader
|
||||
+ HashedPostStateProvider
|
||||
+ Clone
|
||||
+ Send
|
||||
+ Sync
|
||||
+ 'static,
|
||||
N: NodePrimitives,
|
||||
V: PayloadValidator<Types, Block = N::Block>,
|
||||
Evm: ConfigureEngineEvm<Types::ExecutionData, Primitives = N> + 'static,
|
||||
Types: PayloadTypes<BuiltPayload: BuiltPayload<Primitives = N>>,
|
||||
{
|
||||
type Provider = P;
|
||||
|
||||
fn validate_payload_attributes_against_header(
|
||||
&self,
|
||||
attr: &Types::PayloadAttributes,
|
||||
@@ -1212,7 +1271,7 @@ where
|
||||
fn validate_payload(
|
||||
&mut self,
|
||||
payload: Types::ExecutionData,
|
||||
ctx: TreeCtx<'_, N>,
|
||||
ctx: TreeCtx<'_, N, P>,
|
||||
) -> ValidationOutcome<N> {
|
||||
self.validate_block_with_state(BlockOrPayload::Payload(payload), ctx)
|
||||
}
|
||||
@@ -1220,7 +1279,7 @@ where
|
||||
fn validate_block(
|
||||
&mut self,
|
||||
block: SealedBlock<N::Block>,
|
||||
ctx: TreeCtx<'_, N>,
|
||||
ctx: TreeCtx<'_, N, P>,
|
||||
) -> ValidationOutcome<N> {
|
||||
self.validate_block_with_state(BlockOrPayload::Block(block), ctx)
|
||||
}
|
||||
|
||||
@@ -1279,6 +1279,7 @@ pub trait EngineValidatorBuilder<Node: FullNodeComponents>: Send + Sync + Clone
|
||||
type EngineValidator: EngineValidator<
|
||||
<Node::Types as NodeTypes>::Payload,
|
||||
<Node::Types as NodeTypes>::Primitives,
|
||||
Provider = Node::Provider,
|
||||
>;
|
||||
|
||||
/// Builds the tree validator for the consensus engine.
|
||||
|
||||
Reference in New Issue
Block a user