1use super::ipfs::{self, Cid};
10use super::wire::{pb_bytes, pb_fields_bounded, pb_varint, PbValue};
11use anyhow::{bail, ensure, Context, Result};
12use std::collections::HashMap;
13
14pub const BITSWAP_120: &str = "/ipfs/bitswap/1.2.0";
15pub const BITSWAP_110: &str = "/ipfs/bitswap/1.1.0";
16pub const BITSWAP_100: &str = "/ipfs/bitswap/1.0.0";
17pub const BITSWAP_LEGACY: &str = "/ipfs/bitswap";
18pub const PROTOCOLS: &[&str] = &[BITSWAP_120, BITSWAP_110, BITSWAP_100, BITSWAP_LEGACY];
20pub const MAX_MESSAGE: usize = 4 * 1024 * 1024;
22pub const MAX_WANTLIST_ENTRIES: usize = 1024;
24pub const MAX_MESSAGE_FIELDS: usize = 4096;
26pub const MAX_OUTGOING: usize = MAX_MESSAGE - 64 * 1024;
28pub const MAX_SUPPLIED_BLOCKS: usize = 64;
30
31pub fn is_bitswap(protocol: &str) -> bool {
32 PROTOCOLS.contains(&protocol)
33}
34
35#[derive(Clone, Copy, PartialEq, Eq, Debug)]
37pub enum Version {
38 V100,
39 V110,
40 V120,
41}
42
43impl Version {
44 pub fn of(protocol: &str) -> Version {
45 match protocol {
46 BITSWAP_120 => Version::V120,
47 BITSWAP_110 => Version::V110,
48 _ => Version::V100,
49 }
50 }
51}
52
53#[derive(Clone, Copy, PartialEq, Eq, Debug)]
54pub enum WantType {
55 Block,
56 Have,
57}
58
59impl WantType {
60 pub fn name(self) -> &'static str {
61 match self {
62 WantType::Block => "block",
63 WantType::Have => "have",
64 }
65 }
66}
67
68#[derive(Clone, Debug, PartialEq)]
69pub struct Entry {
70 pub cid: Cid,
71 pub priority: i32,
72 pub cancel: bool,
73 pub want_type: WantType,
74 pub send_dont_have: bool,
75}
76
77#[derive(Clone, Debug, PartialEq)]
80pub struct Block {
81 pub cid: Cid,
82 pub data: Vec<u8>,
83}
84
85#[derive(Clone, Debug, Default, PartialEq)]
86pub struct Message {
87 pub entries: Vec<Entry>,
88 pub full: bool,
89 pub blocks: Vec<Block>,
90 pub presences: Vec<(Cid, bool)>,
92 pub unreadable: Vec<String>,
96}
97
98pub fn decode(b: &[u8]) -> Result<Message> {
101 ensure!(
102 b.len() <= MAX_MESSAGE,
103 "Bitswap message of {} bytes is over {MAX_MESSAGE}",
104 b.len()
105 );
106 let mut m = Message::default();
107 for (f, v) in pb_fields_bounded(b, MAX_MESSAGE_FIELDS)? {
108 match (f, v) {
109 (1, PbValue::Bytes(w)) => decode_wantlist(w, &mut m)?,
110 (2, PbValue::Bytes(data)) => {
111 ensure!(
112 data.len() <= ipfs::MAX_BLOCK,
113 "block over {} bytes",
114 ipfs::MAX_BLOCK
115 );
116 m.blocks.push(Block {
117 cid: Cid {
118 version: 0,
119 codec: ipfs::DAG_PB,
120 digest: ipfs::sha256(data),
121 },
122 data: data.to_vec(),
123 });
124 }
125 (3, PbValue::Bytes(p)) => {
126 let (mut prefix, mut data) = (None, None);
127 for (bf, bv) in pb_fields_bounded(p, 8)? {
128 match (bf, bv) {
129 (1, PbValue::Bytes(x)) => prefix = Some(x),
130 (2, PbValue::Bytes(x)) => data = Some(x),
131 _ => bail!("malformed Bitswap block"),
132 }
133 }
134 let data = data.unwrap_or_default();
135 ensure!(
136 data.len() <= ipfs::MAX_BLOCK,
137 "block over {} bytes",
138 ipfs::MAX_BLOCK
139 );
140 match Cid::from_prefix(prefix.context("a Bitswap block has no prefix")?, data) {
141 Ok(cid) => m.blocks.push(Block {
142 cid,
143 data: data.to_vec(),
144 }),
145 Err(e) => m.unreadable.push(format!("block: {e:#}")),
146 }
147 }
148 (4, PbValue::Bytes(p)) => {
149 let (mut cid, mut kind) = (None, 0);
150 for (pf, pv) in pb_fields_bounded(p, 8)? {
151 match (pf, pv) {
152 (1, PbValue::Bytes(x)) => cid = Some(x),
153 (2, PbValue::Varint(k)) => kind = k,
154 _ => bail!("malformed Bitswap block presence"),
155 }
156 }
157 match Cid::from_bytes(cid.context("a block presence has no CID")?) {
158 Ok(c) => m.presences.push((c, kind == 0)),
159 Err(e) => m.unreadable.push(format!("presence: {e:#}")),
160 }
161 }
162 (5, PbValue::Varint(_)) => {}
164 _ => bail!("malformed Bitswap message (field {f})"),
165 }
166 }
167 Ok(m)
168}
169
170fn decode_wantlist(w: &[u8], m: &mut Message) -> Result<()> {
171 for (f, v) in pb_fields_bounded(w, MAX_WANTLIST_ENTRIES + 1)? {
172 match (f, v) {
173 (1, PbValue::Bytes(e)) => {
174 ensure!(
175 m.entries.len() < MAX_WANTLIST_ENTRIES,
176 "wantlist has more than {MAX_WANTLIST_ENTRIES} entries"
177 );
178 let mut cid = None;
179 let mut entry = Entry {
180 cid: Cid {
181 version: 0,
182 codec: 0,
183 digest: [0; 32],
184 },
185 priority: 0,
186 cancel: false,
187 want_type: WantType::Block,
188 send_dont_have: false,
189 };
190 for (ef, ev) in pb_fields_bounded(e, 16)? {
191 match (ef, ev) {
192 (1, PbValue::Bytes(c)) => cid = Some(c),
193 (2, PbValue::Varint(p)) => entry.priority = p as i32,
194 (3, PbValue::Varint(c)) => entry.cancel = c != 0,
195 (4, PbValue::Varint(t)) => {
196 entry.want_type = if t == 1 {
197 WantType::Have
198 } else {
199 WantType::Block
200 }
201 }
202 (5, PbValue::Varint(s)) => entry.send_dont_have = s != 0,
203 _ => bail!("malformed wantlist entry"),
204 }
205 }
206 let cid = cid.context("a wantlist entry has no CID")?;
207 match Cid::from_bytes(cid) {
208 Ok(c) => {
209 entry.cid = c;
210 m.entries.push(entry);
211 }
212 Err(e) => m.unreadable.push(format!("want: {e:#}")),
213 }
214 }
215 (2, PbValue::Varint(full)) => m.full = full != 0,
216 _ => bail!("malformed wantlist (field {f})"),
217 }
218 }
219 Ok(())
220}
221
222pub fn encode(m: &Message, version: Version) -> Vec<u8> {
225 let mut out = Vec::new();
226 if !m.entries.is_empty() || m.full {
227 let mut w = Vec::new();
228 for e in &m.entries {
229 let mut entry = Vec::new();
230 let cid = if version == Version::V100 {
231 e.cid.multihash()
233 } else {
234 e.cid.to_bytes()
235 };
236 pb_bytes(&mut entry, 1, &cid);
237 pb_varint(&mut entry, 2, e.priority as u32 as u64);
238 if e.cancel {
239 pb_varint(&mut entry, 3, 1);
240 }
241 if version == Version::V120 {
242 if e.want_type == WantType::Have {
243 pb_varint(&mut entry, 4, 1);
244 }
245 if e.send_dont_have {
246 pb_varint(&mut entry, 5, 1);
247 }
248 }
249 pb_bytes(&mut w, 1, &entry);
250 }
251 if m.full {
252 pb_varint(&mut w, 2, 1);
253 }
254 pb_bytes(&mut out, 1, &w);
255 }
256 for b in &m.blocks {
257 if version == Version::V100 {
258 pb_bytes(&mut out, 2, &b.data);
259 } else {
260 let mut block = Vec::new();
261 pb_bytes(&mut block, 1, &b.cid.prefix());
262 pb_bytes(&mut block, 2, &b.data);
263 pb_bytes(&mut out, 3, &block);
264 }
265 }
266 if version == Version::V120 {
267 for (cid, have) in &m.presences {
268 let mut p = Vec::new();
269 pb_bytes(&mut p, 1, &cid.to_bytes());
270 pb_varint(&mut p, 2, if *have { 0 } else { 1 });
271 pb_bytes(&mut out, 4, &p);
272 }
273 }
274 out
275}
276
277pub fn split(m: Message) -> Vec<Message> {
279 let mut out = Vec::new();
280 let mut cur = Message {
281 presences: m.presences,
282 ..Default::default()
283 };
284 let mut size = cur.presences.len() * 48;
285 for b in m.blocks {
286 let len = b.data.len() + 64;
287 if size + len > MAX_OUTGOING && (!cur.blocks.is_empty() || !cur.presences.is_empty()) {
288 out.push(std::mem::take(&mut cur));
289 size = 0;
290 }
291 size += len;
292 cur.blocks.push(b);
293 }
294 if !cur.blocks.is_empty() || !cur.presences.is_empty() {
295 out.push(cur);
296 }
297 out
298}
299
300#[derive(Clone, Copy, PartialEq, Eq, Debug)]
305pub enum Format {
306 Raw,
308 UnixFsFile,
310}
311
312impl Format {
313 pub fn parse(s: &str) -> Result<Format> {
314 match s {
315 "raw" => Ok(Format::Raw),
316 "unixfs" | "unixfs-file" | "file" => Ok(Format::UnixFsFile),
317 other => bail!("format must be raw or unixfs, not {other:?}"),
318 }
319 }
320}
321
322#[derive(Clone, Debug)]
324pub struct Supplied {
325 pub codec: u64,
326 pub bytes: Vec<u8>,
327 pub claimed: Option<Cid>,
328}
329
330impl Supplied {
331 pub fn new(content: &[u8], format: Format, claimed: Option<Cid>) -> Result<Supplied> {
332 let (codec, bytes) = match format {
333 Format::Raw => (ipfs::RAW, content.to_vec()),
334 Format::UnixFsFile => (ipfs::DAG_PB, ipfs::unixfs_file_block(content)),
335 };
336 ensure!(
337 bytes.len() <= ipfs::MAX_BLOCK,
338 "a block is at most {} bytes; this one would be {}",
339 ipfs::MAX_BLOCK,
340 bytes.len()
341 );
342 Ok(Supplied {
343 codec,
344 bytes,
345 claimed,
346 })
347 }
348 pub fn cid(&self) -> Cid {
350 Cid::v1(self.codec, &self.bytes)
351 }
352}
353
354#[derive(Debug, Default)]
356pub struct Answer {
357 pub blocks: Vec<Block>,
359 pub dont_have: Vec<Cid>,
360 pub refused: Vec<String>,
362}
363
364pub fn answer_wants(wants: &[Entry], supplied: &[Supplied]) -> Answer {
371 let mut answer = Answer::default();
372 let mut usable: Vec<(Cid, &Supplied)> = Vec::new();
373 for s in supplied {
374 let actual = s.cid();
375 match s.claimed {
376 Some(claimed) if !claimed.same_block(&actual) => answer.refused.push(format!(
377 "a block claimed as {claimed} hashes to {actual}; not sent"
378 )),
379 _ => usable.push((actual, s)),
380 }
381 }
382 let mut used = vec![false; usable.len()];
383 for want in wants.iter().filter(|w| !w.cancel) {
384 match usable.iter().position(|(c, _)| c.same_block(&want.cid)) {
385 Some(i)
386 if usable[i].1.bytes.len() <= ipfs::MAX_BLOCK
387 && want.cid.matches(&usable[i].1.bytes) =>
388 {
389 used[i] = true;
390 answer.blocks.push(Block {
391 cid: want.cid,
392 data: usable[i].1.bytes.clone(),
393 });
394 }
395 _ if want.send_dont_have => answer.dont_have.push(want.cid),
396 _ => {}
397 }
398 }
399 for (i, (cid, _)) in usable.iter().enumerate() {
400 if !used[i] {
401 answer
402 .refused
403 .push(format!("a block hashing to {cid} was not wanted; not sent"));
404 }
405 }
406 answer
407}
408
409#[derive(Default)]
415pub struct WantSet {
416 wants: HashMap<(u64, [u8; 32]), (Cid, WantType)>,
417}
418
419pub const MAX_OUTSTANDING_WANTS: usize = 1024;
421
422impl WantSet {
423 pub fn add(&mut self, cid: Cid, want: WantType) -> Result<()> {
424 cid.supported()?;
425 let key = (cid.codec, cid.digest);
426 if !self.wants.contains_key(&key) {
427 ensure!(
428 self.wants.len() < MAX_OUTSTANDING_WANTS,
429 "{MAX_OUTSTANDING_WANTS} wants are already outstanding"
430 );
431 }
432 self.wants.insert(key, (cid, want));
433 Ok(())
434 }
435 pub fn remove(&mut self, cid: &Cid) -> Option<(Cid, WantType)> {
436 self.wants.remove(&(cid.codec, cid.digest))
437 }
438 pub fn get(&self, cid: &Cid) -> Option<(Cid, WantType)> {
439 self.wants.get(&(cid.codec, cid.digest)).copied()
440 }
441 pub fn len(&self) -> usize {
442 self.wants.len()
443 }
444 pub fn is_empty(&self) -> bool {
445 self.wants.is_empty()
446 }
447}
448
449#[derive(Debug, Default)]
451pub struct Received {
452 pub blocks: Vec<Block>,
455 pub presences: Vec<(Cid, bool)>,
457 pub refused: Vec<String>,
458}
459
460pub fn accept_blocks(m: &Message, wants: &mut WantSet) -> Received {
464 let mut r = Received::default();
465 for b in &m.blocks {
466 match wants.get(&b.cid) {
467 Some((asked, _)) if b.cid.supported().is_ok() && asked.matches(&b.data) => {
468 wants.remove(&asked);
469 r.blocks.push(Block {
470 cid: asked,
471 data: b.data.clone(),
472 });
473 }
474 _ => r.refused.push(format!(
475 "a {}-byte block hashing to {} matches nothing that was wanted; refused",
476 b.data.len(),
477 b.cid
478 )),
479 }
480 }
481 for (cid, have) in &m.presences {
482 if let Some((asked, _)) = wants.get(cid) {
483 if !*have {
484 wants.remove(&asked);
485 }
486 r.presences.push((asked, *have));
487 }
488 }
489 r.refused.extend(m.unreadable.iter().cloned());
490 r
491}