Skip to content

Commit d244cd0

Browse files
authored
feat(forge): loosen tx gas limit restrictions ahead of Osaka + make enforceable w/ --enable-tx-gas-limit (#11427)
1 parent 81d50d9 commit d244cd0

File tree

12 files changed

+176
-19
lines changed

12 files changed

+176
-19
lines changed

crates/anvil/src/config.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,8 @@ pub struct NodeConfig {
9898
pub gas_limit: Option<u64>,
9999
/// If set to `true`, disables the block gas limit
100100
pub disable_block_gas_limit: bool,
101+
/// If set to `true`, enables the tx gas limit as imposed by Osaka (EIP-7825)
102+
pub enable_tx_gas_limit: bool,
101103
/// Default gas price for all txs
102104
pub gas_price: Option<u128>,
103105
/// Default base fee
@@ -443,6 +445,7 @@ impl Default for NodeConfig {
443445
chain_id: None,
444446
gas_limit: None,
445447
disable_block_gas_limit: false,
448+
enable_tx_gas_limit: false,
446449
gas_price: None,
447450
hardfork: None,
448451
signer_accounts: genesis_accounts.clone(),
@@ -622,6 +625,15 @@ impl NodeConfig {
622625
self
623626
}
624627

628+
/// Enable tx gas limit check
629+
///
630+
/// If set to `true`, enables the tx gas limit as imposed by Osaka (EIP-7825)
631+
#[must_use]
632+
pub fn enable_tx_gas_limit(mut self, enable_tx_gas_limit: bool) -> Self {
633+
self.enable_tx_gas_limit = enable_tx_gas_limit;
634+
self
635+
}
636+
625637
/// Sets the gas price
626638
#[must_use]
627639
pub fn with_gas_price(mut self, gas_price: Option<u128>) -> Self {
@@ -1068,6 +1080,10 @@ impl NodeConfig {
10681080
cfg.disable_eip3607 = true;
10691081
cfg.disable_block_gas_limit = self.disable_block_gas_limit;
10701082

1083+
if !self.enable_tx_gas_limit {
1084+
cfg.tx_gas_limit_cap = Some(u64::MAX);
1085+
}
1086+
10711087
if let Some(value) = self.memory_limit {
10721088
cfg.memory_limit = value;
10731089
}

crates/anvil/src/eth/backend/executor.rs

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ use foundry_evm_core::{either_evm::EitherEvm, precompiles::EC_RECOVER};
4040
use op_revm::{L1BlockInfo, OpContext, precompiles::OpPrecompiles};
4141
use revm::{
4242
Database, DatabaseRef, Inspector, Journal,
43-
context::{Block as RevmBlock, BlockEnv, CfgEnv, Evm as RevmEvm, JournalTr, LocalContext},
43+
context::{Block as RevmBlock, BlockEnv, Cfg, CfgEnv, Evm as RevmEvm, JournalTr, LocalContext},
4444
context_interface::result::{EVMError, ExecutionResult, Output},
4545
database::WrapDatabaseRef,
4646
handler::{EthPrecompiles, instructions::EthInstructions},
@@ -174,14 +174,18 @@ impl<DB: Db + ?Sized, V: TransactionValidator> TransactionExecutor<'_, DB, V> {
174174
included.push(tx.transaction.clone());
175175
tx
176176
}
177-
TransactionExecutionOutcome::Exhausted(tx) => {
177+
TransactionExecutionOutcome::BlockGasExhausted(tx) => {
178178
trace!(target: "backend", tx_gas_limit = %tx.pending_transaction.transaction.gas_limit(), ?tx, "block gas limit exhausting, skipping transaction");
179179
continue;
180180
}
181181
TransactionExecutionOutcome::BlobGasExhausted(tx) => {
182182
trace!(target: "backend", blob_gas = %tx.pending_transaction.transaction.blob_gas().unwrap_or_default(), ?tx, "block blob gas limit exhausting, skipping transaction");
183183
continue;
184184
}
185+
TransactionExecutionOutcome::TransactionGasExhausted(tx) => {
186+
trace!(target: "backend", tx_gas_limit = %tx.pending_transaction.transaction.gas_limit(), ?tx, "transaction gas limit exhausting, skipping transaction");
187+
continue;
188+
}
185189
TransactionExecutionOutcome::Invalid(tx, _) => {
186190
trace!(target: "backend", ?tx, "skipping invalid transaction");
187191
invalid.push(tx);
@@ -285,10 +289,12 @@ pub enum TransactionExecutionOutcome {
285289
Executed(ExecutedTransaction),
286290
/// Invalid transaction not executed
287291
Invalid(Arc<PoolTransaction>, InvalidTransactionError),
288-
/// Execution skipped because could exceed gas limit
289-
Exhausted(Arc<PoolTransaction>),
292+
/// Execution skipped because could exceed block gas limit
293+
BlockGasExhausted(Arc<PoolTransaction>),
290294
/// Execution skipped because it exceeded the blob gas limit
291295
BlobGasExhausted(Arc<PoolTransaction>),
296+
/// Execution skipped because it exceeded the transaction gas limit
297+
TransactionGasExhausted(Arc<PoolTransaction>),
292298
/// When an error occurred during execution
293299
DatabaseError(Arc<PoolTransaction>, DatabaseError),
294300
}
@@ -306,10 +312,19 @@ impl<DB: Db + ?Sized, V: TransactionValidator> Iterator for &mut TransactionExec
306312
let env = self.env_for(&transaction.pending_transaction);
307313

308314
// check that we comply with the block's gas limit, if not disabled
309-
let max_gas = self.gas_used.saturating_add(env.tx.base.gas_limit);
310-
if !env.evm_env.cfg_env.disable_block_gas_limit && max_gas > env.evm_env.block_env.gas_limit
315+
let max_block_gas = self.gas_used.saturating_add(env.tx.base.gas_limit);
316+
if !env.evm_env.cfg_env.disable_block_gas_limit
317+
&& max_block_gas > env.evm_env.block_env.gas_limit
318+
{
319+
return Some(TransactionExecutionOutcome::BlockGasExhausted(transaction));
320+
}
321+
322+
// check that we comply with the transaction's gas limit as imposed by Osaka (EIP-7825)
323+
if env.evm_env.cfg_env.tx_gas_limit_cap.is_none()
324+
&& transaction.pending_transaction.transaction.gas_limit()
325+
> env.evm_env.cfg_env().tx_gas_limit_cap()
311326
{
312-
return Some(TransactionExecutionOutcome::Exhausted(transaction));
327+
return Some(TransactionExecutionOutcome::TransactionGasExhausted(transaction));
313328
}
314329

315330
// check that we comply with the block's blob gas limit

crates/anvil/src/eth/backend/mem/mod.rs

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ use op_revm::{
108108
use parking_lot::{Mutex, RwLock};
109109
use revm::{
110110
DatabaseCommit, Inspector,
111-
context::{Block as RevmBlock, BlockEnv, TxEnv},
111+
context::{Block as RevmBlock, BlockEnv, Cfg, TxEnv},
112112
context_interface::{
113113
block::BlobExcessGasAndPrice,
114114
result::{ExecutionResult, Output, ResultAndState},
@@ -1534,6 +1534,7 @@ impl Backend {
15341534
///
15351535
/// - `disable_eip3607` is set to `true`
15361536
/// - `disable_base_fee` is set to `true`
1537+
/// - `tx_gas_limit_cap` is set to `Some(u64::MAX)` indicating no gas limit cap
15371538
/// - `nonce` check is skipped if `request.nonce` is None
15381539
fn build_call_env(
15391540
&self,
@@ -1576,6 +1577,7 @@ impl Backend {
15761577
// we want to disable this in eth_call, since this is common practice used by other node
15771578
// impls and providers <https://github.com/foundry-rs/foundry/issues/4388>
15781579
env.evm_env.cfg_env.disable_block_gas_limit = true;
1580+
env.evm_env.cfg_env.tx_gas_limit_cap = Some(u64::MAX);
15791581

15801582
// The basefee should be ignored for calls against state for
15811583
// - eth_call
@@ -3422,7 +3424,7 @@ impl TransactionValidator for Backend {
34223424
return Err(InvalidTransactionError::GasTooLow);
34233425
}
34243426

3425-
// Check gas limit against block gas limit, if block gas limit is set.
3427+
// Check tx gas limit against block gas limit, if block gas limit is set.
34263428
if !env.evm_env.cfg_env.disable_block_gas_limit
34273429
&& tx.gas_limit() > env.evm_env.block_env.gas_limit
34283430
{
@@ -3432,7 +3434,17 @@ impl TransactionValidator for Backend {
34323434
}));
34333435
}
34343436

3435-
// EIP-1559 fee validation (London hard fork and later)
3437+
// Check tx gas limit against tx gas limit cap (Osaka hard fork and later).
3438+
if env.evm_env.cfg_env.tx_gas_limit_cap.is_none()
3439+
&& tx.gas_limit() > env.evm_env.cfg_env().tx_gas_limit_cap()
3440+
{
3441+
warn!(target: "backend", "[{:?}] gas too high", tx.hash());
3442+
return Err(InvalidTransactionError::GasTooHigh(ErrDetail {
3443+
detail: String::from("tx.gas_limit > env.cfg.tx_gas_limit_cap"),
3444+
}));
3445+
}
3446+
3447+
// EIP-1559 fee validation (London hard fork and later).
34363448
if env.evm_env.cfg_env.spec >= SpecId::LONDON {
34373449
if tx.gas_price() < env.evm_env.block_env.basefee.into() && !is_deposit_tx {
34383450
warn!(target: "backend", "max fee per gas={}, too low, block basefee={}",tx.gas_price(), env.evm_env.block_env.basefee);

crates/anvil/tests/it/transaction.rs

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use alloy_sol_types::SolValue;
1616
use anvil::{NodeConfig, spawn};
1717
use eyre::Ok;
1818
use futures::{FutureExt, StreamExt, future::join_all};
19+
use revm::primitives::eip7825::TX_GAS_LIMIT_CAP;
1920
use std::{str::FromStr, time::Duration};
2021
use tokio::time::timeout;
2122

@@ -1296,7 +1297,7 @@ async fn can_mine_multiple_in_block() {
12961297

12971298
// ensures that the gas estimate is running on pending block by default
12981299
#[tokio::test(flavor = "multi_thread")]
1299-
async fn estimates_gas_prague() {
1300+
async fn can_estimate_gas_prague() {
13001301
let (api, _handle) =
13011302
spawn(NodeConfig::test().with_hardfork(Some(EthereumHardfork::Prague.into()))).await;
13021303

@@ -1308,3 +1309,75 @@ async fn estimates_gas_prague() {
13081309
.with_to(address!("0x70997970c51812dc3a010c7d01b50e0d17dc79c8"));
13091310
api.estimate_gas(WithOtherFields::new(req), None, EvmOverrides::default()).await.unwrap();
13101311
}
1312+
1313+
#[tokio::test(flavor = "multi_thread")]
1314+
async fn can_send_tx_osaka_valid_with_limit_enabled() {
1315+
let (_api, handle) = spawn(
1316+
NodeConfig::test()
1317+
.enable_tx_gas_limit(true)
1318+
.with_hardfork(Some(EthereumHardfork::Osaka.into())),
1319+
)
1320+
.await;
1321+
let provider = handle.http_provider();
1322+
let wallet = handle.dev_wallets().next().unwrap();
1323+
let sender = wallet.address();
1324+
let recipient = Address::random();
1325+
1326+
let base_tx = TransactionRequest::default().from(sender).to(recipient).value(U256::from(1e18));
1327+
1328+
// gas limit below the cap is accepted
1329+
let tx = base_tx.clone().gas_limit(TX_GAS_LIMIT_CAP - 1);
1330+
let tx = WithOtherFields::new(tx);
1331+
let pending_tx = provider.send_transaction(tx).await.unwrap();
1332+
let tx_receipt = pending_tx.get_receipt().await.unwrap();
1333+
assert!(tx_receipt.inner.inner.is_success());
1334+
1335+
// gas limit at the cap is accepted
1336+
let tx = base_tx.clone().gas_limit(TX_GAS_LIMIT_CAP);
1337+
let tx = WithOtherFields::new(tx);
1338+
let pending_tx = provider.send_transaction(tx).await.unwrap();
1339+
let tx_receipt = pending_tx.get_receipt().await.unwrap();
1340+
assert!(tx_receipt.inner.inner.is_success());
1341+
1342+
// gas limit above the cap is rejected
1343+
let tx = base_tx.clone().gas_limit(TX_GAS_LIMIT_CAP + 1);
1344+
let tx = WithOtherFields::new(tx);
1345+
let err = provider.send_transaction(tx).await.unwrap_err().to_string();
1346+
assert!(
1347+
err.contains("intrinsic gas too high -- tx.gas_limit > env.cfg.tx_gas_limit_cap"),
1348+
"{err}"
1349+
);
1350+
}
1351+
1352+
#[tokio::test(flavor = "multi_thread")]
1353+
async fn can_send_tx_osaka_valid_with_limit_disabled() {
1354+
let (_api, handle) =
1355+
spawn(NodeConfig::test().with_hardfork(Some(EthereumHardfork::Osaka.into()))).await;
1356+
let provider = handle.http_provider();
1357+
let wallet = handle.dev_wallets().next().unwrap();
1358+
let sender = wallet.address();
1359+
let recipient = Address::random();
1360+
1361+
let base_tx = TransactionRequest::default().from(sender).to(recipient).value(U256::from(1e18));
1362+
1363+
// gas limit below the cap is accepted
1364+
let tx = base_tx.clone().gas_limit(TX_GAS_LIMIT_CAP - 1);
1365+
let tx = WithOtherFields::new(tx);
1366+
let pending_tx = provider.send_transaction(tx).await.unwrap();
1367+
let tx_receipt = pending_tx.get_receipt().await.unwrap();
1368+
assert!(tx_receipt.inner.inner.is_success());
1369+
1370+
// gas limit at the cap is accepted
1371+
let tx = base_tx.clone().gas_limit(TX_GAS_LIMIT_CAP);
1372+
let tx = WithOtherFields::new(tx);
1373+
let pending_tx = provider.send_transaction(tx).await.unwrap();
1374+
let tx_receipt = pending_tx.get_receipt().await.unwrap();
1375+
assert!(tx_receipt.inner.inner.is_success());
1376+
1377+
// gas limit above the cap is accepted when the limit is disabled
1378+
let tx = base_tx.clone().gas_limit(TX_GAS_LIMIT_CAP + 1);
1379+
let tx = WithOtherFields::new(tx);
1380+
let pending_tx = provider.send_transaction(tx).await.unwrap();
1381+
let tx_receipt = pending_tx.get_receipt().await.unwrap();
1382+
assert!(tx_receipt.inner.inner.is_success());
1383+
}

crates/cast/src/cmd/call.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,7 @@ impl CallArgs {
264264

265265
// modify settings that usually set in eth_call
266266
env.evm_env.cfg_env.disable_block_gas_limit = true;
267+
env.evm_env.cfg_env.tx_gas_limit_cap = Some(u64::MAX);
267268
env.evm_env.block_env.gas_limit = u64::MAX;
268269

269270
// Apply the block overrides.

crates/cast/src/cmd/run.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,10 @@ pub struct RunArgs {
105105
/// Disable block gas limit check.
106106
#[arg(long)]
107107
pub disable_block_gas_limit: bool,
108+
109+
/// Enable the tx gas limit checks as imposed by Osaka (EIP-7825).
110+
#[arg(long)]
111+
pub enable_tx_gas_limit: bool,
108112
}
109113

110114
impl RunArgs {
@@ -162,6 +166,13 @@ impl RunArgs {
162166
let mut evm_version = self.evm_version;
163167

164168
env.evm_env.cfg_env.disable_block_gas_limit = self.disable_block_gas_limit;
169+
170+
// By default do not enforce transaction gas limits imposed by Osaka (EIP-7825).
171+
// Users can opt-in to enable these limits by setting `enable_tx_gas_limit` to true.
172+
if !self.enable_tx_gas_limit {
173+
env.evm_env.cfg_env.tx_gas_limit_cap = Some(u64::MAX);
174+
}
175+
165176
env.evm_env.cfg_env.limit_contract_code_size = None;
166177
env.evm_env.block_env.number = U256::from(tx_block_number);
167178

crates/common/src/evm.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,7 @@ pub struct EnvArgs {
252252
pub block_prevrandao: Option<B256>,
253253

254254
/// The block gas limit.
255-
#[arg(long, visible_alias = "gas-limit", value_name = "GAS_LIMIT")]
255+
#[arg(long, visible_aliases = &["block-gas-limit", "gas-limit"], value_name = "BLOCK_GAS_LIMIT")]
256256
#[serde(skip_serializing_if = "Option::is_none")]
257257
pub block_gas_limit: Option<u64>,
258258

@@ -265,9 +265,14 @@ pub struct EnvArgs {
265265
pub memory_limit: Option<u64>,
266266

267267
/// Whether to disable the block gas limit checks.
268-
#[arg(long, visible_alias = "no-gas-limit")]
268+
#[arg(long, visible_aliases = &["no-block-gas-limit", "no-gas-limit"])]
269269
#[serde(skip_serializing_if = "std::ops::Not::not")]
270270
pub disable_block_gas_limit: bool,
271+
272+
/// Whether to enable tx gas limit checks as imposed by Osaka (EIP-7825).
273+
#[arg(long, visible_alias = "tx-gas-limit")]
274+
#[serde(skip_serializing_if = "std::ops::Not::not")]
275+
pub enable_tx_gas_limit: bool,
271276
}
272277

273278
impl EvmArgs {

crates/config/src/lib.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -487,9 +487,12 @@ pub struct Config {
487487
/// Useful for more correct gas accounting and EVM behavior in general.
488488
pub isolate: bool,
489489

490-
/// Whether to disable the block gas limit.
490+
/// Whether to disable the block gas limit checks.
491491
pub disable_block_gas_limit: bool,
492492

493+
/// Whether to enable the tx gas limit checks as imposed by Osaka (EIP-7825).
494+
pub enable_tx_gas_limit: bool,
495+
493496
/// Address labels
494497
pub labels: AddressHashMap<String>,
495498

@@ -2382,6 +2385,7 @@ impl Default for Config {
23822385
block_prevrandao: Default::default(),
23832386
block_gas_limit: None,
23842387
disable_block_gas_limit: false,
2388+
enable_tx_gas_limit: false,
23852389
memory_limit: 1 << 27, // 2**27 = 128MiB = 134_217_728 bytes
23862390
eth_rpc_url: None,
23872391
eth_rpc_accept_invalid_certs: false,

crates/evm/core/src/fork/init.rs

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use revm::context::{BlockEnv, CfgEnv, TxEnv};
99

1010
/// Initializes a REVM block environment based on a forked
1111
/// ethereum provider.
12+
#[allow(clippy::too_many_arguments)]
1213
pub async fn environment<N: Network, P: Provider<N>>(
1314
provider: &P,
1415
memory_limit: u64,
@@ -17,6 +18,7 @@ pub async fn environment<N: Network, P: Provider<N>>(
1718
pin_block: Option<u64>,
1819
origin: Address,
1920
disable_block_gas_limit: bool,
21+
enable_tx_gas_limit: bool,
2022
) -> eyre::Result<(Env, N::BlockResponse)> {
2123
let block_number = if let Some(pin_block) = pin_block {
2224
pin_block
@@ -50,6 +52,7 @@ pub async fn environment<N: Network, P: Provider<N>>(
5052
override_chain_id.unwrap_or(rpc_chain_id),
5153
memory_limit,
5254
disable_block_gas_limit,
55+
enable_tx_gas_limit,
5356
);
5457

5558
let mut env = Env {
@@ -81,7 +84,12 @@ pub async fn environment<N: Network, P: Provider<N>>(
8184
}
8285

8386
/// Configures the environment for the given chain id and memory limit.
84-
pub fn configure_env(chain_id: u64, memory_limit: u64, disable_block_gas_limit: bool) -> CfgEnv {
87+
pub fn configure_env(
88+
chain_id: u64,
89+
memory_limit: u64,
90+
disable_block_gas_limit: bool,
91+
enable_tx_gas_limit: bool,
92+
) -> CfgEnv {
8593
let mut cfg = CfgEnv::default();
8694
cfg.chain_id = chain_id;
8795
cfg.memory_limit = memory_limit;
@@ -92,7 +100,10 @@ pub fn configure_env(chain_id: u64, memory_limit: u64, disable_block_gas_limit:
92100
cfg.disable_eip3607 = true;
93101
cfg.disable_block_gas_limit = disable_block_gas_limit;
94102
cfg.disable_nonce_check = true;
95-
// For Osaka EIP-7825
96-
cfg.tx_gas_limit_cap = Some(u64::MAX);
103+
// By default do not enforce transaction gas limits imposed by Osaka (EIP-7825).
104+
// Users can opt-in to enable these limits by setting `enable_tx_gas_limit` to true.
105+
if !enable_tx_gas_limit {
106+
cfg.tx_gas_limit_cap = Some(u64::MAX);
107+
}
97108
cfg
98109
}

0 commit comments

Comments
 (0)