Skip to content

Commit 81156bd

Browse files
committed
Merge branch 'master' of github.com:foundry-rs/foundry into rusowsky/cheat-script-vars
2 parents 457d10c + ab4375e commit 81156bd

File tree

23 files changed

+957
-493
lines changed

23 files changed

+957
-493
lines changed

Cargo.lock

Lines changed: 228 additions & 207 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -401,8 +401,8 @@ idna_adapter = "=1.1.0"
401401
# alloy-op-evm = { git = "https://github.com/alloy-rs/evm.git", rev = "7762adc" }
402402

403403
## revm
404-
# revm = { git = "https://github.com/bluealloy/revm.git", rev = "b5808253" }
405-
# op-revm = { git = "https://github.com/bluealloy/revm.git", rev = "b5808253" }
404+
revm = { git = "https://github.com/bluealloy/revm.git", rev = "d9cda3a" }
405+
op-revm = { git = "https://github.com/bluealloy/revm.git", rev = "d9cda3a" }
406406
# revm-inspectors = { git = "https://github.com/paradigmxyz/revm-inspectors.git", rev = "956bc98" }
407407

408408
## foundry

crates/anvil/core/src/eth/transaction/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -801,7 +801,7 @@ impl TypedTransaction {
801801
input: t.tx().tx().input.clone(),
802802
nonce: t.tx().tx().nonce,
803803
gas_limit: t.tx().tx().gas_limit,
804-
gas_price: Some(t.tx().tx().max_fee_per_blob_gas),
804+
gas_price: None,
805805
max_fee_per_gas: Some(t.tx().tx().max_fee_per_gas),
806806
max_priority_fee_per_gas: Some(t.tx().tx().max_priority_fee_per_gas),
807807
max_fee_per_blob_gas: Some(t.tx().tx().max_fee_per_blob_gas),
@@ -815,7 +815,7 @@ impl TypedTransaction {
815815
input: t.tx().input.clone(),
816816
nonce: t.tx().nonce,
817817
gas_limit: t.tx().gas_limit,
818-
gas_price: Some(t.tx().max_fee_per_gas),
818+
gas_price: None,
819819
max_fee_per_gas: Some(t.tx().max_fee_per_gas),
820820
max_priority_fee_per_gas: Some(t.tx().max_priority_fee_per_gas),
821821
max_fee_per_blob_gas: None,

crates/anvil/src/cmd.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,7 @@ impl NodeArgs {
280280
.with_optimism(self.evm.optimism)
281281
.with_odyssey(self.evm.odyssey)
282282
.with_disable_default_create2_deployer(self.evm.disable_default_create2_deployer)
283+
.with_disable_pool_balance_checks(self.evm.disable_pool_balance_checks)
283284
.with_slots_in_an_epoch(self.slots_in_an_epoch)
284285
.with_memory_limit(self.evm.memory_limit)
285286
.with_cache_path(self.cache_path))
@@ -592,6 +593,10 @@ pub struct AnvilEvmArgs {
592593
#[arg(long, visible_alias = "no-create2")]
593594
pub disable_default_create2_deployer: bool,
594595

596+
/// Disable pool balance checks
597+
#[arg(long)]
598+
pub disable_pool_balance_checks: bool,
599+
595600
/// The memory limit per EVM execution in bytes.
596601
#[arg(long)]
597602
pub memory_limit: Option<u64>,

crates/anvil/src/config.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,8 @@ pub struct NodeConfig {
184184
pub transaction_block_keeper: Option<usize>,
185185
/// Disable the default CREATE2 deployer
186186
pub disable_default_create2_deployer: bool,
187+
/// Disable pool balance checks
188+
pub disable_pool_balance_checks: bool,
187189
/// Enable Optimism deposit transaction
188190
pub enable_optimism: bool,
189191
/// Slots in an epoch
@@ -484,6 +486,7 @@ impl Default for NodeConfig {
484486
init_state: None,
485487
transaction_block_keeper: None,
486488
disable_default_create2_deployer: false,
489+
disable_pool_balance_checks: false,
487490
enable_optimism: false,
488491
slots_in_an_epoch: 32,
489492
memory_limit: None,
@@ -993,6 +996,13 @@ impl NodeConfig {
993996
self
994997
}
995998

999+
/// Sets whether to disable pool balance checks
1000+
#[must_use]
1001+
pub fn with_disable_pool_balance_checks(mut self, yes: bool) -> Self {
1002+
self.disable_pool_balance_checks = yes;
1003+
self
1004+
}
1005+
9961006
/// Injects precompiles to `anvil`'s EVM.
9971007
#[must_use]
9981008
pub fn with_precompile_factory(mut self, factory: impl PrecompileFactory + 'static) -> Self {

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

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ use revm::{
1919
interpreter::EthInterpreter,
2020
},
2121
};
22+
use revm_inspectors::transfer::TransferInspector;
2223
use std::sync::Arc;
2324

2425
/// The [`revm::Inspector`] used when transacting in the evm
@@ -28,6 +29,8 @@ pub struct AnvilInspector {
2829
pub tracer: Option<TracingInspector>,
2930
/// Collects all `console.sol` logs
3031
pub log_collector: Option<LogCollector>,
32+
/// Collects all internal ETH transfers as ERC20 transfer events.
33+
pub transfer: Option<TransferInspector>,
3134
}
3235

3336
impl AnvilInspector {
@@ -79,6 +82,12 @@ impl AnvilInspector {
7982
self
8083
}
8184

85+
/// Configures the `Tracer` [`revm::Inspector`] with a transfer event collector
86+
pub fn with_transfers(mut self) -> Self {
87+
self.transfer = Some(TransferInspector::new(false).with_logs(true));
88+
self
89+
}
90+
8291
/// Configures the `Tracer` [`revm::Inspector`] with a trace printer
8392
pub fn with_trace_printer(mut self) -> Self {
8493
self.tracer = Some(TracingInspector::new(TracingInspectorConfig::all().with_state_diffs()));
@@ -139,7 +148,7 @@ where
139148
fn call(&mut self, ecx: &mut CTX, inputs: &mut CallInputs) -> Option<CallOutcome> {
140149
call_inspectors!(
141150
#[ret]
142-
[&mut self.tracer, &mut self.log_collector],
151+
[&mut self.tracer, &mut self.log_collector, &mut self.transfer],
143152
|inspector| inspector.call(ecx, inputs).map(Some),
144153
);
145154
None
@@ -152,11 +161,11 @@ where
152161
}
153162

154163
fn create(&mut self, ecx: &mut CTX, inputs: &mut CreateInputs) -> Option<CreateOutcome> {
155-
if let Some(tracer) = &mut self.tracer
156-
&& let Some(out) = tracer.create(ecx, inputs)
157-
{
158-
return Some(out);
159-
}
164+
call_inspectors!(
165+
#[ret]
166+
[&mut self.tracer, &mut self.transfer],
167+
|inspector| inspector.create(ecx, inputs).map(Some),
168+
);
160169
None
161170
}
162171

@@ -168,9 +177,9 @@ where
168177

169178
#[inline]
170179
fn selfdestruct(&mut self, contract: Address, target: Address, value: U256) {
171-
if let Some(tracer) = &mut self.tracer {
172-
<TracingInspector as Inspector<CTX>>::selfdestruct(tracer, contract, target, value);
173-
}
180+
call_inspectors!([&mut self.tracer, &mut self.transfer], |inspector| {
181+
Inspector::<CTX, EthInterpreter>::selfdestruct(inspector, contract, target, value)
182+
});
174183
}
175184
}
176185

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

Lines changed: 85 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,6 @@ use revm::{
118118
primitives::{KECCAK_EMPTY, hardfork::SpecId},
119119
state::AccountInfo,
120120
};
121-
use revm_inspectors::transfer::TransferInspector;
122121
use std::{
123122
collections::BTreeMap,
124123
fmt::Debug,
@@ -247,6 +246,8 @@ pub struct Backend {
247246
// === wallet === //
248247
capabilities: Arc<RwLock<WalletCapabilities>>,
249248
executor_wallet: Arc<RwLock<Option<EthereumWallet>>>,
249+
/// Disable pool balance checks
250+
disable_pool_balance_checks: bool,
250251
}
251252

252253
impl Backend {
@@ -310,9 +311,9 @@ impl Backend {
310311
states = states.disk_path(cache_path);
311312
}
312313

313-
let (slots_in_an_epoch, precompile_factory) = {
314+
let (slots_in_an_epoch, precompile_factory, disable_pool_balance_checks) = {
314315
let cfg = node_config.read().await;
315-
(cfg.slots_in_an_epoch, cfg.precompile_factory.clone())
316+
(cfg.slots_in_an_epoch, cfg.precompile_factory.clone(), cfg.disable_pool_balance_checks)
316317
};
317318

318319
let (capabilities, executor_wallet) = if odyssey {
@@ -377,6 +378,7 @@ impl Backend {
377378
mining: Arc::new(tokio::sync::Mutex::new(())),
378379
capabilities: Arc::new(RwLock::new(capabilities)),
379380
executor_wallet: Arc::new(RwLock::new(executor_wallet)),
381+
disable_pool_balance_checks,
380382
};
381383

382384
if let Some(interval_block_time) = automine_block_time {
@@ -1693,11 +1695,13 @@ impl Backend {
16931695
env.evm_env.block_env.basefee = 0;
16941696
}
16951697

1698+
let mut inspector = self.build_inspector();
1699+
16961700
// transact
16971701
let ResultAndState { result, state } = if trace_transfers {
16981702
// prepare inspector to capture transfer inside the evm so they are
16991703
// recorded and included in logs
1700-
let mut inspector = TransferInspector::new(false).with_logs(true);
1704+
inspector = inspector.with_transfers();
17011705
let mut evm= self.new_evm_with_inspector_ref(
17021706
&cache_db as &dyn DatabaseRef,
17031707
&env,
@@ -1707,7 +1711,6 @@ impl Backend {
17071711
trace!(target: "backend", env=?env.evm_env, spec=?env.evm_env.spec_id(),"simulate evm env");
17081712
evm.transact(env.tx)?
17091713
} else {
1710-
let mut inspector = self.build_inspector();
17111714
let mut evm = self.new_evm_with_inspector_ref(
17121715
&cache_db as &dyn DatabaseRef,
17131716
&env,
@@ -1718,6 +1721,11 @@ impl Backend {
17181721
};
17191722
trace!(target: "backend", ?result, ?request, "simulate call");
17201723

1724+
inspector.print_logs();
1725+
if self.print_traces {
1726+
inspector.into_print_traces(self.call_trace_decoder.clone());
1727+
}
1728+
17211729
// commit the transaction
17221730
cache_db.commit(state);
17231731
gas_used += result.gas_used();
@@ -3336,22 +3344,7 @@ impl TransactionValidator for Backend {
33363344
}
33373345
}
33383346

3339-
if tx.gas_limit() < MIN_TRANSACTION_GAS as u64 {
3340-
warn!(target: "backend", "[{:?}] gas too low", tx.hash());
3341-
return Err(InvalidTransactionError::GasTooLow);
3342-
}
3343-
3344-
// Check gas limit, iff block gas limit is set.
3345-
if !env.evm_env.cfg_env.disable_block_gas_limit
3346-
&& tx.gas_limit() > env.evm_env.block_env.gas_limit
3347-
{
3348-
warn!(target: "backend", "[{:?}] gas too high", tx.hash());
3349-
return Err(InvalidTransactionError::GasTooHigh(ErrDetail {
3350-
detail: String::from("tx.gas_limit > env.block.gas_limit"),
3351-
}));
3352-
}
3353-
3354-
// check nonce
3347+
// Nonce validation
33553348
let is_deposit_tx =
33563349
matches!(&pending.transaction.transaction, TypedTransaction::Deposit(_));
33573350
let nonce = tx.nonce();
@@ -3360,39 +3353,15 @@ impl TransactionValidator for Backend {
33603353
return Err(InvalidTransactionError::NonceTooLow);
33613354
}
33623355

3363-
if env.evm_env.cfg_env.spec >= SpecId::LONDON {
3364-
if tx.gas_price() < env.evm_env.block_env.basefee.into() && !is_deposit_tx {
3365-
warn!(target: "backend", "max fee per gas={}, too low, block basefee={}",tx.gas_price(), env.evm_env.block_env.basefee);
3366-
return Err(InvalidTransactionError::FeeCapTooLow);
3367-
}
3368-
3369-
if let (Some(max_priority_fee_per_gas), Some(max_fee_per_gas)) =
3370-
(tx.essentials().max_priority_fee_per_gas, tx.essentials().max_fee_per_gas)
3371-
&& max_priority_fee_per_gas > max_fee_per_gas
3372-
{
3373-
warn!(target: "backend", "max priority fee per gas={}, too high, max fee per gas={}", max_priority_fee_per_gas, max_fee_per_gas);
3374-
return Err(InvalidTransactionError::TipAboveFeeCap);
3375-
}
3376-
}
3377-
3378-
// EIP-4844 Cancun hard fork validation steps
3356+
// EIP-4844 structural validation
33793357
if env.evm_env.cfg_env.spec >= SpecId::CANCUN && tx.transaction.is_eip4844() {
3380-
// Light checks first: see if the blob fee cap is too low.
3381-
if let Some(max_fee_per_blob_gas) = tx.essentials().max_fee_per_blob_gas
3382-
&& let Some(blob_gas_and_price) = &env.evm_env.block_env.blob_excess_gas_and_price
3383-
&& max_fee_per_blob_gas < blob_gas_and_price.blob_gasprice
3384-
{
3385-
warn!(target: "backend", "max fee per blob gas={}, too low, block blob gas price={}", max_fee_per_blob_gas, blob_gas_and_price.blob_gasprice);
3386-
return Err(InvalidTransactionError::BlobFeeCapTooLow);
3387-
}
3388-
33893358
// Heavy (blob validation) checks
3390-
let tx = match &tx.transaction {
3359+
let blob_tx = match &tx.transaction {
33913360
TypedTransaction::EIP4844(tx) => tx.tx(),
33923361
_ => unreachable!(),
33933362
};
33943363

3395-
let blob_count = tx.tx().blob_versioned_hashes.len();
3364+
let blob_count = blob_tx.tx().blob_versioned_hashes.len();
33963365

33973366
// Ensure there are blob hashes.
33983367
if blob_count == 0 {
@@ -3407,40 +3376,85 @@ impl TransactionValidator for Backend {
34073376

34083377
// Check for any blob validation errors if not impersonating.
34093378
if !self.skip_blob_validation(Some(*pending.sender()))
3410-
&& let Err(err) = tx.validate(EnvKzgSettings::default().get())
3379+
&& let Err(err) = blob_tx.validate(EnvKzgSettings::default().get())
34113380
{
34123381
return Err(InvalidTransactionError::BlobTransactionValidationError(err));
34133382
}
34143383
}
34153384

3416-
let max_cost = tx.max_cost();
3417-
let value = tx.value();
3385+
// Balance and fee related checks
3386+
if !self.disable_pool_balance_checks {
3387+
// Gas limit validation
3388+
if tx.gas_limit() < MIN_TRANSACTION_GAS as u64 {
3389+
warn!(target: "backend", "[{:?}] gas too low", tx.hash());
3390+
return Err(InvalidTransactionError::GasTooLow);
3391+
}
3392+
3393+
// Check gas limit against block gas limit, if block gas limit is set.
3394+
if !env.evm_env.cfg_env.disable_block_gas_limit
3395+
&& tx.gas_limit() > env.evm_env.block_env.gas_limit
3396+
{
3397+
warn!(target: "backend", "[{:?}] gas too high", tx.hash());
3398+
return Err(InvalidTransactionError::GasTooHigh(ErrDetail {
3399+
detail: String::from("tx.gas_limit > env.block.gas_limit"),
3400+
}));
3401+
}
3402+
3403+
// EIP-1559 fee validation (London hard fork and later)
3404+
if env.evm_env.cfg_env.spec >= SpecId::LONDON {
3405+
if tx.gas_price() < env.evm_env.block_env.basefee.into() && !is_deposit_tx {
3406+
warn!(target: "backend", "max fee per gas={}, too low, block basefee={}",tx.gas_price(), env.evm_env.block_env.basefee);
3407+
return Err(InvalidTransactionError::FeeCapTooLow);
3408+
}
34183409

3419-
match &tx.transaction {
3420-
TypedTransaction::Deposit(deposit_tx) => {
3421-
// Deposit transactions
3422-
// https://specs.optimism.io/protocol/deposits.html#execution
3423-
// 1. no gas cost check required since already have prepaid gas from L1
3424-
// 2. increment account balance by deposited amount before checking for sufficient
3425-
// funds `tx.value <= existing account value + deposited value`
3426-
if value > account.balance + U256::from(deposit_tx.mint) {
3427-
warn!(target: "backend", "[{:?}] insufficient balance={}, required={} account={:?}", tx.hash(), account.balance + U256::from(deposit_tx.mint), value, *pending.sender());
3428-
return Err(InvalidTransactionError::InsufficientFunds);
3410+
if let (Some(max_priority_fee_per_gas), Some(max_fee_per_gas)) =
3411+
(tx.essentials().max_priority_fee_per_gas, tx.essentials().max_fee_per_gas)
3412+
&& max_priority_fee_per_gas > max_fee_per_gas
3413+
{
3414+
warn!(target: "backend", "max priority fee per gas={}, too high, max fee per gas={}", max_priority_fee_per_gas, max_fee_per_gas);
3415+
return Err(InvalidTransactionError::TipAboveFeeCap);
34293416
}
34303417
}
3431-
_ => {
3432-
// check sufficient funds: `gas * price + value`
3433-
let req_funds = max_cost.checked_add(value.saturating_to()).ok_or_else(|| {
3434-
warn!(target: "backend", "[{:?}] cost too high", tx.hash());
3435-
InvalidTransactionError::InsufficientFunds
3436-
})?;
3437-
if account.balance < U256::from(req_funds) {
3438-
warn!(target: "backend", "[{:?}] insufficient allowance={}, required={} account={:?}", tx.hash(), account.balance, req_funds, *pending.sender());
3439-
return Err(InvalidTransactionError::InsufficientFunds);
3418+
3419+
// EIP-4844 blob fee validation
3420+
if env.evm_env.cfg_env.spec >= SpecId::CANCUN
3421+
&& tx.transaction.is_eip4844()
3422+
&& let Some(max_fee_per_blob_gas) = tx.essentials().max_fee_per_blob_gas
3423+
&& let Some(blob_gas_and_price) = &env.evm_env.block_env.blob_excess_gas_and_price
3424+
&& max_fee_per_blob_gas < blob_gas_and_price.blob_gasprice
3425+
{
3426+
warn!(target: "backend", "max fee per blob gas={}, too low, block blob gas price={}", max_fee_per_blob_gas, blob_gas_and_price.blob_gasprice);
3427+
return Err(InvalidTransactionError::BlobFeeCapTooLow);
3428+
}
3429+
3430+
let max_cost = tx.max_cost();
3431+
let value = tx.value();
3432+
match &tx.transaction {
3433+
TypedTransaction::Deposit(deposit_tx) => {
3434+
// Deposit transactions
3435+
// https://specs.optimism.io/protocol/deposits.html#execution
3436+
// 1. no gas cost check required since already have prepaid gas from L1
3437+
// 2. increment account balance by deposited amount before checking for
3438+
// sufficient funds `tx.value <= existing account value + deposited value`
3439+
if value > account.balance + U256::from(deposit_tx.mint) {
3440+
warn!(target: "backend", "[{:?}] insufficient balance={}, required={} account={:?}", tx.hash(), account.balance + U256::from(deposit_tx.mint), value, *pending.sender());
3441+
return Err(InvalidTransactionError::InsufficientFunds);
3442+
}
3443+
}
3444+
_ => {
3445+
// check sufficient funds: `gas * price + value`
3446+
let req_funds =
3447+
max_cost.checked_add(value.saturating_to()).ok_or_else(|| {
3448+
warn!(target: "backend", "[{:?}] cost too high", tx.hash());
3449+
InvalidTransactionError::InsufficientFunds
3450+
})?;
3451+
if account.balance < U256::from(req_funds) {
3452+
warn!(target: "backend", "[{:?}] insufficient allowance={}, required={} account={:?}", tx.hash(), account.balance, req_funds, *pending.sender());
3453+
return Err(InvalidTransactionError::InsufficientFunds);
3454+
}
34403455
}
34413456
}
34423457
}
3443-
34443458
Ok(())
34453459
}
34463460

0 commit comments

Comments
 (0)