From eae0f2ad902c684667c9812a614f7e2b885118b9 Mon Sep 17 00:00:00 2001 From: Himess Date: Sat, 11 Apr 2026 15:36:34 +0300 Subject: [PATCH 1/2] fix: use checked arithmetic for cumulative gas accounting in payload builder Use checked_add and saturating_add for cumulative gas tracking to prevent potential u64 overflow, consistent with the defensive arithmetic pattern applied to reward_beneficiary in #21. --- crates/execution-payload/src/payload.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/execution-payload/src/payload.rs b/crates/execution-payload/src/payload.rs index 4975b53..8ed9fff 100644 --- a/crates/execution-payload/src/payload.rs +++ b/crates/execution-payload/src/payload.rs @@ -539,7 +539,7 @@ where let chain_spec = client.chain_spec(); info!(target: "payload_builder", id=%attributes.id, parent_header = ?parent_header.hash(), parent_number = parent_header.number, "(arc) building new payload"); - let mut cumulative_gas_used = 0; + let mut cumulative_gas_used: u64 = 0; let block_gas_limit: u64 = builder.evm_mut().block().gas_limit(); let base_fee = builder.evm_mut().block().basefee(); @@ -576,7 +576,7 @@ where } // ensure we still have capacity for this transaction - if cumulative_gas_used + pool_tx.gas_limit() > block_gas_limit { + if cumulative_gas_used.checked_add(pool_tx.gas_limit()).is_none_or(|total| total > block_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 @@ -657,7 +657,7 @@ where .effective_tip_per_gas(base_fee) .expect("fee is always valid; execution succeeded"); total_fees += U256::from(miner_fee) * U256::from(gas_used); - cumulative_gas_used += gas_used; + cumulative_gas_used = cumulative_gas_used.saturating_add(gas_used); } PayloadBuildMetrics::record_stage_tx_execution(loop_started.elapsed()); From 4755eddeed92526ac3651f6bd9cb6776cc530ad9 Mon Sep 17 00:00:00 2001 From: Himess Date: Mon, 13 Apr 2026 17:11:22 +0300 Subject: [PATCH 2/2] fix: replace saturating_add with checked_add + error propagation per review Per reviewer feedback, saturating_add silently clamps at u64::MAX which would corrupt cumulative_gas_used rather than fail the block build cleanly. Switch to checked_add with PayloadBuilderError propagation, matching the defensive pattern used at the capacity check on line 579. --- crates/execution-payload/src/payload.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/crates/execution-payload/src/payload.rs b/crates/execution-payload/src/payload.rs index 8ed9fff..ca1f11f 100644 --- a/crates/execution-payload/src/payload.rs +++ b/crates/execution-payload/src/payload.rs @@ -657,7 +657,13 @@ where .effective_tip_per_gas(base_fee) .expect("fee is always valid; execution succeeded"); total_fees += U256::from(miner_fee) * U256::from(gas_used); - cumulative_gas_used = cumulative_gas_used.saturating_add(gas_used); + cumulative_gas_used = cumulative_gas_used + .checked_add(gas_used) + .ok_or_else(|| { + PayloadBuilderError::other(eyre::eyre!( + "cumulative_gas_used overflow: {cumulative_gas_used} + {gas_used}" + )) + })?; } PayloadBuildMetrics::record_stage_tx_execution(loop_started.elapsed());