1use async_trait::async_trait;
8use futures::{stream::FuturesUnordered, FutureExt, StreamExt};
9use std::collections::hash_map::DefaultHasher;
10use std::collections::{HashMap, HashSet, VecDeque};
11use std::future::Future;
12use std::hash::{Hash as _, Hasher};
13use std::ops::Range;
14use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
15use std::sync::{Arc, Mutex as StdMutex};
16use std::time::Duration;
17use tokio::sync::{oneshot, Mutex, Notify, RwLock};
18use tokio::time::Instant;
19
20use hashtree_core::{BlobReply, BlobRequest, BlobRoute, Hash, Store, StoreError, BLOB_MAX_BYTES};
21
22use crate::peer_selector::{PeerMetadataSnapshot, PeerSelector, SelectionStrategy};
23use crate::protocol::{
24 create_pubsub_frame, create_pubsub_interest, create_pubsub_inventory, create_pubsub_want,
25 create_quote_request, create_quote_response_available, create_quote_response_unavailable,
26 create_request, create_request_with_quote, create_response, encode_pubsub_frame,
27 encode_pubsub_interest, encode_pubsub_inventory, encode_pubsub_want, encode_quote_request,
28 encode_quote_response, encode_request, encode_response, hash_to_key, parse_message,
29 DataMessage, DataQuoteRequest, DataQuoteResponse, PubsubFrame, PubsubInterest, PubsubInventory,
30 PubsubWant,
31};
32use crate::pubsub_strategy::{
33 reciprocal_virtual_finish, select_reciprocal_outbound_job, OutboundJobCandidate,
34 PeerTrafficSnapshot, PubsubCandidate, PubsubSchedulerConfig,
35};
36use crate::signaling::MeshRouter;
37use crate::transport::{PeerLinkFactory, SignalingTransport, TransportError};
38use crate::types::{
39 should_forward_htl, PeerHTLConfig, SignalingMessage, TimedSeenSet, MAX_HTL, MESH_EVENT_POLICY,
40};
41
42const PEER_METADATA_POINTER_SLOT_KEY: &[u8] = b"hashtree-mesh/peer-metadata/latest/v1";
45const PUBSUB_SEEN_CAPACITY: usize = 16_384;
46const PUBSUB_INBOX_CAPACITY: usize = 4_096;
47const PUBSUB_FRAME_CACHE_CAPACITY: usize = 4_096;
48const VERIFIED_BLOCK_DELIVERY_CAPACITY: usize = 4_096;
49const PUBSUB_SEEN_TTL: Duration = Duration::from_secs(120);
50
51struct PendingRequest {
53 owner: Arc<()>,
54 response_tx: oneshot::Sender<Option<Vec<u8>>>,
55 started_at: Instant,
56 queried_peers: Vec<String>,
57}
58
59#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
60struct PendingRequestKey {
61 hash: Hash,
62 htl: u8,
63}
64
65impl PendingRequestKey {
66 fn new(hash: Hash, htl: u8) -> Self {
67 Self { hash, htl }
68 }
69}
70
71struct PendingQuoteRequest {
72 response_tx: oneshot::Sender<Option<NegotiatedQuote>>,
73 preferred_mint_url: Option<String>,
74 offered_payment_sat: u64,
75}
76
77struct PendingForwardRequest {
78 requester_ids: HashSet<String>,
79}
80
81type PeerWireStats = PeerTrafficSnapshot;
82
83struct PendingResponseSend {
84 job_id: u64,
85 peer_id: String,
86 bytes: Vec<u8>,
87 ready_at: Instant,
88 queue_sequence: u64,
89}
90
91#[derive(Clone, Copy, Debug, PartialEq, Eq)]
92pub enum BlobRouteKind {
93 Terminal,
94 MeshPeer,
95}
96
97pub trait MeshReadSource: BlobRoute {
98 fn id(&self) -> &str;
99
100 fn kind(&self) -> BlobRouteKind {
101 BlobRouteKind::Terminal
102 }
103
104 fn is_available(&self) -> bool {
105 true
106 }
107}
108
109pub struct NamedBlobRoute {
110 id: String,
111 kind: BlobRouteKind,
112 route: Arc<dyn BlobRoute>,
113}
114
115impl NamedBlobRoute {
116 pub fn terminal(id: impl Into<String>, route: Arc<dyn BlobRoute>) -> Self {
117 Self {
118 id: id.into(),
119 kind: BlobRouteKind::Terminal,
120 route,
121 }
122 }
123
124 pub fn mesh_peer(id: impl Into<String>, route: Arc<dyn BlobRoute>) -> Self {
125 Self {
126 id: id.into(),
127 kind: BlobRouteKind::MeshPeer,
128 route,
129 }
130 }
131}
132
133#[async_trait]
134impl BlobRoute for NamedBlobRoute {
135 async fn route(&self, request: BlobRequest) -> Result<BlobReply, StoreError> {
136 self.route.route(request).await
137 }
138}
139
140impl MeshReadSource for NamedBlobRoute {
141 fn id(&self) -> &str {
142 &self.id
143 }
144
145 fn kind(&self) -> BlobRouteKind {
146 self.kind
147 }
148}
149
150#[derive(Debug, Clone)]
151struct NegotiatedQuote {
152 peer_id: String,
153 quote_id: u64,
154 #[allow(dead_code)]
155 mint_url: Option<String>,
156}
157
158struct IssuedQuote {
159 expires_at: Instant,
160 #[allow(dead_code)]
161 payment_sat: u64,
162 #[allow(dead_code)]
163 mint_url: Option<String>,
164}
165
166#[derive(Debug, Clone, Default)]
167struct AdaptiveSourceStats {
168 requests: u64,
169 successes: u64,
170 misses: u64,
171 failures: u64,
172 timeouts: u64,
173 srtt_ms: f64,
174 rttvar_ms: f64,
175 backoff_level: u32,
176 backed_off_until: Option<Instant>,
177 last_success_at: Option<Instant>,
178 last_failure_at: Option<Instant>,
179}
180
181#[derive(Debug, Clone)]
182enum RouteFetchOutcome {
183 Hit(Vec<u8>),
184 Miss,
185 Timeout,
186 Failure,
187}
188
189impl RouteFetchOutcome {
190 fn combine_without_hit(self, other: Self) -> Self {
191 match (self, other) {
192 (Self::Hit(data), _) | (_, Self::Hit(data)) => Self::Hit(data),
193 (Self::Failure, _) | (_, Self::Failure) => Self::Failure,
194 (Self::Timeout, _) | (_, Self::Timeout) => Self::Timeout,
195 (Self::Miss, Self::Miss) => Self::Miss,
196 }
197 }
198}
199
200struct InflightSourceFetch {
201 owner: Arc<()>,
202 waiters: Vec<oneshot::Sender<RouteFetchOutcome>>,
203}
204
205fn complete_inflight_source_fetch(
206 inflight_source_fetches: &StdMutex<HashMap<String, InflightSourceFetch>>,
207 hash_key: &str,
208 expected_owner: &Arc<()>,
209 result: RouteFetchOutcome,
210) {
211 let mut inflight = inflight_source_fetches
212 .lock()
213 .unwrap_or_else(|poisoned| poisoned.into_inner());
214 let owns_entry = inflight
215 .get(hash_key)
216 .is_some_and(|entry| Arc::ptr_eq(&entry.owner, expected_owner));
217 let waiters = owns_entry
218 .then(|| inflight.remove(hash_key))
219 .flatten()
220 .map(|inflight| inflight.waiters)
221 .unwrap_or_default();
222 drop(inflight);
223 for waiter in waiters {
224 let _ = waiter.send(result.clone());
225 }
226}
227
228struct InflightSourceFetchGuard<'a> {
234 inflight_source_fetches: &'a StdMutex<HashMap<String, InflightSourceFetch>>,
235 hash_key: String,
236 owner: Arc<()>,
237}
238
239impl InflightSourceFetchGuard<'_> {
240 fn complete(self, result: RouteFetchOutcome) {
241 complete_inflight_source_fetch(
242 self.inflight_source_fetches,
243 &self.hash_key,
244 &self.owner,
245 result,
246 );
247 }
248}
249
250impl Drop for InflightSourceFetchGuard<'_> {
251 fn drop(&mut self) {
252 complete_inflight_source_fetch(
253 self.inflight_source_fetches,
254 &self.hash_key,
255 &self.owner,
256 RouteFetchOutcome::Timeout,
257 );
258 }
259}
260
261enum SourceFetchOutcome {
262 Hit {
263 source_id: String,
264 data: Vec<u8>,
265 elapsed_ms: u64,
266 },
267 Miss {
268 source_id: String,
269 },
270 Failure {
271 source_id: String,
272 },
273}
274
275const INITIAL_SOURCE_BACKOFF_MS: u64 = 250;
276const MAX_SOURCE_BACKOFF_MS: u64 = 10_000;
277const SOURCE_SCORE_TIE_DELTA: f64 = 0.15;
278const RECENT_SOURCE_SUCCESS_WINDOW: Duration = Duration::from_secs(60);
279const ACTIVE_PEER_REQUEST_RANK_PENALTY: usize = 3;
280
281fn source_reliability_score(stats: &AdaptiveSourceStats) -> f64 {
282 (stats.successes as f64 + 1.0) / (stats.requests as f64 + 2.0)
283}
284
285fn source_latency_score(stats: &AdaptiveSourceStats) -> f64 {
286 if stats.srtt_ms <= 0.0 {
287 return 0.5;
288 }
289 (500.0 / (stats.srtt_ms + 50.0)).min(1.0)
290}
291
292fn source_has_history(stats: &AdaptiveSourceStats) -> bool {
293 stats.requests > 0
294 || stats.successes > 0
295 || stats.misses > 0
296 || stats.failures > 0
297 || stats.timeouts > 0
298}
299
300fn adaptive_source_score(stats: &AdaptiveSourceStats, now: Instant) -> f64 {
301 if let Some(backed_off_until) = stats.backed_off_until {
302 if backed_off_until > now {
303 return f64::NEG_INFINITY;
304 }
305 }
306
307 let miss_penalty = if stats.requests > 0 {
308 (stats.misses as f64 / stats.requests as f64) * 0.15
309 } else {
310 0.0
311 };
312 let failure_penalty = if stats.requests > 0 {
313 ((stats.failures + stats.timeouts) as f64 / stats.requests as f64) * 0.3
314 } else {
315 0.0
316 };
317 let recency_bonus = if stats
318 .last_success_at
319 .is_some_and(|last| now.duration_since(last) < RECENT_SOURCE_SUCCESS_WINDOW)
320 {
321 0.1
322 } else {
323 0.0
324 };
325
326 0.6 * source_reliability_score(stats) + 0.3 * source_latency_score(stats) + recency_bonus
327 - miss_penalty
328 - failure_penalty
329}
330
331fn peer_endpoint_has_history(stats: &crate::peer_selector::PeerStats) -> bool {
332 stats.requests_sent > 0 || stats.successes > 0 || stats.failures > 0 || stats.timeouts > 0
333}
334
335fn peer_endpoint_score(stats: &crate::peer_selector::PeerStats, now: Instant) -> f64 {
336 if stats.backed_off_until.is_some_and(|until| until > now) {
337 return f64::NEG_INFINITY;
338 }
339
340 let miss_penalty = 0.0;
341 let failure_penalty = if stats.requests_sent > 0 {
342 ((stats.failures + stats.timeouts) as f64 / stats.requests_sent as f64) * 0.3
343 } else {
344 0.0
345 };
346 let recency_bonus = if stats
347 .last_success
348 .is_some_and(|last| now.duration_since(last) < RECENT_SOURCE_SUCCESS_WINDOW)
349 {
350 0.1
351 } else {
352 0.0
353 };
354
355 0.6 * stats.success_rate()
356 + 0.3
357 * source_latency_score(&AdaptiveSourceStats {
358 srtt_ms: stats.srtt_ms,
359 ..AdaptiveSourceStats::default()
360 })
361 + recency_bonus
362 - miss_penalty
363 - failure_penalty
364}
365
366#[derive(Clone)]
367enum ReadRoute {
368 Peers(Vec<String>),
369 Sources,
370}
371
372impl ReadRoute {
373 fn id(&self) -> &'static str {
374 match self {
375 Self::Peers(_) => "peers",
376 Self::Sources => "sources",
377 }
378 }
379}
380
381struct RankedReadRoute {
382 route: ReadRoute,
383 best_endpoint_id: String,
384 score: f64,
385 has_history: bool,
386}
387
388fn ranked_route_kind(route: &ReadRoute) -> u8 {
389 match route {
390 ReadRoute::Sources => 0,
391 ReadRoute::Peers(_) => 1,
392 }
393}
394
395#[derive(Debug, Clone)]
396struct MeshReadContext {
397 exclude_peer_id: Option<String>,
398 request_htl: u8,
399}
400
401impl Default for MeshReadContext {
402 fn default() -> Self {
403 Self {
404 exclude_peer_id: None,
405 request_htl: MAX_HTL,
406 }
407 }
408}
409
410#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
412pub struct DataPumpStats {
413 pub processed: usize,
414 pub request_messages: usize,
415 pub response_messages: usize,
416 pub quote_request_messages: u64,
417 pub quote_response_messages: u64,
418 pub pubsub_interest_messages: u64,
419 pub pubsub_frame_messages: u64,
420 pub pubsub_inventory_messages: u64,
421 pub pubsub_want_messages: u64,
422 pub processed_bytes: u64,
423}
424
425#[derive(Debug, Clone, PartialEq, Eq)]
427pub struct PubsubEvent {
428 pub stream_id: String,
429 pub seq: u64,
430 pub origin_peer_id: String,
431 pub from_peer_id: String,
432 pub payload: Vec<u8>,
433}
434
435#[derive(Debug, Clone, PartialEq, Eq)]
437pub struct VerifiedBlockDelivery {
438 pub hash: Hash,
439 pub provider_peer_id: String,
440 pub payload_bytes: u64,
441}
442
443#[derive(Debug, Clone, Default, PartialEq, Eq)]
448pub struct VerifiedBlockDeliveryBatch {
449 pub deliveries: Vec<VerifiedBlockDelivery>,
450 pub dropped_since_last_drain: u64,
451}
452
453#[derive(Default)]
454struct VerifiedBlockDeliveryBuffer {
455 deliveries: VecDeque<VerifiedBlockDelivery>,
456 dropped_since_last_drain: u64,
457}
458
459#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
461pub struct PubsubPublishStats {
462 pub selected_peers: usize,
463 pub sent_peers: usize,
464 pub sent_bytes: u64,
465 pub deferred_peers: usize,
466}
467
468#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
470pub enum PubsubDeliveryMode {
471 InterestPush,
473 #[default]
475 HtlInvWant,
476}
477
478#[derive(Debug, Clone, Copy)]
484pub struct RequestDispatchConfig {
485 pub initial_fanout: usize,
487 pub hedge_fanout: usize,
489 pub max_fanout: usize,
491 pub hedge_interval_ms: u64,
493}
494
495impl Default for RequestDispatchConfig {
496 fn default() -> Self {
497 Self {
498 initial_fanout: 2,
499 hedge_fanout: 1,
500 max_fanout: 4,
501 hedge_interval_ms: 50,
502 }
503 }
504}
505
506pub fn normalize_dispatch_config(
508 dispatch: RequestDispatchConfig,
509 available_peers: usize,
510) -> RequestDispatchConfig {
511 let mut cfg = dispatch;
512 let cap = if cfg.max_fanout == 0 {
513 available_peers
514 } else {
515 cfg.max_fanout.min(available_peers)
516 };
517 cfg.max_fanout = cap;
518 cfg.initial_fanout = if cfg.initial_fanout == 0 {
519 1
520 } else {
521 cfg.initial_fanout.min(cap.max(1))
522 };
523 cfg.hedge_fanout = if cfg.hedge_fanout == 0 {
524 1
525 } else {
526 cfg.hedge_fanout.min(cap.max(1))
527 };
528 cfg
529}
530
531pub fn build_hedged_wave_plan(peer_count: usize, dispatch: RequestDispatchConfig) -> Vec<usize> {
533 if peer_count == 0 {
534 return Vec::new();
535 }
536 let cap = dispatch.max_fanout.min(peer_count);
537 if cap == 0 {
538 return Vec::new();
539 }
540
541 let mut plan = Vec::new();
542 let mut sent = 0usize;
543 let first = dispatch.initial_fanout.min(cap).max(1);
544 plan.push(first);
545 sent += first;
546
547 while sent < cap {
548 let next = dispatch.hedge_fanout.min(cap - sent).max(1);
549 plan.push(next);
550 sent += next;
551 }
552 plan
553}
554
555#[derive(Debug)]
557pub enum HedgedWaveAction<T> {
558 Continue,
559 Success(T),
560 Abort,
561}
562
563pub async fn run_hedged_waves<T, SendWave, SendWaveFut, WaitWave, WaitWaveFut>(
568 peer_count: usize,
569 dispatch: RequestDispatchConfig,
570 request_timeout: Duration,
571 mut send_wave: SendWave,
572 mut wait_wave: WaitWave,
573) -> Option<T>
574where
575 SendWave: FnMut(Range<usize>) -> SendWaveFut,
576 SendWaveFut: Future<Output = usize>,
577 WaitWave: FnMut(Duration) -> WaitWaveFut,
578 WaitWaveFut: Future<Output = HedgedWaveAction<T>>,
579{
580 let dispatch = normalize_dispatch_config(dispatch, peer_count);
581 let wave_plan = build_hedged_wave_plan(peer_count, dispatch);
582 if wave_plan.is_empty() {
583 return None;
584 }
585
586 let deadline = Instant::now() + request_timeout;
587 let mut sent_total = 0usize;
588 let mut next_peer_idx = 0usize;
589
590 for (wave_idx, wave_size) in wave_plan.iter().copied().enumerate() {
591 let from = next_peer_idx;
592 let to = (next_peer_idx + wave_size).min(peer_count);
593 next_peer_idx = to;
594
595 if from == to {
596 continue;
597 }
598
599 sent_total += send_wave(from..to).await;
600 if sent_total == 0 {
601 if next_peer_idx >= peer_count {
602 break;
603 }
604 continue;
605 }
606
607 let now = Instant::now();
608 if now >= deadline {
609 break;
610 }
611 let remaining = deadline.saturating_duration_since(now);
612 let is_last_wave = wave_idx + 1 == wave_plan.len() || next_peer_idx >= peer_count;
613 let wait = if is_last_wave {
614 remaining
615 } else if dispatch.hedge_interval_ms == 0 {
616 Duration::ZERO
617 } else {
618 Duration::from_millis(dispatch.hedge_interval_ms).min(remaining)
619 };
620
621 if wait.is_zero() {
622 continue;
623 }
624
625 match wait_wave(wait).await {
626 HedgedWaveAction::Continue => {}
627 HedgedWaveAction::Success(value) => return Some(value),
628 HedgedWaveAction::Abort => break,
629 }
630 }
631
632 None
633}
634
635pub async fn sync_selector_peers(selector: &RwLock<PeerSelector>, current_peer_ids: &[String]) {
637 let mut selector = selector.write().await;
638 let current: HashSet<&str> = current_peer_ids.iter().map(String::as_str).collect();
639 let known: Vec<String> = selector.all_stats().map(|s| s.peer_id.clone()).collect();
640 for peer_id in known {
641 if !current.contains(peer_id.as_str()) {
642 selector.remove_peer(&peer_id);
643 }
644 }
645 for peer_id in current_peer_ids {
646 selector.add_peer(peer_id.clone());
647 }
648}
649
650#[derive(Debug, Clone, Copy)]
654pub struct ResponseBehaviorConfig {
655 pub drop_response_prob: f64,
657 pub corrupt_response_prob: f64,
659 pub extra_delay_ms: u64,
661 pub first_byte_delay_ms: u64,
663 pub bytes_per_second: u64,
665 pub stall_response_prob: f64,
667 pub stall_delay_ms: u64,
669}
670
671impl Default for ResponseBehaviorConfig {
672 fn default() -> Self {
673 Self {
674 drop_response_prob: 0.0,
675 corrupt_response_prob: 0.0,
676 extra_delay_ms: 0,
677 first_byte_delay_ms: 0,
678 bytes_per_second: 0,
679 stall_response_prob: 0.0,
680 stall_delay_ms: 0,
681 }
682 }
683}
684
685impl ResponseBehaviorConfig {
686 fn normalized(self) -> Self {
687 Self {
688 drop_response_prob: self.drop_response_prob.clamp(0.0, 1.0),
689 corrupt_response_prob: self.corrupt_response_prob.clamp(0.0, 1.0),
690 extra_delay_ms: self.extra_delay_ms,
691 first_byte_delay_ms: self.first_byte_delay_ms,
692 bytes_per_second: self.bytes_per_second,
693 stall_response_prob: self.stall_response_prob.clamp(0.0, 1.0),
694 stall_delay_ms: self.stall_delay_ms,
695 }
696 }
697}
698
699#[derive(Debug, Clone)]
701pub struct MeshRoutingConfig {
702 pub selection_strategy: SelectionStrategy,
703 pub fairness_enabled: bool,
704 pub cashu_payment_weight: f64,
706 pub cashu_payment_default_block_threshold: u64,
709 pub cashu_accepted_mints: Vec<String>,
711 pub cashu_default_mint: Option<String>,
713 pub cashu_peer_suggested_mint_base_cap_sat: u64,
715 pub cashu_peer_suggested_mint_success_step_sat: u64,
717 pub cashu_peer_suggested_mint_receipt_step_sat: u64,
719 pub cashu_peer_suggested_mint_max_cap_sat: u64,
721 pub dispatch: RequestDispatchConfig,
722 pub response_behavior: ResponseBehaviorConfig,
723 pub pubsub_scheduler: PubsubSchedulerConfig,
724 pub pubsub_delivery_mode: PubsubDeliveryMode,
725 pub pubsub_forwarding: bool,
727 pub pubsub_max_htl: u8,
729}
730
731impl Default for MeshRoutingConfig {
732 fn default() -> Self {
733 Self {
734 selection_strategy: SelectionStrategy::Weighted,
735 fairness_enabled: true,
736 cashu_payment_weight: 0.0,
737 cashu_payment_default_block_threshold: 0,
738 cashu_accepted_mints: Vec::new(),
739 cashu_default_mint: None,
740 cashu_peer_suggested_mint_base_cap_sat: 0,
741 cashu_peer_suggested_mint_success_step_sat: 0,
742 cashu_peer_suggested_mint_receipt_step_sat: 0,
743 cashu_peer_suggested_mint_max_cap_sat: 0,
744 dispatch: RequestDispatchConfig::default(),
745 response_behavior: ResponseBehaviorConfig::default(),
746 pubsub_scheduler: PubsubSchedulerConfig::default(),
747 pubsub_delivery_mode: PubsubDeliveryMode::HtlInvWant,
748 pubsub_forwarding: true,
749 pubsub_max_htl: MESH_EVENT_POLICY.max_htl,
750 }
751 }
752}
753
754impl MeshRoutingConfig {
755 fn pubsub_initial_htl(&self) -> u8 {
756 self.pubsub_max_htl.clamp(1, MAX_HTL)
757 }
758}
759
760pub struct MeshStoreCore<S, R, F>
767where
768 S: Store + Send + Sync + 'static,
769 R: SignalingTransport + Send + Sync + 'static,
770 F: PeerLinkFactory + Send + Sync + 'static,
771{
772 local_store: Arc<S>,
774 signaling: Arc<MeshRouter<R, F>>,
776 htl_configs: RwLock<HashMap<String, PeerHTLConfig>>,
778 pending_requests: RwLock<HashMap<PendingRequestKey, Vec<PendingRequest>>>,
780 pending_quotes: RwLock<HashMap<String, PendingQuoteRequest>>,
782 pending_forward_requests: RwLock<HashMap<PendingRequestKey, PendingForwardRequest>>,
784 issued_quotes: RwLock<HashMap<(String, String, u64), IssuedQuote>>,
787 next_quote_id: RwLock<u64>,
789 read_sources: RwLock<HashMap<String, Arc<dyn MeshReadSource>>>,
791 read_source_stats: RwLock<HashMap<String, AdaptiveSourceStats>>,
793 inflight_source_fetches: StdMutex<HashMap<String, InflightSourceFetch>>,
795 peer_selector: RwLock<PeerSelector>,
797 peer_active_requests: RwLock<HashMap<String, usize>>,
799 peer_wire_stats: RwLock<HashMap<String, PeerWireStats>>,
801 pubsub_local_interests: RwLock<HashSet<String>>,
803 pubsub_local_interest_versions: RwLock<HashMap<String, u64>>,
805 pubsub_peer_interests: RwLock<HashMap<String, HashSet<String>>>,
807 pubsub_interest_routes: RwLock<HashMap<(String, String), String>>,
809 pubsub_interest_versions: RwLock<HashMap<(String, String), u64>>,
811 pubsub_seen_interests: Mutex<TimedSeenSet>,
813 pubsub_seen_frames: Mutex<TimedSeenSet>,
815 pubsub_seen_inventories: Mutex<TimedSeenSet>,
817 pubsub_seen_wants: Mutex<TimedSeenSet>,
819 pubsub_inventory_routes: RwLock<HashMap<String, String>>,
821 pubsub_want_routes: RwLock<HashMap<String, HashSet<String>>>,
823 pubsub_upstream_wants: Mutex<TimedSeenSet>,
825 pubsub_frame_cache: Mutex<VecDeque<(String, PubsubFrame)>>,
827 pubsub_inbox: Mutex<VecDeque<PubsubEvent>>,
829 verified_block_deliveries: Mutex<VerifiedBlockDeliveryBuffer>,
831 pubsub_notify: Notify,
833 pubsub_deferred_counts: RwLock<HashMap<(String, String), u64>>,
835 next_pubsub_interest_seq: AtomicU64,
837 pending_response_sends: Mutex<Vec<PendingResponseSend>>,
839 response_scheduler_running: AtomicBool,
841 next_response_job_id: AtomicU64,
843 routing: MeshRoutingConfig,
845 request_timeout: Duration,
847 debug: bool,
849 running: RwLock<bool>,
851}
852
853impl<S, R, F> MeshStoreCore<S, R, F>
854where
855 S: Store + Send + Sync + 'static,
856 R: SignalingTransport + Send + Sync + 'static,
857 F: PeerLinkFactory + Send + Sync + 'static,
858{
859 pub fn new(
861 local_store: Arc<S>,
862 signaling: Arc<MeshRouter<R, F>>,
863 request_timeout: Duration,
864 debug: bool,
865 ) -> Self {
866 Self::new_with_routing(
867 local_store,
868 signaling,
869 request_timeout,
870 debug,
871 Default::default(),
872 )
873 }
874
875 pub fn new_with_routing(
877 local_store: Arc<S>,
878 signaling: Arc<MeshRouter<R, F>>,
879 request_timeout: Duration,
880 debug: bool,
881 routing: MeshRoutingConfig,
882 ) -> Self {
883 let mut selector = PeerSelector::with_strategy(routing.selection_strategy);
884 selector.set_fairness(routing.fairness_enabled);
885 selector.set_cashu_payment_weight(routing.cashu_payment_weight);
886 Self {
887 local_store,
888 signaling,
889 htl_configs: RwLock::new(HashMap::new()),
890 pending_requests: RwLock::new(HashMap::new()),
891 pending_quotes: RwLock::new(HashMap::new()),
892 pending_forward_requests: RwLock::new(HashMap::new()),
893 issued_quotes: RwLock::new(HashMap::new()),
894 next_quote_id: RwLock::new(1),
895 read_sources: RwLock::new(HashMap::new()),
896 read_source_stats: RwLock::new(HashMap::new()),
897 inflight_source_fetches: StdMutex::new(HashMap::new()),
898 peer_selector: RwLock::new(selector),
899 peer_active_requests: RwLock::new(HashMap::new()),
900 peer_wire_stats: RwLock::new(HashMap::new()),
901 pubsub_local_interests: RwLock::new(HashSet::new()),
902 pubsub_local_interest_versions: RwLock::new(HashMap::new()),
903 pubsub_peer_interests: RwLock::new(HashMap::new()),
904 pubsub_interest_routes: RwLock::new(HashMap::new()),
905 pubsub_interest_versions: RwLock::new(HashMap::new()),
906 pubsub_seen_interests: Mutex::new(TimedSeenSet::new(
907 PUBSUB_SEEN_CAPACITY,
908 PUBSUB_SEEN_TTL,
909 )),
910 pubsub_seen_frames: Mutex::new(TimedSeenSet::new(
911 PUBSUB_SEEN_CAPACITY,
912 PUBSUB_SEEN_TTL,
913 )),
914 pubsub_seen_inventories: Mutex::new(TimedSeenSet::new(
915 PUBSUB_SEEN_CAPACITY,
916 PUBSUB_SEEN_TTL,
917 )),
918 pubsub_seen_wants: Mutex::new(TimedSeenSet::new(PUBSUB_SEEN_CAPACITY, PUBSUB_SEEN_TTL)),
919 pubsub_inventory_routes: RwLock::new(HashMap::new()),
920 pubsub_want_routes: RwLock::new(HashMap::new()),
921 pubsub_upstream_wants: Mutex::new(TimedSeenSet::new(
922 PUBSUB_SEEN_CAPACITY,
923 PUBSUB_SEEN_TTL,
924 )),
925 pubsub_frame_cache: Mutex::new(VecDeque::new()),
926 pubsub_inbox: Mutex::new(VecDeque::new()),
927 verified_block_deliveries: Mutex::new(VerifiedBlockDeliveryBuffer::default()),
928 pubsub_notify: Notify::new(),
929 pubsub_deferred_counts: RwLock::new(HashMap::new()),
930 next_pubsub_interest_seq: AtomicU64::new(1),
931 pending_response_sends: Mutex::new(Vec::new()),
932 response_scheduler_running: AtomicBool::new(false),
933 next_response_job_id: AtomicU64::new(1),
934 routing,
935 request_timeout,
936 debug,
937 running: RwLock::new(false),
938 }
939 }
940
941 pub async fn start(&self) -> Result<(), TransportError> {
943 *self.running.write().await = true;
944
945 self.signaling.send_hello(vec![]).await?;
947
948 Ok(())
949 }
950
951 pub async fn stop(&self) {
953 *self.running.write().await = false;
954 }
955
956 pub async fn process_signaling(&self, msg: SignalingMessage) -> Result<(), TransportError> {
958 let peer_id = msg.peer_id().to_string();
960 {
961 let mut configs = self.htl_configs.write().await;
962 if !configs.contains_key(&peer_id) {
963 configs.insert(peer_id.clone(), PeerHTLConfig::random());
964 }
965 }
966 self.peer_selector.write().await.add_peer(peer_id.clone());
967
968 let result = self.signaling.handle_message(msg).await;
969 if result.is_ok() {
970 self.announce_pubsub_interests_to_peer(&peer_id).await;
971 }
972 result
973 }
974
975 pub fn signaling(&self) -> &Arc<MeshRouter<R, F>> {
977 &self.signaling
978 }
979
980 fn response_behavior(&self) -> ResponseBehaviorConfig {
981 self.routing.response_behavior.normalized()
982 }
983
984 async fn record_peer_wire_sent(&self, peer_id: &str, bytes: u64) {
985 if bytes == 0 {
986 return;
987 }
988 let mut stats = self.peer_wire_stats.write().await;
989 let entry = stats.entry(peer_id.to_string()).or_default();
990 entry.bytes_sent = entry.bytes_sent.saturating_add(bytes);
991 }
992
993 async fn record_peer_wire_received(&self, peer_id: &str, bytes: u64) {
994 if bytes == 0 {
995 return;
996 }
997 let mut stats = self.peer_wire_stats.write().await;
998 let entry = stats.entry(peer_id.to_string()).or_default();
999 entry.bytes_received = entry.bytes_received.saturating_add(bytes);
1000 }
1001
1002 pub async fn record_useful_bytes_received_from_peer(&self, peer_id: &str, bytes: u64) {
1007 if bytes == 0 {
1008 return;
1009 }
1010 let mut stats = self.peer_wire_stats.write().await;
1011 let entry = stats.entry(peer_id.to_string()).or_default();
1012 entry.useful_bytes_received = entry.useful_bytes_received.saturating_add(bytes);
1013 }
1014
1015 pub async fn peer_traffic_snapshot(&self, peer_id: &str) -> PeerTrafficSnapshot {
1017 self.peer_wire_stats
1018 .read()
1019 .await
1020 .get(peer_id)
1021 .copied()
1022 .unwrap_or_default()
1023 }
1024
1025 pub async fn peer_traffic_snapshots(&self) -> HashMap<String, PeerTrafficSnapshot> {
1027 self.peer_wire_stats.read().await.clone()
1028 }
1029
1030 fn pubsub_key(origin_peer_id: &str, stream_id: &str, seq: u64) -> String {
1031 format!("{origin_peer_id}:{stream_id}:{seq}")
1032 }
1033
1034 fn pubsub_frame_key(frame: &PubsubFrame) -> String {
1035 Self::pubsub_key(&frame.origin_peer_id, &frame.stream_id, frame.seq)
1036 }
1037
1038 fn pubsub_interest_key(interest: &PubsubInterest) -> String {
1039 format!(
1040 "{}:{}:{}:{}",
1041 interest.subscriber_peer_id, interest.stream_id, interest.seq, interest.active
1042 )
1043 }
1044
1045 fn next_pubsub_interest_seq(&self) -> u64 {
1046 self.next_pubsub_interest_seq
1047 .fetch_add(1, Ordering::Relaxed)
1048 }
1049
1050 async fn record_peer_pubsub_wire_sent(&self, peer_id: &str, bytes: u64, bandwidth_debt: f64) {
1051 if bytes == 0 {
1052 return;
1053 }
1054 let mut stats = self.peer_wire_stats.write().await;
1055 let entry = stats.entry(peer_id.to_string()).or_default();
1056 entry.bytes_sent = entry.bytes_sent.saturating_add(bytes);
1057 entry.bandwidth_debt = bandwidth_debt;
1058 }
1059
1060 async fn send_pubsub_interest_to_peers(
1061 &self,
1062 interest: &PubsubInterest,
1063 exclude_peer_id: Option<&str>,
1064 ) -> PubsubPublishStats {
1065 if !should_forward_htl(interest.htl) {
1066 return PubsubPublishStats::default();
1067 }
1068
1069 let mut peer_ids = self.signaling.peer_ids().await;
1070 peer_ids.sort();
1071 peer_ids.retain(|peer_id| exclude_peer_id.is_none_or(|exclude| peer_id != exclude));
1072
1073 let bytes = encode_pubsub_interest(interest);
1074 let mut stats = PubsubPublishStats {
1075 selected_peers: peer_ids.len(),
1076 ..Default::default()
1077 };
1078 for peer_id in peer_ids {
1079 let Some(channel) = self.signaling.get_channel(&peer_id).await else {
1080 continue;
1081 };
1082 if channel.send(bytes.clone()).await.is_ok() {
1083 stats.sent_peers += 1;
1084 stats.sent_bytes = stats.sent_bytes.saturating_add(bytes.len() as u64);
1085 self.record_peer_wire_sent(&peer_id, bytes.len() as u64)
1086 .await;
1087 }
1088 }
1089 stats
1090 }
1091
1092 async fn announce_pubsub_interests_to_peer(&self, peer_id: &str) {
1093 let mut interests = self
1094 .pubsub_local_interests
1095 .read()
1096 .await
1097 .iter()
1098 .cloned()
1099 .collect::<Vec<_>>();
1100 interests.sort();
1101 if interests.is_empty() {
1102 return;
1103 }
1104
1105 let interests = {
1106 let versions = self.pubsub_local_interest_versions.read().await;
1107 interests
1108 .into_iter()
1109 .filter_map(|stream_id| {
1110 versions
1111 .get(&stream_id)
1112 .copied()
1113 .map(|seq| (stream_id, seq))
1114 })
1115 .collect::<Vec<_>>()
1116 };
1117
1118 for (stream_id, seq) in interests {
1119 let interest = create_pubsub_interest(
1120 stream_id,
1121 self.signaling.peer_id().to_string(),
1122 seq,
1123 true,
1124 MAX_HTL,
1125 );
1126 let Some(channel) = self.signaling.get_channel(peer_id).await else {
1127 continue;
1128 };
1129 let bytes = encode_pubsub_interest(&interest);
1130 if channel.send(bytes.clone()).await.is_ok() {
1131 self.record_peer_wire_sent(peer_id, bytes.len() as u64)
1132 .await;
1133 }
1134 }
1135 }
1136
1137 fn remove_pubsub_peer_interest(
1138 peer_interests: &mut HashMap<String, HashSet<String>>,
1139 routes: &HashMap<(String, String), String>,
1140 stream_id: &str,
1141 peer_id: &str,
1142 ) {
1143 let still_has_route = routes
1144 .iter()
1145 .any(|((stream, _subscriber), peer)| stream == stream_id && peer == peer_id);
1146 if still_has_route {
1147 return;
1148 }
1149 if let Some(peers) = peer_interests.get_mut(stream_id) {
1150 peers.remove(peer_id);
1151 if peers.is_empty() {
1152 peer_interests.remove(stream_id);
1153 }
1154 }
1155 }
1156
1157 async fn apply_pubsub_interest_route(
1158 &self,
1159 from_peer: &str,
1160 interest: &PubsubInterest,
1161 ) -> bool {
1162 if interest.stream_id.is_empty() || interest.subscriber_peer_id.is_empty() {
1163 return false;
1164 }
1165 if interest.subscriber_peer_id == self.signaling.peer_id() {
1166 return false;
1167 }
1168
1169 let interest_key = Self::pubsub_interest_key(interest);
1170 if !self
1171 .pubsub_seen_interests
1172 .lock()
1173 .await
1174 .insert_if_new(interest_key)
1175 {
1176 return false;
1177 }
1178
1179 let route_key = (
1180 interest.stream_id.clone(),
1181 interest.subscriber_peer_id.clone(),
1182 );
1183 {
1184 let mut versions = self.pubsub_interest_versions.write().await;
1185 if versions
1186 .get(&route_key)
1187 .is_some_and(|latest| *latest >= interest.seq)
1188 {
1189 return false;
1190 }
1191 versions.insert(route_key.clone(), interest.seq);
1192 }
1193
1194 let mut peer_interests = self.pubsub_peer_interests.write().await;
1195 let mut routes = self.pubsub_interest_routes.write().await;
1196 if interest.active {
1197 if let Some(previous_peer) = routes.insert(route_key, from_peer.to_string()) {
1198 if previous_peer != from_peer {
1199 Self::remove_pubsub_peer_interest(
1200 &mut peer_interests,
1201 &routes,
1202 &interest.stream_id,
1203 &previous_peer,
1204 );
1205 }
1206 }
1207 peer_interests
1208 .entry(interest.stream_id.clone())
1209 .or_default()
1210 .insert(from_peer.to_string());
1211 } else if let Some(previous_peer) = routes.remove(&route_key) {
1212 Self::remove_pubsub_peer_interest(
1213 &mut peer_interests,
1214 &routes,
1215 &interest.stream_id,
1216 &previous_peer,
1217 );
1218 } else {
1219 Self::remove_pubsub_peer_interest(
1220 &mut peer_interests,
1221 &routes,
1222 &interest.stream_id,
1223 from_peer,
1224 );
1225 }
1226
1227 true
1228 }
1229
1230 async fn interested_pubsub_peers(
1231 &self,
1232 stream_id: &str,
1233 exclude_peer_id: Option<&str>,
1234 ) -> Vec<String> {
1235 let connected = self
1236 .signaling
1237 .peer_ids()
1238 .await
1239 .into_iter()
1240 .collect::<HashSet<_>>();
1241 let mut peers = self
1242 .pubsub_peer_interests
1243 .read()
1244 .await
1245 .get(stream_id)
1246 .map(|peers| peers.iter().cloned().collect::<Vec<_>>())
1247 .unwrap_or_default();
1248 peers.retain(|peer_id| {
1249 connected.contains(peer_id) && exclude_peer_id.is_none_or(|exclude| peer_id != exclude)
1250 });
1251 peers.sort();
1252 peers
1253 }
1254
1255 async fn decrement_pubsub_htl_for_peer(&self, peer_id: &str, htl: u8) -> u8 {
1256 let htl_config = {
1257 let configs = self.htl_configs.read().await;
1258 configs
1259 .get(peer_id)
1260 .cloned()
1261 .unwrap_or_else(PeerHTLConfig::random)
1262 };
1263 htl_config.decrement_with_policy(htl, &MESH_EVENT_POLICY)
1264 }
1265
1266 async fn send_pubsub_inventory_to_peers(
1267 &self,
1268 inv: &PubsubInventory,
1269 peer_ids: &[String],
1270 ) -> PubsubPublishStats {
1271 if peer_ids.is_empty() || !should_forward_htl(inv.htl) {
1272 return PubsubPublishStats::default();
1273 }
1274
1275 let mut stats = PubsubPublishStats {
1276 selected_peers: peer_ids.len(),
1277 ..Default::default()
1278 };
1279 for peer_id in peer_ids {
1280 let send_htl = self.decrement_pubsub_htl_for_peer(peer_id, inv.htl).await;
1281 if !should_forward_htl(send_htl) {
1282 continue;
1283 }
1284 let Some(channel) = self.signaling.get_channel(peer_id).await else {
1285 continue;
1286 };
1287 let mut outgoing = inv.clone();
1288 outgoing.htl = send_htl;
1289 let bytes = encode_pubsub_inventory(&outgoing);
1290 let message_bytes = bytes.len() as u64;
1291 if channel.send(bytes).await.is_ok() {
1292 stats.sent_peers += 1;
1293 stats.sent_bytes = stats.sent_bytes.saturating_add(message_bytes);
1294 self.record_peer_wire_sent(peer_id, message_bytes).await;
1295 }
1296 }
1297 stats
1298 }
1299
1300 async fn send_pubsub_want_to_peer(&self, want: &PubsubWant, peer_id: &str) -> bool {
1301 let Some(channel) = self.signaling.get_channel(peer_id).await else {
1302 return false;
1303 };
1304 let bytes = encode_pubsub_want(want);
1305 let message_bytes = bytes.len() as u64;
1306 match channel.send(bytes).await {
1307 Ok(()) => {
1308 self.record_peer_wire_sent(peer_id, message_bytes).await;
1309 true
1310 }
1311 Err(_) => false,
1312 }
1313 }
1314
1315 async fn send_pubsub_want_upstream(
1316 &self,
1317 key: &str,
1318 want: &PubsubWant,
1319 exclude_peer_id: Option<&str>,
1320 ) -> bool {
1321 let upstream = {
1322 let routes = self.pubsub_inventory_routes.read().await;
1323 routes.get(key).cloned()
1324 };
1325 let Some(upstream) = upstream else {
1326 return false;
1327 };
1328 if exclude_peer_id.is_some_and(|exclude| exclude == upstream) {
1329 return false;
1330 }
1331 let want_key = format!("{key}:{upstream}");
1332 if !self
1333 .pubsub_upstream_wants
1334 .lock()
1335 .await
1336 .insert_if_new(want_key)
1337 {
1338 return false;
1339 }
1340 self.send_pubsub_want_to_peer(want, &upstream).await
1341 }
1342
1343 async fn cache_pubsub_frame(&self, key: String, frame: PubsubFrame) {
1344 let mut cache = self.pubsub_frame_cache.lock().await;
1345 if let Some(index) = cache.iter().position(|(cached_key, _)| cached_key == &key) {
1346 cache.remove(index);
1347 }
1348 cache.push_back((key, frame));
1349 while cache.len() > PUBSUB_FRAME_CACHE_CAPACITY {
1350 cache.pop_front();
1351 }
1352 }
1353
1354 async fn cached_pubsub_frame(&self, key: &str) -> Option<PubsubFrame> {
1355 self.pubsub_frame_cache
1356 .lock()
1357 .await
1358 .iter()
1359 .find_map(|(cached_key, frame)| {
1360 if cached_key == key {
1361 Some(frame.clone())
1362 } else {
1363 None
1364 }
1365 })
1366 }
1367
1368 async fn remember_pubsub_want_peer(&self, key: String, from_peer: &str) -> bool {
1369 let mut routes = self.pubsub_want_routes.write().await;
1370 routes.entry(key).or_default().insert(from_peer.to_string())
1371 }
1372
1373 async fn take_pubsub_want_peers(
1374 &self,
1375 key: &str,
1376 exclude_peer_id: Option<&str>,
1377 ) -> Vec<String> {
1378 let connected = self
1379 .signaling
1380 .peer_ids()
1381 .await
1382 .into_iter()
1383 .collect::<HashSet<_>>();
1384 let mut peers = self
1385 .pubsub_want_routes
1386 .write()
1387 .await
1388 .remove(key)
1389 .map(|peers| peers.into_iter().collect::<Vec<_>>())
1390 .unwrap_or_default();
1391 peers.retain(|peer_id| {
1392 connected.contains(peer_id) && exclude_peer_id.is_none_or(|exclude| peer_id != exclude)
1393 });
1394 peers.sort();
1395 peers
1396 }
1397
1398 async fn select_pubsub_peers(
1399 &self,
1400 stream_id: &str,
1401 seq: u64,
1402 message_bytes: u64,
1403 peer_ids: &[String],
1404 ) -> (Vec<String>, Vec<String>) {
1405 let traffic = self.peer_wire_stats.read().await;
1406 let deferred_counts = self.pubsub_deferred_counts.read().await;
1407 let candidates = peer_ids
1408 .iter()
1409 .map(|peer_id| PubsubCandidate {
1410 peer_id: peer_id.clone(),
1411 traffic: traffic.get(peer_id).copied().unwrap_or_default(),
1412 deferred_count: deferred_counts
1413 .get(&(stream_id.to_string(), peer_id.clone()))
1414 .copied()
1415 .unwrap_or_default(),
1416 })
1417 .collect::<Vec<_>>();
1418 drop(deferred_counts);
1419 drop(traffic);
1420
1421 let selection = self.routing.pubsub_scheduler.select(
1422 stream_id,
1423 seq,
1424 self.signaling.peer_id(),
1425 message_bytes,
1426 &candidates,
1427 );
1428
1429 {
1430 let mut deferred_counts = self.pubsub_deferred_counts.write().await;
1431 for peer_id in &selection.deferred {
1432 *deferred_counts
1433 .entry((stream_id.to_string(), peer_id.clone()))
1434 .or_insert(0) += 1;
1435 }
1436 for peer_id in &selection.selected {
1437 deferred_counts.remove(&(stream_id.to_string(), peer_id.clone()));
1438 }
1439 }
1440
1441 (selection.selected, selection.deferred)
1442 }
1443
1444 async fn send_pubsub_frame_to_peers(
1445 &self,
1446 frame: &PubsubFrame,
1447 peer_ids: &[String],
1448 ) -> PubsubPublishStats {
1449 if peer_ids.is_empty() || !should_forward_htl(frame.htl) {
1450 return PubsubPublishStats::default();
1451 }
1452
1453 let bytes = encode_pubsub_frame(frame);
1454 let message_bytes = bytes.len() as u64;
1455 let (selected, deferred) = self
1456 .select_pubsub_peers(&frame.stream_id, frame.seq, message_bytes, peer_ids)
1457 .await;
1458 let mut stats = PubsubPublishStats {
1459 selected_peers: selected.len(),
1460 deferred_peers: deferred.len(),
1461 ..Default::default()
1462 };
1463
1464 for peer_id in selected {
1465 let Some(channel) = self.signaling.get_channel(&peer_id).await else {
1466 continue;
1467 };
1468 let snapshot = self.peer_traffic_snapshot(&peer_id).await;
1469 let bandwidth_debt = reciprocal_virtual_finish(snapshot, message_bytes);
1470 if channel.send(bytes.clone()).await.is_ok() {
1471 stats.sent_peers += 1;
1472 stats.sent_bytes = stats.sent_bytes.saturating_add(message_bytes);
1473 self.record_peer_pubsub_wire_sent(&peer_id, message_bytes, bandwidth_debt)
1474 .await;
1475 }
1476 }
1477
1478 stats
1479 }
1480
1481 async fn enqueue_pubsub_event(&self, event: PubsubEvent) {
1482 let mut inbox = self.pubsub_inbox.lock().await;
1483 inbox.push_back(event);
1484 while inbox.len() > PUBSUB_INBOX_CAPACITY {
1485 inbox.pop_front();
1486 }
1487 self.pubsub_notify.notify_one();
1488 }
1489
1490 pub async fn subscribe_pubsub(
1492 self: &Arc<Self>,
1493 stream_id: impl Into<String>,
1494 ) -> PubsubPublishStats {
1495 let stream_id = stream_id.into();
1496 if stream_id.is_empty() {
1497 return PubsubPublishStats::default();
1498 }
1499 self.pubsub_local_interests
1500 .write()
1501 .await
1502 .insert(stream_id.clone());
1503 let seq = {
1504 let mut versions = self.pubsub_local_interest_versions.write().await;
1505 match versions.get(&stream_id).copied() {
1506 Some(seq) => seq,
1507 None => {
1508 let seq = self.next_pubsub_interest_seq();
1509 versions.insert(stream_id.clone(), seq);
1510 seq
1511 }
1512 }
1513 };
1514 let interest = create_pubsub_interest(
1515 stream_id,
1516 self.signaling.peer_id().to_string(),
1517 seq,
1518 true,
1519 self.routing.pubsub_initial_htl(),
1520 );
1521 self.send_pubsub_interest_to_peers(&interest, None).await
1522 }
1523
1524 pub async fn unsubscribe_pubsub(
1526 self: &Arc<Self>,
1527 stream_id: impl Into<String>,
1528 ) -> PubsubPublishStats {
1529 let stream_id = stream_id.into();
1530 if stream_id.is_empty() {
1531 return PubsubPublishStats::default();
1532 }
1533 self.pubsub_local_interests.write().await.remove(&stream_id);
1534 self.pubsub_local_interest_versions
1535 .write()
1536 .await
1537 .remove(&stream_id);
1538 let interest = create_pubsub_interest(
1539 stream_id,
1540 self.signaling.peer_id().to_string(),
1541 self.next_pubsub_interest_seq(),
1542 false,
1543 self.routing.pubsub_initial_htl(),
1544 );
1545 self.send_pubsub_interest_to_peers(&interest, None).await
1546 }
1547
1548 pub async fn publish_pubsub(
1550 self: &Arc<Self>,
1551 stream_id: impl Into<String>,
1552 seq: u64,
1553 payload: Vec<u8>,
1554 ) -> PubsubPublishStats {
1555 let stream_id = stream_id.into();
1556 if stream_id.is_empty() {
1557 return PubsubPublishStats::default();
1558 }
1559 let payload_bytes = payload.len() as u64;
1560 let frame = create_pubsub_frame(
1561 stream_id.clone(),
1562 seq,
1563 self.signaling.peer_id().to_string(),
1564 payload.clone(),
1565 self.routing.pubsub_initial_htl(),
1566 );
1567 let frame_key = Self::pubsub_frame_key(&frame);
1568 self.pubsub_seen_frames
1569 .lock()
1570 .await
1571 .insert_if_new(frame_key.clone());
1572 self.cache_pubsub_frame(frame_key, frame.clone()).await;
1573
1574 if self
1575 .pubsub_local_interests
1576 .read()
1577 .await
1578 .contains(&stream_id)
1579 {
1580 self.enqueue_pubsub_event(PubsubEvent {
1581 stream_id: stream_id.clone(),
1582 seq,
1583 origin_peer_id: self.signaling.peer_id().to_string(),
1584 from_peer_id: self.signaling.peer_id().to_string(),
1585 payload,
1586 })
1587 .await;
1588 }
1589
1590 match self.routing.pubsub_delivery_mode {
1591 PubsubDeliveryMode::InterestPush => {
1592 let peers = self.interested_pubsub_peers(&stream_id, None).await;
1593 self.send_pubsub_frame_to_peers(&frame, &peers).await
1594 }
1595 PubsubDeliveryMode::HtlInvWant => {
1596 let inv = create_pubsub_inventory(
1597 stream_id,
1598 seq,
1599 self.signaling.peer_id().to_string(),
1600 payload_bytes,
1601 self.routing.pubsub_initial_htl(),
1602 );
1603 let peers = self.interested_pubsub_peers(&inv.stream_id, None).await;
1604 self.send_pubsub_inventory_to_peers(&inv, &peers).await
1605 }
1606 }
1607 }
1608
1609 pub async fn drain_pubsub_events(&self) -> Vec<PubsubEvent> {
1611 self.pubsub_inbox.lock().await.drain(..).collect()
1612 }
1613
1614 pub async fn drain_verified_block_deliveries(&self) -> VerifiedBlockDeliveryBatch {
1616 let mut buffer = self.verified_block_deliveries.lock().await;
1617 VerifiedBlockDeliveryBatch {
1618 deliveries: buffer.deliveries.drain(..).collect(),
1619 dropped_since_last_drain: std::mem::take(&mut buffer.dropped_since_last_drain),
1620 }
1621 }
1622
1623 pub async fn recv_pubsub_event(&self) -> PubsubEvent {
1625 loop {
1626 if let Some(event) = self.pubsub_inbox.lock().await.pop_front() {
1627 return event;
1628 }
1629 self.pubsub_notify.notified().await;
1630 }
1631 }
1632
1633 pub async fn pubsub_interest_peers(&self, stream_id: &str) -> Vec<String> {
1635 self.interested_pubsub_peers(stream_id, None).await
1636 }
1637
1638 fn choose_ready_response_job(
1639 ready_jobs: &[(u64, String, usize, Instant, u64)],
1640 stats: &HashMap<String, PeerWireStats>,
1641 ) -> Option<(u64, f64)> {
1642 let jobs = ready_jobs
1643 .iter()
1644 .map(|job| OutboundJobCandidate {
1645 job_id: job.0,
1646 peer_id: job.1.clone(),
1647 message_bytes: job.2 as u64,
1648 queue_sequence: job.4,
1649 })
1650 .collect::<Vec<_>>();
1651 select_reciprocal_outbound_job(&jobs, |peer_id| {
1652 stats.get(peer_id).copied().unwrap_or_default()
1653 })
1654 .map(|choice| (choice.job_id, choice.virtual_finish))
1655 }
1656
1657 async fn enqueue_response_send(
1658 self: &Arc<Self>,
1659 peer_id: String,
1660 bytes: Vec<u8>,
1661 ready_at: Instant,
1662 ) {
1663 let job_id = self.next_response_job_id.fetch_add(1, Ordering::Relaxed);
1664 {
1665 let mut queue = self.pending_response_sends.lock().await;
1666 queue.push(PendingResponseSend {
1667 job_id,
1668 peer_id,
1669 bytes,
1670 ready_at,
1671 queue_sequence: job_id,
1672 });
1673 }
1674
1675 if self
1676 .response_scheduler_running
1677 .compare_exchange(false, true, Ordering::AcqRel, Ordering::Acquire)
1678 .is_ok()
1679 {
1680 let this = Arc::clone(self);
1681 tokio::spawn(async move {
1682 this.run_response_scheduler().await;
1683 });
1684 }
1685 }
1686
1687 async fn run_response_scheduler(self: Arc<Self>) {
1688 loop {
1689 let snapshot = {
1690 let queue = self.pending_response_sends.lock().await;
1691 if queue.is_empty() {
1692 self.response_scheduler_running
1693 .store(false, Ordering::Release);
1694 return;
1695 }
1696 queue
1697 .iter()
1698 .map(|job| {
1699 (
1700 job.job_id,
1701 job.peer_id.clone(),
1702 job.bytes.len(),
1703 job.ready_at,
1704 job.queue_sequence,
1705 )
1706 })
1707 .collect::<Vec<_>>()
1708 };
1709
1710 let now = Instant::now();
1711 let mut earliest_ready_at: Option<Instant> = None;
1712 let mut ready_jobs = Vec::new();
1713 for job in &snapshot {
1714 if job.3 <= now {
1715 ready_jobs.push(job.clone());
1716 } else {
1717 earliest_ready_at = Some(match earliest_ready_at {
1718 Some(current) => current.min(job.3),
1719 None => job.3,
1720 });
1721 }
1722 }
1723
1724 if ready_jobs.is_empty() {
1725 if let Some(ready_at) = earliest_ready_at {
1726 tokio::time::sleep(ready_at.saturating_duration_since(Instant::now())).await;
1727 continue;
1728 }
1729 self.response_scheduler_running
1730 .store(false, Ordering::Release);
1731 return;
1732 }
1733
1734 let (selected_job_id, selected_finish) = {
1735 let stats = self.peer_wire_stats.read().await;
1736 Self::choose_ready_response_job(&ready_jobs, &stats).expect("ready response job")
1737 };
1738
1739 let selected = {
1740 let mut queue = self.pending_response_sends.lock().await;
1741 let Some(index) = queue.iter().position(|job| job.job_id == selected_job_id) else {
1742 continue;
1743 };
1744 queue.swap_remove(index)
1745 };
1746
1747 let sent = if let Some(channel) = self.signaling.get_channel(&selected.peer_id).await {
1748 channel.send(selected.bytes.clone()).await.is_ok()
1749 } else {
1750 false
1751 };
1752
1753 let queued_peers = {
1754 let queue = self.pending_response_sends.lock().await;
1755 queue
1756 .iter()
1757 .map(|job| job.peer_id.clone())
1758 .collect::<HashSet<_>>()
1759 };
1760 let mut stats = self.peer_wire_stats.write().await;
1761 let entry = stats.entry(selected.peer_id.clone()).or_default();
1762 if sent {
1763 entry.bytes_sent = entry.bytes_sent.saturating_add(selected.bytes.len() as u64);
1764 entry.bandwidth_debt = selected_finish;
1765 }
1766 if queued_peers.is_empty() {
1767 for peer_stats in stats.values_mut() {
1768 peer_stats.bandwidth_debt = 0.0;
1769 }
1770 } else {
1771 let floor = queued_peers
1772 .iter()
1773 .filter_map(|peer_id| stats.get(peer_id).map(|peer| peer.bandwidth_debt))
1774 .fold(f64::INFINITY, f64::min);
1775 if floor.is_finite() && floor > 0.0 {
1776 for peer_id in queued_peers {
1777 if let Some(peer_stats) = stats.get_mut(&peer_id) {
1778 peer_stats.bandwidth_debt =
1779 (peer_stats.bandwidth_debt - floor).max(0.0);
1780 }
1781 }
1782 }
1783 }
1784 }
1785 }
1786
1787 fn deterministic_actor_draw_for(peer_id: &str, hash: &Hash, salt: u64) -> f64 {
1788 let mut hasher = DefaultHasher::new();
1789 peer_id.hash(&mut hasher);
1790 hash.hash(&mut hasher);
1791 salt.hash(&mut hasher);
1792 let v = hasher.finish();
1793 (v as f64) / (u64::MAX as f64)
1794 }
1795
1796 fn deterministic_actor_draw(&self, hash: &Hash, salt: u64) -> f64 {
1797 Self::deterministic_actor_draw_for(self.signaling.peer_id(), hash, salt)
1798 }
1799
1800 fn peer_metadata_pointer_slot_hash() -> Hash {
1801 hashtree_core::sha256(PEER_METADATA_POINTER_SLOT_KEY)
1802 }
1803
1804 fn decode_hash_hex(hash_hex: &str) -> Result<Hash, StoreError> {
1805 let bytes = hex::decode(hash_hex)
1806 .map_err(|e| StoreError::Other(format!("Invalid hash hex: {e}")))?;
1807 if bytes.len() != 32 {
1808 return Err(StoreError::Other(format!(
1809 "Invalid hash length {}, expected 32 bytes",
1810 bytes.len()
1811 )));
1812 }
1813 let mut hash = [0u8; 32];
1814 hash.copy_from_slice(&bytes);
1815 Ok(hash)
1816 }
1817
1818 fn should_drop_response(&self, hash: &Hash) -> bool {
1819 let p = self.response_behavior().drop_response_prob;
1820 if p <= 0.0 {
1821 return false;
1822 }
1823 self.deterministic_actor_draw(hash, 0xD0_D0_D0_D0_D0_D0_D0_D0) < p
1824 }
1825
1826 fn should_corrupt_response(&self, hash: &Hash) -> bool {
1827 let p = self.response_behavior().corrupt_response_prob;
1828 if p <= 0.0 {
1829 return false;
1830 }
1831 self.deterministic_actor_draw(hash, 0xC0_C0_C0_C0_C0_C0_C0_C0) < p
1832 }
1833
1834 fn should_stall_response(&self, hash: &Hash) -> bool {
1835 let p = self.response_behavior().stall_response_prob;
1836 if p <= 0.0 {
1837 return false;
1838 }
1839 self.deterministic_actor_draw(hash, 0x5A_11_5A_11_5A_11_5A_11) < p
1840 }
1841
1842 fn response_send_delay(&self, hash: &Hash, payload_len: usize) -> Duration {
1843 let behavior = self.response_behavior();
1844 let mut total_ms = behavior
1845 .extra_delay_ms
1846 .saturating_add(behavior.first_byte_delay_ms);
1847
1848 if behavior.bytes_per_second > 0 && payload_len > 0 {
1849 let throughput_ms = ((payload_len as u128) * 1000)
1850 .div_ceil(behavior.bytes_per_second as u128)
1851 .min(u64::MAX as u128) as u64;
1852 total_ms = total_ms.saturating_add(throughput_ms);
1853 }
1854
1855 if behavior.stall_delay_ms > 0 && self.should_stall_response(hash) {
1856 total_ms = total_ms.saturating_add(behavior.stall_delay_ms);
1857 }
1858
1859 Duration::from_millis(total_ms)
1860 }
1861
1862 async fn ordered_connected_peers(&self, exclude_peer_id: Option<&str>) -> Vec<String> {
1863 let current_peer_ids = self.signaling.peer_ids().await;
1864 if current_peer_ids.is_empty() {
1865 return Vec::new();
1866 }
1867
1868 sync_selector_peers(&self.peer_selector, ¤t_peer_ids).await;
1869 let hash_get_peer_ids: HashSet<String> = self
1870 .signaling
1871 .hash_get_peer_ids()
1872 .await
1873 .into_iter()
1874 .collect();
1875 let mut candidate_peer_ids: Vec<String> = current_peer_ids
1876 .into_iter()
1877 .filter(|peer_id| hash_get_peer_ids.contains(peer_id))
1878 .filter(|peer_id| exclude_peer_id.is_none_or(|exclude| peer_id != exclude))
1879 .collect();
1880 if candidate_peer_ids.is_empty() {
1881 return Vec::new();
1882 }
1883
1884 let current_set: HashSet<&str> = candidate_peer_ids.iter().map(String::as_str).collect();
1885 let mut selector = self.peer_selector.write().await;
1886 let mut selector_order = selector.select_peers();
1887 selector_order.retain(|peer_id| current_set.contains(peer_id.as_str()));
1888 if selector_order.is_empty() {
1889 let mut fallback = candidate_peer_ids;
1890 fallback.sort();
1891 return fallback;
1892 }
1893 let backed_off: HashMap<String, bool> = candidate_peer_ids
1894 .iter()
1895 .map(|peer_id| (peer_id.clone(), selector.is_peer_backed_off(peer_id)))
1896 .collect();
1897 drop(selector);
1898
1899 let rank: HashMap<&str, usize> = selector_order
1900 .iter()
1901 .enumerate()
1902 .map(|(idx, peer_id)| (peer_id.as_str(), idx))
1903 .collect();
1904 let active = self.peer_active_requests.read().await;
1905 candidate_peer_ids.sort_by(|left, right| {
1906 let left_backed_off = backed_off.get(left).copied().unwrap_or(false);
1907 let right_backed_off = backed_off.get(right).copied().unwrap_or(false);
1908 if left_backed_off != right_backed_off {
1909 return if left_backed_off {
1910 std::cmp::Ordering::Greater
1911 } else {
1912 std::cmp::Ordering::Less
1913 };
1914 }
1915 let left_rank = rank.get(left.as_str()).copied().unwrap_or(usize::MAX / 2);
1916 let right_rank = rank.get(right.as_str()).copied().unwrap_or(usize::MAX / 2);
1917 let left_load = active.get(left).copied().unwrap_or(0);
1918 let right_load = active.get(right).copied().unwrap_or(0);
1919 (left_rank + left_load.saturating_mul(ACTIVE_PEER_REQUEST_RANK_PENALTY))
1920 .cmp(&(right_rank + right_load.saturating_mul(ACTIVE_PEER_REQUEST_RANK_PENALTY)))
1921 .then_with(|| left.cmp(right))
1922 });
1923 candidate_peer_ids
1924 }
1925
1926 async fn reserve_peer_request(&self, peer_id: &str) {
1927 let mut active = self.peer_active_requests.write().await;
1928 *active.entry(peer_id.to_string()).or_insert(0) += 1;
1929 }
1930
1931 async fn release_peer_request(&self, peer_id: &str) {
1932 let mut active = self.peer_active_requests.write().await;
1933 let Some(count) = active.get_mut(peer_id) else {
1934 return;
1935 };
1936 if *count <= 1 {
1937 active.remove(peer_id);
1938 } else {
1939 *count -= 1;
1940 }
1941 }
1942
1943 async fn release_queried_peer_requests(&self, peer_ids: &[String]) {
1944 for peer_id in peer_ids {
1945 self.release_peer_request(peer_id).await;
1946 }
1947 }
1948
1949 fn requested_quote_mint(&self) -> Option<&str> {
1950 if let Some(default_mint) = self.routing.cashu_default_mint.as_deref() {
1951 if self.routing.cashu_accepted_mints.is_empty()
1952 || self
1953 .routing
1954 .cashu_accepted_mints
1955 .iter()
1956 .any(|mint| mint == default_mint)
1957 {
1958 return Some(default_mint);
1959 }
1960 }
1961
1962 self.routing
1963 .cashu_accepted_mints
1964 .first()
1965 .map(String::as_str)
1966 }
1967
1968 fn choose_quote_mint(&self, requested_mint: Option<&str>) -> Option<String> {
1969 if let Some(requested_mint) = requested_mint {
1970 if self.accepts_quote_mint(Some(requested_mint)) {
1971 return Some(requested_mint.to_string());
1972 }
1973 }
1974 if let Some(default_mint) = self.routing.cashu_default_mint.as_ref() {
1975 return Some(default_mint.clone());
1976 }
1977 if let Some(first_mint) = self.routing.cashu_accepted_mints.first() {
1978 return Some(first_mint.clone());
1979 }
1980 requested_mint.map(str::to_string)
1981 }
1982
1983 fn accepts_quote_mint(&self, mint_url: Option<&str>) -> bool {
1984 if self.routing.cashu_accepted_mints.is_empty() {
1985 return true;
1986 }
1987
1988 let Some(mint_url) = mint_url else {
1989 return false;
1990 };
1991 self.routing
1992 .cashu_accepted_mints
1993 .iter()
1994 .any(|mint| mint == mint_url)
1995 }
1996
1997 fn trusts_quote_mint(&self, mint_url: Option<&str>) -> bool {
1998 let Some(mint_url) = mint_url else {
1999 return self.routing.cashu_default_mint.is_none()
2000 && self.routing.cashu_accepted_mints.is_empty();
2001 };
2002 self.routing.cashu_default_mint.as_deref() == Some(mint_url)
2003 || self
2004 .routing
2005 .cashu_accepted_mints
2006 .iter()
2007 .any(|mint| mint == mint_url)
2008 }
2009
2010 async fn peer_suggested_mint_cap_sat(&self, peer_id: &str) -> u64 {
2011 let base = self.routing.cashu_peer_suggested_mint_base_cap_sat;
2012 if base == 0 {
2013 return 0;
2014 }
2015
2016 let selector = self.peer_selector.read().await;
2017 let Some(stats) = selector.get_stats(peer_id) else {
2018 let max_cap = self.routing.cashu_peer_suggested_mint_max_cap_sat;
2019 return if max_cap > 0 { base.min(max_cap) } else { base };
2020 };
2021
2022 if stats.cashu_payment_defaults > 0
2023 && stats.cashu_payment_defaults >= stats.cashu_payment_receipts
2024 {
2025 return 0;
2026 }
2027
2028 let success_bonus = stats
2029 .successes
2030 .saturating_mul(self.routing.cashu_peer_suggested_mint_success_step_sat);
2031 let receipt_bonus = stats
2032 .cashu_payment_receipts
2033 .saturating_mul(self.routing.cashu_peer_suggested_mint_receipt_step_sat);
2034 let mut cap = base
2035 .saturating_add(success_bonus)
2036 .saturating_add(receipt_bonus);
2037 let max_cap = self.routing.cashu_peer_suggested_mint_max_cap_sat;
2038 if max_cap > 0 {
2039 cap = cap.min(max_cap);
2040 }
2041 cap
2042 }
2043
2044 async fn should_accept_quote_response(
2045 &self,
2046 from_peer: &str,
2047 preferred_mint_url: Option<&str>,
2048 offered_payment_sat: u64,
2049 res: &DataQuoteResponse,
2050 ) -> bool {
2051 let Some(payment_sat) = res.p else {
2052 return false;
2053 };
2054 if payment_sat > offered_payment_sat {
2055 return false;
2056 }
2057
2058 let response_mint = res.m.as_deref();
2059 if response_mint == preferred_mint_url {
2060 return true;
2061 }
2062 if self.trusts_quote_mint(response_mint) {
2063 return true;
2064 }
2065 if response_mint.is_none() {
2066 return false;
2067 }
2068
2069 payment_sat <= self.peer_suggested_mint_cap_sat(from_peer).await
2070 }
2071
2072 async fn issue_quote(
2073 &self,
2074 peer_id: &str,
2075 hash_key: &str,
2076 payment_sat: u64,
2077 ttl_ms: u32,
2078 mint_url: Option<&str>,
2079 ) -> u64 {
2080 let quote_id = {
2081 let mut next = self.next_quote_id.write().await;
2082 let quote_id = *next;
2083 *next = next.saturating_add(1);
2084 quote_id
2085 };
2086
2087 let expires_at = Instant::now() + Duration::from_millis(ttl_ms as u64);
2088 self.issued_quotes.write().await.insert(
2089 (peer_id.to_string(), hash_key.to_string(), quote_id),
2090 IssuedQuote {
2091 expires_at,
2092 payment_sat,
2093 mint_url: mint_url.map(str::to_string),
2094 },
2095 );
2096 quote_id
2097 }
2098
2099 async fn take_valid_quote(&self, peer_id: &str, hash_key: &str, quote_id: u64) -> bool {
2100 let key = (peer_id.to_string(), hash_key.to_string(), quote_id);
2101 let Some(quote) = self.issued_quotes.write().await.remove(&key) else {
2102 return false;
2103 };
2104 quote.expires_at > Instant::now()
2105 }
2106
2107 async fn send_request_to_peer(
2108 &self,
2109 peer_id: &str,
2110 hash: &Hash,
2111 request_htl: u8,
2112 quote_id: Option<u64>,
2113 ) -> bool {
2114 if !should_forward_htl(request_htl) {
2115 return false;
2116 }
2117
2118 let channel = match self.signaling.get_channel(peer_id).await {
2119 Some(c) => c,
2120 None => return false,
2121 };
2122
2123 let send_htl = request_htl.saturating_sub(1);
2127 let req = match quote_id {
2128 Some(quote_id) => create_request_with_quote(hash, send_htl, quote_id),
2129 None => create_request(hash, send_htl),
2130 };
2131 let request_bytes = encode_request(&req);
2132 let request_len = request_bytes.len() as u64;
2133
2134 {
2135 let mut selector = self.peer_selector.write().await;
2136 selector.record_request(peer_id, request_len);
2137 }
2138
2139 match channel.send(request_bytes).await {
2140 Ok(()) => {
2141 self.record_peer_wire_sent(peer_id, request_len).await;
2142 true
2143 }
2144 Err(_) => {
2145 self.peer_selector.write().await.record_failure(peer_id);
2146 false
2147 }
2148 }
2149 }
2150
2151 async fn send_quote_request_to_peer(
2152 &self,
2153 peer_id: &str,
2154 hash: &Hash,
2155 payment_sat: u64,
2156 ttl_ms: u32,
2157 mint_url: Option<&str>,
2158 ) -> bool {
2159 let channel = match self.signaling.get_channel(peer_id).await {
2160 Some(c) => c,
2161 None => return false,
2162 };
2163
2164 let req = create_quote_request(hash, ttl_ms, payment_sat, mint_url);
2165 let request_bytes = encode_quote_request(&req);
2166 let request_len = request_bytes.len() as u64;
2167
2168 match channel.send(request_bytes).await {
2169 Ok(()) => {
2170 self.record_peer_wire_sent(peer_id, request_len).await;
2171 true
2172 }
2173 Err(_) => false,
2174 }
2175 }
2176
2177 pub async fn set_read_sources(&self, sources: Vec<Arc<dyn MeshReadSource>>) {
2178 let mut by_id = HashMap::new();
2179 let mut stats = self.read_source_stats.write().await;
2180 for source in sources {
2181 let source_id = source.id().to_string();
2182 by_id.insert(source_id.clone(), source);
2183 stats
2184 .entry(source_id)
2185 .or_insert_with(AdaptiveSourceStats::default);
2186 }
2187 *self.read_sources.write().await = by_id;
2188 }
2189
2190 async fn record_read_source_request(&self, source_id: &str) {
2191 let mut stats = self.read_source_stats.write().await;
2192 stats
2193 .entry(source_id.to_string())
2194 .or_insert_with(AdaptiveSourceStats::default)
2195 .requests += 1;
2196 }
2197
2198 async fn record_read_source_miss(&self, source_id: &str) {
2199 let mut stats = self.read_source_stats.write().await;
2200 stats
2201 .entry(source_id.to_string())
2202 .or_insert_with(AdaptiveSourceStats::default)
2203 .misses += 1;
2204 }
2205
2206 async fn record_read_source_success(&self, source_id: &str, elapsed_ms: u64) {
2207 let now = Instant::now();
2208 let mut stats = self.read_source_stats.write().await;
2209 let stats = stats
2210 .entry(source_id.to_string())
2211 .or_insert_with(AdaptiveSourceStats::default);
2212 stats.successes += 1;
2213 stats.last_success_at = Some(now);
2214 stats.backoff_level = 0;
2215 stats.backed_off_until = None;
2216 if stats.srtt_ms <= 0.0 {
2217 stats.srtt_ms = elapsed_ms as f64;
2218 stats.rttvar_ms = elapsed_ms as f64 / 2.0;
2219 return;
2220 }
2221 let elapsed = elapsed_ms as f64;
2222 stats.rttvar_ms = 0.75 * stats.rttvar_ms + 0.25 * (stats.srtt_ms - elapsed).abs();
2223 stats.srtt_ms = 0.875 * stats.srtt_ms + 0.125 * elapsed;
2224 }
2225
2226 async fn record_read_source_failure(&self, source_id: &str) {
2227 let now = Instant::now();
2228 let mut stats = self.read_source_stats.write().await;
2229 let stats = stats
2230 .entry(source_id.to_string())
2231 .or_insert_with(AdaptiveSourceStats::default);
2232 stats.failures += 1;
2233 stats.last_failure_at = Some(now);
2234 Self::apply_source_backoff(stats, now);
2235 }
2236
2237 async fn record_read_source_timeout(&self, source_id: &str) {
2238 let now = Instant::now();
2239 let mut stats = self.read_source_stats.write().await;
2240 let stats = stats
2241 .entry(source_id.to_string())
2242 .or_insert_with(AdaptiveSourceStats::default);
2243 stats.timeouts += 1;
2244 stats.last_failure_at = Some(now);
2245 Self::apply_source_backoff(stats, now);
2246 }
2247
2248 fn apply_source_backoff(stats: &mut AdaptiveSourceStats, now: Instant) {
2249 stats.backoff_level = stats.backoff_level.saturating_add(1);
2250 let backoff_ms = (INITIAL_SOURCE_BACKOFF_MS
2251 .saturating_mul(2u64.saturating_pow(stats.backoff_level.saturating_sub(1))))
2252 .min(MAX_SOURCE_BACKOFF_MS);
2253 stats.backed_off_until = Some(now + Duration::from_millis(backoff_ms));
2254 }
2255
2256 async fn ordered_read_sources(&self) -> Vec<Arc<dyn MeshReadSource>> {
2257 let sources = self.read_sources.read().await;
2258 if sources.is_empty() {
2259 return Vec::new();
2260 }
2261
2262 let mut available: Vec<Arc<dyn MeshReadSource>> = sources
2263 .values()
2264 .filter(|source| source.is_available())
2265 .cloned()
2266 .collect();
2267 if available.is_empty() {
2268 return Vec::new();
2269 }
2270
2271 let now = Instant::now();
2272 let stats = self.read_source_stats.read().await;
2273 let mut healthy: Vec<Arc<dyn MeshReadSource>> = available
2274 .iter()
2275 .filter(|source| {
2276 stats
2277 .get(source.id())
2278 .and_then(|s| s.backed_off_until)
2279 .is_none_or(|until| until <= now)
2280 })
2281 .cloned()
2282 .collect();
2283 if !healthy.is_empty() {
2284 available = std::mem::take(&mut healthy);
2285 }
2286
2287 available.sort_by(|left, right| {
2288 let left_stats = stats.get(left.id()).cloned().unwrap_or_default();
2289 let right_stats = stats.get(right.id()).cloned().unwrap_or_default();
2290 adaptive_source_score(&right_stats, now)
2291 .partial_cmp(&adaptive_source_score(&left_stats, now))
2292 .unwrap_or(std::cmp::Ordering::Equal)
2293 .then_with(|| left.id().cmp(right.id()))
2294 });
2295 available
2296 }
2297
2298 async fn should_probe_multiple_read_sources(
2299 &self,
2300 ordered_sources: &[Arc<dyn MeshReadSource>],
2301 ) -> bool {
2302 if ordered_sources.len() <= 1 {
2303 return false;
2304 }
2305 let stats = self.read_source_stats.read().await;
2306 let best = stats
2307 .get(ordered_sources[0].id())
2308 .cloned()
2309 .unwrap_or_default();
2310 let second = stats
2311 .get(ordered_sources[1].id())
2312 .cloned()
2313 .unwrap_or_default();
2314 if !source_has_history(&best) || !source_has_history(&second) {
2315 return false;
2316 }
2317 let now = Instant::now();
2318 adaptive_source_score(&best, now) - adaptive_source_score(&second, now)
2319 < SOURCE_SCORE_TIE_DELTA
2320 }
2321
2322 async fn source_dispatch_for(&self, source_count: usize) -> RequestDispatchConfig {
2323 if source_count == 0 {
2324 return self.routing.dispatch;
2325 }
2326 let ordered_sources = self.ordered_read_sources().await;
2327 let probe_multiple = self
2328 .should_probe_multiple_read_sources(&ordered_sources)
2329 .await;
2330 let initial_fanout = if probe_multiple {
2331 source_count.min(2)
2332 } else {
2333 1
2334 };
2335 RequestDispatchConfig {
2336 initial_fanout,
2337 hedge_fanout: self.routing.dispatch.hedge_fanout,
2338 max_fanout: self.routing.dispatch.max_fanout.min(source_count),
2339 hedge_interval_ms: self.routing.dispatch.hedge_interval_ms,
2340 }
2341 }
2342
2343 pub async fn peer_count(&self) -> usize {
2345 self.signaling.peer_count().await
2346 }
2347
2348 pub async fn peer_ids(&self) -> Vec<String> {
2350 self.signaling.peer_ids().await
2351 }
2352
2353 pub async fn needs_peers(&self) -> bool {
2355 self.signaling.needs_peers().await
2356 }
2357
2358 pub async fn send_hello(&self) -> Result<(), TransportError> {
2360 self.signaling.send_hello(vec![]).await
2361 }
2362
2363 pub async fn drain_available_data_messages(self: &Arc<Self>) -> DataPumpStats {
2368 let mut stats = DataPumpStats::default();
2369 let peer_ids = self.signaling.peer_ids().await;
2370 for peer_id in peer_ids {
2371 let Some(channel) = self.signaling.get_channel(&peer_id).await else {
2372 continue;
2373 };
2374
2375 while let Some(data) = channel.try_recv() {
2376 stats.processed += 1;
2377 stats.processed_bytes += data.len() as u64;
2378 if let Some(msg) = parse_message(&data) {
2379 match msg {
2380 DataMessage::Request(_) => stats.request_messages += 1,
2381 DataMessage::Response(_) => stats.response_messages += 1,
2382 DataMessage::QuoteRequest(_) => stats.quote_request_messages += 1,
2383 DataMessage::QuoteResponse(_) => stats.quote_response_messages += 1,
2384 DataMessage::PubsubInterest(_) => stats.pubsub_interest_messages += 1,
2385 DataMessage::PubsubFrame(_) => stats.pubsub_frame_messages += 1,
2386 DataMessage::PubsubInventory(_) => stats.pubsub_inventory_messages += 1,
2387 DataMessage::PubsubWant(_) => stats.pubsub_want_messages += 1,
2388 DataMessage::Payment(_)
2389 | DataMessage::PaymentAck(_)
2390 | DataMessage::Chunk(_)
2391 | DataMessage::PeerHints(_) => {}
2392 }
2393 }
2394 self.handle_data_message(&peer_id, &data).await;
2395 }
2396 }
2397 stats
2398 }
2399
2400 pub async fn record_cashu_payment_for_peer(&self, peer_id: &str, amount_sat: u64) {
2402 self.peer_selector
2403 .write()
2404 .await
2405 .record_cashu_payment(peer_id, amount_sat);
2406 }
2407
2408 pub async fn record_cashu_receipt_from_peer(&self, peer_id: &str, amount_sat: u64) {
2410 self.peer_selector
2411 .write()
2412 .await
2413 .record_cashu_receipt(peer_id, amount_sat);
2414 }
2415
2416 pub async fn record_cashu_payment_default_from_peer(&self, peer_id: &str) {
2418 self.peer_selector
2419 .write()
2420 .await
2421 .record_cashu_payment_default(peer_id);
2422 }
2423
2424 pub async fn selector_summary(&self) -> crate::peer_selector::SelectorSummary {
2426 self.peer_selector.read().await.summary()
2427 }
2428
2429 fn should_refuse_requests_from_peer(&self, selector: &PeerSelector, peer_id: &str) -> bool {
2430 selector.is_peer_blocked_for_payment_defaults(
2431 peer_id,
2432 self.routing.cashu_payment_default_block_threshold,
2433 )
2434 }
2435
2436 pub async fn peer_metadata_snapshot(&self) -> PeerMetadataSnapshot {
2438 self.peer_selector
2439 .read()
2440 .await
2441 .export_peer_metadata_snapshot()
2442 }
2443
2444 pub async fn persist_peer_metadata(&self) -> Result<Hash, StoreError> {
2449 let snapshot = self
2450 .peer_selector
2451 .read()
2452 .await
2453 .export_peer_metadata_snapshot();
2454 let bytes = serde_json::to_vec(&snapshot).map_err(|e| {
2455 StoreError::Other(format!("Failed to encode peer metadata snapshot: {e}"))
2456 })?;
2457 let snapshot_hash = hashtree_core::sha256(&bytes);
2458 let _ = self.local_store.put(snapshot_hash, bytes).await?;
2459
2460 let pointer_slot = Self::peer_metadata_pointer_slot_hash();
2461 let pointer_bytes = hex::encode(snapshot_hash).into_bytes();
2462 let _ = self.local_store.delete(&pointer_slot).await?;
2463 let _ = self.local_store.put(pointer_slot, pointer_bytes).await?;
2464
2465 Ok(snapshot_hash)
2466 }
2467
2468 pub async fn load_peer_metadata(&self) -> Result<bool, StoreError> {
2470 let pointer_slot = Self::peer_metadata_pointer_slot_hash();
2471 let Some(pointer_bytes) = self.local_store.get(&pointer_slot).await? else {
2472 return Ok(false);
2473 };
2474 let pointer_hex = std::str::from_utf8(&pointer_bytes).map_err(|e| {
2475 StoreError::Other(format!("Peer metadata pointer is not valid UTF-8: {e}"))
2476 })?;
2477 let snapshot_hash = Self::decode_hash_hex(pointer_hex.trim())?;
2478
2479 let Some(snapshot_bytes) = self.local_store.get(&snapshot_hash).await? else {
2480 return Ok(false);
2481 };
2482 let snapshot: PeerMetadataSnapshot =
2483 serde_json::from_slice(&snapshot_bytes).map_err(|e| {
2484 StoreError::Other(format!("Failed to decode peer metadata snapshot: {e}"))
2485 })?;
2486 self.peer_selector
2487 .write()
2488 .await
2489 .import_peer_metadata_snapshot(&snapshot);
2490 Ok(true)
2491 }
2492
2493 pub async fn get_with_quote(
2498 &self,
2499 hash: &Hash,
2500 payment_sat: u64,
2501 quote_ttl: Duration,
2502 ) -> Result<Option<Vec<u8>>, StoreError> {
2503 if let Some(data) = self.local_store.get(hash).await? {
2504 if hashtree_core::sha256(&data) != *hash {
2505 return Err(StoreError::Other(
2506 "local store returned corrupt content".to_string(),
2507 ));
2508 }
2509 return Ok(Some(data));
2510 }
2511 self.request_from_peers_with_quote(hash, payment_sat, quote_ttl)
2512 .await
2513 }
2514
2515 async fn request_from_peers_with_quote(
2516 &self,
2517 hash: &Hash,
2518 payment_sat: u64,
2519 quote_ttl: Duration,
2520 ) -> Result<Option<Vec<u8>>, StoreError> {
2521 let ordered_peer_ids = self.ordered_connected_peers(None).await;
2522 if ordered_peer_ids.is_empty() {
2523 return Ok(None);
2524 }
2525
2526 if let Some(quote) = self
2527 .request_quote_from_peers(hash, payment_sat, quote_ttl, &ordered_peer_ids)
2528 .await
2529 {
2530 if let Some(data) = self
2531 .request_from_single_peer(hash, "e.peer_id, MAX_HTL, Some(quote.quote_id))
2532 .await
2533 {
2534 return Ok(Some(data));
2535 }
2536 }
2537
2538 match self
2539 .request_from_mesh_with_context(hash, &MeshReadContext::default())
2540 .await
2541 {
2542 RouteFetchOutcome::Hit(data) => Ok(Some(data)),
2543 RouteFetchOutcome::Miss => Ok(None),
2544 RouteFetchOutcome::Timeout => Err(StoreError::Other(
2545 "blob retrieval deadline expired before the search completed".to_string(),
2546 )),
2547 RouteFetchOutcome::Failure => Err(StoreError::Other(
2548 "blob retrieval failed before the search completed".to_string(),
2549 )),
2550 }
2551 }
2552
2553 async fn request_quote_from_peers(
2554 &self,
2555 hash: &Hash,
2556 payment_sat: u64,
2557 quote_ttl: Duration,
2558 ordered_peer_ids: &[String],
2559 ) -> Option<NegotiatedQuote> {
2560 if ordered_peer_ids.is_empty() {
2561 return None;
2562 }
2563 let ttl_ms = quote_ttl.as_millis().min(u32::MAX as u128) as u32;
2564 if ttl_ms == 0 {
2565 return None;
2566 }
2567 let requested_mint = self.requested_quote_mint().map(str::to_string);
2568
2569 let hash_key = hash_to_key(hash);
2570 let (tx, rx) = oneshot::channel();
2571 self.pending_quotes.write().await.insert(
2572 hash_key.clone(),
2573 PendingQuoteRequest {
2574 response_tx: tx,
2575 preferred_mint_url: requested_mint.clone(),
2576 offered_payment_sat: payment_sat,
2577 },
2578 );
2579
2580 let rx = Arc::new(Mutex::new(rx));
2581 let result = run_hedged_waves(
2582 ordered_peer_ids.len(),
2583 self.routing.dispatch,
2584 self.request_timeout,
2585 |range| {
2586 let wave_peer_ids = ordered_peer_ids[range].to_vec();
2587 let requested_mint = requested_mint.clone();
2588 let hash = *hash;
2589 async move {
2590 let mut sent = 0usize;
2591 for peer_id in wave_peer_ids {
2592 if self
2593 .send_quote_request_to_peer(
2594 &peer_id,
2595 &hash,
2596 payment_sat,
2597 ttl_ms,
2598 requested_mint.as_deref(),
2599 )
2600 .await
2601 {
2602 sent += 1;
2603 }
2604 }
2605 sent
2606 }
2607 },
2608 |wait| {
2609 let rx = rx.clone();
2610 async move {
2611 let mut rx = rx.lock().await;
2612 match tokio::time::timeout(wait, &mut *rx).await {
2613 Ok(Ok(Some(quote))) => HedgedWaveAction::Success(quote),
2614 Ok(Ok(None)) | Ok(Err(_)) => HedgedWaveAction::Abort,
2615 Err(_) => HedgedWaveAction::Continue,
2616 }
2617 }
2618 },
2619 )
2620 .await;
2621 let _ = self.pending_quotes.write().await.remove(&hash_key);
2622 result
2623 }
2624
2625 async fn register_pending_request(
2626 &self,
2627 request_key: PendingRequestKey,
2628 queried_peers: Vec<String>,
2629 ) -> (Arc<()>, oneshot::Receiver<Option<Vec<u8>>>) {
2630 let owner = Arc::new(());
2631 let (response_tx, response_rx) = oneshot::channel();
2632 self.pending_requests
2633 .write()
2634 .await
2635 .entry(request_key)
2636 .or_default()
2637 .push(PendingRequest {
2638 owner: owner.clone(),
2639 response_tx,
2640 started_at: Instant::now(),
2641 queried_peers,
2642 });
2643 (owner, response_rx)
2644 }
2645
2646 async fn take_pending_request(
2647 &self,
2648 request_key: PendingRequestKey,
2649 owner: &Arc<()>,
2650 ) -> Option<(PendingRequest, bool)> {
2651 let mut pending = self.pending_requests.write().await;
2652 let (request, remove_key) = {
2653 let requests = pending.get_mut(&request_key)?;
2654 let index = requests
2655 .iter()
2656 .position(|request| Arc::ptr_eq(&request.owner, owner))?;
2657 let request = requests.swap_remove(index);
2658 (request, requests.is_empty())
2659 };
2660 if remove_key {
2661 pending.remove(&request_key);
2662 }
2663 Some((request, remove_key))
2664 }
2665
2666 async fn request_from_single_peer(
2667 &self,
2668 hash: &Hash,
2669 peer_id: &str,
2670 request_htl: u8,
2671 quote_id: Option<u64>,
2672 ) -> Option<Vec<u8>> {
2673 let request_key = PendingRequestKey::new(*hash, request_htl);
2674 let (owner, rx) = self
2675 .register_pending_request(request_key, vec![peer_id.to_string()])
2676 .await;
2677
2678 let mut rx = rx;
2679 if !self
2680 .send_request_to_peer(peer_id, hash, request_htl, quote_id)
2681 .await
2682 {
2683 if self
2684 .take_pending_request(request_key, &owner)
2685 .await
2686 .is_some_and(|(_, last)| last)
2687 {
2688 let _ = self.take_forward_requesters(request_key).await;
2689 }
2690 return None;
2691 }
2692 self.reserve_peer_request(peer_id).await;
2693
2694 if let Ok(Ok(Some(data))) = tokio::time::timeout(self.request_timeout, &mut rx).await {
2695 if data.len() <= BLOB_MAX_BYTES && hashtree_core::sha256(&data) == *hash {
2696 let _ = self.local_store.put(*hash, data.clone()).await;
2697 return Some(data);
2698 }
2699 }
2700
2701 if let Some((pending, last)) = self.take_pending_request(request_key, &owner).await {
2702 self.release_queried_peer_requests(&pending.queried_peers)
2703 .await;
2704 for peer_id in pending.queried_peers {
2705 self.peer_selector.write().await.record_timeout(&peer_id);
2706 }
2707 if last {
2708 let _ = self.take_forward_requesters(request_key).await;
2709 }
2710 }
2711 None
2712 }
2713
2714 async fn request_from_ordered_peers(
2715 &self,
2716 hash: &Hash,
2717 ordered_peer_ids: &[String],
2718 request_htl: u8,
2719 ) -> RouteFetchOutcome {
2720 let request_key = PendingRequestKey::new(*hash, request_htl);
2721 let (owner, rx) = self.register_pending_request(request_key, Vec::new()).await;
2722
2723 let rx = Arc::new(Mutex::new(rx));
2724 let result = run_hedged_waves(
2725 ordered_peer_ids.len(),
2726 self.routing.dispatch,
2727 self.request_timeout,
2728 |range| {
2729 let wave_peer_ids = ordered_peer_ids[range].to_vec();
2730 let hash = *hash;
2731 let owner = owner.clone();
2732 async move {
2733 let mut sent = 0usize;
2734 for peer_id in wave_peer_ids {
2735 if self
2736 .send_request_to_peer(&peer_id, &hash, request_htl, None)
2737 .await
2738 {
2739 sent += 1;
2740 self.reserve_peer_request(&peer_id).await;
2741 if let Some(pending) = self
2742 .pending_requests
2743 .write()
2744 .await
2745 .get_mut(&request_key)
2746 .and_then(|requests| {
2747 requests
2748 .iter_mut()
2749 .find(|request| Arc::ptr_eq(&request.owner, &owner))
2750 })
2751 {
2752 pending.queried_peers.push(peer_id);
2753 }
2754 }
2755 }
2756 sent
2757 }
2758 },
2759 |wait| {
2760 let rx = rx.clone();
2761 async move {
2762 let mut rx = rx.lock().await;
2763 match tokio::time::timeout(wait, &mut *rx).await {
2764 Ok(Ok(Some(data)))
2765 if data.len() <= BLOB_MAX_BYTES
2766 && hashtree_core::sha256(&data) == *hash =>
2767 {
2768 HedgedWaveAction::Success(data)
2769 }
2770 Ok(Ok(Some(_))) => HedgedWaveAction::Continue,
2771 Ok(Ok(None)) | Ok(Err(_)) => HedgedWaveAction::Abort,
2772 Err(_) => HedgedWaveAction::Continue,
2773 }
2774 }
2775 },
2776 )
2777 .await;
2778
2779 let Some(data) = result else {
2780 if let Some((pending, last)) = self.take_pending_request(request_key, &owner).await {
2781 self.release_queried_peer_requests(&pending.queried_peers)
2782 .await;
2783 for peer_id in pending.queried_peers {
2784 self.peer_selector.write().await.record_timeout(&peer_id);
2785 }
2786 if last {
2787 let _ = self.take_forward_requesters(request_key).await;
2788 }
2789 }
2790 return RouteFetchOutcome::Timeout;
2791 };
2792
2793 let _ = self.local_store.put(*hash, data.clone()).await;
2794 RouteFetchOutcome::Hit(data)
2795 }
2796
2797 async fn request_from_read_sources_inner(&self, request: BlobRequest) -> RouteFetchOutcome {
2798 if request.htl == 0 {
2799 return RouteFetchOutcome::Miss;
2800 }
2801 let ordered_sources = self.ordered_read_sources().await;
2802 if ordered_sources.is_empty() {
2803 return RouteFetchOutcome::Miss;
2804 }
2805
2806 let dispatch = normalize_dispatch_config(
2807 self.source_dispatch_for(ordered_sources.len()).await,
2808 ordered_sources.len(),
2809 );
2810 let wave_plan = build_hedged_wave_plan(ordered_sources.len(), dispatch);
2811 if wave_plan.is_empty() {
2812 return RouteFetchOutcome::Miss;
2813 }
2814
2815 let deadline = Instant::now() + self.request_timeout;
2816 let mut pending = FuturesUnordered::new();
2817 let mut pending_source_ids = HashSet::new();
2818 let mut saw_timeout = false;
2819 let mut saw_failure = false;
2820 let mut next_source_idx = 0usize;
2821
2822 for (wave_idx, wave_size) in wave_plan.iter().copied().enumerate() {
2823 let from = next_source_idx;
2824 let to = (next_source_idx + wave_size).min(ordered_sources.len());
2825 next_source_idx = to;
2826
2827 for source in &ordered_sources[from..to] {
2828 let source = Arc::clone(source);
2829 let source_id = source.id().to_string();
2830 let source_request = BlobRequest {
2831 hash: request.hash,
2832 htl: match source.kind() {
2833 BlobRouteKind::Terminal => request.htl,
2834 BlobRouteKind::MeshPeer => request.htl.saturating_sub(1),
2835 },
2836 };
2837 self.record_read_source_request(&source_id).await;
2838 pending_source_ids.insert(source_id.clone());
2839 pending.push(async move {
2840 let started_at = Instant::now();
2841 let result = std::panic::AssertUnwindSafe(source.route(source_request))
2842 .catch_unwind()
2843 .await;
2844 match result {
2845 Ok(Ok(BlobReply::Data(data)))
2846 if data.len() <= BLOB_MAX_BYTES
2847 && hashtree_core::sha256(&data) == source_request.hash =>
2848 {
2849 SourceFetchOutcome::Hit {
2850 source_id,
2851 data,
2852 elapsed_ms: started_at.elapsed().as_millis().max(1) as u64,
2853 }
2854 }
2855 Ok(Ok(BlobReply::NoResult)) => SourceFetchOutcome::Miss { source_id },
2856 Ok(Ok(BlobReply::Data(_)) | Err(_)) | Err(_) => {
2857 SourceFetchOutcome::Failure { source_id }
2858 }
2859 }
2860 });
2861 }
2862
2863 let is_last_wave =
2864 wave_idx + 1 == wave_plan.len() || next_source_idx >= ordered_sources.len();
2865 let window_end = if is_last_wave {
2866 deadline
2867 } else {
2868 (Instant::now() + Duration::from_millis(dispatch.hedge_interval_ms)).min(deadline)
2869 };
2870
2871 while Instant::now() < window_end {
2872 let remaining = window_end.saturating_duration_since(Instant::now());
2873 let Some(outcome) = tokio::time::timeout(remaining, pending.next())
2874 .await
2875 .ok()
2876 .flatten()
2877 else {
2878 break;
2879 };
2880 match outcome {
2881 SourceFetchOutcome::Hit {
2882 source_id,
2883 data,
2884 elapsed_ms,
2885 } => {
2886 pending_source_ids.remove(&source_id);
2887 self.record_read_source_success(&source_id, elapsed_ms)
2888 .await;
2889 return RouteFetchOutcome::Hit(data);
2890 }
2891 SourceFetchOutcome::Miss { source_id } => {
2892 pending_source_ids.remove(&source_id);
2893 self.record_read_source_miss(&source_id).await;
2894 }
2895 SourceFetchOutcome::Failure { source_id } => {
2896 pending_source_ids.remove(&source_id);
2897 saw_failure = true;
2898 self.record_read_source_failure(&source_id).await;
2899 }
2900 }
2901 }
2902
2903 if Instant::now() >= deadline {
2904 break;
2905 }
2906 }
2907
2908 for source_id in pending_source_ids {
2909 saw_timeout = true;
2910 self.record_read_source_timeout(&source_id).await;
2911 }
2912 if saw_timeout {
2913 RouteFetchOutcome::Timeout
2914 } else if saw_failure {
2915 RouteFetchOutcome::Failure
2916 } else {
2917 RouteFetchOutcome::Miss
2918 }
2919 }
2920
2921 async fn request_from_read_sources(&self, request: BlobRequest) -> RouteFetchOutcome {
2922 let hash_key = format!("{}:{}", hash_to_key(&request.hash), request.htl);
2923 let (existing_wait, owner_guard) = {
2924 let mut inflight = self
2925 .inflight_source_fetches
2926 .lock()
2927 .unwrap_or_else(|poisoned| poisoned.into_inner());
2928 if let Some(existing) = inflight.get_mut(&hash_key) {
2929 let (tx, rx) = oneshot::channel();
2930 existing.waiters.push(tx);
2931 (Some(rx), None)
2932 } else {
2933 let owner = Arc::new(());
2934 inflight.insert(
2935 hash_key.clone(),
2936 InflightSourceFetch {
2937 owner: owner.clone(),
2938 waiters: Vec::new(),
2939 },
2940 );
2941 (
2942 None,
2943 Some(InflightSourceFetchGuard {
2944 inflight_source_fetches: &self.inflight_source_fetches,
2945 hash_key: hash_key.clone(),
2946 owner,
2947 }),
2948 )
2949 }
2950 };
2951
2952 if let Some(wait) = existing_wait {
2953 return match tokio::time::timeout(self.request_timeout, wait).await {
2954 Ok(Ok(result)) => result,
2955 Ok(Err(_)) | Err(_) => RouteFetchOutcome::Timeout,
2956 };
2957 }
2958
2959 let owner_guard = owner_guard.expect("new source fetch owns its inflight entry");
2960 let result = self.request_from_read_sources_inner(request).await;
2961 if let RouteFetchOutcome::Hit(hit) = &result {
2962 let _ = self.local_store.put(request.hash, hit.clone()).await;
2963 }
2964 owner_guard.complete(result.clone());
2965
2966 result
2967 }
2968
2969 async fn cancel_pending_peer_route(&self, request_key: PendingRequestKey) {
2970 if let Some(pending) = self.pending_requests.write().await.remove(&request_key) {
2971 let queried_peers: Vec<_> = pending
2972 .iter()
2973 .flat_map(|request| request.queried_peers.iter().cloned())
2974 .collect();
2975 self.release_queried_peer_requests(&queried_peers).await;
2976 }
2977 }
2978
2979 async fn cancel_losing_route(&self, request: BlobRequest, route: &ReadRoute) {
2980 if matches!(route, ReadRoute::Peers(_)) {
2981 self.cancel_pending_peer_route(PendingRequestKey::new(request.hash, request.htl))
2982 .await;
2983 }
2984 }
2985
2986 async fn ranked_read_routes(&self, context: &MeshReadContext) -> Vec<RankedReadRoute> {
2987 let mut routes = Vec::new();
2988 let ordered_peers = if should_forward_htl(context.request_htl) {
2989 self.ordered_connected_peers(context.exclude_peer_id.as_deref())
2990 .await
2991 } else {
2992 Vec::new()
2993 };
2994 if !ordered_peers.is_empty() {
2995 let best_peer_id = ordered_peers[0].clone();
2996 let selector = self.peer_selector.read().await;
2997 let best_peer = selector.get_stats(&best_peer_id).cloned();
2998 let now = Instant::now();
2999 let (score, has_history) = match best_peer.as_ref() {
3000 Some(stats) => (
3001 peer_endpoint_score(stats, now),
3002 peer_endpoint_has_history(stats),
3003 ),
3004 None => (0.0, false),
3005 };
3006 routes.push(RankedReadRoute {
3007 route: ReadRoute::Peers(ordered_peers),
3008 best_endpoint_id: format!("peer:{best_peer_id}"),
3009 score,
3010 has_history,
3011 });
3012 }
3013 let ordered_sources = self.ordered_read_sources().await;
3014 if let Some(best_source) = ordered_sources.first() {
3015 let stats = self.read_source_stats.read().await;
3016 let best_source_stats = stats.get(best_source.id()).cloned().unwrap_or_default();
3017 let now = Instant::now();
3018 routes.push(RankedReadRoute {
3019 route: ReadRoute::Sources,
3020 best_endpoint_id: format!("source:{}", best_source.id()),
3021 score: adaptive_source_score(&best_source_stats, now),
3022 has_history: source_has_history(&best_source_stats),
3023 });
3024 }
3025 if routes.len() <= 1 {
3026 return routes;
3027 }
3028
3029 routes.sort_by(|left, right| {
3030 right
3031 .score
3032 .partial_cmp(&left.score)
3033 .unwrap_or(std::cmp::Ordering::Equal)
3034 .then_with(|| ranked_route_kind(&left.route).cmp(&ranked_route_kind(&right.route)))
3035 .then_with(|| left.best_endpoint_id.cmp(&right.best_endpoint_id))
3036 .then_with(|| left.route.id().cmp(right.route.id()))
3037 });
3038 routes
3039 }
3040
3041 fn should_probe_multiple_routes(&self, routes: &[RankedReadRoute]) -> bool {
3042 if routes.len() <= 1 {
3043 return false;
3044 }
3045 if !routes[0].has_history || !routes[1].has_history {
3046 return false;
3047 }
3048 (routes[0].score - routes[1].score) < SOURCE_SCORE_TIE_DELTA
3049 }
3050
3051 async fn run_read_route(
3052 &self,
3053 hash: &Hash,
3054 route: &ReadRoute,
3055 context: &MeshReadContext,
3056 ) -> RouteFetchOutcome {
3057 match route {
3058 ReadRoute::Peers(peer_ids) => {
3059 self.request_from_ordered_peers(hash, peer_ids, context.request_htl)
3060 .await
3061 }
3062 ReadRoute::Sources => {
3063 self.request_from_read_sources(BlobRequest {
3064 hash: *hash,
3065 htl: context.request_htl,
3066 })
3067 .await
3068 }
3069 }
3070 }
3071
3072 async fn request_from_mesh_with_context(
3073 &self,
3074 hash: &Hash,
3075 context: &MeshReadContext,
3076 ) -> RouteFetchOutcome {
3077 let routes = self.ranked_read_routes(context).await;
3078 match routes.as_slice() {
3079 [] => RouteFetchOutcome::Miss,
3080 [ranked] => self.run_read_route(hash, &ranked.route, context).await,
3081 [first, second, ..] => {
3082 if self.should_probe_multiple_routes(&routes) {
3083 let first_fut = self.run_read_route(hash, &first.route, context);
3084 let second_fut = self.run_read_route(hash, &second.route, context);
3085 tokio::pin!(first_fut);
3086 tokio::pin!(second_fut);
3087 let mut first_done = false;
3088 let mut second_done = false;
3089 let mut combined = RouteFetchOutcome::Miss;
3090 loop {
3091 tokio::select! {
3092 result = &mut first_fut, if !first_done => {
3093 first_done = true;
3094 if matches!(result, RouteFetchOutcome::Hit(_)) {
3095 if !second_done {
3096 self.cancel_losing_route(
3097 BlobRequest {
3098 hash: *hash,
3099 htl: context.request_htl,
3100 },
3101 &second.route,
3102 ).await;
3103 }
3104 return result;
3105 }
3106 combined = combined.combine_without_hit(result);
3107 }
3108 result = &mut second_fut, if !second_done => {
3109 second_done = true;
3110 if matches!(result, RouteFetchOutcome::Hit(_)) {
3111 if !first_done {
3112 self.cancel_losing_route(
3113 BlobRequest {
3114 hash: *hash,
3115 htl: context.request_htl,
3116 },
3117 &first.route,
3118 ).await;
3119 }
3120 return result;
3121 }
3122 combined = combined.combine_without_hit(result);
3123 }
3124 else => break,
3125 }
3126 if first_done && second_done {
3127 break;
3128 }
3129 }
3130 combined
3131 } else {
3132 let mut combined = self.run_read_route(hash, &first.route, context).await;
3133 if matches!(combined, RouteFetchOutcome::Hit(_)) {
3134 return combined;
3135 }
3136 for ranked in routes.iter().skip(1) {
3137 let result = self.run_read_route(hash, &ranked.route, context).await;
3138 if matches!(result, RouteFetchOutcome::Hit(_)) {
3139 return result;
3140 }
3141 combined = combined.combine_without_hit(result);
3142 }
3143 combined
3144 }
3145 }
3146 }
3147 }
3148
3149 async fn begin_forward_request(
3150 &self,
3151 request_key: PendingRequestKey,
3152 requester_id: &str,
3153 ) -> bool {
3154 let mut pending = self.pending_forward_requests.write().await;
3155 if let Some(existing) = pending.get_mut(&request_key) {
3156 existing.requester_ids.insert(requester_id.to_string());
3157 return false;
3158 }
3159
3160 let mut requester_ids = HashSet::new();
3161 requester_ids.insert(requester_id.to_string());
3162 pending.insert(request_key, PendingForwardRequest { requester_ids });
3163 true
3164 }
3165
3166 async fn take_forward_requesters(&self, request_key: PendingRequestKey) -> Vec<String> {
3167 self.pending_forward_requests
3168 .write()
3169 .await
3170 .remove(&request_key)
3171 .map(|pending| pending.requester_ids.into_iter().collect())
3172 .unwrap_or_default()
3173 }
3174
3175 async fn complete_pending_response(
3176 self: &Arc<Self>,
3177 from_peer: &str,
3178 hash: &Hash,
3179 payload: Vec<u8>,
3180 ) {
3181 let pending = {
3182 let mut requests = self.pending_requests.write().await;
3183 let matching_keys: Vec<_> = requests
3184 .keys()
3185 .filter(|key| key.hash == *hash)
3186 .copied()
3187 .collect();
3188 matching_keys
3189 .into_iter()
3190 .flat_map(|key| {
3191 requests
3192 .remove(&key)
3193 .into_iter()
3194 .flatten()
3195 .map(move |request| (key, request))
3196 })
3197 .collect::<Vec<_>>()
3198 };
3199 if pending.is_empty() {
3200 return;
3201 }
3202
3203 let payload_bytes = payload.len() as u64;
3204 self.record_useful_bytes_received_from_peer(from_peer, payload_bytes)
3205 .await;
3206 {
3207 let mut deliveries = self.verified_block_deliveries.lock().await;
3208 deliveries.deliveries.push_back(VerifiedBlockDelivery {
3209 hash: *hash,
3210 provider_peer_id: from_peer.to_string(),
3211 payload_bytes,
3212 });
3213 while deliveries.deliveries.len() > VERIFIED_BLOCK_DELIVERY_CAPACITY {
3214 deliveries.deliveries.pop_front();
3215 deliveries.dropped_since_last_drain =
3216 deliveries.dropped_since_last_drain.saturating_add(1);
3217 }
3218 }
3219
3220 let rtt_ms = pending
3221 .iter()
3222 .map(|(_, request)| request.started_at.elapsed().as_millis() as u64)
3223 .max()
3224 .unwrap_or_default();
3225 let queried_peers: Vec<_> = pending
3226 .iter()
3227 .flat_map(|(_, request)| request.queried_peers.iter().cloned())
3228 .collect();
3229 self.release_queried_peer_requests(&queried_peers).await;
3230 self.peer_selector
3231 .write()
3232 .await
3233 .record_success(from_peer, rtt_ms, payload_bytes);
3234
3235 let mut forward_requesters = HashSet::new();
3236 for (request_key, pending) in pending {
3237 forward_requesters.extend(self.take_forward_requesters(request_key).await);
3238 let _ = pending.response_tx.send(Some(payload.clone()));
3239 }
3240 if !forward_requesters.is_empty() {
3241 let response_bytes = encode_response(&create_response(hash, payload));
3242 for requester_id in forward_requesters {
3243 Arc::clone(self)
3244 .enqueue_response_send(requester_id, response_bytes.clone(), Instant::now())
3245 .await;
3246 }
3247 }
3248 }
3249
3250 async fn handle_quote_response_message(&self, from_peer: &str, res: DataQuoteResponse) {
3251 if !res.a {
3252 return;
3253 }
3254
3255 let Some(quote_id) = res.q else {
3256 return;
3257 };
3258
3259 let hash_key = hash_to_key(&res.h);
3260 let (preferred_mint_url, offered_payment_sat) = {
3261 let pending_quotes = self.pending_quotes.read().await;
3262 let Some(pending) = pending_quotes.get(&hash_key) else {
3263 return;
3264 };
3265 (
3266 pending.preferred_mint_url.clone(),
3267 pending.offered_payment_sat,
3268 )
3269 };
3270 if !self
3271 .should_accept_quote_response(
3272 from_peer,
3273 preferred_mint_url.as_deref(),
3274 offered_payment_sat,
3275 &res,
3276 )
3277 .await
3278 {
3279 return;
3280 }
3281 let mut pending_quotes = self.pending_quotes.write().await;
3282 if let Some(pending) = pending_quotes.remove(&hash_key) {
3283 let _ = pending.response_tx.send(Some(NegotiatedQuote {
3284 peer_id: from_peer.to_string(),
3285 quote_id,
3286 mint_url: res.m,
3287 }));
3288 }
3289 }
3290
3291 async fn handle_response_message(
3292 self: &Arc<Self>,
3293 from_peer: &str,
3294 res: crate::protocol::DataResponse,
3295 ) {
3296 let hash_key = hash_to_key(&res.h);
3297 let hash = match crate::protocol::bytes_to_hash(&res.h) {
3298 Some(h) => h,
3299 None => return,
3300 };
3301
3302 if hashtree_core::sha256(&res.d) != hash {
3304 self.peer_selector.write().await.record_failure(from_peer);
3305 if self.debug {
3306 println!("[MeshStoreCore] Ignoring invalid response payload for {hash_key}");
3307 }
3308 return;
3309 }
3310
3311 self.complete_pending_response(from_peer, &hash, res.d)
3312 .await;
3313 }
3314
3315 async fn handle_quote_request_message(&self, from_peer: &str, req: DataQuoteRequest) {
3316 let hash = match crate::protocol::bytes_to_hash(&req.h) {
3317 Some(h) => h,
3318 None => return,
3319 };
3320 let hash_key = hash_to_key(&hash);
3321
3322 {
3323 let selector = self.peer_selector.read().await;
3324 if self.should_refuse_requests_from_peer(&selector, from_peer) {
3325 if self.debug {
3326 println!(
3327 "[MeshStoreCore] Refusing quote request from delinquent peer {}",
3328 from_peer
3329 );
3330 }
3331 return;
3332 }
3333 }
3334
3335 let chosen_mint = self.choose_quote_mint(req.m.as_deref());
3336 let can_serve = self.local_store.has(&hash).await.ok().unwrap_or(false)
3337 && !self.should_drop_response(&hash)
3338 && !self.should_corrupt_response(&hash);
3339
3340 let res = if can_serve {
3341 let quote_id = self
3342 .issue_quote(from_peer, &hash_key, req.p, req.t, chosen_mint.as_deref())
3343 .await;
3344 create_quote_response_available(&hash, quote_id, req.p, req.t, chosen_mint.as_deref())
3345 } else {
3346 create_quote_response_unavailable(&hash)
3347 };
3348 let response_bytes = encode_quote_response(&res);
3349 if let Some(channel) = self.signaling.get_channel(from_peer).await {
3350 if channel.send(response_bytes.clone()).await.is_ok() {
3351 self.record_peer_wire_sent(from_peer, response_bytes.len() as u64)
3352 .await;
3353 }
3354 }
3355 }
3356
3357 async fn handle_request_message(
3358 self: &Arc<Self>,
3359 from_peer: &str,
3360 req: crate::protocol::DataRequest,
3361 ) {
3362 if req.htl > MAX_HTL {
3363 return;
3364 }
3365 let hash = match crate::protocol::bytes_to_hash(&req.h) {
3366 Some(h) => h,
3367 None => return,
3368 };
3369 let hash_key = hash_to_key(&hash);
3370 let request_key = PendingRequestKey::new(hash, req.htl);
3371
3372 if let Some(quote_id) = req.q {
3373 if !self.take_valid_quote(from_peer, &hash_key, quote_id).await {
3374 if self.debug {
3375 println!(
3376 "[MeshStoreCore] Refusing request with invalid or expired quote {} from {}",
3377 quote_id, from_peer
3378 );
3379 }
3380 return;
3381 }
3382 }
3383
3384 let allow_peer_forwarding = {
3385 let selector = self.peer_selector.read().await;
3386 !self.should_refuse_requests_from_peer(&selector, from_peer)
3387 };
3388
3389 if let Ok(Some(mut data)) = self.local_store.get(&hash).await {
3391 if data.len() <= BLOB_MAX_BYTES && hashtree_core::sha256(&data) == hash {
3392 if self.should_drop_response(&hash) {
3393 if self.debug {
3394 println!(
3395 "[MeshStoreCore] Dropping response for {} due to actor profile",
3396 hash_to_key(&hash)
3397 );
3398 }
3399 return;
3400 }
3401
3402 let response_delay = self.response_send_delay(&hash, data.len());
3403 if self.should_corrupt_response(&hash) {
3404 if data.is_empty() {
3405 data.push(0x80);
3406 } else {
3407 data[0] ^= 0x80;
3408 }
3409 }
3410
3411 let res = create_response(&hash, data);
3412 let response_bytes = encode_response(&res);
3413 let ready_at = Instant::now() + response_delay;
3414 Arc::clone(self)
3415 .enqueue_response_send(from_peer.to_string(), response_bytes, ready_at)
3416 .await;
3417 return;
3418 }
3419 }
3420
3421 if self
3422 .pending_requests
3423 .read()
3424 .await
3425 .contains_key(&request_key)
3426 {
3427 let _ = self.begin_forward_request(request_key, from_peer).await;
3428 return;
3429 }
3430
3431 if !self.begin_forward_request(request_key, from_peer).await {
3432 return;
3433 }
3434
3435 let from_peer = from_peer.to_string();
3436 let this = Arc::clone(self);
3437 let request_htl = req.htl;
3438 tokio::spawn(async move {
3439 let result = if allow_peer_forwarding {
3440 let context = MeshReadContext {
3441 exclude_peer_id: Some(from_peer.clone()),
3442 request_htl,
3443 };
3444 this.request_from_mesh_with_context(&hash, &context).await
3445 } else {
3446 if this.debug {
3447 println!(
3448 "[MeshStoreCore] Serving request from delinquent peer {} via read sources only",
3449 from_peer
3450 );
3451 }
3452 this.request_from_read_sources(BlobRequest {
3453 hash,
3454 htl: request_htl,
3455 })
3456 .await
3457 };
3458 let requester_ids = this.take_forward_requesters(request_key).await;
3459 match result {
3460 RouteFetchOutcome::Hit(data) => {
3461 let ready_at = Instant::now() + this.response_send_delay(&hash, data.len());
3462 let res = create_response(&hash, data);
3463 let response_bytes = encode_response(&res);
3464 for requester_id in requester_ids {
3465 Arc::clone(&this)
3466 .enqueue_response_send(requester_id, response_bytes.clone(), ready_at)
3467 .await;
3468 }
3469 }
3470 RouteFetchOutcome::Miss
3471 | RouteFetchOutcome::Timeout
3472 | RouteFetchOutcome::Failure => {}
3473 }
3474 });
3475 }
3476
3477 async fn handle_pubsub_interest_message(
3478 self: &Arc<Self>,
3479 from_peer: &str,
3480 mut interest: PubsubInterest,
3481 ) {
3482 if !self.apply_pubsub_interest_route(from_peer, &interest).await {
3483 return;
3484 }
3485
3486 if !self.routing.pubsub_forwarding || interest.htl <= 1 {
3487 return;
3488 }
3489 interest.htl = interest.htl.saturating_sub(1);
3490 let _ = self
3491 .send_pubsub_interest_to_peers(&interest, Some(from_peer))
3492 .await;
3493 }
3494
3495 async fn handle_pubsub_frame_message(
3496 self: &Arc<Self>,
3497 from_peer: &str,
3498 mut frame: PubsubFrame,
3499 wire_bytes: usize,
3500 ) {
3501 if frame.stream_id.is_empty() || frame.origin_peer_id.is_empty() {
3502 return;
3503 }
3504 if frame.origin_peer_id == self.signaling.peer_id() {
3505 return;
3506 }
3507
3508 let frame_key = Self::pubsub_frame_key(&frame);
3509 if !self
3510 .pubsub_seen_frames
3511 .lock()
3512 .await
3513 .insert_if_new(frame_key.clone())
3514 {
3515 return;
3516 }
3517 self.cache_pubsub_frame(frame_key.clone(), frame.clone())
3518 .await;
3519
3520 let local_interested = self
3521 .pubsub_local_interests
3522 .read()
3523 .await
3524 .contains(&frame.stream_id);
3525 let mut downstream_peers = if self.routing.pubsub_forwarding && frame.htl > 1 {
3526 match self.routing.pubsub_delivery_mode {
3527 PubsubDeliveryMode::InterestPush => {
3528 let mut peers = self
3529 .interested_pubsub_peers(&frame.stream_id, Some(from_peer))
3530 .await;
3531 peers.extend(
3532 self.take_pubsub_want_peers(&frame_key, Some(from_peer))
3533 .await,
3534 );
3535 peers.sort();
3536 peers.dedup();
3537 peers
3538 }
3539 PubsubDeliveryMode::HtlInvWant => {
3540 self.take_pubsub_want_peers(&frame_key, Some(from_peer))
3541 .await
3542 }
3543 }
3544 } else {
3545 Vec::new()
3546 };
3547 downstream_peers.retain(|peer_id| peer_id != from_peer);
3548
3549 if local_interested || !downstream_peers.is_empty() {
3550 self.record_useful_bytes_received_from_peer(from_peer, wire_bytes as u64)
3551 .await;
3552 }
3553
3554 if local_interested {
3555 self.enqueue_pubsub_event(PubsubEvent {
3556 stream_id: frame.stream_id.clone(),
3557 seq: frame.seq,
3558 origin_peer_id: frame.origin_peer_id.clone(),
3559 from_peer_id: from_peer.to_string(),
3560 payload: frame.payload.clone(),
3561 })
3562 .await;
3563 }
3564
3565 if downstream_peers.is_empty() {
3566 return;
3567 }
3568
3569 frame.htl = frame.htl.saturating_sub(1);
3570 let _ = self
3571 .send_pubsub_frame_to_peers(&frame, &downstream_peers)
3572 .await;
3573 }
3574
3575 async fn handle_pubsub_inventory_message(
3576 self: &Arc<Self>,
3577 from_peer: &str,
3578 inv: PubsubInventory,
3579 wire_bytes: usize,
3580 ) {
3581 if inv.stream_id.is_empty() || inv.origin_peer_id.is_empty() {
3582 return;
3583 }
3584 if inv.origin_peer_id == self.signaling.peer_id() {
3585 return;
3586 }
3587
3588 let key = Self::pubsub_key(&inv.origin_peer_id, &inv.stream_id, inv.seq);
3589 if !self
3590 .pubsub_seen_inventories
3591 .lock()
3592 .await
3593 .insert_if_new(key.clone())
3594 {
3595 return;
3596 }
3597 {
3598 let mut routes = self.pubsub_inventory_routes.write().await;
3599 routes
3600 .entry(key.clone())
3601 .or_insert_with(|| from_peer.to_string());
3602 }
3603
3604 let local_interested = self
3605 .pubsub_local_interests
3606 .read()
3607 .await
3608 .contains(&inv.stream_id);
3609 let downstream_peers = if self.routing.pubsub_forwarding {
3610 self.interested_pubsub_peers(&inv.stream_id, Some(from_peer))
3611 .await
3612 } else {
3613 Vec::new()
3614 };
3615 if local_interested || !downstream_peers.is_empty() {
3616 self.record_useful_bytes_received_from_peer(from_peer, wire_bytes as u64)
3617 .await;
3618 let want =
3619 create_pubsub_want(inv.stream_id.clone(), inv.seq, inv.origin_peer_id.clone());
3620 let _ = self.send_pubsub_want_upstream(&key, &want, None).await;
3621 }
3622
3623 if !self.routing.pubsub_forwarding
3624 || downstream_peers.is_empty()
3625 || !should_forward_htl(inv.htl)
3626 {
3627 return;
3628 }
3629 let _ = self
3630 .send_pubsub_inventory_to_peers(&inv, &downstream_peers)
3631 .await;
3632 }
3633
3634 async fn handle_pubsub_want_message(
3635 self: &Arc<Self>,
3636 from_peer: &str,
3637 want: PubsubWant,
3638 wire_bytes: usize,
3639 ) {
3640 if want.stream_id.is_empty() || want.origin_peer_id.is_empty() {
3641 return;
3642 }
3643 if want.origin_peer_id == from_peer {
3644 return;
3645 }
3646
3647 let key = Self::pubsub_key(&want.origin_peer_id, &want.stream_id, want.seq);
3648 let want_key = format!("{from_peer}:{key}");
3649 if !self.pubsub_seen_wants.lock().await.insert_if_new(want_key) {
3650 return;
3651 }
3652
3653 if let Some(frame) = self.cached_pubsub_frame(&key).await {
3654 self.record_useful_bytes_received_from_peer(from_peer, wire_bytes as u64)
3655 .await;
3656 let peers = vec![from_peer.to_string()];
3657 let _ = self.send_pubsub_frame_to_peers(&frame, &peers).await;
3658 return;
3659 }
3660
3661 let has_upstream_route = self.pubsub_inventory_routes.read().await.contains_key(&key);
3662 if !has_upstream_route {
3663 return;
3664 }
3665
3666 if self.remember_pubsub_want_peer(key.clone(), from_peer).await {
3667 self.record_useful_bytes_received_from_peer(from_peer, wire_bytes as u64)
3668 .await;
3669 }
3670 let _ = self
3671 .send_pubsub_want_upstream(&key, &want, Some(from_peer))
3672 .await;
3673 }
3674
3675 pub async fn handle_data_message(self: &Arc<Self>, from_peer: &str, data: &[u8]) {
3677 self.record_peer_wire_received(from_peer, data.len() as u64)
3678 .await;
3679 let parsed = match parse_message(data) {
3680 Some(m) => m,
3681 None => return,
3682 };
3683
3684 match parsed {
3685 DataMessage::Request(req) => {
3686 self.handle_request_message(from_peer, req).await;
3687 }
3688 DataMessage::Response(res) => {
3689 self.handle_response_message(from_peer, res).await;
3690 }
3691 DataMessage::QuoteRequest(req) => {
3692 self.handle_quote_request_message(from_peer, req).await;
3693 }
3694 DataMessage::QuoteResponse(res) => {
3695 self.handle_quote_response_message(from_peer, res).await;
3696 }
3697 DataMessage::PubsubInterest(interest) => {
3698 self.handle_pubsub_interest_message(from_peer, interest)
3699 .await;
3700 }
3701 DataMessage::PubsubFrame(frame) => {
3702 self.handle_pubsub_frame_message(from_peer, frame, data.len())
3703 .await;
3704 }
3705 DataMessage::PubsubInventory(inv) => {
3706 self.handle_pubsub_inventory_message(from_peer, inv, data.len())
3707 .await;
3708 }
3709 DataMessage::PubsubWant(want) => {
3710 self.handle_pubsub_want_message(from_peer, want, data.len())
3711 .await;
3712 }
3713 DataMessage::Payment(_)
3714 | DataMessage::PaymentAck(_)
3715 | DataMessage::Chunk(_)
3716 | DataMessage::PeerHints(_) => {}
3717 }
3718 }
3719}
3720
3721#[async_trait]
3722impl<S, R, F> BlobRoute for MeshStoreCore<S, R, F>
3723where
3724 S: Store + Send + Sync + 'static,
3725 R: SignalingTransport + Send + Sync + 'static,
3726 F: PeerLinkFactory + Send + Sync + 'static,
3727{
3728 async fn route(&self, request: BlobRequest) -> Result<BlobReply, StoreError> {
3729 if request.htl > MAX_HTL {
3730 return Err(StoreError::Other(format!(
3731 "Hashtree blob HTL {} exceeds the maximum of {MAX_HTL}",
3732 request.htl
3733 )));
3734 }
3735 if let Some(data) = self.local_store.get(&request.hash).await? {
3736 if data.len() > BLOB_MAX_BYTES {
3737 return Err(StoreError::Other(format!(
3738 "local store returned {} bytes, exceeding the {BLOB_MAX_BYTES}-byte limit",
3739 data.len()
3740 )));
3741 }
3742 if hashtree_core::sha256(&data) != request.hash {
3743 return Err(StoreError::Other(
3744 "local store returned corrupt content".to_string(),
3745 ));
3746 }
3747 return Ok(BlobReply::Data(data));
3748 }
3749
3750 if request.htl == 0 {
3751 return Ok(BlobReply::NoResult);
3752 }
3753
3754 let context = MeshReadContext {
3755 exclude_peer_id: None,
3756 request_htl: request.htl,
3757 };
3758 match self
3759 .request_from_mesh_with_context(&request.hash, &context)
3760 .await
3761 {
3762 RouteFetchOutcome::Hit(data) => {
3763 if data.len() > BLOB_MAX_BYTES {
3764 return Err(StoreError::Other(format!(
3765 "blob route returned {} bytes, exceeding the {BLOB_MAX_BYTES}-byte limit",
3766 data.len()
3767 )));
3768 }
3769 if hashtree_core::sha256(&data) != request.hash {
3770 return Err(StoreError::Other(
3771 "blob route returned corrupt content".to_string(),
3772 ));
3773 }
3774 Ok(BlobReply::Data(data))
3775 }
3776 RouteFetchOutcome::Miss => Ok(BlobReply::NoResult),
3777 RouteFetchOutcome::Timeout => Err(StoreError::Other(
3778 "blob retrieval deadline expired before the search completed".to_string(),
3779 )),
3780 RouteFetchOutcome::Failure => Err(StoreError::Other(
3781 "blob retrieval failed before the search completed".to_string(),
3782 )),
3783 }
3784 }
3785}
3786
3787#[async_trait]
3788impl<S, R, F> Store for MeshStoreCore<S, R, F>
3789where
3790 S: Store + Send + Sync + 'static,
3791 R: SignalingTransport + Send + Sync + 'static,
3792 F: PeerLinkFactory + Send + Sync + 'static,
3793{
3794 async fn put(&self, hash: Hash, data: Vec<u8>) -> Result<bool, StoreError> {
3795 self.local_store.put(hash, data).await
3796 }
3797
3798 async fn get(&self, hash: &Hash) -> Result<Option<Vec<u8>>, StoreError> {
3799 Ok(
3800 match self
3801 .route(BlobRequest {
3802 hash: *hash,
3803 htl: MAX_HTL,
3804 })
3805 .await?
3806 {
3807 BlobReply::Data(data) => Some(data),
3808 BlobReply::NoResult => None,
3809 },
3810 )
3811 }
3812
3813 async fn has(&self, hash: &Hash) -> Result<bool, StoreError> {
3814 self.local_store.has(hash).await
3815 }
3816
3817 async fn delete(&self, hash: &Hash) -> Result<bool, StoreError> {
3818 self.local_store.delete(hash).await
3819 }
3820}
3821
3822#[cfg(test)]
3823mod delivery_tests;
3824
3825#[cfg(test)]
3826mod tests;
3827
3828pub type SimMeshStore<S> =
3830 MeshStoreCore<S, crate::mock::MockRelayTransport, crate::mock::MockConnectionFactory>;