ant_node/spawn/
node_spawner.rs1use crate::utils::get_root_dir_and_keypair;
10use crate::{NodeBuilder, RunningNode};
11use ant_bootstrap::{BootstrapConfig, bootstrap::Bootstrap};
12pub use ant_evm::{EvmNetwork, RewardsAddress};
13pub use libp2p::Multiaddr;
14use std::net::{IpAddr, Ipv4Addr, SocketAddr};
15use std::path::PathBuf;
16
17#[derive(Debug, Clone)]
18pub struct NodeSpawner {
19 socket_addr: SocketAddr,
21 evm_network: EvmNetwork,
23 rewards_address: RewardsAddress,
25 bootstrap_config: Option<BootstrapConfig>,
27 no_upnp: bool,
29 root_dir: Option<PathBuf>,
31}
32
33impl NodeSpawner {
34 pub fn new() -> Self {
36 Self {
37 socket_addr: SocketAddr::new(IpAddr::from(Ipv4Addr::UNSPECIFIED), 0),
38 evm_network: Default::default(),
39 rewards_address: Default::default(),
40 bootstrap_config: None,
41 no_upnp: false,
42 root_dir: None,
43 }
44 }
45
46 pub fn with_socket_addr(mut self, socket_addr: SocketAddr) -> Self {
52 self.socket_addr = socket_addr;
53 self
54 }
55
56 pub fn with_evm_network(mut self, evm_network: EvmNetwork) -> Self {
62 self.evm_network = evm_network;
63 self
64 }
65
66 pub fn with_rewards_address(mut self, rewards_address: RewardsAddress) -> Self {
72 self.rewards_address = rewards_address;
73 self
74 }
75
76 pub fn with_bootstrap_config(mut self, bootstrap_config: BootstrapConfig) -> Self {
82 self.bootstrap_config = Some(bootstrap_config);
83 self
84 }
85
86 pub fn with_no_upnp(mut self, no_upnp: bool) -> Self {
92 self.no_upnp = no_upnp;
93 self
94 }
95
96 pub fn with_root_dir(mut self, root_dir: Option<PathBuf>) -> Self {
102 self.root_dir = root_dir;
103 self
104 }
105
106 pub async fn spawn(self) -> eyre::Result<RunningNode> {
112 spawn_node(
113 self.socket_addr,
114 self.evm_network,
115 self.rewards_address,
116 self.bootstrap_config,
117 self.no_upnp,
118 &self.root_dir,
119 )
120 .await
121 }
122}
123
124impl Default for NodeSpawner {
125 fn default() -> Self {
126 Self::new()
127 }
128}
129
130async fn spawn_node(
131 socket_addr: SocketAddr,
132 evm_network: EvmNetwork,
133 rewards_address: RewardsAddress,
134 bootstrap_config: Option<BootstrapConfig>,
135 no_upnp: bool,
136 root_dir: &Option<PathBuf>,
137) -> eyre::Result<RunningNode> {
138 let (root_dir, keypair) = get_root_dir_and_keypair(root_dir)?;
139
140 let bootstrap_config = bootstrap_config.unwrap_or_default();
141 let local = bootstrap_config.local;
142 let bootstrap = Bootstrap::new(bootstrap_config).await?;
143
144 let mut node_builder = NodeBuilder::new(
145 keypair,
146 bootstrap,
147 rewards_address,
148 evm_network,
149 socket_addr,
150 root_dir,
151 );
152 node_builder.local(local);
153 node_builder.no_upnp(no_upnp);
154
155 let running_node = node_builder.build_and_run()?;
156
157 let mut retries: u8 = 0;
159
160 let listen_addrs: Vec<Multiaddr> = loop {
161 if let Ok(listen_addrs) = running_node.get_listen_addrs().await
163 && !listen_addrs.is_empty()
164 {
165 break Ok(listen_addrs);
166 }
167
168 if retries >= 3 {
169 break Err(eyre::eyre!(
170 "Failed to get listen addresses after {} retries",
171 retries
172 ));
173 }
174
175 retries += 1;
176
177 tokio::time::sleep(tokio::time::Duration::from_secs(retries as u64)).await;
178 }?;
179
180 info!("Node listening on addresses: {:?}", listen_addrs);
181
182 Ok(running_node)
183}
184
185#[cfg(test)]
186mod tests {
187 use super::*;
188 use ant_evm::EvmNetwork;
189 use futures::StreamExt;
190 use libp2p::swarm::dummy;
191
192 #[tokio::test]
193 async fn test_launch_node() {
194 let evm_network = EvmNetwork::ArbitrumSepoliaTest;
195
196 let bootstrap_config = BootstrapConfig::new(true)
197 .with_first(true)
198 .with_disable_cache_reading(true)
199 .with_disable_env_peers(true);
200
201 let running_node = NodeSpawner::new()
202 .with_evm_network(evm_network)
203 .with_bootstrap_config(bootstrap_config)
204 .spawn()
205 .await
206 .unwrap();
207
208 let listen_addrs = running_node.get_listen_addrs().await.unwrap();
209
210 assert!(!listen_addrs.is_empty());
211
212 let mut swarm = libp2p::SwarmBuilder::with_new_identity()
213 .with_tokio()
214 .with_quic()
215 .with_behaviour(|_| dummy::Behaviour)
216 .unwrap()
217 .build();
218
219 let address = listen_addrs.first().unwrap().clone();
220
221 assert!(swarm.dial(address).is_ok());
222 assert!(matches!(
223 swarm.next().await,
224 Some(libp2p::swarm::SwarmEvent::ConnectionEstablished { .. })
225 ));
226
227 running_node.shutdown();
228 }
229}