Skip to content

Commit

Permalink
fix: don't reinitialize created accounts
Browse files Browse the repository at this point in the history
  • Loading branch information
mattsse committed Dec 6, 2023
1 parent fdad9fb commit 0563356
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 5 deletions.
26 changes: 22 additions & 4 deletions crates/evm/core/src/backend/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -777,6 +777,13 @@ impl Backend {
self.inner.precompiles().contains(addr)
}

/// Sets the initial journaled state to use when initializing forks
#[inline]
fn set_init_journaled_state(&mut self, journaled_state: JournaledState) {
trace!("recording fork init journaled_state");
self.fork_init_journaled_state = journaled_state;
}

/// Cleans up already loaded accounts that would be initialized without the correct data from
/// the fork.
///
Expand All @@ -800,10 +807,21 @@ impl Backend {
let mut journaled_state = self.fork_init_journaled_state.clone();
for loaded_account in loaded_accounts.iter().copied() {
trace!(?loaded_account, "replacing account on init");
let fork_account = Database::basic(&mut fork.db, loaded_account)?
.ok_or(DatabaseError::MissingAccount(loaded_account))?;
let init_account =
journaled_state.state.get_mut(&loaded_account).expect("exists; qed");

// here's and edge case where we need to check if this account has been created, in
// which case we don't need to replace it with the account from the fork because the
// created account takes precedence
if init_account.is_created() {
trace!(?loaded_account, "skipping created account");
continue
}

// otherwise we need to replace the account's info with the one from the fork's
// database
let fork_account = Database::basic(&mut fork.db, loaded_account)?
.ok_or(DatabaseError::MissingAccount(loaded_account))?;
init_account.info = fork_account;
}
fork.journaled_state = journaled_state;
Expand Down Expand Up @@ -1043,8 +1061,8 @@ impl DatabaseExt for Backend {
// different forks. Since the `JournaledState` is valid for all forks until the
// first fork is selected, we need to update it for all forks and use it as init state
// for all future forks
trace!("recording fork init journaled_state");
self.fork_init_journaled_state = active_journaled_state.clone();

self.set_init_journaled_state(active_journaled_state.clone());
self.prepare_init_journal_state()?;

// Make sure that the next created fork has a depth of 0.
Expand Down
6 changes: 5 additions & 1 deletion crates/forge/tests/it/repros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ async fn repro_config(issue: usize, should_fail: bool, sender: Option<Address>)
let filter = Filter::path(&format!(".*repros/Issue{issue}.t.sol"));

let mut config = Config::with_root(PROJECT.root());
config.fs_permissions = FsPermissions::new(vec![PathPermission::read("./fixtures")]);
config.fs_permissions =
FsPermissions::new(vec![PathPermission::read("./fixtures"), PathPermission::read("out")]);
if let Some(sender) = sender {
config.sender = sender;
}
Expand Down Expand Up @@ -190,6 +191,9 @@ test_repro!(5948);
// https://github.com/foundry-rs/foundry/issues/6006
test_repro!(6006);

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

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

Expand Down
48 changes: 48 additions & 0 deletions testdata/repros/Issue6032.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// 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/6032
contract Issue6032Test is DSTest {
Vm constant vm = Vm(HEVM_ADDRESS);

function testEtchFork() public {
// Deploy initial contract
Counter counter = new Counter();
counter.setNumber(42);

address counterAddress = address(counter);
// Enter the fork
vm.createSelectFork("rpcAlias");
assert(counterAddress.code.length > 0);
// `Counter` is not deployed on the fork, which is expected.

// Etch the contract into the fork.
bytes memory code = vm.getDeployedCode("Issue6032.t.sol:CounterEtched");
vm.etch(counterAddress, code);
// `Counter` is now deployed on the fork.
assert(counterAddress.code.length > 0);

// Now we can etch the code, but state will remain.
CounterEtched counterEtched = CounterEtched(counterAddress);
assertEq(counterEtched.numberHalf(), 21);
}
}

contract Counter {
uint256 public number;

function setNumber(uint256 newNumber) public {
number = newNumber;
}
}

contract CounterEtched {
uint256 public number;

function numberHalf() public view returns (uint256) {
return number / 2;
}
}

0 comments on commit 0563356

Please sign in to comment.