Skip to main content

firecloud_net/
behaviour.rs

1//! Combined libp2p behaviour for FireCloud
2
3use crate::codec::{TransferCodec, MessageCodec};
4use crate::protocol::{TransferRequest, TransferResponse, PROTOCOL_NAME};
5use crate::messaging::{MessageRequest, MessageResponse, MESSAGING_PROTOCOL};
6use libp2p::{
7    dcutr, relay,
8    gossipsub::{self, MessageAuthenticity, ValidationMode},
9    identify, kad, mdns, ping,
10    request_response::{self, ProtocolSupport},
11    swarm::NetworkBehaviour,
12    PeerId,
13};
14use std::time::Duration;
15
16/// Combined behaviour for FireCloud networking
17#[derive(NetworkBehaviour)]
18#[behaviour(to_swarm = "FireCloudEvent")]
19pub struct FireCloudBehaviour {
20    /// mDNS for local peer discovery
21    pub mdns: mdns::tokio::Behaviour,
22    /// Kademlia DHT for distributed hash table
23    pub kademlia: kad::Behaviour<kad::store::MemoryStore>,
24    /// GossipSub for pub/sub messaging
25    pub gossipsub: gossipsub::Behaviour,
26    /// Identify protocol for peer info exchange
27    pub identify: identify::Behaviour,
28    /// Ping for connection liveness
29    pub ping: ping::Behaviour,
30    /// Request-Response for file transfer
31    pub transfer: request_response::Behaviour<TransferCodec>,
32    /// Request-Response for direct messaging
33    pub messaging: request_response::Behaviour<MessageCodec>,
34    /// DCUtR for NAT hole punching
35    pub dcutr: dcutr::Behaviour,
36    /// Relay client for circuit relay
37    pub relay_client: relay::client::Behaviour,
38}
39
40/// Events emitted by FireCloudBehaviour
41#[derive(Debug)]
42pub enum FireCloudEvent {
43    Mdns(mdns::Event),
44    Kademlia(kad::Event),
45    Gossipsub(gossipsub::Event),
46    Identify(identify::Event),
47    Ping(ping::Event),
48    Transfer(request_response::Event<TransferRequest, TransferResponse>),
49    Messaging(request_response::Event<MessageRequest, MessageResponse>),
50    Dcutr(dcutr::Event),
51    RelayClient(relay::client::Event),
52}
53
54impl From<mdns::Event> for FireCloudEvent {
55    fn from(event: mdns::Event) -> Self {
56        FireCloudEvent::Mdns(event)
57    }
58}
59
60impl From<kad::Event> for FireCloudEvent {
61    fn from(event: kad::Event) -> Self {
62        FireCloudEvent::Kademlia(event)
63    }
64}
65
66impl From<gossipsub::Event> for FireCloudEvent {
67    fn from(event: gossipsub::Event) -> Self {
68        FireCloudEvent::Gossipsub(event)
69    }
70}
71
72impl From<identify::Event> for FireCloudEvent {
73    fn from(event: identify::Event) -> Self {
74        FireCloudEvent::Identify(event)
75    }
76}
77
78impl From<ping::Event> for FireCloudEvent {
79    fn from(event: ping::Event) -> Self {
80        FireCloudEvent::Ping(event)
81    }
82}
83
84impl From<request_response::Event<TransferRequest, TransferResponse>> for FireCloudEvent {
85    fn from(event: request_response::Event<TransferRequest, TransferResponse>) -> Self {
86        FireCloudEvent::Transfer(event)
87    }
88}
89
90impl From<request_response::Event<MessageRequest, MessageResponse>> for FireCloudEvent {
91    fn from(event: request_response::Event<MessageRequest, MessageResponse>) -> Self {
92        FireCloudEvent::Messaging(event)
93    }
94}
95
96impl From<dcutr::Event> for FireCloudEvent {
97    fn from(event: dcutr::Event) -> Self {
98        FireCloudEvent::Dcutr(event)
99    }
100}
101
102impl From<relay::client::Event> for FireCloudEvent {
103    fn from(event: relay::client::Event) -> Self {
104        FireCloudEvent::RelayClient(event)
105    }
106}
107
108impl FireCloudBehaviour {
109    /// Create a new FireCloud behaviour with relay client (called by SwarmBuilder)
110    pub fn new_with_relay(
111        local_peer_id: PeerId,
112        local_key: &libp2p::identity::Keypair,
113        relay_client: relay::client::Behaviour,
114    ) -> anyhow::Result<Self> {
115        // mDNS config
116        let mdns = mdns::tokio::Behaviour::new(
117            mdns::Config::default(),
118            local_peer_id,
119        )?;
120
121        // Kademlia config
122        let mut kad_config = kad::Config::new(kad::PROTOCOL_NAME);
123        kad_config.set_query_timeout(Duration::from_secs(60));
124        
125        let kad_store = kad::store::MemoryStore::new(local_peer_id);
126        let kademlia = kad::Behaviour::with_config(local_peer_id, kad_store, kad_config);
127
128        // GossipSub config
129        let gossipsub_config = gossipsub::ConfigBuilder::default()
130            .heartbeat_interval(Duration::from_secs(1))
131            .validation_mode(ValidationMode::Strict)
132            .build()
133            .map_err(|e| anyhow::anyhow!("GossipSub config error: {}", e))?;
134
135        let gossipsub = gossipsub::Behaviour::new(
136            MessageAuthenticity::Signed(local_key.clone()),
137            gossipsub_config,
138        )
139        .map_err(|e| anyhow::anyhow!("GossipSub error: {}", e))?;
140
141        // Identify config
142        let identify = identify::Behaviour::new(identify::Config::new(
143            "/firecloud/1.0.0".to_string(),
144            local_key.public(),
145        ));
146
147        // Ping config
148        let ping = ping::Behaviour::new(ping::Config::new());
149
150        // Request-Response for file transfer (custom CBOR codec with error handling)
151        let protocols = vec![(PROTOCOL_NAME, ProtocolSupport::Full)];
152        let transfer_config = request_response::Config::default()
153            .with_request_timeout(Duration::from_secs(60));
154        let transfer = request_response::Behaviour::with_codec(
155            TransferCodec,
156            protocols,
157            transfer_config,
158        );
159
160        // Request-Response for direct messaging (bincode codec for efficiency)
161        let messaging_protocols = vec![(MESSAGING_PROTOCOL, ProtocolSupport::Full)];
162        let messaging_config = request_response::Config::default()
163            .with_request_timeout(Duration::from_secs(30)); // Shorter timeout for messages
164        let messaging = request_response::Behaviour::with_codec(
165            MessageCodec,
166            messaging_protocols,
167            messaging_config,
168        );
169
170        // DCUtR for NAT hole punching
171        let dcutr = dcutr::Behaviour::new(local_peer_id);
172
173        Ok(Self {
174            mdns,
175            kademlia,
176            gossipsub,
177            identify,
178            ping,
179            transfer,
180            messaging,
181            dcutr,
182            relay_client,
183        })
184    }
185
186    /// Create a new FireCloud behaviour (legacy method for tests)
187    pub fn new(local_peer_id: PeerId, local_key: &libp2p::identity::Keypair) -> anyhow::Result<Self> {
188        // mDNS config
189        let mdns = mdns::tokio::Behaviour::new(
190            mdns::Config::default(),
191            local_peer_id,
192        )?;
193
194        // Kademlia config
195        let mut kad_config = kad::Config::new(kad::PROTOCOL_NAME);
196        kad_config.set_query_timeout(Duration::from_secs(60));
197        
198        let kad_store = kad::store::MemoryStore::new(local_peer_id);
199        let kademlia = kad::Behaviour::with_config(local_peer_id, kad_store, kad_config);
200
201        // GossipSub config
202        let gossipsub_config = gossipsub::ConfigBuilder::default()
203            .heartbeat_interval(Duration::from_secs(1))
204            .validation_mode(ValidationMode::Strict)
205            .build()
206            .map_err(|e| anyhow::anyhow!("GossipSub config error: {}", e))?;
207
208        let gossipsub = gossipsub::Behaviour::new(
209            MessageAuthenticity::Signed(local_key.clone()),
210            gossipsub_config,
211        )
212        .map_err(|e| anyhow::anyhow!("GossipSub error: {}", e))?;
213
214        // Identify config
215        let identify = identify::Behaviour::new(identify::Config::new(
216            "/firecloud/1.0.0".to_string(),
217            local_key.public(),
218        ));
219
220        // Ping config
221        let ping = ping::Behaviour::new(ping::Config::new());
222
223        // Request-Response for file transfer (custom CBOR codec with error handling)
224        let protocols = vec![(PROTOCOL_NAME, ProtocolSupport::Full)];
225        let transfer_config = request_response::Config::default()
226            .with_request_timeout(Duration::from_secs(60));
227        let transfer = request_response::Behaviour::with_codec(
228            TransferCodec,
229            protocols,
230            transfer_config,
231        );
232
233        // Request-Response for direct messaging
234        let messaging_protocols = vec![(MESSAGING_PROTOCOL, ProtocolSupport::Full)];
235        let messaging_config = request_response::Config::default()
236            .with_request_timeout(Duration::from_secs(30));
237        let messaging = request_response::Behaviour::with_codec(
238            MessageCodec,
239            messaging_protocols,
240            messaging_config,
241        );
242
243        // DCUtR for NAT hole punching
244        let dcutr = dcutr::Behaviour::new(local_peer_id);
245
246        // Create relay client for testing (transport not used in tests)
247        let (_relay_transport, relay_client) = relay::client::new(local_peer_id);
248
249        Ok(Self {
250            mdns,
251            kademlia,
252            gossipsub,
253            identify,
254            ping,
255            transfer,
256            messaging,
257            dcutr,
258            relay_client,
259        })
260    }
261}