Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(anvil): reset to non fork #7500

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 3 additions & 3 deletions crates/anvil/src/eth/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1587,17 +1587,17 @@ impl EthApi {

/// Reset the fork to a fresh forked state, and optionally update the fork config.
///
/// If `forking` is `None` then this will disable forking entirely.
/// If `forking` is `None` then this will disable forking entirely and reset to a fresh state.
///
/// Handler for RPC call: `anvil_reset`
pub async fn anvil_reset(&self, forking: Option<Forking>) -> Result<()> {
node_info!("anvil_reset");
self.reset_instance_id();
if let Some(forking) = forking {
// if we're resetting the fork we need to reset the instance id
self.reset_instance_id();
self.backend.reset_fork(forking).await
} else {
Err(BlockchainError::RpcUnimplemented)
self.backend.reset_to_non_fork().await
}
}

Expand Down
101 changes: 97 additions & 4 deletions crates/anvil/src/eth/backend/mem/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ use crate::{
genesis::GenesisConfig,
mem::storage::MinedTransactionReceipt,
notifications::{NewBlockNotification, NewBlockNotifications},
time::{utc_from_secs, TimeManager},
time::{duration_since_unix_epoch, utc_from_secs, TimeManager},
validate::TransactionValidator,
},
error::{BlockchainError, ErrDetail, InvalidTransactionError},
fees::{FeeDetails, FeeManager},
fees::{FeeDetails, FeeManager, INITIAL_BASE_FEE, INITIAL_GAS_PRICE},
macros::node_info,
pool::transactions::PoolTransaction,
util::get_precompiles_for,
Expand All @@ -31,7 +31,9 @@ use crate::{
};
use alloy_consensus::{Header, Receipt, ReceiptWithBloom};
use alloy_network::Sealable;
use alloy_primitives::{keccak256, Address, Bytes, TxHash, B256, B64, U128, U256, U64, U8};
use alloy_primitives::{
keccak256, utils::Unit, Address, Bytes, TxHash, B256, B64, U128, U256, U64, U8,
};
use alloy_rlp::Decodable;
use alloy_rpc_trace_types::{
geth::{DefaultFrame, GethDebugTracingOptions, GethDefaultTracingOptions, GethTrace},
Expand All @@ -58,7 +60,7 @@ use anvil_core::{
};
use anvil_rpc::error::RpcError;
use flate2::{read::GzDecoder, write::GzEncoder, Compression};
use foundry_common::types::ToAlloy;
use foundry_common::{types::ToAlloy, DEV_CHAIN_ID};
use foundry_evm::{
backend::{DatabaseError, DatabaseResult, RevertSnapshotAction},
constants::DEFAULT_CREATE2_DEPLOYER_RUNTIME_CODE,
Expand Down Expand Up @@ -468,6 +470,97 @@ impl Backend {
}
}

/// Reset to fresh state
pub async fn reset_to_non_fork(&self) -> Result<(), BlockchainError> {
// Clear the db
let mut db = self.db.write().await;
// clear database
db.clear();

let genesis_init = self.genesis.genesis_init.to_owned();
if let Some(fork) = self.get_fork() {
// Node is currently in fork mode, reintialize the env and db
// Clear the fork storage and set ClientFork to None
fork.clear_cached_storage();

self.fork.write().take();

// Reset the env and db
{
let mut env = self.env.write();

env.clear();

// Check if `genesis.json` is provided
if let Some(ref genesis) = genesis_init {
env.cfg.chain_id = genesis.config.chain_id;
env.block.timestamp = U256::from(genesis.timestamp);
if let Some(base_fee) = genesis.base_fee_per_gas {
env.block.basefee = U256::from(base_fee);
}
if let Some(number) = genesis.number {
env.block.number = U256::from(number);
}
env.block.coinbase = genesis.coinbase;
env.block.gas_limit = U256::from(genesis.gas_limit);
} else {
// Anvil defaults for non-forked node.
env.cfg.chain_id = DEV_CHAIN_ID;
env.block = BlockEnv::default();
env.block.timestamp = U256::from(duration_since_unix_epoch().as_secs());
env.block.basefee = U256::from(INITIAL_BASE_FEE);
env.block.gas_limit = U256::from(30_000_000);
}

// Reset time
self.time.reset(env.block.timestamp.to::<u64>());
}

// genesis accounts
let fork_genesis_accounts = self.genesis.fork_genesis_account_infos.lock();
for (address, info) in
self.genesis.accounts.iter().copied().zip(fork_genesis_accounts.iter().cloned())
{
db.insert_account(address, info);
}
} else {
// TODO: ENV related changes.

// genesis accounts

for (account, info) in self.genesis.account_infos() {
db.insert_account(account, info);
}
}

let node_config = if let Some(ref genesis) = genesis_init {
NodeConfig::default()
.with_base_fee(Some(U256::from(
genesis.base_fee_per_gas.unwrap_or(INITIAL_BASE_FEE),
)))
.with_gas_price(Some(U256::from(INITIAL_GAS_PRICE)))
.with_genesis_balance(Unit::ETHER.wei().saturating_mul(U256::from(10000)))
.with_genesis(genesis_init)
} else {
NodeConfig::default()
.with_base_fee(Some(U256::from(INITIAL_BASE_FEE)))
.with_gas_price(Some(U256::from(INITIAL_GAS_PRICE)))
.with_genesis_balance(Unit::ETHER.wei().saturating_mul(U256::from(10000)))
};

*self.node_config.write().await = node_config;
// reset fees
self.fees.set_base_fee(U256::from(INITIAL_BASE_FEE));
self.fees.set_gas_price(U256::from(INITIAL_GAS_PRICE));
// Clear the state
*self.blockchain.storage.write() = BlockchainStorage::empty();
self.states.write().clear();

// Reset genensis alloc
self.genesis.apply_genesis_json_alloc(db)?;

Ok(())
}
/// Returns the `TimeManager` responsible for timestamps
pub fn time(&self) -> &TimeManager {
&self.time
Expand Down