From c5d8ff0d2fd26316b78c71828a3510eb8a22adad Mon Sep 17 00:00:00 2001 From: Denis Kolodin Date: Thu, 19 Jun 2025 14:23:00 +0200 Subject: [PATCH 1/4] fix: use the method of the PayloadBuilderError --- crates/op-rbuilder/src/builders/context.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/op-rbuilder/src/builders/context.rs b/crates/op-rbuilder/src/builders/context.rs index f9db06de..ef9fe78d 100644 --- a/crates/op-rbuilder/src/builders/context.rs +++ b/crates/op-rbuilder/src/builders/context.rs @@ -283,7 +283,7 @@ impl OpPayloadBuilderCtx { continue; } // this is an error that we should treat as fatal for this attempt - return Err(PayloadBuilderError::EvmExecutionError(Box::new(err))); + return Err(PayloadBuilderError::evm(err)); } }; @@ -457,7 +457,7 @@ impl OpPayloadBuilderCtx { } // this is an error that we should treat as fatal for this attempt log_txn(TxnExecutionResult::EvmError); - return Err(PayloadBuilderError::EvmExecutionError(Box::new(err))); + return Err(PayloadBuilderError::evm(err)); } }; @@ -567,7 +567,7 @@ impl OpPayloadBuilderCtx { let ResultAndState { result, state } = evm .transact(&builder_tx) - .map_err(|err| PayloadBuilderError::EvmExecutionError(Box::new(err)))?; + .map_err(PayloadBuilderError::evm)?; // Add gas used by the transaction to cumulative gas used, before creating the receipt let gas_used = result.gas_used(); From a041b4b8c732a8abf4ee2e63a7a7dfe0accf2ccb Mon Sep 17 00:00:00 2001 From: Denis Kolodin Date: Thu, 19 Jun 2025 15:14:17 +0200 Subject: [PATCH 2/4] feat: add the BestTransactionsExecutor scope --- Cargo.lock | 5 +- crates/op-rbuilder/Cargo.toml | 1 + crates/op-rbuilder/src/builders/context.rs | 248 ++++++++++++++++++++- 3 files changed, 250 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9969f843..8e64a9d0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2984,9 +2984,9 @@ dependencies = [ [[package]] name = "derive-where" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e73f2692d4bd3cac41dca28934a39894200c9fabf49586d77d0e5954af1d7902" +checksum = "510c292c8cf384b1a340b816a9a6cf2599eb8f566a44949024af88418000c50b" dependencies = [ "proc-macro2", "quote", @@ -6217,6 +6217,7 @@ dependencies = [ "reth-transaction-pool", "reth-trie", "revm", + "revm-inspector", "rlimit", "rollup-boost", "secp256k1", diff --git a/crates/op-rbuilder/Cargo.toml b/crates/op-rbuilder/Cargo.toml index 1076478e..b6557918 100644 --- a/crates/op-rbuilder/Cargo.toml +++ b/crates/op-rbuilder/Cargo.toml @@ -132,6 +132,7 @@ tar = { version = "0.4", optional = true } ctor = { version = "0.4.2", optional = true } rlimit = { version = "0.10", optional = true } macros = { path = "src/tests/framework/macros", optional = true } +revm-inspector = "5.0.1" [target.'cfg(unix)'.dependencies] tikv-jemallocator = { version = "0.6", optional = true } diff --git a/crates/op-rbuilder/src/builders/context.rs b/crates/op-rbuilder/src/builders/context.rs index ef9fe78d..5a1737a0 100644 --- a/crates/op-rbuilder/src/builders/context.rs +++ b/crates/op-rbuilder/src/builders/context.rs @@ -6,17 +6,19 @@ use alloy_op_evm::block::receipt_builder::OpReceiptBuilder; use alloy_primitives::{Address, Bytes, TxKind, U256}; use alloy_rpc_types_eth::Withdrawals; use core::fmt::Debug; +use derive_more::Deref; use op_alloy_consensus::{OpDepositReceipt, OpTypedTransaction}; use op_revm::OpSpecId; use reth::payload::PayloadBuilderAttributes; use reth_basic_payload_builder::PayloadConfig; use reth_chainspec::{EthChainSpec, EthereumHardforks}; use reth_evm::{ - eth::receipt_builder::ReceiptBuilderCtx, ConfigureEvm, Evm, EvmEnv, EvmError, InvalidTxError, + eth::receipt_builder::ReceiptBuilderCtx, precompiles::PrecompilesMap, ConfigureEvm, Evm, + EvmEnv, EvmError, InvalidTxError, }; use reth_node_api::PayloadBuilderError; use reth_optimism_chainspec::OpChainSpec; -use reth_optimism_evm::{OpEvmConfig, OpNextBlockEnvAttributes}; +use reth_optimism_evm::{OpEvm, OpEvmConfig, OpNextBlockEnvAttributes}; use reth_optimism_forks::OpHardforks; use reth_optimism_node::OpPayloadBuilderAttributes; use reth_optimism_payload_builder::{config::OpDAConfig, error::OpPayloadBuilderError}; @@ -33,6 +35,7 @@ use reth_provider::ProviderError; use reth_revm::{context::Block, State}; use reth_transaction_pool::{BestTransactionsAttributes, PoolTransaction}; use revm::{context::result::ResultAndState, Database, DatabaseCommit}; +use revm_inspector::NoOpInspector; use std::{sync::Arc, time::Instant}; use tokio_util::sync::CancellationToken; use tracing::{debug, info, trace, warn}; @@ -193,6 +196,13 @@ impl OpPayloadBuilderCtx { } } +pub enum StepStatus { + // TODO: Add a reason here + Continue, + Interrupt, + Failed(PayloadBuilderError), +} + impl OpPayloadBuilderCtx { /// Constructs a receipt for the given transaction. fn build_receipt( @@ -688,3 +698,237 @@ where Ok(builder_tx) } + +#[derive(Deref)] +pub struct BestTransactionsExecutor<'a, DB, E, TXS> +where + DB: Database, + E: Debug + Default, + TXS: PayloadTxsBounds, +{ + #[deref] + ctx: &'a OpPayloadBuilderCtx, + info: &'a mut ExecutionInfo, + // db: &'a mut State, + best_txs: TXS, + block_gas_limit: u64, + block_da_limit: Option, + + evm: OpEvm<&'a mut State, NoOpInspector, PrecompilesMap>, + base_fee: u64, + tx_da_limit: Option, + block_attr: BlockConditionalAttributes, + + execute_txs_start_time: Instant, + num_txs_considered: usize, + num_txs_simulated: usize, + num_txs_simulated_success: usize, + num_txs_simulated_fail: usize, + num_bundles_reverted: usize, +} + +impl<'a, DB, E, TXS> BestTransactionsExecutor<'a, DB, E, TXS> +where + DB: Database, + E: Debug + Default, + TXS: PayloadTxsBounds, +{ + pub fn new( + ctx: &'a OpPayloadBuilderCtx, + info: &'a mut ExecutionInfo, + db: &'a mut State, + best_txs: TXS, + block_gas_limit: u64, + block_da_limit: Option, + ) -> Self { + let start = Instant::now(); + let base_fee = ctx.base_fee(); + let tx_da_limit = ctx.da_config.max_da_tx_size(); + let env = ctx.evm_env.clone(); + let evm = ctx.evm_config.evm_with_env(&mut *db, env); + + let block_attr = BlockConditionalAttributes { + number: ctx.block_number(), + timestamp: ctx.attributes().timestamp(), + }; + Self { + ctx, + info, + // db, + best_txs, + block_gas_limit, + block_da_limit, + + evm, + base_fee, + tx_da_limit, + block_attr, + + execute_txs_start_time: start, + num_txs_considered: 0, + num_txs_simulated: 0, + num_txs_simulated_success: 0, + num_txs_simulated_fail: 0, + num_bundles_reverted: 0, + } + } + + pub fn execute_best_one(&mut self, tx: TXS::Transaction) -> StepStatus { + let interop = tx.interop_deadline(); + let reverted_hashes = tx.reverted_hashes().clone(); + let conditional = tx.conditional().cloned(); + + let tx_da_size = tx.estimated_da_size(); + let tx = tx.into_consensus(); + let tx_hash = tx.tx_hash(); + + // exclude reverting transaction if: + // - the transaction comes from a bundle (is_some) and the hash **is not** in reverted hashes + // Note that we need to use the Option to signal whether the transaction comes from a bundle, + // otherwise, we would exclude all transactions that are not in the reverted hashes. + let is_bundle_tx = reverted_hashes.is_some(); + let exclude_reverting_txs = is_bundle_tx && !reverted_hashes.unwrap().contains(&tx_hash); + + let log_txn = |result: TxnExecutionResult| { + debug!( + target: "payload_builder", + message = "Considering transaction", + tx_hash = ?tx_hash, + tx_da_size = ?tx_da_size, + exclude_reverting_txs = ?exclude_reverting_txs, + result = %result, + ); + }; + + self.num_txs_considered += 1; + + if let Some(conditional) = conditional { + // TODO: ideally we should get this from the txpool stream + if !conditional.matches_block_attributes(&self.block_attr) { + self.best_txs.mark_invalid(tx.signer(), tx.nonce()); + return StepStatus::Continue; + } + } + + // TODO: remove this condition and feature once we are comfortable enabling interop for everything + if cfg!(feature = "interop") { + // We skip invalid cross chain txs, they would be removed on the next block update in + // the maintenance job + if let Some(interop) = interop { + if !is_valid_interop(interop, self.config.attributes.timestamp()) { + log_txn(TxnExecutionResult::InteropFailed); + self.best_txs.mark_invalid(tx.signer(), tx.nonce()); + return StepStatus::Continue; + } + } + } + + // ensure we still have capacity for this transaction + if let Err(result) = self.info.is_tx_over_limits( + tx_da_size, + self.block_gas_limit, + self.tx_da_limit, + self.block_da_limit, + tx.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 + log_txn(result); + self.best_txs.mark_invalid(tx.signer(), tx.nonce()); + return StepStatus::Continue; + } + + // A sequencer's block should never contain blob or deposit transactions from the pool. + if tx.is_eip4844() || tx.is_deposit() { + log_txn(TxnExecutionResult::SequencerTransaction); + self.best_txs.mark_invalid(tx.signer(), tx.nonce()); + return StepStatus::Continue; + } + + // check if the job was cancelled, if so we can exit early + if self.cancel.is_cancelled() { + return StepStatus::Interrupt; + } + + let tx_simulation_start_time = Instant::now(); + let ResultAndState { result, state } = match self.evm.transact(&tx) { + Ok(res) => res, + Err(err) => { + if let Some(err) = err.as_invalid_tx_err() { + if err.is_nonce_too_low() { + // if the nonce is too low, we can skip this transaction + log_txn(TxnExecutionResult::NonceTooLow); + trace!(target: "payload_builder", %err, ?tx, "skipping nonce too low transaction"); + } else { + // if the transaction is invalid, we can skip it and all of its + // descendants + log_txn(TxnExecutionResult::InternalError(err.clone())); + trace!(target: "payload_builder", %err, ?tx, "skipping invalid transaction and its descendants"); + self.best_txs.mark_invalid(tx.signer(), tx.nonce()); + } + + return StepStatus::Continue; + } + // this is an error that we should treat as fatal for this attempt + log_txn(TxnExecutionResult::EvmError); + return StepStatus::Failed(PayloadBuilderError::evm(err)); + } + }; + + self.metrics + .tx_simulation_duration + .record(tx_simulation_start_time.elapsed()); + self.metrics.tx_byte_size.record(tx.inner().size() as f64); + self.num_txs_simulated += 1; + if result.is_success() { + log_txn(TxnExecutionResult::Success); + self.num_txs_simulated_success += 1; + } else { + self.num_txs_simulated_fail += 1; + if is_bundle_tx { + self.num_bundles_reverted += 1; + } + if exclude_reverting_txs { + log_txn(TxnExecutionResult::RevertedAndExcluded); + info!(target: "payload_builder", tx_hash = ?tx.tx_hash(), "skipping reverted transaction"); + self.best_txs.mark_invalid(tx.signer(), tx.nonce()); + return StepStatus::Continue; + } else { + log_txn(TxnExecutionResult::Reverted); + } + } + + // add gas used by the transaction to cumulative gas used, before creating the + // receipt + let gas_used = result.gas_used(); + self.info.cumulative_gas_used += gas_used; + // record tx da size + self.info.cumulative_da_bytes_used += tx_da_size; + + // Push transaction changeset and calculate header bloom filter for receipt. + let ctx = ReceiptBuilderCtx { + tx: tx.inner(), + evm: &self.evm, + result, + state: &state, + cumulative_gas_used: self.info.cumulative_gas_used, + }; + self.info.receipts.push(self.build_receipt(ctx, None)); + + // commit changes + self.evm.db_mut().commit(state); + + // update add to total fees + let miner_fee = tx + .effective_tip_per_gas(self.base_fee) + .expect("fee is always valid; execution succeeded"); + self.info.total_fees += U256::from(miner_fee) * U256::from(gas_used); + + // append sender and transaction to the respective lists + self.info.executed_senders.push(tx.signer()); + self.info.executed_transactions.push(tx.into_inner()); + + StepStatus::Continue + } +} From 2c8c821de00f76e2e7f9a860b078bc47ff73ea97 Mon Sep 17 00:00:00 2001 From: Denis Kolodin Date: Thu, 19 Jun 2025 15:22:17 +0200 Subject: [PATCH 3/4] feat: add execute_best_transactions_method --- crates/op-rbuilder/src/builders/context.rs | 71 +++++++++++++++++++++- 1 file changed, 69 insertions(+), 2 deletions(-) diff --git a/crates/op-rbuilder/src/builders/context.rs b/crates/op-rbuilder/src/builders/context.rs index 5a1737a0..0b38747c 100644 --- a/crates/op-rbuilder/src/builders/context.rs +++ b/crates/op-rbuilder/src/builders/context.rs @@ -709,7 +709,6 @@ where #[deref] ctx: &'a OpPayloadBuilderCtx, info: &'a mut ExecutionInfo, - // db: &'a mut State, best_txs: TXS, block_gas_limit: u64, block_da_limit: Option, @@ -754,7 +753,6 @@ where Self { ctx, info, - // db, best_txs, block_gas_limit, block_da_limit, @@ -773,6 +771,75 @@ where } } + /// Executes the given best transactions and updates the execution info. + /// + /// Returns `Ok(Some(())` if the job was cancelled. + pub fn execute_best_transactions(&mut self) -> Result, PayloadBuilderError> + where + DB: Database, + { + info!( + target: "payload_builder", + message = "Executing best transactions", + block_da_limit = ?self.block_da_limit, + tx_da_limit = ?self.tx_da_limit, + block_gas_limit = ?self.block_gas_limit, + ); + + // Remove once we merge Reth 1.4.4 + // Fixed in https://github.com/paradigmxyz/reth/pull/16514 + let block_da_limit = self.block_da_limit.map_or(-1.0, |v| v as f64); + self.metrics.da_block_size_limit.set(block_da_limit); + let tx_da_limit = self.tx_da_limit.map_or(-1.0, |v| v as f64); + self.metrics.da_tx_size_limit.set(tx_da_limit); + + let block_attr = BlockConditionalAttributes { + number: self.block_number(), + timestamp: self.attributes().timestamp(), + }; + + while let Some(tx) = self.best_txs.next(()) { + match self.execute_best_one(tx) { + StepStatus::Continue => {} + StepStatus::Interrupt => { + return Ok(Some(())); + } + StepStatus::Failed(err) => { + return Err(err); + } + } + } + + self.metrics + .payload_tx_simulation_duration + .record(self.execute_txs_start_time.elapsed()); + self.metrics + .payload_num_tx_considered + .record(self.num_txs_considered as f64); + self.metrics + .payload_num_tx_simulated + .record(self.num_txs_simulated as f64); + self.metrics + .payload_num_tx_simulated_success + .record(self.num_txs_simulated_success as f64); + self.metrics + .payload_num_tx_simulated_fail + .record(self.num_txs_simulated_fail as f64); + self.metrics + .bundles_reverted + .record(self.num_bundles_reverted as f64); + + debug!( + target: "payload_builder", + message = "Completed executing best transactions", + txs_executed = self.num_txs_considered, + txs_applied = self.num_txs_simulated_success, + txs_rejected = self.num_txs_simulated_fail, + bundles_reverted = self.num_bundles_reverted, + ); + Ok(None) + } + pub fn execute_best_one(&mut self, tx: TXS::Transaction) -> StepStatus { let interop = tx.interop_deadline(); let reverted_hashes = tx.reverted_hashes().clone(); From c57de24688d7c7d4bc328113d7697e3442b90830 Mon Sep 17 00:00:00 2001 From: Denis Kolodin Date: Thu, 19 Jun 2025 15:32:38 +0200 Subject: [PATCH 4/4] feat: use the best transactions executor --- crates/op-rbuilder/src/builders/context.rs | 243 +----------------- .../src/builders/flashblocks/payload.rs | 5 +- .../src/builders/standard/payload.rs | 5 +- 3 files changed, 17 insertions(+), 236 deletions(-) diff --git a/crates/op-rbuilder/src/builders/context.rs b/crates/op-rbuilder/src/builders/context.rs index 0b38747c..d312f965 100644 --- a/crates/op-rbuilder/src/builders/context.rs +++ b/crates/op-rbuilder/src/builders/context.rs @@ -197,7 +197,6 @@ impl OpPayloadBuilderCtx { } pub enum StepStatus { - // TODO: Add a reason here Continue, Interrupt, Failed(PayloadBuilderError), @@ -324,235 +323,20 @@ impl OpPayloadBuilderCtx { /// Executes the given best transactions and updates the execution info. /// /// Returns `Ok(Some(())` if the job was cancelled. - pub fn execute_best_transactions( - &self, - info: &mut ExecutionInfo, - db: &mut State, - mut best_txs: impl PayloadTxsBounds, + pub fn best_transactions_executor<'a, DB, E, TXS>( + &'a self, + info: &'a mut ExecutionInfo, + db: &'a mut State, + best_txs: TXS, block_gas_limit: u64, block_da_limit: Option, - ) -> Result, PayloadBuilderError> + ) -> BestTransactionsExecutor<'a, DB, E, TXS> where + E: Debug + Default, DB: Database, + TXS: PayloadTxsBounds, { - let execute_txs_start_time = Instant::now(); - let mut num_txs_considered = 0; - let mut num_txs_simulated = 0; - let mut num_txs_simulated_success = 0; - let mut num_txs_simulated_fail = 0; - let mut num_bundles_reverted = 0; - let base_fee = self.base_fee(); - let tx_da_limit = self.da_config.max_da_tx_size(); - let mut evm = self.evm_config.evm_with_env(&mut *db, self.evm_env.clone()); - - info!( - target: "payload_builder", - message = "Executing best transactions", - block_da_limit = ?block_da_limit, - tx_da_limit = ?tx_da_limit, - block_gas_limit = ?block_gas_limit, - ); - - // Remove once we merge Reth 1.4.4 - // Fixed in https://github.com/paradigmxyz/reth/pull/16514 - self.metrics - .da_block_size_limit - .set(block_da_limit.map_or(-1.0, |v| v as f64)); - self.metrics - .da_tx_size_limit - .set(tx_da_limit.map_or(-1.0, |v| v as f64)); - - let block_attr = BlockConditionalAttributes { - number: self.block_number(), - timestamp: self.attributes().timestamp(), - }; - - while let Some(tx) = best_txs.next(()) { - let interop = tx.interop_deadline(); - let reverted_hashes = tx.reverted_hashes().clone(); - let conditional = tx.conditional().cloned(); - - let tx_da_size = tx.estimated_da_size(); - let tx = tx.into_consensus(); - let tx_hash = tx.tx_hash(); - - // exclude reverting transaction if: - // - the transaction comes from a bundle (is_some) and the hash **is not** in reverted hashes - // Note that we need to use the Option to signal whether the transaction comes from a bundle, - // otherwise, we would exclude all transactions that are not in the reverted hashes. - let is_bundle_tx = reverted_hashes.is_some(); - let exclude_reverting_txs = - is_bundle_tx && !reverted_hashes.unwrap().contains(&tx_hash); - - let log_txn = |result: TxnExecutionResult| { - debug!( - target: "payload_builder", - message = "Considering transaction", - tx_hash = ?tx_hash, - tx_da_size = ?tx_da_size, - exclude_reverting_txs = ?exclude_reverting_txs, - result = %result, - ); - }; - - num_txs_considered += 1; - - if let Some(conditional) = conditional { - // TODO: ideally we should get this from the txpool stream - if !conditional.matches_block_attributes(&block_attr) { - best_txs.mark_invalid(tx.signer(), tx.nonce()); - continue; - } - } - - // TODO: remove this condition and feature once we are comfortable enabling interop for everything - if cfg!(feature = "interop") { - // We skip invalid cross chain txs, they would be removed on the next block update in - // the maintenance job - if let Some(interop) = interop { - if !is_valid_interop(interop, self.config.attributes.timestamp()) { - log_txn(TxnExecutionResult::InteropFailed); - best_txs.mark_invalid(tx.signer(), tx.nonce()); - continue; - } - } - } - - // ensure we still have capacity for this transaction - if let Err(result) = info.is_tx_over_limits( - tx_da_size, - block_gas_limit, - tx_da_limit, - block_da_limit, - tx.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 - log_txn(result); - best_txs.mark_invalid(tx.signer(), tx.nonce()); - continue; - } - - // A sequencer's block should never contain blob or deposit transactions from the pool. - if tx.is_eip4844() || tx.is_deposit() { - log_txn(TxnExecutionResult::SequencerTransaction); - best_txs.mark_invalid(tx.signer(), tx.nonce()); - continue; - } - - // check if the job was cancelled, if so we can exit early - if self.cancel.is_cancelled() { - return Ok(Some(())); - } - - let tx_simulation_start_time = Instant::now(); - let ResultAndState { result, state } = match evm.transact(&tx) { - Ok(res) => res, - Err(err) => { - if let Some(err) = err.as_invalid_tx_err() { - if err.is_nonce_too_low() { - // if the nonce is too low, we can skip this transaction - log_txn(TxnExecutionResult::NonceTooLow); - trace!(target: "payload_builder", %err, ?tx, "skipping nonce too low transaction"); - } else { - // if the transaction is invalid, we can skip it and all of its - // descendants - log_txn(TxnExecutionResult::InternalError(err.clone())); - trace!(target: "payload_builder", %err, ?tx, "skipping invalid transaction and its descendants"); - best_txs.mark_invalid(tx.signer(), tx.nonce()); - } - - continue; - } - // this is an error that we should treat as fatal for this attempt - log_txn(TxnExecutionResult::EvmError); - return Err(PayloadBuilderError::evm(err)); - } - }; - - self.metrics - .tx_simulation_duration - .record(tx_simulation_start_time.elapsed()); - self.metrics.tx_byte_size.record(tx.inner().size() as f64); - num_txs_simulated += 1; - if result.is_success() { - log_txn(TxnExecutionResult::Success); - num_txs_simulated_success += 1; - } else { - num_txs_simulated_fail += 1; - if is_bundle_tx { - num_bundles_reverted += 1; - } - if exclude_reverting_txs { - log_txn(TxnExecutionResult::RevertedAndExcluded); - info!(target: "payload_builder", tx_hash = ?tx.tx_hash(), "skipping reverted transaction"); - best_txs.mark_invalid(tx.signer(), tx.nonce()); - continue; - } else { - log_txn(TxnExecutionResult::Reverted); - } - } - - // add gas used by the transaction to cumulative gas used, before creating the - // receipt - let gas_used = result.gas_used(); - info.cumulative_gas_used += gas_used; - // record tx da size - info.cumulative_da_bytes_used += tx_da_size; - - // Push transaction changeset and calculate header bloom filter for receipt. - let ctx = ReceiptBuilderCtx { - tx: tx.inner(), - evm: &evm, - result, - state: &state, - cumulative_gas_used: info.cumulative_gas_used, - }; - info.receipts.push(self.build_receipt(ctx, None)); - - // commit changes - evm.db_mut().commit(state); - - // update add to total fees - let miner_fee = tx - .effective_tip_per_gas(base_fee) - .expect("fee is always valid; execution succeeded"); - info.total_fees += U256::from(miner_fee) * U256::from(gas_used); - - // append sender and transaction to the respective lists - info.executed_senders.push(tx.signer()); - info.executed_transactions.push(tx.into_inner()); - } - - self.metrics - .payload_tx_simulation_duration - .record(execute_txs_start_time.elapsed()); - self.metrics - .payload_num_tx_considered - .record(num_txs_considered as f64); - self.metrics - .payload_num_tx_simulated - .record(num_txs_simulated as f64); - self.metrics - .payload_num_tx_simulated_success - .record(num_txs_simulated_success as f64); - self.metrics - .payload_num_tx_simulated_fail - .record(num_txs_simulated_fail as f64); - self.metrics - .bundles_reverted - .record(num_bundles_reverted as f64); - - debug!( - target: "payload_builder", - message = "Completed executing best transactions", - txs_executed = num_txs_considered, - txs_applied = num_txs_simulated_success, - txs_rejected = num_txs_simulated_fail, - bundles_reverted = num_bundles_reverted, - ); - Ok(None) + BestTransactionsExecutor::new(self, info, db, best_txs, block_gas_limit, block_da_limit) } pub fn add_builder_tx( @@ -774,7 +558,7 @@ where /// Executes the given best transactions and updates the execution info. /// /// Returns `Ok(Some(())` if the job was cancelled. - pub fn execute_best_transactions(&mut self) -> Result, PayloadBuilderError> + pub fn execute(&mut self) -> Result, PayloadBuilderError> where DB: Database, { @@ -793,11 +577,6 @@ where let tx_da_limit = self.tx_da_limit.map_or(-1.0, |v| v as f64); self.metrics.da_tx_size_limit.set(tx_da_limit); - let block_attr = BlockConditionalAttributes { - number: self.block_number(), - timestamp: self.attributes().timestamp(), - }; - while let Some(tx) = self.best_txs.next(()) { match self.execute_best_one(tx) { StepStatus::Continue => {} @@ -840,7 +619,7 @@ where Ok(None) } - pub fn execute_best_one(&mut self, tx: TXS::Transaction) -> StepStatus { + fn execute_best_one(&mut self, tx: TXS::Transaction) -> StepStatus { let interop = tx.interop_deadline(); let reverted_hashes = tx.reverted_hashes().clone(); let conditional = tx.conditional().cloned(); diff --git a/crates/op-rbuilder/src/builders/flashblocks/payload.rs b/crates/op-rbuilder/src/builders/flashblocks/payload.rs index 56c1cb18..5d0fee55 100644 --- a/crates/op-rbuilder/src/builders/flashblocks/payload.rs +++ b/crates/op-rbuilder/src/builders/flashblocks/payload.rs @@ -461,13 +461,14 @@ where let tx_execution_start_time = Instant::now(); if ctx - .execute_best_transactions( + .best_transactions_executor( &mut info, &mut db, best_txs, total_gas_per_batch.min(ctx.block_gas_limit()), total_da_per_batch, - )? + ) + .execute()? .is_some() { // Handles job cancellation diff --git a/crates/op-rbuilder/src/builders/standard/payload.rs b/crates/op-rbuilder/src/builders/standard/payload.rs index eaf98c2c..fabeb1ec 100644 --- a/crates/op-rbuilder/src/builders/standard/payload.rs +++ b/crates/op-rbuilder/src/builders/standard/payload.rs @@ -405,13 +405,14 @@ impl OpBuilder<'_, Txs> { .transaction_pool_fetch_duration .record(best_txs_start_time.elapsed()); if ctx - .execute_best_transactions( + .best_transactions_executor( &mut info, state, best_txs, block_gas_limit, block_da_limit, - )? + ) + .execute()? .is_some() { return Ok(BuildOutcomeKind::Cancelled);