ant_node/spawn/
network_spawner.rs1use crate::RunningNode;
10use crate::spawn::node_spawner::NodeSpawner;
11use ant_bootstrap::BootstrapConfig;
12use ant_evm::{EvmNetwork, RewardsAddress};
13use libp2p::Multiaddr;
14use std::net::{IpAddr, Ipv4Addr, SocketAddr};
15use std::path::PathBuf;
16
17#[derive(Debug, Clone)]
18pub struct NetworkSpawner {
19 bootstrap_config: Option<BootstrapConfig>,
21 evm_network: EvmNetwork,
23 rewards_address: RewardsAddress,
25 no_upnp: bool,
27 root_dir: Option<PathBuf>,
29 size: usize,
31}
32
33impl NetworkSpawner {
34 pub fn new() -> Self {
44 Self {
45 evm_network: Default::default(),
46 rewards_address: Default::default(),
47 no_upnp: false,
48 root_dir: None,
49 size: 5,
50 bootstrap_config: None,
51 }
52 }
53
54 pub fn with_evm_network(mut self, evm_network: EvmNetwork) -> Self {
60 self.evm_network = evm_network;
61 self
62 }
63
64 pub fn with_rewards_address(mut self, rewards_address: RewardsAddress) -> Self {
70 self.rewards_address = rewards_address;
71 self
72 }
73
74 pub fn with_bootstrap_config(mut self, bootstrap_config: BootstrapConfig) -> Self {
80 self.bootstrap_config = Some(bootstrap_config);
81 self
82 }
83
84 pub fn with_no_upnp(mut self, value: bool) -> Self {
90 self.no_upnp = value;
91 self
92 }
93
94 pub fn with_root_dir(mut self, root_dir: Option<PathBuf>) -> Self {
100 self.root_dir = root_dir;
101 self
102 }
103
104 pub fn with_size(mut self, size: usize) -> Self {
110 self.size = size;
111 self
112 }
113
114 pub async fn spawn(self) -> eyre::Result<RunningNetwork> {
121 spawn_network(
122 self.evm_network,
123 self.rewards_address,
124 self.no_upnp,
125 self.root_dir,
126 self.size,
127 self.bootstrap_config,
128 )
129 .await
130 }
131}
132
133impl Default for NetworkSpawner {
134 fn default() -> Self {
135 Self::new()
136 }
137}
138
139pub struct RunningNetwork {
140 running_nodes: Vec<RunningNode>,
141}
142
143impl RunningNetwork {
144 pub async fn bootstrap_peer(&self) -> Multiaddr {
146 self.running_nodes()
147 .first()
148 .expect("No nodes running, cannot get bootstrap peer")
149 .get_listen_addrs_with_peer_id()
150 .await
151 .expect("Could not get listen addresses for bootstrap peer")
152 .last()
153 .expect("Bootstrap peer has no listen addresses")
154 .clone()
155 }
156
157 pub fn running_nodes(&self) -> &Vec<RunningNode> {
159 &self.running_nodes
160 }
161
162 pub fn shutdown(self) {
164 for node in self.running_nodes.into_iter() {
165 node.shutdown();
166 }
167 }
168}
169
170async fn spawn_network(
171 evm_network: EvmNetwork,
172 rewards_address: RewardsAddress,
173 no_upnp: bool,
174 root_dir: Option<PathBuf>,
175 size: usize,
176 bootstrap_config: Option<BootstrapConfig>,
177) -> eyre::Result<RunningNetwork> {
178 let mut running_nodes: Vec<RunningNode> = vec![];
179
180 let local = bootstrap_config.as_ref().map(|c| c.local).unwrap_or(false);
182
183 for i in 0..size {
184 let ip = match local {
185 true => IpAddr::V4(Ipv4Addr::LOCALHOST),
186 false => IpAddr::V4(Ipv4Addr::UNSPECIFIED),
187 };
188
189 let socket_addr = SocketAddr::new(ip, 0);
190
191 let mut initial_peers: Vec<Multiaddr> = vec![];
193
194 for peer in running_nodes.iter() {
195 if let Ok(listen_addrs_with_peer_id) = peer.get_listen_addrs_with_peer_id().await {
196 initial_peers.extend(listen_addrs_with_peer_id);
197 }
198 }
199
200 let mut node_bootstrap_config = bootstrap_config.clone().unwrap_or_default();
202 node_bootstrap_config.initial_peers.extend(initial_peers);
203 node_bootstrap_config.first = running_nodes.is_empty();
204 node_bootstrap_config.local = local;
205
206 let node = NodeSpawner::new()
207 .with_socket_addr(socket_addr)
208 .with_evm_network(evm_network.clone())
209 .with_rewards_address(rewards_address)
210 .with_bootstrap_config(node_bootstrap_config)
211 .with_no_upnp(no_upnp)
212 .with_root_dir(root_dir.clone())
213 .spawn()
214 .await?;
215
216 let listen_addrs = node.get_listen_addrs().await;
217
218 info!(
219 "Spawned node #{} with listen addresses: {:?}",
220 i + 1,
221 listen_addrs
222 );
223
224 running_nodes.push(node);
225 }
226
227 Ok(RunningNetwork { running_nodes })
228}
229
230#[cfg(test)]
231mod tests {
232 use super::*;
233 use std::time::Duration;
234 use tokio::time::sleep;
235
236 #[tokio::test(flavor = "multi_thread")]
237 async fn test_spawn_network() {
238 let network_size = 20;
239
240 let bootstrap_config = BootstrapConfig::new(true)
241 .with_disable_cache_reading(true)
242 .with_disable_env_peers(true);
243
244 let running_network = NetworkSpawner::new()
245 .with_evm_network(Default::default())
246 .with_bootstrap_config(bootstrap_config)
247 .with_no_upnp(true)
248 .with_size(network_size)
249 .spawn()
250 .await
251 .unwrap();
252
253 assert_eq!(running_network.running_nodes().len(), network_size);
254
255 sleep(Duration::from_secs(15)).await;
257
258 for node in running_network.running_nodes() {
260 let peers_in_routing_table = node
261 .get_swarm_local_state()
262 .await
263 .unwrap()
264 .peers_in_routing_table;
265
266 assert!(
267 peers_in_routing_table >= network_size - 2 && peers_in_routing_table < network_size,
268 "Node with PeerId {} has {} peers in its routing table, expected between {} and {}",
269 node.peer_id(),
270 peers_in_routing_table,
271 network_size - 2,
272 network_size - 1
273 );
274 }
275
276 running_network.shutdown();
277 }
278}