1use std::time::{Duration, Instant};
8
9use crate::cid_collections::CidHashMap;
10use crate::prelude::*;
11use ahash::HashSet;
12use flume::TryRecvError;
13use futures::StreamExt;
14use libp2p::PeerId;
15use parking_lot::RwLock;
16
17use crate::libp2p_bitswap::{event_handlers::*, *};
18
19const BITSWAP_BLOCK_REQUEST_INTERVAL: Duration = Duration::from_millis(500);
20
21pub type ValidatePeerCallback = dyn Fn(PeerId) -> bool + Send + Sync;
22
23#[derive(Debug, Clone)]
24struct ResponseChannels {
25 block_have: flume::Sender<PeerId>,
26 block_received: flume::Sender<Option<Vec<u8>>>,
27}
28
29pub struct BitswapRequestManager {
32 outbound_have_request_tx: flume::Sender<(PeerId, Cid)>,
34 outbound_have_request_rx: flume::Receiver<(PeerId, Cid)>,
35 outbound_cancel_request_tx: flume::Sender<(PeerId, Cid)>,
37 outbound_cancel_request_rx: flume::Receiver<(PeerId, Cid)>,
38 outbound_block_request_tx: flume::Sender<(PeerId, Cid)>,
40 outbound_block_request_rx: flume::Receiver<(PeerId, Cid)>,
41 peers: RwLock<HashSet<PeerId>>,
42 response_channels: RwLock<CidHashMap<ResponseChannels>>,
43}
44
45impl BitswapRequestManager {
46 pub fn outbound_request_stream(
51 &self,
52 ) -> impl futures::stream::Stream<Item = (PeerId, BitswapRequest)> + '_ {
53 type MapperType = fn((libp2p::PeerId, Cid)) -> (libp2p::PeerId, BitswapRequest);
54
55 fn new_block((peer, cid): (PeerId, Cid)) -> (PeerId, BitswapRequest) {
56 (peer, BitswapRequest::new_block(cid).send_dont_have(false))
57 }
58
59 fn new_have((peer, cid): (PeerId, Cid)) -> (PeerId, BitswapRequest) {
60 (peer, BitswapRequest::new_have(cid).send_dont_have(false))
61 }
62
63 fn new_cancel((peer, cid): (PeerId, Cid)) -> (PeerId, BitswapRequest) {
64 (peer, BitswapRequest::new_cancel(cid).send_dont_have(false))
65 }
66
67 let streams = vec![
69 self.outbound_block_request_rx
70 .stream()
71 .map(new_block as MapperType),
72 self.outbound_have_request_rx
73 .stream()
74 .map(new_have as MapperType),
75 self.outbound_cancel_request_rx
76 .stream()
77 .map(new_cancel as MapperType),
78 ];
79 futures::stream::select_all(streams)
80 }
81}
82
83impl Default for BitswapRequestManager {
84 fn default() -> Self {
85 let (outbound_have_request_tx, outbound_have_request_rx) = flume::unbounded();
86 let (outbound_cancel_request_tx, outbound_cancel_request_rx) = flume::unbounded();
87 let (outbound_block_request_tx, outbound_block_request_rx) = flume::unbounded();
88 Self {
89 outbound_have_request_tx,
90 outbound_have_request_rx,
91 outbound_cancel_request_tx,
92 outbound_cancel_request_rx,
93 outbound_block_request_tx,
94 outbound_block_request_rx,
95 peers: RwLock::new(HashSet::new()),
96 response_channels: RwLock::new(CidHashMap::new()),
97 }
98 }
99}
100
101impl BitswapRequestManager {
102 pub fn handle_event<S: BitswapStoreRead>(
104 self: &Arc<Self>,
105 bitswap: &mut BitswapBehaviour,
106 store: &S,
107 event: BitswapBehaviourEvent,
108 ) -> anyhow::Result<()> {
109 handle_event_impl(self, bitswap, store, event)
110 }
111
112 #[cfg(not(target_arch = "wasm32"))]
116 pub fn get_block(
117 self: Arc<Self>,
118 store: impl BitswapStoreReadWrite + ShallowClone,
119 cid: Cid,
120 timeout: Duration,
121 responder: Option<flume::Sender<bool>>,
122 validate_peer: Option<Arc<ValidatePeerCallback>>,
123 ) {
124 let start = Instant::now();
125 task::spawn(async move {
126 let mut success = store.contains(&cid).unwrap_or_default();
127 if !success {
128 let deadline = start.checked_add(timeout).expect("Infallible");
129 success = task::spawn_blocking({
130 let store = store.shallow_clone();
131 move || self.get_block_sync(&store, cid, deadline, validate_peer)
132 })
133 .await
134 .unwrap_or_default();
135 while !success && Instant::now() < deadline {
138 task::sleep(BITSWAP_BLOCK_REQUEST_INTERVAL).await;
139 success = store.contains(&cid).unwrap_or_default();
140 }
141 }
142
143 if success {
144 metrics::message_counter_get_block_success().inc();
145 } else {
146 metrics::message_counter_get_block_failure().inc();
147 }
148
149 if let Some(responder) = responder
150 && let Err(e) = responder.send_async(success).await
151 {
152 debug!("{e}");
153 }
154
155 metrics::GET_BLOCK_TIME.observe((Instant::now() - start).as_secs_f64());
156 });
157 }
158
159 fn get_block_sync(
160 &self,
161 store: &impl BitswapStoreReadWrite,
162 cid: Cid,
163 deadline: Instant,
164 validate_peer: Option<Arc<ValidatePeerCallback>>,
165 ) -> bool {
166 if self.response_channels.read().contains_key(&cid) {
168 return false;
169 }
170
171 let (block_have_tx, block_have_rx) = flume::unbounded();
172 let (block_saved_tx, block_saved_rx) = flume::unbounded();
173 let channels = ResponseChannels {
174 block_have: block_have_tx,
175 block_received: block_saved_tx,
176 };
177 {
178 self.response_channels.write().insert(cid, channels);
179 }
180
181 let peers: Vec<_> = self.peers.read().iter().cloned().collect();
182 let validated_peers: Vec<_> = peers
183 .iter()
184 .filter(|&&p| validate_peer.as_ref().map(|f| f(p)).unwrap_or(true))
185 .cloned()
186 .collect();
187
188 debug!("Found {} valid peers for {cid}", validated_peers.len());
189 let selected_peers = if validated_peers.is_empty() {
190 peers
192 } else {
193 validated_peers
194 };
195
196 for peer in selected_peers {
197 if let Err(e) = self.outbound_have_request_tx.send((peer, cid)) {
198 debug!("{e}");
199 }
200 }
201
202 let mut success = false;
203 let mut block_data = None;
204 while !success && Instant::now() < deadline {
205 match block_have_rx.try_recv() {
206 Ok(peer) => {
207 _ = self.outbound_block_request_tx.send((peer, cid));
208 }
209 Err(TryRecvError::Empty) => {}
210 Err(TryRecvError::Disconnected) => {
211 break;
212 }
213 }
214
215 if let Ok(data) = block_saved_rx.recv_timeout(BITSWAP_BLOCK_REQUEST_INTERVAL) {
216 success = true;
217 block_data = data;
218 }
219 }
220
221 if !success && let Ok(data) = block_saved_rx.recv_deadline(deadline) {
222 success = true;
223 block_data = data;
224 }
225
226 if let Some(data) = block_data {
227 success = match Block::new(cid, data) {
228 Ok(block) => match store.insert(&block) {
229 Ok(()) => {
230 metrics::message_counter_inbound_response_block_update_db().inc();
231 true
232 }
233 Err(e) => {
234 metrics::message_counter_inbound_response_block_update_db_failure().inc();
235 warn!(
236 "Failed to update db, cid: {cid}, data: {:?}, error: {e:#}",
237 block.data()
238 );
239 false
240 }
241 },
242 Err(e) => {
243 warn!("Failed to construct block, cid: {cid}, error: {e:#}");
244 false
245 }
246 };
247 }
248
249 {
251 let mut response_channels = self.response_channels.write();
252 if response_channels.remove(&cid).is_some() {
253 response_channels.shrink_to_fit();
254 metrics::response_channel_container_capacity()
255 .set(response_channels.total_capacity() as _);
256 }
257 }
258
259 success
260 }
261
262 pub(in crate::libp2p_bitswap) fn on_inbound_response_event<S: BitswapStoreRead>(
263 &self,
264 store: &S,
265 response: BitswapInboundResponseEvent,
266 ) {
267 use BitswapInboundResponseEvent::*;
268
269 match response {
270 HaveBlock(peer, cid) => {
271 if let Some(chans) = self.response_channels.read().get(&cid) {
272 _ = chans.block_have.send(peer);
273 }
274 }
275 DataBlock(_peer, cid, data) => {
276 if let Some(chans) = self.response_channels.read().get(&cid) {
277 if let Ok(true) = store.contains(&cid) {
278 metrics::message_counter_inbound_response_block_already_exists_in_db()
280 .inc();
281 _ = chans.block_received.send(None);
282 } else {
283 _ = chans.block_received.send(Some(data));
284 }
285
286 for &peer in self.peers.read().iter() {
291 if let Err(e) = self.outbound_cancel_request_tx.send((peer, cid)) {
292 debug!("{e}");
293 }
294 }
295 } else {
296 metrics::message_counter_inbound_response_block_not_requested().inc();
297 }
298 }
299 }
300 }
301
302 pub(in crate::libp2p_bitswap) fn on_peer_connected(&self, peer: PeerId) -> bool {
303 let mut peers = self.peers.write();
304 let success = peers.insert(peer);
305 if success {
306 metrics::peer_container_capacity().set(peers.capacity() as _);
307 }
308 success
309 }
310
311 pub(in crate::libp2p_bitswap) fn on_peer_disconnected(&self, peer: &PeerId) -> bool {
312 let mut peers = self.peers.write();
313 let success = peers.remove(peer);
314 if success {
315 peers.shrink_to_fit();
316 metrics::peer_container_capacity().set(peers.capacity() as _);
317 }
318 success
319 }
320}