diff --git a/crates/ethereum/payload/src/lib.rs b/crates/ethereum/payload/src/lib.rs index 53f7be3fcac3..baebd19fa973 100644 --- a/crates/ethereum/payload/src/lib.rs +++ b/crates/ethereum/payload/src/lib.rs @@ -29,9 +29,7 @@ use reth_payload_builder::{ error::PayloadBuilderError, EthBuiltPayload, EthPayloadBuilderAttributes, }; use reth_primitives::{ - constants::{ - eip4844::MAX_DATA_GAS_PER_BLOCK, BEACON_NONCE, EMPTY_RECEIPTS, EMPTY_TRANSACTIONS, - }, + constants::{eip4844::MAX_DATA_GAS_PER_BLOCK, BEACON_NONCE}, eip4844::calculate_excess_blob_gas, proofs::{self, calculate_requests_root}, Block, EthereumHardforks, Header, IntoRecoveredTransaction, Receipt, EMPTY_OMMER_ROOT_HASH, @@ -39,7 +37,9 @@ use reth_primitives::{ }; use reth_provider::StateProviderFactory; use reth_revm::database::StateProviderDatabase; -use reth_transaction_pool::{BestTransactionsAttributes, TransactionPool}; +use reth_transaction_pool::{ + noop::NoopTransactionPool, BestTransactionsAttributes, TransactionPool, +}; use reth_trie::HashedPostState; use revm::{ db::states::bundle_state::BundleRetention, @@ -83,7 +83,7 @@ where &self, args: BuildArguments, ) -> Result, PayloadBuilderError> { - default_ethereum_payload_builder(self.evm_config.clone(), args) + default_ethereum_payload(self.evm_config.clone(), args) } fn build_empty_payload( @@ -91,173 +91,18 @@ where client: &Client, config: PayloadConfig, ) -> Result { - let extra_data = config.extra_data(); - let PayloadConfig { - initialized_block_env, - parent_block, - attributes, - chain_spec, - initialized_cfg, - .. - } = config; - - debug!(target: "payload_builder", parent_hash = ?parent_block.hash(), parent_number = parent_block.number, "building empty payload"); - - let state = client.state_by_block_hash(parent_block.hash()).map_err(|err| { - warn!(target: "payload_builder", - parent_hash=%parent_block.hash(), - %err, - "failed to get state for empty payload" - ); - err - })?; - - let mut db = State::builder() - .with_database(StateProviderDatabase::new(state)) - .with_bundle_update() - .build(); - - let base_fee = initialized_block_env.basefee.to::(); - let block_gas_limit = - initialized_block_env.gas_limit.try_into().unwrap_or(chain_spec.max_gas_limit); - - // apply eip-4788 pre block contract call - pre_block_beacon_root_contract_call( - &mut db, - &self.evm_config, - &chain_spec, - &initialized_cfg, - &initialized_block_env, - attributes.parent_beacon_block_root, - ) - .map_err(|err| { - warn!(target: "payload_builder", - parent_hash=%parent_block.hash(), - %err, - "failed to apply beacon root contract call for empty payload" - ); - PayloadBuilderError::Internal(err.into()) - })?; - - // apply eip-2935 blockhashes update - pre_block_blockhashes_contract_call( - &mut db, - &self.evm_config, - &chain_spec, - &initialized_cfg, - &initialized_block_env, - parent_block.hash(), - ) - .map_err(|err| { - warn!(target: "payload_builder", parent_hash=%parent_block.hash(), %err, "failed to update blockhashes for empty payload"); - PayloadBuilderError::Internal(err.into()) - })?; - - let WithdrawalsOutcome { withdrawals_root, withdrawals } = commit_withdrawals( - &mut db, - &chain_spec, - attributes.timestamp, - attributes.withdrawals.clone(), - ) - .map_err(|err| { - warn!(target: "payload_builder", - parent_hash=%parent_block.hash(), - %err, - "failed to commit withdrawals for empty payload" - ); - err - })?; - - // merge all transitions into bundle state, this would apply the withdrawal balance - // changes and 4788 contract call - db.merge_transitions(BundleRetention::PlainState); - - // calculate the state root - let bundle_state = db.take_bundle(); - let state_root = db - .database - .state_root(HashedPostState::from_bundle_state(&bundle_state.state)) - .map_err(|err| { - warn!(target: "payload_builder", - parent_hash=%parent_block.hash(), - %err, - "failed to calculate state root for empty payload" - ); - err - })?; - - let mut excess_blob_gas = None; - let mut blob_gas_used = None; - - if chain_spec.is_cancun_active_at_timestamp(attributes.timestamp) { - excess_blob_gas = if chain_spec.is_cancun_active_at_timestamp(parent_block.timestamp) { - let parent_excess_blob_gas = parent_block.excess_blob_gas.unwrap_or_default(); - let parent_blob_gas_used = parent_block.blob_gas_used.unwrap_or_default(); - Some(calculate_excess_blob_gas(parent_excess_blob_gas, parent_blob_gas_used)) - } else { - // for the first post-fork block, both parent.blob_gas_used and - // parent.excess_blob_gas are evaluated as 0 - Some(calculate_excess_blob_gas(0, 0)) - }; - - blob_gas_used = Some(0); - } - - // Calculate the requests and the requests root. - let (requests, requests_root) = - if chain_spec.is_prague_active_at_timestamp(attributes.timestamp) { - // We do not calculate the EIP-6110 deposit requests because there are no - // transactions in an empty payload. - let withdrawal_requests = post_block_withdrawal_requests_contract_call( - &self.evm_config, - &mut db, - &initialized_cfg, - &initialized_block_env, - ) - .map_err(|err| PayloadBuilderError::Internal(err.into()))?; - let consolidation_requests = post_block_consolidation_requests_contract_call( - &self.evm_config, - &mut db, - &initialized_cfg, - &initialized_block_env, - ) - .map_err(|err| PayloadBuilderError::Internal(err.into()))?; - - let requests = [withdrawal_requests, consolidation_requests].concat(); - let requests_root = calculate_requests_root(&requests); - (Some(requests.into()), Some(requests_root)) - } else { - (None, None) - }; - - let header = Header { - parent_hash: parent_block.hash(), - ommers_hash: EMPTY_OMMER_ROOT_HASH, - beneficiary: initialized_block_env.coinbase, - state_root, - transactions_root: EMPTY_TRANSACTIONS, - withdrawals_root, - receipts_root: EMPTY_RECEIPTS, - logs_bloom: Default::default(), - timestamp: attributes.timestamp, - mix_hash: attributes.prev_randao, - nonce: BEACON_NONCE, - base_fee_per_gas: Some(base_fee), - number: parent_block.number + 1, - gas_limit: block_gas_limit, - difficulty: U256::ZERO, - gas_used: 0, - extra_data, - blob_gas_used, - excess_blob_gas, - parent_beacon_block_root: attributes.parent_beacon_block_root, - requests_root, + let args = BuildArguments { + client, + config, + // we use defaults here because for the empty payload we don't need to execute anything + pool: NoopTransactionPool::default(), + cached_reads: Default::default(), + cancel: Default::default(), + best_payload: None, }; - - let block = Block { header, body: vec![], ommers: vec![], withdrawals, requests }; - let sealed_block = block.seal_slow(); - - Ok(EthBuiltPayload::new(attributes.payload_id(), sealed_block, U256::ZERO, None)) + default_ethereum_payload(self.evm_config.clone(), args)? + .into_payload() + .ok_or_else(|| PayloadBuilderError::MissingPayload) } } @@ -267,7 +112,7 @@ where /// and configuration, this function creates a transaction payload. Returns /// a result indicating success with the payload or an error in case of failure. #[inline] -pub fn default_ethereum_payload_builder( +pub fn default_ethereum_payload( evm_config: EvmConfig, args: BuildArguments, ) -> Result, PayloadBuilderError> diff --git a/crates/optimism/payload/src/builder.rs b/crates/optimism/payload/src/builder.rs index 883d085a107d..702b8a98ca98 100644 --- a/crates/optimism/payload/src/builder.rs +++ b/crates/optimism/payload/src/builder.rs @@ -12,13 +12,14 @@ use reth_evm::{system_calls::pre_block_beacon_root_contract_call, ConfigureEvm}; use reth_execution_types::ExecutionOutcome; use reth_payload_builder::error::PayloadBuilderError; use reth_primitives::{ - constants::{BEACON_NONCE, EMPTY_RECEIPTS, EMPTY_TRANSACTIONS}, - eip4844::calculate_excess_blob_gas, - proofs, Block, Header, IntoRecoveredTransaction, Receipt, TxType, EMPTY_OMMER_ROOT_HASH, + constants::BEACON_NONCE, eip4844::calculate_excess_blob_gas, proofs, Block, Header, + IntoRecoveredTransaction, Receipt, TxType, EMPTY_OMMER_ROOT_HASH, }; use reth_provider::StateProviderFactory; use reth_revm::database::StateProviderDatabase; -use reth_transaction_pool::{BestTransactionsAttributes, TransactionPool}; +use reth_transaction_pool::{ + noop::NoopTransactionPool, BestTransactionsAttributes, TransactionPool, +}; use reth_trie::HashedPostState; use revm::{ db::states::bundle_state::BundleRetention, @@ -75,7 +76,7 @@ where &self, args: BuildArguments, ) -> Result, PayloadBuilderError> { - optimism_payload_builder(self.evm_config.clone(), args, self.compute_pending_block) + optimism_payload(self.evm_config.clone(), args, self.compute_pending_block) } fn on_missing_payload( @@ -87,137 +88,25 @@ where MissingPayloadBehaviour::AwaitInProgress } + // NOTE: this should only be used for testing purposes because this doesn't have access to L1 + // system txs, hence on_missing_payload we return [MissingPayloadBehaviour::AwaitInProgress]. fn build_empty_payload( &self, client: &Client, config: PayloadConfig, ) -> Result { - let extra_data = config.extra_data(); - let PayloadConfig { - initialized_block_env, - parent_block, - attributes, - chain_spec, - initialized_cfg, - .. - } = config; - - debug!(target: "payload_builder", parent_hash = ?parent_block.hash(), parent_number = parent_block.number, "building empty payload"); - - let state = client.state_by_block_hash(parent_block.hash()).map_err(|err| { - warn!(target: "payload_builder", parent_hash=%parent_block.hash(), %err, "failed to get state for empty payload"); - err - })?; - let mut db = State::builder() - .with_database(StateProviderDatabase::new(state)) - .with_bundle_update() - .build(); - - let base_fee = initialized_block_env.basefee.to::(); - let block_gas_limit: u64 = - initialized_block_env.gas_limit.try_into().unwrap_or(chain_spec.max_gas_limit); - - // apply eip-4788 pre block contract call - pre_block_beacon_root_contract_call( - &mut db, - &self.evm_config, - &chain_spec, - &initialized_cfg, - &initialized_block_env, - attributes.payload_attributes.parent_beacon_block_root, - ) - .map_err(|err| { - warn!(target: "payload_builder", - parent_hash=%parent_block.hash(), - %err, - "failed to apply beacon root contract call for empty payload" - ); - PayloadBuilderError::Internal(err.into()) - })?; - - let WithdrawalsOutcome { withdrawals_root, withdrawals } = commit_withdrawals( - &mut db, - &chain_spec, - attributes.payload_attributes.timestamp, - attributes.payload_attributes.withdrawals.clone(), - ) - .map_err(|err| { - warn!(target: "payload_builder", - parent_hash=%parent_block.hash(), - %err, - "failed to commit withdrawals for empty payload" - ); - err - })?; - - // merge all transitions into bundle state, this would apply the withdrawal balance - // changes and 4788 contract call - db.merge_transitions(BundleRetention::PlainState); - - // calculate the state root - let bundle_state = db.take_bundle(); - let hashed_state = HashedPostState::from_bundle_state(&bundle_state.state); - let state_root = db.database.state_root(hashed_state).map_err(|err| { - warn!(target: "payload_builder", - parent_hash=%parent_block.hash(), - %err, - "failed to calculate state root for empty payload" - ); - err - })?; - - let mut excess_blob_gas = None; - let mut blob_gas_used = None; - - if chain_spec.is_cancun_active_at_timestamp(attributes.payload_attributes.timestamp) { - excess_blob_gas = if chain_spec.is_cancun_active_at_timestamp(parent_block.timestamp) { - let parent_excess_blob_gas = parent_block.excess_blob_gas.unwrap_or_default(); - let parent_blob_gas_used = parent_block.blob_gas_used.unwrap_or_default(); - Some(calculate_excess_blob_gas(parent_excess_blob_gas, parent_blob_gas_used)) - } else { - // for the first post-fork block, both parent.blob_gas_used and - // parent.excess_blob_gas are evaluated as 0 - Some(calculate_excess_blob_gas(0, 0)) - }; - - blob_gas_used = Some(0); - } - - let header = Header { - parent_hash: parent_block.hash(), - ommers_hash: EMPTY_OMMER_ROOT_HASH, - beneficiary: initialized_block_env.coinbase, - state_root, - transactions_root: EMPTY_TRANSACTIONS, - withdrawals_root, - receipts_root: EMPTY_RECEIPTS, - logs_bloom: Default::default(), - timestamp: attributes.payload_attributes.timestamp, - mix_hash: attributes.payload_attributes.prev_randao, - nonce: BEACON_NONCE, - base_fee_per_gas: Some(base_fee), - number: parent_block.number + 1, - gas_limit: block_gas_limit, - difficulty: U256::ZERO, - gas_used: 0, - extra_data, - blob_gas_used, - excess_blob_gas, - parent_beacon_block_root: attributes.payload_attributes.parent_beacon_block_root, - requests_root: None, + let args = BuildArguments { + client, + config, + // we use defaults here because for the empty payload we don't need to execute anything + pool: NoopTransactionPool::default(), + cached_reads: Default::default(), + cancel: Default::default(), + best_payload: None, }; - - let block = Block { header, body: vec![], ommers: vec![], withdrawals, requests: None }; - let sealed_block = block.seal_slow(); - - Ok(OptimismBuiltPayload::new( - attributes.payload_attributes.payload_id(), - sealed_block, - U256::ZERO, - chain_spec, - attributes, - None, - )) + optimism_payload(self.evm_config.clone(), args, false)? + .into_payload() + .ok_or_else(|| PayloadBuilderError::MissingPayload) } } @@ -230,7 +119,7 @@ where /// and configuration, this function creates a transaction payload. Returns /// a result indicating success with the payload or an error in case of failure. #[inline] -pub(crate) fn optimism_payload_builder( +pub(crate) fn optimism_payload( evm_config: EvmConfig, args: BuildArguments, _compute_pending_block: bool, diff --git a/crates/payload/basic/src/lib.rs b/crates/payload/basic/src/lib.rs index 5df63fe4b71e..3847d46a5d99 100644 --- a/crates/payload/basic/src/lib.rs +++ b/crates/payload/basic/src/lib.rs @@ -746,6 +746,31 @@ pub enum BuildOutcome { Cancelled, } +impl BuildOutcome { + /// Consumes the type and returns the payload if the outcome is `Better`. + pub fn into_payload(self) -> Option { + match self { + Self::Better { payload, .. } => Some(payload), + _ => None, + } + } + + /// Returns true if the outcome is `Better`. + pub const fn is_better(&self) -> bool { + matches!(self, Self::Better { .. }) + } + + /// Returns true if the outcome is `Aborted`. + pub const fn is_aborted(&self) -> bool { + matches!(self, Self::Aborted { .. }) + } + + /// Returns true if the outcome is `Cancelled`. + pub const fn is_cancelled(&self) -> bool { + matches!(self, Self::Cancelled) + } +} + /// A collection of arguments used for building payloads. /// /// This struct encapsulates the essential components and configuration required for the payload @@ -756,6 +781,8 @@ pub struct BuildArguments { /// How to interact with the chain. pub client: Client, /// The transaction pool. + /// + /// Or the type that provides the transactions to build the payload. pub pool: Pool, /// Previously cached disk reads pub cached_reads: CachedReads, @@ -779,6 +806,18 @@ impl BuildArguments Self { Self { client, pool, cached_reads, config, cancel, best_payload } } + + /// Maps the transaction pool to a new type. + pub fn with_pool

(self, pool: P) -> BuildArguments { + BuildArguments { + client: self.client, + pool, + cached_reads: self.cached_reads, + config: self.config, + cancel: self.cancel, + best_payload: self.best_payload, + } + } } /// A trait for building payloads that encapsulate Ethereum transactions. diff --git a/crates/transaction-pool/src/noop.rs b/crates/transaction-pool/src/noop.rs index aaa221c53f11..e4055bd5225b 100644 --- a/crates/transaction-pool/src/noop.rs +++ b/crates/transaction-pool/src/noop.rs @@ -220,6 +220,13 @@ impl TransactionPool for NoopTransactionPool { vec![] } + fn get_pending_transactions_by_origin( + &self, + _origin: TransactionOrigin, + ) -> Vec>> { + vec![] + } + fn unique_senders(&self) -> HashSet

{ Default::default() } @@ -251,13 +258,6 @@ impl TransactionPool for NoopTransactionPool { ) -> Result>, BlobStoreError> { Ok(vec![None; versioned_hashes.len()]) } - - fn get_pending_transactions_by_origin( - &self, - _origin: TransactionOrigin, - ) -> Vec>> { - vec![] - } } /// A [`TransactionValidator`] that does nothing.