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