ferripfs_network/
swarm.rs1use std::time::Duration;
11
12use libp2p::{
13 identity::Keypair, noise, yamux, Multiaddr, PeerId, Swarm,
14 SwarmBuilder as Libp2pSwarmBuilder,
15};
16
17use crate::{behavior::FerripfsBehavior, NetworkError, NetworkResult};
18
19#[derive(Debug, Clone)]
21pub struct SwarmConfig {
22 pub tcp_enabled: bool,
24 pub quic_enabled: bool,
26 pub websocket_enabled: bool,
28 pub mdns_enabled: bool,
30 pub relay_enabled: bool,
32 pub idle_timeout: Duration,
34 pub max_connections_per_peer: u32,
36 pub conn_mgr_low: u32,
38 pub conn_mgr_high: u32,
40}
41
42impl Default for SwarmConfig {
43 fn default() -> Self {
44 Self {
45 tcp_enabled: true,
46 quic_enabled: true,
47 websocket_enabled: true,
48 mdns_enabled: true,
49 relay_enabled: true,
50 idle_timeout: Duration::from_secs(30),
51 max_connections_per_peer: 8,
52 conn_mgr_low: 32,
53 conn_mgr_high: 96,
54 }
55 }
56}
57
58impl SwarmConfig {
59 pub fn from_config(config: &ferripfs_config::Config) -> Self {
61 let mut sc = Self::default();
62
63 if let Some(ref flag) = config.swarm.transports.network.tcp {
65 sc.tcp_enabled = flag.with_default(true);
66 }
67 if let Some(ref flag) = config.swarm.transports.network.quic {
68 sc.quic_enabled = flag.with_default(true);
69 }
70 if let Some(ref flag) = config.swarm.transports.network.websocket {
71 sc.websocket_enabled = flag.with_default(true);
72 }
73 if let Some(ref flag) = config.swarm.relay_client.enabled {
74 sc.relay_enabled = flag.with_default(true);
75 }
76
77 if let Some(ref low) = config.swarm.conn_mgr.low_water {
79 if let Some(v) = low.value() {
80 sc.conn_mgr_low = v as u32;
81 }
82 }
83 if let Some(ref high) = config.swarm.conn_mgr.high_water {
84 if let Some(v) = high.value() {
85 sc.conn_mgr_high = v as u32;
86 }
87 }
88
89 sc
90 }
91}
92
93pub struct SwarmBuilder {
95 keypair: Keypair,
96 config: SwarmConfig,
97 listen_addrs: Vec<Multiaddr>,
98 bootstrap_peers: Vec<(PeerId, Multiaddr)>,
99}
100
101impl SwarmBuilder {
102 pub fn new(keypair: Keypair) -> Self {
104 Self {
105 keypair,
106 config: SwarmConfig::default(),
107 listen_addrs: Vec::new(),
108 bootstrap_peers: Vec::new(),
109 }
110 }
111
112 pub fn with_config(mut self, config: SwarmConfig) -> Self {
114 self.config = config;
115 self
116 }
117
118 pub fn with_listen_addrs(mut self, addrs: Vec<Multiaddr>) -> Self {
120 self.listen_addrs = addrs;
121 self
122 }
123
124 pub fn with_bootstrap_peers(mut self, peers: Vec<(PeerId, Multiaddr)>) -> Self {
126 self.bootstrap_peers = peers;
127 self
128 }
129
130 pub fn build(self) -> NetworkResult<Swarm<FerripfsBehavior>> {
132 let swarm = Libp2pSwarmBuilder::with_existing_identity(self.keypair)
134 .with_tokio()
135 .with_tcp(
136 libp2p::tcp::Config::default(),
137 noise::Config::new,
138 yamux::Config::default,
139 )
140 .map_err(|e| NetworkError::Transport(e.to_string()))?
141 .with_quic()
142 .with_dns()
143 .map_err(|e| NetworkError::Transport(e.to_string()))?
144 .with_relay_client(noise::Config::new, yamux::Config::default)
145 .map_err(|e| NetworkError::Transport(e.to_string()))?
146 .with_behaviour(|keypair, relay_client| {
147 FerripfsBehavior::new(
148 PeerId::from(keypair.public()),
149 keypair.public(),
150 relay_client,
151 )
152 .expect("Failed to create network behavior")
153 })
154 .expect("Infallible error occurred")
155 .with_swarm_config(|c| c.with_idle_connection_timeout(self.config.idle_timeout))
156 .build();
157
158 Ok(swarm)
159 }
160}
161
162pub fn parse_multiaddr(s: &str) -> NetworkResult<Multiaddr> {
164 s.parse()
165 .map_err(|e: multiaddr::Error| NetworkError::InvalidMultiaddr(e.to_string()))
166}
167
168pub fn parse_peer_id(s: &str) -> NetworkResult<PeerId> {
170 s.parse()
171 .map_err(|e: libp2p::identity::ParseError| NetworkError::InvalidPeerId(e.to_string()))
172}
173
174pub fn extract_peer_id(addr: &Multiaddr) -> Option<PeerId> {
176 addr.iter().find_map(|p| {
177 if let multiaddr::Protocol::P2p(peer_id) = p {
178 Some(peer_id)
179 } else {
180 None
181 }
182 })
183}
184
185pub fn parse_bootstrap_peer(addr_str: &str) -> NetworkResult<(PeerId, Multiaddr)> {
187 let addr = parse_multiaddr(addr_str)?;
188 let peer_id = extract_peer_id(&addr).ok_or_else(|| {
189 NetworkError::InvalidMultiaddr("Missing peer ID in bootstrap address".into())
190 })?;
191
192 let transport_addr: Multiaddr = addr
194 .iter()
195 .filter(|p| !matches!(p, multiaddr::Protocol::P2p(_)))
196 .collect();
197
198 Ok((peer_id, transport_addr))
199}
200
201#[cfg(test)]
202mod tests {
203 use super::*;
204
205 #[test]
206 fn test_parse_multiaddr() {
207 let addr = parse_multiaddr("/ip4/127.0.0.1/tcp/4001").unwrap();
208 assert!(addr.to_string().contains("127.0.0.1"));
209 }
210
211 #[test]
212 fn test_parse_bootstrap_peer() {
213 let addr =
214 "/ip4/104.131.131.82/tcp/4001/p2p/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ";
215 let (peer_id, transport) = parse_bootstrap_peer(addr).unwrap();
216 assert_eq!(
217 peer_id.to_string(),
218 "QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ"
219 );
220 assert!(transport.to_string().contains("104.131.131.82"));
221 }
222}