helia_utils/
libp2p_behaviour.rs

1//! libp2p behavior implementation for Helia
2
3use helia_bitswap::BitswapBehaviour;
4use libp2p::identity::Keypair;
5use libp2p::{
6    autonat, dcutr, gossipsub, identify, kad, mdns, noise, ping, relay, swarm::NetworkBehaviour,
7    tcp, yamux, StreamProtocol, Swarm, SwarmBuilder,
8};
9use std::collections::hash_map::DefaultHasher;
10use std::hash::{Hash, Hasher};
11use std::time::Duration;
12
13/// The combined libp2p behavior for Helia
14#[derive(NetworkBehaviour)]
15pub struct HeliaBehaviour {
16    /// Ping protocol for liveness checking
17    pub ping: ping::Behaviour,
18    /// Identify protocol for peer identification
19    pub identify: identify::Behaviour,
20    /// Kademlia DHT for content and peer routing
21    pub kademlia: kad::Behaviour<kad::store::MemoryStore>,
22    /// Gossipsub for pubsub messaging
23    pub gossipsub: gossipsub::Behaviour,
24    /// mDNS for local peer discovery
25    pub mdns: mdns::tokio::Behaviour,
26    /// AutoNAT for NAT detection
27    pub autonat: autonat::Behaviour,
28    /// Relay support
29    pub relay: relay::Behaviour,
30    /// DCUtR (Direct Connection Upgrade through Relay)
31    pub dcutr: dcutr::Behaviour,
32    /// Bitswap protocol for block exchange
33    pub bitswap: BitswapBehaviour,
34}
35
36/// Create a libp2p Swarm with Helia's default configuration
37pub async fn create_swarm() -> Result<Swarm<HeliaBehaviour>, Box<dyn std::error::Error>> {
38    // Generate a random keypair for this node
39    let local_key = Keypair::generate_ed25519();
40    let local_peer_id = local_key.public().to_peer_id();
41
42    // Create the behaviour
43    let behaviour = create_behaviour(local_key.clone(), local_peer_id).await?;
44
45    // Build the swarm
46    let swarm = SwarmBuilder::with_existing_identity(local_key)
47        .with_tokio()
48        .with_tcp(
49            tcp::Config::default(),
50            noise::Config::new,
51            yamux::Config::default,
52        )?
53        .with_behaviour(|_| behaviour)?
54        .with_swarm_config(|c| c.with_idle_connection_timeout(Duration::from_secs(60)))
55        .build();
56
57    Ok(swarm)
58}
59
60/// Create a libp2p Swarm with custom keypair
61pub async fn create_swarm_with_keypair(
62    keypair: Keypair,
63) -> Result<Swarm<HeliaBehaviour>, Box<dyn std::error::Error>> {
64    let local_peer_id = keypair.public().to_peer_id();
65
66    // Create the behaviour
67    let behaviour = create_behaviour(keypair.clone(), local_peer_id).await?;
68
69    // Build the swarm
70    let swarm = SwarmBuilder::with_existing_identity(keypair)
71        .with_tokio()
72        .with_tcp(
73            tcp::Config::default(),
74            noise::Config::new,
75            yamux::Config::default,
76        )?
77        .with_behaviour(|_| behaviour)?
78        .with_swarm_config(|c| c.with_idle_connection_timeout(Duration::from_secs(60)))
79        .build();
80
81    Ok(swarm)
82}
83
84async fn create_behaviour(
85    local_key: Keypair,
86    local_peer_id: libp2p::PeerId,
87) -> Result<HeliaBehaviour, Box<dyn std::error::Error>> {
88    // Create ping behaviour
89    let ping = ping::Behaviour::new(ping::Config::new());
90
91    // Create identify behaviour
92    let identify = identify::Behaviour::new(identify::Config::new(
93        "/helia/1.0.0".to_string(),
94        local_key.public(),
95    ));
96
97    // Create Kademlia behaviour
98    let mut kademlia_config = kad::Config::default();
99    kademlia_config.set_protocol_names(vec![StreamProtocol::new("/ipfs/kad/1.0.0")]);
100    let store = kad::store::MemoryStore::new(local_peer_id);
101    let kademlia = kad::Behaviour::with_config(local_peer_id, store, kademlia_config);
102
103    // Create Gossipsub behaviour
104    let gossipsub_config = gossipsub::ConfigBuilder::default()
105        .heartbeat_interval(Duration::from_secs(10))
106        .validation_mode(gossipsub::ValidationMode::Strict)
107        .build()
108        .expect("Valid config");
109
110    // Generate a deterministic message-id function from the peer ID
111    let _message_id_fn = |message: &gossipsub::Message| {
112        let mut s = DefaultHasher::new();
113        message.data.hash(&mut s);
114        gossipsub::MessageId::from(s.finish().to_string())
115    };
116    let gossipsub = gossipsub::Behaviour::new(
117        gossipsub::MessageAuthenticity::Signed(local_key.clone()),
118        gossipsub_config,
119    )
120    .expect("Correct configuration");
121
122    // Create mDNS behaviour
123    let mdns = mdns::tokio::Behaviour::new(mdns::Config::default(), local_peer_id)?;
124
125    // Create AutoNAT behaviour
126    let autonat = autonat::Behaviour::new(local_peer_id, autonat::Config::default());
127
128    // Create Relay behaviour
129    let relay = relay::Behaviour::new(local_peer_id, relay::Config::default());
130
131    // Create DCUtR behaviour
132    let dcutr = dcutr::Behaviour::new(local_peer_id);
133
134    // Create Bitswap behaviour
135    let bitswap = BitswapBehaviour::new();
136
137    Ok(HeliaBehaviour {
138        ping,
139        identify,
140        kademlia,
141        gossipsub,
142        mdns,
143        autonat,
144        relay,
145        dcutr,
146        bitswap,
147    })
148}
149
150#[cfg(test)]
151mod tests {
152    use super::*;
153
154    #[tokio::test]
155    async fn test_create_swarm() {
156        let swarm = create_swarm().await;
157        assert!(swarm.is_ok());
158    }
159
160    #[tokio::test]
161    async fn test_create_swarm_with_keypair() {
162        let keypair = Keypair::generate_ed25519();
163        let swarm = create_swarm_with_keypair(keypair).await;
164        assert!(swarm.is_ok());
165    }
166}