From 82ef4b12007ae0a5ec1200d93c0fedc95f1b14b9 Mon Sep 17 00:00:00 2001 From: Zero Hero <0xzerohero@gmail.com> Date: Fri, 23 Jun 2023 21:25:55 +0300 Subject: [PATCH 1/9] draft(anvil): add ability to use more than one socket address --- anvil/src/lib.rs | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/anvil/src/lib.rs b/anvil/src/lib.rs index 310d138590e3f..7e8400c918762 100644 --- a/anvil/src/lib.rs +++ b/anvil/src/lib.rs @@ -27,7 +27,7 @@ use parking_lot::Mutex; use std::{ future::Future, io, - net::{IpAddr, Ipv4Addr, SocketAddr}, + net::{IpAddr, SocketAddr}, pin::Pin, sync::Arc, task::{Context, Poll}, @@ -162,15 +162,21 @@ pub async fn spawn(mut config: NodeConfig) -> (EthApi, NodeHandle) { let node_service = tokio::task::spawn(NodeService::new(pool, backend, miner, fee_history_service, filters)); - let host = config.host.unwrap_or(IpAddr::V4(Ipv4Addr::LOCALHOST)); - let mut addr = SocketAddr::new(host, port); + let servers: Vec<_> = vec!["127.0.0.1".parse::().unwrap(), "::1".parse().unwrap()] + .into_iter() + .map(|addr| { + let sock_addr = SocketAddr::new(addr, port); + server::serve(sock_addr, api.clone(), server_config.clone()) + }) + .collect(); - // configure the rpc server and use its actual local address - let server = server::serve(addr, api.clone(), server_config); - addr = server.local_addr(); + let addr = servers[0].local_addr(); // spawn the server on a new task - let serve = tokio::task::spawn(server.map_err(NodeError::from)); + let servers = servers + .into_iter() + .map(|server| tokio::task::spawn(server.map_err(NodeError::from))) + .collect(); let tokio_handle = Handle::current(); let (signal, on_shutdown) = shutdown::signal(); @@ -181,7 +187,7 @@ pub async fn spawn(mut config: NodeConfig) -> (EthApi, NodeHandle) { let handle = NodeHandle { config, node_service, - server: serve, + servers, ipc_task, address: addr, _signal: Some(signal), @@ -205,7 +211,7 @@ pub struct NodeHandle { /// Join handle for the Node Service pub node_service: JoinHandle>, /// Join handle for the Anvil server - pub server: JoinHandle>, + pub servers: Vec>>, // The future that joins the ipc server, if any ipc_task: Option, /// A signal that fires the shutdown, fired on drop. @@ -352,7 +358,13 @@ impl Future for NodeHandle { return Poll::Ready(res) } - pin.server.poll_unpin(cx) + for server in pin.servers.iter_mut() { + if let Poll::Ready(res) = server.poll_unpin(cx) { + return Poll::Ready(res) + } + } + + Poll::Pending } } From 7579e968dd017d3817479e6534f71269a1194181 Mon Sep 17 00:00:00 2001 From: Zero Hero <0xzerohero@gmail.com> Date: Sat, 24 Jun 2023 00:53:56 +0300 Subject: [PATCH 2/9] chore(anvil): refactor to use one loop --- anvil/src/lib.rs | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/anvil/src/lib.rs b/anvil/src/lib.rs index 7e8400c918762..f31cf2e45ab32 100644 --- a/anvil/src/lib.rs +++ b/anvil/src/lib.rs @@ -162,21 +162,19 @@ pub async fn spawn(mut config: NodeConfig) -> (EthApi, NodeHandle) { let node_service = tokio::task::spawn(NodeService::new(pool, backend, miner, fee_history_service, filters)); - let servers: Vec<_> = vec!["127.0.0.1".parse::().unwrap(), "::1".parse().unwrap()] - .into_iter() - .map(|addr| { - let sock_addr = SocketAddr::new(addr, port); - server::serve(sock_addr, api.clone(), server_config.clone()) - }) - .collect(); - - let addr = servers[0].local_addr(); - - // spawn the server on a new task - let servers = servers - .into_iter() - .map(|server| tokio::task::spawn(server.map_err(NodeError::from))) - .collect(); + let mut servers = Vec::new(); + let mut addrs = Vec::new(); + + for addr in ["127.0.0.1".parse::().unwrap(), "::1".parse().unwrap()] { + let sock_addr = SocketAddr::new(addr, port); + let srv = server::serve(sock_addr, api.clone(), server_config.clone()); + + addrs.push(srv.local_addr()); + + // spawn the server on a new task + let srv = tokio::task::spawn(srv.map_err(NodeError::from)); + servers.push(srv); + } let tokio_handle = Handle::current(); let (signal, on_shutdown) = shutdown::signal(); @@ -189,7 +187,7 @@ pub async fn spawn(mut config: NodeConfig) -> (EthApi, NodeHandle) { node_service, servers, ipc_task, - address: addr, + address: addrs[0], _signal: Some(signal), task_manager, }; From 0d5edfeb5096e457b94a7a5e72a2b60abce7e3c9 Mon Sep 17 00:00:00 2001 From: Zero Hero <0xzerohero@gmail.com> Date: Mon, 26 Jun 2023 14:07:39 +0300 Subject: [PATCH 3/9] draft(anvil): re-enable --host option --- anvil/src/cmd.rs | 10 ++++++++-- anvil/src/config.rs | 13 +++++++++---- anvil/src/lib.rs | 6 +++--- 3 files changed, 20 insertions(+), 9 deletions(-) diff --git a/anvil/src/cmd.rs b/anvil/src/cmd.rs index 24e00f8773a2a..4cee19cd99122 100644 --- a/anvil/src/cmd.rs +++ b/anvil/src/cmd.rs @@ -84,8 +84,14 @@ pub struct NodeArgs { pub no_mining: bool, /// The host the server will listen on. - #[clap(long, value_name = "IP_ADDR", env = "ANVIL_IP_ADDR", help_heading = "Server options")] - pub host: Option, + #[clap( + long, + value_name = "IP_ADDR", + env = "ANVIL_IP_ADDR", + default_value = "127.0.0.1", + help_heading = "Server options" + )] + pub host: Vec, /// How transactions are sorted in the mempool. #[clap(long, default_value = "fees")] diff --git a/anvil/src/config.rs b/anvil/src/config.rs index 8f487bb58f2a9..22649f0aaf4b4 100644 --- a/anvil/src/config.rs +++ b/anvil/src/config.rs @@ -41,7 +41,12 @@ use foundry_evm::{ use parking_lot::RwLock; use serde_json::{json, to_writer, Value}; use std::{ - collections::HashMap, fmt::Write as FmtWrite, fs::File, net::IpAddr, path::PathBuf, sync::Arc, + collections::HashMap, + fmt::Write as FmtWrite, + fs::File, + net::{IpAddr, Ipv4Addr}, + path::PathBuf, + sync::Arc, time::Duration, }; use yansi::Paint; @@ -128,7 +133,7 @@ pub struct NodeConfig { /// How to configure the server pub server_config: ServerConfig, /// The host the server will listen on - pub host: Option, + pub host: Vec, /// How transactions are sorted in the mempool pub transaction_order: TransactionOrder, /// Filename to write anvil output as json @@ -376,7 +381,7 @@ impl Default for NodeConfig { enable_steps_tracing: false, no_storage_caching: false, server_config: Default::default(), - host: None, + host: vec![IpAddr::V4(Ipv4Addr::LOCALHOST)], transaction_order: Default::default(), config_out: None, genesis: None, @@ -707,7 +712,7 @@ impl NodeConfig { /// Sets the host the server will listen on #[must_use] - pub fn with_host(mut self, host: Option) -> Self { + pub fn with_host(mut self, host: Vec) -> Self { self.host = host; self } diff --git a/anvil/src/lib.rs b/anvil/src/lib.rs index f31cf2e45ab32..cc918d85f3624 100644 --- a/anvil/src/lib.rs +++ b/anvil/src/lib.rs @@ -27,7 +27,7 @@ use parking_lot::Mutex; use std::{ future::Future, io, - net::{IpAddr, SocketAddr}, + net::SocketAddr, pin::Pin, sync::Arc, task::{Context, Poll}, @@ -165,8 +165,8 @@ pub async fn spawn(mut config: NodeConfig) -> (EthApi, NodeHandle) { let mut servers = Vec::new(); let mut addrs = Vec::new(); - for addr in ["127.0.0.1".parse::().unwrap(), "::1".parse().unwrap()] { - let sock_addr = SocketAddr::new(addr, port); + for addr in config.host.iter() { + let sock_addr = SocketAddr::new(addr.to_owned(), port); let srv = server::serve(sock_addr, api.clone(), server_config.clone()); addrs.push(srv.local_addr()); From 68ce2a98452ca01a2e7754f92c5efff6b07f8b17 Mon Sep 17 00:00:00 2001 From: Zero Hero <0xzerohero@gmail.com> Date: Mon, 26 Jun 2023 19:45:50 +0300 Subject: [PATCH 4/9] chore(anvil): add comment --- anvil/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/anvil/src/lib.rs b/anvil/src/lib.rs index cc918d85f3624..b49a7817cfdca 100644 --- a/anvil/src/lib.rs +++ b/anvil/src/lib.rs @@ -356,6 +356,7 @@ impl Future for NodeHandle { return Poll::Ready(res) } + // poll the axum server handles for server in pin.servers.iter_mut() { if let Poll::Ready(res) = server.poll_unpin(cx) { return Poll::Ready(res) From 8f866a5fb2d660f995fa9e0cded997699be2434f Mon Sep 17 00:00:00 2001 From: Zero Hero <0xzerohero@gmail.com> Date: Mon, 26 Jun 2023 20:11:34 +0300 Subject: [PATCH 5/9] fix(anvil): print all listening addresses on startup --- anvil/src/lib.rs | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/anvil/src/lib.rs b/anvil/src/lib.rs index b49a7817cfdca..e5fa4d18d67c9 100644 --- a/anvil/src/lib.rs +++ b/anvil/src/lib.rs @@ -163,13 +163,13 @@ pub async fn spawn(mut config: NodeConfig) -> (EthApi, NodeHandle) { tokio::task::spawn(NodeService::new(pool, backend, miner, fee_history_service, filters)); let mut servers = Vec::new(); - let mut addrs = Vec::new(); + let mut addresses = Vec::new(); for addr in config.host.iter() { let sock_addr = SocketAddr::new(addr.to_owned(), port); let srv = server::serve(sock_addr, api.clone(), server_config.clone()); - addrs.push(srv.local_addr()); + addresses.push(srv.local_addr()); // spawn the server on a new task let srv = tokio::task::spawn(srv.map_err(NodeError::from)); @@ -187,7 +187,7 @@ pub async fn spawn(mut config: NodeConfig) -> (EthApi, NodeHandle) { node_service, servers, ipc_task, - address: addrs[0], + addresses, _signal: Some(signal), task_manager, }; @@ -205,7 +205,7 @@ type IpcTask = JoinHandle>; pub struct NodeHandle { config: NodeConfig, /// The address of the running rpc server - address: SocketAddr, + addresses: Vec, /// Join handle for the Node Service pub node_service: JoinHandle>, /// Join handle for the Anvil server @@ -228,7 +228,14 @@ impl NodeHandle { pub(crate) fn print(&self, fork: Option<&ClientFork>) { self.config.print(fork); if !self.config.silent { - println!("Listening on {}", self.socket_address()) + println!( + "Listening on {}", + self.addresses + .iter() + .map(|addr| { addr.to_string() }) + .collect::>() + .join(", ") + ) } } @@ -237,7 +244,7 @@ impl NodeHandle { /// **N.B.** this may not necessarily be the same `host + port` as configured in the /// `NodeConfig`, if port was set to 0, then the OS auto picks an available port pub fn socket_address(&self) -> &SocketAddr { - &self.address + &self.addresses[0] } /// Returns the http endpoint From acc18447971755ef4686a0300af2f2b176ac813e Mon Sep 17 00:00:00 2001 From: Zero Hero <0xzerohero@gmail.com> Date: Tue, 27 Jun 2023 15:49:50 +0300 Subject: [PATCH 6/9] add tests --- anvil/src/cmd.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/anvil/src/cmd.rs b/anvil/src/cmd.rs index 4cee19cd99122..6f6a0616c4257 100644 --- a/anvil/src/cmd.rs +++ b/anvil/src/cmd.rs @@ -614,6 +614,8 @@ impl FromStr for ForkUrl { #[cfg(test)] mod tests { + use std::{env, net::Ipv4Addr}; + use super::*; #[test] @@ -670,4 +672,22 @@ mod tests { NodeArgs::try_parse_from(["anvil", "--disable-block-gas-limit", "--gas-limit", "100"]); assert!(args.is_err()); } + + #[test] + fn can_parse_host() { + let args = NodeArgs::parse_from(["anvil"]); + assert_eq!(args.host, vec![IpAddr::V4(Ipv4Addr::LOCALHOST)]); + + let args = NodeArgs::parse_from([ + "anvil", "--host", "::1", "--host", "1.1.1.1", "--host", "2.2.2.2", + ]); + assert_eq!( + args.host, + ["::1", "1.1.1.1", "2.2.2.2"].map(|ip| ip.parse::().unwrap()).to_vec() + ); + + env::set_var("ANVIL_IP_ADDR", "1.1.1.1"); + let args = NodeArgs::parse_from(["anvil"]); + assert_eq!(args.host, vec!["1.1.1.1".parse::().unwrap()]); + } } From 8abafa51dadd7bbd00a18bbdbb4681b7ac27f81a Mon Sep 17 00:00:00 2001 From: Zero Hero <0xzerohero@gmail.com> Date: Tue, 27 Jun 2023 17:50:03 +0300 Subject: [PATCH 7/9] add ability to set multiple socket addresses in ANVIL_IP_ADDR --- anvil/src/cmd.rs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/anvil/src/cmd.rs b/anvil/src/cmd.rs index 6f6a0616c4257..33ae134348e4f 100644 --- a/anvil/src/cmd.rs +++ b/anvil/src/cmd.rs @@ -89,7 +89,8 @@ pub struct NodeArgs { value_name = "IP_ADDR", env = "ANVIL_IP_ADDR", default_value = "127.0.0.1", - help_heading = "Server options" + help_heading = "Server options", + value_delimiter = ',' )] pub host: Vec, @@ -686,8 +687,21 @@ mod tests { ["::1", "1.1.1.1", "2.2.2.2"].map(|ip| ip.parse::().unwrap()).to_vec() ); + let args = NodeArgs::parse_from(["anvil", "--host", "::1,1.1.1.1,2.2.2.2"]); + assert_eq!( + args.host, + ["::1", "1.1.1.1", "2.2.2.2"].map(|ip| ip.parse::().unwrap()).to_vec() + ); + env::set_var("ANVIL_IP_ADDR", "1.1.1.1"); let args = NodeArgs::parse_from(["anvil"]); assert_eq!(args.host, vec!["1.1.1.1".parse::().unwrap()]); + + env::set_var("ANVIL_IP_ADDR", "::1,1.1.1.1,2.2.2.2"); + let args = NodeArgs::parse_from(["anvil"]); + assert_eq!( + args.host, + ["::1", "1.1.1.1", "2.2.2.2"].map(|ip| ip.parse::().unwrap()).to_vec() + ); } } From 7ac12718df83956151e3997238074f13c37e2f1f Mon Sep 17 00:00:00 2001 From: Zero Hero <0xzerohero@gmail.com> Date: Thu, 6 Jul 2023 14:30:03 +0300 Subject: [PATCH 8/9] chore(anvil): update docs --- anvil/src/cmd.rs | 2 +- anvil/src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/anvil/src/cmd.rs b/anvil/src/cmd.rs index 33ae134348e4f..bd001e16fd787 100644 --- a/anvil/src/cmd.rs +++ b/anvil/src/cmd.rs @@ -83,7 +83,7 @@ pub struct NodeArgs { #[clap(long, visible_alias = "no-mine", conflicts_with = "block-time")] pub no_mining: bool, - /// The host the server will listen on. + /// The hosts the server will listen on. #[clap( long, value_name = "IP_ADDR", diff --git a/anvil/src/lib.rs b/anvil/src/lib.rs index e5fa4d18d67c9..636d879fa7488 100644 --- a/anvil/src/lib.rs +++ b/anvil/src/lib.rs @@ -208,7 +208,7 @@ pub struct NodeHandle { addresses: Vec, /// Join handle for the Node Service pub node_service: JoinHandle>, - /// Join handle for the Anvil server + /// Join handles (one per socket) for the Anvil server. pub servers: Vec>>, // The future that joins the ipc server, if any ipc_task: Option, From 3af2d7b1d799febac1a5961b8db2d24ebd0f1dc0 Mon Sep 17 00:00:00 2001 From: Zero Hero <0xzerohero@gmail.com> Date: Fri, 7 Jul 2023 22:11:35 +0300 Subject: [PATCH 9/9] fix(anvil): use localhost if host is empty --- anvil/src/config.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/anvil/src/config.rs b/anvil/src/config.rs index 22649f0aaf4b4..be1c321151e9a 100644 --- a/anvil/src/config.rs +++ b/anvil/src/config.rs @@ -713,7 +713,7 @@ impl NodeConfig { /// Sets the host the server will listen on #[must_use] pub fn with_host(mut self, host: Vec) -> Self { - self.host = host; + self.host = if host.is_empty() { vec![IpAddr::V4(Ipv4Addr::LOCALHOST)] } else { host }; self }