Skip to content

Commit 2845545

Browse files
yash-atreyarplusq
authored andcommitted
feat(anvil): --cache-path (foundry-rs#9343)
* feat(`anvil`): --cache-path * nits * test * nit * run with tempdir * nit
1 parent ae165b6 commit 2845545

File tree

9 files changed

+88
-5
lines changed

9 files changed

+88
-5
lines changed

Cargo.lock

Lines changed: 18 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,7 @@ alloy-transport = { version = "0.6.4", default-features = false }
203203
alloy-transport-http = { version = "0.6.4", default-features = false }
204204
alloy-transport-ipc = { version = "0.6.4", default-features = false }
205205
alloy-transport-ws = { version = "0.6.4", default-features = false }
206+
alloy-node-bindings = { version = "0.6.4", default-features = false }
206207

207208
## alloy-core
208209
alloy-dyn-abi = "0.8.11"

crates/anvil/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ alloy-rpc-client = { workspace = true, features = ["pubsub"] }
117117
alloy-transport-ipc = { workspace = true, features = ["mock"] }
118118
alloy-provider = { workspace = true, features = ["txpool-api"] }
119119
alloy-transport-ws.workspace = true
120+
alloy-node-bindings.workspace = true
120121
alloy-json-rpc.workspace = true
121122
alloy-pubsub.workspace = true
122123
foundry-test-utils.workspace = true

crates/anvil/src/cmd.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,10 @@ pub struct NodeArgs {
189189

190190
#[command(flatten)]
191191
pub server_config: ServerConfig,
192+
193+
/// Path to the cache directory where states are stored.
194+
#[arg(long, value_name = "PATH")]
195+
pub cache_path: Option<PathBuf>,
192196
}
193197

194198
#[cfg(windows)]
@@ -274,7 +278,8 @@ impl NodeArgs {
274278
.with_alphanet(self.evm_opts.alphanet)
275279
.with_disable_default_create2_deployer(self.evm_opts.disable_default_create2_deployer)
276280
.with_slots_in_an_epoch(self.slots_in_an_epoch)
277-
.with_memory_limit(self.evm_opts.memory_limit))
281+
.with_memory_limit(self.evm_opts.memory_limit)
282+
.with_cache_path(self.cache_path))
278283
}
279284

280285
fn account_generator(&self) -> AccountGenerator {

crates/anvil/src/config.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,8 @@ pub struct NodeConfig {
189189
pub alphanet: bool,
190190
/// Do not print log messages.
191191
pub silent: bool,
192+
/// The path where states are cached.
193+
pub cache_path: Option<PathBuf>,
192194
}
193195

194196
impl NodeConfig {
@@ -465,6 +467,7 @@ impl Default for NodeConfig {
465467
precompile_factory: None,
466468
alphanet: false,
467469
silent: false,
470+
cache_path: None,
468471
}
469472
}
470473
}
@@ -969,6 +972,13 @@ impl NodeConfig {
969972
self
970973
}
971974

975+
/// Sets the path where states are cached
976+
#[must_use]
977+
pub fn with_cache_path(mut self, cache_path: Option<PathBuf>) -> Self {
978+
self.cache_path = cache_path;
979+
self
980+
}
981+
972982
/// Configures everything related to env, backend and database and returns the
973983
/// [Backend](mem::Backend)
974984
///
@@ -1051,6 +1061,7 @@ impl NodeConfig {
10511061
self.max_persisted_states,
10521062
self.transaction_block_keeper,
10531063
self.block_time,
1064+
self.cache_path.clone(),
10541065
Arc::new(tokio::sync::RwLock::new(self.clone())),
10551066
)
10561067
.await;

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ pub struct DiskStateCache {
1818
}
1919

2020
impl DiskStateCache {
21+
/// Specify the path where to create the tempdir in
22+
pub fn with_path(self, temp_path: PathBuf) -> Self {
23+
Self { temp_path: Some(temp_path), temp_dir: None }
24+
}
2125
/// Returns the cache file for the given hash
2226
fn with_cache_file<F, R>(&mut self, hash: B256, f: F) -> Option<R>
2327
where

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

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,12 +103,12 @@ use revm::{
103103
use std::{
104104
collections::BTreeMap,
105105
io::{Read, Write},
106+
path::PathBuf,
106107
sync::Arc,
107108
time::Duration,
108109
};
109110
use storage::{Blockchain, MinedTransaction, DEFAULT_HISTORY_LIMIT};
110111
use tokio::sync::RwLock as AsyncRwLock;
111-
112112
pub mod cache;
113113
pub mod fork_db;
114114
pub mod in_memory_db;
@@ -227,6 +227,7 @@ impl Backend {
227227
max_persisted_states: Option<usize>,
228228
transaction_block_keeper: Option<usize>,
229229
automine_block_time: Option<Duration>,
230+
cache_path: Option<PathBuf>,
230231
node_config: Arc<AsyncRwLock<NodeConfig>>,
231232
) -> Self {
232233
// if this is a fork then adjust the blockchain storage
@@ -249,7 +250,7 @@ impl Backend {
249250
genesis.timestamp
250251
};
251252

252-
let states = if prune_state_history_config.is_config_enabled() {
253+
let mut states = if prune_state_history_config.is_config_enabled() {
253254
// if prune state history is enabled, configure the state cache only for memory
254255
prune_state_history_config
255256
.max_memory_history
@@ -264,6 +265,10 @@ impl Backend {
264265
Default::default()
265266
};
266267

268+
if let Some(cache_path) = cache_path {
269+
states = states.disk_path(cache_path);
270+
}
271+
267272
let (slots_in_an_epoch, precompile_factory) = {
268273
let cfg = node_config.read().await;
269274
(cfg.slots_in_an_epoch, cfg.precompile_factory.clone())

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ use foundry_evm::{
4141
};
4242
use parking_lot::RwLock;
4343
use revm::primitives::SpecId;
44-
use std::{collections::VecDeque, fmt, sync::Arc, time::Duration};
44+
use std::{collections::VecDeque, fmt, path::PathBuf, sync::Arc, time::Duration};
4545
// use yansi::Paint;
4646

4747
// === various limits in number of blocks ===
@@ -94,6 +94,12 @@ impl InMemoryBlockStates {
9494
self
9595
}
9696

97+
/// Configures the path on disk where the states will cached.
98+
pub fn disk_path(mut self, path: PathBuf) -> Self {
99+
self.disk_cache = self.disk_cache.with_path(path);
100+
self
101+
}
102+
97103
/// This modifies the `limit` what to keep stored in memory.
98104
///
99105
/// This will ensure the new limit adjusts based on the block time.

crates/anvil/tests/it/anvil.rs

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@
22
33
use alloy_consensus::EMPTY_ROOT_HASH;
44
use alloy_eips::BlockNumberOrTag;
5-
use alloy_primitives::Address;
5+
use alloy_node_bindings::utils::run_with_tempdir;
6+
use alloy_primitives::{Address, U256};
67
use alloy_provider::Provider;
78
use anvil::{spawn, EthereumHardfork, NodeConfig};
9+
use std::time::Duration;
810

911
#[tokio::test(flavor = "multi_thread")]
1012
async fn test_can_change_mining_mode() {
@@ -118,3 +120,33 @@ async fn test_cancun_fields() {
118120
assert!(block.header.blob_gas_used.is_some());
119121
assert!(block.header.excess_blob_gas.is_some());
120122
}
123+
124+
#[tokio::test(flavor = "multi_thread")]
125+
#[cfg(not(windows))]
126+
async fn test_cache_path() {
127+
run_with_tempdir("custom-anvil-cache", |tmp_dir| async move {
128+
let cache_path = tmp_dir.join("cache");
129+
let (api, _handle) = spawn(
130+
NodeConfig::test()
131+
.with_cache_path(Some(cache_path.clone()))
132+
.with_max_persisted_states(Some(5_usize))
133+
.with_blocktime(Some(Duration::from_millis(1))),
134+
)
135+
.await;
136+
137+
api.anvil_mine(Some(U256::from(1000)), None).await.unwrap();
138+
139+
// sleep to ensure the cache is written
140+
tokio::time::sleep(Duration::from_secs(2)).await;
141+
142+
assert!(cache_path.exists());
143+
assert!(cache_path.read_dir().unwrap().count() > 0);
144+
145+
// Clean the directory, this is to prevent an error when temp_dir is dropped.
146+
let _ = std::fs::remove_dir_all(cache_path);
147+
148+
//sleep to ensure OS file handles are released
149+
tokio::time::sleep(Duration::from_secs(1)).await;
150+
})
151+
.await;
152+
}

0 commit comments

Comments
 (0)