1#![allow(
2 clippy::cast_possible_truncation,
3 clippy::cast_possible_wrap,
4 clippy::cast_sign_loss,
5 reason = "M175: BitTorrent wire format — message field widths fixed by BEP 3/6/10 spec (u32 piece indices, u32 chunk lengths)"
6)]
7
8use bytes::{BufMut, Bytes, BytesMut};
9
10use crate::error::{Error, Result};
11
12#[derive(Debug, Clone, PartialEq, Eq)]
23pub enum Message<B = Bytes> {
24 KeepAlive,
26 Choke,
28 Unchoke,
30 Interested,
32 NotInterested,
34 Have {
36 index: u32,
38 },
39 Bitfield(B),
41 Request {
43 index: u32,
45 begin: u32,
47 length: u32,
49 },
50 Piece {
55 index: u32,
57 begin: u32,
59 data_0: B,
61 data_1: B,
63 },
64 Cancel {
66 index: u32,
68 begin: u32,
70 length: u32,
72 },
73 Port(u16),
75 Extended {
77 ext_id: u8,
79 payload: B,
81 },
82 SuggestPiece(u32),
84 HaveAll,
86 HaveNone,
88 RejectRequest {
90 index: u32,
92 begin: u32,
94 length: u32,
96 },
97 AllowedFast(u32),
99 HashRequest {
101 pieces_root: irontide_core::Id32,
103 base: u32,
105 index: u32,
107 count: u32,
109 proof_layers: u32,
111 },
112 Hashes {
114 pieces_root: irontide_core::Id32,
116 base: u32,
118 index: u32,
120 count: u32,
122 proof_layers: u32,
124 hashes: Vec<irontide_core::Id32>,
126 },
127 HashReject {
129 pieces_root: irontide_core::Id32,
131 base: u32,
133 index: u32,
135 count: u32,
137 proof_layers: u32,
139 },
140}
141
142const ID_CHOKE: u8 = 0;
144const ID_UNCHOKE: u8 = 1;
145const ID_INTERESTED: u8 = 2;
146const ID_NOT_INTERESTED: u8 = 3;
147const ID_HAVE: u8 = 4;
148const ID_BITFIELD: u8 = 5;
149const ID_REQUEST: u8 = 6;
150const ID_PIECE: u8 = 7;
151const ID_CANCEL: u8 = 8;
152const ID_PORT: u8 = 9;
153const ID_EXTENDED: u8 = 20;
154
155const ID_SUGGEST_PIECE: u8 = 0x0D;
157const ID_HAVE_ALL: u8 = 0x0E;
158const ID_HAVE_NONE: u8 = 0x0F;
159const ID_REJECT_REQUEST: u8 = 0x10;
160const ID_ALLOWED_FAST: u8 = 0x11;
161
162const ID_HASH_REQUEST: u8 = 21;
164const ID_HASHES: u8 = 22;
165const ID_HASH_REJECT: u8 = 23;
166
167impl<B: AsRef<[u8]>> Message<B> {
168 pub fn to_bytes(&self) -> Bytes {
172 match self {
173 Self::KeepAlive => {
174 let mut buf = BytesMut::with_capacity(4);
175 buf.put_u32(0);
176 buf.freeze()
177 }
178 Self::Choke => fixed_msg(ID_CHOKE),
179 Self::Unchoke => fixed_msg(ID_UNCHOKE),
180 Self::Interested => fixed_msg(ID_INTERESTED),
181 Self::NotInterested => fixed_msg(ID_NOT_INTERESTED),
182 Self::Have { index } => {
183 let mut buf = BytesMut::with_capacity(9);
184 buf.put_u32(5);
185 buf.put_u8(ID_HAVE);
186 buf.put_u32(*index);
187 buf.freeze()
188 }
189 Self::Bitfield(bits) => {
190 let bits = bits.as_ref();
191 let mut buf = BytesMut::with_capacity(5 + bits.len());
192 buf.put_u32(1 + bits.len() as u32);
193 buf.put_u8(ID_BITFIELD);
194 buf.put_slice(bits);
195 buf.freeze()
196 }
197 Self::Request {
198 index,
199 begin,
200 length,
201 } => triple_msg(ID_REQUEST, *index, *begin, *length),
202 Self::Piece {
203 index,
204 begin,
205 data_0,
206 data_1,
207 } => {
208 let d0 = data_0.as_ref();
209 let d1 = data_1.as_ref();
210 let data_len = d0.len() + d1.len();
211 let mut buf = BytesMut::with_capacity(13 + data_len);
212 buf.put_u32(9 + data_len as u32);
213 buf.put_u8(ID_PIECE);
214 buf.put_u32(*index);
215 buf.put_u32(*begin);
216 buf.put_slice(d0);
217 buf.put_slice(d1);
218 buf.freeze()
219 }
220 Self::Cancel {
221 index,
222 begin,
223 length,
224 } => triple_msg(ID_CANCEL, *index, *begin, *length),
225 Self::Port(port) => {
226 let mut buf = BytesMut::with_capacity(7);
227 buf.put_u32(3);
228 buf.put_u8(ID_PORT);
229 buf.put_u16(*port);
230 buf.freeze()
231 }
232 Self::Extended { ext_id, payload } => {
233 let payload = payload.as_ref();
234 let mut buf = BytesMut::with_capacity(6 + payload.len());
235 buf.put_u32(2 + payload.len() as u32);
236 buf.put_u8(ID_EXTENDED);
237 buf.put_u8(*ext_id);
238 buf.put_slice(payload);
239 buf.freeze()
240 }
241 Self::SuggestPiece(index) => {
242 let mut buf = BytesMut::with_capacity(9);
243 buf.put_u32(5);
244 buf.put_u8(ID_SUGGEST_PIECE);
245 buf.put_u32(*index);
246 buf.freeze()
247 }
248 Self::HaveAll => fixed_msg(ID_HAVE_ALL),
249 Self::HaveNone => fixed_msg(ID_HAVE_NONE),
250 Self::RejectRequest {
251 index,
252 begin,
253 length,
254 } => triple_msg(ID_REJECT_REQUEST, *index, *begin, *length),
255 Self::AllowedFast(index) => {
256 let mut buf = BytesMut::with_capacity(9);
257 buf.put_u32(5);
258 buf.put_u8(ID_ALLOWED_FAST);
259 buf.put_u32(*index);
260 buf.freeze()
261 }
262 Self::HashRequest {
263 pieces_root,
264 base,
265 index,
266 count,
267 proof_layers,
268 }
269 | Self::HashReject {
270 pieces_root,
271 base,
272 index,
273 count,
274 proof_layers,
275 } => {
276 let id = match self {
277 Self::HashRequest { .. } => ID_HASH_REQUEST,
278 _ => ID_HASH_REJECT,
279 };
280 let mut buf = BytesMut::with_capacity(53);
281 buf.put_u32(49); buf.put_u8(id);
283 buf.put_slice(&pieces_root.0);
284 buf.put_u32(*base);
285 buf.put_u32(*index);
286 buf.put_u32(*count);
287 buf.put_u32(*proof_layers);
288 buf.freeze()
289 }
290 Self::Hashes {
291 pieces_root,
292 base,
293 index,
294 count,
295 proof_layers,
296 hashes,
297 } => {
298 let hash_bytes = hashes.len() * 32;
299 let payload_len = 1 + 32 + 16 + hash_bytes;
300 let mut buf = BytesMut::with_capacity(4 + payload_len);
301 buf.put_u32(payload_len as u32);
302 buf.put_u8(ID_HASHES);
303 buf.put_slice(&pieces_root.0);
304 buf.put_u32(*base);
305 buf.put_u32(*index);
306 buf.put_u32(*count);
307 buf.put_u32(*proof_layers);
308 for h in hashes {
309 buf.put_slice(&h.0);
310 }
311 buf.freeze()
312 }
313 }
314 }
315
316 pub fn encode_into(&self, dst: &mut BytesMut) {
322 match self {
323 Self::KeepAlive => {
324 dst.put_u32(0);
325 }
326 Self::Choke => encode_fixed_into(dst, ID_CHOKE),
327 Self::Unchoke => encode_fixed_into(dst, ID_UNCHOKE),
328 Self::Interested => encode_fixed_into(dst, ID_INTERESTED),
329 Self::NotInterested => encode_fixed_into(dst, ID_NOT_INTERESTED),
330 Self::Have { index } => {
331 dst.put_u32(5);
332 dst.put_u8(ID_HAVE);
333 dst.put_u32(*index);
334 }
335 Self::Bitfield(bits) => {
336 let bits = bits.as_ref();
337 dst.reserve(5 + bits.len());
338 dst.put_u32(1 + bits.len() as u32);
339 dst.put_u8(ID_BITFIELD);
340 dst.put_slice(bits);
341 }
342 Self::Request {
343 index,
344 begin,
345 length,
346 } => encode_triple_into(dst, ID_REQUEST, *index, *begin, *length),
347 Self::Piece {
348 index,
349 begin,
350 data_0,
351 data_1,
352 } => {
353 let d0 = data_0.as_ref();
354 let d1 = data_1.as_ref();
355 let data_len = d0.len() + d1.len();
356 dst.reserve(13 + data_len);
357 dst.put_u32(9 + data_len as u32);
358 dst.put_u8(ID_PIECE);
359 dst.put_u32(*index);
360 dst.put_u32(*begin);
361 dst.put_slice(d0);
362 dst.put_slice(d1);
363 }
364 Self::Cancel {
365 index,
366 begin,
367 length,
368 } => encode_triple_into(dst, ID_CANCEL, *index, *begin, *length),
369 Self::Port(port) => {
370 dst.put_u32(3);
371 dst.put_u8(ID_PORT);
372 dst.put_u16(*port);
373 }
374 Self::Extended { ext_id, payload } => {
375 let payload = payload.as_ref();
376 dst.reserve(6 + payload.len());
377 dst.put_u32(2 + payload.len() as u32);
378 dst.put_u8(ID_EXTENDED);
379 dst.put_u8(*ext_id);
380 dst.put_slice(payload);
381 }
382 Self::SuggestPiece(index) => {
383 dst.put_u32(5);
384 dst.put_u8(ID_SUGGEST_PIECE);
385 dst.put_u32(*index);
386 }
387 Self::HaveAll => encode_fixed_into(dst, ID_HAVE_ALL),
388 Self::HaveNone => encode_fixed_into(dst, ID_HAVE_NONE),
389 Self::RejectRequest {
390 index,
391 begin,
392 length,
393 } => encode_triple_into(dst, ID_REJECT_REQUEST, *index, *begin, *length),
394 Self::AllowedFast(index) => {
395 dst.put_u32(5);
396 dst.put_u8(ID_ALLOWED_FAST);
397 dst.put_u32(*index);
398 }
399 Self::HashRequest {
400 pieces_root,
401 base,
402 index,
403 count,
404 proof_layers,
405 }
406 | Self::HashReject {
407 pieces_root,
408 base,
409 index,
410 count,
411 proof_layers,
412 } => {
413 let id = match self {
414 Self::HashRequest { .. } => ID_HASH_REQUEST,
415 _ => ID_HASH_REJECT,
416 };
417 dst.put_u32(49); dst.put_u8(id);
419 dst.put_slice(&pieces_root.0);
420 dst.put_u32(*base);
421 dst.put_u32(*index);
422 dst.put_u32(*count);
423 dst.put_u32(*proof_layers);
424 }
425 Self::Hashes {
426 pieces_root,
427 base,
428 index,
429 count,
430 proof_layers,
431 hashes,
432 } => {
433 let hash_bytes = hashes.len() * 32;
434 let payload_len = 1 + 32 + 16 + hash_bytes;
435 dst.reserve(4 + payload_len);
436 dst.put_u32(payload_len as u32);
437 dst.put_u8(ID_HASHES);
438 dst.put_slice(&pieces_root.0);
439 dst.put_u32(*base);
440 dst.put_u32(*index);
441 dst.put_u32(*count);
442 dst.put_u32(*proof_layers);
443 for h in hashes {
444 dst.put_slice(&h.0);
445 }
446 }
447 }
448 }
449
450 #[must_use]
457 pub fn wire_len(&self) -> usize {
458 match self {
459 Self::KeepAlive => 4,
460 Self::Choke
461 | Self::Unchoke
462 | Self::Interested
463 | Self::NotInterested
464 | Self::HaveAll
465 | Self::HaveNone => 5,
466 Self::Have { .. } | Self::SuggestPiece(_) | Self::AllowedFast(_) => 9,
467 Self::Port(_) => 7,
468 Self::Request { .. } | Self::Cancel { .. } | Self::RejectRequest { .. } => 17,
469 Self::Bitfield(bits) => 5 + bits.as_ref().len(),
470 Self::Piece { data_0, data_1, .. } => {
471 13 + data_0.as_ref().len() + data_1.as_ref().len()
472 }
473 Self::Extended { payload, .. } => 6 + payload.as_ref().len(),
474 Self::HashRequest { .. } | Self::HashReject { .. } => 53,
475 Self::Hashes { hashes, .. } => 53 + hashes.len() * 32,
476 }
477 }
478
479 #[must_use]
488 pub fn encode_to_slice(&self, dst: &mut [u8]) -> usize {
489 use std::io::{Cursor, Write};
490
491 let mut cursor = Cursor::new(dst);
492
493 match self {
494 Self::KeepAlive => {
495 cursor.write_all(&0u32.to_be_bytes()).unwrap();
496 }
497 Self::Choke => {
498 cursor.write_all(&1u32.to_be_bytes()).unwrap();
499 cursor.write_all(&[ID_CHOKE]).unwrap();
500 }
501 Self::Unchoke => {
502 cursor.write_all(&1u32.to_be_bytes()).unwrap();
503 cursor.write_all(&[ID_UNCHOKE]).unwrap();
504 }
505 Self::Interested => {
506 cursor.write_all(&1u32.to_be_bytes()).unwrap();
507 cursor.write_all(&[ID_INTERESTED]).unwrap();
508 }
509 Self::NotInterested => {
510 cursor.write_all(&1u32.to_be_bytes()).unwrap();
511 cursor.write_all(&[ID_NOT_INTERESTED]).unwrap();
512 }
513 Self::Have { index } => {
514 cursor.write_all(&5u32.to_be_bytes()).unwrap();
515 cursor.write_all(&[ID_HAVE]).unwrap();
516 cursor.write_all(&index.to_be_bytes()).unwrap();
517 }
518 Self::Bitfield(bits) => {
519 let bits = bits.as_ref();
520 cursor
521 .write_all(&(1 + bits.len() as u32).to_be_bytes())
522 .unwrap();
523 cursor.write_all(&[ID_BITFIELD]).unwrap();
524 cursor.write_all(bits).unwrap();
525 }
526 Self::Request {
527 index,
528 begin,
529 length,
530 } => {
531 cursor.write_all(&13u32.to_be_bytes()).unwrap();
532 cursor.write_all(&[ID_REQUEST]).unwrap();
533 cursor.write_all(&index.to_be_bytes()).unwrap();
534 cursor.write_all(&begin.to_be_bytes()).unwrap();
535 cursor.write_all(&length.to_be_bytes()).unwrap();
536 }
537 Self::Piece {
538 index,
539 begin,
540 data_0,
541 data_1,
542 } => {
543 let d0 = data_0.as_ref();
544 let d1 = data_1.as_ref();
545 let data_len = d0.len() + d1.len();
546 cursor
547 .write_all(&(9 + data_len as u32).to_be_bytes())
548 .unwrap();
549 cursor.write_all(&[ID_PIECE]).unwrap();
550 cursor.write_all(&index.to_be_bytes()).unwrap();
551 cursor.write_all(&begin.to_be_bytes()).unwrap();
552 cursor.write_all(d0).unwrap();
553 cursor.write_all(d1).unwrap();
554 }
555 Self::Cancel {
556 index,
557 begin,
558 length,
559 } => {
560 cursor.write_all(&13u32.to_be_bytes()).unwrap();
561 cursor.write_all(&[ID_CANCEL]).unwrap();
562 cursor.write_all(&index.to_be_bytes()).unwrap();
563 cursor.write_all(&begin.to_be_bytes()).unwrap();
564 cursor.write_all(&length.to_be_bytes()).unwrap();
565 }
566 Self::Port(port) => {
567 cursor.write_all(&3u32.to_be_bytes()).unwrap();
568 cursor.write_all(&[ID_PORT]).unwrap();
569 cursor.write_all(&port.to_be_bytes()).unwrap();
570 }
571 Self::Extended { ext_id, payload } => {
572 let payload = payload.as_ref();
573 cursor
574 .write_all(&(2 + payload.len() as u32).to_be_bytes())
575 .unwrap();
576 cursor.write_all(&[ID_EXTENDED]).unwrap();
577 cursor.write_all(&[*ext_id]).unwrap();
578 cursor.write_all(payload).unwrap();
579 }
580 Self::SuggestPiece(index) => {
581 cursor.write_all(&5u32.to_be_bytes()).unwrap();
582 cursor.write_all(&[ID_SUGGEST_PIECE]).unwrap();
583 cursor.write_all(&index.to_be_bytes()).unwrap();
584 }
585 Self::HaveAll => {
586 cursor.write_all(&1u32.to_be_bytes()).unwrap();
587 cursor.write_all(&[ID_HAVE_ALL]).unwrap();
588 }
589 Self::HaveNone => {
590 cursor.write_all(&1u32.to_be_bytes()).unwrap();
591 cursor.write_all(&[ID_HAVE_NONE]).unwrap();
592 }
593 Self::RejectRequest {
594 index,
595 begin,
596 length,
597 } => {
598 cursor.write_all(&13u32.to_be_bytes()).unwrap();
599 cursor.write_all(&[ID_REJECT_REQUEST]).unwrap();
600 cursor.write_all(&index.to_be_bytes()).unwrap();
601 cursor.write_all(&begin.to_be_bytes()).unwrap();
602 cursor.write_all(&length.to_be_bytes()).unwrap();
603 }
604 Self::AllowedFast(index) => {
605 cursor.write_all(&5u32.to_be_bytes()).unwrap();
606 cursor.write_all(&[ID_ALLOWED_FAST]).unwrap();
607 cursor.write_all(&index.to_be_bytes()).unwrap();
608 }
609 Self::HashRequest {
610 pieces_root,
611 base,
612 index,
613 count,
614 proof_layers,
615 }
616 | Self::HashReject {
617 pieces_root,
618 base,
619 index,
620 count,
621 proof_layers,
622 } => {
623 let id = match self {
624 Self::HashRequest { .. } => ID_HASH_REQUEST,
625 _ => ID_HASH_REJECT,
626 };
627 cursor.write_all(&49u32.to_be_bytes()).unwrap();
628 cursor.write_all(&[id]).unwrap();
629 cursor.write_all(&pieces_root.0).unwrap();
630 cursor.write_all(&base.to_be_bytes()).unwrap();
631 cursor.write_all(&index.to_be_bytes()).unwrap();
632 cursor.write_all(&count.to_be_bytes()).unwrap();
633 cursor.write_all(&proof_layers.to_be_bytes()).unwrap();
634 }
635 Self::Hashes {
636 pieces_root,
637 base,
638 index,
639 count,
640 proof_layers,
641 hashes,
642 } => {
643 let hash_bytes = hashes.len() * 32;
644 let payload_len = 1 + 32 + 16 + hash_bytes;
645 cursor
646 .write_all(&(payload_len as u32).to_be_bytes())
647 .unwrap();
648 cursor.write_all(&[ID_HASHES]).unwrap();
649 cursor.write_all(&pieces_root.0).unwrap();
650 cursor.write_all(&base.to_be_bytes()).unwrap();
651 cursor.write_all(&index.to_be_bytes()).unwrap();
652 cursor.write_all(&count.to_be_bytes()).unwrap();
653 cursor.write_all(&proof_layers.to_be_bytes()).unwrap();
654 for h in hashes {
655 cursor.write_all(&h.0).unwrap();
656 }
657 }
658 }
659
660 cursor.position() as usize
661 }
662}
663
664impl Message<&[u8]> {
665 #[must_use]
671 pub fn to_owned_bytes(&self) -> Message<Bytes> {
672 match *self {
673 Message::KeepAlive => Message::KeepAlive,
674 Message::Choke => Message::Choke,
675 Message::Unchoke => Message::Unchoke,
676 Message::Interested => Message::Interested,
677 Message::NotInterested => Message::NotInterested,
678 Message::Have { index } => Message::Have { index },
679 Message::Bitfield(data) => Message::Bitfield(Bytes::copy_from_slice(data)),
680 Message::Request {
681 index,
682 begin,
683 length,
684 } => Message::Request {
685 index,
686 begin,
687 length,
688 },
689 Message::Piece {
690 index,
691 begin,
692 data_0,
693 data_1,
694 } => Message::Piece {
695 index,
696 begin,
697 data_0: Bytes::copy_from_slice(data_0),
698 data_1: Bytes::copy_from_slice(data_1),
699 },
700 Message::Cancel {
701 index,
702 begin,
703 length,
704 } => Message::Cancel {
705 index,
706 begin,
707 length,
708 },
709 Message::Port(port) => Message::Port(port),
710 Message::Extended { ext_id, payload } => Message::Extended {
711 ext_id,
712 payload: Bytes::copy_from_slice(payload),
713 },
714 Message::SuggestPiece(index) => Message::SuggestPiece(index),
715 Message::HaveAll => Message::HaveAll,
716 Message::HaveNone => Message::HaveNone,
717 Message::RejectRequest {
718 index,
719 begin,
720 length,
721 } => Message::RejectRequest {
722 index,
723 begin,
724 length,
725 },
726 Message::AllowedFast(index) => Message::AllowedFast(index),
727 Message::HashRequest {
728 pieces_root,
729 base,
730 index,
731 count,
732 proof_layers,
733 } => Message::HashRequest {
734 pieces_root,
735 base,
736 index,
737 count,
738 proof_layers,
739 },
740 Message::Hashes {
741 ref pieces_root,
742 base,
743 index,
744 count,
745 proof_layers,
746 ref hashes,
747 } => Message::Hashes {
748 pieces_root: *pieces_root,
749 base,
750 index,
751 count,
752 proof_layers,
753 hashes: hashes.clone(),
754 },
755 Message::HashReject {
756 pieces_root,
757 base,
758 index,
759 count,
760 proof_layers,
761 } => Message::HashReject {
762 pieces_root,
763 base,
764 index,
765 count,
766 proof_layers,
767 },
768 }
769 }
770}
771
772impl Message<Bytes> {
773 #[allow(clippy::needless_pass_by_value, reason = "pub API stability")]
780 pub fn from_payload(payload: Bytes) -> Result<Self> {
781 if payload.is_empty() {
782 return Ok(Self::KeepAlive);
783 }
784
785 let id = payload[0];
786 let body = &payload[1..];
787
788 match id {
789 ID_CHOKE => Ok(Self::Choke),
790 ID_UNCHOKE => Ok(Self::Unchoke),
791 ID_INTERESTED => Ok(Self::Interested),
792 ID_NOT_INTERESTED => Ok(Self::NotInterested),
793 ID_HAVE => {
794 ensure_len(body, 4, "Have")?;
795 Ok(Self::Have {
796 index: read_u32(body),
797 })
798 }
799 ID_BITFIELD => Ok(Self::Bitfield(payload.slice(1..))),
800 ID_REQUEST => {
801 ensure_len(body, 12, "Request")?;
802 Ok(Self::Request {
803 index: read_u32(body),
804 begin: read_u32(&body[4..]),
805 length: read_u32(&body[8..]),
806 })
807 }
808 ID_PIECE => {
809 ensure_len(body, 8, "Piece")?;
810 let index = read_u32(body);
811 let begin = read_u32(&body[4..]);
812 Ok(Self::Piece {
813 index,
814 begin,
815 data_0: payload.slice(9..),
816 data_1: Bytes::new(),
817 })
818 }
819 ID_CANCEL => {
820 ensure_len(body, 12, "Cancel")?;
821 Ok(Self::Cancel {
822 index: read_u32(body),
823 begin: read_u32(&body[4..]),
824 length: read_u32(&body[8..]),
825 })
826 }
827 ID_PORT => {
828 ensure_len(body, 2, "Port")?;
829 Ok(Self::Port(u16::from_be_bytes([body[0], body[1]])))
830 }
831 ID_EXTENDED => {
832 ensure_len(body, 1, "Extended")?;
833 let ext_id = body[0];
834 Ok(Self::Extended {
835 ext_id,
836 payload: payload.slice(2..),
837 })
838 }
839 ID_SUGGEST_PIECE => {
840 ensure_len(body, 4, "SuggestPiece")?;
841 Ok(Self::SuggestPiece(read_u32(body)))
842 }
843 ID_HAVE_ALL => Ok(Self::HaveAll),
844 ID_HAVE_NONE => Ok(Self::HaveNone),
845 ID_REJECT_REQUEST => {
846 ensure_len(body, 12, "RejectRequest")?;
847 Ok(Self::RejectRequest {
848 index: read_u32(body),
849 begin: read_u32(&body[4..]),
850 length: read_u32(&body[8..]),
851 })
852 }
853 ID_ALLOWED_FAST => {
854 ensure_len(body, 4, "AllowedFast")?;
855 Ok(Self::AllowedFast(read_u32(body)))
856 }
857 ID_HASH_REQUEST | ID_HASH_REJECT => {
858 ensure_len(body, 48, "HashRequest/Reject")?;
859 let mut root = [0u8; 32];
860 root.copy_from_slice(&body[..32]);
861 let pieces_root = irontide_core::Id32(root);
862 let base = read_u32(&body[32..]);
863 let index = read_u32(&body[36..]);
864 let count = read_u32(&body[40..]);
865 let proof_layers = read_u32(&body[44..]);
866 if id == ID_HASH_REQUEST {
867 Ok(Self::HashRequest {
868 pieces_root,
869 base,
870 index,
871 count,
872 proof_layers,
873 })
874 } else {
875 Ok(Self::HashReject {
876 pieces_root,
877 base,
878 index,
879 count,
880 proof_layers,
881 })
882 }
883 }
884 ID_HASHES => {
885 ensure_len(body, 48, "Hashes")?;
886 let mut root = [0u8; 32];
887 root.copy_from_slice(&body[..32]);
888 let pieces_root = irontide_core::Id32(root);
889 let base = read_u32(&body[32..]);
890 let index = read_u32(&body[36..]);
891 let count = read_u32(&body[40..]);
892 let proof_layers = read_u32(&body[44..]);
893 let hash_data = &body[48..];
894 if !hash_data.len().is_multiple_of(32) {
895 return Err(Error::MessageTooShort {
896 expected: 48 + 32,
897 got: body.len(),
898 });
899 }
900 let hashes = hash_data
901 .chunks_exact(32)
902 .map(|chunk| {
903 let mut h = [0u8; 32];
904 h.copy_from_slice(chunk);
905 irontide_core::Id32(h)
906 })
907 .collect();
908 Ok(Self::Hashes {
909 pieces_root,
910 base,
911 index,
912 count,
913 proof_layers,
914 hashes,
915 })
916 }
917 _ => Err(Error::InvalidMessageId(id)),
918 }
919 }
920}
921
922fn encode_fixed_into(dst: &mut BytesMut, id: u8) {
923 dst.put_u32(1);
924 dst.put_u8(id);
925}
926
927fn encode_triple_into(dst: &mut BytesMut, id: u8, a: u32, b: u32, c: u32) {
928 dst.put_u32(13);
929 dst.put_u8(id);
930 dst.put_u32(a);
931 dst.put_u32(b);
932 dst.put_u32(c);
933}
934
935fn fixed_msg(id: u8) -> Bytes {
936 let mut buf = BytesMut::with_capacity(5);
937 buf.put_u32(1);
938 buf.put_u8(id);
939 buf.freeze()
940}
941
942fn triple_msg(id: u8, a: u32, b: u32, c: u32) -> Bytes {
943 let mut buf = BytesMut::with_capacity(17);
944 buf.put_u32(13);
945 buf.put_u8(id);
946 buf.put_u32(a);
947 buf.put_u32(b);
948 buf.put_u32(c);
949 buf.freeze()
950}
951
952fn read_u32(buf: &[u8]) -> u32 {
953 let mut b = [0u8; 4];
954 b.copy_from_slice(&buf[..4]);
955 u32::from_be_bytes(b)
956}
957
958fn ensure_len(body: &[u8], min: usize, _name: &str) -> Result<()> {
959 if body.len() < min {
960 Err(Error::MessageTooShort {
961 expected: min,
962 got: body.len(),
963 })
964 } else {
965 Ok(())
966 }
967}
968
969#[must_use]
977pub fn allowed_fast_set(
978 info_hash: &irontide_core::Id20,
979 peer_ip: std::net::Ipv4Addr,
980 num_pieces: u32,
981 count: usize,
982) -> Vec<u32> {
983 allowed_fast_set_for_ip(info_hash, std::net::IpAddr::V4(peer_ip), num_pieces, count)
984}
985
986#[must_use]
990pub fn allowed_fast_set_for_ip(
991 info_hash: &irontide_core::Id20,
992 peer_ip: std::net::IpAddr,
993 num_pieces: u32,
994 count: usize,
995) -> Vec<u32> {
996 use irontide_core::sha1;
997
998 if num_pieces == 0 {
999 return Vec::new();
1000 }
1001
1002 let count = count.min(num_pieces as usize);
1003 let mut result = Vec::with_capacity(count);
1004
1005 let masked: Vec<u8> = match peer_ip {
1007 std::net::IpAddr::V4(ipv4) => {
1008 let o = ipv4.octets();
1010 vec![o[0], o[1], o[2], 0]
1011 }
1012 std::net::IpAddr::V6(ipv6) => {
1013 let o = ipv6.octets();
1015 let mut masked = [0u8; 16];
1016 masked[..6].copy_from_slice(&o[..6]);
1017 masked.to_vec()
1018 }
1019 };
1020
1021 let mut input = Vec::with_capacity(masked.len() + 20);
1023 input.extend_from_slice(&masked);
1024 input.extend_from_slice(info_hash.as_bytes());
1025 let mut hash = sha1(&input);
1026
1027 while result.len() < count {
1028 let hash_bytes = hash.as_bytes();
1029 for i in (0..20).step_by(4) {
1031 if result.len() >= count {
1032 break;
1033 }
1034 let index = u32::from_be_bytes([
1035 hash_bytes[i],
1036 hash_bytes[i + 1],
1037 hash_bytes[i + 2],
1038 hash_bytes[i + 3],
1039 ]) % num_pieces;
1040 if !result.contains(&index) {
1041 result.push(index);
1042 }
1043 }
1044 hash = sha1(hash.as_bytes());
1046 }
1047
1048 result
1049}
1050
1051#[cfg(test)]
1052mod tests {
1053 use super::*;
1054
1055 #[allow(clippy::needless_pass_by_value, reason = "test helper convenience")]
1056 fn round_trip(msg: Message) {
1057 let bytes = msg.to_bytes();
1058 let parsed = Message::from_payload(Bytes::copy_from_slice(&bytes[4..])).unwrap();
1060 assert_eq!(msg, parsed);
1061 }
1062
1063 #[test]
1064 fn keepalive() {
1065 round_trip(Message::KeepAlive);
1066 }
1067
1068 #[test]
1069 fn choke_unchoke() {
1070 round_trip(Message::Choke);
1071 round_trip(Message::Unchoke);
1072 }
1073
1074 #[test]
1075 fn interested() {
1076 round_trip(Message::Interested);
1077 round_trip(Message::NotInterested);
1078 }
1079
1080 #[test]
1081 fn have() {
1082 round_trip(Message::Have { index: 42 });
1083 }
1084
1085 #[test]
1086 fn bitfield() {
1087 round_trip(Message::Bitfield(Bytes::from_static(&[0xFF, 0x80])));
1088 }
1089
1090 #[test]
1091 fn request() {
1092 round_trip(Message::Request {
1093 index: 1,
1094 begin: 0,
1095 length: 16384,
1096 });
1097 }
1098
1099 #[test]
1100 fn piece() {
1101 round_trip(Message::Piece {
1102 index: 1,
1103 begin: 0,
1104 data_0: Bytes::from_static(b"hello world"),
1105 data_1: Bytes::new(),
1106 });
1107 }
1108
1109 #[test]
1110 fn cancel() {
1111 round_trip(Message::Cancel {
1112 index: 1,
1113 begin: 0,
1114 length: 16384,
1115 });
1116 }
1117
1118 #[test]
1119 fn port() {
1120 round_trip(Message::Port(6881));
1121 }
1122
1123 #[test]
1124 fn extended() {
1125 round_trip(Message::Extended {
1126 ext_id: 1,
1127 payload: Bytes::from_static(b"test payload"),
1128 });
1129 }
1130
1131 #[test]
1132 fn invalid_message_id() {
1133 assert!(Message::from_payload(Bytes::from_static(&[99u8])).is_err());
1134 }
1135
1136 #[test]
1137 fn suggest_piece() {
1138 round_trip(Message::SuggestPiece(42));
1139 }
1140
1141 #[test]
1142 fn have_all() {
1143 round_trip(Message::HaveAll);
1144 }
1145
1146 #[test]
1147 fn have_none() {
1148 round_trip(Message::HaveNone);
1149 }
1150
1151 #[test]
1152 fn reject_request() {
1153 round_trip(Message::RejectRequest {
1154 index: 1,
1155 begin: 0,
1156 length: 16384,
1157 });
1158 }
1159
1160 #[test]
1161 fn allowed_fast() {
1162 round_trip(Message::AllowedFast(7));
1163 }
1164
1165 #[test]
1166 fn allowed_fast_set_deterministic() {
1167 use irontide_core::Id20;
1168 let ih = Id20::from_hex("aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d").unwrap();
1169 let ip: std::net::Ipv4Addr = "192.168.1.100".parse().unwrap();
1170 let set1 = allowed_fast_set(&ih, ip, 1000, 10);
1171 let set2 = allowed_fast_set(&ih, ip, 1000, 10);
1172 assert_eq!(set1, set2);
1173 assert_eq!(set1.len(), 10);
1174 assert!(set1.iter().all(|&i| i < 1000));
1176 }
1177
1178 #[test]
1179 fn allowed_fast_set_unique() {
1180 use irontide_core::Id20;
1181 let ih = Id20::from_hex("aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d").unwrap();
1182 let ip: std::net::Ipv4Addr = "10.0.0.1".parse().unwrap();
1183 let set = allowed_fast_set(&ih, ip, 50, 10);
1184 let unique: std::collections::HashSet<u32> = set.iter().copied().collect();
1185 assert_eq!(set.len(), unique.len(), "all indices should be unique");
1186 }
1187
1188 #[test]
1189 fn allowed_fast_set_empty_torrent() {
1190 use irontide_core::Id20;
1191 let ih = Id20::ZERO;
1192 let ip: std::net::Ipv4Addr = "127.0.0.1".parse().unwrap();
1193 assert!(allowed_fast_set(&ih, ip, 0, 10).is_empty());
1194 }
1195
1196 #[test]
1197 fn allowed_fast_set_ipv6() {
1198 use irontide_core::Id20;
1199 let ih = Id20::from_hex("aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d").unwrap();
1200 let ip: std::net::IpAddr = "2001:db8::1".parse().unwrap();
1201 let set = allowed_fast_set_for_ip(&ih, ip, 1000, 10);
1202 assert_eq!(set.len(), 10);
1203 assert!(set.iter().all(|&i| i < 1000));
1204
1205 let ip2: std::net::IpAddr = "2001:db8::ffff".parse().unwrap();
1207 let set2 = allowed_fast_set_for_ip(&ih, ip2, 1000, 10);
1208 assert_eq!(set, set2);
1209
1210 let ip3: std::net::IpAddr = "2001:db9::1".parse().unwrap();
1212 let set3 = allowed_fast_set_for_ip(&ih, ip3, 1000, 10);
1213 assert_ne!(set, set3);
1214 }
1215
1216 #[test]
1217 fn hash_request_round_trip() {
1218 let msg = Message::HashRequest {
1219 pieces_root: irontide_core::Id32::ZERO,
1220 base: 7,
1221 index: 0,
1222 count: 512,
1223 proof_layers: 3,
1224 };
1225 round_trip(msg);
1226 }
1227
1228 #[test]
1229 fn hash_reject_round_trip() {
1230 let msg = Message::HashReject {
1231 pieces_root: irontide_core::Id32::ZERO,
1232 base: 7,
1233 index: 0,
1234 count: 512,
1235 proof_layers: 3,
1236 };
1237 round_trip(msg);
1238 }
1239
1240 #[test]
1241 fn hashes_round_trip() {
1242 let h1 = irontide_core::sha256(b"block1");
1243 let h2 = irontide_core::sha256(b"block2");
1244 let uncle = irontide_core::sha256(b"uncle");
1245 let msg = Message::Hashes {
1246 pieces_root: irontide_core::Id32::ZERO,
1247 base: 0,
1248 index: 0,
1249 count: 2,
1250 proof_layers: 1,
1251 hashes: vec![h1, h2, uncle],
1252 };
1253 round_trip(msg);
1254 }
1255
1256 #[test]
1257 fn hash_request_exact_wire_size() {
1258 let msg: Message = Message::HashRequest {
1259 pieces_root: irontide_core::Id32::ZERO,
1260 base: 0,
1261 index: 0,
1262 count: 1,
1263 proof_layers: 0,
1264 };
1265 let bytes = msg.to_bytes();
1266 assert_eq!(bytes.len(), 53);
1268 }
1269
1270 #[test]
1271 fn hashes_variable_length() {
1272 let h = irontide_core::sha256(b"test");
1273 let msg: Message = Message::Hashes {
1274 pieces_root: irontide_core::Id32::ZERO,
1275 base: 0,
1276 index: 0,
1277 count: 1,
1278 proof_layers: 0,
1279 hashes: vec![h],
1280 };
1281 let bytes = msg.to_bytes();
1282 assert_eq!(bytes.len(), 85);
1284 }
1285
1286 #[test]
1287 fn hash_request_too_short() {
1288 let mut payload = vec![21u8];
1290 payload.extend_from_slice(&[0u8; 10]);
1291 assert!(Message::from_payload(Bytes::from(payload)).is_err());
1292 }
1293
1294 #[test]
1295 fn encode_into_matches_to_bytes() {
1296 let messages = vec![
1297 Message::KeepAlive,
1298 Message::Choke,
1299 Message::Unchoke,
1300 Message::Interested,
1301 Message::NotInterested,
1302 Message::Have { index: 42 },
1303 Message::Bitfield(Bytes::from_static(b"\xff\x00")),
1304 Message::Request {
1305 index: 1,
1306 begin: 0,
1307 length: 16384,
1308 },
1309 Message::Piece {
1310 index: 0,
1311 begin: 0,
1312 data_0: Bytes::from_static(b"block data here"),
1313 data_1: Bytes::new(),
1314 },
1315 Message::Cancel {
1316 index: 1,
1317 begin: 0,
1318 length: 16384,
1319 },
1320 Message::Port(6881),
1321 Message::Extended {
1322 ext_id: 0,
1323 payload: Bytes::from_static(b"ext payload"),
1324 },
1325 Message::SuggestPiece(7),
1326 Message::HaveAll,
1327 Message::HaveNone,
1328 Message::RejectRequest {
1329 index: 1,
1330 begin: 0,
1331 length: 16384,
1332 },
1333 Message::AllowedFast(5),
1334 Message::HashRequest {
1335 pieces_root: irontide_core::Id32::ZERO,
1336 base: 7,
1337 index: 0,
1338 count: 512,
1339 proof_layers: 3,
1340 },
1341 Message::HashReject {
1342 pieces_root: irontide_core::Id32::ZERO,
1343 base: 7,
1344 index: 0,
1345 count: 512,
1346 proof_layers: 3,
1347 },
1348 Message::Hashes {
1349 pieces_root: irontide_core::Id32::ZERO,
1350 base: 0,
1351 index: 0,
1352 count: 2,
1353 proof_layers: 1,
1354 hashes: vec![
1355 irontide_core::sha256(b"block1"),
1356 irontide_core::sha256(b"block2"),
1357 irontide_core::sha256(b"uncle"),
1358 ],
1359 },
1360 ];
1361 for msg in messages {
1362 let expected = msg.to_bytes();
1363 let mut buf = BytesMut::new();
1364 msg.encode_into(&mut buf);
1365 assert_eq!(&expected[..], &buf[..], "mismatch for {msg:?}");
1366 }
1367 }
1368
1369 #[test]
1370 fn allowed_fast_set_ipv4_compat() {
1371 use irontide_core::Id20;
1373 let ih = Id20::from_hex("aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d").unwrap();
1374 let ipv4: std::net::Ipv4Addr = "192.168.1.100".parse().unwrap();
1375 let set_v4 = allowed_fast_set(&ih, ipv4, 1000, 10);
1376 let set_ip = allowed_fast_set_for_ip(&ih, std::net::IpAddr::V4(ipv4), 1000, 10);
1377 assert_eq!(set_v4, set_ip);
1378 }
1379
1380 #[test]
1383 fn allowed_fast_bep6_spec_vector_k7() {
1384 use irontide_core::Id20;
1386 let ih = Id20([0xaa; 20]);
1387 let ip: std::net::Ipv4Addr = "80.4.4.200".parse().unwrap();
1388 let set = allowed_fast_set(&ih, ip, 1313, 7);
1389 assert_eq!(set, vec![1059, 431, 808, 1217, 287, 376, 1188]);
1390 }
1391
1392 #[test]
1393 fn allowed_fast_bep6_spec_vector_k9() {
1394 use irontide_core::Id20;
1396 let ih = Id20([0xaa; 20]);
1397 let ip: std::net::Ipv4Addr = "80.4.4.200".parse().unwrap();
1398 let set = allowed_fast_set(&ih, ip, 1313, 9);
1399 assert_eq!(set, vec![1059, 431, 808, 1217, 287, 376, 1188, 353, 508]);
1400 }
1401
1402 #[test]
1403 fn allowed_fast_ip_masking() {
1404 use irontide_core::Id20;
1408 let ih = Id20([0xaa; 20]);
1409 let ip_a: std::net::Ipv4Addr = "80.4.4.200".parse().unwrap();
1410 let ip_b: std::net::Ipv4Addr = "80.4.4.0".parse().unwrap();
1411 let ip_c: std::net::Ipv4Addr = "80.4.4.255".parse().unwrap();
1412 let set_a = allowed_fast_set(&ih, ip_a, 1313, 7);
1413 let set_b = allowed_fast_set(&ih, ip_b, 1313, 7);
1414 let set_c = allowed_fast_set(&ih, ip_c, 1313, 7);
1415 assert_eq!(
1416 set_a, set_b,
1417 "80.4.4.200 and 80.4.4.0 must produce same set (same /24)"
1418 );
1419 assert_eq!(
1420 set_a, set_c,
1421 "80.4.4.200 and 80.4.4.255 must produce same set (same /24)"
1422 );
1423
1424 let octets = ip_a.octets();
1426 let masked = [octets[0], octets[1], octets[2], 0u8];
1427 assert_eq!(masked, [0x50, 0x04, 0x04, 0x00]);
1428 }
1429
1430 #[test]
1433 fn message_piece_two_fields_round_trip() {
1434 let msg = Message::Piece {
1436 index: 5,
1437 begin: 16384,
1438 data_0: Bytes::from_static(b"block payload here"),
1439 data_1: Bytes::new(),
1440 };
1441 let bytes = msg.to_bytes();
1442 let parsed = Message::from_payload(Bytes::copy_from_slice(&bytes[4..])).unwrap();
1443 assert_eq!(msg, parsed);
1444 }
1445
1446 #[test]
1447 fn message_piece_split_data_round_trip() {
1448 let msg = Message::Piece {
1451 index: 3,
1452 begin: 0,
1453 data_0: Bytes::from_static(b"first half"),
1454 data_1: Bytes::from_static(b" second half"),
1455 };
1456 let bytes = msg.to_bytes();
1457 let parsed = Message::from_payload(Bytes::copy_from_slice(&bytes[4..])).unwrap();
1458 assert_eq!(
1461 parsed,
1462 Message::Piece {
1463 index: 3,
1464 begin: 0,
1465 data_0: Bytes::from_static(b"first half second half"),
1466 data_1: Bytes::new(),
1467 }
1468 );
1469 }
1470
1471 #[test]
1472 fn message_generic_encode_borrowed() {
1473 let borrowed: Message<&[u8]> = Message::Piece {
1475 index: 1,
1476 begin: 0,
1477 data_0: b"borrowed data",
1478 data_1: b"",
1479 };
1480 let owned: Message<Bytes> = Message::Piece {
1481 index: 1,
1482 begin: 0,
1483 data_0: Bytes::from_static(b"borrowed data"),
1484 data_1: Bytes::new(),
1485 };
1486 let mut buf_borrowed = BytesMut::new();
1487 borrowed.encode_into(&mut buf_borrowed);
1488 let mut buf_owned = BytesMut::new();
1489 owned.encode_into(&mut buf_owned);
1490 assert_eq!(
1491 buf_borrowed, buf_owned,
1492 "borrowed and owned encode identically"
1493 );
1494
1495 assert_eq!(borrowed.to_bytes(), owned.to_bytes());
1497
1498 let bf_borrowed: Message<&[u8]> = Message::Bitfield(b"\xff\x80");
1500 let bf_owned: Message<Bytes> = Message::Bitfield(Bytes::from_static(b"\xff\x80"));
1501 assert_eq!(bf_borrowed.to_bytes(), bf_owned.to_bytes());
1502
1503 let ext_borrowed: Message<&[u8]> = Message::Extended {
1505 ext_id: 1,
1506 payload: b"payload",
1507 };
1508 let ext_owned: Message<Bytes> = Message::Extended {
1509 ext_id: 1,
1510 payload: Bytes::from_static(b"payload"),
1511 };
1512 assert_eq!(ext_borrowed.to_bytes(), ext_owned.to_bytes());
1513 }
1514
1515 fn all_message_variants() -> Vec<Message> {
1519 vec![
1520 Message::KeepAlive,
1521 Message::Choke,
1522 Message::Unchoke,
1523 Message::Interested,
1524 Message::NotInterested,
1525 Message::Have { index: 42 },
1526 Message::Bitfield(Bytes::from_static(b"\xff\x00")),
1527 Message::Request {
1528 index: 1,
1529 begin: 0,
1530 length: 16384,
1531 },
1532 Message::Piece {
1533 index: 0,
1534 begin: 0,
1535 data_0: Bytes::from_static(b"block data here"),
1536 data_1: Bytes::new(),
1537 },
1538 Message::Piece {
1539 index: 3,
1540 begin: 0,
1541 data_0: Bytes::from_static(b"first half"),
1542 data_1: Bytes::from_static(b" second half"),
1543 },
1544 Message::Cancel {
1545 index: 1,
1546 begin: 0,
1547 length: 16384,
1548 },
1549 Message::Port(6881),
1550 Message::Extended {
1551 ext_id: 0,
1552 payload: Bytes::from_static(b"ext payload"),
1553 },
1554 Message::SuggestPiece(7),
1555 Message::HaveAll,
1556 Message::HaveNone,
1557 Message::RejectRequest {
1558 index: 1,
1559 begin: 0,
1560 length: 16384,
1561 },
1562 Message::AllowedFast(5),
1563 Message::HashRequest {
1564 pieces_root: irontide_core::Id32::ZERO,
1565 base: 7,
1566 index: 0,
1567 count: 512,
1568 proof_layers: 3,
1569 },
1570 Message::HashReject {
1571 pieces_root: irontide_core::Id32::ZERO,
1572 base: 7,
1573 index: 0,
1574 count: 512,
1575 proof_layers: 3,
1576 },
1577 Message::Hashes {
1578 pieces_root: irontide_core::Id32::ZERO,
1579 base: 0,
1580 index: 0,
1581 count: 2,
1582 proof_layers: 1,
1583 hashes: vec![
1584 irontide_core::sha256(b"block1"),
1585 irontide_core::sha256(b"block2"),
1586 irontide_core::sha256(b"uncle"),
1587 ],
1588 },
1589 ]
1590 }
1591
1592 #[test]
1593 fn encode_to_slice_roundtrip() {
1594 for msg in all_message_variants() {
1595 let mut buf = [0u8; 4096];
1596 let n = msg.encode_to_slice(&mut buf);
1597 let parsed = Message::from_payload(Bytes::copy_from_slice(&buf[4..n])).unwrap();
1599 match &msg {
1601 Message::Piece {
1602 index,
1603 begin,
1604 data_0,
1605 data_1,
1606 } if !data_1.is_empty() => {
1607 let mut combined = Vec::from(data_0.as_ref());
1608 combined.extend_from_slice(data_1.as_ref());
1609 let expected = Message::Piece {
1610 index: *index,
1611 begin: *begin,
1612 data_0: Bytes::from(combined),
1613 data_1: Bytes::new(),
1614 };
1615 assert_eq!(parsed, expected, "roundtrip mismatch for split Piece");
1616 }
1617 _ => {
1618 assert_eq!(msg, parsed, "roundtrip mismatch for {msg:?}");
1619 }
1620 }
1621 }
1622 }
1623
1624 #[test]
1625 fn encode_to_slice_matches_encode_into() {
1626 for msg in all_message_variants() {
1627 let mut slice_buf = [0u8; 4096];
1628 let n = msg.encode_to_slice(&mut slice_buf);
1629
1630 let mut bytes_buf = BytesMut::new();
1631 msg.encode_into(&mut bytes_buf);
1632
1633 assert_eq!(
1634 &slice_buf[..n],
1635 &bytes_buf[..],
1636 "encode_to_slice vs encode_into mismatch for {msg:?}"
1637 );
1638 }
1639 }
1640
1641 #[test]
1642 fn wire_len_matches_encoded_size() {
1643 for msg in all_message_variants() {
1644 let expected = msg.to_bytes().len();
1645 assert_eq!(msg.wire_len(), expected, "wire_len mismatch for {msg:?}");
1646 }
1647 }
1648
1649 #[test]
1650 fn wire_len_large_bitfield() {
1651 let bits = vec![0xFFu8; 20_000];
1653 let msg = Message::Bitfield(Bytes::from(bits.clone()));
1654 assert_eq!(msg.wire_len(), 5 + bits.len());
1655 assert_eq!(msg.wire_len(), msg.to_bytes().len());
1656 }
1657}