diff --git a/Cargo.lock b/Cargo.lock index a4e0e1fd0a8..3007e1c3c2c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3463,7 +3463,6 @@ dependencies = [ "op-alloy-rpc-types", "op-alloy-rpc-types-engine", "op-revm", - "reth-chain-state", "reth-codecs", "reth-db-api", "reth-engine-primitives", @@ -9464,10 +9463,10 @@ dependencies = [ "alloy-rpc-types-debug", "alloy-rpc-types-engine", "derive_more", + "either", "op-alloy-consensus", "op-alloy-rpc-types-engine", "reth-basic-payload-builder", - "reth-chain-state", "reth-chainspec", "reth-evm", "reth-execution-types", @@ -9675,7 +9674,9 @@ dependencies = [ "reth-chain-state", "reth-chainspec", "reth-errors", + "reth-execution-types", "reth-primitives-traits", + "reth-trie-common", "serde", "thiserror 2.0.17", "tokio", diff --git a/crates/node/builder/src/launch/engine.rs b/crates/node/builder/src/launch/engine.rs index 9faf9fcfa95..b66577a6711 100644 --- a/crates/node/builder/src/launch/engine.rs +++ b/crates/node/builder/src/launch/engine.rs @@ -276,8 +276,8 @@ impl EngineNodeLauncher { tokio::select! { payload = built_payloads.select_next_some() => { if let Some(executed_block) = payload.executed_block() { - debug!(target: "reth::cli", block=?executed_block.recovered_block().num_hash(), "inserting built payload"); - engine_service.orchestrator_mut().handler_mut().handler_mut().on_event(EngineApiRequest::InsertExecutedBlock(executed_block).into()); + debug!(target: "reth::cli", block=?executed_block.recovered_block.num_hash(), "inserting built payload"); + engine_service.orchestrator_mut().handler_mut().handler_mut().on_event(EngineApiRequest::InsertExecutedBlock(executed_block.into_executed_payload()).into()); } } event = engine_service.next() => { diff --git a/crates/optimism/payload/Cargo.toml b/crates/optimism/payload/Cargo.toml index e75075a12cf..0674ed7cf73 100644 --- a/crates/optimism/payload/Cargo.toml +++ b/crates/optimism/payload/Cargo.toml @@ -25,7 +25,6 @@ reth-payload-builder-primitives.workspace = true reth-payload-util.workspace = true reth-payload-primitives = { workspace = true, features = ["op"] } reth-basic-payload-builder.workspace = true -reth-chain-state.workspace = true reth-payload-validator.workspace = true # op-reth @@ -52,3 +51,4 @@ tracing.workspace = true thiserror.workspace = true sha2.workspace = true serde.workspace = true +either.workspace = true diff --git a/crates/optimism/payload/src/builder.rs b/crates/optimism/payload/src/builder.rs index 05f33d3b699..af270b54006 100644 --- a/crates/optimism/payload/src/builder.rs +++ b/crates/optimism/payload/src/builder.rs @@ -11,7 +11,6 @@ use alloy_primitives::{B256, U256}; use alloy_rpc_types_debug::ExecutionWitness; use alloy_rpc_types_engine::PayloadId; use reth_basic_payload_builder::*; -use reth_chain_state::ExecutedBlock; use reth_chainspec::{ChainSpecProvider, EthChainSpec}; use reth_evm::{ block::BlockExecutorFor, @@ -30,7 +29,7 @@ use reth_optimism_txpool::{ OpPooledTx, }; use reth_payload_builder_primitives::PayloadBuilderError; -use reth_payload_primitives::{BuildNextEnv, PayloadBuilderAttributes}; +use reth_payload_primitives::{BuildNextEnv, BuiltPayloadExecutedBlock, PayloadBuilderAttributes}; use reth_payload_util::{BestPayloadTransactions, NoopPayloadTransactions, PayloadTransactions}; use reth_primitives_traits::{ HeaderTy, NodePrimitives, SealedHeader, SealedHeaderFor, SignedTransaction, TxTy, @@ -386,11 +385,11 @@ impl OpBuilder<'_, Txs> { ); // create the executed block data - let executed: ExecutedBlock = ExecutedBlock { + let executed: BuiltPayloadExecutedBlock = BuiltPayloadExecutedBlock { recovered_block: Arc::new(block), execution_output: Arc::new(execution_outcome), - hashed_state: Arc::new(hashed_state), - trie_updates: Arc::new(trie_updates), + hashed_state: either::Either::Left(Arc::new(hashed_state)), + trie_updates: either::Either::Left(Arc::new(trie_updates)), }; let no_tx_pool = ctx.attributes().no_tx_pool(); diff --git a/crates/optimism/payload/src/payload.rs b/crates/optimism/payload/src/payload.rs index 6f530acd853..b44f69ddb7e 100644 --- a/crates/optimism/payload/src/payload.rs +++ b/crates/optimism/payload/src/payload.rs @@ -16,12 +16,13 @@ use op_alloy_consensus::{encode_holocene_extra_data, encode_jovian_extra_data, E use op_alloy_rpc_types_engine::{ OpExecutionPayloadEnvelopeV3, OpExecutionPayloadEnvelopeV4, OpExecutionPayloadV4, }; -use reth_chain_state::ExecutedBlock; use reth_chainspec::EthChainSpec; use reth_optimism_evm::OpNextBlockEnvAttributes; use reth_optimism_forks::OpHardforks; use reth_payload_builder::{EthPayloadBuilderAttributes, PayloadBuilderError}; -use reth_payload_primitives::{BuildNextEnv, BuiltPayload, PayloadBuilderAttributes}; +use reth_payload_primitives::{ + BuildNextEnv, BuiltPayload, BuiltPayloadExecutedBlock, PayloadBuilderAttributes, +}; use reth_primitives_traits::{ NodePrimitives, SealedBlock, SealedHeader, SignedTransaction, WithEncoded, }; @@ -176,7 +177,7 @@ pub struct OpBuiltPayload { /// Sealed block pub(crate) block: Arc>, /// Block execution data for the payload, if any. - pub(crate) executed_block: Option>, + pub(crate) executed_block: Option>, /// The fees of the block pub(crate) fees: U256, } @@ -189,7 +190,7 @@ impl OpBuiltPayload { id: PayloadId, block: Arc>, fees: U256, - executed_block: Option>, + executed_block: Option>, ) -> Self { Self { id, block, fees, executed_block } } @@ -226,7 +227,7 @@ impl BuiltPayload for OpBuiltPayload { self.fees } - fn executed_block(&self) -> Option> { + fn executed_block(&self) -> Option> { self.executed_block.clone() } diff --git a/crates/payload/primitives/Cargo.toml b/crates/payload/primitives/Cargo.toml index 670727e3c6d..ce8e07fc8ee 100644 --- a/crates/payload/primitives/Cargo.toml +++ b/crates/payload/primitives/Cargo.toml @@ -17,6 +17,8 @@ reth-primitives-traits.workspace = true reth-chainspec.workspace = true reth-errors.workspace = true reth-chain-state.workspace = true +reth-execution-types.workspace = true +reth-trie-common.workspace = true # alloy alloy-eips.workspace = true @@ -38,6 +40,8 @@ assert_matches.workspace = true default = ["std"] std = [ "reth-chainspec/std", + "reth-execution-types/std", + "reth-trie-common/std", "alloy-eips/std", "alloy-primitives/std", "alloy-rpc-types-engine/std", diff --git a/crates/payload/primitives/src/lib.rs b/crates/payload/primitives/src/lib.rs index ca3cccda883..1c32bb3fa3d 100644 --- a/crates/payload/primitives/src/lib.rs +++ b/crates/payload/primitives/src/lib.rs @@ -26,8 +26,8 @@ pub use error::{ mod traits; pub use traits::{ - BuildNextEnv, BuiltPayload, PayloadAttributes, PayloadAttributesBuilder, - PayloadBuilderAttributes, + BuildNextEnv, BuiltPayload, BuiltPayloadExecutedBlock, PayloadAttributes, + PayloadAttributesBuilder, PayloadBuilderAttributes, }; mod payload; diff --git a/crates/payload/primitives/src/traits.rs b/crates/payload/primitives/src/traits.rs index 160956afa27..fdc078887dc 100644 --- a/crates/payload/primitives/src/traits.rs +++ b/crates/payload/primitives/src/traits.rs @@ -1,7 +1,7 @@ //! Core traits for working with execution payloads. use crate::PayloadBuilderError; -use alloc::{boxed::Box, vec::Vec}; +use alloc::{boxed::Box, sync::Arc, vec::Vec}; use alloy_eips::{ eip4895::{Withdrawal, Withdrawals}, eip7685::Requests, @@ -9,8 +9,60 @@ use alloy_eips::{ use alloy_primitives::{Address, B256, U256}; use alloy_rpc_types_engine::{PayloadAttributes as EthPayloadAttributes, PayloadId}; use core::fmt; -use reth_chain_state::ExecutedBlock; -use reth_primitives_traits::{NodePrimitives, SealedBlock, SealedHeader}; +use either::Either; +use reth_execution_types::ExecutionOutcome; +use reth_primitives_traits::{NodePrimitives, RecoveredBlock, SealedBlock, SealedHeader}; +use reth_trie_common::{ + updates::{TrieUpdates, TrieUpdatesSorted}, + HashedPostState, HashedPostStateSorted, +}; + +/// Represents an executed block for payload building purposes. +/// +/// This type captures the complete execution state of a built block, +/// including the recovered block, execution outcome, hashed state, and trie updates. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct BuiltPayloadExecutedBlock { + /// Recovered Block + pub recovered_block: Arc>, + /// Block's execution outcome. + pub execution_output: Arc>, + /// Block's hashed state. + /// + /// Supports both unsorted and sorted variants so payload builders can avoid cloning in order + /// to convert from one to the other when it's not necessary. + pub hashed_state: Either, Arc>, + /// Trie updates that result from calculating the state root for the block. + /// + /// Supports both unsorted and sorted variants so payload builders can avoid cloning in order + /// to convert from one to the other when it's not necessary. + pub trie_updates: Either, Arc>, +} + +impl BuiltPayloadExecutedBlock { + /// Converts this into an [`reth_chain_state::ExecutedBlock`]. + /// + /// If the hashed state or trie updates are in sorted form, they will be converted + /// back to their unsorted representations. + pub fn into_executed_payload(self) -> reth_chain_state::ExecutedBlock { + let hashed_state = match self.hashed_state { + Either::Left(unsorted) => unsorted, + Either::Right(sorted) => Arc::new(Arc::unwrap_or_clone(sorted).into()), + }; + + let trie_updates = match self.trie_updates { + Either::Left(unsorted) => unsorted, + Either::Right(sorted) => Arc::new(Arc::unwrap_or_clone(sorted).into()), + }; + + reth_chain_state::ExecutedBlock { + recovered_block: self.recovered_block, + execution_output: self.execution_output, + hashed_state, + trie_updates, + } + } +} /// Represents a successfully built execution payload (block). /// @@ -30,7 +82,7 @@ pub trait BuiltPayload: Send + Sync + fmt::Debug { /// Returns the complete execution result including state updates. /// /// Returns `None` if execution data is not available or not tracked. - fn executed_block(&self) -> Option> { + fn executed_block(&self) -> Option> { None } @@ -155,7 +207,7 @@ where } } -impl PayloadAttributesBuilder for either::Either +impl PayloadAttributesBuilder for Either where L: PayloadAttributesBuilder, R: PayloadAttributesBuilder, diff --git a/crates/trie/common/src/hashed_state.rs b/crates/trie/common/src/hashed_state.rs index e693776c4e8..dcc3e74fdcc 100644 --- a/crates/trie/common/src/hashed_state.rs +++ b/crates/trie/common/src/hashed_state.rs @@ -615,6 +615,49 @@ impl HashedStorageSorted { } } +impl From for HashedStorage { + fn from(sorted: HashedStorageSorted) -> Self { + let mut storage = B256Map::default(); + + // Add all non-zero valued slots + for (slot, value) in sorted.non_zero_valued_slots { + storage.insert(slot, value); + } + + // Add all zero valued slots + for slot in sorted.zero_valued_slots { + storage.insert(slot, U256::ZERO); + } + + Self { wiped: sorted.wiped, storage } + } +} + +impl From for HashedPostState { + fn from(sorted: HashedPostStateSorted) -> Self { + let mut accounts = B256Map::default(); + + // Add all updated accounts + for (address, account) in sorted.accounts.accounts { + accounts.insert(address, Some(account)); + } + + // Add all destroyed accounts + for address in sorted.accounts.destroyed_accounts { + accounts.insert(address, None); + } + + // Convert storages + let storages = sorted + .storages + .into_iter() + .map(|(address, storage)| (address, storage.into())) + .collect(); + + Self { accounts, storages } + } +} + /// An iterator that yields chunks of the state updates of at most `size` account and storage /// targets. /// diff --git a/examples/custom-node/Cargo.toml b/examples/custom-node/Cargo.toml index fe1f0006256..8eb3dbd143b 100644 --- a/examples/custom-node/Cargo.toml +++ b/examples/custom-node/Cargo.toml @@ -7,7 +7,6 @@ license.workspace = true [dependencies] # reth -reth-chain-state.workspace = true reth-codecs.workspace = true reth-network-peers.workspace = true reth-node-builder.workspace = true diff --git a/examples/custom-node/src/engine.rs b/examples/custom-node/src/engine.rs index 0c80e52a661..d7eabdc19f7 100644 --- a/examples/custom-node/src/engine.rs +++ b/examples/custom-node/src/engine.rs @@ -6,14 +6,13 @@ use crate::{ }; use alloy_eips::eip2718::WithEncoded; use op_alloy_rpc_types_engine::{OpExecutionData, OpExecutionPayload}; -use reth_chain_state::ExecutedBlock; use reth_engine_primitives::EngineApiValidator; use reth_ethereum::{ node::api::{ - validate_version_specific_fields, AddOnsContext, BuiltPayload, EngineApiMessageVersion, - EngineObjectValidationError, ExecutionPayload, FullNodeComponents, NewPayloadError, - NodePrimitives, PayloadAttributes, PayloadBuilderAttributes, PayloadOrAttributes, - PayloadTypes, PayloadValidator, + validate_version_specific_fields, AddOnsContext, BuiltPayload, BuiltPayloadExecutedBlock, + EngineApiMessageVersion, EngineObjectValidationError, ExecutionPayload, FullNodeComponents, + NewPayloadError, NodePrimitives, PayloadAttributes, PayloadBuilderAttributes, + PayloadOrAttributes, PayloadTypes, PayloadValidator, }, primitives::{RecoveredBlock, SealedBlock}, storage::StateProviderFactory, @@ -167,7 +166,7 @@ impl BuiltPayload for CustomBuiltPayload { self.0.fees() } - fn executed_block(&self) -> Option> { + fn executed_block(&self) -> Option> { self.0.executed_block() }