Skip to main content

ferripfs_network/
behavior.rs

1// Ported from: kubo/core/node/libp2p
2// Kubo version: v0.39.0
3//
4// Original work: Copyright (c) Protocol Labs, Inc.
5// Port: Copyright (c) 2026 ferripfs contributors
6// SPDX-License-Identifier: MIT OR Apache-2.0
7
8//! Combined network behavior for ferripfs.
9
10use libp2p::{autonat, dcutr, identify, kad, mdns, ping, relay, swarm::NetworkBehaviour, PeerId};
11
12/// Combined network behavior for ferripfs node.
13///
14/// This behavior combines:
15/// - Identify: Exchange peer information
16/// - Ping: Measure latency to peers
17/// - Kademlia: DHT for peer/content routing
18/// - mDNS: Local peer discovery
19/// - AutoNAT: NAT detection
20/// - Relay: Circuit relay client
21/// - DCUtR: Direct connection upgrade through relay
22#[derive(NetworkBehaviour)]
23pub struct FerripfsBehavior {
24    /// Identify protocol for exchanging peer info
25    pub identify: identify::Behaviour,
26    /// Ping protocol for latency measurement
27    pub ping: ping::Behaviour,
28    /// Kademlia DHT for routing
29    pub kademlia: kad::Behaviour<kad::store::MemoryStore>,
30    /// mDNS for local discovery
31    pub mdns: mdns::tokio::Behaviour,
32    /// AutoNAT for NAT detection
33    pub autonat: autonat::Behaviour,
34    /// Relay client for NAT traversal
35    pub relay_client: relay::client::Behaviour,
36    /// DCUtR for hole punching
37    pub dcutr: dcutr::Behaviour,
38}
39
40impl FerripfsBehavior {
41    /// Create a new combined behavior.
42    pub fn new(
43        local_peer_id: PeerId,
44        local_public_key: libp2p::identity::PublicKey,
45        relay_client: relay::client::Behaviour,
46    ) -> Result<Self, Box<dyn std::error::Error>> {
47        // Configure identify
48        let identify_config = identify::Config::new(
49            crate::PROTOCOL_VERSION.to_string(),
50            local_public_key.clone(),
51        )
52        .with_agent_version(crate::AGENT_VERSION.to_string());
53        let identify = identify::Behaviour::new(identify_config);
54
55        // Configure ping with default settings
56        let ping = ping::Behaviour::new(ping::Config::new());
57
58        // Configure Kademlia
59        let kad_store = kad::store::MemoryStore::new(local_peer_id);
60        let mut kad_config = kad::Config::new(libp2p::StreamProtocol::new("/ipfs/kad/1.0.0"));
61        kad_config.set_query_timeout(std::time::Duration::from_secs(60));
62        let kademlia = kad::Behaviour::with_config(local_peer_id, kad_store, kad_config);
63
64        // Configure mDNS
65        let mdns = mdns::tokio::Behaviour::new(mdns::Config::default(), local_peer_id)?;
66
67        // Configure AutoNAT
68        let autonat = autonat::Behaviour::new(local_peer_id, autonat::Config::default());
69
70        // DCUtR for hole punching
71        let dcutr = dcutr::Behaviour::new(local_peer_id);
72
73        Ok(Self {
74            identify,
75            ping,
76            kademlia,
77            mdns,
78            autonat,
79            relay_client,
80            dcutr,
81        })
82    }
83
84    /// Add a peer address to Kademlia routing table.
85    pub fn add_address(&mut self, peer_id: &PeerId, addr: libp2p::Multiaddr) {
86        self.kademlia.add_address(peer_id, addr);
87    }
88
89    /// Bootstrap Kademlia DHT.
90    pub fn bootstrap(&mut self) -> Result<kad::QueryId, kad::NoKnownPeers> {
91        self.kademlia.bootstrap()
92    }
93
94    /// Get providers for a key.
95    pub fn get_providers(&mut self, key: kad::RecordKey) -> kad::QueryId {
96        self.kademlia.get_providers(key)
97    }
98
99    /// Start providing content for a key.
100    pub fn start_providing(
101        &mut self,
102        key: kad::RecordKey,
103    ) -> Result<kad::QueryId, kad::store::Error> {
104        self.kademlia.start_providing(key)
105    }
106
107    /// Find the closest peers to a given peer ID.
108    pub fn get_closest_peers(&mut self, peer_id: PeerId) -> kad::QueryId {
109        self.kademlia.get_closest_peers(peer_id)
110    }
111
112    /// Store a value in the DHT.
113    pub fn put_value(&mut self, key: kad::RecordKey, value: Vec<u8>) -> kad::QueryId {
114        self.kademlia
115            .put_record(kad::Record::new(key, value), kad::Quorum::One)
116            .expect("put_record should not fail with Quorum::One")
117    }
118
119    /// Get a value from the DHT.
120    pub fn get_value(&mut self, key: kad::RecordKey) -> kad::QueryId {
121        self.kademlia.get_record(key)
122    }
123
124    /// Set the DHT mode (client or server).
125    pub fn set_mode(&mut self, mode: Option<kad::Mode>) {
126        self.kademlia.set_mode(mode);
127    }
128
129    /// Get DHT statistics.
130    pub fn get_routing_table_info(&mut self) -> (usize, usize) {
131        let mut total_peers = 0;
132        let mut total_buckets = 0;
133        for bucket in self.kademlia.kbuckets() {
134            total_buckets += 1;
135            total_peers += bucket.num_entries();
136        }
137        (total_buckets, total_peers)
138    }
139}