Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 16 additions & 9 deletions crates/anvil/src/eth/backend/mem/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,9 @@ use foundry_evm::{
TracingInspectorConfig,
},
utils::{
block_env_from_header, get_blob_base_fee_update_fraction,
get_blob_base_fee_update_fraction_by_spec_id, get_blob_params_by_spec_id,
apply_chain_specific_tx_replay_env_changes, block_env_from_header,
get_blob_base_fee_update_fraction, get_blob_base_fee_update_fraction_by_spec_id,
get_blob_params_by_spec_id,
},
};
use foundry_evm_networks::{NetworkConfigs, arbitrum};
Expand Down Expand Up @@ -3188,21 +3189,26 @@ where
// to ensure the timestamp is as close as possible to the actual execution.
evm_env.block_env.timestamp = U256::from(self.time.next_timestamp());

let spec_id = *evm_env.spec_id();
// Forced historical transactions bypass pool admission and are replayed while
// mining. Keep this exception local to the disposable mining environment.
let mut mining_evm_env = evm_env.clone();
apply_chain_specific_tx_replay_env_changes(&mut mining_evm_env);
Comment thread
stevencartavia marked this conversation as resolved.
Outdated

let spec_id = *mining_evm_env.spec_id();

let inspector_tx_config = self.inspector_tx_config();
let gas_config = self.pool_tx_gas_config(&evm_env);
let gas_config = self.pool_tx_gas_config(&mining_evm_env);

let (pool_result, block_result) = self.execute_with_block_executor(
&mut **db,
&evm_env,
&mining_evm_env,
Comment thread
stevencartavia marked this conversation as resolved.
best_hash,
spec_id,
&pool_transactions,
&gas_config,
&inspector_tx_config,
&|pending, account| {
self.validate_pool_transaction_for(pending, account, &evm_env)
self.validate_pool_transaction_for(pending, account, &mining_evm_env)
},
);

Expand All @@ -3212,7 +3218,7 @@ where

let state_root = db.maybe_state_root().unwrap_or_default();
let block_info = self.build_block_info(
&evm_env,
&mining_evm_env,
best_hash,
block_number,
state_root,
Expand Down Expand Up @@ -5953,8 +5959,9 @@ where
return Err(InvalidTransactionError::FeeCapTooLow);
}

if let (Some(max_priority_fee_per_gas), max_fee_per_gas) =
(tx.as_ref().max_priority_fee_per_gas(), tx.as_ref().max_fee_per_gas())
if !evm_env.cfg_env.disable_priority_fee_check
&& let (Some(max_priority_fee_per_gas), max_fee_per_gas) =
(tx.as_ref().max_priority_fee_per_gas(), tx.as_ref().max_fee_per_gas())
&& max_priority_fee_per_gas > max_fee_per_gas
{
debug!(target: "backend", "max priority fee per gas={}, too high, max fee per gas={}", max_priority_fee_per_gas, max_fee_per_gas);
Expand Down
54 changes: 54 additions & 0 deletions crates/anvil/tests/it/anvil_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use crate::{
fork::fork_config,
utils::http_provider_with_signer,
};
use alloy_chains::NamedChain;
use alloy_consensus::{SignableTransaction, TxEip1559};
use alloy_network::{EthereumWallet, TransactionBuilder, TxSignerSync};
use alloy_primitives::{Address, Bytes, TxKind, U256, address, b256, fixed_bytes};
Expand Down Expand Up @@ -877,6 +878,59 @@ async fn flaky_test_reorg() {
assert!(res.is_err());
}

#[tokio::test(flavor = "multi_thread")]
async fn can_replay_arbitrum_transaction_with_priority_fee_above_max_fee() {
let (api, handle) =
spawn(NodeConfig::test().with_chain_id(Some(NamedChain::Arbitrum as u64))).await;
let provider = handle.http_provider();
let accounts = handle.dev_wallets().collect::<Vec<_>>();
api.mine_one().await;

let recipient = accounts[1].address();
let value = U256::from(100);
let mut tx = TxEip1559 {
chain_id: api.chain_id(),
to: TxKind::Call(recipient),
value,
max_priority_fee_per_gas: 3_000_000_000,
max_fee_per_gas: 2_000_000_000,
gas_limit: 21_000,
..Default::default()
};
let signature = accounts[0].sign_transaction_sync(&mut tx).unwrap();
let tx = tx.into_signed(signature);
let mut encoded = vec![];
tx.eip2718_encode(&mut encoded);

let balance_before = provider.get_balance(recipient).await.unwrap();
api.anvil_reorg(ReorgOptions {
depth: 1,
tx_block_pairs: vec![(TransactionData::Raw(encoded.into()), 0)],
})
.await
.unwrap();
let balance_after = provider.get_balance(recipient).await.unwrap();

assert_eq!(balance_after, balance_before + value);

let mut tx = TxEip1559 {
chain_id: api.chain_id(),
nonce: 1,
to: TxKind::Call(recipient),
max_priority_fee_per_gas: 3_000_000_000,
max_fee_per_gas: 2_000_000_000,
gas_limit: 21_000,
..Default::default()
};
let signature = accounts[0].sign_transaction_sync(&mut tx).unwrap();
let tx = tx.into_signed(signature);
let mut encoded = vec![];
tx.eip2718_encode(&mut encoded);

let err = provider.send_raw_transaction(&encoded).await.unwrap_err();
assert!(err.to_string().contains("max priority fee per gas higher than max fee per gas"));
}

#[tokio::test(flavor = "multi_thread")]
async fn test_reorg_blockhash_opcode_consistency() {
let (api, handle) = spawn(NodeConfig::test()).await;
Expand Down
6 changes: 5 additions & 1 deletion crates/cast/src/cmd/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ use crate::{
call_frame_to_arena_with_root_address, is_method_not_found_error, is_missing_state_error,
},
traces::TraceKind,
utils::{apply_chain_and_block_specific_env_changes, block_env_from_header},
utils::{
apply_chain_and_block_specific_env_changes, apply_chain_specific_tx_replay_env_changes,
block_env_from_header,
},
};
use alloy_consensus::{BlockHeader, Transaction, transaction::SignerRecoverable};

Expand Down Expand Up @@ -365,6 +368,7 @@ impl RunArgs {
config.networks,
);
}
apply_chain_specific_tx_replay_env_changes(&mut evm_env);

let trace_requirements = TraceRequirements::none()
.with_calls(true)
Expand Down
1 change: 1 addition & 0 deletions crates/evm/core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ revm = { workspace = true, features = [
"optional_eip3607",
"optional_block_gas_limit",
"optional_no_base_fee",
"optional_priority_fee_check",
"arbitrary",
"c-kzg",
"blst",
Expand Down
6 changes: 4 additions & 2 deletions crates/evm/core/src/backend/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use crate::{
},
fork::{CreateFork, ForkId, MultiFork},
state_snapshot::StateSnapshots,
utils::get_blob_base_fee_update_fraction,
utils::{apply_chain_specific_tx_replay_env_changes, get_blob_base_fee_update_fraction},
};
use alloy_consensus::{BlockHeader, Typed2718};
use alloy_evm::{Evm, EvmEnv, EvmFactory};
Expand Down Expand Up @@ -935,7 +935,7 @@ impl<FEN: FoundryEvmNetwork> Backend<FEN> {
pub fn replay_until(
&mut self,
id: LocalForkId,
evm_env: EvmEnvFor<FEN>,
mut evm_env: EvmEnvFor<FEN>,
tx_hash: B256,
journaled_state: &mut JournaledState,
) -> eyre::Result<Option<AnyRpcTransaction>> {
Expand Down Expand Up @@ -969,6 +969,7 @@ impl<FEN: FoundryEvmNetwork> Backend<FEN> {

// Clone the fork's CacheDB once. The underlying SharedBackend is Arc-backed,
// so only the local cache layer is actually duplicated.
apply_chain_specific_tx_replay_env_changes(&mut evm_env);
Comment thread
stevencartavia marked this conversation as resolved.
Outdated
let chain_id = evm_env.cfg_env.chain_id;
let timestamp = evm_env.block_env.timestamp().saturating_to();
let replay_db = fork.db.clone();
Expand Down Expand Up @@ -1390,6 +1391,7 @@ impl<FEN: FoundryEvmNetwork> DatabaseExt<FEN::EvmFactory> for Backend<FEN> {
let (_fork_block, block) =
self.get_block_number_and_block_for_transaction(id, transaction)?;
update_env_block(&mut evm_env, block.header());
apply_chain_specific_tx_replay_env_changes(&mut evm_env);

let fork = self.inner.get_fork_by_id_mut(id)?;
commit_transaction::<FEN>(
Expand Down
26 changes: 26 additions & 0 deletions crates/evm/core/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,14 @@ pub fn block_env_from_header<BLOCK: FoundryBlock + Default>(header: &impl BlockH
block
}

/// Applies chain-specific changes required to replay transactions accepted on-chain.
pub fn apply_chain_specific_tx_replay_env_changes<SPEC, BLOCK>(evm_env: &mut EvmEnv<SPEC, BLOCK>) {
if NamedChain::try_from(evm_env.cfg_env.chain_id).is_ok_and(|chain| chain.is_arbitrum()) {
// Arbitrum does not enforce the EIP-1559 priority fee ordering constraint.
evm_env.cfg_env.disable_priority_fee_check = true;
Comment thread
mablr marked this conversation as resolved.
}
}

/// Depending on the configured chain id and block number this should apply any specific changes
///
/// - checks for prevrandao mixhash after merge
Expand Down Expand Up @@ -166,6 +174,24 @@ pub fn get_function<'a>(
mod tests {
use super::*;

#[test]
fn tx_replay_env_changes_disable_priority_fee_check_only_for_arbitrum() {
let mut evm_env = EvmEnv::new(
revm::context::CfgEnv::<SpecId>::default(),
revm::context::BlockEnv::default(),
);
evm_env.cfg_env.chain_id = NamedChain::Arbitrum as u64;

apply_chain_specific_tx_replay_env_changes(&mut evm_env);
assert!(evm_env.cfg_env.disable_priority_fee_check);

evm_env.cfg_env.chain_id = NamedChain::Mainnet as u64;
evm_env.cfg_env.disable_priority_fee_check = false;

apply_chain_specific_tx_replay_env_changes(&mut evm_env);
assert!(!evm_env.cfg_env.disable_priority_fee_check);
}

#[test]
fn blob_params_by_spec_id_tracks_latest_known_blob_schedule() {
assert_eq!(get_blob_params_by_spec_id(SpecId::CANCUN), BlobParams::cancun());
Expand Down
23 changes: 23 additions & 0 deletions crates/forge/tests/cli/test_cmd/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -977,6 +977,29 @@ contract BlobForkTest is Test {
cmd.args(["test", "-vvvv"]).assert_success();
});

// https://github.com/foundry-rs/foundry/issues/10689
forgetest_init!(flaky_roll_fork_arbitrum_priority_fee_above_max_fee, |prj, cmd| {
Comment thread
mablr marked this conversation as resolved.
let endpoint = "https://arb-mainnet.g.alchemy.com/public";

prj.add_test(
"ArbitrumFork.t.sol",
&r#"
import {Test} from "forge-std/Test.sol";

contract ArbitrumForkTest is Test {
function test_rollFork() public {
vm.createSelectFork("<url>");
bytes32 txHash = 0x2e43e9ececcbb9cd08ce061edc3b4d39ca2b0ba480034e5f4650ba0065bf6b62;
vm.rollFork(txHash);
}
}
"#
.replace("<url>", endpoint),
);

cmd.arg("test").assert_success();
});

// https://github.com/foundry-rs/foundry/issues/6579
forgetest_init!(include_custom_types_in_traces, |prj, cmd| {
prj.add_test(
Expand Down
2 changes: 2 additions & 0 deletions crates/verify/src/bytecode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ use foundry_evm::{
evm::{EthEvmNetwork, FoundryEvmNetwork, SpecFor, TempoEvmNetwork, TxEnvFor},
},
executors::EvmError,
utils::apply_chain_specific_tx_replay_env_changes,
};
use foundry_evm_networks::NetworkVariant;
use revm::{context::Block as _, state::AccountInfo};
Expand Down Expand Up @@ -660,6 +661,7 @@ impl VerifyBytecodeArgs {
let prev_block_nonce =
provider.get_transaction_count(transaction.from()).block_id(prev_block_id).await?;

apply_chain_specific_tx_replay_env_changes(&mut evm_env);
if let Some(ref block) = block {
configure_env_block::<FEN>(&mut evm_env, block, config.networks);

Expand Down
Loading