1pub use hashtree_webrtc::{
4 decrement_htl_with_policy, should_forward_htl, validate_mesh_frame, HtlMode, HtlPolicy,
5 MeshNostrFrame, MeshNostrPayload, PeerHTLConfig, RequestDispatchConfig, SelectionStrategy,
6 TimedSeenSet, BLOB_REQUEST_POLICY, DECREMENT_AT_MAX_PROB, DECREMENT_AT_MIN_PROB, MAX_HTL,
7 MESH_DEFAULT_HTL, MESH_EVENT_POLICY, MESH_MAX_HTL, MESH_PROTOCOL, MESH_PROTOCOL_VERSION,
8};
9use serde::{Deserialize, Serialize};
10
11pub fn decrement_htl(htl: u8, config: &PeerHTLConfig) -> u8 {
13 decrement_htl_with_policy(htl, &BLOB_REQUEST_POLICY, config)
14}
15
16pub fn should_forward(htl: u8) -> bool {
18 should_forward_htl(htl)
19}
20
21pub const WEBRTC_KIND: u64 = 25050;
24
25pub const HELLO_TAG: &str = "hello";
27
28pub const WEBRTC_TAG: &str = "webrtc";
30
31pub fn generate_uuid() -> String {
33 use rand::Rng;
34 let mut rng = rand::thread_rng();
35 format!(
36 "{}{}",
37 (0..15)
38 .map(|_| char::from_digit(rng.gen_range(0..36), 36).unwrap())
39 .collect::<String>(),
40 (0..15)
41 .map(|_| char::from_digit(rng.gen_range(0..36), 36).unwrap())
42 .collect::<String>()
43 )
44}
45
46#[derive(Debug, Clone, PartialEq, Eq, Hash)]
48pub struct PeerId {
49 pub pubkey: String,
50 pub uuid: String,
51}
52
53impl PeerId {
54 pub fn new(pubkey: String, uuid: Option<String>) -> Self {
55 Self {
56 pubkey,
57 uuid: uuid.unwrap_or_else(generate_uuid),
58 }
59 }
60
61 pub fn from_string(s: &str) -> Option<Self> {
62 let parts: Vec<&str> = s.split(':').collect();
63 if parts.len() == 2 {
64 Some(Self {
65 pubkey: parts[0].to_string(),
66 uuid: parts[1].to_string(),
67 })
68 } else {
69 None
70 }
71 }
72
73 pub fn short(&self) -> String {
74 format!(
75 "{}:{}",
76 &self.pubkey[..8.min(self.pubkey.len())],
77 &self.uuid[..6.min(self.uuid.len())]
78 )
79 }
80}
81
82impl std::fmt::Display for PeerId {
83 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
84 write!(f, "{}:{}", self.pubkey, self.uuid)
85 }
86}
87
88#[derive(Debug, Clone, Serialize, Deserialize)]
90pub struct HelloMessage {
91 #[serde(rename = "type")]
92 pub msg_type: String,
93 #[serde(rename = "peerId")]
94 pub peer_id: String,
95}
96
97#[derive(Debug, Clone, Serialize, Deserialize)]
99pub struct OfferMessage {
100 #[serde(rename = "type")]
101 pub msg_type: String,
102 pub offer: serde_json::Value,
103 pub recipient: String,
104 #[serde(rename = "peerId")]
105 pub peer_id: String,
106}
107
108#[derive(Debug, Clone, Serialize, Deserialize)]
110pub struct AnswerMessage {
111 #[serde(rename = "type")]
112 pub msg_type: String,
113 pub answer: serde_json::Value,
114 pub recipient: String,
115 #[serde(rename = "peerId")]
116 pub peer_id: String,
117}
118
119#[derive(Debug, Clone, Serialize, Deserialize)]
121pub struct CandidateMessage {
122 #[serde(rename = "type")]
123 pub msg_type: String,
124 pub candidate: serde_json::Value,
125 pub recipient: String,
126 #[serde(rename = "peerId")]
127 pub peer_id: String,
128}
129
130#[derive(Debug, Clone, Serialize, Deserialize)]
132pub struct CandidatesMessage {
133 #[serde(rename = "type")]
134 pub msg_type: String,
135 pub candidates: Vec<serde_json::Value>,
136 pub recipient: String,
137 #[serde(rename = "peerId")]
138 pub peer_id: String,
139}
140
141#[derive(Debug, Clone, Serialize, Deserialize)]
143#[serde(tag = "type")]
144pub enum SignalingMessage {
145 #[serde(rename = "hello")]
146 Hello {
147 #[serde(rename = "peerId")]
148 peer_id: String,
149 },
150 #[serde(rename = "offer")]
151 Offer {
152 offer: serde_json::Value,
153 recipient: String,
154 #[serde(rename = "peerId")]
155 peer_id: String,
156 },
157 #[serde(rename = "answer")]
158 Answer {
159 answer: serde_json::Value,
160 recipient: String,
161 #[serde(rename = "peerId")]
162 peer_id: String,
163 },
164 #[serde(rename = "candidate")]
165 Candidate {
166 candidate: serde_json::Value,
167 recipient: String,
168 #[serde(rename = "peerId")]
169 peer_id: String,
170 },
171 #[serde(rename = "candidates")]
172 Candidates {
173 candidates: Vec<serde_json::Value>,
174 recipient: String,
175 #[serde(rename = "peerId")]
176 peer_id: String,
177 },
178}
179
180impl SignalingMessage {
181 pub fn msg_type(&self) -> &str {
182 match self {
183 SignalingMessage::Hello { .. } => "hello",
184 SignalingMessage::Offer { .. } => "offer",
185 SignalingMessage::Answer { .. } => "answer",
186 SignalingMessage::Candidate { .. } => "candidate",
187 SignalingMessage::Candidates { .. } => "candidates",
188 }
189 }
190
191 pub fn recipient(&self) -> Option<&str> {
192 match self {
193 SignalingMessage::Hello { .. } => None,
194 SignalingMessage::Offer { recipient, .. } => Some(recipient),
195 SignalingMessage::Answer { recipient, .. } => Some(recipient),
196 SignalingMessage::Candidate { recipient, .. } => Some(recipient),
197 SignalingMessage::Candidates { recipient, .. } => Some(recipient),
198 }
199 }
200
201 pub fn peer_id(&self) -> &str {
202 match self {
203 SignalingMessage::Hello { peer_id } => peer_id,
204 SignalingMessage::Offer { peer_id, .. } => peer_id,
205 SignalingMessage::Answer { peer_id, .. } => peer_id,
206 SignalingMessage::Candidate { peer_id, .. } => peer_id,
207 SignalingMessage::Candidates { peer_id, .. } => peer_id,
208 }
209 }
210
211 pub fn hello(peer_id: &str) -> Self {
212 SignalingMessage::Hello {
213 peer_id: peer_id.to_string(),
214 }
215 }
216
217 pub fn offer(offer: serde_json::Value, recipient: &str, peer_id: &str) -> Self {
218 SignalingMessage::Offer {
219 offer,
220 recipient: recipient.to_string(),
221 peer_id: peer_id.to_string(),
222 }
223 }
224
225 pub fn answer(answer: serde_json::Value, recipient: &str, peer_id: &str) -> Self {
226 SignalingMessage::Answer {
227 answer,
228 recipient: recipient.to_string(),
229 peer_id: peer_id.to_string(),
230 }
231 }
232
233 pub fn candidate(candidate: serde_json::Value, recipient: &str, peer_id: &str) -> Self {
234 SignalingMessage::Candidate {
235 candidate,
236 recipient: recipient.to_string(),
237 peer_id: peer_id.to_string(),
238 }
239 }
240}
241
242#[derive(Clone)]
244pub struct WebRTCConfig {
245 pub relays: Vec<String>,
247 pub max_outbound: usize,
249 pub max_inbound: usize,
251 pub hello_interval_ms: u64,
253 pub message_timeout_ms: u64,
255 pub stun_servers: Vec<String>,
257 pub debug: bool,
259 pub pools: PoolSettings,
261 pub request_selection_strategy: SelectionStrategy,
263 pub request_fairness_enabled: bool,
265 pub request_dispatch: RequestDispatchConfig,
267}
268
269impl Default for WebRTCConfig {
270 fn default() -> Self {
271 Self {
272 relays: vec![
273 "wss://relay.damus.io".to_string(),
274 "wss://relay.primal.net".to_string(),
275 "wss://nos.lol".to_string(),
276 "wss://temp.iris.to".to_string(),
277 "wss://relay.snort.social".to_string(),
278 ],
279 max_outbound: 6,
280 max_inbound: 6,
281 hello_interval_ms: 3000,
282 message_timeout_ms: 15000,
283 stun_servers: vec![
284 "stun:stun.iris.to:3478".to_string(),
285 "stun:stun.l.google.com:19302".to_string(),
286 "stun:stun.cloudflare.com:3478".to_string(),
287 ],
288 debug: false,
289 pools: PoolSettings::default(),
290 request_selection_strategy: SelectionStrategy::TitForTat,
291 request_fairness_enabled: true,
292 request_dispatch: RequestDispatchConfig {
293 initial_fanout: 2,
294 hedge_fanout: 1,
295 max_fanout: 8,
296 hedge_interval_ms: 120,
297 },
298 }
299 }
300}
301
302#[derive(Debug, Clone)]
304pub struct PeerStatus {
305 pub peer_id: String,
306 pub pubkey: String,
307 pub state: String,
308 pub direction: PeerDirection,
309 pub connected_at: Option<std::time::Instant>,
310 pub pool: PeerPool,
311}
312
313#[derive(Debug, Clone, Copy, PartialEq, Eq)]
315pub enum PeerDirection {
316 Inbound,
317 Outbound,
318}
319
320#[derive(Debug, Clone)]
322pub enum PeerStateEvent {
323 Connected(PeerId),
325 Failed(PeerId),
327 Disconnected(PeerId),
329}
330
331#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
333pub enum PeerPool {
334 Follows,
336 Other,
338}
339
340#[derive(Debug, Clone, Copy)]
342pub struct PoolConfig {
343 pub max_connections: usize,
345 pub satisfied_connections: usize,
347}
348
349impl Default for PoolConfig {
350 fn default() -> Self {
351 Self {
352 max_connections: 16,
353 satisfied_connections: 8,
354 }
355 }
356}
357
358#[derive(Debug, Clone)]
360pub struct PoolSettings {
361 pub follows: PoolConfig,
362 pub other: PoolConfig,
363}
364
365impl Default for PoolSettings {
366 fn default() -> Self {
367 Self {
368 follows: PoolConfig {
369 max_connections: 16,
370 satisfied_connections: 8,
371 },
372 other: PoolConfig {
373 max_connections: 16,
374 satisfied_connections: 8,
375 },
376 }
377 }
378}
379
380impl std::fmt::Display for PeerDirection {
381 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
382 match self {
383 PeerDirection::Inbound => write!(f, "inbound"),
384 PeerDirection::Outbound => write!(f, "outbound"),
385 }
386 }
387}
388
389pub const MSG_TYPE_REQUEST: u8 = 0x00;
391pub const MSG_TYPE_RESPONSE: u8 = 0x01;
392pub const MSG_TYPE_QUOTE_REQUEST: u8 = 0x02;
393pub const MSG_TYPE_QUOTE_RESPONSE: u8 = 0x03;
394pub const MSG_TYPE_PAYMENT: u8 = 0x04;
395pub const MSG_TYPE_PAYMENT_ACK: u8 = 0x05;
396pub const MSG_TYPE_CHUNK: u8 = 0x06;
397
398#[derive(Debug, Clone, Serialize, Deserialize)]
411pub struct DataRequest {
412 #[serde(with = "serde_bytes")]
413 pub h: Vec<u8>, #[serde(default = "default_htl", skip_serializing_if = "is_max_htl")]
415 pub htl: u8,
416 #[serde(skip_serializing_if = "Option::is_none")]
417 pub q: Option<u64>,
418}
419
420#[derive(Debug, Clone, Serialize, Deserialize)]
421pub struct DataResponse {
422 #[serde(with = "serde_bytes")]
423 pub h: Vec<u8>, #[serde(with = "serde_bytes")]
425 pub d: Vec<u8>, }
427
428#[derive(Debug, Clone, Serialize, Deserialize)]
429pub struct DataQuoteRequest {
430 #[serde(with = "serde_bytes")]
431 pub h: Vec<u8>,
432 pub p: u64,
433 pub t: u32,
434 #[serde(skip_serializing_if = "Option::is_none")]
435 pub m: Option<String>,
436}
437
438#[derive(Debug, Clone, Serialize, Deserialize)]
439pub struct DataQuoteResponse {
440 #[serde(with = "serde_bytes")]
441 pub h: Vec<u8>,
442 pub a: bool,
443 #[serde(skip_serializing_if = "Option::is_none")]
444 pub q: Option<u64>,
445 #[serde(skip_serializing_if = "Option::is_none")]
446 pub p: Option<u64>,
447 #[serde(skip_serializing_if = "Option::is_none")]
448 pub t: Option<u32>,
449 #[serde(skip_serializing_if = "Option::is_none")]
450 pub m: Option<String>,
451}
452
453#[derive(Debug, Clone, Serialize, Deserialize)]
454pub struct DataPayment {
455 #[serde(with = "serde_bytes")]
456 pub h: Vec<u8>,
457 pub q: u64,
458 pub c: u32,
459 pub p: u64,
460 #[serde(skip_serializing_if = "Option::is_none")]
461 pub m: Option<String>,
462 pub tok: String,
463}
464
465#[derive(Debug, Clone, Serialize, Deserialize)]
466pub struct DataPaymentAck {
467 #[serde(with = "serde_bytes")]
468 pub h: Vec<u8>,
469 pub q: u64,
470 pub c: u32,
471 pub a: bool,
472 #[serde(skip_serializing_if = "Option::is_none")]
473 pub e: Option<String>,
474}
475
476#[derive(Debug, Clone, Serialize, Deserialize)]
477pub struct DataChunk {
478 #[serde(with = "serde_bytes")]
479 pub h: Vec<u8>,
480 pub q: u64,
481 pub c: u32,
482 pub n: u32,
483 pub p: u64,
484 #[serde(with = "serde_bytes")]
485 pub d: Vec<u8>,
486}
487
488#[derive(Debug, Clone)]
489pub enum DataMessage {
490 Request(DataRequest),
491 Response(DataResponse),
492 QuoteRequest(DataQuoteRequest),
493 QuoteResponse(DataQuoteResponse),
494 Payment(DataPayment),
495 PaymentAck(DataPaymentAck),
496 Chunk(DataChunk),
497}
498
499fn default_htl() -> u8 {
500 BLOB_REQUEST_POLICY.max_htl
501}
502
503fn is_max_htl(htl: &u8) -> bool {
504 *htl == BLOB_REQUEST_POLICY.max_htl
505}
506
507pub fn encode_request(req: &DataRequest) -> Result<Vec<u8>, rmp_serde::encode::Error> {
510 let body = rmp_serde::to_vec_named(req)?;
511 let mut result = Vec::with_capacity(1 + body.len());
512 result.push(MSG_TYPE_REQUEST);
513 result.extend(body);
514 Ok(result)
515}
516
517pub fn encode_response(res: &DataResponse) -> Result<Vec<u8>, rmp_serde::encode::Error> {
520 let body = rmp_serde::to_vec_named(res)?;
521 let mut result = Vec::with_capacity(1 + body.len());
522 result.push(MSG_TYPE_RESPONSE);
523 result.extend(body);
524 Ok(result)
525}
526
527pub fn encode_quote_request(req: &DataQuoteRequest) -> Result<Vec<u8>, rmp_serde::encode::Error> {
529 let body = rmp_serde::to_vec_named(req)?;
530 let mut result = Vec::with_capacity(1 + body.len());
531 result.push(MSG_TYPE_QUOTE_REQUEST);
532 result.extend(body);
533 Ok(result)
534}
535
536pub fn encode_quote_response(res: &DataQuoteResponse) -> Result<Vec<u8>, rmp_serde::encode::Error> {
538 let body = rmp_serde::to_vec_named(res)?;
539 let mut result = Vec::with_capacity(1 + body.len());
540 result.push(MSG_TYPE_QUOTE_RESPONSE);
541 result.extend(body);
542 Ok(result)
543}
544
545pub fn encode_payment(req: &DataPayment) -> Result<Vec<u8>, rmp_serde::encode::Error> {
546 let body = rmp_serde::to_vec_named(req)?;
547 let mut result = Vec::with_capacity(1 + body.len());
548 result.push(MSG_TYPE_PAYMENT);
549 result.extend(body);
550 Ok(result)
551}
552
553pub fn encode_payment_ack(res: &DataPaymentAck) -> Result<Vec<u8>, rmp_serde::encode::Error> {
554 let body = rmp_serde::to_vec_named(res)?;
555 let mut result = Vec::with_capacity(1 + body.len());
556 result.push(MSG_TYPE_PAYMENT_ACK);
557 result.extend(body);
558 Ok(result)
559}
560
561pub fn encode_chunk(chunk: &DataChunk) -> Result<Vec<u8>, rmp_serde::encode::Error> {
562 let body = rmp_serde::to_vec_named(chunk)?;
563 let mut result = Vec::with_capacity(1 + body.len());
564 result.push(MSG_TYPE_CHUNK);
565 result.extend(body);
566 Ok(result)
567}
568
569pub fn parse_message(data: &[u8]) -> Result<DataMessage, rmp_serde::decode::Error> {
571 if data.is_empty() {
572 return Err(rmp_serde::decode::Error::LengthMismatch(0));
573 }
574
575 let msg_type = data[0];
576 let body = &data[1..];
577
578 match msg_type {
579 MSG_TYPE_REQUEST => {
580 let req: DataRequest = rmp_serde::from_slice(body)?;
581 Ok(DataMessage::Request(req))
582 }
583 MSG_TYPE_RESPONSE => {
584 let res: DataResponse = rmp_serde::from_slice(body)?;
585 Ok(DataMessage::Response(res))
586 }
587 MSG_TYPE_QUOTE_REQUEST => {
588 let req: DataQuoteRequest = rmp_serde::from_slice(body)?;
589 Ok(DataMessage::QuoteRequest(req))
590 }
591 MSG_TYPE_QUOTE_RESPONSE => {
592 let res: DataQuoteResponse = rmp_serde::from_slice(body)?;
593 Ok(DataMessage::QuoteResponse(res))
594 }
595 MSG_TYPE_PAYMENT => {
596 let req: DataPayment = rmp_serde::from_slice(body)?;
597 Ok(DataMessage::Payment(req))
598 }
599 MSG_TYPE_PAYMENT_ACK => {
600 let res: DataPaymentAck = rmp_serde::from_slice(body)?;
601 Ok(DataMessage::PaymentAck(res))
602 }
603 MSG_TYPE_CHUNK => {
604 let chunk: DataChunk = rmp_serde::from_slice(body)?;
605 Ok(DataMessage::Chunk(chunk))
606 }
607 _ => Err(rmp_serde::decode::Error::LengthMismatch(msg_type as u32)),
608 }
609}
610
611pub fn hash_to_hex(hash: &[u8]) -> String {
613 hash.iter().map(|b| format!("{:02x}", b)).collect()
614}
615
616pub fn encode_message(msg: &DataMessage) -> Result<Vec<u8>, rmp_serde::encode::Error> {
618 match msg {
619 DataMessage::Request(req) => encode_request(req),
620 DataMessage::Response(res) => encode_response(res),
621 DataMessage::QuoteRequest(req) => encode_quote_request(req),
622 DataMessage::QuoteResponse(res) => encode_quote_response(res),
623 DataMessage::Payment(req) => encode_payment(req),
624 DataMessage::PaymentAck(res) => encode_payment_ack(res),
625 DataMessage::Chunk(chunk) => encode_chunk(chunk),
626 }
627}