From fb9988cc12af02a36d6b1cf22cc215780775ed9a Mon Sep 17 00:00:00 2001 From: Cody Wang Date: Mon, 22 Dec 2025 15:04:57 -0500 Subject: [PATCH 1/3] set max bundle tx limit and gas limit --- crates/ingress-rpc/src/lib.rs | 8 ++++++ crates/ingress-rpc/src/service.rs | 48 ++++++++++++++++++++++++------- 2 files changed, 45 insertions(+), 11 deletions(-) diff --git a/crates/ingress-rpc/src/lib.rs b/crates/ingress-rpc/src/lib.rs index 54f57a2..87c2dc5 100644 --- a/crates/ingress-rpc/src/lib.rs +++ b/crates/ingress-rpc/src/lib.rs @@ -193,6 +193,14 @@ pub struct Config { #[arg(long, env = "TIPS_INGRESS_BACKRUN_ENABLED", default_value = "false")] pub backrun_enabled: bool, + /// Maximum number of transactions allowed in a backrun bundle (including target tx) + #[arg(long, env = "MAX_BACKRUN_TXS", default_value = "5")] + pub max_backrun_txs: usize, + + /// Maximum total gas limit for all transactions in a backrun bundle + #[arg(long, env = "MAX_BACKRUN_GAS_LIMIT", default_value = "5000000")] + pub max_backrun_gas_limit: u64, + /// URL of third-party RPC endpoint to forward raw transactions to (enables forwarding if set) #[arg(long, env = "TIPS_INGRESS_RAW_TX_FORWARD_RPC")] pub raw_tx_forward_rpc: Option, diff --git a/crates/ingress-rpc/src/service.rs b/crates/ingress-rpc/src/service.rs index 7b2ad79..d9f5648 100644 --- a/crates/ingress-rpc/src/service.rs +++ b/crates/ingress-rpc/src/service.rs @@ -82,6 +82,8 @@ pub struct IngressService { builder_tx: broadcast::Sender, backrun_enabled: bool, builder_backrun_tx: broadcast::Sender, + max_backrun_txs: usize, + max_backrun_gas_limit: u64, } impl IngressService { @@ -127,6 +129,8 @@ impl IngressService { builder_tx, backrun_enabled: config.backrun_enabled, builder_backrun_tx, + max_backrun_txs: config.max_backrun_txs, + max_backrun_gas_limit: config.max_backrun_gas_limit, } } } @@ -135,10 +139,6 @@ impl IngressService { impl IngressApiServer for IngressService { async fn send_backrun_bundle(&self, bundle: Bundle) -> RpcResult { if !self.backrun_enabled { - info!( - message = "Backrun bundle submission is disabled", - backrun_enabled = self.backrun_enabled - ); return Err( EthApiError::InvalidParams("Backrun bundle submission is disabled".into()) .into_rpc_err(), @@ -149,16 +149,40 @@ impl IngressApiServer for Ingre let (accepted_bundle, bundle_hash) = self.validate_parse_and_meter_bundle(&bundle, false).await?; - self.metrics.backrun_bundles_received_total.increment(1); + if accepted_bundle.txs.len() < 2 { + return Err(EthApiError::InvalidParams( + "Backrun bundle must have at least 2 transactions (target + backrun)".into(), + ) + .into_rpc_err()); + } - if let Err(e) = self.builder_backrun_tx.send(accepted_bundle.clone()) { - warn!( - message = "Failed to send backrun bundle to builders", - bundle_hash = %bundle_hash, - error = %e - ); + if accepted_bundle.txs.len() > self.max_backrun_txs { + return Err(EthApiError::InvalidParams(format!( + "Backrun bundle exceeds max transaction count: {} > {}", + accepted_bundle.txs.len(), + self.max_backrun_txs + )) + .into_rpc_err()); } + let total_gas_limit: u64 = accepted_bundle.txs.iter().map(|tx| tx.gas_limit()).sum(); + if total_gas_limit > self.max_backrun_gas_limit { + return Err(EthApiError::InvalidParams(format!( + "Backrun bundle exceeds max gas limit: {} > {}", + total_gas_limit, self.max_backrun_gas_limit + )) + .into_rpc_err()); + } + + self.metrics.backrun_bundles_received_total.increment(1); + + self.builder_backrun_tx + .send(accepted_bundle.clone()) + .map_err(|e| { + EthApiError::InvalidParams(format!("Failed to send backrun bundle: {e}")) + .into_rpc_err() + })?; + self.send_audit_event(&accepted_bundle, bundle_hash); self.metrics @@ -578,6 +602,8 @@ mod tests { raw_tx_forward_rpc: None, chain_id: 11, user_operation_topic: String::new(), + max_backrun_txs: 5, + max_backrun_gas_limit: 5000000, } } From 2bdb83ffb391ea72741bfedbfb05de3c481bf90a Mon Sep 17 00:00:00 2001 From: Cody Wang Date: Mon, 22 Dec 2025 16:09:56 -0500 Subject: [PATCH 2/3] use helper --- crates/ingress-rpc/src/service.rs | 78 ++++++++++++++++++++++--------- 1 file changed, 55 insertions(+), 23 deletions(-) diff --git a/crates/ingress-rpc/src/service.rs b/crates/ingress-rpc/src/service.rs index d9f5648..fa2430c 100644 --- a/crates/ingress-rpc/src/service.rs +++ b/crates/ingress-rpc/src/service.rs @@ -135,6 +135,32 @@ impl IngressService { } } +fn validate_backrun_bundle_limits( + txs_count: usize, + total_gas_limit: u64, + max_backrun_txs: usize, + max_backrun_gas_limit: u64, +) -> Result<(), String> { + if txs_count < 2 { + return Err( + "Backrun bundle must have at least 2 transactions (target + backrun)".to_string(), + ); + } + if txs_count > max_backrun_txs { + return Err(format!( + "Backrun bundle exceeds max transaction count: {} > {}", + txs_count, max_backrun_txs + )); + } + if total_gas_limit > max_backrun_gas_limit { + return Err(format!( + "Backrun bundle exceeds max gas limit: {} > {}", + total_gas_limit, max_backrun_gas_limit + )); + } + Ok(()) +} + #[async_trait] impl IngressApiServer for IngressService { async fn send_backrun_bundle(&self, bundle: Bundle) -> RpcResult { @@ -149,30 +175,14 @@ impl IngressApiServer for Ingre let (accepted_bundle, bundle_hash) = self.validate_parse_and_meter_bundle(&bundle, false).await?; - if accepted_bundle.txs.len() < 2 { - return Err(EthApiError::InvalidParams( - "Backrun bundle must have at least 2 transactions (target + backrun)".into(), - ) - .into_rpc_err()); - } - - if accepted_bundle.txs.len() > self.max_backrun_txs { - return Err(EthApiError::InvalidParams(format!( - "Backrun bundle exceeds max transaction count: {} > {}", - accepted_bundle.txs.len(), - self.max_backrun_txs - )) - .into_rpc_err()); - } - let total_gas_limit: u64 = accepted_bundle.txs.iter().map(|tx| tx.gas_limit()).sum(); - if total_gas_limit > self.max_backrun_gas_limit { - return Err(EthApiError::InvalidParams(format!( - "Backrun bundle exceeds max gas limit: {} > {}", - total_gas_limit, self.max_backrun_gas_limit - )) - .into_rpc_err()); - } + validate_backrun_bundle_limits( + accepted_bundle.txs.len(), + total_gas_limit, + self.max_backrun_txs, + self.max_backrun_gas_limit, + ) + .map_err(|e| EthApiError::InvalidParams(e).into_rpc_err())?; self.metrics.backrun_bundles_received_total.increment(1); @@ -880,4 +890,26 @@ mod tests { assert!(wrong_user_op_result.is_err()); } + + #[test] + fn test_validate_backrun_bundle_rejects_invalid() { + // Too few transactions (need at least 2: target + backrun) + let result = validate_backrun_bundle_limits(1, 21000, 5, 5000000); + assert!(result.is_err()); + assert!(result.unwrap_err().contains("at least 2 transactions")); + + // Exceeds max tx count + let result = validate_backrun_bundle_limits(6, 21000, 5, 5000000); + assert!(result.is_err()); + assert!( + result + .unwrap_err() + .contains("exceeds max transaction count") + ); + + // Exceeds max gas limit + let result = validate_backrun_bundle_limits(2, 6000000, 5, 5000000); + assert!(result.is_err()); + assert!(result.unwrap_err().contains("exceeds max gas limit")); + } } From 9b657a52df9d6710790d2aa57afea147c6502313 Mon Sep 17 00:00:00 2001 From: Cody Wang Date: Mon, 22 Dec 2025 17:04:19 -0500 Subject: [PATCH 3/3] fix lint --- crates/ingress-rpc/src/service.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/crates/ingress-rpc/src/service.rs b/crates/ingress-rpc/src/service.rs index fa2430c..56cd41a 100644 --- a/crates/ingress-rpc/src/service.rs +++ b/crates/ingress-rpc/src/service.rs @@ -148,14 +148,12 @@ fn validate_backrun_bundle_limits( } if txs_count > max_backrun_txs { return Err(format!( - "Backrun bundle exceeds max transaction count: {} > {}", - txs_count, max_backrun_txs + "Backrun bundle exceeds max transaction count: {txs_count} > {max_backrun_txs}", )); } if total_gas_limit > max_backrun_gas_limit { return Err(format!( - "Backrun bundle exceeds max gas limit: {} > {}", - total_gas_limit, max_backrun_gas_limit + "Backrun bundle exceeds max gas limit: {total_gas_limit} > {max_backrun_gas_limit}", )); } Ok(())