Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions crates/op-rbuilder/src/args/op.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ pub struct OpRbuilderArgs {
)]
pub chain_block_time: u64,

/// max gas a transaction can use
#[arg(long = "builder.max_gas_per_txn")]
pub max_gas_per_txn: Option<u64>,

/// Signals whether to log pool transaction events
#[arg(long = "builder.log-pool-transactions", default_value = "false")]
pub log_pool_transactions: bool,
Expand Down
10 changes: 10 additions & 0 deletions crates/op-rbuilder/src/builders/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ pub struct OpPayloadBuilderCtx<ExtraCtx: Debug + Default = ()> {
pub metrics: Arc<OpRBuilderMetrics>,
/// Extra context for the payload builder
pub extra_ctx: ExtraCtx,
/// Max gas that can be used by a transaction.
pub max_gas_per_txn: Option<u64>,
}

impl<ExtraCtx: Debug + Default> OpPayloadBuilderCtx<ExtraCtx> {
Expand Down Expand Up @@ -491,6 +493,14 @@ impl<ExtraCtx: Debug + Default> OpPayloadBuilderCtx<ExtraCtx> {
// add gas used by the transaction to cumulative gas used, before creating the
// receipt
let gas_used = result.gas_used();
if let Some(max_gas_per_txn) = self.max_gas_per_txn {
if gas_used > max_gas_per_txn {
log_txn(TxnExecutionResult::MaxGasUsageExceeded);
best_txs.mark_invalid(tx.signer(), tx.nonce());
continue;
}
}

info.cumulative_gas_used += gas_used;
// record tx da size
info.cumulative_da_bytes_used += tx_da_size;
Expand Down
1 change: 1 addition & 0 deletions crates/op-rbuilder/src/builders/flashblocks/payload.rs
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,7 @@ where
flashblock_index: 0,
target_flashblock_count: self.config.flashblocks_per_block(),
},
max_gas_per_txn: self.config.max_gas_per_txn,
};

let state_provider = self.client.state_by_block_hash(ctx.parent().hash())?;
Expand Down
4 changes: 4 additions & 0 deletions crates/op-rbuilder/src/builders/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,8 @@ pub struct BuilderConfig<Specific: Clone> {

/// Configuration values that are specific to the block builder implementation used.
pub specific: Specific,
/// Maximum gas a transaction can use before being excluded.
pub max_gas_per_txn: Option<u64>,
}

impl<S: Debug + Clone> core::fmt::Debug for BuilderConfig<S> {
Expand Down Expand Up @@ -145,6 +147,7 @@ impl<S: Default + Clone> Default for BuilderConfig<S> {
da_config: OpDAConfig::default(),
specific: S::default(),
sampling_ratio: 100,
max_gas_per_txn: None,
}
}
}
Expand All @@ -164,6 +167,7 @@ where
block_time_leeway: Duration::from_secs(args.extra_block_deadline_secs),
da_config: Default::default(),
sampling_ratio: args.telemetry.sampling_ratio,
max_gas_per_txn: args.max_gas_per_txn,
specific: S::try_from(args)?,
})
}
Expand Down
1 change: 1 addition & 0 deletions crates/op-rbuilder/src/builders/standard/payload.rs
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,7 @@ where
builder_signer: self.config.builder_signer,
metrics: self.metrics.clone(),
extra_ctx: Default::default(),
max_gas_per_txn: self.config.max_gas_per_txn,
};

let builder = OpBuilder::new(best);
Expand Down
1 change: 1 addition & 0 deletions crates/op-rbuilder/src/primitives/reth/execution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ pub enum TxnExecutionResult {
Success,
Reverted,
RevertedAndExcluded,
MaxGasUsageExceeded,
}

#[derive(Default, Debug)]
Expand Down
7 changes: 7 additions & 0 deletions crates/op-rbuilder/src/tests/framework/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ use super::{TransactionBuilder, FUNDED_PRIVATE_KEYS};
pub trait TransactionBuilderExt {
fn random_valid_transfer(self) -> Self;
fn random_reverting_transaction(self) -> Self;
fn random_big_transaction(self) -> Self;
}

impl TransactionBuilderExt for TransactionBuilder {
Expand All @@ -33,6 +34,12 @@ impl TransactionBuilderExt for TransactionBuilder {
fn random_reverting_transaction(self) -> Self {
self.with_create().with_input(hex!("60006000fd").into()) // PUSH1 0x00 PUSH1 0x00 REVERT
}

fn random_big_transaction(self) -> Self {
// PUSH13 0x63ffffffff60005260046000f3 PUSH1 0x00 MSTORE PUSH1 0x02 PUSH1 0x0d PUSH1 0x13 PUSH1 0x00 CREATE2
self.with_create()
.with_input(hex!("6c63ffffffff60005260046000f36000526002600d60136000f5").into())
}
}

pub trait ChainDriverExt {
Expand Down
80 changes: 79 additions & 1 deletion crates/op-rbuilder/src/tests/smoke.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
use crate::tests::{LocalInstance, TransactionBuilderExt};
use crate::{
args::OpRbuilderArgs,
tests::{LocalInstance, TransactionBuilderExt},
};
use alloy_primitives::TxHash;

use core::{
sync::atomic::{AtomicBool, Ordering},
time::Duration,
Expand Down Expand Up @@ -189,3 +193,77 @@ async fn test_no_tx_pool(rbuilder: LocalInstance) -> eyre::Result<()> {

Ok(())
}

#[rb_test(args = OpRbuilderArgs {
max_gas_per_txn: Some(25000),
..Default::default()
})]
async fn chain_produces_big_tx_with_gas_limit(rbuilder: LocalInstance) -> eyre::Result<()> {
let driver = rbuilder.driver().await?;

#[cfg(target_os = "linux")]
let driver = driver
.with_validation_node(crate::tests::ExternalNode::reth().await?)
.await?;

// insert valid txn under limit
let tx = driver
.create_transaction()
.random_valid_transfer()
.send()
.await
.expect("Failed to send transaction");

// insert txn with gas usage above limit
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add an assertion to verify that this tx isn't in the block

let tx_high_gas = driver
.create_transaction()
.random_big_transaction()
.send()
.await
.expect("Failed to send transaction");

let block = driver.build_new_block_with_current_timestamp(None).await?;
let txs = block.transactions;

assert!(txs.len() == 4);
// assert we included the tx with gas under limit
let inclusion_result = txs.hashes().find(|hash| hash == tx.tx_hash());
assert!(inclusion_result.is_some());

// assert we do not include the tx with gas above limit
let exclusion_result = txs.hashes().find(|hash| hash == tx_high_gas.tx_hash());
assert!(exclusion_result.is_none());

Ok(())
}

#[rb_test(args = OpRbuilderArgs {
..Default::default()
})]
async fn chain_produces_big_tx_without_gas_limit(rbuilder: LocalInstance) -> eyre::Result<()> {
let driver = rbuilder.driver().await?;

#[cfg(target_os = "linux")]
let driver = driver
.with_validation_node(crate::tests::ExternalNode::reth().await?)
.await?;

// insert txn with gas usage but there is no limit
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

similarly assert that this tx is included in the block

let tx = driver
.create_transaction()
.random_big_transaction()
.send()
.await
.expect("Failed to send transaction");

let block = driver.build_new_block_with_current_timestamp(None).await?;
let txs = block.transactions;

// assert we included the tx
let inclusion_result = txs.hashes().find(|hash| hash == tx.tx_hash());
assert!(inclusion_result.is_some());

assert!(txs.len() == 4);

Ok(())
}
Loading