Skip to content

Commit 84e50ef

Browse files
authored
feat(anvil): reset to in-mem (#10897)
* feat(`anvil`): reset to in-mem * fmt + clippy
1 parent b1c8150 commit 84e50ef

File tree

3 files changed

+144
-2
lines changed

3 files changed

+144
-2
lines changed

crates/anvil/src/eth/api.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1900,13 +1900,15 @@ impl EthApi {
19001900
///
19011901
/// Handler for RPC call: `anvil_reset`
19021902
pub async fn anvil_reset(&self, forking: Option<Forking>) -> Result<()> {
1903+
self.reset_instance_id();
19031904
node_info!("anvil_reset");
19041905
if let Some(forking) = forking {
19051906
// if we're resetting the fork we need to reset the instance id
1906-
self.reset_instance_id();
19071907
self.backend.reset_fork(forking).await
19081908
} else {
1909-
Err(BlockchainError::RpcUnimplemented)
1909+
// Reset to a fresh in-memory state
1910+
1911+
self.backend.reset_to_in_mem().await
19101912
}
19111913
}
19121914

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

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -614,6 +614,54 @@ impl Backend {
614614
}
615615
}
616616

617+
/// Resets the backend to a fresh in-memory state, clearing all existing data
618+
pub async fn reset_to_in_mem(&self) -> Result<(), BlockchainError> {
619+
// Clear the fork if any exists
620+
*self.fork.write() = None;
621+
622+
// Get environment and genesis config
623+
let env = self.env.read().clone();
624+
let genesis_timestamp = self.genesis.timestamp;
625+
let genesis_number = self.genesis.number;
626+
let spec_id = self.spec_id();
627+
628+
// Reset environment to genesis state
629+
{
630+
let mut env = self.env.write();
631+
env.evm_env.block_env.number = genesis_number;
632+
env.evm_env.block_env.timestamp = genesis_timestamp;
633+
// Reset other block env fields to their defaults
634+
env.evm_env.block_env.basefee = self.fees.base_fee();
635+
env.evm_env.block_env.prevrandao = Some(B256::ZERO);
636+
}
637+
638+
// Clear all storage and reinitialize with genesis
639+
let base_fee = if self.fees.is_eip1559() { Some(self.fees.base_fee()) } else { None };
640+
*self.blockchain.storage.write() =
641+
BlockchainStorage::new(&env, spec_id, base_fee, genesis_timestamp, genesis_number);
642+
self.states.write().clear();
643+
644+
// Clear the database
645+
self.db.write().await.clear();
646+
647+
// Reset time manager
648+
self.time.reset(genesis_timestamp);
649+
650+
// Reset fees to initial state
651+
if self.fees.is_eip1559() {
652+
self.fees.set_base_fee(crate::eth::fees::INITIAL_BASE_FEE);
653+
}
654+
655+
self.fees.set_gas_price(crate::eth::fees::INITIAL_GAS_PRICE);
656+
657+
// Reapply genesis configuration
658+
self.apply_genesis().await?;
659+
660+
trace!(target: "backend", "reset to fresh in-memory state");
661+
662+
Ok(())
663+
}
664+
617665
async fn reset_block_number(
618666
&self,
619667
fork_url: String,

crates/anvil/tests/it/anvil_api.rs

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1055,3 +1055,95 @@ async fn test_mine_first_block_with_interval() {
10551055
let second_block = api.block_by_number(2.into()).await.unwrap().unwrap();
10561056
assert_eq!(second_block.header.timestamp, init_timestamp + 120);
10571057
}
1058+
1059+
#[tokio::test(flavor = "multi_thread")]
1060+
async fn test_anvil_reset_non_fork() {
1061+
let (api, handle) = spawn(NodeConfig::test()).await;
1062+
let provider = handle.http_provider();
1063+
1064+
// Get initial state
1065+
let init_block = provider.get_block(BlockId::latest()).await.unwrap().unwrap();
1066+
let init_accounts = api.accounts().unwrap();
1067+
let init_balance = provider.get_balance(init_accounts[0]).await.unwrap();
1068+
1069+
// Store the instance id before reset
1070+
let instance_id_before = api.instance_id();
1071+
1072+
// Mine some blocks and make transactions
1073+
for _ in 0..5 {
1074+
api.mine_one().await;
1075+
}
1076+
1077+
// Send a transaction
1078+
let to = Address::random();
1079+
let val = U256::from(1337);
1080+
let tx = TransactionRequest::default().with_from(init_accounts[0]).with_to(to).with_value(val);
1081+
let tx = WithOtherFields::new(tx);
1082+
1083+
let _ = provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap();
1084+
1085+
// Check state has changed
1086+
let block_before_reset = provider.get_block(BlockId::latest()).await.unwrap().unwrap();
1087+
assert!(block_before_reset.header.number > init_block.header.number);
1088+
1089+
let balance_before_reset = provider.get_balance(init_accounts[0]).await.unwrap();
1090+
assert!(balance_before_reset < init_balance);
1091+
1092+
let to_balance_before_reset = provider.get_balance(to).await.unwrap();
1093+
assert_eq!(to_balance_before_reset, val);
1094+
1095+
// Reset to fresh in-memory state (non-fork)
1096+
api.anvil_reset(None).await.unwrap();
1097+
1098+
// Check instance id has changed
1099+
let instance_id_after = api.instance_id();
1100+
assert_ne!(instance_id_before, instance_id_after);
1101+
1102+
// Check we're back at genesis
1103+
let block_after_reset = provider.get_block(BlockId::latest()).await.unwrap().unwrap();
1104+
assert_eq!(block_after_reset.header.number, 0);
1105+
1106+
// Check accounts are restored to initial state
1107+
let balance_after_reset = provider.get_balance(init_accounts[0]).await.unwrap();
1108+
assert_eq!(balance_after_reset, init_balance);
1109+
1110+
// Check the recipient's balance is zero
1111+
let to_balance_after_reset = provider.get_balance(to).await.unwrap();
1112+
assert_eq!(to_balance_after_reset, U256::ZERO);
1113+
1114+
// Test we can continue mining after reset
1115+
api.mine_one().await;
1116+
let new_block = provider.get_block(BlockId::latest()).await.unwrap().unwrap();
1117+
assert_eq!(new_block.header.number, 1);
1118+
}
1119+
1120+
#[tokio::test(flavor = "multi_thread")]
1121+
async fn test_anvil_reset_fork_to_non_fork() {
1122+
let (api, handle) = spawn(fork_config()).await;
1123+
let provider = handle.http_provider();
1124+
1125+
// Verify we're in fork mode
1126+
let metadata = api.anvil_metadata().await.unwrap();
1127+
assert!(metadata.forked_network.is_some());
1128+
1129+
// Mine some blocks
1130+
for _ in 0..3 {
1131+
api.mine_one().await;
1132+
}
1133+
1134+
// Reset to non-fork mode
1135+
api.anvil_reset(None).await.unwrap();
1136+
1137+
// Verify we're no longer in fork mode
1138+
let metadata_after = api.anvil_metadata().await.unwrap();
1139+
assert!(metadata_after.forked_network.is_none());
1140+
1141+
// Check we're at block 0
1142+
let block = provider.get_block(BlockId::latest()).await.unwrap().unwrap();
1143+
assert_eq!(block.header.number, 0);
1144+
1145+
// Verify we can still mine blocks
1146+
api.mine_one().await;
1147+
let new_block = provider.get_block(BlockId::latest()).await.unwrap().unwrap();
1148+
assert_eq!(new_block.header.number, 1);
1149+
}

0 commit comments

Comments
 (0)