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