Skip to main content

anvil/
lib.rs

1//! Anvil is a fast local Ethereum development node.
2
3#![cfg_attr(not(test), warn(unused_crate_dependencies))]
4#![cfg_attr(docsrs, feature(doc_cfg))]
5
6#[cfg(feature = "optimism")]
7use op_alloy_rpc_types as _;
8
9use crate::{
10    error::{NodeError, NodeResult},
11    eth::{
12        EthApi,
13        backend::{info::StorageInfo, mem},
14        fees::{FeeHistoryService, FeeManager},
15        miner::{Miner, MiningMode},
16        pool::Pool,
17        sign::{DevSigner, Signer as EthSigner},
18    },
19    filter::Filters,
20    logging::{LoggingManager, NodeLogLayer},
21    service::NodeService,
22    shutdown::Signal,
23    tasks::TaskManager,
24};
25use alloy_primitives::{Address, U256};
26use alloy_signer_local::PrivateKeySigner;
27use eth::backend::fork::ClientFork;
28use eyre::{Result, WrapErr};
29use foundry_common::provider::{ProviderBuilder, RetryProvider};
30pub use foundry_evm::hardfork::EthereumHardfork;
31use foundry_primitives::FoundryNetwork;
32use futures::{FutureExt, TryFutureExt};
33use parking_lot::Mutex;
34use server::try_spawn_ipc;
35use std::{
36    net::SocketAddr,
37    pin::Pin,
38    sync::Arc,
39    task::{Context, Poll},
40};
41use tokio::{
42    runtime::Handle,
43    task::{JoinError, JoinHandle},
44};
45use tracing_subscriber::EnvFilter;
46
47/// contains the background service that drives the node
48mod service;
49
50mod config;
51pub use config::{
52    AccountGenerator, CHAIN_ID, DEFAULT_GAS_LIMIT, ForkChoice, NodeConfig, VERSION_MESSAGE,
53};
54
55mod error;
56/// ethereum related implementations
57pub mod eth;
58/// Evm related abstractions
59mod evm;
60pub use evm::PrecompileFactory;
61
62/// support for polling filters
63pub mod filter;
64/// commandline output
65pub mod logging;
66/// types for subscriptions
67pub mod pubsub;
68/// axum RPC server implementations
69pub mod server;
70/// Futures for shutdown signal
71mod shutdown;
72/// additional task management
73mod tasks;
74
75/// contains cli command
76#[cfg(feature = "cmd")]
77pub mod cmd;
78
79#[cfg(feature = "cmd")]
80pub mod args;
81
82#[cfg(feature = "cmd")]
83pub mod opts;
84
85#[macro_use]
86extern crate foundry_common;
87
88#[macro_use]
89extern crate tracing;
90
91/// Creates the node and runs the server.
92///
93/// Returns the [EthApi] that can be used to interact with the node and the [JoinHandle] of the
94/// task.
95///
96/// # Panics
97///
98/// Panics if any error occurs. For a non-panicking version, use [`try_spawn`].
99///
100///
101/// # Examples
102///
103/// ```no_run
104/// # use anvil::NodeConfig;
105/// # async fn spawn() -> eyre::Result<()> {
106/// let config = NodeConfig::default();
107/// let (api, handle) = anvil::spawn(config).await;
108///
109/// // use api
110///
111/// // wait forever
112/// handle.await.unwrap().unwrap();
113/// # Ok(())
114/// # }
115/// ```
116pub async fn spawn(config: NodeConfig) -> (EthApi<FoundryNetwork>, NodeHandle) {
117    try_spawn(config).await.expect("failed to spawn node")
118}
119
120/// Creates the node and runs the server
121///
122/// Returns the [EthApi] that can be used to interact with the node and the [JoinHandle] of the
123/// task.
124///
125/// # Examples
126///
127/// ```no_run
128/// # use anvil::NodeConfig;
129/// # async fn spawn() -> eyre::Result<()> {
130/// let config = NodeConfig::default();
131/// let (api, handle) = anvil::try_spawn(config).await?;
132///
133/// // use api
134///
135/// // wait forever
136/// handle.await??;
137/// # Ok(())
138/// # }
139/// ```
140pub async fn try_spawn(mut config: NodeConfig) -> Result<(EthApi<FoundryNetwork>, NodeHandle)> {
141    let logger = if config.enable_tracing { init_tracing() } else { Default::default() };
142    logger.set_enabled(!config.silent);
143
144    let init_state = config.init_state.take();
145    let (backend, fork_transaction_replay) = config.setup::<FoundryNetwork>().await?;
146
147    if let Some(state) = init_state {
148        backend.load_state(state).await.wrap_err("failed to load init state")?;
149    }
150
151    if let Some(replay) = fork_transaction_replay {
152        backend
153            .apply_fork_transaction_replay(replay)
154            .await
155            .wrap_err("failed to replay fork transaction prefix")?;
156    }
157
158    let backend = Arc::new(backend);
159
160    if config.enable_auto_impersonate {
161        backend.auto_impersonate_account(true);
162    }
163
164    let fork = backend.get_fork();
165
166    let NodeConfig {
167        signer_accounts,
168        block_time,
169        port,
170        max_transactions,
171        server_config,
172        no_mining,
173        transaction_order,
174        genesis,
175        mixed_mining,
176        ..
177    } = config.clone();
178
179    let pool = Arc::new(Pool::default());
180
181    let mode = if let Some(block_time) = block_time {
182        if mixed_mining {
183            let listener = pool.add_ready_listener();
184            MiningMode::mixed(max_transactions, listener, block_time)
185        } else {
186            MiningMode::interval(block_time)
187        }
188    } else if no_mining {
189        MiningMode::None
190    } else {
191        // get a listener for ready transactions
192        let listener = pool.add_ready_listener();
193        MiningMode::instant(max_transactions, listener)
194    };
195
196    let miner = Miner::new(mode);
197
198    let dev_signer: Box<dyn EthSigner<foundry_primitives::FoundryNetwork>> =
199        Box::new(DevSigner::new(signer_accounts));
200    let mut signers = vec![dev_signer];
201    if let Some(genesis) = genesis {
202        let genesis_signers = genesis
203            .alloc
204            .values()
205            .filter_map(|acc| acc.private_key)
206            .flat_map(|k| PrivateKeySigner::from_bytes(&k))
207            .collect::<Vec<_>>();
208        if !genesis_signers.is_empty() {
209            signers.push(Box::new(DevSigner::new(genesis_signers)));
210        }
211    }
212
213    let fee_history_cache = Arc::new(Mutex::new(Default::default()));
214    let fee_history_service = FeeHistoryService::new(
215        backend.blob_params(),
216        backend.new_block_notifications(),
217        Arc::clone(&fee_history_cache),
218        StorageInfo::new(Arc::clone(&backend)),
219    );
220    // create an entry for the best block
221    if let Some(header) = backend.get_block(backend.best_number()).map(|block| block.header) {
222        fee_history_service.insert_cache_entry_for_block(header.hash_slow(), &header);
223    }
224
225    let filters = Filters::default();
226
227    // create the cloneable api wrapper
228    let api = EthApi::new(
229        Arc::clone(&pool),
230        Arc::clone(&backend),
231        Arc::new(signers),
232        fee_history_cache,
233        fee_history_service.fee_history_limit(),
234        miner.clone(),
235        logger,
236        filters.clone(),
237        transaction_order,
238    );
239
240    // spawn the node service
241    let node_service =
242        tokio::task::spawn(NodeService::new(pool, backend, miner, fee_history_service, filters));
243
244    let mut servers = Vec::with_capacity(config.host.len());
245    let mut addresses = Vec::with_capacity(config.host.len());
246
247    for addr in &config.host {
248        let sock_addr = SocketAddr::new(*addr, port);
249
250        // Create a TCP listener.
251        let tcp_listener = tokio::net::TcpListener::bind(sock_addr).await?;
252        addresses.push(tcp_listener.local_addr()?);
253
254        // Spawn the server future on a new task.
255        let srv = server::serve_on(tcp_listener, api.clone(), server_config.clone());
256        servers.push(tokio::task::spawn(srv.map_err(Into::into)));
257    }
258
259    let tokio_handle = Handle::current();
260    let (signal, on_shutdown) = shutdown::signal();
261    let task_manager = TaskManager::new(tokio_handle, on_shutdown);
262
263    let ipc_task =
264        config.get_ipc_path().map(|path| try_spawn_ipc(api.clone(), path)).transpose()?;
265
266    let handle = NodeHandle {
267        config,
268        node_service,
269        servers,
270        ipc_task,
271        addresses,
272        _signal: Some(signal),
273        task_manager,
274    };
275
276    handle.print(fork.as_ref())?;
277
278    Ok((api, handle))
279}
280
281type IpcTask = JoinHandle<()>;
282
283/// A handle to the spawned node and server tasks.
284///
285/// This future will resolve if either the node or server task resolve/fail.
286pub struct NodeHandle {
287    config: NodeConfig,
288    /// The address of the running rpc server.
289    addresses: Vec<SocketAddr>,
290    /// Join handle for the Node Service.
291    node_service: JoinHandle<Result<(), NodeError>>,
292    /// Join handles (one per socket) for the Anvil server.
293    servers: Vec<JoinHandle<Result<(), NodeError>>>,
294    /// The future that joins the ipc server, if any.
295    ipc_task: Option<IpcTask>,
296    /// A signal that fires the shutdown, fired on drop.
297    _signal: Option<Signal>,
298    /// A task manager that can be used to spawn additional tasks.
299    task_manager: TaskManager,
300}
301
302impl Drop for NodeHandle {
303    fn drop(&mut self) {
304        // Fire shutdown signal to make sure anvil instance is terminated.
305        if let Some(signal) = self._signal.take() {
306            let _ = signal.fire();
307        }
308        self.node_service.abort();
309        for server in &self.servers {
310            server.abort();
311        }
312        if let Some(ipc_task) = &self.ipc_task {
313            ipc_task.abort();
314        }
315    }
316}
317
318impl NodeHandle {
319    /// The [NodeConfig] the node was launched with.
320    pub const fn config(&self) -> &NodeConfig {
321        &self.config
322    }
323
324    /// Prints the launch info.
325    pub(crate) fn print(&self, fork: Option<&ClientFork>) -> Result<()> {
326        self.config.print(fork)?;
327        if !self.config.silent {
328            if let Some(ipc_path) = self.ipc_path() {
329                sh_println!("IPC path: {ipc_path}")?;
330            }
331            sh_println!(
332                "Listening on {}",
333                self.addresses
334                    .iter()
335                    .map(|addr| { addr.to_string() })
336                    .collect::<Vec<String>>()
337                    .join(", ")
338            )?;
339        }
340        Ok(())
341    }
342
343    /// The address of the launched server.
344    ///
345    /// **N.B.** this may not necessarily be the same `host + port` as configured in the
346    /// `NodeConfig`, if port was set to 0, then the OS auto picks an available port.
347    pub fn socket_address(&self) -> &SocketAddr {
348        &self.addresses[0]
349    }
350
351    /// Returns the http endpoint.
352    pub fn http_endpoint(&self) -> String {
353        format!("http://{}", self.socket_address())
354    }
355
356    /// Returns the websocket endpoint.
357    pub fn ws_endpoint(&self) -> String {
358        format!("ws://{}", self.socket_address())
359    }
360
361    /// Returns the path of the launched ipc server, if any.
362    pub fn ipc_path(&self) -> Option<String> {
363        self.config.get_ipc_path()
364    }
365
366    /// Constructs a [`RetryProvider`] for this handle's HTTP endpoint.
367    pub fn http_provider(&self) -> RetryProvider {
368        ProviderBuilder::new(&self.http_endpoint()).build().expect("failed to build HTTP provider")
369    }
370
371    /// Constructs a [`RetryProvider`] for this handle's WS endpoint.
372    pub fn ws_provider(&self) -> RetryProvider {
373        ProviderBuilder::new(&self.ws_endpoint()).build().expect("failed to build WS provider")
374    }
375
376    /// Constructs a [`RetryProvider`] for this handle's IPC endpoint, if any.
377    pub fn ipc_provider(&self) -> Option<RetryProvider> {
378        ProviderBuilder::new(&self.config.get_ipc_path()?).build().ok()
379    }
380
381    /// Signer accounts that can sign messages/transactions from the EVM node.
382    pub fn dev_accounts(&self) -> impl Iterator<Item = Address> + '_ {
383        self.config.signer_accounts.iter().map(|wallet| wallet.address())
384    }
385
386    /// Signer accounts that can sign messages/transactions from the EVM node.
387    pub fn dev_wallets(&self) -> impl Iterator<Item = PrivateKeySigner> + '_ {
388        self.config.signer_accounts.iter().cloned()
389    }
390
391    /// Accounts that will be initialised with `genesis_balance` in the genesis block.
392    pub fn genesis_accounts(&self) -> impl Iterator<Item = Address> + '_ {
393        self.config.genesis_accounts.iter().map(|w| w.address())
394    }
395
396    /// Native token balance of every genesis account in the genesis block.
397    pub const fn genesis_balance(&self) -> U256 {
398        self.config.genesis_balance
399    }
400
401    /// Default gas price for all txs.
402    pub fn gas_price(&self) -> u128 {
403        self.config.get_gas_price()
404    }
405
406    /// Returns the shutdown signal.
407    pub const fn shutdown_signal(&self) -> &Option<Signal> {
408        &self._signal
409    }
410
411    /// Returns mutable access to the shutdown signal.
412    ///
413    /// This can be used to extract the Signal.
414    pub const fn shutdown_signal_mut(&mut self) -> &mut Option<Signal> {
415        &mut self._signal
416    }
417
418    /// Returns the task manager that can be used to spawn new tasks.
419    ///
420    /// ```
421    /// use anvil::NodeHandle;
422    /// # fn t(handle: NodeHandle) {
423    /// let task_manager = handle.task_manager();
424    /// let on_shutdown = task_manager.on_shutdown();
425    ///
426    /// task_manager.spawn(async move {
427    ///     on_shutdown.await;
428    ///     // do something
429    /// });
430    ///
431    /// # }
432    /// ```
433    pub const fn task_manager(&self) -> &TaskManager {
434        &self.task_manager
435    }
436}
437
438impl Future for NodeHandle {
439    type Output = Result<NodeResult<()>, JoinError>;
440
441    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
442        let pin = self.get_mut();
443
444        // poll the ipc task
445        if let Some(mut ipc) = pin.ipc_task.take() {
446            if let Poll::Ready(res) = ipc.poll_unpin(cx) {
447                return Poll::Ready(res.map(|()| Ok(())));
448            }
449            pin.ipc_task = Some(ipc);
450        }
451
452        // poll the node service task
453        if let Poll::Ready(res) = pin.node_service.poll_unpin(cx) {
454            return Poll::Ready(res);
455        }
456
457        // poll the axum server handles
458        for server in &mut pin.servers {
459            if let Poll::Ready(res) = server.poll_unpin(cx) {
460                return Poll::Ready(res);
461            }
462        }
463
464        Poll::Pending
465    }
466}
467
468#[doc(hidden)]
469pub fn init_tracing() -> LoggingManager {
470    use tracing_subscriber::prelude::*;
471
472    let manager = LoggingManager::default();
473
474    let _ = if let Ok(rust_log_val) = std::env::var("RUST_LOG")
475        && !rust_log_val.contains('=')
476    {
477        // Mutate the given filter to include `node` logs if it is not already present.
478        // This prevents the unexpected behaviour of not seeing any node logs if a RUST_LOG
479        // is already present that doesn't set it.
480        let rust_log_val = if rust_log_val.contains("node") {
481            rust_log_val
482        } else {
483            format!("{rust_log_val},node=info")
484        };
485
486        let env_filter: EnvFilter =
487            rust_log_val.parse().expect("failed to parse modified RUST_LOG");
488        tracing_subscriber::registry()
489            .with(env_filter)
490            .with(tracing_subscriber::fmt::layer())
491            .try_init()
492    } else {
493        tracing_subscriber::Registry::default()
494            .with(NodeLogLayer::new(manager.clone()))
495            .with(
496                tracing_subscriber::fmt::layer()
497                    .without_time()
498                    .with_target(false)
499                    .with_level(false),
500            )
501            .try_init()
502    };
503
504    manager
505}