1use crate::BlockAccessLists;
9use alloc::vec::Vec;
10use alloy_primitives::{Bytes, B256};
11use alloy_rlp::{BufMut, Decodable, Encodable, RlpDecodable, RlpEncodable};
12use reth_codecs_derive::add_arbitrary_tests;
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Hash)]
16#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
17#[repr(u8)]
18pub enum SnapVersion {
19 #[default]
21 V2 = 2,
22}
23
24impl SnapVersion {
25 pub const fn message_count(self) -> u8 {
28 match self {
29 Self::V2 => 10,
30 }
31 }
32
33 pub const fn supports_message_id(self, id: u8) -> bool {
38 match self {
39 Self::V2 => {
41 id <= SnapMessageId::ByteCodes as u8 ||
42 id == SnapMessageId::GetBlockAccessLists as u8 ||
43 id == SnapMessageId::BlockAccessLists as u8
44 }
45 }
46 }
47}
48
49#[derive(Debug, Clone, Copy, PartialEq, Eq)]
51pub enum SnapMessageId {
52 GetAccountRange = 0x00,
54 AccountRange = 0x01,
57 GetStorageRanges = 0x02,
59 StorageRanges = 0x03,
61 GetByteCodes = 0x04,
63 ByteCodes = 0x05,
65 GetBlockAccessLists = 0x08,
67 BlockAccessLists = 0x09,
69}
70
71impl SnapMessageId {
72 pub const fn response(self) -> Option<Self> {
75 match self {
76 Self::GetAccountRange => Some(Self::AccountRange),
77 Self::GetStorageRanges => Some(Self::StorageRanges),
78 Self::GetByteCodes => Some(Self::ByteCodes),
79 Self::GetBlockAccessLists => Some(Self::BlockAccessLists),
80 Self::AccountRange | Self::StorageRanges | Self::ByteCodes | Self::BlockAccessLists => {
81 None
82 }
83 }
84 }
85}
86
87#[derive(Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)]
90#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
91#[add_arbitrary_tests(rlp)]
92pub struct GetAccountRangeMessage {
93 pub request_id: u64,
95 pub root_hash: B256,
97 pub starting_hash: B256,
99 pub limit_hash: B256,
101 pub response_bytes: u64,
103}
104
105#[derive(Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)]
107#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
108#[add_arbitrary_tests(rlp)]
109pub struct AccountData {
110 pub hash: B256,
112 pub body: Bytes,
114}
115
116#[derive(Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)]
119#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
120#[add_arbitrary_tests(rlp)]
121pub struct AccountRangeMessage {
122 pub request_id: u64,
124 pub accounts: Vec<AccountData>,
126 pub proof: Vec<Bytes>,
128}
129
130#[derive(Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)]
133#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
134#[add_arbitrary_tests(rlp)]
135pub struct GetStorageRangesMessage {
136 pub request_id: u64,
138 pub root_hash: B256,
140 pub account_hashes: Vec<B256>,
142 pub starting_hash: RangeBound,
145 pub limit_hash: RangeBound,
148 pub response_bytes: u64,
150}
151
152#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
156#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
157pub struct RangeBound(Option<B256>);
158
159impl RangeBound {
160 pub const fn unwrap_or(self, default: B256) -> B256 {
162 match self.0 {
163 Some(hash) => hash,
164 None => default,
165 }
166 }
167}
168
169impl From<B256> for RangeBound {
170 fn from(hash: B256) -> Self {
171 Self(Some(hash))
172 }
173}
174
175impl Encodable for RangeBound {
176 fn encode(&self, out: &mut dyn BufMut) {
177 match self.0 {
178 Some(hash) => hash.encode(out),
179 None => Bytes::new().encode(out),
180 }
181 }
182
183 fn length(&self) -> usize {
184 match self.0 {
185 Some(hash) => hash.length(),
186 None => Bytes::new().length(),
187 }
188 }
189}
190
191impl Decodable for RangeBound {
192 fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
193 let bytes = Bytes::decode(buf)?;
194 match bytes.len() {
195 0 => Ok(Self(None)),
196 32 => Ok(Self(Some(B256::from_slice(&bytes)))),
197 _ => Err(alloy_rlp::Error::UnexpectedLength),
198 }
199 }
200}
201
202#[derive(Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)]
204#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
205#[add_arbitrary_tests(rlp)]
206pub struct StorageData {
207 pub hash: B256,
209 pub data: Bytes,
211}
212
213#[derive(Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)]
218#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
219#[add_arbitrary_tests(rlp)]
220pub struct StorageRangesMessage {
221 pub request_id: u64,
223 pub slots: Vec<Vec<StorageData>>,
225 pub proof: Vec<Bytes>,
227}
228
229#[derive(Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)]
232#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
233#[add_arbitrary_tests(rlp)]
234pub struct GetByteCodesMessage {
235 pub request_id: u64,
237 pub hashes: Vec<B256>,
239 pub response_bytes: u64,
241}
242
243#[derive(Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)]
246#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
247#[add_arbitrary_tests(rlp)]
248pub struct ByteCodesMessage {
249 pub request_id: u64,
251 pub codes: Vec<Bytes>,
253}
254
255#[derive(Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)]
257#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
258#[add_arbitrary_tests(rlp)]
259pub struct GetBlockAccessListsMessage {
260 pub request_id: u64,
262 pub block_hashes: Vec<B256>,
264 pub response_bytes: u64,
266}
267
268#[derive(Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)]
270#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
271#[add_arbitrary_tests(rlp)]
272pub struct BlockAccessListsMessage {
273 pub request_id: u64,
275 pub block_access_lists: BlockAccessLists,
277}
278
279#[derive(Debug, Clone, PartialEq, Eq)]
281pub enum SnapProtocolMessage {
282 GetAccountRange(GetAccountRangeMessage),
284 AccountRange(AccountRangeMessage),
286 GetStorageRanges(GetStorageRangesMessage),
288 StorageRanges(StorageRangesMessage),
290 GetByteCodes(GetByteCodesMessage),
292 ByteCodes(ByteCodesMessage),
294 GetBlockAccessLists(GetBlockAccessListsMessage),
296 BlockAccessLists(BlockAccessListsMessage),
298}
299
300#[derive(thiserror::Error, Debug)]
302pub enum SnapProtocolError {
303 #[error("empty snap message")]
305 Empty,
306 #[error("message id {0:#x} is invalid for snap/{1:?}")]
309 UnsupportedMessageId(u8, SnapVersion),
310 #[error("RLP error: {0}")]
312 Rlp(#[from] alloy_rlp::Error),
313}
314
315impl SnapProtocolMessage {
316 pub const fn message_id(&self) -> SnapMessageId {
320 match self {
321 Self::GetAccountRange(_) => SnapMessageId::GetAccountRange,
322 Self::AccountRange(_) => SnapMessageId::AccountRange,
323 Self::GetStorageRanges(_) => SnapMessageId::GetStorageRanges,
324 Self::StorageRanges(_) => SnapMessageId::StorageRanges,
325 Self::GetByteCodes(_) => SnapMessageId::GetByteCodes,
326 Self::ByteCodes(_) => SnapMessageId::ByteCodes,
327 Self::GetBlockAccessLists(_) => SnapMessageId::GetBlockAccessLists,
328 Self::BlockAccessLists(_) => SnapMessageId::BlockAccessLists,
329 }
330 }
331
332 pub const fn request_id(&self) -> u64 {
334 match self {
335 Self::GetAccountRange(m) => m.request_id,
336 Self::AccountRange(m) => m.request_id,
337 Self::GetStorageRanges(m) => m.request_id,
338 Self::StorageRanges(m) => m.request_id,
339 Self::GetByteCodes(m) => m.request_id,
340 Self::ByteCodes(m) => m.request_id,
341 Self::GetBlockAccessLists(m) => m.request_id,
342 Self::BlockAccessLists(m) => m.request_id,
343 }
344 }
345
346 pub const fn is_response(&self) -> bool {
348 matches!(
349 self,
350 Self::AccountRange(_) |
351 Self::StorageRanges(_) |
352 Self::ByteCodes(_) |
353 Self::BlockAccessLists(_)
354 )
355 }
356
357 pub const fn set_request_id(&mut self, request_id: u64) {
360 match self {
361 Self::GetAccountRange(m) => m.request_id = request_id,
362 Self::AccountRange(m) => m.request_id = request_id,
363 Self::GetStorageRanges(m) => m.request_id = request_id,
364 Self::StorageRanges(m) => m.request_id = request_id,
365 Self::GetByteCodes(m) => m.request_id = request_id,
366 Self::ByteCodes(m) => m.request_id = request_id,
367 Self::GetBlockAccessLists(m) => m.request_id = request_id,
368 Self::BlockAccessLists(m) => m.request_id = request_id,
369 }
370 }
371
372 pub fn encode(&self) -> Bytes {
374 let mut buf = Vec::new();
375 buf.push(self.message_id() as u8);
377
378 match self {
380 Self::GetAccountRange(msg) => msg.encode(&mut buf),
381 Self::AccountRange(msg) => msg.encode(&mut buf),
382 Self::GetStorageRanges(msg) => msg.encode(&mut buf),
383 Self::StorageRanges(msg) => msg.encode(&mut buf),
384 Self::GetByteCodes(msg) => msg.encode(&mut buf),
385 Self::ByteCodes(msg) => msg.encode(&mut buf),
386 Self::GetBlockAccessLists(msg) => msg.encode(&mut buf),
387 Self::BlockAccessLists(msg) => msg.encode(&mut buf),
388 }
389
390 Bytes::from(buf)
391 }
392
393 pub fn decode(message_id: u8, buf: &mut &[u8]) -> Result<Self, alloy_rlp::Error> {
395 macro_rules! decode_snap_message_variant {
397 ($message_id:expr, $buf:expr, $id:expr, $variant:ident, $msg_type:ty) => {
398 if $message_id == $id as u8 {
399 return Ok(Self::$variant(<$msg_type>::decode($buf)?));
400 }
401 };
402 }
403
404 decode_snap_message_variant!(
406 message_id,
407 buf,
408 SnapMessageId::GetAccountRange,
409 GetAccountRange,
410 GetAccountRangeMessage
411 );
412 decode_snap_message_variant!(
413 message_id,
414 buf,
415 SnapMessageId::AccountRange,
416 AccountRange,
417 AccountRangeMessage
418 );
419 decode_snap_message_variant!(
420 message_id,
421 buf,
422 SnapMessageId::GetStorageRanges,
423 GetStorageRanges,
424 GetStorageRangesMessage
425 );
426 decode_snap_message_variant!(
427 message_id,
428 buf,
429 SnapMessageId::StorageRanges,
430 StorageRanges,
431 StorageRangesMessage
432 );
433 decode_snap_message_variant!(
434 message_id,
435 buf,
436 SnapMessageId::GetByteCodes,
437 GetByteCodes,
438 GetByteCodesMessage
439 );
440 decode_snap_message_variant!(
441 message_id,
442 buf,
443 SnapMessageId::ByteCodes,
444 ByteCodes,
445 ByteCodesMessage
446 );
447 decode_snap_message_variant!(
448 message_id,
449 buf,
450 SnapMessageId::GetBlockAccessLists,
451 GetBlockAccessLists,
452 GetBlockAccessListsMessage
453 );
454 decode_snap_message_variant!(
455 message_id,
456 buf,
457 SnapMessageId::BlockAccessLists,
458 BlockAccessLists,
459 BlockAccessListsMessage
460 );
461
462 Err(alloy_rlp::Error::Custom("Unknown message ID"))
463 }
464
465 pub fn decode_versioned(version: SnapVersion, bytes: &[u8]) -> Result<Self, SnapProtocolError> {
470 let (&id, mut body) = bytes.split_first().ok_or(SnapProtocolError::Empty)?;
471 if !version.supports_message_id(id) {
472 return Err(SnapProtocolError::UnsupportedMessageId(id, version));
473 }
474 let msg = Self::decode(id, &mut body)?;
475 if !body.is_empty() {
476 return Err(SnapProtocolError::Rlp(alloy_rlp::Error::UnexpectedLength));
477 }
478 Ok(msg)
479 }
480}
481
482#[cfg(test)]
483mod tests {
484 use super::*;
485 use test_case::test_case;
486
487 fn b256_from_u64(value: u64) -> B256 {
489 B256::left_padding_from(&value.to_be_bytes())
490 }
491
492 fn test_roundtrip(original: SnapProtocolMessage) {
494 let encoded = original.encode();
495
496 assert_eq!(encoded[0], original.message_id() as u8);
498
499 let mut buf = &encoded[1..];
500 let decoded = SnapProtocolMessage::decode(encoded[0], &mut buf).unwrap();
501
502 assert_eq!(decoded, original);
504 }
505
506 #[derive(alloy_rlp::RlpEncodable)]
510 struct GethStorageRequest {
511 request_id: u64,
512 root_hash: B256,
513 account_hashes: Vec<B256>,
514 origin: Bytes,
515 limit: Bytes,
516 response_bytes: u64,
517 }
518
519 #[test]
520 fn test_all_message_roundtrips() {
521 assert_eq!(SnapVersion::V2.message_count(), 10);
522
523 test_roundtrip(SnapProtocolMessage::GetAccountRange(GetAccountRangeMessage {
524 request_id: 42,
525 root_hash: b256_from_u64(123),
526 starting_hash: b256_from_u64(456),
527 limit_hash: b256_from_u64(789),
528 response_bytes: 1024,
529 }));
530
531 test_roundtrip(SnapProtocolMessage::AccountRange(AccountRangeMessage {
532 request_id: 42,
533 accounts: vec![AccountData {
534 hash: b256_from_u64(123),
535 body: Bytes::from(vec![1, 2, 3]),
536 }],
537 proof: vec![Bytes::from(vec![4, 5, 6])],
538 }));
539
540 test_roundtrip(SnapProtocolMessage::GetStorageRanges(GetStorageRangesMessage {
541 request_id: 42,
542 root_hash: b256_from_u64(123),
543 account_hashes: vec![b256_from_u64(456)],
544 starting_hash: b256_from_u64(789).into(),
545 limit_hash: b256_from_u64(101112).into(),
546 response_bytes: 2048,
547 }));
548
549 test_roundtrip(SnapProtocolMessage::GetStorageRanges(GetStorageRangesMessage {
551 request_id: 43,
552 root_hash: b256_from_u64(123),
553 account_hashes: vec![b256_from_u64(456), b256_from_u64(789)],
554 starting_hash: RangeBound::default(),
555 limit_hash: RangeBound::default(),
556 response_bytes: 2048,
557 }));
558
559 test_roundtrip(SnapProtocolMessage::StorageRanges(StorageRangesMessage {
560 request_id: 42,
561 slots: vec![vec![StorageData {
562 hash: b256_from_u64(123),
563 data: Bytes::from(vec![1, 2, 3]),
564 }]],
565 proof: vec![Bytes::from(vec![4, 5, 6])],
566 }));
567
568 test_roundtrip(SnapProtocolMessage::GetByteCodes(GetByteCodesMessage {
569 request_id: 42,
570 hashes: vec![b256_from_u64(123)],
571 response_bytes: 1024,
572 }));
573
574 test_roundtrip(SnapProtocolMessage::ByteCodes(ByteCodesMessage {
575 request_id: 42,
576 codes: vec![Bytes::from(vec![1, 2, 3])],
577 }));
578
579 test_roundtrip(SnapProtocolMessage::GetBlockAccessLists(GetBlockAccessListsMessage {
580 request_id: 42,
581 block_hashes: vec![b256_from_u64(123), b256_from_u64(456)],
582 response_bytes: 4096,
583 }));
584
585 test_roundtrip(SnapProtocolMessage::BlockAccessLists(BlockAccessListsMessage {
586 request_id: 42,
587 block_access_lists: BlockAccessLists(vec![
588 Some(Bytes::from_static(&[alloy_rlp::EMPTY_LIST_CODE])),
589 Some(Bytes::from_static(&[0xc1, alloy_rlp::EMPTY_LIST_CODE])),
590 ]),
591 }));
592 }
593
594 #[test]
595 fn test_unknown_message_id() {
596 let data = Bytes::from(vec![1, 2, 3, 4]);
598 let mut buf = data.as_ref();
599
600 let result = SnapProtocolMessage::decode(255, &mut buf);
602
603 assert!(result.is_err());
604 if let Err(e) = result {
605 assert_eq!(e.to_string(), "Unknown message ID");
606 }
607 }
608
609 #[test]
610 fn test_snap_v2_message_validity() {
611 let v2 = SnapVersion::V2;
612 for id in 0x00..=0x05 {
614 assert!(v2.supports_message_id(id), "snap/2 should accept {id:#x}");
615 }
616 assert!(!v2.supports_message_id(0x06));
618 assert!(!v2.supports_message_id(0x07));
619 assert!(v2.supports_message_id(SnapMessageId::GetBlockAccessLists as u8));
621 assert!(v2.supports_message_id(SnapMessageId::BlockAccessLists as u8));
622 assert!(!v2.supports_message_id(0x0a));
623 assert!(!v2.supports_message_id(0xff));
624 }
625
626 #[test_case(
627 SnapProtocolMessage::GetAccountRange(GetAccountRangeMessage {
628 request_id: 1, root_hash: B256::ZERO, starting_hash: B256::ZERO,
629 limit_hash: B256::ZERO, response_bytes: 0,
630 }), 1, false ; "get_account_range is a request"
631 )]
632 #[test_case(
633 SnapProtocolMessage::AccountRange(AccountRangeMessage {
634 request_id: 2, accounts: vec![], proof: vec![],
635 }), 2, true ; "account_range is a response"
636 )]
637 #[test_case(
638 SnapProtocolMessage::GetStorageRanges(GetStorageRangesMessage {
639 request_id: 3, root_hash: B256::ZERO, account_hashes: vec![],
640 starting_hash: B256::ZERO.into(), limit_hash: B256::ZERO.into(), response_bytes: 0,
641 }), 3, false ; "get_storage_ranges is a request"
642 )]
643 #[test_case(
644 SnapProtocolMessage::StorageRanges(StorageRangesMessage {
645 request_id: 4, slots: vec![], proof: vec![],
646 }), 4, true ; "storage_ranges is a response"
647 )]
648 #[test_case(
649 SnapProtocolMessage::GetByteCodes(GetByteCodesMessage {
650 request_id: 5, hashes: vec![], response_bytes: 0,
651 }), 5, false ; "get_byte_codes is a request"
652 )]
653 #[test_case(
654 SnapProtocolMessage::ByteCodes(ByteCodesMessage { request_id: 6, codes: vec![] }),
655 6, true ; "byte_codes is a response"
656 )]
657 #[test_case(
658 SnapProtocolMessage::GetBlockAccessLists(GetBlockAccessListsMessage {
659 request_id: 7, block_hashes: vec![], response_bytes: 0,
660 }), 7, false ; "get_block_access_lists is a request"
661 )]
662 #[test_case(
663 SnapProtocolMessage::BlockAccessLists(BlockAccessListsMessage {
664 request_id: 8, block_access_lists: BlockAccessLists(vec![]),
665 }), 8, true ; "block_access_lists is a response"
666 )]
667 fn request_id_and_is_response(msg: SnapProtocolMessage, expected_id: u64, is_response: bool) {
668 assert_eq!(msg.request_id(), expected_id);
669 assert_eq!(msg.is_response(), is_response);
670 }
671
672 #[test_case(
673 SnapProtocolMessage::GetAccountRange(GetAccountRangeMessage {
674 request_id: 1, root_hash: B256::ZERO, starting_hash: B256::ZERO,
675 limit_hash: B256::ZERO, response_bytes: 0,
676 }) ; "get_account_range"
677 )]
678 #[test_case(
679 SnapProtocolMessage::AccountRange(AccountRangeMessage {
680 request_id: 1, accounts: vec![], proof: vec![],
681 }) ; "account_range"
682 )]
683 #[test_case(
684 SnapProtocolMessage::GetStorageRanges(GetStorageRangesMessage {
685 request_id: 1, root_hash: B256::ZERO, account_hashes: vec![],
686 starting_hash: B256::ZERO.into(), limit_hash: B256::ZERO.into(), response_bytes: 0,
687 }) ; "get_storage_ranges"
688 )]
689 #[test_case(
690 SnapProtocolMessage::StorageRanges(StorageRangesMessage {
691 request_id: 1, slots: vec![], proof: vec![],
692 }) ; "storage_ranges"
693 )]
694 #[test_case(
695 SnapProtocolMessage::GetByteCodes(GetByteCodesMessage {
696 request_id: 1, hashes: vec![], response_bytes: 0,
697 }) ; "get_byte_codes"
698 )]
699 #[test_case(
700 SnapProtocolMessage::ByteCodes(ByteCodesMessage { request_id: 1, codes: vec![] }) ;
701 "byte_codes"
702 )]
703 #[test_case(
704 SnapProtocolMessage::GetBlockAccessLists(GetBlockAccessListsMessage {
705 request_id: 1, block_hashes: vec![], response_bytes: 0,
706 }) ; "get_block_access_lists"
707 )]
708 #[test_case(
709 SnapProtocolMessage::BlockAccessLists(BlockAccessListsMessage {
710 request_id: 1, block_access_lists: BlockAccessLists(vec![]),
711 }) ; "block_access_lists"
712 )]
713 fn per_variant_request_id_and_round_trip(mut msg: SnapProtocolMessage) {
714 msg.set_request_id(42);
716 assert_eq!(msg.request_id(), 42);
717
718 let decoded =
720 SnapProtocolMessage::decode_versioned(SnapVersion::V2, &msg.encode()).unwrap();
721 assert_eq!(decoded, msg);
722 }
723
724 #[test]
725 fn decode_versioned_rejects_empty() {
726 assert!(matches!(
728 SnapProtocolMessage::decode_versioned(SnapVersion::V2, &[]),
729 Err(SnapProtocolError::Empty)
730 ));
731 }
732
733 #[test]
734 fn decode_versioned_rejects_trie_node_ids_in_v2() {
735 for id in [0x06u8, 0x07] {
738 assert!(matches!(
739 SnapProtocolMessage::decode_versioned(SnapVersion::V2, &[id]),
740 Err(SnapProtocolError::UnsupportedMessageId(got, SnapVersion::V2)) if got == id
741 ));
742 }
743 }
744
745 #[test]
746 fn decode_versioned_reports_malformed_body() {
747 assert!(matches!(
750 SnapProtocolMessage::decode_versioned(SnapVersion::V2, &[0x08, 0xff]),
751 Err(SnapProtocolError::Rlp(_))
752 ));
753 }
754
755 #[test]
756 fn decode_versioned_rejects_trailing_bytes() {
757 let original = SnapProtocolMessage::GetBlockAccessLists(GetBlockAccessListsMessage {
760 request_id: 7,
761 block_hashes: vec![b256_from_u64(1)],
762 response_bytes: 1024,
763 });
764 let mut framed = original.encode().to_vec();
765 framed.push(0xff);
766 assert!(matches!(
767 SnapProtocolMessage::decode_versioned(SnapVersion::V2, &framed),
768 Err(SnapProtocolError::Rlp(alloy_rlp::Error::UnexpectedLength))
769 ));
770 }
771
772 #[test]
773 fn get_storage_ranges_decodes_geths_empty_origin_and_limit() {
774 let body = alloy_rlp::encode(GethStorageRequest {
775 request_id: 21,
776 root_hash: B256::ZERO,
777 account_hashes: vec![B256::repeat_byte(1), B256::repeat_byte(2)],
778 origin: Bytes::new(),
779 limit: Bytes::new(),
780 response_bytes: 1024,
781 });
782 let mut framed = vec![SnapMessageId::GetStorageRanges as u8];
783 framed.extend_from_slice(&body);
784
785 let decoded = SnapProtocolMessage::decode_versioned(SnapVersion::V2, &framed).unwrap();
786 let SnapProtocolMessage::GetStorageRanges(msg) = decoded else {
787 panic!("expected a GetStorageRanges message");
788 };
789 assert_eq!(msg.starting_hash.unwrap_or(B256::ZERO), B256::ZERO);
790 assert_eq!(msg.limit_hash.unwrap_or(B256::repeat_byte(0xff)), B256::repeat_byte(0xff));
791 }
792}