1use rand::Rng;
4use serde::{Deserialize, Serialize};
5
6pub const MAX_HTL: u8 = 10;
8pub const DECREMENT_AT_MAX_PROB: f64 = 0.5; pub const DECREMENT_AT_MIN_PROB: f64 = 0.25; #[derive(Debug, Clone)]
14pub struct PeerHTLConfig {
15 pub decrement_at_max: bool, pub decrement_at_min: bool, }
18
19impl PeerHTLConfig {
20 pub fn new() -> Self {
23 let mut rng = rand::thread_rng();
24 Self {
25 decrement_at_max: rng.gen_bool(DECREMENT_AT_MAX_PROB),
26 decrement_at_min: rng.gen_bool(DECREMENT_AT_MIN_PROB),
27 }
28 }
29}
30
31impl Default for PeerHTLConfig {
32 fn default() -> Self {
33 Self::new()
34 }
35}
36
37pub fn decrement_htl(htl: u8, config: &PeerHTLConfig) -> u8 {
44 let htl = htl.min(MAX_HTL);
46
47 if htl == 0 {
49 return 0;
50 }
51
52 if htl == MAX_HTL {
54 return if config.decrement_at_max { htl - 1 } else { htl };
55 }
56
57 if htl == 1 {
59 return if config.decrement_at_min { 0 } else { htl };
60 }
61
62 htl - 1
64}
65
66pub fn should_forward(htl: u8) -> bool {
68 htl > 0
69}
70
71pub const WEBRTC_KIND: u64 = 25050;
74
75pub const HELLO_TAG: &str = "hello";
77
78pub const WEBRTC_TAG: &str = "webrtc";
80
81pub fn generate_uuid() -> String {
83 use rand::Rng;
84 let mut rng = rand::thread_rng();
85 format!(
86 "{}{}",
87 (0..15).map(|_| char::from_digit(rng.gen_range(0..36), 36).unwrap()).collect::<String>(),
88 (0..15).map(|_| char::from_digit(rng.gen_range(0..36), 36).unwrap()).collect::<String>()
89 )
90}
91
92#[derive(Debug, Clone, PartialEq, Eq, Hash)]
94pub struct PeerId {
95 pub pubkey: String,
96 pub uuid: String,
97}
98
99impl PeerId {
100 pub fn new(pubkey: String, uuid: Option<String>) -> Self {
101 Self {
102 pubkey,
103 uuid: uuid.unwrap_or_else(generate_uuid),
104 }
105 }
106
107 pub fn from_string(s: &str) -> Option<Self> {
108 let parts: Vec<&str> = s.split(':').collect();
109 if parts.len() == 2 {
110 Some(Self {
111 pubkey: parts[0].to_string(),
112 uuid: parts[1].to_string(),
113 })
114 } else {
115 None
116 }
117 }
118
119 pub fn short(&self) -> String {
120 format!("{}:{}", &self.pubkey[..8.min(self.pubkey.len())], &self.uuid[..6.min(self.uuid.len())])
121 }
122}
123
124impl std::fmt::Display for PeerId {
125 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
126 write!(f, "{}:{}", self.pubkey, self.uuid)
127 }
128}
129
130#[derive(Debug, Clone, Serialize, Deserialize)]
132pub struct HelloMessage {
133 #[serde(rename = "type")]
134 pub msg_type: String,
135 #[serde(rename = "peerId")]
136 pub peer_id: String,
137}
138
139#[derive(Debug, Clone, Serialize, Deserialize)]
141pub struct OfferMessage {
142 #[serde(rename = "type")]
143 pub msg_type: String,
144 pub offer: serde_json::Value,
145 pub recipient: String,
146 #[serde(rename = "peerId")]
147 pub peer_id: String,
148}
149
150#[derive(Debug, Clone, Serialize, Deserialize)]
152pub struct AnswerMessage {
153 #[serde(rename = "type")]
154 pub msg_type: String,
155 pub answer: serde_json::Value,
156 pub recipient: String,
157 #[serde(rename = "peerId")]
158 pub peer_id: String,
159}
160
161#[derive(Debug, Clone, Serialize, Deserialize)]
163pub struct CandidateMessage {
164 #[serde(rename = "type")]
165 pub msg_type: String,
166 pub candidate: serde_json::Value,
167 pub recipient: String,
168 #[serde(rename = "peerId")]
169 pub peer_id: String,
170}
171
172#[derive(Debug, Clone, Serialize, Deserialize)]
174pub struct CandidatesMessage {
175 #[serde(rename = "type")]
176 pub msg_type: String,
177 pub candidates: Vec<serde_json::Value>,
178 pub recipient: String,
179 #[serde(rename = "peerId")]
180 pub peer_id: String,
181}
182
183#[derive(Debug, Clone, Serialize, Deserialize)]
185#[serde(tag = "type")]
186pub enum SignalingMessage {
187 #[serde(rename = "hello")]
188 Hello { #[serde(rename = "peerId")] peer_id: String },
189 #[serde(rename = "offer")]
190 Offer {
191 offer: serde_json::Value,
192 recipient: String,
193 #[serde(rename = "peerId")]
194 peer_id: String,
195 },
196 #[serde(rename = "answer")]
197 Answer {
198 answer: serde_json::Value,
199 recipient: String,
200 #[serde(rename = "peerId")]
201 peer_id: String,
202 },
203 #[serde(rename = "candidate")]
204 Candidate {
205 candidate: serde_json::Value,
206 recipient: String,
207 #[serde(rename = "peerId")]
208 peer_id: String,
209 },
210 #[serde(rename = "candidates")]
211 Candidates {
212 candidates: Vec<serde_json::Value>,
213 recipient: String,
214 #[serde(rename = "peerId")]
215 peer_id: String,
216 },
217}
218
219impl SignalingMessage {
220 pub fn msg_type(&self) -> &str {
221 match self {
222 SignalingMessage::Hello { .. } => "hello",
223 SignalingMessage::Offer { .. } => "offer",
224 SignalingMessage::Answer { .. } => "answer",
225 SignalingMessage::Candidate { .. } => "candidate",
226 SignalingMessage::Candidates { .. } => "candidates",
227 }
228 }
229
230 pub fn recipient(&self) -> Option<&str> {
231 match self {
232 SignalingMessage::Hello { .. } => None,
233 SignalingMessage::Offer { recipient, .. } => Some(recipient),
234 SignalingMessage::Answer { recipient, .. } => Some(recipient),
235 SignalingMessage::Candidate { recipient, .. } => Some(recipient),
236 SignalingMessage::Candidates { recipient, .. } => Some(recipient),
237 }
238 }
239
240 pub fn peer_id(&self) -> &str {
241 match self {
242 SignalingMessage::Hello { peer_id } => peer_id,
243 SignalingMessage::Offer { peer_id, .. } => peer_id,
244 SignalingMessage::Answer { peer_id, .. } => peer_id,
245 SignalingMessage::Candidate { peer_id, .. } => peer_id,
246 SignalingMessage::Candidates { peer_id, .. } => peer_id,
247 }
248 }
249
250 pub fn hello(peer_id: &str) -> Self {
251 SignalingMessage::Hello {
252 peer_id: peer_id.to_string(),
253 }
254 }
255
256 pub fn offer(offer: serde_json::Value, recipient: &str, peer_id: &str) -> Self {
257 SignalingMessage::Offer {
258 offer,
259 recipient: recipient.to_string(),
260 peer_id: peer_id.to_string(),
261 }
262 }
263
264 pub fn answer(answer: serde_json::Value, recipient: &str, peer_id: &str) -> Self {
265 SignalingMessage::Answer {
266 answer,
267 recipient: recipient.to_string(),
268 peer_id: peer_id.to_string(),
269 }
270 }
271
272 pub fn candidate(candidate: serde_json::Value, recipient: &str, peer_id: &str) -> Self {
273 SignalingMessage::Candidate {
274 candidate,
275 recipient: recipient.to_string(),
276 peer_id: peer_id.to_string(),
277 }
278 }
279}
280
281#[derive(Clone)]
283pub struct WebRTCConfig {
284 pub relays: Vec<String>,
286 pub max_outbound: usize,
288 pub max_inbound: usize,
290 pub hello_interval_ms: u64,
292 pub message_timeout_ms: u64,
294 pub stun_servers: Vec<String>,
296 pub debug: bool,
298 pub pools: PoolSettings,
300}
301
302impl Default for WebRTCConfig {
303 fn default() -> Self {
304 Self {
305 relays: vec![
306 "wss://relay.damus.io".to_string(),
307 "wss://relay.primal.net".to_string(),
308 "wss://nos.lol".to_string(),
309 "wss://temp.iris.to".to_string(),
310 "wss://relay.snort.social".to_string(),
311 ],
312 max_outbound: 6,
313 max_inbound: 6,
314 hello_interval_ms: 10000,
315 message_timeout_ms: 15000,
316 stun_servers: vec![
317 "stun:stun.iris.to:3478".to_string(),
318 "stun:stun.l.google.com:19302".to_string(),
319 "stun:stun.cloudflare.com:3478".to_string(),
320 ],
321 debug: false,
322 pools: PoolSettings::default(),
323 }
324 }
325}
326
327#[derive(Debug, Clone)]
329pub struct PeerStatus {
330 pub peer_id: String,
331 pub pubkey: String,
332 pub state: String,
333 pub direction: PeerDirection,
334 pub connected_at: Option<std::time::Instant>,
335 pub pool: PeerPool,
336}
337
338#[derive(Debug, Clone, Copy, PartialEq, Eq)]
340pub enum PeerDirection {
341 Inbound,
342 Outbound,
343}
344
345#[derive(Debug, Clone)]
347pub enum PeerStateEvent {
348 Connected(PeerId),
350 Failed(PeerId),
352 Disconnected(PeerId),
354}
355
356#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
358pub enum PeerPool {
359 Follows,
361 Other,
363}
364
365#[derive(Debug, Clone, Copy)]
367pub struct PoolConfig {
368 pub max_connections: usize,
370 pub satisfied_connections: usize,
372}
373
374impl Default for PoolConfig {
375 fn default() -> Self {
376 Self {
377 max_connections: 10,
378 satisfied_connections: 5,
379 }
380 }
381}
382
383#[derive(Debug, Clone)]
385pub struct PoolSettings {
386 pub follows: PoolConfig,
387 pub other: PoolConfig,
388}
389
390impl Default for PoolSettings {
391 fn default() -> Self {
392 Self {
393 follows: PoolConfig {
394 max_connections: 20,
395 satisfied_connections: 10,
396 },
397 other: PoolConfig {
398 max_connections: 10,
399 satisfied_connections: 5,
400 },
401 }
402 }
403}
404
405impl std::fmt::Display for PeerDirection {
406 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
407 match self {
408 PeerDirection::Inbound => write!(f, "inbound"),
409 PeerDirection::Outbound => write!(f, "outbound"),
410 }
411 }
412}
413
414pub const MSG_TYPE_REQUEST: u8 = 0x00;
416pub const MSG_TYPE_RESPONSE: u8 = 0x01;
417
418#[derive(Debug, Clone, Serialize, Deserialize)]
426pub struct DataRequest {
427 #[serde(with = "serde_bytes")]
428 pub h: Vec<u8>, #[serde(default = "default_htl", skip_serializing_if = "is_max_htl")]
430 pub htl: u8,
431}
432
433#[derive(Debug, Clone, Serialize, Deserialize)]
434pub struct DataResponse {
435 #[serde(with = "serde_bytes")]
436 pub h: Vec<u8>, #[serde(with = "serde_bytes")]
438 pub d: Vec<u8>, }
440
441#[derive(Debug, Clone)]
442pub enum DataMessage {
443 Request(DataRequest),
444 Response(DataResponse),
445}
446
447fn default_htl() -> u8 {
448 MAX_HTL
449}
450
451fn is_max_htl(htl: &u8) -> bool {
452 *htl == MAX_HTL
453}
454
455pub fn encode_request(req: &DataRequest) -> Result<Vec<u8>, rmp_serde::encode::Error> {
458 let body = rmp_serde::to_vec_named(req)?;
459 let mut result = Vec::with_capacity(1 + body.len());
460 result.push(MSG_TYPE_REQUEST);
461 result.extend(body);
462 Ok(result)
463}
464
465pub fn encode_response(res: &DataResponse) -> Result<Vec<u8>, rmp_serde::encode::Error> {
468 let body = rmp_serde::to_vec_named(res)?;
469 let mut result = Vec::with_capacity(1 + body.len());
470 result.push(MSG_TYPE_RESPONSE);
471 result.extend(body);
472 Ok(result)
473}
474
475pub fn parse_message(data: &[u8]) -> Result<DataMessage, rmp_serde::decode::Error> {
477 if data.is_empty() {
478 return Err(rmp_serde::decode::Error::LengthMismatch(0));
479 }
480
481 let msg_type = data[0];
482 let body = &data[1..];
483
484 match msg_type {
485 MSG_TYPE_REQUEST => {
486 let req: DataRequest = rmp_serde::from_slice(body)?;
487 Ok(DataMessage::Request(req))
488 }
489 MSG_TYPE_RESPONSE => {
490 let res: DataResponse = rmp_serde::from_slice(body)?;
491 Ok(DataMessage::Response(res))
492 }
493 _ => Err(rmp_serde::decode::Error::LengthMismatch(msg_type as u32)),
494 }
495}
496
497pub fn hash_to_hex(hash: &[u8]) -> String {
499 hash.iter().map(|b| format!("{:02x}", b)).collect()
500}
501
502pub fn encode_message(msg: &DataMessage) -> Result<Vec<u8>, rmp_serde::encode::Error> {
504 match msg {
505 DataMessage::Request(req) => encode_request(req),
506 DataMessage::Response(res) => encode_response(res),
507 }
508}