1use crate::constructor::DummyRecordStore;
4use crate::protocols::request_response::handlers::cached_piece_by_index::{
5 CachedPieceByIndexRequest, CachedPieceByIndexResponse, PieceResult,
6};
7use crate::protocols::request_response::handlers::piece_by_index::{
8 PieceByIndexRequest, PieceByIndexResponse,
9};
10use crate::utils::multihash::ToMultihash;
11use crate::{Multihash, Node};
12use ab_core_primitives::pieces::{Piece, PieceIndex};
13use async_lock::{Semaphore, SemaphoreGuard};
14use async_trait::async_trait;
15use futures::channel::mpsc;
16use futures::future::FusedFuture;
17use futures::stream::FuturesUnordered;
18use futures::task::noop_waker_ref;
19use futures::{FutureExt, Stream, StreamExt, stream};
20use libp2p::kad::{Behaviour as Kademlia, KBucketKey, RecordKey};
21use libp2p::swarm::NetworkBehaviour;
22use libp2p::{Multiaddr, PeerId};
23use rand::prelude::*;
24use std::any::type_name;
25use std::collections::{HashMap, HashSet};
26use std::fmt;
27use std::future::ready;
28use std::pin::Pin;
29use std::sync::Arc as StdArc;
30use std::task::{Context, Poll};
31use tokio_stream::StreamMap;
32use tracing::{Instrument, debug, trace, warn};
33use triomphe::Arc;
34
35#[async_trait]
37pub trait PieceValidator: Sync + Send {
38 async fn validate_piece(
40 &self,
41 source_peer_id: PeerId,
42 piece_index: PieceIndex,
43 piece: Piece,
44 ) -> Option<Piece>;
45}
46
47#[derive(Debug, Clone, Copy)]
49pub struct NoPieceValidator;
50
51#[async_trait]
52impl PieceValidator for NoPieceValidator {
53 async fn validate_piece(&self, _: PeerId, _: PieceIndex, piece: Piece) -> Option<Piece> {
54 Some(piece)
55 }
56}
57
58#[derive(Clone)]
61pub struct PieceProvider<PV> {
62 node: Node,
63 piece_validator: PV,
64 piece_downloading_semaphore: Arc<Semaphore>,
65}
66
67impl<PV> fmt::Debug for PieceProvider<PV> {
68 #[inline]
69 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
70 f.debug_struct(&format!("PieceProvider<{}>", type_name::<PV>()))
71 .finish_non_exhaustive()
72 }
73}
74
75impl<PV> PieceProvider<PV>
76where
77 PV: PieceValidator,
78{
79 pub fn new(
81 node: Node,
82 piece_validator: PV,
83 piece_downloading_semaphore: Arc<Semaphore>,
84 ) -> Self {
85 Self {
86 node,
87 piece_validator,
88 piece_downloading_semaphore,
89 }
90 }
91
92 pub fn get_from_cache<'a, PieceIndices>(
96 &'a self,
97 piece_indices: PieceIndices,
98 ) -> impl Future<Output = impl Stream<Item = (PieceIndex, Option<Piece>)> + Unpin + 'a>
99 where
100 PieceIndices: IntoIterator<Item = PieceIndex> + 'a,
101 {
102 let download_id = rand::random::<u64>();
103 let (tx, mut rx) = mpsc::unbounded();
104 let fut = async move {
105 let not_downloaded_pieces = download_cached_pieces(
106 piece_indices,
107 &self.node,
108 &self.piece_validator,
109 &tx,
110 &self.piece_downloading_semaphore,
111 )
112 .await;
113
114 if not_downloaded_pieces.is_empty() {
115 debug!("Done");
116 return;
117 }
118
119 for piece_index in not_downloaded_pieces {
120 tx.unbounded_send((piece_index, None))
121 .expect("This future isn't polled after receiver is dropped; qed");
122 }
123
124 debug!("Done #2");
125 };
126
127 let mut fut = Box::pin(fut.instrument(tracing::info_span!("", %download_id)).fuse());
128
129 ready(stream::poll_fn(move |cx| {
131 if !fut.is_terminated() {
132 let _: Poll<()> = fut.poll_unpin(cx);
134 }
135
136 if let Poll::Ready(maybe_result) = rx.poll_next_unpin(cx) {
137 return Poll::Ready(maybe_result);
138 }
139
140 Poll::Pending
142 }))
143 }
144
145 pub async fn get_piece_from_cache(&self, piece_index: PieceIndex) -> Option<Piece> {
147 let key = RecordKey::from(piece_index.to_multihash());
148
149 let request_batch = self.node.get_requests_batch_handle().await;
150 let mut get_providers_stream = request_batch
151 .get_providers(key.clone())
152 .await
153 .inspect_err(|err| warn!(%piece_index,?key, ?err, "get_providers returned an error"))
154 .ok()?;
155
156 let key = hex::encode(&key);
157 while let Some(provider_id) = get_providers_stream.next().await {
158 trace!(%piece_index, key, %provider_id, "get_providers returned an item");
159
160 let Ok(PieceByIndexResponse {
161 piece,
162 cached_pieces: _,
163 }) = request_batch
164 .send_generic_request(
165 provider_id,
166 Vec::new(),
167 PieceByIndexRequest {
168 piece_index,
169 cached_pieces: StdArc::default(),
170 },
171 )
172 .await
173 .inspect_err(
174 |error| debug!(%piece_index, key, %provider_id, ?error, "Piece request failed"),
175 )
176 else {
177 continue;
178 };
179
180 if let Some(piece) = piece {
181 trace!(%piece_index, key, %provider_id, "Piece request succeeded");
182
183 return self
184 .piece_validator
185 .validate_piece(provider_id, piece_index, piece)
186 .await;
187 }
188
189 debug!(%piece_index, key, %provider_id, "Piece request returned empty piece");
190 }
191
192 None
193 }
194
195 pub async fn get_piece_from_peer(
197 &self,
198 peer_id: PeerId,
199 piece_index: PieceIndex,
200 ) -> Option<Piece> {
201 let PieceByIndexResponse {
203 piece,
204 cached_pieces: _,
205 } = self
206 .node
207 .send_generic_request(
208 peer_id,
209 Vec::new(),
210 PieceByIndexRequest {
211 piece_index,
212 cached_pieces: StdArc::default(),
213 },
214 )
215 .await
216 .inspect_err(|error| debug!(%peer_id, %piece_index, ?error, "Piece request failed"))
217 .ok()?;
218
219 if let Some(piece) = piece {
220 trace!(%peer_id, %piece_index, "Piece request succeeded");
221
222 return self
223 .piece_validator
224 .validate_piece(peer_id, piece_index, piece)
225 .await;
226 }
227
228 debug!(%peer_id, %piece_index, "Piece request returned empty piece");
229
230 None
231 }
232
233 pub async fn get_piece_from_archival_storage(
236 &self,
237 piece_index: PieceIndex,
238 max_random_walking_rounds: usize,
239 ) -> Option<Piece> {
240 trace!(%piece_index, "Getting piece from archival storage..");
242
243 let connected_servers = {
244 let connected_servers = match self.node.connected_servers().await {
245 Ok(connected_servers) => connected_servers,
246 Err(err) => {
247 debug!(%piece_index, ?err, "Cannot get connected peers (DSN L1 lookup)");
248
249 Vec::new()
250 }
251 };
252
253 HashSet::<PeerId>::from_iter(connected_servers)
254 };
255
256 if connected_servers.is_empty() {
257 debug!(%piece_index, "Cannot acquire piece from no connected peers (DSN L1 lookup)");
258 } else {
259 for peer_id in connected_servers {
260 let maybe_piece = self.get_piece_from_peer(peer_id, piece_index).await;
261
262 if maybe_piece.is_some() {
263 trace!(%piece_index, %peer_id, "DSN L1 lookup from connected peers succeeded");
264
265 return maybe_piece;
266 }
267 }
268 }
269
270 trace!(%piece_index, "Getting piece from DSN L1 using random walk.");
271 let random_walk_result = self
272 .get_piece_by_random_walking(piece_index, max_random_walking_rounds)
273 .await;
274
275 if random_walk_result.is_some() {
276 trace!(%piece_index, "DSN L1 lookup via random walk succeeded");
277
278 return random_walk_result;
279 }
280
281 debug!(
282 %piece_index,
283 %max_random_walking_rounds,
284 "Cannot acquire piece from DSN L1: random walk failed"
285 );
286
287 None
288 }
289
290 async fn get_piece_by_random_walking(
292 &self,
293 piece_index: PieceIndex,
294 walking_rounds: usize,
295 ) -> Option<Piece> {
296 for round in 0..walking_rounds {
297 debug!(%piece_index, round, "Random walk round");
298
299 let result = self
300 .get_piece_by_random_walking_from_single_round(piece_index, round)
301 .await;
302
303 if result.is_some() {
304 return result;
305 }
306 }
307
308 debug!(%piece_index, "Random walking piece retrieval failed.");
309
310 None
311 }
312
313 async fn get_piece_by_random_walking_from_single_round(
315 &self,
316 piece_index: PieceIndex,
317 round: usize,
318 ) -> Option<Piece> {
319 trace!(%piece_index, "get_piece_by_random_walking round");
321
322 let key = PeerId::random();
324
325 let request_batch = self.node.get_requests_batch_handle().await;
326 let mut get_closest_peers_stream = request_batch
327 .get_closest_peers(key.into())
328 .await
329 .inspect_err(|err| warn!(%piece_index, ?key, ?err, %round, "get_closest_peers returned an error"))
330 .ok()?;
331
332 while let Some(peer_id) = get_closest_peers_stream.next().await {
333 trace!(%piece_index, %peer_id, %round, "get_closest_peers returned an item");
334
335 let Ok(PieceByIndexResponse {
336 piece,
337 cached_pieces: _,
338 }) = request_batch
339 .send_generic_request(
340 peer_id,
341 Vec::new(),
342 PieceByIndexRequest {
343 piece_index,
344 cached_pieces: StdArc::default(),
345 },
346 )
347 .await
348 .inspect_err(
349 |error| debug!(%peer_id, %piece_index, ?key, %round, ?error, "Piece request failed."),
350 )
351 else {
352 continue;
353 };
354
355 if let Some(piece) = piece {
356 trace!(%peer_id, %piece_index, ?key, %round, "Piece request succeeded.");
357
358 return self
359 .piece_validator
360 .validate_piece(peer_id, piece_index, piece)
361 .await;
362 }
363
364 debug!(%peer_id, %piece_index, ?key, %round, "Piece request returned empty piece.");
365 }
366
367 None
368 }
369}
370
371struct KademliaWrapper {
373 local_peer_id: PeerId,
374 kademlia: Kademlia<DummyRecordStore>,
375}
376
377impl KademliaWrapper {
378 fn new(local_peer_id: PeerId) -> Self {
379 Self {
380 local_peer_id,
381 kademlia: Kademlia::new(local_peer_id, DummyRecordStore),
382 }
383 }
384
385 fn add_peer(&mut self, peer_id: &PeerId, addresses: Vec<Multiaddr>) {
386 for address in addresses {
387 self.kademlia.add_address(peer_id, address);
388 }
389 while self
390 .kademlia
391 .poll(&mut Context::from_waker(noop_waker_ref()))
392 .is_ready()
393 {
394 }
396 }
397
398 fn closest_peers(
400 &mut self,
401 key: &KBucketKey<Multihash>,
402 ) -> impl Iterator<Item = (PeerId, Vec<Multiaddr>)> + 'static {
403 let mut closest_peers = self
404 .kademlia
405 .find_closest_local_peers(key, &self.local_peer_id)
406 .map(|peer| {
407 (
408 KBucketKey::from(peer.node_id).distance(key),
409 peer.node_id,
410 peer.multiaddrs,
411 )
412 })
413 .collect::<Vec<_>>();
414
415 closest_peers.sort_unstable_by_key(|a| a.0);
416 closest_peers
417 .into_iter()
418 .map(|(_distance, peer_id, addresses)| (peer_id, addresses))
419 }
420}
421
422async fn download_cached_pieces<PV, PieceIndices>(
425 piece_indices: PieceIndices,
426 node: &Node,
427 piece_validator: &PV,
428 results: &mpsc::UnboundedSender<(PieceIndex, Option<Piece>)>,
429 semaphore: &Semaphore,
430) -> impl ExactSizeIterator<Item = PieceIndex>
431where
432 PV: PieceValidator,
433 PieceIndices: IntoIterator<Item = PieceIndex>,
434{
435 let mut pieces_to_download = piece_indices
441 .into_iter()
442 .map(|piece_index| async move {
443 let mut kademlia = KademliaWrapper::new(node.id());
444 let key = piece_index.to_multihash();
445
446 let local_closest_peers = node
447 .get_closest_local_peers(key, None)
448 .await
449 .unwrap_or_default();
450
451 for (peer_id, addresses) in local_closest_peers {
453 kademlia.add_peer(&peer_id, addresses);
454 }
455
456 (piece_index, kademlia)
457 })
458 .collect::<FuturesUnordered<_>>()
459 .collect::<HashMap<_, _>>()
460 .await;
461
462 let num_pieces = pieces_to_download.len();
463 debug!(%num_pieces, "Starting");
464
465 let mut checked_peers = HashSet::new();
466
467 let Ok(connected_servers) = node.connected_servers().await else {
468 trace!("Connected servers error");
469 return pieces_to_download.into_keys();
470 };
471
472 let num_connected_servers = connected_servers.len();
473 debug!(
474 %num_connected_servers,
475 %num_pieces,
476 "Starting downloading"
477 );
478
479 let mut downloading_stream = connected_servers
481 .into_iter()
482 .take(num_pieces)
483 .enumerate()
484 .map(|(peer_index, peer_id)| {
485 checked_peers.insert(peer_id);
486
487 let step = num_pieces / num_connected_servers.min(num_pieces);
489
490 let mut check_cached_pieces = pieces_to_download
493 .keys()
494 .cycle()
495 .skip(step * peer_index)
496 .take(num_pieces.min(CachedPieceByIndexRequest::RECOMMENDED_LIMIT + 1))
498 .copied()
499 .collect::<Vec<_>>();
500 let piece_index = check_cached_pieces.swap_remove(0);
502
503 trace!(%peer_id, %piece_index, "Downloading piece from initially connected peer");
504
505 let permit = semaphore.try_acquire();
506
507 let fut = async move {
508 let permit = match permit {
509 Some(permit) => permit,
510 None => semaphore.acquire().await,
511 };
512
513 download_cached_piece_from_peer(
514 node,
515 piece_validator,
516 peer_id,
517 Vec::new(),
518 StdArc::new(check_cached_pieces),
519 piece_index,
520 HashSet::new(),
521 HashSet::new(),
522 permit,
523 )
524 .await
525 };
526
527 (piece_index, Box::pin(fut.into_stream()) as _)
528 })
529 .collect::<StreamMap<_, _>>();
530
531 loop {
532 let mut additional_pieces_to_download =
534 (num_pieces / 2).saturating_sub(downloading_stream.len());
535 if additional_pieces_to_download > 0 {
536 trace!(
537 %additional_pieces_to_download,
538 num_pieces,
539 currently_downloading = %downloading_stream.len(),
540 "Downloading additional pieces from closest peers"
541 );
542 'outer: for peer_id in node
544 .connected_servers()
545 .await
546 .unwrap_or_default()
547 .into_iter()
548 .filter(|peer_id| checked_peers.insert(*peer_id))
549 .take(additional_pieces_to_download)
550 {
551 let permit = if downloading_stream.is_empty() {
552 semaphore.acquire().await
553 } else if let Some(permit) = semaphore.try_acquire() {
554 permit
555 } else {
556 break;
557 };
558
559 for &piece_index in pieces_to_download.keys() {
560 if downloading_stream.contains_key(&piece_index) {
561 continue;
562 }
563
564 trace!(%peer_id, %piece_index, "Downloading piece from newly connected peer");
565
566 let check_cached_pieces = sample_cached_piece_indices(
567 pieces_to_download.keys(),
568 &HashSet::new(),
569 &HashSet::new(),
570 piece_index,
571 );
572 let fut = download_cached_piece_from_peer(
573 node,
574 piece_validator,
575 peer_id,
576 Vec::new(),
577 StdArc::new(check_cached_pieces),
578 piece_index,
579 HashSet::new(),
580 HashSet::new(),
581 permit,
582 );
583
584 downloading_stream.insert(piece_index, Box::pin(fut.into_stream()) as _);
585 additional_pieces_to_download -= 1;
586
587 continue 'outer;
588 }
589
590 break;
591 }
592
593 let pieces_indices_to_download = pieces_to_download.keys().copied().collect::<Vec<_>>();
597 for piece_index in pieces_indices_to_download {
598 if additional_pieces_to_download == 0 {
599 break;
600 }
601 if downloading_stream.contains_key(&piece_index) {
602 continue;
603 }
604 let permit = if downloading_stream.is_empty() {
605 semaphore.acquire().await
606 } else if let Some(permit) = semaphore.try_acquire() {
607 permit
608 } else {
609 break;
610 };
611
612 let kbucket_key = KBucketKey::from(piece_index.to_multihash());
613 let closest_peers_to_check = pieces_to_download
614 .get_mut(&piece_index)
615 .expect("Entries are not removed here; qed")
616 .closest_peers(&kbucket_key);
617 for (peer_id, addresses) in closest_peers_to_check {
618 if !checked_peers.insert(peer_id) {
619 continue;
620 }
621
622 trace!(%peer_id, %piece_index, "Downloading piece from closest peer");
623
624 let check_cached_pieces = sample_cached_piece_indices(
625 pieces_to_download.keys(),
626 &HashSet::new(),
627 &HashSet::new(),
628 piece_index,
629 );
630 let fut = download_cached_piece_from_peer(
631 node,
632 piece_validator,
633 peer_id,
634 addresses,
635 StdArc::new(check_cached_pieces),
636 piece_index,
637 HashSet::new(),
638 HashSet::new(),
639 permit,
640 );
641
642 downloading_stream.insert(piece_index, Box::pin(fut.into_stream()) as _);
643 additional_pieces_to_download -= 1;
644 break;
645 }
646 }
647
648 trace!(
649 pieces_left = %additional_pieces_to_download,
650 "Initiated downloading additional pieces from closest peers"
651 );
652 }
653
654 let Some((piece_index, result)) = downloading_stream.next().await else {
655 if !pieces_to_download.is_empty() {
656 debug!(
657 %num_pieces,
658 to_download = %pieces_to_download.len(),
659 "Finished downloading early"
660 );
661 break;
663 }
664 break;
665 };
666 process_downloading_result(
667 piece_index,
668 result,
669 &mut pieces_to_download,
670 &mut downloading_stream,
671 node,
672 piece_validator,
673 results,
674 );
675
676 if pieces_to_download.is_empty() {
677 break;
678 }
679 }
680
681 pieces_to_download.into_keys()
682}
683
684fn process_downloading_result<'a, 'b, PV>(
685 piece_index: PieceIndex,
686 result: DownloadedPieceFromPeer<'a>,
687 pieces_to_download: &'b mut HashMap<PieceIndex, KademliaWrapper>,
688 downloading_stream: &'b mut StreamMap<
689 PieceIndex,
690 Pin<Box<dyn Stream<Item = DownloadedPieceFromPeer<'a>> + Send + 'a>>,
691 >,
692 node: &'a Node,
693 piece_validator: &'a PV,
694 results: &'a mpsc::UnboundedSender<(PieceIndex, Option<Piece>)>,
695) where
696 PV: PieceValidator,
697{
698 let DownloadedPieceFromPeer {
699 peer_id,
700 result,
701 mut cached_pieces,
702 not_cached_pieces,
703 permit,
704 } = result;
705 trace!(%piece_index, %peer_id, result = %result.is_some(), "Piece response");
706
707 let Some(result) = result else {
708 return;
710 };
711
712 match result {
713 PieceResult::Piece(piece) => {
714 trace!(%piece_index, %peer_id, "Got piece");
715
716 pieces_to_download.remove(&piece_index);
718
719 results
720 .unbounded_send((piece_index, Some(piece)))
721 .expect("This future isn't polled after receiver is dropped; qed");
722
723 if pieces_to_download.is_empty() {
724 return;
725 }
726
727 cached_pieces.remove(&piece_index);
728 }
729 PieceResult::ClosestPeers(closest_peers) => {
730 trace!(%piece_index, %peer_id, "Got closest peers");
731
732 if let Some(kademlia) = pieces_to_download.get_mut(&piece_index) {
734 for (peer_id, addresses) in Vec::from(closest_peers) {
735 kademlia.add_peer(&peer_id, addresses);
736 }
737 }
738
739 if cached_pieces.remove(&piece_index) {
741 return;
742 }
743 }
744 }
745
746 let mut maybe_piece_index_to_download_next = None;
747 cached_pieces.retain(|piece_index| {
749 if !pieces_to_download.contains_key(piece_index) {
751 return false;
752 }
753
754 if maybe_piece_index_to_download_next.is_none()
756 && !downloading_stream.contains_key(piece_index)
757 {
758 maybe_piece_index_to_download_next.replace(*piece_index);
759 return true;
761 }
762
763 true
765 });
766
767 let piece_index_to_download_next = if let Some(piece_index) = maybe_piece_index_to_download_next
768 {
769 trace!(%piece_index, %peer_id, "Next piece to download from peer");
770 piece_index
771 } else {
772 trace!(%peer_id, "Peer doesn't have anything else");
773 return;
775 };
776
777 let fut = download_cached_piece_from_peer(
778 node,
779 piece_validator,
780 peer_id,
781 Vec::new(),
782 StdArc::new(sample_cached_piece_indices(
785 pieces_to_download.keys(),
786 &cached_pieces,
787 ¬_cached_pieces,
788 piece_index_to_download_next,
789 )),
790 piece_index_to_download_next,
791 cached_pieces,
792 not_cached_pieces,
793 permit,
794 );
795 downloading_stream.insert(piece_index_to_download_next, Box::pin(fut.into_stream()));
796}
797
798fn sample_cached_piece_indices<'a, I>(
799 pieces_to_download: I,
800 cached_pieces: &HashSet<PieceIndex>,
801 not_cached_pieces: &HashSet<PieceIndex>,
802 piece_index_to_download_next: PieceIndex,
803) -> Vec<PieceIndex>
804where
805 I: Iterator<Item = &'a PieceIndex>,
806{
807 pieces_to_download
808 .filter_map(|piece_index| {
811 if piece_index == &piece_index_to_download_next
812 || cached_pieces.contains(piece_index)
813 || not_cached_pieces.contains(piece_index)
814 {
815 None
816 } else {
817 Some(*piece_index)
818 }
819 })
820 .sample(
821 &mut rand::rng(),
822 CachedPieceByIndexRequest::RECOMMENDED_LIMIT,
823 )
824}
825
826struct DownloadedPieceFromPeer<'a> {
827 peer_id: PeerId,
828 result: Option<PieceResult>,
829 cached_pieces: HashSet<PieceIndex>,
830 not_cached_pieces: HashSet<PieceIndex>,
831 permit: SemaphoreGuard<'a>,
832}
833
834#[expect(clippy::too_many_arguments, reason = "Internal API")]
838async fn download_cached_piece_from_peer<'a, PV>(
839 node: &'a Node,
840 piece_validator: &'a PV,
841 peer_id: PeerId,
842 addresses: Vec<Multiaddr>,
843 check_cached_pieces: StdArc<Vec<PieceIndex>>,
844 piece_index: PieceIndex,
845 mut cached_pieces: HashSet<PieceIndex>,
846 mut not_cached_pieces: HashSet<PieceIndex>,
847 permit: SemaphoreGuard<'a>,
848) -> DownloadedPieceFromPeer<'a>
849where
850 PV: PieceValidator,
851{
852 let result = match node
853 .send_generic_request(
854 peer_id,
855 addresses,
856 CachedPieceByIndexRequest {
857 piece_index,
858 cached_pieces: StdArc::clone(&check_cached_pieces),
859 },
860 )
861 .await
862 {
863 Ok(response) => {
864 let CachedPieceByIndexResponse {
865 result,
866 cached_pieces,
867 } = response;
868
869 match result {
870 PieceResult::Piece(piece) => piece_validator
871 .validate_piece(peer_id, piece_index, piece)
872 .await
873 .map(|piece| CachedPieceByIndexResponse {
874 result: PieceResult::Piece(piece),
875 cached_pieces,
876 }),
877 PieceResult::ClosestPeers(closest_peers) => Some(CachedPieceByIndexResponse {
878 result: PieceResult::ClosestPeers(closest_peers),
879 cached_pieces,
880 }),
881 }
882 }
883 Err(error) => {
884 debug!(%error, %peer_id, %piece_index, "Failed to download cached piece from peer");
885
886 None
887 }
888 };
889
890 match result {
891 Some(result) => {
892 cached_pieces.extend(result.cached_pieces);
893 not_cached_pieces.extend(
894 check_cached_pieces
895 .iter()
896 .filter(|piece_index| !cached_pieces.contains(piece_index))
897 .copied(),
898 );
899
900 DownloadedPieceFromPeer {
901 peer_id,
902 result: Some(result.result),
903 cached_pieces: { cached_pieces },
904 not_cached_pieces,
905 permit,
906 }
907 }
908 None => DownloadedPieceFromPeer {
909 peer_id,
910 result: None,
911 cached_pieces,
912 not_cached_pieces,
913 permit,
914 },
915 }
916}