p2p_chat/network/layer/
builder.rs

1//! This module contains the builder logic for the `NetworkLayer`.
2use std::str::FromStr;
3use std::sync::Arc;
4use std::time::Duration;
5
6use anyhow::Result;
7use libp2p::{ping, swarm::Swarm, Multiaddr};
8use tokio::sync::mpsc;
9use tracing::{info, warn};
10
11use crate::crypto::Identity;
12use crate::net::{build_transport, DiscoveryBehaviour};
13use crate::storage::SledMailboxStore;
14
15use super::super::behaviour::P2PBehaviour;
16use super::super::handle::NetworkHandle;
17use super::super::message::NetworkCommand;
18use super::NetworkLayer;
19
20impl NetworkLayer {
21    /// Creates a new `NetworkLayer` and `NetworkHandle`.
22    ///
23    /// # Arguments
24    ///
25    /// * `identity` - The identity of the local node.
26    /// * `listen_addr` - The address to listen on for incoming connections.
27    /// * `is_mailbox` - Whether the node is a mailbox node.
28    /// * `bootstrap_nodes` - A list of bootstrap nodes to connect to.
29    ///
30    /// # Errors
31    ///
32    /// This function will return an error if the network layer cannot be created.
33    pub fn new(
34        identity: Arc<Identity>,
35        listen_addr: Multiaddr,
36        is_mailbox: bool,
37        bootstrap_nodes: Vec<&str>,
38    ) -> Result<(Self, NetworkHandle)> {
39        Self::new_with_mailbox_storage(identity, listen_addr, is_mailbox, None, bootstrap_nodes)
40    }
41
42    /// Creates a new `NetworkLayer` and `NetworkHandle` with optional mailbox storage.
43    ///
44    /// # Arguments
45    ///
46    /// * `identity` - The identity of the local node.
47    /// * `listen_addr` - The address to listen on for incoming connections.
48    /// * `is_mailbox` - Whether the node is a mailbox node.
49    /// * `mailbox_storage` - The storage for the mailbox, if this is a mailbox node.
50    /// * `bootstrap_nodes` - A list of bootstrap nodes to connect to.
51    ///
52    /// # Errors
53    ///
54    /// This function will return an error if the network layer cannot be created.
55    pub fn new_with_mailbox_storage(
56        identity: Arc<Identity>,
57        listen_addr: Multiaddr,
58        is_mailbox: bool,
59        mailbox_storage: Option<Arc<SledMailboxStore>>,
60        bootstrap_nodes: Vec<&str>,
61    ) -> Result<(Self, NetworkHandle)> {
62        let keypair = identity.libp2p_keypair.clone();
63        let peer_id = identity.peer_id;
64
65        let transport = build_transport(&keypair)?;
66
67        let ping_config = ping::Config::new()
68            .with_interval(Duration::from_secs(30))
69            .with_timeout(Duration::from_secs(10));
70
71        let mut behaviour = P2PBehaviour {
72            chat: crate::net::chat::create_chat_behaviour(),
73            mailbox: crate::net::mailbox::create_mailbox_behaviour(),
74            discovery: DiscoveryBehaviour::new(peer_id)?,
75            ping: ping::Behaviour::new(ping_config),
76        };
77
78        for node in bootstrap_nodes {
79            match Multiaddr::from_str(node) {
80                Ok(addr) => {
81                    if let Some(peer_id) = addr.iter().find_map(|p| {
82                        if let libp2p::multiaddr::Protocol::P2p(peer_id) = p {
83                            Some(peer_id)
84                        } else {
85                            None
86                        }
87                    }) {
88                        info!("Adding bootstrap node: {} -> {}", peer_id, addr);
89                        behaviour.discovery.kademlia.add_address(&peer_id, addr);
90                    } else {
91                        warn!("Bootstrap address did not contain a PeerId: {}", node);
92                    }
93                }
94                Err(e) => {
95                    warn!("Failed to parse bootstrap address '{}': {}", node, e);
96                }
97            }
98        }
99
100        let swarm_config = libp2p::swarm::Config::with_tokio_executor()
101            .with_idle_connection_timeout(Duration::from_secs(60 * 60));
102
103        let mut swarm = Swarm::new(transport, behaviour, peer_id, swarm_config);
104        swarm.listen_on(listen_addr)?;
105
106        if let Err(e) = swarm.behaviour_mut().discovery.bootstrap() {
107            warn!("Initial DHT bootstrap failed: {}", e);
108        }
109
110        let (command_sender, command_receiver) = mpsc::unbounded_channel::<NetworkCommand>();
111
112        let network_layer = NetworkLayer {
113            swarm,
114            command_receiver,
115            pending_requests: Default::default(),
116            sync_event_tx: None,
117            ui_notify_tx: None,
118            mailbox_storage,
119            blocked_peers: Default::default(),
120        };
121
122        let handle = NetworkHandle { command_sender };
123
124        info!(
125            "Network layer initialized for peer: {} (mailbox: {})",
126            peer_id, is_mailbox
127        );
128
129        Ok((network_layer, handle))
130    }
131}