1use super::{
28 is_cid_supported, BitswapCommand, BitswapHandle, Cid, FetchItem, LOG_TARGET, MAX_WANTED_BLOCKS,
29};
30use crate::{
31 handle::BitswapError,
32 metrics::{
33 errors as metric_errors, outbound_events, outcomes as metric_outcomes, BitswapMetrics,
34 },
35};
36
37use async_trait::async_trait;
38use bytes::Bytes;
39use futures::{Stream, StreamExt};
40use litep2p::protocol::libp2p::bitswap::{
41 BitswapEvent, BitswapHandle as Litep2pBitswapHandle, BlockPresenceType, ResponseType, WantType,
42};
43use prometheus_endpoint::Registry;
44use rand::{seq::IteratorRandom, Rng};
45use sc_client_api::BlockBackend;
46use sc_network_common::role::Roles;
47use sc_network_sync::{SyncEvent, SyncEventStream};
48use slotmap::{new_key_type, SlotMap};
49use smallvec::SmallVec;
50use sp_core::H256;
51use sp_runtime::traits::Block as BlockT;
52use std::{
53 collections::{hash_map::Entry, HashMap, HashSet, VecDeque},
54 future::Future,
55 pin::Pin,
56 sync::Arc,
57 time::Duration,
58};
59use tokio::{
60 sync::{mpsc, OwnedSemaphorePermit, Semaphore},
61 time::Instant,
62};
63
64#[async_trait]
67trait BitswapTransport: Send {
68 async fn next_event(&mut self) -> Option<TransportEvent>;
69 async fn send_request(&self, peer: litep2p::PeerId, cids: Vec<(Cid, WantType)>);
70 async fn send_response(&self, peer: litep2p::PeerId, responses: Vec<ResponseType>);
71}
72
73enum TransportEvent {
74 Request { peer: litep2p::PeerId, cids: Vec<(Cid, WantType)> },
75 Response { peer: litep2p::PeerId, responses: Vec<TransportResponse> },
76}
77
78enum TransportResponse {
79 VerifiedBlock {
83 cid: Cid,
84 bytes: Bytes,
85 },
86 Presence {
87 cid: Cid,
88 presence: BlockPresenceType,
89 },
90}
91
92#[async_trait]
97impl BitswapTransport for Litep2pBitswapHandle {
98 async fn next_event(&mut self) -> Option<TransportEvent> {
99 StreamExt::next(self).await.map(|event| match event {
100 BitswapEvent::Request { peer, cids } => TransportEvent::Request { peer, cids },
101 BitswapEvent::Response { peer, responses } => TransportEvent::Response {
102 peer,
103 responses: responses
104 .into_iter()
105 .map(|response| match response {
106 ResponseType::Block { cid, block } => {
107 TransportResponse::VerifiedBlock { cid, bytes: block.into() }
108 },
109 ResponseType::Presence { cid, presence } => {
110 TransportResponse::Presence { cid, presence }
111 },
112 })
113 .collect(),
114 },
115 })
116 }
117
118 async fn send_request(&self, peer: litep2p::PeerId, cids: Vec<(Cid, WantType)>) {
119 let send = Litep2pBitswapHandle::send_request(self, peer, cids);
120 if tokio::time::timeout(TRANSPORT_SEND_TIMEOUT, send).await.is_err() {
121 log::warn!(
122 target: LOG_TARGET,
123 "litep2p command channel congested; dropped bitswap request to {peer:?}",
124 );
125 }
126 }
127
128 async fn send_response(&self, peer: litep2p::PeerId, responses: Vec<ResponseType>) {
129 let send = Litep2pBitswapHandle::send_response(self, peer, responses);
130 if tokio::time::timeout(TRANSPORT_SEND_TIMEOUT, send).await.is_err() {
131 log::warn!(
132 target: LOG_TARGET,
133 "litep2p command channel congested; dropped bitswap response to {peer:?}",
134 );
135 }
136 }
137}
138
139const MAX_LIVE_CIDS: usize = 1024;
140const MAX_CONCURRENT_INBOUND_LOOKUPS: usize = 8;
141const MAX_QUEUED_INBOUND_ENTRIES_PER_PEER: usize = MAX_LIVE_CIDS;
142const CMD_CHANNEL_CAPACITY: usize = 256;
143const TRANSPORT_SEND_TIMEOUT: Duration = Duration::from_secs(10);
146const PER_PEER_TIMEOUT: Duration = Duration::from_secs(5);
147const ROUND_RETRY_DELAY: Duration = Duration::from_secs(5);
148const SWEEP_INTERVAL: Duration = Duration::from_secs(1);
149
150new_key_type! { struct UserRequestId; }
151
152#[derive(Clone, Copy, Default)]
153enum CidRequestPhase {
154 #[default]
155 Ready,
156 Queued {
157 retry_at: Option<Instant>,
158 },
159 InFlight {
160 peer: litep2p::PeerId,
161 deadline: Instant,
162 },
163 RetryAt(Instant),
164}
165
166#[derive(Default)]
167struct CidRequestState {
168 tried_peers: HashSet<litep2p::PeerId>,
169 user_requests: SmallVec<[UserRequestId; 2]>,
170 phase: CidRequestPhase,
171}
172
173impl CidRequestState {
174 fn has_user_requests(&self) -> bool {
175 !self.user_requests.is_empty()
176 }
177
178 fn is_idle(&self) -> bool {
179 self.user_requests.is_empty() && !matches!(self.phase, CidRequestPhase::InFlight { .. })
180 }
181
182 fn is_queued(&self) -> bool {
183 matches!(self.phase, CidRequestPhase::Queued { .. })
184 }
185
186 fn queue(&mut self) -> bool {
189 let retry_at = match self.phase {
190 CidRequestPhase::Ready => None,
191 CidRequestPhase::RetryAt(at) => Some(at),
192 CidRequestPhase::Queued { .. } | CidRequestPhase::InFlight { .. } => return false,
193 };
194 self.phase = CidRequestPhase::Queued { retry_at };
195 true
196 }
197
198 fn dequeue(&mut self) -> bool {
201 let CidRequestPhase::Queued { retry_at } = self.phase else { return false };
202 self.phase = retry_at.map_or(CidRequestPhase::Ready, CidRequestPhase::RetryAt);
203 true
204 }
205
206 fn finish_peer(&mut self, peer: litep2p::PeerId) -> bool {
209 let CidRequestPhase::InFlight { peer: active_peer, .. } = self.phase else { return false };
210 if active_peer != peer {
211 return false;
212 }
213 self.phase = CidRequestPhase::Ready;
214 true
215 }
216
217 fn schedule_retry(&mut self, at: Instant) -> bool {
220 match &mut self.phase {
221 CidRequestPhase::Ready => self.phase = CidRequestPhase::RetryAt(at),
222 CidRequestPhase::Queued { retry_at: slot @ None } => *slot = Some(at),
223 CidRequestPhase::Queued { retry_at: Some(_) } |
224 CidRequestPhase::InFlight { .. } |
225 CidRequestPhase::RetryAt(_) => return false,
226 }
227 true
228 }
229
230 fn restart_round(&mut self, now: Instant) -> bool {
233 self.phase = match self.phase {
234 CidRequestPhase::RetryAt(at) if at <= now => CidRequestPhase::Ready,
235 CidRequestPhase::Queued { retry_at: Some(at) } if at <= now => {
236 CidRequestPhase::Queued { retry_at: None }
237 },
238 _ => return false,
239 };
240 self.tried_peers.clear();
241 true
242 }
243}
244
245struct RequestScheduler {
246 cid_states: HashMap<Cid, CidRequestState>,
247 pending: VecDeque<Cid>,
251 live_cids: usize,
253 queued_cids: usize,
257 max_live_cids: usize,
259}
260
261impl RequestScheduler {
262 fn new(max_live_cids: usize) -> Self {
263 Self {
264 cid_states: HashMap::new(),
265 pending: VecDeque::new(),
266 live_cids: 0,
267 queued_cids: 0,
268 max_live_cids,
269 }
270 }
271
272 fn contains(&self, cid: &Cid) -> bool {
273 self.cid_states.contains_key(cid)
274 }
275
276 fn add_user_request(&mut self, cid: Cid, user_request: UserRequestId) {
277 self.cid_states.entry(cid).or_default().user_requests.push(user_request);
278 }
279
280 fn remove_user_request(&mut self, cid: Cid, user_request: UserRequestId) {
283 if let Some(cid_state) = self.cid_states.get_mut(&cid) {
284 cid_state.user_requests.retain(|r| *r != user_request);
285 }
286 self.remove_if_idle(cid);
287 }
288
289 fn all_cids(&self) -> Vec<Cid> {
290 self.cid_states.keys().copied().collect()
291 }
292
293 fn take_user_requests_for_delivered_cid(
296 &mut self,
297 cid: Cid,
298 ) -> Option<SmallVec<[UserRequestId; 2]>> {
299 self.cid_states.remove(&cid).map(|cid_state| {
300 if matches!(cid_state.phase, CidRequestPhase::InFlight { .. }) {
301 self.live_cids -= 1;
302 }
303 if cid_state.is_queued() {
304 self.queued_cids -= 1;
305 }
306 cid_state.user_requests
307 })
308 }
309
310 fn has_window_capacity(&self) -> bool {
311 self.live_cids < self.max_live_cids
312 }
313
314 fn pop_pending(&mut self) -> Option<Cid> {
317 while let Some(cid) = self.pending.pop_front() {
318 if let Some(cid_state) = self.cid_states.get_mut(&cid) {
319 if cid_state.dequeue() {
320 self.queued_cids -= 1;
321 return Some(cid);
322 }
323 }
324 }
325 None
326 }
327
328 fn next_peer_to_request<R: Rng + ?Sized>(
331 &mut self,
332 cid: Cid,
333 connected_peers: &HashSet<litep2p::PeerId>,
334 now: Instant,
335 rng: &mut R,
336 ) -> Option<litep2p::PeerId> {
337 let has_window_capacity = self.has_window_capacity();
338 let cid_state = self.cid_states.get_mut(&cid)?;
339 if !cid_state.has_user_requests() ||
340 matches!(cid_state.phase, CidRequestPhase::InFlight { .. })
341 {
342 return None;
343 }
344
345 if !has_window_capacity {
346 if cid_state.queue() {
348 self.pending.push_back(cid);
349 self.queued_cids += 1;
350 }
351 return None;
352 }
353
354 let is_untried = |peer: &litep2p::PeerId| !cid_state.tried_peers.contains(peer);
355 let Some(peer) = connected_peers
356 .iter()
357 .filter(|peer| is_untried(peer))
358 .choose(&mut *rng)
359 .copied()
360 else {
361 if !connected_peers.is_empty() {
362 if cid_state.schedule_retry(now + ROUND_RETRY_DELAY) {
364 log::trace!(
365 target: LOG_TARGET,
366 "all peers tried for {cid}, scheduling new round",
367 );
368 }
369 }
370 return None;
371 };
372
373 if cid_state.is_queued() {
374 self.queued_cids -= 1;
375 }
376 self.live_cids += 1;
377 cid_state.phase = CidRequestPhase::InFlight { peer, deadline: now + PER_PEER_TIMEOUT };
378
379 Some(peer)
380 }
381
382 fn mark_peer_done_for_cid(&mut self, peer: litep2p::PeerId, cid: Cid) {
385 if let Some(cid_state) = self.cid_states.get_mut(&cid) {
386 if cid_state.finish_peer(peer) {
387 self.live_cids -= 1;
388 }
389 cid_state.tried_peers.insert(peer);
390 }
391 self.remove_if_idle(cid);
392 }
393
394 fn remove_in_flight_peer(&mut self, peer: litep2p::PeerId) -> Vec<Cid> {
397 let mut affected_cids = Vec::new();
398 for (cid, cid_state) in self.cid_states.iter_mut() {
399 if cid_state.finish_peer(peer) {
400 self.live_cids -= 1;
401 affected_cids.push(*cid);
402 }
403 }
404
405 self.remove_idle_and_filter_existing(affected_cids)
406 }
407
408 fn expire_peer_timeouts(&mut self, now: Instant) -> (Vec<Cid>, usize) {
411 let mut timed_out_cids = Vec::new();
412 for (cid, cid_state) in self.cid_states.iter_mut() {
413 if let CidRequestPhase::InFlight { peer, deadline } = cid_state.phase {
414 if deadline <= now {
415 cid_state.phase = CidRequestPhase::Ready;
416 cid_state.tried_peers.insert(peer);
417 timed_out_cids.push(*cid);
418 }
419 }
420 }
421 let timed_out_count = timed_out_cids.len();
422 self.live_cids -= timed_out_count;
423
424 (self.remove_idle_and_filter_existing(timed_out_cids), timed_out_count)
425 }
426
427 fn restart_exhausted_rounds(&mut self, now: Instant) -> Vec<Cid> {
430 let mut restarted_cids = Vec::new();
431 for (cid, cid_state) in self.cid_states.iter_mut() {
432 if cid_state.has_user_requests() && cid_state.restart_round(now) {
433 restarted_cids.push(*cid);
434 }
435 }
436 restarted_cids
437 }
438
439 fn clear(&mut self) {
440 self.cid_states.clear();
441 self.pending.clear();
442 self.live_cids = 0;
443 self.queued_cids = 0;
444 }
445
446 fn remove_idle_and_filter_existing(&mut self, cids: Vec<Cid>) -> Vec<Cid> {
449 for cid in &cids {
450 self.remove_if_idle(*cid);
451 }
452 cids.into_iter().filter(|cid| self.cid_states.contains_key(cid)).collect()
453 }
454
455 fn remove_if_idle(&mut self, cid: Cid) {
456 let Entry::Occupied(entry) = self.cid_states.entry(cid) else { return };
457 if !entry.get().is_idle() {
458 return;
459 }
460 let cid_state = entry.remove();
461 if cid_state.is_queued() {
462 self.queued_cids -= 1;
463 }
464 }
465}
466
467struct UserRequest {
468 cids_remaining: HashSet<Cid>,
469 sink: mpsc::Sender<FetchItem>,
470}
471
472type InboundLookupResult = (litep2p::PeerId, Vec<ResponseType>, OwnedSemaphorePermit);
476
477struct InboundLookupPool<B: BlockT> {
478 client: Arc<dyn BlockBackend<B> + Send + Sync>,
479 result_tx: mpsc::Sender<InboundLookupResult>,
480 semaphore: Arc<Semaphore>,
481 metrics: BitswapMetrics,
482}
483
484impl<B: BlockT> InboundLookupPool<B> {
485 fn new(
486 client: Arc<dyn BlockBackend<B> + Send + Sync>,
487 max_lookups: usize,
488 metrics: BitswapMetrics,
489 ) -> (Self, mpsc::Receiver<InboundLookupResult>) {
490 let (result_tx, result_rx) = mpsc::channel(max_lookups);
491 (
492 Self { client, result_tx, semaphore: Arc::new(Semaphore::new(max_lookups)), metrics },
493 result_rx,
494 )
495 }
496
497 fn try_acquire_worker(&self) -> Option<OwnedSemaphorePermit> {
500 self.semaphore.clone().try_acquire_owned().ok()
501 }
502
503 fn submit(
506 &self,
507 permit: OwnedSemaphorePermit,
508 peer: litep2p::PeerId,
509 cids: Vec<(Cid, WantType)>,
510 ) {
511 let client = self.client.clone();
512 let result_tx = self.result_tx.clone();
513 let metrics = self.metrics.clone();
514 tokio::task::spawn_blocking(move || {
515 let responses = serve_inbound(&*client, cids, &metrics);
516 let _ = result_tx.blocking_send((peer, responses, permit));
517 });
518 }
519}
520
521struct InboundQueue {
523 per_peer: HashMap<litep2p::PeerId, VecDeque<(Cid, WantType)>>,
524 rotation: VecDeque<litep2p::PeerId>,
525 max_entries_per_peer: usize,
526}
527
528impl InboundQueue {
529 fn new(max_entries_per_peer: usize) -> Self {
530 Self { per_peer: HashMap::new(), rotation: VecDeque::new(), max_entries_per_peer }
531 }
532
533 fn enqueue(&mut self, peer: litep2p::PeerId, mut entries: Vec<(Cid, WantType)>) -> usize {
536 if entries.is_empty() {
537 return 0;
538 }
539 let queue = self.per_peer.entry(peer).or_default();
540 let free_slots = self.max_entries_per_peer.saturating_sub(queue.len());
541 let dropped = entries.len().saturating_sub(free_slots);
542 entries.truncate(free_slots);
543 if queue.is_empty() && !entries.is_empty() {
545 self.rotation.push_back(peer);
546 }
547 queue.extend(entries);
548 if queue.is_empty() {
549 self.per_peer.remove(&peer);
550 }
551 dropped
552 }
553
554 fn remove_peer(&mut self, peer: &litep2p::PeerId) -> usize {
556 let removed = self.per_peer.remove(peer).map_or(0, |queue| queue.len());
557 if removed > 0 {
558 self.rotation.retain(|rotated| rotated != peer);
559 }
560 removed
561 }
562
563 fn next_batch(&mut self) -> Option<(litep2p::PeerId, Vec<(Cid, WantType)>)> {
566 while let Some(peer) = self.rotation.pop_front() {
567 let Some(queue) = self.per_peer.get_mut(&peer) else {
568 log::error!(target: LOG_TARGET, "stale peer in inbound queue rotation: {peer}");
569 continue;
570 };
571 if queue.is_empty() {
572 log::error!(target: LOG_TARGET, "empty peer queue in inbound rotation: {peer}");
573 self.per_peer.remove(&peer);
574 continue;
575 }
576
577 let batch: Vec<_> = queue.drain(..queue.len().min(MAX_WANTED_BLOCKS)).collect();
578 if queue.is_empty() {
579 self.per_peer.remove(&peer);
580 } else {
581 self.rotation.push_back(peer);
582 }
583 return Some((peer, batch));
584 }
585 None
586 }
587}
588
589pub(crate) struct BitswapService<B: BlockT> {
590 handle: Box<dyn BitswapTransport>,
591
592 cmd_rx: mpsc::Receiver<BitswapCommand>,
593 cmd_channel_closed: bool,
594 sync_event_stream: Pin<Box<dyn Stream<Item = SyncEvent> + Send>>,
595 inbound_lookup_pool: InboundLookupPool<B>,
597 inbound_lookup_rx: mpsc::Receiver<InboundLookupResult>,
598 inbound_queue: InboundQueue,
600
601 connected_peers: HashSet<litep2p::PeerId>,
602 scheduler: RequestScheduler,
604 user_requests: SlotMap<UserRequestId, UserRequest>,
606 metrics: BitswapMetrics,
607}
608
609pub fn start<B: BlockT, S>(
611 client: Arc<dyn BlockBackend<B> + Send + Sync>,
612 sync: &S,
613 litep2p_handle: Litep2pBitswapHandle,
614 metrics_registry: Option<&Registry>,
615) -> (Pin<Box<dyn Future<Output = ()> + Send>>, BitswapHandle)
616where
617 S: SyncEventStream + ?Sized,
618{
619 let (cmd_tx, cmd_rx) = mpsc::channel(CMD_CHANNEL_CAPACITY);
620 let metrics = BitswapMetrics::new(metrics_registry).unwrap_or_else(|err| {
621 log::debug!(target: LOG_TARGET, "failed to register bitswap metrics: {err}");
622 BitswapMetrics::default()
623 });
624 let (inbound_lookup_pool, inbound_lookup_rx) =
625 InboundLookupPool::new(client, MAX_CONCURRENT_INBOUND_LOOKUPS, metrics.clone());
626
627 let user_handle = BitswapHandle::new(cmd_tx);
628 let sync_event_stream = sync.event_stream("bitswap");
629
630 let service = BitswapService {
631 handle: Box::new(litep2p_handle),
632 cmd_rx,
633 cmd_channel_closed: false,
634 sync_event_stream,
635 inbound_lookup_pool,
636 inbound_lookup_rx,
637 inbound_queue: InboundQueue::new(MAX_QUEUED_INBOUND_ENTRIES_PER_PEER),
638 connected_peers: HashSet::new(),
639 scheduler: RequestScheduler::new(MAX_LIVE_CIDS),
640 user_requests: SlotMap::with_key(),
641 metrics,
642 };
643
644 let future = Box::pin(async move { service.run().await });
645
646 (future, user_handle)
647}
648
649impl<B: BlockT> BitswapService<B> {
650 async fn run(mut self) {
653 log::debug!(target: LOG_TARGET, "BitswapService starting");
654 let mut sweep_ticker = tokio::time::interval(SWEEP_INTERVAL);
655 sweep_ticker.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Delay);
656 sweep_ticker.tick().await;
657 self.update_metrics();
658
659 loop {
660 tokio::select! {
661 event = self.handle.next_event() => match event {
662 Some(TransportEvent::Request { peer, cids }) =>
663 self.on_inbound_request(peer, cids),
664 Some(TransportEvent::Response { peer, responses }) =>
665 self.on_inbound_response(peer, responses).await,
666 None => {
667 log::debug!(target: LOG_TARGET, "litep2p bitswap stream ended; shutting down");
668 self.shutdown_user_requests();
669 return;
670 },
671 },
672
673 cmd = self.cmd_rx.recv(), if !self.cmd_channel_closed => match cmd {
674 Some(BitswapCommand::RequestStream { cids, sink }) =>
675 self.on_request_stream(cids, sink).await,
676 None => {
677 log::debug!(
678 target: LOG_TARGET,
679 "all bitswap handles dropped; serving inbound requests only",
680 );
681 self.cmd_channel_closed = true;
682 },
683 },
684
685 sync_event = self.sync_event_stream.next() => match sync_event {
686 Some(SyncEvent::PeerConnected { peer_id, roles }) =>
687 self.on_peer_connected(peer_id.into(), roles).await,
688 Some(SyncEvent::PeerDisconnected(peer)) => self.on_peer_disconnected(peer.into()).await,
689 None => {
690 log::debug!(target: LOG_TARGET, "sync event stream ended; shutting down");
691 self.shutdown_user_requests();
692 return;
693 },
694 },
695
696 Some((peer, responses, permit)) = self.inbound_lookup_rx.recv() => {
697 self.handle.send_response(peer, responses).await;
698 drop(permit);
699 self.dispatch_inbound_lookups();
700 },
701
702 _ = sweep_ticker.tick() => {
703 self.on_sweep().await;
704 },
705 }
706 self.update_metrics();
707 }
708 }
709
710 async fn on_request_stream(&mut self, cids: HashSet<Cid>, sink: mpsc::Sender<FetchItem>) {
712 let user_request_id =
713 self.user_requests.insert(UserRequest { cids_remaining: cids.clone(), sink });
714
715 for cid in &cids {
716 self.scheduler.add_user_request(*cid, user_request_id);
717 }
718
719 self.top_up_in_flight(cids).await;
720 }
721
722 async fn top_up_in_flight(&mut self, cids: impl IntoIterator<Item = Cid>) {
725 let now = Instant::now();
726 let wants_by_peer = {
727 let mut rng = rand::thread_rng();
728 let mut wants_by_peer: HashMap<litep2p::PeerId, Vec<(Cid, WantType)>> = HashMap::new();
729 for cid in cids {
730 if let Some(peer) =
731 self.scheduler.next_peer_to_request(cid, &self.connected_peers, now, &mut rng)
732 {
733 log::trace!(target: LOG_TARGET, "WANT-BLOCK {cid} -> {peer:?}");
734 wants_by_peer.entry(peer).or_default().push((cid, WantType::Block));
735 }
736 }
737
738 while self.scheduler.has_window_capacity() {
740 let Some(cid) = self.scheduler.pop_pending() else { break };
741 if let Some(peer) =
742 self.scheduler.next_peer_to_request(cid, &self.connected_peers, now, &mut rng)
743 {
744 log::trace!(target: LOG_TARGET, "WANT-BLOCK {cid} -> {peer:?} (promoted)");
745 wants_by_peer.entry(peer).or_default().push((cid, WantType::Block));
746 }
747 }
748 wants_by_peer
749 };
750
751 for (peer, wants) in wants_by_peer {
752 self.metrics.record_outbound(outbound_events::REQUESTED, wants.len());
753 for chunk in wants.chunks(MAX_WANTED_BLOCKS) {
754 self.handle.send_request(peer, chunk.to_vec()).await;
755 }
756 }
757 }
758
759 async fn on_inbound_response(
762 &mut self,
763 peer: litep2p::PeerId,
764 responses: Vec<TransportResponse>,
765 ) {
766 let mut cids_to_top_up: HashSet<Cid> = HashSet::new();
767
768 for response in responses {
769 match response {
770 TransportResponse::VerifiedBlock { cid, bytes } => {
771 self.scheduler.mark_peer_done_for_cid(peer, cid);
773
774 if self.scheduler.contains(&cid) {
775 self.deliver_block(cid, bytes);
776 } else {
777 log::debug!(
778 target: LOG_TARGET,
779 "{peer:?} sent unsolicited or unwanted block for {cid}",
780 );
781 }
782 },
783 TransportResponse::Presence { cid, presence } => match presence {
784 BlockPresenceType::DontHave => {
785 log::trace!(target: LOG_TARGET, "{peer:?} DONT_HAVE {cid}");
786 self.scheduler.mark_peer_done_for_cid(peer, cid);
787 cids_to_top_up.insert(cid);
788 },
789 BlockPresenceType::Have => {
790 log::debug!(
791 target: LOG_TARGET,
792 "{peer:?} sent unsolicited HAVE for {cid}; only WANT-BLOCK is issued",
793 );
794 },
795 },
796 }
797 }
798
799 self.top_up_in_flight(cids_to_top_up).await;
800 }
801
802 fn deliver_block(&mut self, cid: Cid, bytes: Bytes) {
805 let Some(user_request_ids) = self.scheduler.take_user_requests_for_delivered_cid(cid)
806 else {
807 return;
808 };
809 self.metrics.record_outbound(outbound_events::DELIVERED, 1);
810
811 for user_request_id in user_request_ids {
812 let Some(user_request) = self.user_requests.get_mut(user_request_id) else { continue };
813 if !user_request.cids_remaining.remove(&cid) {
814 continue;
815 }
816 if user_request.sink.try_send(Ok((cid, bytes.clone()))).is_err() {
817 log::trace!(target: LOG_TARGET, "user request sink full/closed for {cid}");
818 self.drop_user_request(user_request_id);
819 continue;
820 }
821 if user_request.cids_remaining.is_empty() {
822 self.drop_user_request(user_request_id);
823 }
824 }
825 }
826
827 fn drop_user_request(&mut self, id: UserRequestId) {
830 let Some(user_request) = self.user_requests.remove(id) else { return };
831
832 for cid in &user_request.cids_remaining {
833 self.scheduler.remove_user_request(*cid, id);
834 }
835 }
836
837 async fn on_peer_connected(&mut self, peer: litep2p::PeerId, roles: Roles) {
840 if roles.is_light() {
841 return;
842 }
843 self.connected_peers.insert(peer);
844 let cids = self.scheduler.all_cids();
845 self.top_up_in_flight(cids).await;
846 }
847
848 async fn on_peer_disconnected(&mut self, peer: litep2p::PeerId) {
853 self.connected_peers.remove(&peer);
854 let dropped = self.inbound_queue.remove_peer(&peer);
855 if dropped > 0 {
856 log::trace!(
857 target: LOG_TARGET,
858 "dropped {dropped} queued inbound entries of disconnected {peer:?}",
859 );
860 }
861 let cids_to_top_up = self.scheduler.remove_in_flight_peer(peer);
862 self.top_up_in_flight(cids_to_top_up).await;
863 }
864
865 async fn on_sweep(&mut self) {
868 let abandoned_user_requests: Vec<UserRequestId> = self
869 .user_requests
870 .iter()
871 .filter_map(|(id, user_request)| user_request.sink.is_closed().then_some(id))
872 .collect();
873 self.metrics
874 .record_outbound(outbound_events::ABANDONED, abandoned_user_requests.len());
875 for id in abandoned_user_requests {
876 log::trace!(target: LOG_TARGET, "dropping abandoned user request {id:?}");
877 self.drop_user_request(id);
878 }
879
880 let now = Instant::now();
881 let (mut cids_to_top_up, timed_out_count) = self.scheduler.expire_peer_timeouts(now);
882 self.metrics.record_outbound(outbound_events::TIMED_OUT, timed_out_count);
883 let restarted_cids = self.scheduler.restart_exhausted_rounds(now);
884 self.metrics
885 .record_outbound(outbound_events::ROUND_RESTARTED, restarted_cids.len());
886 cids_to_top_up.extend(restarted_cids);
887 self.top_up_in_flight(cids_to_top_up).await;
888
889 self.dispatch_inbound_lookups();
891 }
892
893 fn on_inbound_request(&mut self, peer: litep2p::PeerId, cids: Vec<(Cid, WantType)>) {
896 let dropped = self.inbound_queue.enqueue(peer, cids);
897 if dropped > 0 {
898 self.metrics.record_inbound_queue_overflow(dropped);
899 log::debug!(
900 target: LOG_TARGET,
901 "inbound queue for {peer:?} full; dropped {dropped} wantlist entries",
902 );
903 }
904 self.dispatch_inbound_lookups();
905 }
906
907 fn dispatch_inbound_lookups(&mut self) {
910 while let Some(permit) = self.inbound_lookup_pool.try_acquire_worker() {
911 let Some((peer, batch)) = self.inbound_queue.next_batch() else { break };
912 self.inbound_lookup_pool.submit(permit, peer, batch);
913 }
914 }
915
916 fn update_metrics(&self) {
917 self.metrics.set_state(
918 self.scheduler.live_cids,
919 self.scheduler.queued_cids,
920 self.user_requests.len(),
921 );
922 }
923
924 fn shutdown_user_requests(&mut self) {
927 for (_, user_request) in self.user_requests.drain() {
928 let _ = user_request.sink.try_send(Err(BitswapError::ServiceClosed));
929 }
930 self.scheduler.clear();
931 self.update_metrics();
932 }
933}
934
935fn serve_inbound<B: BlockT>(
938 client: &(dyn BlockBackend<B> + Send + Sync),
939 cids: Vec<(Cid, WantType)>,
940 metrics: &BitswapMetrics,
941) -> Vec<ResponseType> {
942 let started = std::time::Instant::now();
943 let mut responses = Vec::with_capacity(cids.len());
944 for (cid, want_type) in cids {
945 let response = {
946 if !is_cid_supported(&cid) {
947 metrics.record_entry(metric_outcomes::UNSUPPORTED_CID);
948 responses
949 .push(ResponseType::Presence { cid, presence: BlockPresenceType::DontHave });
950 continue;
951 }
952 let hash = H256::from_slice(&cid.hash().digest()[0..32]);
954 match want_type {
955 WantType::Have => match client.has_indexed_transaction(hash) {
958 Ok(true) => ResponseType::Presence { cid, presence: BlockPresenceType::Have },
959 Ok(false) => {
960 ResponseType::Presence { cid, presence: BlockPresenceType::DontHave }
961 },
962 Err(e) => {
963 metrics.record_error(metric_errors::CLIENT);
964 log::error!(
965 target: LOG_TARGET,
966 "has_indexed_transaction({hash}) failed: {e}",
967 );
968 ResponseType::Presence { cid, presence: BlockPresenceType::DontHave }
969 },
970 },
971 WantType::Block => match client.indexed_transaction(hash) {
972 Ok(Some(transaction)) => ResponseType::Block { cid, block: transaction },
973 Ok(None) => {
974 ResponseType::Presence { cid, presence: BlockPresenceType::DontHave }
975 },
976 Err(e) => {
977 metrics.record_error(metric_errors::CLIENT);
978 log::error!(target: LOG_TARGET, "indexed_transaction({hash}) failed: {e}");
979 ResponseType::Presence { cid, presence: BlockPresenceType::DontHave }
980 },
981 },
982 }
983 };
984 metrics.record_response(&response);
985 responses.push(response);
986 }
987 metrics.record_response_bytes(&responses);
988 metrics.record_duration(started.elapsed());
989 responses
990}
991
992#[cfg(test)]
993mod tests;