Skip to content

Commit

Permalink
fix: update env in transact cheatcode
Browse files Browse the repository at this point in the history
  • Loading branch information
mattsse committed Jan 17, 2024
1 parent f32550c commit 701a839
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 17 deletions.
46 changes: 29 additions & 17 deletions crates/evm/core/src/backend/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@ use revm::{
},
Database, DatabaseCommit, Inspector, JournaledState, EVM,
};
use std::collections::{HashMap, HashSet};
use std::{
collections::{HashMap, HashSet},
time::Instant,
};

mod diagnostic;
pub use diagnostic::RevertDiagnostic;
Expand Down Expand Up @@ -888,6 +891,7 @@ impl Backend {
if is_known_system_sender(tx.from) ||
tx.transaction_type.map(|ty| ty.to::<u64>()) == Some(SYSTEM_TRANSACTION_TYPE)
{
trace!(tx=?tx.hash, "skipping system transaction");
continue;
}

Expand Down Expand Up @@ -1199,14 +1203,7 @@ impl DatabaseExt for Backend {
// roll the fork to the transaction's block or latest if it's pending
self.roll_fork(Some(id), fork_block.to(), env, journaled_state)?;

// update the block's env accordingly
env.block.timestamp = block.header.timestamp;
env.block.coinbase = block.header.miner;
env.block.difficulty = block.header.difficulty;
env.block.prevrandao = Some(block.header.mix_hash.unwrap_or_default());
env.block.basefee = block.header.base_fee_per_gas.unwrap_or_default();
env.block.gas_limit = block.header.gas_limit;
env.block.number = block.header.number.map(|n| n.to()).unwrap_or(fork_block.to());
update_env_block(env, fork_block, &block);

// replay all transactions that came before
let env = env.clone();
Expand All @@ -1228,17 +1225,19 @@ impl DatabaseExt for Backend {
let id = self.ensure_fork(maybe_id)?;
let fork_id = self.ensure_fork_id(id).cloned()?;

let env = if maybe_id.is_none() {
self.forks
.get_env(fork_id.clone())?
.ok_or_else(|| eyre::eyre!("Requested fork `{}` does not exit", id))?
} else {
env.clone()
let tx = {
let fork = self.inner.get_fork_by_id_mut(id)?;
fork.db.db.get_transaction(transaction)?
};

let fork = self.inner.get_fork_by_id_mut(id)?;
let tx = fork.db.db.get_transaction(transaction)?;
// This is a bit ambiguous because the user wants to transact an arbitrary transaction in the current context, but we're assuming the user wants to transact the transaction as it was mined. Usually this is used in a combination of a fork at the transaction's parent transaction in the block and then the transaction is transacted: <https://github.com/foundry-rs/foundry/issues/6538>
// So we modify the env to match the transaction's block
let (fork_block, block) =
self.get_block_number_and_block_for_transaction(id, transaction)?;
let mut env = env.clone();
update_env_block(&mut env, fork_block, &block);

let fork = self.inner.get_fork_by_id_mut(id)?;
commit_transaction(tx, env, journaled_state, fork, &fork_id, inspector)
}

Expand Down Expand Up @@ -1856,6 +1855,17 @@ fn is_contract_in_state(journaled_state: &JournaledState, acc: Address) -> bool
.unwrap_or_default()
}

/// Updates the env's block with the block's data
fn update_env_block(env: &mut Env, fork_block: U64, block: &Block) {
env.block.timestamp = block.header.timestamp;
env.block.coinbase = block.header.miner;
env.block.difficulty = block.header.difficulty;
env.block.prevrandao = Some(block.header.mix_hash.unwrap_or_default());
env.block.basefee = block.header.base_fee_per_gas.unwrap_or_default();
env.block.gas_limit = block.header.gas_limit;
env.block.number = block.header.number.map(|n| n.to()).unwrap_or(fork_block.to());
}

/// Executes the given transaction and commits state changes to the database _and_ the journaled
/// state, with an optional inspector
fn commit_transaction<I: Inspector<Backend>>(
Expand All @@ -1868,6 +1878,7 @@ fn commit_transaction<I: Inspector<Backend>>(
) -> eyre::Result<()> {
configure_tx_env(&mut env, &tx);

let now = Instant::now();
let state = {
let mut evm = EVM::new();
evm.env = env;
Expand All @@ -1883,6 +1894,7 @@ fn commit_transaction<I: Inspector<Backend>>(
Err(e) => eyre::bail!("backend: failed committing transaction: {e}"),
}
};
trace!(elapsed = ?now.elapsed(), "transacted transaction");

apply_state_changeset(state, journaled_state, fork);
Ok(())
Expand Down
4 changes: 4 additions & 0 deletions crates/forge/tests/it/repros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ macro_rules! test_repro {
}

async fn repro_config(issue: usize, should_fail: bool, sender: Option<Address>) -> TestConfig {
foundry_test_utils::init_tracing();
let filter = Filter::path(&format!(".*repros/Issue{issue}.t.sol"));

let mut config = Config::with_root(PROJECT.root());
Expand Down Expand Up @@ -280,6 +281,9 @@ test_repro!(6501, false, None, |res| {
}
});

// https://github.com/foundry-rs/foundry/issues/6538
test_repro!(6538);

// https://github.com/foundry-rs/foundry/issues/6554
test_repro!(6554; |config| {
let mut cheats_config = config.runner.cheats_config.as_ref().clone();
Expand Down
17 changes: 17 additions & 0 deletions testdata/repros/Issue6538.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity 0.8.18;

import "ds-test/test.sol";
import "../cheats/Vm.sol";

// https://github.com/foundry-rs/foundry/issues/6538
contract Issue6538Test is DSTest {
Vm constant vm = Vm(HEVM_ADDRESS);

function test_transact() public {
bytes32 lastHash = 0xdbdce1d5c14a6ca17f0e527ab762589d6a73f68697606ae0bb90df7ac9ec5087;
vm.createSelectFork("rpcAlias", lastHash);
bytes32 txhash = 0xadbe5cf9269a001d50990d0c29075b402bcc3a0b0f3258821881621b787b35c6;
vm.transact(txhash);
}
}

0 comments on commit 701a839

Please sign in to comment.