1use anyhow::Result;
5use git_remote_htree::nostr_client::hashtree_root_kinds;
6use nostr::{nips::nip19::FromBech32, Alphabet, Event, Filter, PublicKey, SingleLetterTag};
7use serde::{Deserialize, Serialize};
8use std::collections::BTreeSet;
9use std::collections::HashMap;
10use std::sync::atomic::{AtomicU64, Ordering};
11use std::sync::Arc;
12use std::time::Duration;
13use tokio::sync::RwLock;
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
17pub enum ConnectionState {
18 Discovered,
19 Connecting,
20 Connected,
21 Failed,
22 Disconnected,
23}
24
25#[derive(Debug)]
27pub struct PeerEntry {
28 pub peer_id: PeerId,
29 pub direction: PeerDirection,
30 pub state: ConnectionState,
31 pub last_seen: std::time::Instant,
32 pub peer: Option<DummyPeer>,
33 pub pool: PeerPool,
34 pub transport: PeerTransport,
35 pub signal_paths: BTreeSet<PeerSignalPath>,
36 pub bytes_sent: u64,
37 pub bytes_received: u64,
38}
39
40#[derive(Debug, Clone, PartialEq, Eq, Hash)]
42pub struct PeerId {
43 pub pubkey: String,
44}
45
46impl PeerId {
47 pub fn new(pubkey: String) -> Self {
48 Self { pubkey }
49 }
50
51 pub fn short(&self) -> String {
52 self.pubkey[..8.min(self.pubkey.len())].to_string()
53 }
54}
55
56impl std::fmt::Display for PeerId {
57 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
58 f.write_str(&self.pubkey)
59 }
60}
61
62#[derive(Debug, Clone, Copy, PartialEq, Eq)]
64pub enum PeerDirection {
65 Inbound,
66 Outbound,
67}
68
69#[derive(Debug, Clone, Copy, PartialEq, Eq)]
71pub enum PeerTransport {
72 WebRtc,
73 Bluetooth,
74}
75
76impl std::fmt::Display for PeerTransport {
77 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
78 match self {
79 PeerTransport::WebRtc => f.write_str("webrtc"),
80 PeerTransport::Bluetooth => f.write_str("bluetooth"),
81 }
82 }
83}
84
85#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
87pub enum PeerSignalPath {
88 Relay,
89 Multicast,
90 WifiAware,
91 Bluetooth,
92}
93
94impl std::fmt::Display for PeerSignalPath {
95 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
96 match self {
97 PeerSignalPath::Relay => f.write_str("relay"),
98 PeerSignalPath::Multicast => f.write_str("multicast"),
99 PeerSignalPath::WifiAware => f.write_str("wifi-aware"),
100 PeerSignalPath::Bluetooth => f.write_str("bluetooth"),
101 }
102 }
103}
104
105#[derive(Debug)]
107pub struct DummyPeer;
108
109impl DummyPeer {
110 pub fn is_ready(&self) -> bool {
111 false
112 }
113
114 pub fn has_data_channel(&self) -> bool {
115 false
116 }
117
118 pub fn state(&self) -> &str {
119 "Disabled"
120 }
121
122 pub fn as_webrtc(&self) -> Option<&Self> {
123 None
124 }
125
126 pub async fn request(&self, _hash: &str) -> Result<Option<Vec<u8>>> {
127 Ok(None)
128 }
129}
130
131#[derive(Debug, Clone, Copy)]
133pub enum PeerPool {
134 Follows,
135 Other,
136}
137
138#[derive(Debug, Clone)]
139pub struct PeerRootEvent {
140 pub hash: String,
141 pub key: Option<String>,
142 pub encrypted_key: Option<String>,
143 pub self_encrypted_key: Option<String>,
144 pub event_id: String,
145 pub created_at: u64,
146 pub peer_id: String,
147}
148
149#[derive(Debug)]
151pub struct WebRTCState {
152 pub peers: Arc<RwLock<HashMap<String, PeerEntry>>>,
153 bytes_sent: AtomicU64,
154 bytes_received: AtomicU64,
155}
156
157impl Default for WebRTCState {
158 fn default() -> Self {
159 Self {
160 peers: Arc::new(RwLock::new(HashMap::new())),
161 bytes_sent: AtomicU64::new(0),
162 bytes_received: AtomicU64::new(0),
163 }
164 }
165}
166
167impl WebRTCState {
168 pub fn new() -> Self {
169 Self::default()
170 }
171
172 pub async fn query_peers_for_data(&self, _hash: &str) -> Option<Vec<u8>> {
174 None
175 }
176
177 pub async fn request_from_peers(&self, _hash: &str) -> Option<Vec<u8>> {
179 None
180 }
181
182 pub async fn request_from_peers_with_source(&self, _hash: &str) -> Option<(Vec<u8>, String)> {
184 None
185 }
186
187 pub async fn record_sent(&self, peer_id: &str, bytes: u64) {
188 self.bytes_sent.fetch_add(bytes, Ordering::Relaxed);
189 if let Some(entry) = self.peers.write().await.get_mut(peer_id) {
190 entry.bytes_sent += bytes;
191 }
192 }
193
194 pub async fn record_received(&self, peer_id: &str, bytes: u64) {
195 self.bytes_received.fetch_add(bytes, Ordering::Relaxed);
196 if let Some(entry) = self.peers.write().await.get_mut(peer_id) {
197 entry.bytes_received += bytes;
198 }
199 }
200
201 pub fn get_bandwidth(&self) -> (u64, u64) {
203 (
204 self.bytes_sent.load(Ordering::Relaxed),
205 self.bytes_received.load(Ordering::Relaxed),
206 )
207 }
208
209 pub fn get_mesh_stats(&self) -> (u64, u64, u64) {
211 (0, 0, 0)
212 }
213
214 pub async fn resolve_root_from_peers(
216 &self,
217 _owner_pubkey: &str,
218 _tree_name: &str,
219 _per_peer_timeout: Duration,
220 ) -> Option<PeerRootEvent> {
221 None
222 }
223
224 pub async fn resolve_root_from_local_buses_with_source(
225 &self,
226 _owner_pubkey: &str,
227 _tree_name: &str,
228 _timeout: Duration,
229 ) -> Option<(&'static str, PeerRootEvent)> {
230 None
231 }
232}
233
234pub fn build_root_filter(owner_pubkey: &str, tree_name: &str) -> Option<Filter> {
235 let author = PublicKey::from_hex(owner_pubkey)
236 .or_else(|_| PublicKey::from_bech32(owner_pubkey))
237 .ok()?;
238 Some(
239 Filter::new()
240 .kinds(hashtree_root_kinds())
241 .author(author)
242 .custom_tag(
243 SingleLetterTag::lowercase(Alphabet::D),
244 tree_name.to_string(),
245 )
246 .custom_tag(SingleLetterTag::lowercase(Alphabet::L), "hashtree")
247 .limit(50),
248 )
249}
250
251pub fn pick_latest_event<'a, I>(events: I) -> Option<&'a Event>
252where
253 I: IntoIterator<Item = &'a Event>,
254{
255 events.into_iter().max_by(|a, b| {
256 let ordering = a.created_at.cmp(&b.created_at);
257 if ordering == std::cmp::Ordering::Equal {
258 a.id.cmp(&b.id)
259 } else {
260 ordering
261 }
262 })
263}
264
265pub fn root_event_from_peer(
266 event: &Event,
267 peer_id: &str,
268 tree_name: &str,
269) -> Option<PeerRootEvent> {
270 let mut tree_match = false;
271 let mut labeled = false;
272 let mut key = None;
273 let mut encrypted_key = None;
274 let mut self_encrypted_key = None;
275 let mut hash_tag = None;
276
277 for tag in event.tags.iter() {
278 let slice = tag.as_slice();
279 if slice.len() < 2 {
280 continue;
281 }
282 match slice[0].as_str() {
283 "d" => tree_match = slice[1].as_str() == tree_name,
284 "l" => labeled |= slice[1].as_str() == "hashtree",
285 "hash" => hash_tag = Some(slice[1].to_string()),
286 "key" => key = Some(slice[1].to_string()),
287 "encryptedKey" => encrypted_key = Some(slice[1].to_string()),
288 "selfEncryptedKey" => self_encrypted_key = Some(slice[1].to_string()),
289 _ => {}
290 }
291 }
292
293 if !tree_match || !labeled {
294 return None;
295 }
296
297 let hash = hash_tag.or_else(|| {
298 if event.content.is_empty() {
299 None
300 } else {
301 Some(event.content.clone())
302 }
303 })?;
304
305 Some(PeerRootEvent {
306 hash,
307 key,
308 encrypted_key,
309 self_encrypted_key,
310 event_id: event.id.to_hex(),
311 created_at: event.created_at.as_secs(),
312 peer_id: peer_id.to_string(),
313 })
314}
315
316pub trait ContentStore: Send + Sync + 'static {
318 fn get(&self, hash_hex: &str) -> Result<Option<Vec<u8>>>;
320}
321
322pub mod types {
323 use super::*;
324
325 pub const MAX_HTL: u8 = 7;
326 pub const MSG_TYPE_REQUEST: u8 = 0x00;
327 pub const MSG_TYPE_RESPONSE: u8 = 0x01;
328 pub const MSG_TYPE_QUOTE_REQUEST: u8 = 0x02;
329 pub const MSG_TYPE_QUOTE_RESPONSE: u8 = 0x03;
330 pub const MSG_TYPE_PAYMENT: u8 = 0x04;
331 pub const MSG_TYPE_PAYMENT_ACK: u8 = 0x05;
332 pub const MSG_TYPE_CHUNK: u8 = 0x06;
333 pub const MSG_TYPE_PEER_HINTS: u8 = 0x07;
334 pub const MSG_TYPE_PUBSUB_INTEREST: u8 = 0x08;
335 pub const MSG_TYPE_PUBSUB_FRAME: u8 = 0x09;
336 pub const MSG_TYPE_PUBSUB_INVENTORY: u8 = 0x0a;
337 pub const MSG_TYPE_PUBSUB_WANT: u8 = 0x0b;
338
339 #[derive(Debug, Clone, Serialize, Deserialize)]
340 pub struct DataRequest {
341 #[serde(with = "serde_bytes")]
342 pub h: Vec<u8>,
343 #[serde(default = "default_htl")]
344 pub htl: u8,
345 #[serde(skip_serializing_if = "Option::is_none")]
346 pub q: Option<u64>,
347 }
348
349 #[derive(Debug, Clone, Serialize, Deserialize)]
350 pub struct DataResponse {
351 #[serde(with = "serde_bytes")]
352 pub h: Vec<u8>,
353 #[serde(with = "serde_bytes")]
354 pub d: Vec<u8>,
355 #[serde(skip_serializing_if = "Option::is_none")]
356 pub i: Option<u32>,
357 #[serde(skip_serializing_if = "Option::is_none")]
358 pub n: Option<u32>,
359 }
360
361 #[derive(Debug, Clone, Serialize, Deserialize)]
362 pub struct DataQuoteRequest {
363 #[serde(with = "serde_bytes")]
364 pub h: Vec<u8>,
365 pub p: u64,
366 pub t: u32,
367 #[serde(skip_serializing_if = "Option::is_none")]
368 pub m: Option<String>,
369 }
370
371 #[derive(Debug, Clone, Serialize, Deserialize)]
372 pub struct DataQuoteResponse {
373 #[serde(with = "serde_bytes")]
374 pub h: Vec<u8>,
375 pub a: bool,
376 #[serde(skip_serializing_if = "Option::is_none")]
377 pub q: Option<u64>,
378 #[serde(skip_serializing_if = "Option::is_none")]
379 pub p: Option<u64>,
380 #[serde(skip_serializing_if = "Option::is_none")]
381 pub t: Option<u32>,
382 #[serde(skip_serializing_if = "Option::is_none")]
383 pub m: Option<String>,
384 }
385
386 #[derive(Debug, Clone, Serialize, Deserialize)]
387 pub struct DataPayment {
388 #[serde(with = "serde_bytes")]
389 pub h: Vec<u8>,
390 pub q: u64,
391 pub c: u32,
392 pub p: u64,
393 #[serde(skip_serializing_if = "Option::is_none")]
394 pub m: Option<String>,
395 pub tok: String,
396 }
397
398 #[derive(Debug, Clone, Serialize, Deserialize)]
399 pub struct DataPaymentAck {
400 #[serde(with = "serde_bytes")]
401 pub h: Vec<u8>,
402 pub q: u64,
403 pub c: u32,
404 pub a: bool,
405 #[serde(skip_serializing_if = "Option::is_none")]
406 pub e: Option<String>,
407 }
408
409 #[derive(Debug, Clone, Serialize, Deserialize)]
410 pub struct DataChunk {
411 #[serde(with = "serde_bytes")]
412 pub h: Vec<u8>,
413 pub q: u64,
414 pub c: u32,
415 pub n: u32,
416 pub p: u64,
417 #[serde(with = "serde_bytes")]
418 pub d: Vec<u8>,
419 }
420
421 #[derive(Debug, Clone, Serialize, Deserialize)]
422 pub struct PeerHints {
423 #[serde(default, rename = "u")]
424 pub signal_urls: Vec<String>,
425 }
426
427 #[derive(Debug, Clone, Serialize, Deserialize)]
428 pub struct PubsubInterest {
429 #[serde(rename = "s")]
430 pub stream_id: String,
431 #[serde(rename = "sub")]
432 pub subscriber_peer_id: String,
433 #[serde(rename = "q")]
434 pub seq: u64,
435 #[serde(rename = "a")]
436 pub active: bool,
437 #[serde(default = "default_htl", skip_serializing_if = "is_max_htl")]
438 pub htl: u8,
439 }
440
441 #[derive(Debug, Clone, Serialize, Deserialize)]
442 pub struct PubsubFrame {
443 #[serde(rename = "s")]
444 pub stream_id: String,
445 #[serde(rename = "q")]
446 pub seq: u64,
447 #[serde(rename = "o")]
448 pub origin_peer_id: String,
449 #[serde(default = "default_htl", skip_serializing_if = "is_max_htl")]
450 pub htl: u8,
451 #[serde(with = "serde_bytes", rename = "d")]
452 pub payload: Vec<u8>,
453 }
454
455 #[derive(Debug, Clone, Serialize, Deserialize)]
456 pub struct PubsubInventory {
457 #[serde(rename = "s")]
458 pub stream_id: String,
459 #[serde(rename = "q")]
460 pub seq: u64,
461 #[serde(rename = "o")]
462 pub origin_peer_id: String,
463 #[serde(rename = "b")]
464 pub payload_bytes: u64,
465 #[serde(default = "default_htl", skip_serializing_if = "is_max_htl")]
466 pub htl: u8,
467 }
468
469 #[derive(Debug, Clone, Serialize, Deserialize)]
470 pub struct PubsubWant {
471 #[serde(rename = "s")]
472 pub stream_id: String,
473 #[serde(rename = "q")]
474 pub seq: u64,
475 #[serde(rename = "o")]
476 pub origin_peer_id: String,
477 }
478
479 #[derive(Debug, Clone)]
480 pub enum DataMessage {
481 Request(DataRequest),
482 Response(DataResponse),
483 QuoteRequest(DataQuoteRequest),
484 QuoteResponse(DataQuoteResponse),
485 Payment(DataPayment),
486 PaymentAck(DataPaymentAck),
487 Chunk(DataChunk),
488 PeerHints(PeerHints),
489 PubsubInterest(PubsubInterest),
490 PubsubFrame(PubsubFrame),
491 PubsubInventory(PubsubInventory),
492 PubsubWant(PubsubWant),
493 }
494
495 fn default_htl() -> u8 {
496 MAX_HTL
497 }
498
499 fn is_max_htl(htl: &u8) -> bool {
500 *htl == MAX_HTL
501 }
502
503 pub fn encode_request(req: &DataRequest) -> Result<Vec<u8>, rmp_serde::encode::Error> {
504 let body = rmp_serde::to_vec_named(req)?;
505 let mut result = Vec::with_capacity(1 + body.len());
506 result.push(MSG_TYPE_REQUEST);
507 result.extend(body);
508 Ok(result)
509 }
510
511 pub fn encode_response(res: &DataResponse) -> Result<Vec<u8>, rmp_serde::encode::Error> {
512 let body = rmp_serde::to_vec_named(res)?;
513 let mut result = Vec::with_capacity(1 + body.len());
514 result.push(MSG_TYPE_RESPONSE);
515 result.extend(body);
516 Ok(result)
517 }
518
519 pub fn encode_quote_request(
520 req: &DataQuoteRequest,
521 ) -> Result<Vec<u8>, rmp_serde::encode::Error> {
522 let body = rmp_serde::to_vec_named(req)?;
523 let mut result = Vec::with_capacity(1 + body.len());
524 result.push(MSG_TYPE_QUOTE_REQUEST);
525 result.extend(body);
526 Ok(result)
527 }
528
529 pub fn encode_quote_response(
530 res: &DataQuoteResponse,
531 ) -> Result<Vec<u8>, rmp_serde::encode::Error> {
532 let body = rmp_serde::to_vec_named(res)?;
533 let mut result = Vec::with_capacity(1 + body.len());
534 result.push(MSG_TYPE_QUOTE_RESPONSE);
535 result.extend(body);
536 Ok(result)
537 }
538
539 pub fn encode_payment(req: &DataPayment) -> Result<Vec<u8>, rmp_serde::encode::Error> {
540 let body = rmp_serde::to_vec_named(req)?;
541 let mut result = Vec::with_capacity(1 + body.len());
542 result.push(MSG_TYPE_PAYMENT);
543 result.extend(body);
544 Ok(result)
545 }
546
547 pub fn encode_payment_ack(res: &DataPaymentAck) -> Result<Vec<u8>, rmp_serde::encode::Error> {
548 let body = rmp_serde::to_vec_named(res)?;
549 let mut result = Vec::with_capacity(1 + body.len());
550 result.push(MSG_TYPE_PAYMENT_ACK);
551 result.extend(body);
552 Ok(result)
553 }
554
555 pub fn encode_chunk(chunk: &DataChunk) -> Result<Vec<u8>, rmp_serde::encode::Error> {
556 let body = rmp_serde::to_vec_named(chunk)?;
557 let mut result = Vec::with_capacity(1 + body.len());
558 result.push(MSG_TYPE_CHUNK);
559 result.extend(body);
560 Ok(result)
561 }
562
563 pub fn parse_message(data: &[u8]) -> Result<DataMessage, rmp_serde::decode::Error> {
564 if data.is_empty() {
565 return Err(rmp_serde::decode::Error::LengthMismatch(0));
566 }
567
568 match data[0] {
569 MSG_TYPE_REQUEST => Ok(DataMessage::Request(rmp_serde::from_slice(&data[1..])?)),
570 MSG_TYPE_RESPONSE => Ok(DataMessage::Response(rmp_serde::from_slice(&data[1..])?)),
571 MSG_TYPE_QUOTE_REQUEST => Ok(DataMessage::QuoteRequest(rmp_serde::from_slice(
572 &data[1..],
573 )?)),
574 MSG_TYPE_QUOTE_RESPONSE => Ok(DataMessage::QuoteResponse(rmp_serde::from_slice(
575 &data[1..],
576 )?)),
577 MSG_TYPE_PAYMENT => Ok(DataMessage::Payment(rmp_serde::from_slice(&data[1..])?)),
578 MSG_TYPE_PAYMENT_ACK => Ok(DataMessage::PaymentAck(rmp_serde::from_slice(&data[1..])?)),
579 MSG_TYPE_CHUNK => Ok(DataMessage::Chunk(rmp_serde::from_slice(&data[1..])?)),
580 MSG_TYPE_PEER_HINTS => Ok(DataMessage::PeerHints(rmp_serde::from_slice(&data[1..])?)),
581 MSG_TYPE_PUBSUB_INTEREST => Ok(DataMessage::PubsubInterest(rmp_serde::from_slice(
582 &data[1..],
583 )?)),
584 MSG_TYPE_PUBSUB_FRAME => {
585 Ok(DataMessage::PubsubFrame(rmp_serde::from_slice(&data[1..])?))
586 }
587 MSG_TYPE_PUBSUB_INVENTORY => Ok(DataMessage::PubsubInventory(rmp_serde::from_slice(
588 &data[1..],
589 )?)),
590 MSG_TYPE_PUBSUB_WANT => Ok(DataMessage::PubsubWant(rmp_serde::from_slice(&data[1..])?)),
591 other => Err(rmp_serde::decode::Error::LengthMismatch(other as u32)),
592 }
593 }
594}