Skip to content

Commit 7d47bb7

Browse files
committed
gas-limiter: split bundle and mempool into independent per-address pools
Bundle txs and mempool txs share one per-address budget today, so any limit tight enough to deter bundle abuse also throttles normal users. Give each source its own AddressGasLimiter (held together as Option<GasLimiters>) so bundle and mempool capacity/refill can be configured separately, and route at the call site rather than via an internal scope flag. Also relabel op_rbuilder_tx_simulation_duration with kind={bundle,regular} and result={success,reverted} labels so simulation latency can be sliced by tx source and EVM outcome.
1 parent 790480f commit 7d47bb7

7 files changed

Lines changed: 192 additions & 106 deletions

File tree

crates/op-rbuilder/src/builder/context.rs

Lines changed: 25 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@ use tracing::{debug, info, trace};
3838
use crate::{
3939
backrun_bundle::BackrunBundlesPayloadCtx,
4040
evm::OpBlockEvmFactory,
41-
gas_limiter::AddressGasLimiter,
42-
metrics::OpRBuilderMetrics,
41+
gas_limiter::GasLimiters,
42+
metrics::{OpRBuilderMetrics, record_tx_simulation_duration},
4343
primitives::reth::{ExecutionInfo, TxnExecutionResult},
4444
traits::PayloadTxsBounds,
4545
};
@@ -66,8 +66,9 @@ pub struct OpPayloadBuilderCtx {
6666
pub max_gas_per_txn: Option<u64>,
6767
/// Maximum cumulative uncompressed (EIP-2718 encoded) block size in bytes.
6868
pub max_uncompressed_block_size: Option<u64>,
69-
/// Rate limiting based on gas. This is an optional feature.
70-
pub address_gas_limiter: AddressGasLimiter,
69+
/// Per-source gas rate limiters (one per tx source). `None` when the
70+
/// limiter is disabled in config.
71+
pub gas_limiters: Option<GasLimiters>,
7172
/// Backrun bundles context.
7273
pub backrun_ctx: BackrunBundlesPayloadCtx,
7374
/// Skip reverted txs in subsequent flashblocks
@@ -560,9 +561,11 @@ impl OpPayloadBuilderCtx {
560561
}
561562
};
562563

563-
self.metrics
564-
.tx_simulation_duration
565-
.record(tx_simulation_start_time.elapsed());
564+
record_tx_simulation_duration(
565+
tx_simulation_start_time.elapsed(),
566+
is_bundle_tx,
567+
!result.is_success(),
568+
);
566569
self.metrics.tx_byte_size.record(tx.inner().size() as f64);
567570
num_txs_simulated += 1;
568571

@@ -583,11 +586,11 @@ impl OpPayloadBuilderCtx {
583586
);
584587
}
585588

586-
if self
587-
.address_gas_limiter
588-
.consume_gas(tx.signer(), gas_used)
589-
.is_err()
590-
{
589+
let gas_limiter = self
590+
.gas_limiters
591+
.as_ref()
592+
.map(|l| if is_bundle_tx { &l.bundle } else { &l.mempool });
593+
if gas_limiter.is_some_and(|l| l.consume_gas(tx.signer(), gas_used).is_err()) {
591594
log_txn(TxnExecutionResult::MaxGasUsageExceeded);
592595
best_txs.mark_invalid(tx.signer(), tx.nonce());
593596
continue;
@@ -834,21 +837,23 @@ impl OpPayloadBuilderCtx {
834837
continue;
835838
}
836839
};
837-
self.metrics
838-
.tx_simulation_duration
839-
.record(br_simulation_start.elapsed());
840+
record_tx_simulation_duration(
841+
br_simulation_start.elapsed(),
842+
true,
843+
!br_result.is_success(),
844+
);
840845
self.metrics
841846
.tx_byte_size
842847
.record(bundle.backrun_tx.inner().size() as f64);
843848
num_txs_simulated += 1;
844849

845850
let br_gas_used = br_result.gas_used();
846851

847-
if self
848-
.address_gas_limiter
849-
.consume_gas(bundle.backrun_tx.signer(), br_gas_used)
850-
.is_err()
851-
{
852+
if self.gas_limiters.as_ref().is_some_and(|l| {
853+
l.bundle
854+
.consume_gas(bundle.backrun_tx.signer(), br_gas_used)
855+
.is_err()
856+
}) {
852857
log_br_txn(TxnExecutionResult::MaxGasUsageExceeded);
853858
continue;
854859
}

crates/op-rbuilder/src/builder/payload.rs

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use crate::{
1010
timing::{FlashblockScheduler, compute_slot_offset_ms},
1111
},
1212
evm::OpBlockEvmFactory,
13-
gas_limiter::AddressGasLimiter,
13+
gas_limiter::GasLimiters,
1414
metrics::{OpRBuilderMetrics, record_flashblock_publish_timing},
1515
primitives::reth::ExecutionInfo,
1616
runtime_ext::RuntimeExt,
@@ -300,8 +300,8 @@ pub(super) struct OpPayloadBuilderInner<Pool, Client, BuilderTx> {
300300
metrics: Arc<OpRBuilderMetrics>,
301301
/// The end of builder transaction type
302302
builder_tx: BuilderTx,
303-
/// Rate limiting based on gas. This is an optional feature.
304-
address_gas_limiter: AddressGasLimiter,
303+
/// Per-source gas rate limiters. `None` when the limiter is disabled.
304+
gas_limiters: Option<GasLimiters>,
305305
/// Tokio task metrics for monitoring spawned tasks
306306
task_metrics: Arc<FlashblocksTaskMetrics>,
307307
/// Task executor used to offload blocking work.
@@ -339,7 +339,7 @@ impl<Pool, Client, BuilderTx> OpPayloadBuilder<Pool, Client, BuilderTx> {
339339
task_metrics: Arc<FlashblocksTaskMetrics>,
340340
executor: Runtime,
341341
) -> Self {
342-
let address_gas_limiter = AddressGasLimiter::new(config.gas_limiter_config.clone());
342+
let gas_limiters = GasLimiters::from_args(&config.gas_limiter_config);
343343
Self {
344344
inner: Arc::new(OpPayloadBuilderInner {
345345
evm_config,
@@ -351,7 +351,7 @@ impl<Pool, Client, BuilderTx> OpPayloadBuilder<Pool, Client, BuilderTx> {
351351
config,
352352
metrics,
353353
builder_tx,
354-
address_gas_limiter,
354+
gas_limiters,
355355
task_metrics,
356356
executor,
357357
}),
@@ -427,7 +427,7 @@ where
427427
metrics: self.metrics.clone(),
428428
max_gas_per_txn: self.config.max_gas_per_txn,
429429
max_uncompressed_block_size: self.config.max_uncompressed_block_size,
430-
address_gas_limiter: self.address_gas_limiter.clone(),
430+
gas_limiters: self.gas_limiters.clone(),
431431
backrun_ctx,
432432
exclude_reverts_between_flashblocks: self.config.exclude_reverts_between_flashblocks,
433433
enable_tx_tracking_debug_logs: self.config.enable_tx_tracking_debug_logs,
@@ -476,7 +476,9 @@ where
476476
ctx.enable_incremental_state_root,
477477
);
478478

479-
self.address_gas_limiter.refresh(ctx.block_number());
479+
if let Some(limiters) = &self.gas_limiters {
480+
limiters.refresh(ctx.block_number());
481+
}
480482

481483
// Phase 1: Build the fallback block.
482484
let fallback_span = if span.is_none() {

crates/op-rbuilder/src/builder/syncer_ctx.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ use crate::{
22
backrun_bundle::{BackrunBundleArgs, BackrunBundleGlobalPool, BackrunBundlesPayloadCtx},
33
builder::{BuilderConfig, OpPayloadBuilderCtx},
44
evm::OpBlockEvmFactory,
5-
gas_limiter::{AddressGasLimiter, args::GasLimiterArgs},
65
metrics::OpRBuilderMetrics,
76
traits::ClientBounds,
87
};
@@ -117,7 +116,7 @@ impl OpPayloadSyncerCtx {
117116
metrics: self.metrics,
118117
max_gas_per_txn: self.max_gas_per_txn,
119118
max_uncompressed_block_size: self.max_uncompressed_block_size,
120-
address_gas_limiter: AddressGasLimiter::new(GasLimiterArgs::default()),
119+
gas_limiters: None,
121120
backrun_ctx,
122121
exclude_reverts_between_flashblocks: self.exclude_reverts_between_flashblocks,
123122
enable_tx_tracking_debug_logs: self.enable_tx_tracking_debug_logs,

crates/op-rbuilder/src/gas_limiter/args.rs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use clap::Args;
22

33
#[derive(Debug, Clone, Default, PartialEq, Eq, Args)]
44
pub struct GasLimiterArgs {
5-
/// Enable address-based gas rate limiting
5+
/// Enable address-based gas rate limiting for public-mempool txs.
66
#[arg(long = "gas-limiter.enabled", env)]
77
pub gas_limiter_enabled: bool,
88

@@ -25,4 +25,21 @@ pub struct GasLimiterArgs {
2525
/// How many blocks to wait before cleaning up stale buckets for addresses.
2626
#[arg(long = "gas-limiter.cleanup-interval", env, default_value = "100")]
2727
pub cleanup_interval: u64,
28+
29+
/// Per-address cap for the bundle limiter. Defaults to 10 million gas.
30+
#[arg(
31+
long = "gas-limiter.bundle-max-gas-per-address",
32+
env,
33+
default_value = "10000000"
34+
)]
35+
pub bundle_max_gas_per_address: u64,
36+
37+
/// Refill rate per block for the bundle limiter. Defaults to 1 million
38+
/// gas per block.
39+
#[arg(
40+
long = "gas-limiter.bundle-refill-rate-per-block",
41+
env,
42+
default_value = "1000000"
43+
)]
44+
pub bundle_refill_rate_per_block: u64,
2845
}

0 commit comments

Comments
 (0)