1use std::{
2 cmp::min,
3 collections::{HashMap, HashSet},
4 net::{IpAddr, SocketAddr, ToSocketAddrs},
5 sync::Arc,
6 time::{Duration, SystemTime},
7};
8
9use duration_string::DurationString;
10use futures_util::future::{join_all, try_join_all};
11use itertools::Itertools;
12use kaspa_addressmanager::{AddressManager, NetAddress};
13use kaspa_core::{debug, info, warn};
14use kaspa_p2p_lib::{common::ProtocolError, ConnectionError, Peer};
15use kaspa_utils::triggers::SingleTrigger;
16use parking_lot::Mutex as ParkingLotMutex;
17use rand::{seq::SliceRandom, thread_rng};
18use tokio::{
19 select,
20 sync::{
21 mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender},
22 Mutex as TokioMutex,
23 },
24 time::{interval, MissedTickBehavior},
25};
26
27pub struct ConnectionManager {
28 p2p_adaptor: Arc<kaspa_p2p_lib::Adaptor>,
29 outbound_target: usize,
30 inbound_limit: usize,
31 dns_seeders: &'static [&'static str],
32 default_port: u16,
33 address_manager: Arc<ParkingLotMutex<AddressManager>>,
34 connection_requests: TokioMutex<HashMap<SocketAddr, ConnectionRequest>>,
35 force_next_iteration: UnboundedSender<()>,
36 shutdown_signal: SingleTrigger,
37}
38
39#[derive(Clone, Debug)]
40struct ConnectionRequest {
41 next_attempt: SystemTime,
42 is_permanent: bool,
43 attempts: u32,
44}
45
46impl ConnectionRequest {
47 fn new(is_permanent: bool) -> Self {
48 Self { next_attempt: SystemTime::now(), is_permanent, attempts: 0 }
49 }
50}
51
52impl ConnectionManager {
53 pub fn new(
54 p2p_adaptor: Arc<kaspa_p2p_lib::Adaptor>,
55 outbound_target: usize,
56 inbound_limit: usize,
57 dns_seeders: &'static [&'static str],
58 default_port: u16,
59 address_manager: Arc<ParkingLotMutex<AddressManager>>,
60 ) -> Arc<Self> {
61 let (tx, rx) = unbounded_channel::<()>();
62 let manager = Arc::new(Self {
63 p2p_adaptor,
64 outbound_target,
65 inbound_limit,
66 address_manager,
67 connection_requests: Default::default(),
68 force_next_iteration: tx,
69 shutdown_signal: SingleTrigger::new(),
70 dns_seeders,
71 default_port,
72 });
73 manager.clone().start_event_loop(rx);
74 manager.force_next_iteration.send(()).unwrap();
75 manager
76 }
77
78 fn start_event_loop(self: Arc<Self>, mut rx: UnboundedReceiver<()>) {
79 let mut ticker = interval(Duration::from_secs(30));
80 ticker.set_missed_tick_behavior(MissedTickBehavior::Delay);
81 tokio::spawn(async move {
82 loop {
83 if self.shutdown_signal.trigger.is_triggered() {
84 break;
85 }
86 select! {
87 _ = rx.recv() => self.clone().handle_event().await,
88 _ = ticker.tick() => self.clone().handle_event().await,
89 _ = self.shutdown_signal.listener.clone() => break,
90 }
91 }
92 debug!("Connection manager event loop exiting");
93 });
94 }
95
96 async fn handle_event(self: Arc<Self>) {
97 debug!("Starting connection loop iteration");
98 let peers = self.p2p_adaptor.active_peers();
99 let peer_by_address: HashMap<SocketAddr, Peer> = peers.into_iter().map(|peer| (peer.net_address(), peer)).collect();
100
101 self.handle_connection_requests(&peer_by_address).await;
102 self.handle_outbound_connections(&peer_by_address).await;
103 self.handle_inbound_connections(&peer_by_address).await;
104 }
105
106 pub async fn add_connection_request(&self, address: SocketAddr, is_permanent: bool) {
107 self.connection_requests.lock().await.insert(address, ConnectionRequest::new(is_permanent));
109 self.force_next_iteration.send(()).unwrap(); }
111
112 pub async fn stop(&self) {
113 self.shutdown_signal.trigger.trigger()
114 }
115
116 async fn handle_connection_requests(self: &Arc<Self>, peer_by_address: &HashMap<SocketAddr, Peer>) {
117 let mut requests = self.connection_requests.lock().await;
118 let mut new_requests = HashMap::with_capacity(requests.len());
119 for (address, request) in requests.iter() {
120 let address = *address;
121 let request = request.clone();
122 let is_connected = peer_by_address.contains_key(&address);
123 if is_connected && !request.is_permanent {
124 continue;
126 }
127
128 if !is_connected && request.next_attempt <= SystemTime::now() {
129 debug!("Connecting to peer request {}", address);
130 match self.p2p_adaptor.connect_peer(address.to_string()).await {
131 Err(err) => {
132 debug!("Failed connecting to peer request: {}, {}", address, err);
133 if request.is_permanent {
134 const MAX_ACCOUNTABLE_ATTEMPTS: u32 = 4;
135 let retry_duration =
136 Duration::from_secs(30u64 * 2u64.pow(min(request.attempts, MAX_ACCOUNTABLE_ATTEMPTS)));
137 debug!("Will retry peer request {} in {}", address, DurationString::from(retry_duration));
138 new_requests.insert(
139 address,
140 ConnectionRequest {
141 next_attempt: SystemTime::now() + retry_duration,
142 attempts: request.attempts + 1,
143 is_permanent: true,
144 },
145 );
146 }
147 }
148 Ok(_) if request.is_permanent => {
149 new_requests.insert(address, ConnectionRequest::new(true));
151 }
152 Ok(_) => {}
153 }
154 } else {
155 new_requests.insert(address, request);
156 }
157 }
158
159 *requests = new_requests;
160 }
161
162 async fn handle_outbound_connections(self: &Arc<Self>, peer_by_address: &HashMap<SocketAddr, Peer>) {
163 let active_outbound: HashSet<kaspa_addressmanager::NetAddress> =
164 peer_by_address.values().filter(|peer| peer.is_outbound()).map(|peer| peer.net_address().into()).collect();
165 if active_outbound.len() >= self.outbound_target {
166 return;
167 }
168
169 let mut missing_connections = self.outbound_target - active_outbound.len();
170 let mut addr_iter = self.address_manager.lock().iterate_prioritized_random_addresses(active_outbound);
171
172 let mut progressing = true;
173 let mut connecting = true;
174 while connecting && missing_connections > 0 {
175 if self.shutdown_signal.trigger.is_triggered() {
176 return;
177 }
178 let mut addrs_to_connect = Vec::with_capacity(missing_connections);
179 let mut jobs = Vec::with_capacity(missing_connections);
180 for _ in 0..missing_connections {
181 let Some(net_addr) = addr_iter.next() else {
182 connecting = false;
183 break;
184 };
185 let socket_addr = SocketAddr::new(net_addr.ip.into(), net_addr.port).to_string();
186 debug!("Connecting to {}", &socket_addr);
187 addrs_to_connect.push(net_addr);
188 jobs.push(self.p2p_adaptor.connect_peer(socket_addr.clone()));
189 }
190
191 if progressing && !jobs.is_empty() {
192 info!(
194 "Connection manager: has {}/{} outgoing P2P connections, trying to obtain {} additional connection(s)...",
195 self.outbound_target - missing_connections,
196 self.outbound_target,
197 jobs.len(),
198 );
199 progressing = false;
200 } else {
201 debug!(
202 "Connection manager: outgoing: {}/{} , connecting: {}, iterator: {}",
203 self.outbound_target - missing_connections,
204 self.outbound_target,
205 jobs.len(),
206 addr_iter.len(),
207 );
208 }
209
210 for (res, net_addr) in (join_all(jobs).await).into_iter().zip(addrs_to_connect) {
211 match res {
212 Ok(_) => {
213 self.address_manager.lock().mark_connection_success(net_addr);
214 missing_connections -= 1;
215 progressing = true;
216 }
217 Err(ConnectionError::ProtocolError(ProtocolError::PeerAlreadyExists(_))) => {
218 debug!("Failed connecting to {:?}, peer already exists", net_addr);
220 }
221 Err(err) => {
222 debug!("Failed connecting to {:?}, err: {}", net_addr, err);
223 self.address_manager.lock().mark_connection_failure(net_addr);
224 }
225 }
226 }
227 }
228
229 if missing_connections > 0 && !self.dns_seeders.is_empty() {
230 if missing_connections > self.outbound_target / 2 {
231 self.dns_seed_many(self.dns_seeders.len()).await;
234 } else {
235 self.dns_seed_with_address_target(2 * missing_connections).await;
237 }
238 }
239 }
240
241 async fn handle_inbound_connections(self: &Arc<Self>, peer_by_address: &HashMap<SocketAddr, Peer>) {
242 let active_inbound = peer_by_address.values().filter(|peer| !peer.is_outbound()).collect_vec();
243 let active_inbound_len = active_inbound.len();
244 if self.inbound_limit >= active_inbound_len {
245 return;
246 }
247
248 let mut futures = Vec::with_capacity(active_inbound_len - self.inbound_limit);
249 for peer in active_inbound.choose_multiple(&mut thread_rng(), active_inbound_len - self.inbound_limit) {
250 debug!("Disconnecting from {} because we're above the inbound limit", peer.net_address());
251 futures.push(self.p2p_adaptor.terminate(peer.key()));
252 }
253 join_all(futures).await;
254 }
255
256 async fn dns_seed_with_address_target(self: &Arc<Self>, min_addresses_to_fetch: usize) {
258 let cmgr = self.clone();
259 tokio::task::spawn_blocking(move || cmgr.dns_seed_with_address_target_blocking(min_addresses_to_fetch)).await.unwrap();
260 }
261
262 fn dns_seed_with_address_target_blocking(self: &Arc<Self>, mut min_addresses_to_fetch: usize) {
263 let shuffled_dns_seeders = self.dns_seeders.choose_multiple(&mut thread_rng(), self.dns_seeders.len());
264 for &seeder in shuffled_dns_seeders {
265 let addrs_len = self.dns_seed_single(seeder);
267 if addrs_len >= min_addresses_to_fetch {
268 break;
269 } else {
270 min_addresses_to_fetch -= addrs_len;
271 }
272 }
273 }
274
275 async fn dns_seed_many(self: &Arc<Self>, num_seeders_to_query: usize) -> usize {
277 info!("Querying {} DNS seeders", num_seeders_to_query);
278 let shuffled_dns_seeders = self.dns_seeders.choose_multiple(&mut thread_rng(), num_seeders_to_query);
279 let jobs = shuffled_dns_seeders.map(|seeder| {
280 let cmgr = self.clone();
281 tokio::task::spawn_blocking(move || cmgr.dns_seed_single(seeder))
282 });
283 try_join_all(jobs).await.unwrap().into_iter().sum()
284 }
285
286 fn dns_seed_single(self: &Arc<Self>, seeder: &str) -> usize {
291 info!("Querying DNS seeder {}", seeder);
292 let addrs = match (seeder, self.default_port).to_socket_addrs() {
294 Ok(addrs) => addrs,
295 Err(e) => {
296 warn!("Error connecting to DNS seeder {}: {}", seeder, e);
297 return 0;
298 }
299 };
300
301 let addrs_len = addrs.len();
302 info!("Retrieved {} addresses from DNS seeder {}", addrs_len, seeder);
303 let mut amgr_lock = self.address_manager.lock();
304 for addr in addrs {
305 amgr_lock.add_address(NetAddress::new(addr.ip().into(), addr.port()));
306 }
307
308 addrs_len
309 }
310
311 pub async fn ban(&self, ip: IpAddr) {
315 if self.ip_has_permanent_connection(ip).await {
316 return;
317 }
318 for peer in self.p2p_adaptor.active_peers() {
319 if peer.net_address().ip() == ip {
320 self.p2p_adaptor.terminate(peer.key()).await;
321 }
322 }
323 self.address_manager.lock().ban(ip.into());
324 }
325
326 pub async fn is_banned(&self, address: &SocketAddr) -> bool {
328 !self.is_permanent(address).await && self.address_manager.lock().is_banned(address.ip().into())
329 }
330
331 pub async fn is_permanent(&self, address: &SocketAddr) -> bool {
333 self.connection_requests.lock().await.contains_key(address)
334 }
335
336 pub async fn ip_has_permanent_connection(&self, ip: IpAddr) -> bool {
338 self.connection_requests.lock().await.iter().any(|(address, request)| request.is_permanent && address.ip() == ip)
339 }
340}