use crate::utils::get_root_dir_and_keypair;
use crate::{NodeBuilder, RunningNode};
use ant_evm::{EvmNetwork, RewardsAddress};
use libp2p::Multiaddr;
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use std::path::PathBuf;
pub struct NodeSpawner {
socket_addr: SocketAddr,
evm_network: EvmNetwork,
rewards_address: RewardsAddress,
initial_peers: Vec<Multiaddr>,
local: bool,
no_upnp: bool,
root_dir: Option<PathBuf>,
}
impl NodeSpawner {
pub fn new() -> Self {
Self {
socket_addr: SocketAddr::new(IpAddr::from(Ipv4Addr::UNSPECIFIED), 0),
evm_network: Default::default(),
rewards_address: Default::default(),
initial_peers: vec![],
local: false,
no_upnp: false,
root_dir: None,
}
}
pub fn with_socket_addr(mut self, socket_addr: SocketAddr) -> Self {
self.socket_addr = socket_addr;
self
}
pub fn with_evm_network(mut self, evm_network: EvmNetwork) -> Self {
self.evm_network = evm_network;
self
}
pub fn with_rewards_address(mut self, rewards_address: RewardsAddress) -> Self {
self.rewards_address = rewards_address;
self
}
pub fn with_initial_peers(mut self, initial_peers: Vec<Multiaddr>) -> Self {
self.initial_peers = initial_peers;
self
}
pub fn with_local(mut self, local: bool) -> Self {
self.local = local;
self
}
pub fn with_no_upnp(mut self, no_upnp: bool) -> Self {
self.no_upnp = no_upnp;
self
}
pub fn with_root_dir(mut self, root_dir: Option<PathBuf>) -> Self {
self.root_dir = root_dir;
self
}
pub async fn spawn(self) -> eyre::Result<RunningNode> {
spawn_node(
self.socket_addr,
self.evm_network,
self.rewards_address,
self.initial_peers,
self.local,
self.no_upnp,
&self.root_dir,
)
.await
}
}
impl Default for NodeSpawner {
fn default() -> Self {
Self::new()
}
}
async fn spawn_node(
socket_addr: SocketAddr,
evm_network: EvmNetwork,
rewards_address: RewardsAddress,
initial_peers: Vec<Multiaddr>,
local: bool,
no_upnp: bool,
root_dir: &Option<PathBuf>,
) -> eyre::Result<RunningNode> {
let (root_dir, keypair) = get_root_dir_and_keypair(root_dir)?;
let mut node_builder = NodeBuilder::new(
keypair,
initial_peers,
rewards_address,
evm_network,
socket_addr,
root_dir,
);
node_builder.local(local);
node_builder.no_upnp(no_upnp);
let running_node = node_builder.build_and_run()?;
let mut retries: u8 = 0;
let listen_addrs: Vec<Multiaddr> = loop {
if let Ok(listen_addrs) = running_node.get_listen_addrs().await {
if !listen_addrs.is_empty() {
break Ok(listen_addrs);
}
}
if retries >= 3 {
break Err(eyre::eyre!(
"Failed to get listen addresses after {} retries",
retries
));
}
retries += 1;
tokio::time::sleep(tokio::time::Duration::from_secs(retries as u64)).await;
}?;
info!("Node listening on addresses: {:?}", listen_addrs);
Ok(running_node)
}
#[cfg(test)]
mod tests {
use super::*;
use ant_evm::EvmNetwork;
use futures::StreamExt;
use libp2p::swarm::dummy;
#[tokio::test]
async fn test_launch_node() {
let evm_network = EvmNetwork::ArbitrumSepoliaTest;
let running_node = NodeSpawner::new()
.with_evm_network(evm_network)
.with_local(true)
.spawn()
.await
.unwrap();
let listen_addrs = running_node.get_listen_addrs().await.unwrap();
assert!(!listen_addrs.is_empty());
let mut swarm = libp2p::SwarmBuilder::with_new_identity()
.with_tokio()
.with_quic()
.with_behaviour(|_| dummy::Behaviour)
.unwrap()
.build();
let address = listen_addrs.first().unwrap().clone();
assert!(swarm.dial(address).is_ok());
assert!(matches!(
swarm.next().await,
Some(libp2p::swarm::SwarmEvent::ConnectionEstablished { .. })
));
running_node.shutdown();
}
}