From 86e72baa49eae4207a208d45f571b6afa60624db Mon Sep 17 00:00:00 2001 From: julio4 <30329843+julio4@users.noreply.github.com> Date: Tue, 14 Oct 2025 15:45:53 +0900 Subject: [PATCH 1/4] feat: build_payload based on block context StateProvider --- examples/checkpoints-eth.rs | 5 +++-- examples/checkpoints-op.rs | 5 +++-- src/payload/block.rs | 9 +++++++++ src/payload/checkpoint.rs | 9 +++++++++ src/pipelines/exec/mod.rs | 11 ++++------- src/platform/ethereum/mod.rs | 9 ++++++--- src/platform/mod.rs | 15 +++++++-------- src/platform/optimism/mod.rs | 17 +++++------------ 8 files changed, 46 insertions(+), 34 deletions(-) diff --git a/examples/checkpoints-eth.rs b/examples/checkpoints-eth.rs index ca00411..6e372c6 100644 --- a/examples/checkpoints-eth.rs +++ b/examples/checkpoints-eth.rs @@ -26,7 +26,7 @@ use { fn main() -> eyre::Result<()> { // This is the entry point of the payload building API. We construct a // building context for a given block and attributes. - let (block, provider) = BlockContext::::mocked(); + let (block, _) = BlockContext::::mocked(); // Next we progressively build the payload by creating checkpoints that have // state mutations applied to them. @@ -47,7 +47,8 @@ fn main() -> eyre::Result<()> { // Checkpoints can be applied on top of each other, creating a progressive // history of state changes. let payload = start.apply(tx1)?.apply(bundle)?.apply(tx4)?; - let built_payload = Ethereum::build_payload(payload, &provider) + let built_payload = payload + .build_payload() .expect("payload should be built successfully"); println!("{built_payload:#?}"); diff --git a/examples/checkpoints-op.rs b/examples/checkpoints-op.rs index b98cb85..ad86af8 100644 --- a/examples/checkpoints-op.rs +++ b/examples/checkpoints-op.rs @@ -27,7 +27,7 @@ use { fn main() -> eyre::Result<()> { // This is the entry point of the payload building API. We construct a // building context for a given block and attributes. - let (block, provider) = BlockContext::::mocked(); + let (block, _) = BlockContext::::mocked(); // Next we progressively build the payload by creating checkpoints that have // state mutations applied to them. @@ -48,7 +48,8 @@ fn main() -> eyre::Result<()> { // Checkpoints can be applied on top of each other, creating a progressive // history of state changes. let payload = start.apply(tx1)?.apply(bundle)?.apply(tx4)?; - let built_payload = Optimism::build_payload(payload, &provider) + let built_payload = payload + .build_payload() .expect("payload should be built successfully"); println!("{built_payload:#?}"); diff --git a/src/payload/block.rs b/src/payload/block.rs index 56ebbed..b8ffa04 100644 --- a/src/payload/block.rs +++ b/src/payload/block.rs @@ -160,6 +160,15 @@ impl BlockContext

{ pub fn start(&self) -> Checkpoint

{ Checkpoint::new_at_block(self.clone()) } + + /// Given a payload checkpoint, this method builds a new payload on top of + /// this block that is ready to be handed back to the CL client as a response + /// to the `ForkchoiceUpdated` request. + pub fn build_payload( + payload: &Checkpoint

, + ) -> Result, PayloadBuilderError> { + payload.build_payload() + } } struct BlockContextInner { diff --git a/src/payload/checkpoint.rs b/src/payload/checkpoint.rs index 49a14da..2ad0ea5 100644 --- a/src/payload/checkpoint.rs +++ b/src/payload/checkpoint.rs @@ -224,6 +224,15 @@ impl Checkpoint

{ pub fn barrier_with_tag(&self, tag: impl Into>) -> Self { Self::apply_with(self, Mutation::Barrier, Some(tag.into())) } + + /// Given this checkpoint, this method builds a new payload on top of this + /// block base state that is ready to be handed back to the CL client as a + /// response to the `ForkchoiceUpdated` request. + pub fn build_payload( + &self, + ) -> Result, PayloadBuilderError> { + P::build_payload(self.clone(), self.block().base_state()) + } } /// Internal API diff --git a/src/pipelines/exec/mod.rs b/src/pipelines/exec/mod.rs index 03005d9..89b41cf 100644 --- a/src/pipelines/exec/mod.rs +++ b/src/pipelines/exec/mod.rs @@ -189,9 +189,9 @@ impl> // If there is no next step, we are done with the pipeline execution. // We can finalize the pipeline and return the output as the final // result of the pipeline run. - return Cursor::Finalizing(self.finalize( - P::build_payload(input, self.service.provider()).map_err(Arc::new), - )); + return Cursor::Finalizing( + self.finalize(input.build_payload().map_err(Arc::new)), + ); }; // there is a next step to be executed, create a cursor that will @@ -215,10 +215,7 @@ impl> ); return Cursor::

::Finalizing( - self.finalize( - P::build_payload(self.block.start(), self.service.provider()) - .map_err(Arc::new), - ), + self.finalize(self.block.start().build_payload().map_err(Arc::new)), ); }; diff --git a/src/platform/ethereum/mod.rs b/src/platform/ethereum/mod.rs index 4973282..d4ebf5d 100644 --- a/src/platform/ethereum/mod.rs +++ b/src/platform/ethereum/mod.rs @@ -50,13 +50,12 @@ impl Platform for Ethereum { } } - fn build_payload( + fn build_payload

( payload: Checkpoint

, - provider: &Provider, + provider: &dyn StateProvider, ) -> Result, PayloadBuilderError> where P: traits::PlatformExecBounds, - Provider: traits::ProviderBounds

, { let evm_config = payload.block().evm_config().clone(); let payload_config = PayloadConfig { @@ -78,6 +77,10 @@ impl Platform for Ethereum { let transactions = payload.history().transactions().cloned().collect(); let transactions = Box::new(FixedTransactions::::new(transactions)); + // TODO: wrap provider and payload.block().chainspec() in a + // StateProviderFactory + ChainSpecProvider to be able to use + // reth_ethereum_payload_builder::default_ethereum_payload + default_ethereum_payload( evm_config, provider, diff --git a/src/platform/mod.rs b/src/platform/mod.rs index fc5ea94..70d6f83 100644 --- a/src/platform/mod.rs +++ b/src/platform/mod.rs @@ -12,7 +12,7 @@ use { network::Network as AlloyNetwork, signers::Signature, }, - reth::ethereum::primitives::SignedTransaction, + reth::{ethereum::primitives::SignedTransaction, providers::StateProvider}, serde::{Serialize, de::DeserializeOwned}, std::sync::Arc, }; @@ -101,16 +101,15 @@ pub trait Platform: where P: traits::PlatformExecBounds; - /// Given a payload checkpoint and access to the node state, this method - /// builds a new payload that is ready to be handed back to the CL client as - /// a response to the `ForkchoiceUpdated` request. - fn build_payload( + /// Given a payload checkpoint and access to the latest block state, this + /// method builds a new payload that is ready to be handed back to the CL + /// client as a response to the `ForkchoiceUpdated` request. + fn build_payload

( payload: Checkpoint

, - provider: &Provider, + provider: &dyn StateProvider, ) -> Result, PayloadBuilderError> where - P: traits::PlatformExecBounds, - Provider: traits::ProviderBounds; + P: traits::PlatformExecBounds; } /// This is an optional extension trait for platforms that want to provide info diff --git a/src/platform/optimism/mod.rs b/src/platform/optimism/mod.rs index b35688c..712b38d 100644 --- a/src/platform/optimism/mod.rs +++ b/src/platform/optimism/mod.rs @@ -14,6 +14,7 @@ use { }, payload::{builder::*, util::PayloadTransactionsFixed}, primitives::Recovered, + providers::StateProvider, revm::{cancelled::CancelOnDrop, database::StateProviderDatabase}, }, serde::{Deserialize, Serialize}, @@ -77,13 +78,12 @@ impl Platform for Optimism { } } - fn build_payload( + fn build_payload

( payload: Checkpoint

, - provider: &Provider, + provider: &dyn StateProvider, ) -> Result, PayloadBuilderError> where P: traits::PlatformExecBounds, - Provider: traits::ProviderBounds

, { let block = payload.block(); let transactions = extract_external_txs(&payload); @@ -112,16 +112,9 @@ impl Platform for Optimism { best_payload: None, }; - // Top of Block chain state. - let state_provider = - provider.state_by_block_hash(payload.block().parent().hash())?; - // Invoke the builder implementation from reth-optimism-node. - let build_outcome = op_builder.build( - StateProviderDatabase(&state_provider), - &state_provider, - context, - )?; + let build_outcome = + op_builder.build(StateProviderDatabase(&provider), &provider, context)?; // extract the built payload from the build outcome. let built_payload = match build_outcome { From f18f35afa6ddefd7a35ecad6a963f70663cec8f3 Mon Sep 17 00:00:00 2001 From: julio4 <30329843+julio4@users.noreply.github.com> Date: Wed, 15 Oct 2025 10:24:33 +0900 Subject: [PATCH 2/4] fix: custom bundle type example --- examples/custom-bundle-type.rs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/examples/custom-bundle-type.rs b/examples/custom-bundle-type.rs index a436697..16628ae 100644 --- a/examples/custom-bundle-type.rs +++ b/examples/custom-bundle-type.rs @@ -18,6 +18,7 @@ use { ethereum::primitives::SignedTransaction, optimism::primitives::OpTransactionSigned, primitives::Recovered, + providers::{StateProvider, StateProviderFactory}, revm::db::BundleState, }, serde::{Deserialize, Serialize}, @@ -50,15 +51,14 @@ impl Platform for CustomPlatform { ) } - fn build_payload( + fn build_payload

( payload: Checkpoint

, - provider: &Provider, + provider: &dyn StateProvider, ) -> Result, PayloadBuilderError> where P: traits::PlatformExecBounds, - Provider: traits::ProviderBounds, { - Optimism::build_payload::(payload, provider) + Optimism::build_payload::

(payload, provider) } } @@ -97,7 +97,11 @@ fn main() -> eyre::Result<()> { )) )); - let built_payload = CustomPlatform::build_payload(payload, &provider) + // build payload with the latest state provider + let state_provider = provider + .state_by_block_hash(payload.block().parent().hash()) + .expect("failed to retrieve state"); + let built_payload = CustomPlatform::build_payload(payload, &state_provider) .expect("payload should be built successfully"); println!("{built_payload:#?}"); From 902dbdf4c3c31e680592c1818018ded7dfc0ce02 Mon Sep 17 00:00:00 2001 From: julio4 <30329843+julio4@users.noreply.github.com> Date: Wed, 15 Oct 2025 10:25:18 +0900 Subject: [PATCH 3/4] fix: fork ethereum default payload builder --- src/platform/ethereum/mod.rs | 345 +++++++++++++++++++++++++++++++++-- 1 file changed, 333 insertions(+), 12 deletions(-) diff --git a/src/platform/ethereum/mod.rs b/src/platform/ethereum/mod.rs index d4ebf5d..f272f09 100644 --- a/src/platform/ethereum/mod.rs +++ b/src/platform/ethereum/mod.rs @@ -1,18 +1,59 @@ use { super::*, crate::{ - alloy::consensus::BlockHeader, + alloy::{ + consensus::BlockHeader, + evm::revm::database::State, + primitives::U256, + }, reth::{ - ethereum::{evm::EthEvmConfig, node::EthereumNode}, - evm::NextBlockEnvAttributes, - payload::builder::*, - revm::{cached::CachedReads, cancelled::CancelOnDrop}, - transaction_pool::*, + chainspec::EthChainSpec, + errors::ConsensusError, + ethereum::{ + EthPrimitives, + TransactionSigned, + chainspec::EthereumHardforks, + consensus::validation::MAX_RLP_BLOCK_SIZE, + evm::{EthEvmConfig, revm::database::StateProviderDatabase}, + node::EthereumNode, + primitives::transaction::error::InvalidTransactionError, + }, + evm::{ + ConfigureEvm, + Evm, + NextBlockEnvAttributes, + block::{BlockExecutionError, BlockValidationError}, + execute::{BlockBuilder, BlockBuilderOutcome}, + }, + payload::{ + BlobSidecars, + EthBuiltPayload, + EthPayloadBuilderAttributes, + PayloadBuilderAttributes, + builder::{ + BuildArguments, + BuildOutcome, + EthereumBuilderConfig, + PayloadConfig, + is_better_payload, + }, + }, + revm::{ + cached::CachedReads, + cancelled::CancelOnDrop, + context::Block, + primitives::alloy_primitives::private::alloy_rlp::Encodable, + }, + rpc::types::TransactionTrait, + transaction_pool::{ + error::{Eip4844PoolTransactionError, InvalidPoolTransactionError}, + noop::NoopTransactionPool, + *, + }, }, }, limits::EthereumDefaultLimits, pool::FixedTransactions, - reth_transaction_pool::noop::NoopTransactionPool, serde::{Deserialize, Serialize}, std::sync::Arc, }; @@ -58,6 +99,7 @@ impl Platform for Ethereum { P: traits::PlatformExecBounds, { let evm_config = payload.block().evm_config().clone(); + let chain_spec = payload.block().chainspec(); let payload_config = PayloadConfig { parent_header: Arc::new(payload.block().parent().clone()), attributes: payload.block().attributes().clone(), @@ -77,15 +119,12 @@ impl Platform for Ethereum { let transactions = payload.history().transactions().cloned().collect(); let transactions = Box::new(FixedTransactions::::new(transactions)); - // TODO: wrap provider and payload.block().chainspec() in a - // StateProviderFactory + ChainSpecProvider to be able to use - // reth_ethereum_payload_builder::default_ethereum_payload - default_ethereum_payload( evm_config, + chain_spec, provider, NoopTransactionPool::default(), - builder_config, + &builder_config, build_args, |_| { transactions @@ -104,3 +143,285 @@ impl Platform for Ethereum { impl PlatformWithRpcTypes for Ethereum { type RpcTypes = alloy::network::Ethereum; } + +/// Ethereum payload builder code from [`default_ethereum_payload`] adapted to +/// use the given `StateProvider` and chainspec +/// +/// Constructs an Ethereum transaction payload using the best transactions +/// +/// Given build arguments including latest state provider, best transactions, +/// and configuration, this function creates a transaction payload. +/// Returns a result indicating success with the payload or an error in case of +/// failure. +/// +/// # Panics +/// see [`default_ethereum_payload`] +/// +/// [`default_ethereum_payload`]: reth_ethereum_payload_builder::default_ethereum_payload +#[allow(clippy::too_many_lines, clippy::cast_possible_truncation)] +#[inline] +pub fn default_ethereum_payload( + evm_config: EvmConfig, + chain_spec: &Arc>, + state_provider: &dyn StateProvider, + pool: Pool, + builder_config: &EthereumBuilderConfig, + args: BuildArguments, + best_txs: F, +) -> Result, PayloadBuilderError> +where + EvmConfig: ConfigureEvm< + Primitives = EthPrimitives, + NextBlockEnvCtx = NextBlockEnvAttributes, + >, + Pool: TransactionPool< + Transaction: PoolTransaction, + >, + F: FnOnce(BestTransactionsAttributes) -> BestTransactionsFor, +{ + let BuildArguments { + mut cached_reads, + config, + cancel, + best_payload, + } = args; + let PayloadConfig { + parent_header, + attributes, + } = config; + + let state = StateProviderDatabase::new(&state_provider); + let mut db = State::builder() + .with_database(cached_reads.as_db_mut(state)) + .with_bundle_update() + .build(); + + let mut builder = evm_config + .builder_for_next_block(&mut db, &parent_header, NextBlockEnvAttributes { + timestamp: attributes.timestamp(), + suggested_fee_recipient: attributes.suggested_fee_recipient(), + prev_randao: attributes.prev_randao(), + gas_limit: builder_config.gas_limit(parent_header.gas_limit), + parent_beacon_block_root: attributes.parent_beacon_block_root(), + withdrawals: Some(attributes.withdrawals().clone()), + }) + .map_err(PayloadBuilderError::other)?; + + let mut cumulative_gas_used = 0; + let block_gas_limit: u64 = builder.evm_mut().block().gas_limit; + let base_fee = builder.evm_mut().block().basefee; + + let mut best_txs = best_txs(BestTransactionsAttributes::new( + base_fee, + builder + .evm_mut() + .block() + .blob_gasprice() + .map(|gasprice| gasprice as u64), + )); + let mut total_fees = U256::ZERO; + + builder + .apply_pre_execution_changes() + .map_err(|err| PayloadBuilderError::Internal(err.into()))?; + + // initialize empty blob sidecars at first. If cancun is active then this will + // be populated by blob sidecars if any. + let mut blob_sidecars = BlobSidecars::Empty; + + let mut block_blob_count = 0; + let mut block_transactions_rlp_length = 0; + + let blob_params = chain_spec.blob_params_at_timestamp(attributes.timestamp); + let max_blob_count = blob_params + .as_ref() + .map(|params| params.max_blob_count) + .unwrap_or_default(); + + let is_osaka = chain_spec.is_osaka_active_at_timestamp(attributes.timestamp); + + while let Some(pool_tx) = best_txs.next() { + // ensure we still have capacity for this transaction + if cumulative_gas_used + pool_tx.gas_limit() > block_gas_limit { + // we can't fit this transaction into the block, so we need to mark it as + // invalid which also removes all dependent transaction from the + // iterator before we can continue + best_txs.mark_invalid( + &pool_tx, + InvalidPoolTransactionError::ExceedsGasLimit( + pool_tx.gas_limit(), + block_gas_limit, + ), + ); + continue; + } + + // check if the job was cancelled, if so we can exit early + if cancel.is_cancelled() { + return Ok(BuildOutcome::Cancelled); + } + + // convert tx to a signed transaction + let tx = pool_tx.to_consensus(); + + let estimated_block_size_with_tx = block_transactions_rlp_length + + tx.inner().length() + + attributes.withdrawals().length() + + 1024; // 1Kb of overhead for the block header + + if is_osaka && estimated_block_size_with_tx > MAX_RLP_BLOCK_SIZE { + best_txs.mark_invalid( + &pool_tx, + InvalidPoolTransactionError::OversizedData( + estimated_block_size_with_tx, + MAX_RLP_BLOCK_SIZE, + ), + ); + continue; + } + + // There's only limited amount of blob space available per block, so we need + // to check if the EIP-4844 can still fit in the block + let mut blob_tx_sidecar = None; + if let Some(blob_tx) = tx.as_eip4844() { + let tx_blob_count = blob_tx.tx().blob_versioned_hashes.len() as u64; + + if block_blob_count + tx_blob_count > max_blob_count { + // we can't fit this _blob_ transaction into the block, so we mark it as + // invalid, which removes its dependent transactions from + // the iterator. This is similar to the gas limit condition + // for regular transactions above. + best_txs.mark_invalid( + &pool_tx, + InvalidPoolTransactionError::Eip4844( + Eip4844PoolTransactionError::TooManyEip4844Blobs { + have: block_blob_count + tx_blob_count, + permitted: max_blob_count, + }, + ), + ); + continue; + } + + let blob_sidecar_result = 'sidecar: { + let Some(sidecar) = pool + .get_blob(*tx.hash()) + .map_err(PayloadBuilderError::other)? + else { + break 'sidecar Err( + Eip4844PoolTransactionError::MissingEip4844BlobSidecar, + ); + }; + + if is_osaka { + if sidecar.is_eip7594() { + Ok(sidecar) + } else { + Err(Eip4844PoolTransactionError::UnexpectedEip4844SidecarAfterOsaka) + } + } else if sidecar.is_eip4844() { + Ok(sidecar) + } else { + Err(Eip4844PoolTransactionError::UnexpectedEip7594SidecarBeforeOsaka) + } + }; + + blob_tx_sidecar = match blob_sidecar_result { + Ok(sidecar) => Some(sidecar), + Err(error) => { + best_txs.mark_invalid( + &pool_tx, + InvalidPoolTransactionError::Eip4844(error), + ); + continue; + } + }; + } + + let gas_used = match builder.execute_transaction(tx.clone()) { + Ok(gas_used) => gas_used, + Err(BlockExecutionError::Validation( + BlockValidationError::InvalidTx { error, .. }, + )) => { + if error.is_nonce_too_low() { + // if the nonce is too low, we can skip this transaction + } else { + // if the transaction is invalid, we can skip it and all of its + // descendants + best_txs.mark_invalid( + &pool_tx, + InvalidPoolTransactionError::Consensus( + InvalidTransactionError::TxTypeNotSupported, + ), + ); + } + continue; + } + // this is an error that we should treat as fatal for this attempt + Err(err) => return Err(PayloadBuilderError::evm(err)), + }; + + // add to the total blob gas used if the transaction successfully executed + if let Some(blob_tx) = tx.as_eip4844() { + block_blob_count += blob_tx.tx().blob_versioned_hashes.len() as u64; + + // if we've reached the max blob count, we can skip blob txs entirely + if block_blob_count == max_blob_count { + best_txs.skip_blobs(); + } + } + + block_transactions_rlp_length += tx.inner().length(); + + // update and add to total fees + let miner_fee = tx + .effective_tip_per_gas(base_fee) + .expect("fee is always valid; execution succeeded"); + total_fees += U256::from(miner_fee) * U256::from(gas_used); + cumulative_gas_used += gas_used; + + // Add blob tx sidecar to the payload. + if let Some(sidecar) = blob_tx_sidecar { + blob_sidecars.push_sidecar_variant(sidecar.as_ref().clone()); + } + } + + // check if we have a better block + if !is_better_payload(best_payload.as_ref(), total_fees) { + // Release db + drop(builder); + // can skip building the block + return Ok(BuildOutcome::Aborted { + fees: total_fees, + cached_reads, + }); + } + + let BlockBuilderOutcome { + execution_result, + block, + .. + } = builder.finish(state_provider)?; + + let requests = chain_spec + .is_prague_active_at_timestamp(attributes.timestamp) + .then_some(execution_result.requests); + + let sealed_block = Arc::new(block.sealed_block().clone()); + + if is_osaka && sealed_block.rlp_length() > MAX_RLP_BLOCK_SIZE { + return Err(PayloadBuilderError::other(ConsensusError::BlockTooLarge { + rlp_length: sealed_block.rlp_length(), + max_rlp_length: MAX_RLP_BLOCK_SIZE, + })); + } + + let payload = EthBuiltPayload::new(attributes.id, sealed_block, total_fees, requests) + // add blob sidecars from the executed txs + .with_sidecars(blob_sidecars); + + Ok(BuildOutcome::Better { + payload, + cached_reads, + }) +} From 128dd6efd1ccd43613e45307d851b0009fa91bcd Mon Sep 17 00:00:00 2001 From: julio4 <30329843+julio4@users.noreply.github.com> Date: Tue, 21 Oct 2025 19:41:22 +0800 Subject: [PATCH 4/4] feat: remove state provider in mocked block context --- examples/checkpoints-eth.rs | 2 +- examples/checkpoints-op.rs | 2 +- examples/custom-bundle-type.rs | 12 ++++------ src/test_utils/exts/mock.rs | 44 ++++++++++++++++------------------ 4 files changed, 28 insertions(+), 32 deletions(-) diff --git a/examples/checkpoints-eth.rs b/examples/checkpoints-eth.rs index 6e372c6..1368465 100644 --- a/examples/checkpoints-eth.rs +++ b/examples/checkpoints-eth.rs @@ -26,7 +26,7 @@ use { fn main() -> eyre::Result<()> { // This is the entry point of the payload building API. We construct a // building context for a given block and attributes. - let (block, _) = BlockContext::::mocked(); + let block = BlockContext::::mocked(); // Next we progressively build the payload by creating checkpoints that have // state mutations applied to them. diff --git a/examples/checkpoints-op.rs b/examples/checkpoints-op.rs index ad86af8..ed800ee 100644 --- a/examples/checkpoints-op.rs +++ b/examples/checkpoints-op.rs @@ -27,7 +27,7 @@ use { fn main() -> eyre::Result<()> { // This is the entry point of the payload building API. We construct a // building context for a given block and attributes. - let (block, _) = BlockContext::::mocked(); + let block = BlockContext::::mocked(); // Next we progressively build the payload by creating checkpoints that have // state mutations applied to them. diff --git a/examples/custom-bundle-type.rs b/examples/custom-bundle-type.rs index 16628ae..73bb4cd 100644 --- a/examples/custom-bundle-type.rs +++ b/examples/custom-bundle-type.rs @@ -18,7 +18,7 @@ use { ethereum::primitives::SignedTransaction, optimism::primitives::OpTransactionSigned, primitives::Recovered, - providers::{StateProvider, StateProviderFactory}, + providers::StateProvider, revm::db::BundleState, }, serde::{Deserialize, Serialize}, @@ -64,7 +64,7 @@ impl Platform for CustomPlatform { fn main() -> eyre::Result<()> { // Construct a mock build context for the custom platform. - let (block, provider) = BlockContext::::mocked(); + let block = BlockContext::::mocked(); // begin building the payload by creating the first checkpoint for the block. let start = block.start(); @@ -97,11 +97,9 @@ fn main() -> eyre::Result<()> { )) )); - // build payload with the latest state provider - let state_provider = provider - .state_by_block_hash(payload.block().parent().hash()) - .expect("failed to retrieve state"); - let built_payload = CustomPlatform::build_payload(payload, &state_provider) + // build payload + let built_payload = payload + .build_payload() .expect("payload should be built successfully"); println!("{built_payload:#?}"); diff --git a/src/test_utils/exts/mock.rs b/src/test_utils/exts/mock.rs index ada0be9..c711e2b 100644 --- a/src/test_utils/exts/mock.rs +++ b/src/test_utils/exts/mock.rs @@ -89,15 +89,15 @@ impl PayloadBuilderAttributesMocked

/// Allows the creation of a block context for the first block post genesis with /// all [`FundedAccounts`] pre-funded with 100 ETH. pub trait BlockContextMocked { - /// Returns a tuple of: - /// 1. an instance of a [`BlockContext`] that is rooted at the genesis block - /// the given platform's DEV chainspec. - /// 2. An instance of a platform-specific [`StateProviderFactory`] that has - /// its state pre-populated with [`FundedAccounts`] for the given platform. - /// This state provider together with a checkpoint created on top of the - /// returned [`BlockContext`] can be used to construct a payload using - /// [`Platform::build_payload`]. - fn mocked() -> (BlockContext

, impl traits::ProviderBounds

); + /// Returns an instance of a [`BlockContext`] that is rooted at the genesis + /// block the given platform's DEV chainspec. + /// + /// The base state used in the [`BlockContext`] is platform-specific and + /// pre-populated with [`FundedAccounts`] for the given platform. This + /// [`BlockContext`] instance together with a checkpoint created on top of it + /// can be used to construct a payload using [`Platform::build_payload`] with + /// [`BlockContext::build_payload`]. + fn mocked() -> BlockContext

; } impl BlockContextMocked> for BlockContext

@@ -110,9 +110,10 @@ where >, >, { - fn mocked() -> (BlockContext

, impl traits::ProviderBounds

) { + fn mocked() -> BlockContext

{ let chainspec = LazyLock::force(&DEV).clone().with_funded_accounts(); - let provider = GenesisProviderFactory::

::new(chainspec.clone()); + let provider_factory = GenesisProviderFactory::

::new(chainspec.clone()); + let base_state = provider_factory.state_provider(); let parent = SealedHeader::new( chainspec.genesis_header().clone(), @@ -121,15 +122,13 @@ where let payload_attribs = >::mocked(&parent); - let block = BlockContext::

::new( + BlockContext::

::new( parent, payload_attribs, - provider.state_provider(), + base_state, chainspec.clone(), ) - .expect("Failed to create mocked block context"); - - (block, provider) + .expect("Failed to create mocked block context") } } @@ -144,10 +143,11 @@ where >, >, { - fn mocked() -> (BlockContext

, impl traits::ProviderBounds

) { + fn mocked() -> BlockContext

{ use reth::optimism::{chainspec::OP_DEV, node::OpPayloadBuilderAttributes}; let chainspec = LazyLock::force(&OP_DEV).clone().with_funded_accounts(); - let provider = GenesisProviderFactory::

::new(chainspec.clone()); + let provider_factory = GenesisProviderFactory::

::new(chainspec.clone()); + let base_state = provider_factory.state_provider(); let parent = SealedHeader::new( chainspec.genesis_header().clone(), @@ -159,14 +159,12 @@ where &parent, ); - let block = BlockContext::

::new( + BlockContext::

::new( parent, payload_attributes, - provider.state_provider(), + base_state, chainspec.clone(), ) - .expect("Failed to create mocked block context"); - - (block, provider) + .expect("Failed to create mocked block context") } }