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
46fn configured_peer_uuid() -> Option<String> {
47 std::env::var("HTREE_PEER_UUID")
48 .ok()
49 .map(|value| value.trim().to_string())
50 .filter(|value| !value.is_empty())
51}
52
53#[derive(Debug, Clone, PartialEq, Eq, Hash)]
55pub struct PeerId {
56 pub pubkey: String,
57 pub uuid: String,
58}
59
60impl PeerId {
61 pub fn new(pubkey: String, uuid: Option<String>) -> Self {
62 Self {
63 pubkey,
64 uuid: uuid
65 .or_else(configured_peer_uuid)
66 .unwrap_or_else(generate_uuid),
67 }
68 }
69
70 pub fn from_string(s: &str) -> Option<Self> {
71 let parts: Vec<&str> = s.split(':').collect();
72 if parts.len() == 2 {
73 Some(Self {
74 pubkey: parts[0].to_string(),
75 uuid: parts[1].to_string(),
76 })
77 } else {
78 None
79 }
80 }
81
82 pub fn short(&self) -> String {
83 format!(
84 "{}:{}",
85 &self.pubkey[..8.min(self.pubkey.len())],
86 &self.uuid[..6.min(self.uuid.len())]
87 )
88 }
89}
90
91impl std::fmt::Display for PeerId {
92 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
93 write!(f, "{}:{}", self.pubkey, self.uuid)
94 }
95}
96
97#[derive(Debug, Clone, Serialize, Deserialize)]
99pub struct HelloMessage {
100 #[serde(rename = "type")]
101 pub msg_type: String,
102 #[serde(rename = "peerId")]
103 pub peer_id: String,
104}
105
106#[derive(Debug, Clone, Serialize, Deserialize)]
108pub struct OfferMessage {
109 #[serde(rename = "type")]
110 pub msg_type: String,
111 pub offer: serde_json::Value,
112 pub recipient: String,
113 #[serde(rename = "peerId")]
114 pub peer_id: String,
115}
116
117#[derive(Debug, Clone, Serialize, Deserialize)]
119pub struct AnswerMessage {
120 #[serde(rename = "type")]
121 pub msg_type: String,
122 pub answer: serde_json::Value,
123 pub recipient: String,
124 #[serde(rename = "peerId")]
125 pub peer_id: String,
126}
127
128#[derive(Debug, Clone, Serialize, Deserialize)]
130pub struct CandidateMessage {
131 #[serde(rename = "type")]
132 pub msg_type: String,
133 pub candidate: serde_json::Value,
134 pub recipient: String,
135 #[serde(rename = "peerId")]
136 pub peer_id: String,
137}
138
139#[derive(Debug, Clone, Serialize, Deserialize)]
141pub struct CandidatesMessage {
142 #[serde(rename = "type")]
143 pub msg_type: String,
144 pub candidates: Vec<serde_json::Value>,
145 pub recipient: String,
146 #[serde(rename = "peerId")]
147 pub peer_id: String,
148}
149
150#[derive(Debug, Clone, Serialize, Deserialize)]
152#[serde(tag = "type")]
153pub enum SignalingMessage {
154 #[serde(rename = "hello")]
155 Hello {
156 #[serde(rename = "peerId")]
157 peer_id: String,
158 },
159 #[serde(rename = "offer")]
160 Offer {
161 offer: serde_json::Value,
162 recipient: String,
163 #[serde(rename = "peerId")]
164 peer_id: String,
165 },
166 #[serde(rename = "answer")]
167 Answer {
168 answer: serde_json::Value,
169 recipient: String,
170 #[serde(rename = "peerId")]
171 peer_id: String,
172 },
173 #[serde(rename = "candidate")]
174 Candidate {
175 candidate: serde_json::Value,
176 recipient: String,
177 #[serde(rename = "peerId")]
178 peer_id: String,
179 },
180 #[serde(rename = "candidates")]
181 Candidates {
182 candidates: Vec<serde_json::Value>,
183 recipient: String,
184 #[serde(rename = "peerId")]
185 peer_id: String,
186 },
187}
188
189impl SignalingMessage {
190 pub fn msg_type(&self) -> &str {
191 match self {
192 SignalingMessage::Hello { .. } => "hello",
193 SignalingMessage::Offer { .. } => "offer",
194 SignalingMessage::Answer { .. } => "answer",
195 SignalingMessage::Candidate { .. } => "candidate",
196 SignalingMessage::Candidates { .. } => "candidates",
197 }
198 }
199
200 pub fn recipient(&self) -> Option<&str> {
201 match self {
202 SignalingMessage::Hello { .. } => None,
203 SignalingMessage::Offer { recipient, .. } => Some(recipient),
204 SignalingMessage::Answer { recipient, .. } => Some(recipient),
205 SignalingMessage::Candidate { recipient, .. } => Some(recipient),
206 SignalingMessage::Candidates { recipient, .. } => Some(recipient),
207 }
208 }
209
210 pub fn peer_id(&self) -> &str {
211 match self {
212 SignalingMessage::Hello { peer_id } => peer_id,
213 SignalingMessage::Offer { peer_id, .. } => peer_id,
214 SignalingMessage::Answer { peer_id, .. } => peer_id,
215 SignalingMessage::Candidate { peer_id, .. } => peer_id,
216 SignalingMessage::Candidates { peer_id, .. } => peer_id,
217 }
218 }
219
220 pub fn hello(peer_id: &str) -> Self {
221 SignalingMessage::Hello {
222 peer_id: peer_id.to_string(),
223 }
224 }
225
226 pub fn offer(offer: serde_json::Value, recipient: &str, peer_id: &str) -> Self {
227 SignalingMessage::Offer {
228 offer,
229 recipient: recipient.to_string(),
230 peer_id: peer_id.to_string(),
231 }
232 }
233
234 pub fn answer(answer: serde_json::Value, recipient: &str, peer_id: &str) -> Self {
235 SignalingMessage::Answer {
236 answer,
237 recipient: recipient.to_string(),
238 peer_id: peer_id.to_string(),
239 }
240 }
241
242 pub fn candidate(candidate: serde_json::Value, recipient: &str, peer_id: &str) -> Self {
243 SignalingMessage::Candidate {
244 candidate,
245 recipient: recipient.to_string(),
246 peer_id: peer_id.to_string(),
247 }
248 }
249}
250
251#[derive(Clone)]
253pub struct WebRTCConfig {
254 pub relays: Vec<String>,
256 pub signaling_enabled: bool,
258 pub max_outbound: usize,
260 pub max_inbound: usize,
262 pub hello_interval_ms: u64,
264 pub message_timeout_ms: u64,
266 pub stun_servers: Vec<String>,
268 pub debug: bool,
270 pub multicast: super::multicast::MulticastConfig,
272 pub wifi_aware: super::wifi_aware::WifiAwareConfig,
274 pub bluetooth: super::bluetooth::BluetoothConfig,
276 pub pools: PoolSettings,
278 pub request_selection_strategy: SelectionStrategy,
280 pub request_fairness_enabled: bool,
282 pub request_dispatch: RequestDispatchConfig,
284}
285
286impl Default for WebRTCConfig {
287 fn default() -> Self {
288 Self {
289 relays: vec![
290 "wss://relay.damus.io".to_string(),
291 "wss://relay.primal.net".to_string(),
292 "wss://nos.lol".to_string(),
293 "wss://temp.iris.to".to_string(),
294 "wss://relay.snort.social".to_string(),
295 ],
296 signaling_enabled: true,
297 max_outbound: 6,
298 max_inbound: 6,
299 hello_interval_ms: 3000,
300 message_timeout_ms: 15000,
301 stun_servers: vec![
302 "stun:stun.iris.to:3478".to_string(),
303 "stun:stun.l.google.com:19302".to_string(),
304 "stun:stun.cloudflare.com:3478".to_string(),
305 ],
306 debug: false,
307 multicast: super::multicast::MulticastConfig::default(),
308 wifi_aware: super::wifi_aware::WifiAwareConfig::default(),
309 bluetooth: super::bluetooth::BluetoothConfig::default(),
310 pools: PoolSettings::default(),
311 request_selection_strategy: SelectionStrategy::TitForTat,
312 request_fairness_enabled: true,
313 request_dispatch: RequestDispatchConfig {
314 initial_fanout: 2,
315 hedge_fanout: 1,
316 max_fanout: 8,
317 hedge_interval_ms: 120,
318 },
319 }
320 }
321}
322
323pub type PeerRouterConfig = WebRTCConfig;
324
325#[derive(Debug, Clone)]
327pub struct PeerStatus {
328 pub peer_id: String,
329 pub pubkey: String,
330 pub state: String,
331 pub direction: PeerDirection,
332 pub connected_at: Option<std::time::Instant>,
333 pub pool: PeerPool,
334}
335
336#[derive(Debug, Clone, Copy, PartialEq, Eq)]
338pub enum PeerDirection {
339 Inbound,
340 Outbound,
341}
342
343#[derive(Debug, Clone)]
345pub enum PeerStateEvent {
346 Connected(PeerId),
348 Failed(PeerId),
350 Disconnected(PeerId),
352}
353
354#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
356pub enum PeerPool {
357 Follows,
359 Other,
361}
362
363#[derive(Debug, Clone, Copy)]
365pub struct PoolConfig {
366 pub max_connections: usize,
368 pub satisfied_connections: usize,
370}
371
372impl Default for PoolConfig {
373 fn default() -> Self {
374 Self {
375 max_connections: 16,
376 satisfied_connections: 8,
377 }
378 }
379}
380
381#[derive(Debug, Clone)]
383pub struct PoolSettings {
384 pub follows: PoolConfig,
385 pub other: PoolConfig,
386}
387
388impl Default for PoolSettings {
389 fn default() -> Self {
390 Self {
391 follows: PoolConfig {
392 max_connections: 16,
393 satisfied_connections: 8,
394 },
395 other: PoolConfig {
396 max_connections: 16,
397 satisfied_connections: 8,
398 },
399 }
400 }
401}
402
403impl std::fmt::Display for PeerDirection {
404 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
405 match self {
406 PeerDirection::Inbound => write!(f, "inbound"),
407 PeerDirection::Outbound => write!(f, "outbound"),
408 }
409 }
410}
411
412pub const MSG_TYPE_REQUEST: u8 = 0x00;
414pub const MSG_TYPE_RESPONSE: u8 = 0x01;
415pub const MSG_TYPE_QUOTE_REQUEST: u8 = 0x02;
416pub const MSG_TYPE_QUOTE_RESPONSE: u8 = 0x03;
417pub const MSG_TYPE_PAYMENT: u8 = 0x04;
418pub const MSG_TYPE_PAYMENT_ACK: u8 = 0x05;
419pub const MSG_TYPE_CHUNK: u8 = 0x06;
420
421#[derive(Debug, Clone, Serialize, Deserialize)]
434pub struct DataRequest {
435 #[serde(with = "serde_bytes")]
436 pub h: Vec<u8>, #[serde(default = "default_htl", skip_serializing_if = "is_max_htl")]
438 pub htl: u8,
439 #[serde(skip_serializing_if = "Option::is_none")]
440 pub q: Option<u64>,
441}
442
443#[derive(Debug, Clone, Serialize, Deserialize)]
444pub struct DataResponse {
445 #[serde(with = "serde_bytes")]
446 pub h: Vec<u8>, #[serde(with = "serde_bytes")]
448 pub d: Vec<u8>, }
450
451#[derive(Debug, Clone, Serialize, Deserialize)]
452pub struct DataQuoteRequest {
453 #[serde(with = "serde_bytes")]
454 pub h: Vec<u8>,
455 pub p: u64,
456 pub t: u32,
457 #[serde(skip_serializing_if = "Option::is_none")]
458 pub m: Option<String>,
459}
460
461#[derive(Debug, Clone, Serialize, Deserialize)]
462pub struct DataQuoteResponse {
463 #[serde(with = "serde_bytes")]
464 pub h: Vec<u8>,
465 pub a: bool,
466 #[serde(skip_serializing_if = "Option::is_none")]
467 pub q: Option<u64>,
468 #[serde(skip_serializing_if = "Option::is_none")]
469 pub p: Option<u64>,
470 #[serde(skip_serializing_if = "Option::is_none")]
471 pub t: Option<u32>,
472 #[serde(skip_serializing_if = "Option::is_none")]
473 pub m: Option<String>,
474}
475
476#[derive(Debug, Clone, Serialize, Deserialize)]
477pub struct DataPayment {
478 #[serde(with = "serde_bytes")]
479 pub h: Vec<u8>,
480 pub q: u64,
481 pub c: u32,
482 pub p: u64,
483 #[serde(skip_serializing_if = "Option::is_none")]
484 pub m: Option<String>,
485 pub tok: String,
486}
487
488#[derive(Debug, Clone, Serialize, Deserialize)]
489pub struct DataPaymentAck {
490 #[serde(with = "serde_bytes")]
491 pub h: Vec<u8>,
492 pub q: u64,
493 pub c: u32,
494 pub a: bool,
495 #[serde(skip_serializing_if = "Option::is_none")]
496 pub e: Option<String>,
497}
498
499#[derive(Debug, Clone, Serialize, Deserialize)]
500pub struct DataChunk {
501 #[serde(with = "serde_bytes")]
502 pub h: Vec<u8>,
503 pub q: u64,
504 pub c: u32,
505 pub n: u32,
506 pub p: u64,
507 #[serde(with = "serde_bytes")]
508 pub d: Vec<u8>,
509}
510
511#[derive(Debug, Clone)]
512pub enum DataMessage {
513 Request(DataRequest),
514 Response(DataResponse),
515 QuoteRequest(DataQuoteRequest),
516 QuoteResponse(DataQuoteResponse),
517 Payment(DataPayment),
518 PaymentAck(DataPaymentAck),
519 Chunk(DataChunk),
520}
521
522fn default_htl() -> u8 {
523 BLOB_REQUEST_POLICY.max_htl
524}
525
526fn is_max_htl(htl: &u8) -> bool {
527 *htl == BLOB_REQUEST_POLICY.max_htl
528}
529
530pub fn encode_request(req: &DataRequest) -> Result<Vec<u8>, rmp_serde::encode::Error> {
533 let body = rmp_serde::to_vec_named(req)?;
534 let mut result = Vec::with_capacity(1 + body.len());
535 result.push(MSG_TYPE_REQUEST);
536 result.extend(body);
537 Ok(result)
538}
539
540pub fn encode_response(res: &DataResponse) -> Result<Vec<u8>, rmp_serde::encode::Error> {
543 let body = rmp_serde::to_vec_named(res)?;
544 let mut result = Vec::with_capacity(1 + body.len());
545 result.push(MSG_TYPE_RESPONSE);
546 result.extend(body);
547 Ok(result)
548}
549
550pub fn encode_quote_request(req: &DataQuoteRequest) -> Result<Vec<u8>, rmp_serde::encode::Error> {
552 let body = rmp_serde::to_vec_named(req)?;
553 let mut result = Vec::with_capacity(1 + body.len());
554 result.push(MSG_TYPE_QUOTE_REQUEST);
555 result.extend(body);
556 Ok(result)
557}
558
559pub fn encode_quote_response(res: &DataQuoteResponse) -> Result<Vec<u8>, rmp_serde::encode::Error> {
561 let body = rmp_serde::to_vec_named(res)?;
562 let mut result = Vec::with_capacity(1 + body.len());
563 result.push(MSG_TYPE_QUOTE_RESPONSE);
564 result.extend(body);
565 Ok(result)
566}
567
568pub fn encode_payment(req: &DataPayment) -> Result<Vec<u8>, rmp_serde::encode::Error> {
569 let body = rmp_serde::to_vec_named(req)?;
570 let mut result = Vec::with_capacity(1 + body.len());
571 result.push(MSG_TYPE_PAYMENT);
572 result.extend(body);
573 Ok(result)
574}
575
576pub fn encode_payment_ack(res: &DataPaymentAck) -> Result<Vec<u8>, rmp_serde::encode::Error> {
577 let body = rmp_serde::to_vec_named(res)?;
578 let mut result = Vec::with_capacity(1 + body.len());
579 result.push(MSG_TYPE_PAYMENT_ACK);
580 result.extend(body);
581 Ok(result)
582}
583
584pub fn encode_chunk(chunk: &DataChunk) -> Result<Vec<u8>, rmp_serde::encode::Error> {
585 let body = rmp_serde::to_vec_named(chunk)?;
586 let mut result = Vec::with_capacity(1 + body.len());
587 result.push(MSG_TYPE_CHUNK);
588 result.extend(body);
589 Ok(result)
590}
591
592pub fn parse_message(data: &[u8]) -> Result<DataMessage, rmp_serde::decode::Error> {
594 if data.is_empty() {
595 return Err(rmp_serde::decode::Error::LengthMismatch(0));
596 }
597
598 let msg_type = data[0];
599 let body = &data[1..];
600
601 match msg_type {
602 MSG_TYPE_REQUEST => {
603 let req: DataRequest = rmp_serde::from_slice(body)?;
604 Ok(DataMessage::Request(req))
605 }
606 MSG_TYPE_RESPONSE => {
607 let res: DataResponse = rmp_serde::from_slice(body)?;
608 Ok(DataMessage::Response(res))
609 }
610 MSG_TYPE_QUOTE_REQUEST => {
611 let req: DataQuoteRequest = rmp_serde::from_slice(body)?;
612 Ok(DataMessage::QuoteRequest(req))
613 }
614 MSG_TYPE_QUOTE_RESPONSE => {
615 let res: DataQuoteResponse = rmp_serde::from_slice(body)?;
616 Ok(DataMessage::QuoteResponse(res))
617 }
618 MSG_TYPE_PAYMENT => {
619 let req: DataPayment = rmp_serde::from_slice(body)?;
620 Ok(DataMessage::Payment(req))
621 }
622 MSG_TYPE_PAYMENT_ACK => {
623 let res: DataPaymentAck = rmp_serde::from_slice(body)?;
624 Ok(DataMessage::PaymentAck(res))
625 }
626 MSG_TYPE_CHUNK => {
627 let chunk: DataChunk = rmp_serde::from_slice(body)?;
628 Ok(DataMessage::Chunk(chunk))
629 }
630 _ => Err(rmp_serde::decode::Error::LengthMismatch(msg_type as u32)),
631 }
632}
633
634pub fn hash_to_hex(hash: &[u8]) -> String {
636 hash.iter().map(|b| format!("{:02x}", b)).collect()
637}
638
639pub fn encode_message(msg: &DataMessage) -> Result<Vec<u8>, rmp_serde::encode::Error> {
641 match msg {
642 DataMessage::Request(req) => encode_request(req),
643 DataMessage::Response(res) => encode_response(res),
644 DataMessage::QuoteRequest(req) => encode_quote_request(req),
645 DataMessage::QuoteResponse(res) => encode_quote_response(res),
646 DataMessage::Payment(req) => encode_payment(req),
647 DataMessage::PaymentAck(res) => encode_payment_ack(res),
648 DataMessage::Chunk(chunk) => encode_chunk(chunk),
649 }
650}