Skip to main content

forest/libp2p/
service.rs

1// Copyright 2019-2026 ChainSafe Systems
2// SPDX-License-Identifier: Apache-2.0, MIT
3
4use std::time::{Duration, SystemTime, UNIX_EPOCH};
5
6use crate::prelude::*;
7use crate::{blocks::GossipBlock, rpc::net::NetInfoResult};
8use crate::{chain::ChainStore, utils::encoding::from_slice_with_fallback};
9use crate::{
10    libp2p_bitswap::{BitswapStoreReadWrite, request_manager::BitswapRequestManager},
11    utils::flume::FlumeSenderExt as _,
12};
13use crate::{message::SignedMessage, networks::GenesisNetworkName};
14use ahash::{HashMap, HashSet};
15use anyhow::Context as _;
16use flume::Sender;
17use futures::{select, stream::StreamExt as _};
18pub use libp2p::gossipsub::{IdentTopic, Topic};
19use libp2p::{
20    PeerId, Swarm, SwarmBuilder,
21    autonat::NatStatus,
22    connection_limits::Exceeded,
23    core::Multiaddr,
24    gossipsub, identify,
25    identity::Keypair,
26    metrics::{Metrics, Recorder},
27    multiaddr::Protocol,
28    noise, ping, request_response,
29    swarm::{DialError, SwarmEvent},
30    tcp, yamux,
31};
32use nonzero_ext::nonzero;
33use tokio_stream::wrappers::IntervalStream;
34use tracing::{debug, error, info, trace, warn};
35
36use super::{
37    ForestBehaviour, ForestBehaviourEvent, Libp2pConfig,
38    chain_exchange::{ChainExchangeRequest, ChainExchangeResponse, make_chain_exchange_response},
39    discovery::{DerivedDiscoveryBehaviourEvent, PeerInfo},
40};
41use crate::libp2p::{
42    PeerManager, PeerOperation,
43    chain_exchange::ChainExchangeBehaviour,
44    discovery::DiscoveryEvent,
45    hello::{HelloBehaviour, HelloRequest, HelloResponse},
46    rpc::RequestResponseError,
47};
48
49pub(in crate::libp2p) mod metrics {
50    use prometheus_client::metrics::{family::Family, gauge::Gauge};
51    use std::sync::LazyLock;
52
53    use crate::metrics::KindLabel;
54
55    pub static NETWORK_CONTAINER_CAPACITIES: LazyLock<Family<KindLabel, Gauge>> = {
56        LazyLock::new(|| {
57            let metric = Family::default();
58            crate::metrics::default_registry().register(
59                "network_container_capacities",
60                "Capacity for each container",
61                metric.clone(),
62            );
63            metric
64        })
65    };
66
67    pub mod values {
68        use crate::metrics::KindLabel;
69
70        pub const HELLO_REQUEST_TABLE: KindLabel = KindLabel::new("hello_request_table");
71        pub const CHAIN_EXCHANGE_REQUEST_TABLE: KindLabel = KindLabel::new("cx_request_table");
72    }
73}
74
75crate::def_is_env_truthy!(libp2p_metrics_enabled, "FOREST_LIBP2P_METRICS_ENABLED");
76
77/// `Gossipsub` Filecoin blocks topic identifier.
78pub const PUBSUB_BLOCK_STR: &str = "/fil/blocks";
79/// `Gossipsub` Filecoin messages topic identifier.
80pub const PUBSUB_MSG_STR: &str = "/fil/msgs";
81
82const PUBSUB_TOPICS: [&str; 2] = [PUBSUB_BLOCK_STR, PUBSUB_MSG_STR];
83
84pub const BITSWAP_TIMEOUT: Duration = Duration::from_secs(30);
85
86/// Events emitted by this Service.
87#[allow(clippy::large_enum_variant)]
88#[derive(Debug)]
89pub enum NetworkEvent {
90    PubsubMessage {
91        message: PubsubMessage,
92    },
93    HelloRequestInbound,
94    HelloResponseOutbound {
95        source: PeerId,
96        request: HelloRequest,
97    },
98    HelloRequestOutbound,
99    HelloResponseInbound,
100    ChainExchangeRequestOutbound,
101    ChainExchangeResponseInbound,
102    ChainExchangeRequestInbound,
103    ChainExchangeResponseOutbound,
104    PeerConnected(PeerId),
105    PeerDisconnected(PeerId),
106}
107
108/// Message types that can come over `GossipSub`
109#[allow(clippy::large_enum_variant)]
110#[derive(Debug, Clone)]
111pub enum PubsubMessage {
112    /// Messages that come over the block topic
113    Block(GossipBlock),
114    /// Messages that come over the message topic
115    Message(SignedMessage),
116}
117
118/// Messages into the service to handle.
119#[derive(Debug)]
120pub enum NetworkMessage {
121    PubsubMessage {
122        topic: IdentTopic,
123        message: Vec<u8>,
124    },
125    ChainExchangeRequest {
126        peer_id: PeerId,
127        request: ChainExchangeRequest,
128        response_channel: flume::Sender<Result<ChainExchangeResponse, RequestResponseError>>,
129    },
130    HelloRequest {
131        peer_id: PeerId,
132        request: HelloRequest,
133        response_channel: flume::Sender<HelloResponse>,
134    },
135    BitswapRequest {
136        cid: Cid,
137        response_channel: flume::Sender<bool>,
138    },
139    JSONRPCRequest {
140        method: NetRPCMethods,
141    },
142}
143
144/// Network RPC API methods used to gather data from libp2p node.
145#[derive(Debug)]
146pub enum NetRPCMethods {
147    AddrsListen(flume::Sender<(PeerId, HashSet<Multiaddr>)>),
148    Peer(flume::Sender<Option<HashSet<Multiaddr>>>, PeerId),
149    Peers(flume::Sender<HashMap<PeerId, HashSet<Multiaddr>>>),
150    ProtectPeer(flume::Sender<()>, HashSet<PeerId>),
151    UnprotectPeer(flume::Sender<()>, HashSet<PeerId>),
152    ListProtectedPeers(flume::Sender<HashSet<PeerId>>),
153    Info(flume::Sender<NetInfoResult>),
154    Connect(flume::Sender<bool>, PeerId, HashSet<Multiaddr>),
155    Disconnect(flume::Sender<()>, PeerId),
156    AgentVersion(flume::Sender<Option<String>>, PeerId),
157    AutoNATStatus(flume::Sender<NatStatus>),
158}
159
160/// The `Libp2pService` listens to events from the libp2p swarm.
161pub struct Libp2pService {
162    swarm: Swarm<ForestBehaviour>,
163    bootstrap_peers: HashMap<PeerId, Multiaddr>,
164    cs: ChainStore,
165    peer_manager: Arc<PeerManager>,
166    network_receiver_in: flume::Receiver<NetworkMessage>,
167    network_sender_in: Sender<NetworkMessage>,
168    network_receiver_out: flume::Receiver<NetworkEvent>,
169    network_sender_out: Sender<NetworkEvent>,
170    network_name: String,
171    genesis_cid: Cid,
172}
173
174impl Libp2pService {
175    pub async fn new(
176        config: Libp2pConfig,
177        cs: ChainStore,
178        peer_manager: Arc<PeerManager>,
179        net_keypair: Keypair,
180        network_name: GenesisNetworkName,
181        genesis_cid: Cid,
182    ) -> anyhow::Result<Self> {
183        let behaviour =
184            ForestBehaviour::new(&net_keypair, &config, &network_name, peer_manager.clone())
185                .await?;
186        let mut swarm = SwarmBuilder::with_existing_identity(net_keypair)
187            .with_tokio()
188            .with_tcp(
189                tcp::Config::default().nodelay(true),
190                noise::Config::new,
191                yamux::Config::default,
192            )?
193            .with_quic()
194            .with_dns()?
195            .with_bandwidth_metrics(&mut crate::metrics::collector_registry())
196            .with_behaviour(|_| behaviour)?
197            .with_swarm_config(|config| {
198                config
199                    .with_notify_handler_buffer_size(nonzero!(20usize))
200                    .with_per_connection_event_buffer_size(64)
201                    .with_idle_connection_timeout(Duration::from_secs(60 * 10))
202            })
203            .build();
204
205        // Subscribe to gossipsub topics with the network name suffix
206        for topic in PUBSUB_TOPICS.iter() {
207            let t = Topic::new(format!("{topic}/{network_name}"));
208            swarm
209                .behaviour_mut()
210                .subscribe(&t)
211                .with_context(|| format!("Failed to subscribe gossipsub topic {t}"))?;
212        }
213
214        let (network_sender_in, network_receiver_in) = flume::unbounded();
215        let (network_sender_out, network_receiver_out) = flume::unbounded();
216
217        // Hint at the multihash which has to go in the `/p2p/<multihash>` part of the
218        // peer's multiaddress. Useful if others want to use this node to bootstrap
219        // from.
220        info!("p2p network peer id: {}", swarm.local_peer_id());
221
222        // Listen on network endpoints before being detached and connecting to any peers.
223        for addr in &config.listening_multiaddrs {
224            match swarm.listen_on(addr.clone()) {
225                Ok(id) => loop {
226                    if let SwarmEvent::NewListenAddr {
227                        address,
228                        listener_id,
229                    } = swarm.select_next_some().await
230                        && id == listener_id
231                    {
232                        info!("p2p peer is now listening on: {address}");
233                        break;
234                    }
235                },
236                Err(err) => error!("Fail to listen on {addr}: {err}"),
237            }
238        }
239
240        if swarm.listeners().count() == 0 {
241            anyhow::bail!("p2p peer failed to listen on any network endpoints");
242        }
243
244        let bootstrap_peers = config
245            .bootstrap_peers
246            .iter()
247            .filter_map(|ma| match ma.iter().last() {
248                Some(Protocol::P2p(peer)) => Some((peer, ma.clone())),
249                _ => None,
250            })
251            .collect();
252
253        Ok(Libp2pService {
254            swarm,
255            bootstrap_peers,
256            cs,
257            peer_manager,
258            network_receiver_in,
259            network_sender_in,
260            network_receiver_out,
261            network_sender_out,
262            network_name: network_name.into(),
263            genesis_cid,
264        })
265    }
266
267    /// Starts the libp2p service networking stack. This Future resolves when
268    /// shutdown occurs.
269    pub async fn run(mut self) -> anyhow::Result<()> {
270        info!("Running libp2p service");
271
272        // Bootstrap with Kademlia
273        if let Err(e) = self.swarm.behaviour_mut().bootstrap() {
274            warn!("Failed to bootstrap with Kademlia: {e:#}");
275        }
276
277        let bitswap_request_manager = self.swarm.behaviour().bitswap.request_manager();
278        let mut swarm_stream = self.swarm.fuse();
279        let mut network_stream = self.network_receiver_in.stream().fuse();
280        let mut interval =
281            IntervalStream::new(tokio::time::interval(Duration::from_secs(15))).fuse();
282        let pubsub_block_str = format!("{}/{}", PUBSUB_BLOCK_STR, self.network_name);
283        let pubsub_msg_str = format!("{}/{}", PUBSUB_MSG_STR, self.network_name);
284
285        let (cx_response_tx, cx_response_rx) = flume::unbounded();
286
287        let mut cx_response_rx_stream = cx_response_rx.stream().fuse();
288        let mut bitswap_outbound_request_stream =
289            bitswap_request_manager.outbound_request_stream().fuse();
290        let mut peer_ops_rx_stream = self.peer_manager.peer_ops_rx().stream().fuse();
291        let metrics = if libp2p_metrics_enabled() {
292            Some(Metrics::new(&mut crate::metrics::collector_registry()))
293        } else {
294            None
295        };
296
297        const BOOTSTRAP_PEER_DIALER_INTERVAL: tokio::time::Duration =
298            tokio::time::Duration::from_secs(60);
299        let mut bootstrap_peer_dialer_interval_stream =
300            IntervalStream::new(tokio::time::interval_at(
301                tokio::time::Instant::now() + BOOTSTRAP_PEER_DIALER_INTERVAL,
302                BOOTSTRAP_PEER_DIALER_INTERVAL,
303            ))
304            .fuse();
305        loop {
306            select! {
307                swarm_event = swarm_stream.next() => match swarm_event {
308                    // outbound events
309                    Some(SwarmEvent::Behaviour(event)) => {
310                        if let Some(m) = &metrics {
311                            m.record(&event);
312                        }
313                        handle_forest_behaviour_event(
314                            swarm_stream.get_mut(),
315                            &bitswap_request_manager,
316                            &self.peer_manager,
317                            event,
318                            &self.cs,
319                            &self.genesis_cid,
320                            &self.network_sender_out,
321                            cx_response_tx.clone(),
322                            &pubsub_block_str,
323                            &pubsub_msg_str,).await;
324                    },
325                    None => { break; },
326                    _ => { },
327                },
328                rpc_message = network_stream.next() => match rpc_message {
329                    // Inbound messages
330                    Some(message) => {
331                        handle_network_message(
332                            swarm_stream.get_mut(),
333                            self.cs.db_owned(),
334                            bitswap_request_manager.shallow_clone(),
335                            message,
336                            &self.network_sender_out,
337                            &self.peer_manager).await;
338                    }
339                    None => { break; }
340                },
341                interval_event = interval.next() => if interval_event.is_some() {
342                    // Print peer count on an interval.
343                    trace!("Peers connected: {}", swarm_stream.get_mut().behaviour_mut().peers().len());
344                },
345                cs_pair_opt = cx_response_rx_stream.next() => {
346                    if let Some((_request_id, channel, cx_response)) = cs_pair_opt {
347                        let behaviour = swarm_stream.get_mut().behaviour_mut();
348                        if let Err(e) = behaviour.chain_exchange.send_response(channel, cx_response) {
349                            debug!("Error sending chain exchange response: {e:?}");
350                        }
351                    }
352                },
353                bitswap_outbound_request_opt = bitswap_outbound_request_stream.next() => {
354                    if let Some((peer, request)) = bitswap_outbound_request_opt {
355                        let bitswap = &mut swarm_stream.get_mut().behaviour_mut().bitswap;
356                        bitswap.send_request(&peer, request);
357                    }
358                }
359                peer_ops_opt = peer_ops_rx_stream.next() => {
360                    if let Some(peer_ops) = peer_ops_opt {
361                        handle_peer_ops(swarm_stream.get_mut(), peer_ops, &self.bootstrap_peers);
362                    }
363                },
364                _ = bootstrap_peer_dialer_interval_stream.next() => {
365                    dial_to_bootstrap_peers_if_needed(swarm_stream.get_mut(), &self.bootstrap_peers);
366                }
367            };
368        }
369        Ok(())
370    }
371
372    /// Returns a sender which allows sending messages to the libp2p service.
373    pub fn network_sender(&self) -> Sender<NetworkMessage> {
374        self.network_sender_in.clone()
375    }
376
377    /// Returns a receiver to listen to network events emitted from the service.
378    pub fn network_receiver(&self) -> flume::Receiver<NetworkEvent> {
379        self.network_receiver_out.clone()
380    }
381
382    pub fn peer_manager(&self) -> &Arc<PeerManager> {
383        &self.peer_manager
384    }
385}
386
387fn dial_to_bootstrap_peers_if_needed(
388    swarm: &mut Swarm<ForestBehaviour>,
389    bootstrap_peers: &HashMap<PeerId, Multiaddr>,
390) {
391    for (peer, ma) in bootstrap_peers {
392        if !swarm.behaviour().peers().contains(peer) {
393            info!("Re-dialing to bootstrap peer at {ma}");
394            if let Err(e) = swarm.dial(ma.clone()) {
395                warn!("{e}");
396            }
397        }
398    }
399}
400
401fn handle_peer_ops(
402    swarm: &mut Swarm<ForestBehaviour>,
403    peer_ops: PeerOperation,
404    bootstrap_peers: &HashMap<PeerId, Multiaddr>,
405) {
406    use PeerOperation::*;
407    match peer_ops {
408        Ban {
409            peer,
410            user_agent,
411            reason,
412        } => {
413            // Do not ban bootstrap nodes
414            if !bootstrap_peers.contains_key(&peer) {
415                let user_agent = user_agent.unwrap_or_default();
416                debug!(%peer, %user_agent, %reason, "Banning peer");
417                swarm.behaviour_mut().blocked_peers.block_peer(peer);
418            }
419        }
420        Unban(peer) => {
421            debug!(%peer, "Unbanning peer");
422            swarm.behaviour_mut().blocked_peers.unblock_peer(peer);
423        }
424    }
425}
426
427async fn handle_network_message(
428    swarm: &mut Swarm<ForestBehaviour>,
429    store: impl BitswapStoreReadWrite + ShallowClone,
430    bitswap_request_manager: Arc<BitswapRequestManager>,
431    message: NetworkMessage,
432    network_sender_out: &Sender<NetworkEvent>,
433    peer_manager: &PeerManager,
434) {
435    match message {
436        NetworkMessage::PubsubMessage { topic, message } => {
437            match swarm.behaviour_mut().publish(topic, message) {
438                Ok(_) => (),
439                Err(gossipsub::PublishError::Duplicate) => {
440                    // Safe to ignore, the message has already been published and deduped by gossipsub, so no need to log this as a warning.
441                    // This matches `go-libp2p-pubsub` behavior https://github.com/libp2p/go-libp2p-pubsub/blob/v0.15.0/topic.go#L240-L242
442                    debug!("Failed to send gossipsub message: duplicate");
443                }
444                Err(e) => {
445                    warn!("Failed to send gossipsub message: {e:#}");
446                }
447            }
448        }
449        NetworkMessage::HelloRequest {
450            peer_id,
451            request,
452            response_channel,
453        } => {
454            let _request_id =
455                swarm
456                    .behaviour_mut()
457                    .hello
458                    .send_request(&peer_id, request, response_channel);
459            emit_event(network_sender_out, NetworkEvent::HelloRequestOutbound).await;
460        }
461        NetworkMessage::ChainExchangeRequest {
462            peer_id,
463            request,
464            response_channel,
465        } => {
466            let _request_id = swarm.behaviour_mut().chain_exchange.send_request(
467                &peer_id,
468                request,
469                response_channel,
470            );
471            emit_event(
472                network_sender_out,
473                NetworkEvent::ChainExchangeRequestOutbound,
474            )
475            .await;
476        }
477        NetworkMessage::BitswapRequest {
478            cid,
479            response_channel,
480        } => {
481            bitswap_request_manager.get_block(
482                store,
483                cid,
484                BITSWAP_TIMEOUT,
485                Some(response_channel),
486                None,
487            );
488        }
489        NetworkMessage::JSONRPCRequest { method } => {
490            match method {
491                NetRPCMethods::AddrsListen(response_channel) => {
492                    let listeners = Swarm::listeners(swarm).cloned().collect();
493                    let peer_id = Swarm::local_peer_id(swarm);
494                    response_channel.send_or_warn((*peer_id, listeners));
495                }
496                NetRPCMethods::Peer(response_channel, peer) => {
497                    let addresses = swarm.behaviour().peer_addresses().get(&peer).cloned();
498                    response_channel.send_or_warn(addresses);
499                }
500                NetRPCMethods::Peers(response_channel) => {
501                    let peer_addresses = swarm.behaviour().peer_addresses();
502                    response_channel.send_or_warn(peer_addresses);
503                }
504                NetRPCMethods::ProtectPeer(tx, peer_ids) => {
505                    peer_ids.into_iter().for_each(|peer_id| {
506                        peer_manager.protect_peer(peer_id);
507                    });
508                    tx.send_or_warn(());
509                }
510                NetRPCMethods::ListProtectedPeers(tx) => {
511                    tx.send_or_warn(peer_manager.list_protected_peers());
512                }
513                NetRPCMethods::UnprotectPeer(tx, peer_ids) => {
514                    peer_ids.iter().for_each(|peer_id| {
515                        peer_manager.unprotect_peer(peer_id);
516                    });
517                    tx.send_or_warn(());
518                }
519                NetRPCMethods::Info(response_channel) => {
520                    response_channel.send_or_warn(swarm.network_info().into());
521                }
522                NetRPCMethods::Connect(response_channel, peer_id, addresses) => {
523                    let mut success = false;
524                    for mut multiaddr in addresses {
525                        multiaddr.push(Protocol::P2p(peer_id));
526
527                        match Swarm::dial(swarm, multiaddr.clone()) {
528                            Ok(_) => {
529                                info!("Dialed {multiaddr}");
530                                success = true;
531                                break;
532                            }
533                            Err(e) => {
534                                match e {
535                                    DialError::Denied { cause } => {
536                                        // try to get a more specific error cause
537                                        if let Some(cause) = cause.downcast_ref::<Exceeded>() {
538                                            error!(
539                                                "Denied dialing (limits exceeded) {multiaddr}: {cause}"
540                                            );
541                                        } else {
542                                            error!("Denied dialing {multiaddr}: {cause}")
543                                        }
544                                    }
545                                    e => {
546                                        error!("Failed to dial {multiaddr}: {e}");
547                                    }
548                                };
549                            }
550                        };
551                    }
552
553                    response_channel.send_or_warn(success);
554                }
555                NetRPCMethods::Disconnect(response_channel, peer_id) => {
556                    let _ = Swarm::disconnect_peer_id(swarm, peer_id);
557                    response_channel.send_or_warn(());
558                }
559                NetRPCMethods::AgentVersion(response_channel, peer_id) => {
560                    let agent_version = swarm.behaviour().peer_info(&peer_id).and_then(|info| {
561                        info.identify_info
562                            .as_ref()
563                            .map(|id| id.agent_version.clone())
564                    });
565                    response_channel.send_or_warn(agent_version);
566                }
567                NetRPCMethods::AutoNATStatus(response_channel) => {
568                    let nat_status = swarm.behaviour().discovery.nat_status();
569                    response_channel.send_or_warn(nat_status);
570                }
571            }
572        }
573    }
574}
575
576async fn handle_discovery_event(
577    peer_info_map: &HashMap<PeerId, PeerInfo>,
578    discovery_out: DiscoveryEvent,
579    network_sender_out: &Sender<NetworkEvent>,
580    peer_manager: &PeerManager,
581) {
582    match discovery_out {
583        DiscoveryEvent::PeerConnected(peer_id) => {
584            trace!("Peer connected, {peer_id}");
585            emit_event(network_sender_out, NetworkEvent::PeerConnected(peer_id)).await;
586        }
587        DiscoveryEvent::PeerDisconnected(peer_id) => {
588            trace!("Peer disconnected, {peer_id}");
589            emit_event(network_sender_out, NetworkEvent::PeerDisconnected(peer_id)).await;
590        }
591        DiscoveryEvent::Discovery(discovery_event) => match &*discovery_event {
592            DerivedDiscoveryBehaviourEvent::Identify(identify::Event::Received {
593                peer_id,
594                info,
595                ..
596            }) => {
597                let protocols = HashSet::from_iter(info.protocols.iter().map(|p| p.to_string()));
598                if !protocols.contains(super::hello::HELLO_PROTOCOL_NAME) {
599                    peer_manager
600                        .ban_peer_with_default_duration(
601                            *peer_id,
602                            "hello protocol unsupported",
603                            |p| get_user_agent(peer_info_map, p),
604                        )
605                        .await;
606                } else if !protocols.contains(super::chain_exchange::CHAIN_EXCHANGE_PROTOCOL_NAME) {
607                    peer_manager
608                        .ban_peer_with_default_duration(
609                            *peer_id,
610                            "chain exchange protocol unsupported",
611                            |p| get_user_agent(peer_info_map, p),
612                        )
613                        .await;
614                }
615            }
616            DerivedDiscoveryBehaviourEvent::Identify(_) => {}
617            _ => {}
618        },
619    }
620}
621
622async fn handle_gossip_event(
623    e: gossipsub::Event,
624    network_sender_out: &Sender<NetworkEvent>,
625    pubsub_block_str: &str,
626    pubsub_msg_str: &str,
627) {
628    if let gossipsub::Event::Message {
629        propagation_source: source,
630        message,
631        ..
632    } = e
633    {
634        let topic = message.topic.as_str();
635        let message = message.data;
636        trace!("Got a Gossip Message from {:?}", source);
637        if topic == pubsub_block_str {
638            match from_slice_with_fallback::<GossipBlock>(&message) {
639                Ok(b) => {
640                    emit_event(
641                        network_sender_out,
642                        NetworkEvent::PubsubMessage {
643                            message: PubsubMessage::Block(b),
644                        },
645                    )
646                    .await;
647                }
648                Err(e) => {
649                    warn!("Gossip Block from peer {source:?} could not be deserialized: {e:#}",);
650                }
651            }
652        } else if topic == pubsub_msg_str {
653            match from_slice_with_fallback::<SignedMessage>(&message) {
654                Ok(m) => {
655                    emit_event(
656                        network_sender_out,
657                        NetworkEvent::PubsubMessage {
658                            message: PubsubMessage::Message(m),
659                        },
660                    )
661                    .await;
662                }
663                Err(e) => {
664                    warn!("Gossip Message from peer {source:?} could not be deserialized: {e:#}");
665                }
666            }
667        } else {
668            warn!("Getting gossip messages from unknown topic: {topic}");
669        }
670    }
671}
672
673async fn handle_hello_event(
674    peer_info_map: &HashMap<PeerId, PeerInfo>,
675    hello: &mut HelloBehaviour,
676    event: request_response::Event<HelloRequest, HelloResponse, HelloResponse>,
677    peer_manager: &PeerManager,
678    genesis_cid: &Cid,
679    network_sender_out: &Sender<NetworkEvent>,
680) {
681    match event {
682        request_response::Event::Message { peer, message, .. } => match message {
683            request_response::Message::Request {
684                request, channel, ..
685            } => {
686                emit_event(network_sender_out, NetworkEvent::HelloRequestInbound).await;
687
688                let arrival = SystemTime::now()
689                    .duration_since(UNIX_EPOCH)
690                    .expect("System time before unix epoch")
691                    .as_nanos()
692                    .try_into()
693                    .expect("System time since unix epoch should not exceed u64");
694
695                trace!("Received hello request: {:?}", request);
696                if &request.genesis_cid != genesis_cid {
697                    peer_manager
698                        .ban_peer_with_default_duration(
699                            peer,
700                            format!(
701                                "Genesis hash mismatch: {} received, {genesis_cid} expected",
702                                request.genesis_cid
703                            ),
704                            |p| get_user_agent(peer_info_map, p),
705                        )
706                        .await;
707                } else {
708                    let sent = SystemTime::now()
709                        .duration_since(UNIX_EPOCH)
710                        .expect("System time before unix epoch")
711                        .as_nanos()
712                        .try_into()
713                        .expect("System time since unix epoch should not exceed u64");
714
715                    // Send hello response immediately, no need to have the overhead of emitting
716                    // channel and polling future here.
717                    if let Err(e) = hello.send_response(channel, HelloResponse { arrival, sent }) {
718                        warn!("Failed to send HelloResponse: {e:?}");
719                    } else {
720                        emit_event(
721                            network_sender_out,
722                            NetworkEvent::HelloResponseOutbound {
723                                source: peer,
724                                request,
725                            },
726                        )
727                        .await;
728                    }
729                }
730            }
731            request_response::Message::Response {
732                request_id,
733                response,
734            } => {
735                emit_event(network_sender_out, NetworkEvent::HelloResponseInbound).await;
736                hello.handle_response(&request_id, response).await;
737            }
738        },
739        request_response::Event::OutboundFailure {
740            request_id,
741            peer,
742            error,
743            ..
744        } => {
745            hello.on_outbound_failure(&request_id);
746            match error {
747                request_response::OutboundFailure::UnsupportedProtocols => {
748                    peer_manager
749                        .ban_peer_with_default_duration(peer, "Hello protocol unsupported", |p| {
750                            get_user_agent(peer_info_map, p)
751                        })
752                        .await;
753                }
754                _ => {
755                    peer_manager.mark_peer_bad(peer, format!("Hello outbound failure {error}"));
756                }
757            }
758        }
759        request_response::Event::InboundFailure { .. } => {}
760        request_response::Event::ResponseSent { .. } => (),
761    }
762}
763
764async fn handle_ping_event(ping_event: ping::Event) {
765    match ping_event.result {
766        Ok(rtt) => {
767            trace!(
768                "PingSuccess::Ping rtt to {} is {} ms",
769                ping_event.peer,
770                rtt.as_millis()
771            );
772        }
773        Err(ping::Failure::Unsupported) => {
774            debug!(peer=%ping_event.peer, "Ping protocol unsupported");
775        }
776        Err(ping::Failure::Timeout) => {
777            debug!("Ping timeout: {}", ping_event.peer);
778        }
779        Err(ping::Failure::Other { error }) => {
780            debug!("Ping failure: {error}");
781        }
782    }
783}
784
785async fn handle_chain_exchange_event(
786    chain_exchange: &mut ChainExchangeBehaviour,
787    ce_event: request_response::Event<ChainExchangeRequest, ChainExchangeResponse>,
788    db: &ChainStore,
789    network_sender_out: &Sender<NetworkEvent>,
790    cx_response_tx: Sender<(
791        request_response::InboundRequestId,
792        request_response::ResponseChannel<ChainExchangeResponse>,
793        ChainExchangeResponse,
794    )>,
795) {
796    const CHAIN_EXCHANGE_RESPONSE_TIMEOUT: Duration = Duration::from_mins(5);
797    match ce_event {
798        request_response::Event::Message { peer, message, .. } => match message {
799            request_response::Message::Request {
800                request,
801                channel,
802                request_id,
803            } => {
804                let Some(per_peer_permit) = chain_exchange.try_acquire_peer_permit(peer) else {
805                    debug!("Rejecting chain_exchange request from {peer}: per-peer cap reached");
806                    let _ = chain_exchange.send_response(
807                        channel,
808                        ChainExchangeResponse::go_away("per-peer concurrent request cap reached"),
809                    );
810                    return;
811                };
812                let Some(global_permit) = chain_exchange.try_acquire_request_permit() else {
813                    debug!("Rejecting chain_exchange request from {peer}: global cap reached");
814                    let _ = chain_exchange.send_response(
815                        channel,
816                        ChainExchangeResponse::go_away("global concurrent request cap reached"),
817                    );
818                    return;
819                };
820
821                trace!(
822                    "Received chain_exchange request (request_id:{request_id}, peer_id: {peer:?})",
823                );
824                emit_event(
825                    network_sender_out,
826                    NetworkEvent::ChainExchangeRequestInbound,
827                )
828                .await;
829
830                let db = db.shallow_clone();
831                tokio::task::spawn(tokio::time::timeout(
832                    CHAIN_EXCHANGE_RESPONSE_TIMEOUT,
833                    async move {
834                        let _per_peer_permit = per_peer_permit;
835                        let _global_permit = global_permit;
836                        if let Err(e) = cx_response_tx
837                            .send_async((
838                                request_id,
839                                channel,
840                                make_chain_exchange_response(&db, &request),
841                            ))
842                            .await
843                        {
844                            debug!("Failed to send ChainExchangeResponse: {e:?}");
845                        }
846                    },
847                ));
848            }
849            request_response::Message::Response {
850                request_id,
851                response,
852            } => {
853                emit_event(
854                    network_sender_out,
855                    NetworkEvent::ChainExchangeResponseInbound,
856                )
857                .await;
858                chain_exchange
859                    .handle_inbound_response(&request_id, response)
860                    .await;
861            }
862        },
863        request_response::Event::OutboundFailure {
864            request_id, error, ..
865        } => {
866            chain_exchange.on_outbound_error(&request_id, error);
867        }
868        request_response::Event::InboundFailure { peer, error, .. } => {
869            debug!(
870                "ChainExchange inbound error (peer: {:?}): {:?}",
871                peer, error
872            );
873        }
874        request_response::Event::ResponseSent { .. } => {
875            emit_event(
876                network_sender_out,
877                NetworkEvent::ChainExchangeResponseOutbound,
878            )
879            .await;
880        }
881    }
882}
883
884#[allow(clippy::too_many_arguments)]
885async fn handle_forest_behaviour_event(
886    swarm: &mut Swarm<ForestBehaviour>,
887    bitswap_request_manager: &Arc<BitswapRequestManager>,
888    peer_manager: &PeerManager,
889    event: ForestBehaviourEvent,
890    db: &ChainStore,
891    genesis_cid: &Cid,
892    network_sender_out: &Sender<NetworkEvent>,
893    cx_response_tx: Sender<(
894        request_response::InboundRequestId,
895        request_response::ResponseChannel<ChainExchangeResponse>,
896        ChainExchangeResponse,
897    )>,
898    pubsub_block_str: &str,
899    pubsub_msg_str: &str,
900) {
901    match event {
902        ForestBehaviourEvent::Discovery(discovery_out) => {
903            handle_discovery_event(
904                &swarm.behaviour().discovery.peer_info,
905                discovery_out,
906                network_sender_out,
907                peer_manager,
908            )
909            .await
910        }
911        ForestBehaviourEvent::Gossipsub(e) => {
912            handle_gossip_event(e, network_sender_out, pubsub_block_str, pubsub_msg_str).await
913        }
914        ForestBehaviourEvent::Hello(rr_event) => {
915            let behaviour_mut = swarm.behaviour_mut();
916            handle_hello_event(
917                &behaviour_mut.discovery.peer_info,
918                &mut behaviour_mut.hello,
919                rr_event,
920                peer_manager,
921                genesis_cid,
922                network_sender_out,
923            )
924            .await
925        }
926        ForestBehaviourEvent::Bitswap(event) => {
927            if let Err(e) = bitswap_request_manager.handle_event(
928                &mut swarm.behaviour_mut().bitswap,
929                db.db(),
930                event,
931            ) {
932                warn!("bitswap: {e:#}");
933            }
934        }
935        ForestBehaviourEvent::Ping(ping_event) => handle_ping_event(ping_event).await,
936        ForestBehaviourEvent::ConnectionLimits(_) => {}
937        ForestBehaviourEvent::BlockedPeers(_) => {}
938        ForestBehaviourEvent::ChainExchange(ce_event) => {
939            handle_chain_exchange_event(
940                &mut swarm.behaviour_mut().chain_exchange,
941                ce_event,
942                db,
943                network_sender_out,
944                cx_response_tx,
945            )
946            .await
947        }
948    }
949}
950
951async fn emit_event(sender: &Sender<NetworkEvent>, event: NetworkEvent) {
952    if sender.send_async(event).await.is_err() {
953        error!("Failed to emit event: Network channel receiver has been dropped");
954    }
955}
956
957fn get_user_agent(peer_info_map: &HashMap<PeerId, PeerInfo>, peer: &PeerId) -> Option<String> {
958    peer_info_map
959        .get(peer)
960        .and_then(|i| i.identify_info.as_ref())
961        .map(|i| i.agent_version.clone())
962}