use crate::spawn::node_spawner::NodeSpawner;
use crate::RunningNode;
use ant_evm::{EvmNetwork, RewardsAddress};
use libp2p::Multiaddr;
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use std::path::PathBuf;
pub struct NetworkSpawner {
evm_network: EvmNetwork,
rewards_address: RewardsAddress,
local: bool,
no_upnp: bool,
root_dir: Option<PathBuf>,
size: usize,
}
impl NetworkSpawner {
pub fn new() -> Self {
Self {
evm_network: Default::default(),
rewards_address: Default::default(),
local: false,
no_upnp: false,
root_dir: None,
size: 5,
}
}
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_local(mut self, value: bool) -> Self {
self.local = value;
self
}
pub fn with_no_upnp(mut self, value: bool) -> Self {
self.no_upnp = value;
self
}
pub fn with_root_dir(mut self, root_dir: Option<PathBuf>) -> Self {
self.root_dir = root_dir;
self
}
pub fn with_size(mut self, size: usize) -> Self {
self.size = size;
self
}
pub async fn spawn(self) -> eyre::Result<RunningNetwork> {
spawn_network(
self.evm_network,
self.rewards_address,
self.local,
self.no_upnp,
self.root_dir,
self.size,
)
.await
}
}
impl Default for NetworkSpawner {
fn default() -> Self {
Self::new()
}
}
pub struct RunningNetwork {
running_nodes: Vec<RunningNode>,
}
impl RunningNetwork {
pub async fn bootstrap_peer(&self) -> Multiaddr {
self.running_nodes()
.first()
.expect("No nodes running, cannot get bootstrap peer")
.get_listen_addrs_with_peer_id()
.await
.expect("Could not get listen addresses for bootstrap peer")
.last()
.expect("Bootstrap peer has no listen addresses")
.clone()
}
pub fn running_nodes(&self) -> &Vec<RunningNode> {
&self.running_nodes
}
pub fn shutdown(self) {
for node in self.running_nodes.into_iter() {
node.shutdown();
}
}
}
async fn spawn_network(
evm_network: EvmNetwork,
rewards_address: RewardsAddress,
local: bool,
no_upnp: bool,
root_dir: Option<PathBuf>,
size: usize,
) -> eyre::Result<RunningNetwork> {
let mut running_nodes: Vec<RunningNode> = vec![];
for i in 0..size {
let ip = match local {
true => IpAddr::V4(Ipv4Addr::LOCALHOST),
false => IpAddr::V4(Ipv4Addr::UNSPECIFIED),
};
let socket_addr = SocketAddr::new(ip, 0);
let mut initial_peers: Vec<Multiaddr> = vec![];
for peer in running_nodes.iter() {
if let Ok(listen_addrs_with_peer_id) = peer.get_listen_addrs_with_peer_id().await {
initial_peers.extend(listen_addrs_with_peer_id);
}
}
let node = NodeSpawner::new()
.with_socket_addr(socket_addr)
.with_evm_network(evm_network.clone())
.with_rewards_address(rewards_address)
.with_initial_peers(initial_peers)
.with_local(local)
.with_no_upnp(no_upnp)
.with_root_dir(root_dir.clone())
.spawn()
.await?;
let listen_addrs = node.get_listen_addrs().await;
info!(
"Spawned node #{} with listen addresses: {:?}",
i + 1,
listen_addrs
);
running_nodes.push(node);
}
Ok(RunningNetwork { running_nodes })
}
#[cfg(test)]
mod tests {
use super::*;
use std::time::Duration;
use tokio::time::sleep;
#[tokio::test(flavor = "multi_thread")]
async fn test_spawn_network() {
let network_size = 20;
let running_network = NetworkSpawner::new()
.with_evm_network(Default::default())
.with_local(true)
.with_no_upnp(true)
.with_size(network_size)
.spawn()
.await
.unwrap();
assert_eq!(running_network.running_nodes().len(), network_size);
sleep(Duration::from_secs(15)).await;
for node in running_network.running_nodes() {
let peers_in_routing_table = node
.get_swarm_local_state()
.await
.unwrap()
.peers_in_routing_table;
assert!(
peers_in_routing_table >= network_size - 2 && peers_in_routing_table < network_size
);
}
running_network.shutdown();
}
}