From 3f39a858c14e1d257ee9164754501ab1bde11f47 Mon Sep 17 00:00:00 2001 From: Uzair Rehman Date: Wed, 23 Sep 2026 14:17:56 +0500 Subject: [PATCH] fix(EL): payload builder no longer panics on cumulative-gas checked_add overflow Both cumulative-gas accounting sites in the payload builder used checked_add(...).expect("total gas shouldn't overflow"), which aborts the process if the addition overflows u64 instead of failing through the structured error path already used elsewhere in this function. - Pre-execution capacity check: an overflowing add can never fit in a bounded block, so treat it the same as "doesn't fit" (mark the tx invalid via ExceedsGasLimit, evict if permanently un-includable, continue) instead of panicking. - Post-execution cumulative update: return a PayloadBuilderError (wrapping a new CumulativeGasOverflowError) instead of panicking, so the build fails cleanly through the existing Result-based path. No hardfork gate; this is builder-local error handling only. Fixes #452 Co-Authored-By: Claude Sonnet 5 --- crates/execution-payload/src/builder.rs | 12 ++++++++++++ crates/execution-payload/src/payload.rs | 15 ++++++++------- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/crates/execution-payload/src/builder.rs b/crates/execution-payload/src/builder.rs index 8436f6d9..a5778fe9 100644 --- a/crates/execution-payload/src/builder.rs +++ b/crates/execution-payload/src/builder.rs @@ -31,3 +31,15 @@ impl Display for UnprocessableTransactionError { } impl std::error::Error for UnprocessableTransactionError {} + +/// Error type for when cumulative gas accounting overflows a `u64` while building a payload. +#[derive(Debug)] +pub struct CumulativeGasOverflowError; + +impl Display for CumulativeGasOverflowError { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "cumulative gas accounting overflowed u64 while building payload") + } +} + +impl std::error::Error for CumulativeGasOverflowError {} diff --git a/crates/execution-payload/src/payload.rs b/crates/execution-payload/src/payload.rs index e6fbc3de..256ca8e7 100644 --- a/crates/execution-payload/src/payload.rs +++ b/crates/execution-payload/src/payload.rs @@ -66,6 +66,7 @@ use std::{ }; use tracing::{debug, error, info, trace, warn}; +use crate::builder::CumulativeGasOverflowError; use crate::builder::UnprocessableTransactionError; use crate::metrics::PayloadBuildMetrics; use arc_execution_txpool::InvalidTxList; @@ -760,12 +761,12 @@ where break; } - // ensure we still have capacity for this transaction - if block_gas_limit - < cumulative_gas_used - .checked_add(pool_tx.gas_limit()) - .expect("total gas shouldn't overflow") - { + // ensure we still have capacity for this transaction. Treat an overflow of the + // addition the same as "doesn't fit": it can never fit in a bounded block anyway. + let fits_in_block = cumulative_gas_used + .checked_add(pool_tx.gas_limit()) + .is_some_and(|total| total <= block_gas_limit); + if !fits_in_block { // 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 @@ -873,7 +874,7 @@ where } cumulative_gas_used = cumulative_gas_used .checked_add(gas_used) - .expect("total gas shouldn't overflow"); + .ok_or_else(|| PayloadBuilderError::other(CumulativeGasOverflowError))?; } PayloadBuildMetrics::record_stage_tx_execution(loop_started.elapsed());