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