Skip to content

Commit 547e975

Browse files
authored
fix: correctly apply genesis (#8431)
* Add test * fix: correctly apply genesis during fork reset
1 parent 86d583f commit 547e975

File tree

5 files changed

+50
-39
lines changed

5 files changed

+50
-39
lines changed

crates/anvil/src/config.rs

-1
Original file line numberDiff line numberDiff line change
@@ -947,7 +947,6 @@ impl NodeConfig {
947947
timestamp: self.get_genesis_timestamp(),
948948
balance: self.genesis_balance,
949949
accounts: self.genesis_accounts.iter().map(|acc| acc.address()).collect(),
950-
fork_genesis_account_infos: Arc::new(Default::default()),
951950
genesis_init: self.genesis.clone(),
952951
};
953952

crates/anvil/src/eth/backend/genesis.rs

-7
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@ use foundry_evm::{
77
backend::DatabaseResult,
88
revm::primitives::{AccountInfo, Bytecode, KECCAK_EMPTY},
99
};
10-
use parking_lot::Mutex;
11-
use std::sync::Arc;
1210
use tokio::sync::RwLockWriteGuard;
1311

1412
/// Genesis settings
@@ -20,11 +18,6 @@ pub struct GenesisConfig {
2018
pub balance: U256,
2119
/// All accounts that should be initialised at genesis
2220
pub accounts: Vec<Address>,
23-
/// The account object stored in the [`revm::Database`]
24-
///
25-
/// We store this for forking mode so we can cheaply reset the dev accounts and don't
26-
/// need to fetch them again.
27-
pub fork_genesis_account_infos: Arc<Mutex<Vec<AccountInfo>>>,
2821
/// The `genesis.json` if provided
2922
pub genesis_init: Option<Genesis>,
3023
}

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

+6-29
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ use anvil_core::eth::{
6464
use anvil_rpc::error::RpcError;
6565
use flate2::{read::GzDecoder, write::GzEncoder, Compression};
6666
use foundry_evm::{
67-
backend::{BackendError, BackendResult, DatabaseError, DatabaseResult, RevertSnapshotAction},
67+
backend::{DatabaseError, DatabaseResult, RevertSnapshotAction},
6868
constants::DEFAULT_CREATE2_DEPLOYER_RUNTIME_CODE,
6969
decode::RevertDecoder,
7070
inspectors::AccessListInspector,
@@ -277,7 +277,7 @@ impl Backend {
277277
/// Applies the configured genesis settings
278278
///
279279
/// This will fund, create the genesis accounts
280-
async fn apply_genesis(&self) -> BackendResult<()> {
280+
async fn apply_genesis(&self) -> Result<(), DatabaseError> {
281281
trace!(target: "backend", "setting genesis balances");
282282

283283
if self.fork.read().is_some() {
@@ -291,27 +291,18 @@ impl Backend {
291291
genesis_accounts_futures.push(tokio::task::spawn(async move {
292292
let db = db.read().await;
293293
let info = db.basic_ref(address)?.unwrap_or_default();
294-
Ok::<_, BackendError>((address, info))
294+
Ok::<_, DatabaseError>((address, info))
295295
}));
296296
}
297297

298298
let genesis_accounts = futures::future::join_all(genesis_accounts_futures).await;
299299

300300
let mut db = self.db.write().await;
301301

302-
// in fork mode we only set the balance, this way the accountinfo is fetched from the
303-
// remote client, preserving code and nonce. The reason for that is private keys for dev
304-
// accounts are commonly known and are used on testnets
305-
let mut fork_genesis_infos = self.genesis.fork_genesis_account_infos.lock();
306-
fork_genesis_infos.clear();
307-
308302
for res in genesis_accounts {
309-
let (address, mut info) = res.map_err(BackendError::display)??;
303+
let (address, mut info) = res.unwrap()?;
310304
info.balance = self.genesis.balance;
311305
db.insert_account(address, info.clone());
312-
313-
// store the fetched AccountInfo, so we can cheaply reset in [Self::reset_fork()]
314-
fork_genesis_infos.push(info);
315306
}
316307
} else {
317308
let mut db = self.db.write().await;
@@ -459,23 +450,9 @@ impl Backend {
459450
fork.total_difficulty(),
460451
);
461452
self.states.write().clear();
453+
self.db.write().await.clear();
462454

463-
// insert back all genesis accounts, by reusing cached `AccountInfo`s we don't need to
464-
// fetch the data via RPC again
465-
let mut db = self.db.write().await;
466-
467-
// clear database
468-
db.clear();
469-
470-
let fork_genesis_infos = self.genesis.fork_genesis_account_infos.lock();
471-
for (address, info) in
472-
self.genesis.accounts.iter().copied().zip(fork_genesis_infos.iter().cloned())
473-
{
474-
db.insert_account(address, info);
475-
}
476-
477-
// reset the genesis.json alloc
478-
self.genesis.apply_genesis_json_alloc(db)?;
455+
self.apply_genesis().await?;
479456

480457
Ok(())
481458
} else {

crates/anvil/tests/it/fork.rs

+43-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use crate::{
44
abi::{Greeter, ERC721},
55
utils::{http_provider, http_provider_with_signer},
66
};
7-
use alloy_network::{EthereumWallet, TransactionBuilder};
7+
use alloy_network::{EthereumWallet, ReceiptResponse, TransactionBuilder};
88
use alloy_primitives::{address, bytes, Address, Bytes, TxHash, TxKind, U256};
99
use alloy_provider::Provider;
1010
use alloy_rpc_types::{
@@ -1301,3 +1301,45 @@ async fn test_fork_query_at_fork_block() {
13011301

13021302
assert_eq!(balance_before, balance);
13031303
}
1304+
1305+
// <https://github.com/foundry-rs/foundry/issues/4173>
1306+
#[tokio::test(flavor = "multi_thread")]
1307+
async fn test_reset_dev_account_nonce() {
1308+
let config: NodeConfig = fork_config();
1309+
let address = config.genesis_accounts[0].address();
1310+
let (api, handle) = spawn(config).await;
1311+
let provider = handle.http_provider();
1312+
let info = api.anvil_node_info().await.unwrap();
1313+
let number = info.fork_config.fork_block_number.unwrap();
1314+
assert_eq!(number, BLOCK_NUMBER);
1315+
1316+
let nonce_before = provider.get_transaction_count(address).await.unwrap();
1317+
1318+
// Reset to older block with other nonce
1319+
api.anvil_reset(Some(Forking {
1320+
json_rpc_url: None,
1321+
block_number: Some(BLOCK_NUMBER - 1_000_000),
1322+
}))
1323+
.await
1324+
.unwrap();
1325+
1326+
let nonce_after = provider.get_transaction_count(address).await.unwrap();
1327+
1328+
assert!(nonce_before > nonce_after);
1329+
1330+
let receipt = provider
1331+
.send_transaction(WithOtherFields::new(
1332+
TransactionRequest::default()
1333+
.from(address)
1334+
.to(address)
1335+
.nonce(nonce_after)
1336+
.gas_limit(21000u128),
1337+
))
1338+
.await
1339+
.unwrap()
1340+
.get_receipt()
1341+
.await
1342+
.unwrap();
1343+
1344+
assert!(receipt.status());
1345+
}

crates/evm/core/src/backend/error.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ pub enum BackendError {
1414
#[error("cheatcodes are not enabled for {0}; see `vm.allowCheatcodes(address)`")]
1515
NoCheats(Address),
1616
#[error(transparent)]
17-
Backend(#[from] DatabaseError),
17+
Database(#[from] DatabaseError),
1818
#[error("failed to fetch account info for {0}")]
1919
MissingAccount(Address),
2020
#[error(

0 commit comments

Comments
 (0)