1use std::collections::{BTreeMap, HashSet};
28
29use crate::mesh::{MeshError, MeshStream};
30use crate::mesh_artifact::{ArtifactExchange, ExchangeError};
31pub use crate::mesh_artifact_peers::{
32 FetchError, FetchOutcome, PeerLink, PeerSet, PieceScheduler, SequentialScheduler, ServeStats,
33};
34use crate::mesh_artifact_wire::{ArtifactDescriptor, ArtifactKey, ArtifactMsg, Bitfield};
35use crate::mesh_swarm_state::{Audience, ServePeer};
36
37#[derive(Debug, thiserror::Error)]
38pub(crate) enum PeerError {
39 #[error("{0}")]
40 Mesh(#[from] MeshError),
41 #[error("{0}")]
42 Wire(#[from] crate::mesh_artifact_wire::WireError),
43 #[error("{0}")]
44 Local(#[from] ExchangeError),
45 #[error("protocol: {0}")]
46 Protocol(String),
47 #[error("peer refused: {0}")]
48 Refused(String),
49 #[error("timed out waiting for peer")]
50 Timeout,
51}
52
53#[derive(Default)]
54struct Progress {
55 pieces: u32,
56 bytes: u64,
57 rejected: u32,
58 sources: Vec<String>,
59 current: Option<ArtifactDescriptor>,
61 bad_descriptors: Vec<(String, String, String)>,
64 written: HashSet<[u8; 32]>,
67}
68
69pub(crate) async fn send(
70 stream: &mut dyn MeshStream,
71 msg: &ArtifactMsg,
72) -> Result<(), PeerError> {
73 stream.send(&msg.to_wire()?).await?;
74 Ok(())
75}
76
77impl ArtifactExchange {
78 pub(crate) async fn recv(
79 &self,
80 stream: &mut dyn MeshStream,
81 ) -> Result<ArtifactMsg, PeerError> {
82 let raw = tokio::time::timeout(self.config().recv_timeout, stream.recv())
83 .await
84 .map_err(|_| PeerError::Timeout)??;
85 Ok(ArtifactMsg::from_wire(&raw)?)
86 }
87
88 pub async fn serve(
97 &self,
98 stream: &mut dyn MeshStream,
99 peer: &str,
100 ) -> Result<ServeStats, ExchangeError> {
101 self.serve_as(stream, &ServePeer::unverified(peer)).await
102 }
103
104 pub async fn serve_as(
107 &self,
108 stream: &mut dyn MeshStream,
109 who: &ServePeer,
110 ) -> Result<ServeStats, ExchangeError> {
111 let peer = who.node_id.as_str();
112 let mut stats = ServeStats::default();
113 if self.is_banned(peer) {
114 let _ = stream.close().await;
115 return Err(ExchangeError::Banned(peer.to_string()));
116 }
117 loop {
118 let idle = self.config().serve_idle_timeout;
119 let raw = match tokio::time::timeout(idle, stream.recv()).await {
120 Ok(Ok(raw)) => raw,
121 Ok(Err(MeshError::ConnectionClosed)) => return Ok(stats),
122 Ok(Err(e)) => return Err(ExchangeError::Io(e.to_string())),
123 Err(_) => {
124 let _ = stream.close().await;
125 return Err(ExchangeError::Io(format!(
126 "peer {peer} idle for {idle:?}; serve session closed"
127 )));
128 }
129 };
130 let msg = match ArtifactMsg::from_wire(&raw) {
131 Ok(m) => m,
132 Err(e) => {
133 let _ = stream.close().await;
134 return Err(e.into());
135 }
136 };
137 if let Err(e) = self.serve_one(stream, who, msg, &mut stats).await {
138 let _ = stream.close().await;
139 return Err(match e {
140 PeerError::Local(l) => l,
141 other => ExchangeError::Io(other.to_string()),
142 });
143 }
144 }
145 }
146
147 pub async fn serve_frame(
152 &self,
153 stream: &mut dyn MeshStream,
154 peer: &str,
155 raw: &[u8],
156 stats: &mut ServeStats,
157 ) -> Result<(), ExchangeError> {
158 self.serve_frame_as(stream, &ServePeer::unverified(peer), raw, stats)
159 .await
160 }
161
162 pub async fn serve_frame_as(
164 &self,
165 stream: &mut dyn MeshStream,
166 who: &ServePeer,
167 raw: &[u8],
168 stats: &mut ServeStats,
169 ) -> Result<(), ExchangeError> {
170 let peer = who.node_id.as_str();
171 if self.is_banned(peer) {
172 let _ = stream.close().await;
173 return Err(ExchangeError::Banned(peer.to_string()));
174 }
175 let msg = ArtifactMsg::from_wire(raw)?;
176 self.serve_one(stream, who, msg, stats)
177 .await
178 .map_err(|e| match e {
179 PeerError::Local(l) => l,
180 other => ExchangeError::Io(other.to_string()),
181 })
182 }
183
184 async fn serve_one(
185 &self,
186 stream: &mut dyn MeshStream,
187 who: &ServePeer,
188 msg: ArtifactMsg,
189 stats: &mut ServeStats,
190 ) -> Result<(), PeerError> {
191 let peer = who.node_id.as_str();
192 match msg {
193 ArtifactMsg::MetaRequest { key } => match self.servable(&key, who) {
194 Some(d) => {
195 let id = d.id();
196 let have = self.have(&id).unwrap_or_else(|| Bitfield::new(0));
197 send(stream, &ArtifactMsg::Meta { descriptor: d }).await?;
198 send(stream, &ArtifactMsg::Announce { id, have }).await
199 }
200 None => {
201 let reason = "not available or not servable".to_string();
202 send(stream, &ArtifactMsg::Reject { key, reason }).await
203 }
204 },
205 ArtifactMsg::Announce { id, have } => {
206 self.note_peer_have(id, peer, have);
207 Ok(())
208 }
209 ArtifactMsg::Request { id, pieces } => {
210 let key = ArtifactKey::Root(id);
211 let Some(d) = self.servable(&key, who) else {
212 let reason = "not available or not servable".to_string();
213 return send(stream, &ArtifactMsg::Reject { key, reason }).await;
214 };
215 if let Some(cache) = self.swarm.cache() {
216 cache.touch(&id);
217 }
218 for index in pieces {
219 let Ok(data) = self.load_piece(&d, index) else {
220 send(stream, &ArtifactMsg::NoPiece { id, index }).await?;
221 continue;
222 };
223 self.chain_serve_once(&d, peer);
224 for (n, block) in data.chunks(self.config().block_size).enumerate() {
225 let offset = (n * self.config().block_size) as u64;
226 let data = block.to_vec();
227 self.swarm.bandwidth.upload(data.len()).await;
228 send(
229 stream,
230 &ArtifactMsg::Piece {
231 id,
232 index,
233 offset,
234 data,
235 },
236 )
237 .await?;
238 }
239 stats.pieces_served.push(index);
240 stats.bytes_served += data.len() as u64;
241 }
242 Ok(())
243 }
244 other => Err(PeerError::Protocol(format!(
245 "unexpected {:?} frame from fetcher",
246 other.frame_type()
247 ))),
248 }
249 }
250
251 fn servable(&self, key: &ArtifactKey, who: &ServePeer) -> Option<ArtifactDescriptor> {
252 self.resolve(key)
253 .filter(|d| self.is_servable_to(d, &Audience::Serve(who)))
254 }
255
256 pub async fn fetch(
258 &self,
259 peers: &mut PeerSet,
260 key: ArtifactKey,
261 ) -> Result<FetchOutcome, FetchError> {
262 self.fetch_with(peers, key, &mut SequentialScheduler).await
263 }
264
265 pub async fn fetch_with(
272 &self,
273 peers: &mut PeerSet,
274 key: ArtifactKey,
275 scheduler: &mut dyn PieceScheduler,
276 ) -> Result<FetchOutcome, FetchError> {
277 let mut progress = Progress::default();
278 let mut last_error = String::from("no peers");
279 let mut last_peer = String::new();
280 let mut verify_failed = false;
281 let mut done = self.resolve(&key).filter(|d| self.is_complete(d));
282 for link in peers.links.iter_mut().filter(|l| !l.dead) {
283 if done.is_some() {
284 break;
285 }
286 last_peer = link.peer_id.clone();
287 let before = (progress.pieces, progress.bytes);
288 let err = match self.fetch_from(link, key, scheduler, &mut progress).await {
289 Ok(d) if self.have_of(&d).is_complete() => {
290 match self.promote(&d, &progress.written) {
291 Ok(()) => {
292 done = Some(d);
293 continue;
294 }
295 Err(e) => {
296 (progress.pieces, progress.bytes) = before;
298 progress.sources.retain(|p| *p != link.peer_id);
299 progress.current = None;
300 progress.bad_descriptors.push((
301 link.peer_id.clone(),
302 d.id().to_string(),
303 e.to_string(),
304 ));
305 verify_failed = true;
306 PeerError::Local(e)
307 }
308 }
309 }
310 Ok(_) => {
311 verify_failed = false;
312 continue; }
314 Err(e) => {
315 verify_failed = false;
316 e
317 }
318 };
319 last_error = err.to_string();
320 if !matches!(err, PeerError::Refused(_)) {
321 link.dead = true;
322 let _ = link.stream.close().await;
323 }
324 }
325 let Some(d) = done else {
326 let missing = progress
327 .current
328 .as_ref()
329 .map_or(0, |d| d.piece_count() - self.have_of(d).count());
330 let err = self.fetch_failed(&key, &progress, &last_peer, last_error, missing);
331 return Err(match err {
332 FetchError::Incomplete { last_error, .. } if verify_failed => {
333 FetchError::Verification(last_error)
334 }
335 other => other,
336 });
337 };
338 let id = d.id();
339 self.materialize(&id)?;
340 self.chain_fetch(serde_json::json!({
341 "artifact_id": id.to_string(),
342 "content_hash": d.content_hex(),
343 "source_peer": progress.sources.last().cloned().unwrap_or_default(),
344 "sources": progress.sources,
345 "bytes": progress.bytes,
346 "total_size": d.total_size,
347 "pieces_fetched": progress.pieces,
348 "pieces_rejected": progress.rejected,
349 "descriptors_rejected": rejected_json(&progress),
350 "result": "verified",
351 "node": self.node_id(),
352 }));
353 Ok(FetchOutcome {
354 id,
355 descriptor: d,
356 pieces_fetched: progress.pieces,
357 bytes_fetched: progress.bytes,
358 pieces_rejected: progress.rejected,
359 sources: progress.sources,
360 })
361 }
362
363 pub(crate) fn is_complete(&self, d: &ArtifactDescriptor) -> bool {
364 self.have(&d.id()).is_some_and(|h| h.is_complete())
365 }
366
367 fn fetch_failed(
368 &self,
369 key: &ArtifactKey,
370 progress: &Progress,
371 last_peer: &str,
372 last_error: String,
373 missing: u32,
374 ) -> FetchError {
375 self.chain_fetch(serde_json::json!({
376 "key": key.to_string(),
377 "artifact_id": progress.current.as_ref().map(|d| d.id().to_string()),
378 "source_peer": last_peer,
379 "sources": progress.sources,
380 "bytes": progress.bytes,
381 "pieces_fetched": progress.pieces,
382 "pieces_rejected": progress.rejected,
383 "missing_pieces": missing,
384 "descriptors_rejected": rejected_json(progress),
385 "result": "failed",
386 "error": last_error,
387 "node": self.node_id(),
388 }));
389 FetchError::Incomplete {
390 missing,
391 last_error,
392 }
393 }
394
395 async fn fetch_from(
396 &self,
397 link: &mut PeerLink,
398 key: ArtifactKey,
399 scheduler: &mut dyn PieceScheduler,
400 progress: &mut Progress,
401 ) -> Result<ArtifactDescriptor, PeerError> {
402 let stream = link.stream.as_mut();
403 let peer = link.peer_id.clone();
404 send(stream, &ArtifactMsg::MetaRequest { key }).await?;
405 let d = match self.recv(stream).await? {
406 ArtifactMsg::Meta { descriptor } => descriptor,
407 ArtifactMsg::Reject { reason, .. } => return Err(PeerError::Refused(reason)),
408 other => return Err(unexpected(&other)),
409 };
410 let matches_key = match key {
411 ArtifactKey::Root(id) => d.id() == id,
412 ArtifactKey::Content(h) => d.content_hash == h,
413 };
414 if !matches_key {
415 return Err(PeerError::Protocol(format!(
416 "descriptor does not match {key}"
417 )));
418 }
419 self.check_descriptor(&d, &crate::mesh_swarm_fetch::Expect::default())?;
420 let id = self.note_pending(&d)?;
423 progress.current = Some(d.clone());
424 let mut peer_has = match self.recv(stream).await? {
425 ArtifactMsg::Announce { id: aid, have }
426 if aid == id && have.len() == d.piece_count() =>
427 {
428 have
429 }
430 other => return Err(unexpected(&other)),
431 };
432 let mine = self.have_of(&d);
433 send(stream, &ArtifactMsg::Announce { id, have: mine }).await?;
434
435 let mut strikes: BTreeMap<u32, u32> = BTreeMap::new();
436 loop {
437 let mut need = self.have_of(&d);
438 for (&i, &n) in &strikes {
439 if n >= self.config().max_piece_retries {
440 need.set(i, true); }
442 }
443 let batch = scheduler.next_batch(&need, &peer_has, self.config().request_window);
444 if batch.is_empty() {
445 return Ok(d);
446 }
447 send(
448 stream,
449 &ArtifactMsg::Request {
450 id,
451 pieces: batch.clone(),
452 },
453 )
454 .await?;
455 for index in batch {
456 match self.recv_piece(stream, &d, index).await? {
457 Some(data) => {
458 let hash = *blake3::hash(&data).as_bytes();
459 if hash != d.pieces[index as usize] {
460 progress.rejected += 1;
461 *strikes.entry(index).or_default() += 1;
462 self.chain_piece_rejected(&id, index, &peer, "hash mismatch");
463 continue;
464 }
465 if self.store_piece(&data, &hash)? {
466 progress.written.insert(hash);
467 }
468 progress.pieces += 1;
469 progress.bytes += data.len() as u64;
470 if !progress.sources.contains(&peer) {
471 progress.sources.push(peer.clone());
472 }
473 }
474 None => peer_has.set(index, false),
475 }
476 }
477 }
478 }
479
480 async fn recv_piece(
483 &self,
484 stream: &mut dyn MeshStream,
485 d: &ArtifactDescriptor,
486 index: u32,
487 ) -> Result<Option<Vec<u8>>, PeerError> {
488 let want = d.piece_len(index) as usize;
489 let mut buf: Vec<u8> = Vec::new();
490 while buf.len() < want {
491 match self.recv(stream).await? {
492 ArtifactMsg::Piece {
493 id,
494 index: i,
495 offset,
496 data,
497 } if id == d.id() && i == index && offset == buf.len() as u64 => {
498 if buf.len() + data.len() > want {
499 return Err(PeerError::Protocol("piece longer than descriptor".into()));
500 }
501 buf.extend_from_slice(&data);
504 }
505 ArtifactMsg::NoPiece { id, index: i } if id == d.id() && i == index => {
506 return Ok(None);
507 }
508 other => return Err(unexpected(&other)),
509 }
510 }
511 Ok(Some(buf))
512 }
513}
514
515fn rejected_json(progress: &Progress) -> serde_json::Value {
516 progress
517 .bad_descriptors
518 .iter()
519 .map(|(peer, id, reason)| {
520 serde_json::json!({ "peer": peer, "artifact_id": id, "reason": reason })
521 })
522 .collect()
523}
524
525fn unexpected(msg: &ArtifactMsg) -> PeerError {
526 let what = match msg {
527 ArtifactMsg::Reject { reason, .. } => return PeerError::Refused(reason.clone()),
528 ArtifactMsg::MetaRequest { .. } => "meta_request",
529 ArtifactMsg::Announce { .. } => "announce",
530 ArtifactMsg::Request { .. } => "request",
531 ArtifactMsg::Meta { .. } => "meta",
532 ArtifactMsg::Piece { .. } => "piece",
533 ArtifactMsg::NoPiece { .. } => "no_piece",
534 };
535 PeerError::Protocol(format!("unexpected {what} message"))
536}