Skip to content

Commit

Permalink
fix: add missing checks for system transactions (foundry-rs#5991)
Browse files Browse the repository at this point in the history
  • Loading branch information
mattsse authored Oct 5, 2023
1 parent a88afa9 commit 469b856
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 19 deletions.
42 changes: 30 additions & 12 deletions crates/cast/bin/cmd/run.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
use clap::Parser;
use ethers::{prelude::Middleware, solc::EvmVersion, types::H160};
use ethers::{prelude::Middleware, solc::EvmVersion};
use eyre::{Result, WrapErr};
use foundry_cli::{
init_progress,
opts::RpcOpts,
update_progress, utils,
utils::{handle_traces, TraceResult},
};
use foundry_common::{is_known_system_sender, SYSTEM_TRANSACTION_TYPE};
use foundry_config::{find_project_root_path, Config};
use foundry_evm::{
executor::{inspector::cheatcodes::util::configure_tx_env, opts::EvmOpts, EvmError},
Expand All @@ -16,11 +17,6 @@ use foundry_evm::{
use foundry_utils::types::ToAlloy;
use tracing::trace;

const ARBITRUM_SENDER: H160 = H160([
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x0a, 0x4b, 0x05,
]);

/// CLI arguments for `cast run`.
#[derive(Debug, Clone, Parser)]
pub struct RunArgs {
Expand Down Expand Up @@ -101,11 +97,22 @@ impl RunArgs {
.await?
.ok_or_else(|| eyre::eyre!("tx not found: {:?}", tx_hash))?;

// check if the tx is a system transaction
if is_known_system_sender(tx.from) ||
tx.transaction_type.map(|ty| ty.as_u64()) == Some(SYSTEM_TRANSACTION_TYPE)
{
return Err(eyre::eyre!(
"{:?} is a system transaction.\nReplaying system transactions is currently not supported.",
tx.hash
))
}

let tx_block_number = tx
.block_number
.ok_or_else(|| eyre::eyre!("tx may still be pending: {:?}", tx_hash))?
.as_u64();

// we need to fork off the parent block
config.fork_block_number = Some(tx_block_number - 1);

let (mut env, fork, chain) = TracingExecutor::get_fork_material(&config, evm_opts).await?;
Expand All @@ -125,6 +132,8 @@ impl RunArgs {
env.block.gas_limit = block.gas_limit.to_alloy();
}

std::fs::write("block.json", serde_json::to_string_pretty(&block).unwrap()).unwrap();

// Set the state to the moment right before the transaction
if !self.quick {
println!("Executing previous transactions from the block.");
Expand All @@ -134,13 +143,16 @@ impl RunArgs {
pb.set_position(0);

for (index, tx) in block.transactions.into_iter().enumerate() {
// arbitrum L1 transaction at the start of every block that has gas price 0
// and gas limit 0 which causes reverts, so we skip it
if tx.from == ARBITRUM_SENDER {
// System transactions such as on L2s don't contain any pricing info so we skip
// them otherwise this would cause reverts
if is_known_system_sender(tx.from) ||
tx.transaction_type.map(|ty| ty.as_u64()) ==
Some(SYSTEM_TRANSACTION_TYPE)
{
update_progress!(pb, index);
continue
}
if tx.hash().eq(&tx_hash) {
if tx.hash.eq(&tx_hash) {
break
}

Expand All @@ -149,7 +161,10 @@ impl RunArgs {
if let Some(to) = tx.to {
trace!(tx=?tx.hash,?to, "executing previous call transaction");
executor.commit_tx_with_env(env.clone()).wrap_err_with(|| {
format!("Failed to execute transaction: {:?}", tx.hash())
format!(
"Failed to execute transaction: {:?} in block {}",
tx.hash, env.block.number
)
})?;
} else {
trace!(tx=?tx.hash, "executing previous create transaction");
Expand All @@ -159,7 +174,10 @@ impl RunArgs {
EvmError::Execution(_) => (),
error => {
return Err(error).wrap_err_with(|| {
format!("Failed to deploy transaction: {:?}", tx.hash())
format!(
"Failed to deploy transaction: {:?} in block {}",
tx.hash, env.block.number
)
})
}
}
Expand Down
44 changes: 44 additions & 0 deletions crates/common/src/constants.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//! Commonly used constants

use ethers_core::types::H160;
use std::time::Duration;

/// The dev chain-id, inherited from hardhat
Expand All @@ -25,3 +26,46 @@ pub const ALCHEMY_FREE_TIER_CUPS: u64 = 330;
pub const NON_ARCHIVE_NODE_WARNING: &str = "\
It looks like you're trying to fork from an older block with a non-archive node which is not \
supported. Please try to change your RPC url to an archive node if the issue persists.";

/// Arbitrum L1 sender address of the first transaction in every block.
/// `0x00000000000000000000000000000000000a4b05`
pub const ARBITRUM_SENDER: H160 = H160([
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x0a, 0x4b, 0x05,
]);

/// The system address, the sender of the first transaction in every block:
/// `0xdeaddeaddeaddeaddeaddeaddeaddeaddead0001`
///
/// See also <https://github.com/ethereum-optimism/optimism/blob/65ec61dde94ffa93342728d324fecf474d228e1f/specs/deposits.md#l1-attributes-deposited-transaction>
pub const OPTIMISM_SYSTEM_ADDRESS: H160 = H160([
0xDE, 0xAD, 0xDE, 0xAD, 0xDE, 0xAD, 0xDE, 0xAD, 0xDE, 0xAD, 0xDE, 0xAD, 0xDE, 0xAD, 0xDE, 0xAD,
0xDE, 0xAD, 0x00, 0x01,
]);

/// Transaction identifier of System transaction types
pub const SYSTEM_TRANSACTION_TYPE: u64 = 126u64;

/// Returns whether the sender is a known L2 system sender that is the first tx in every block.
///
/// Transactions from these senders usually don't have a any fee information.
///
/// See: [ARBITRUM_SENDER], [OPTIMISM_SYSTEM_ADDRESS]
#[inline]
pub fn is_known_system_sender(sender: H160) -> bool {
[ARBITRUM_SENDER, OPTIMISM_SYSTEM_ADDRESS].contains(&sender)
}

#[cfg(test)]
mod tests {
use super::*;
use std::str::FromStr;

#[test]
fn test_constant_sender() {
let arb = H160::from_str("0x00000000000000000000000000000000000a4b05").unwrap();
assert_eq!(arb, ARBITRUM_SENDER);
let base = H160::from_str("0xdeaddeaddeaddeaddeaddeaddeaddeaddead0001").unwrap();
assert_eq!(base, OPTIMISM_SYSTEM_ADDRESS);
}
}
25 changes: 18 additions & 7 deletions crates/evm/src/executor/backend/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,15 @@
use crate::{
abi::CHEATCODE_ADDRESS,
executor::{
backend::snapshot::BackendSnapshot,
backend::{
error::NoCheatcodeAccessError, in_memory_db::FoundryEvmInMemoryDB,
snapshot::BackendSnapshot,
},
fork::{CreateFork, ForkId, MultiFork, SharedBackend},
inspector::{cheatcodes::Cheatcodes, DEFAULT_CREATE2_DEPLOYER},
inspector::{
cheatcodes::{util::configure_tx_env, Cheatcodes},
DEFAULT_CREATE2_DEPLOYER,
},
snapshot::Snapshots,
},
CALLER, TEST_CONTRACT_ADDRESS,
Expand All @@ -16,6 +22,7 @@ use ethers::{
types::{BlockNumber, Transaction},
utils::keccak256,
};
use foundry_common::{is_known_system_sender, SYSTEM_TRANSACTION_TYPE};
use foundry_utils::types::{ToAlloy, ToEthers};
pub use in_memory_db::MemDb;
use revm::{
Expand Down Expand Up @@ -43,10 +50,6 @@ mod diagnostic;
pub use diagnostic::RevertDiagnostic;

pub mod error;
use crate::executor::{
backend::{error::NoCheatcodeAccessError, in_memory_db::FoundryEvmInMemoryDB},
inspector::cheatcodes::util::configure_tx_env,
};
pub use error::{DatabaseError, DatabaseResult};

mod in_memory_db;
Expand Down Expand Up @@ -861,7 +864,15 @@ impl Backend {
.get_full_block(BlockNumber::Number(env.block.number.to_ethers().as_u64().into()))?;

for tx in full_block.transactions.into_iter() {
if tx.hash().eq(&tx_hash.to_ethers()) {
// System transactions such as on L2s don't contain any pricing info so we skip them
// otherwise this would cause reverts
if is_known_system_sender(tx.from) ||
tx.transaction_type.map(|ty| ty.as_u64()) == Some(SYSTEM_TRANSACTION_TYPE)
{
continue
}

if tx.hash.eq(&tx_hash.to_ethers()) {
// found the target transaction
return Ok(Some(tx))
}
Expand Down

0 comments on commit 469b856

Please sign in to comment.