anvil/
lib.rs

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