1use crate::BlockAccessLists;
9use alloc::vec::Vec;
10use alloy_primitives::{Bytes, B256, KECCAK256_EMPTY, U256};
11use alloy_rlp::{BufMut, Decodable, Encodable, RlpDecodable, RlpEncodable};
12use alloy_trie::{TrieAccount, EMPTY_ROOT_HASH};
13use reth_codecs_derive::add_arbitrary_tests;
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Hash)]
17#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
18#[repr(u8)]
19pub enum SnapVersion {
20 #[default]
22 V2 = 2,
23}
24
25impl SnapVersion {
26 pub const fn message_count(self) -> u8 {
29 match self {
30 Self::V2 => 10,
31 }
32 }
33
34 pub const fn supports_message_id(self, id: u8) -> bool {
39 match self {
40 Self::V2 => {
42 id <= SnapMessageId::ByteCodes as u8 ||
43 id == SnapMessageId::GetBlockAccessLists as u8 ||
44 id == SnapMessageId::BlockAccessLists as u8
45 }
46 }
47 }
48}
49
50#[derive(Debug, Clone, Copy, PartialEq, Eq)]
52pub enum SnapMessageId {
53 GetAccountRange = 0x00,
55 AccountRange = 0x01,
58 GetStorageRanges = 0x02,
60 StorageRanges = 0x03,
62 GetByteCodes = 0x04,
64 ByteCodes = 0x05,
66 GetBlockAccessLists = 0x08,
68 BlockAccessLists = 0x09,
70}
71
72impl SnapMessageId {
73 pub const fn response(self) -> Option<Self> {
76 match self {
77 Self::GetAccountRange => Some(Self::AccountRange),
78 Self::GetStorageRanges => Some(Self::StorageRanges),
79 Self::GetByteCodes => Some(Self::ByteCodes),
80 Self::GetBlockAccessLists => Some(Self::BlockAccessLists),
81 Self::AccountRange | Self::StorageRanges | Self::ByteCodes | Self::BlockAccessLists => {
82 None
83 }
84 }
85 }
86}
87
88#[derive(Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)]
91#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
92#[add_arbitrary_tests(rlp)]
93pub struct GetAccountRangeMessage {
94 pub request_id: u64,
96 pub root_hash: B256,
98 pub starting_hash: B256,
100 pub limit_hash: B256,
102 pub response_bytes: u64,
104}
105
106#[derive(Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)]
108#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
109#[add_arbitrary_tests(rlp)]
110pub struct AccountData {
111 pub hash: B256,
113 pub body: Bytes,
115}
116
117impl AccountData {
118 pub fn from_trie_account(hash: B256, account: &TrieAccount) -> Self {
120 let body = alloy_rlp::encode(SlimAccountBodyRef {
121 nonce: account.nonce,
122 balance: account.balance,
123 storage_root: SlimAccountBodyRef::shorten(&account.storage_root, EMPTY_ROOT_HASH),
124 code_hash: SlimAccountBodyRef::shorten(&account.code_hash, KECCAK256_EMPTY),
125 });
126 Self { hash, body: body.into() }
127 }
128
129 pub fn trie_account(&self) -> alloy_rlp::Result<TrieAccount> {
134 let slim = alloy_rlp::decode_exact::<SlimAccountBody>(&self.body)?;
135
136 Ok(TrieAccount {
137 nonce: slim.nonce,
138 balance: slim.balance,
139 storage_root: SlimAccountBody::restore(&slim.storage_root, EMPTY_ROOT_HASH)?,
140 code_hash: SlimAccountBody::restore(&slim.code_hash, KECCAK256_EMPTY)?,
141 })
142 }
143}
144
145#[derive(Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)]
148#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
149#[add_arbitrary_tests(rlp)]
150pub struct AccountRangeMessage {
151 pub request_id: u64,
153 pub accounts: Vec<AccountData>,
155 pub proof: Vec<Bytes>,
157}
158
159#[derive(Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)]
162#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
163#[add_arbitrary_tests(rlp)]
164pub struct GetStorageRangesMessage {
165 pub request_id: u64,
167 pub root_hash: B256,
169 pub account_hashes: Vec<B256>,
171 pub starting_hash: RangeBound,
174 pub limit_hash: RangeBound,
177 pub response_bytes: u64,
179}
180
181#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
185#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
186pub struct RangeBound(Option<B256>);
187
188impl RangeBound {
189 pub const fn unwrap_or(self, default: B256) -> B256 {
191 match self.0 {
192 Some(hash) => hash,
193 None => default,
194 }
195 }
196}
197
198impl From<B256> for RangeBound {
199 fn from(hash: B256) -> Self {
200 Self(Some(hash))
201 }
202}
203
204impl Encodable for RangeBound {
205 fn encode(&self, out: &mut dyn BufMut) {
206 match self.0 {
207 Some(hash) => hash.encode(out),
208 None => Bytes::new().encode(out),
209 }
210 }
211
212 fn length(&self) -> usize {
213 match self.0 {
214 Some(hash) => hash.length(),
215 None => Bytes::new().length(),
216 }
217 }
218}
219
220impl Decodable for RangeBound {
221 fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
222 let bytes = Bytes::decode(buf)?;
223 match bytes.len() {
224 0 => Ok(Self(None)),
225 32 => Ok(Self(Some(B256::from_slice(&bytes)))),
226 _ => Err(alloy_rlp::Error::UnexpectedLength),
227 }
228 }
229}
230
231#[derive(Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)]
233#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
234#[add_arbitrary_tests(rlp)]
235pub struct StorageData {
236 pub hash: B256,
238 pub data: Bytes,
240}
241
242impl StorageData {
243 pub fn from_value(hash: B256, value: U256) -> Self {
245 Self { hash, data: alloy_rlp::encode(value).into() }
246 }
247
248 pub fn value(&self) -> alloy_rlp::Result<U256> {
250 alloy_rlp::decode_exact(&self.data)
251 }
252}
253
254#[derive(Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)]
259#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
260#[add_arbitrary_tests(rlp)]
261pub struct StorageRangesMessage {
262 pub request_id: u64,
264 pub slots: Vec<Vec<StorageData>>,
266 pub proof: Vec<Bytes>,
268}
269
270#[derive(Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)]
273#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
274#[add_arbitrary_tests(rlp)]
275pub struct GetByteCodesMessage {
276 pub request_id: u64,
278 pub hashes: Vec<B256>,
280 pub response_bytes: u64,
282}
283
284#[derive(Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)]
287#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
288#[add_arbitrary_tests(rlp)]
289pub struct ByteCodesMessage {
290 pub request_id: u64,
292 pub codes: Vec<Bytes>,
294}
295
296#[derive(Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)]
298#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
299#[add_arbitrary_tests(rlp)]
300pub struct GetBlockAccessListsMessage {
301 pub request_id: u64,
303 pub block_hashes: Vec<B256>,
305 pub response_bytes: u64,
307}
308
309#[derive(Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)]
311#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
312#[add_arbitrary_tests(rlp)]
313pub struct BlockAccessListsMessage {
314 pub request_id: u64,
316 pub block_access_lists: BlockAccessLists,
318}
319
320#[derive(Debug, Clone, PartialEq, Eq)]
322pub enum SnapProtocolMessage {
323 GetAccountRange(GetAccountRangeMessage),
325 AccountRange(AccountRangeMessage),
327 GetStorageRanges(GetStorageRangesMessage),
329 StorageRanges(StorageRangesMessage),
331 GetByteCodes(GetByteCodesMessage),
333 ByteCodes(ByteCodesMessage),
335 GetBlockAccessLists(GetBlockAccessListsMessage),
337 BlockAccessLists(BlockAccessListsMessage),
339}
340
341#[derive(thiserror::Error, Debug)]
343pub enum SnapProtocolError {
344 #[error("empty snap message")]
346 Empty,
347 #[error("message id {0:#x} is invalid for snap/{1:?}")]
350 UnsupportedMessageId(u8, SnapVersion),
351 #[error("RLP error: {0}")]
353 Rlp(#[from] alloy_rlp::Error),
354}
355
356impl SnapProtocolMessage {
357 pub const fn message_id(&self) -> SnapMessageId {
361 match self {
362 Self::GetAccountRange(_) => SnapMessageId::GetAccountRange,
363 Self::AccountRange(_) => SnapMessageId::AccountRange,
364 Self::GetStorageRanges(_) => SnapMessageId::GetStorageRanges,
365 Self::StorageRanges(_) => SnapMessageId::StorageRanges,
366 Self::GetByteCodes(_) => SnapMessageId::GetByteCodes,
367 Self::ByteCodes(_) => SnapMessageId::ByteCodes,
368 Self::GetBlockAccessLists(_) => SnapMessageId::GetBlockAccessLists,
369 Self::BlockAccessLists(_) => SnapMessageId::BlockAccessLists,
370 }
371 }
372
373 pub const fn request_id(&self) -> u64 {
375 match self {
376 Self::GetAccountRange(m) => m.request_id,
377 Self::AccountRange(m) => m.request_id,
378 Self::GetStorageRanges(m) => m.request_id,
379 Self::StorageRanges(m) => m.request_id,
380 Self::GetByteCodes(m) => m.request_id,
381 Self::ByteCodes(m) => m.request_id,
382 Self::GetBlockAccessLists(m) => m.request_id,
383 Self::BlockAccessLists(m) => m.request_id,
384 }
385 }
386
387 pub const fn is_response(&self) -> bool {
389 matches!(
390 self,
391 Self::AccountRange(_) |
392 Self::StorageRanges(_) |
393 Self::ByteCodes(_) |
394 Self::BlockAccessLists(_)
395 )
396 }
397
398 pub const fn set_request_id(&mut self, request_id: u64) {
401 match self {
402 Self::GetAccountRange(m) => m.request_id = request_id,
403 Self::AccountRange(m) => m.request_id = request_id,
404 Self::GetStorageRanges(m) => m.request_id = request_id,
405 Self::StorageRanges(m) => m.request_id = request_id,
406 Self::GetByteCodes(m) => m.request_id = request_id,
407 Self::ByteCodes(m) => m.request_id = request_id,
408 Self::GetBlockAccessLists(m) => m.request_id = request_id,
409 Self::BlockAccessLists(m) => m.request_id = request_id,
410 }
411 }
412
413 pub fn encode(&self) -> Bytes {
415 let mut buf = Vec::new();
416 buf.push(self.message_id() as u8);
418
419 match self {
421 Self::GetAccountRange(msg) => msg.encode(&mut buf),
422 Self::AccountRange(msg) => msg.encode(&mut buf),
423 Self::GetStorageRanges(msg) => msg.encode(&mut buf),
424 Self::StorageRanges(msg) => msg.encode(&mut buf),
425 Self::GetByteCodes(msg) => msg.encode(&mut buf),
426 Self::ByteCodes(msg) => msg.encode(&mut buf),
427 Self::GetBlockAccessLists(msg) => msg.encode(&mut buf),
428 Self::BlockAccessLists(msg) => msg.encode(&mut buf),
429 }
430
431 Bytes::from(buf)
432 }
433
434 pub fn decode(message_id: u8, buf: &mut &[u8]) -> Result<Self, alloy_rlp::Error> {
436 macro_rules! decode_snap_message_variant {
438 ($message_id:expr, $buf:expr, $id:expr, $variant:ident, $msg_type:ty) => {
439 if $message_id == $id as u8 {
440 return Ok(Self::$variant(<$msg_type>::decode($buf)?));
441 }
442 };
443 }
444
445 decode_snap_message_variant!(
447 message_id,
448 buf,
449 SnapMessageId::GetAccountRange,
450 GetAccountRange,
451 GetAccountRangeMessage
452 );
453 decode_snap_message_variant!(
454 message_id,
455 buf,
456 SnapMessageId::AccountRange,
457 AccountRange,
458 AccountRangeMessage
459 );
460 decode_snap_message_variant!(
461 message_id,
462 buf,
463 SnapMessageId::GetStorageRanges,
464 GetStorageRanges,
465 GetStorageRangesMessage
466 );
467 decode_snap_message_variant!(
468 message_id,
469 buf,
470 SnapMessageId::StorageRanges,
471 StorageRanges,
472 StorageRangesMessage
473 );
474 decode_snap_message_variant!(
475 message_id,
476 buf,
477 SnapMessageId::GetByteCodes,
478 GetByteCodes,
479 GetByteCodesMessage
480 );
481 decode_snap_message_variant!(
482 message_id,
483 buf,
484 SnapMessageId::ByteCodes,
485 ByteCodes,
486 ByteCodesMessage
487 );
488 decode_snap_message_variant!(
489 message_id,
490 buf,
491 SnapMessageId::GetBlockAccessLists,
492 GetBlockAccessLists,
493 GetBlockAccessListsMessage
494 );
495 decode_snap_message_variant!(
496 message_id,
497 buf,
498 SnapMessageId::BlockAccessLists,
499 BlockAccessLists,
500 BlockAccessListsMessage
501 );
502
503 Err(alloy_rlp::Error::Custom("Unknown message ID"))
504 }
505
506 pub fn decode_versioned(version: SnapVersion, bytes: &[u8]) -> Result<Self, SnapProtocolError> {
511 let (&id, mut body) = bytes.split_first().ok_or(SnapProtocolError::Empty)?;
512 if !version.supports_message_id(id) {
513 return Err(SnapProtocolError::UnsupportedMessageId(id, version));
514 }
515 let msg = Self::decode(id, &mut body)?;
516 if !body.is_empty() {
517 return Err(SnapProtocolError::Rlp(alloy_rlp::Error::UnexpectedLength));
518 }
519 Ok(msg)
520 }
521}
522
523#[derive(RlpDecodable)]
525struct SlimAccountBody {
526 nonce: u64,
528 balance: U256,
530 storage_root: Bytes,
532 code_hash: Bytes,
534}
535
536impl SlimAccountBody {
537 fn restore(value: &[u8], empty: B256) -> alloy_rlp::Result<B256> {
539 match value {
540 [] => Ok(empty),
541 _ => B256::try_from(value).map_err(|_| alloy_rlp::Error::UnexpectedLength),
542 }
543 }
544}
545
546#[derive(RlpEncodable)]
548struct SlimAccountBodyRef<'a> {
549 nonce: u64,
551 balance: U256,
553 storage_root: &'a [u8],
555 code_hash: &'a [u8],
557}
558
559impl<'a> SlimAccountBodyRef<'a> {
560 fn shorten(value: &'a B256, empty: B256) -> &'a [u8] {
562 if *value == empty {
563 &[]
564 } else {
565 value.as_slice()
566 }
567 }
568}
569
570#[cfg(test)]
571mod tests {
572 use super::*;
573 use test_case::test_case;
574
575 fn b256_from_u64(value: u64) -> B256 {
577 B256::left_padding_from(&value.to_be_bytes())
578 }
579
580 fn test_roundtrip(original: SnapProtocolMessage) {
582 let encoded = original.encode();
583
584 assert_eq!(encoded[0], original.message_id() as u8);
586
587 let mut buf = &encoded[1..];
588 let decoded = SnapProtocolMessage::decode(encoded[0], &mut buf).unwrap();
589
590 assert_eq!(decoded, original);
592 }
593
594 #[derive(alloy_rlp::RlpEncodable)]
598 struct GethStorageRequest {
599 request_id: u64,
600 root_hash: B256,
601 account_hashes: Vec<B256>,
602 origin: Bytes,
603 limit: Bytes,
604 response_bytes: u64,
605 }
606
607 #[test]
608 fn test_all_message_roundtrips() {
609 assert_eq!(SnapVersion::V2.message_count(), 10);
610
611 test_roundtrip(SnapProtocolMessage::GetAccountRange(GetAccountRangeMessage {
612 request_id: 42,
613 root_hash: b256_from_u64(123),
614 starting_hash: b256_from_u64(456),
615 limit_hash: b256_from_u64(789),
616 response_bytes: 1024,
617 }));
618
619 test_roundtrip(SnapProtocolMessage::AccountRange(AccountRangeMessage {
620 request_id: 42,
621 accounts: vec![AccountData {
622 hash: b256_from_u64(123),
623 body: Bytes::from(vec![1, 2, 3]),
624 }],
625 proof: vec![Bytes::from(vec![4, 5, 6])],
626 }));
627
628 test_roundtrip(SnapProtocolMessage::GetStorageRanges(GetStorageRangesMessage {
629 request_id: 42,
630 root_hash: b256_from_u64(123),
631 account_hashes: vec![b256_from_u64(456)],
632 starting_hash: b256_from_u64(789).into(),
633 limit_hash: b256_from_u64(101112).into(),
634 response_bytes: 2048,
635 }));
636
637 test_roundtrip(SnapProtocolMessage::GetStorageRanges(GetStorageRangesMessage {
639 request_id: 43,
640 root_hash: b256_from_u64(123),
641 account_hashes: vec![b256_from_u64(456), b256_from_u64(789)],
642 starting_hash: RangeBound::default(),
643 limit_hash: RangeBound::default(),
644 response_bytes: 2048,
645 }));
646
647 test_roundtrip(SnapProtocolMessage::StorageRanges(StorageRangesMessage {
648 request_id: 42,
649 slots: vec![vec![StorageData {
650 hash: b256_from_u64(123),
651 data: Bytes::from(vec![1, 2, 3]),
652 }]],
653 proof: vec![Bytes::from(vec![4, 5, 6])],
654 }));
655
656 test_roundtrip(SnapProtocolMessage::GetByteCodes(GetByteCodesMessage {
657 request_id: 42,
658 hashes: vec![b256_from_u64(123)],
659 response_bytes: 1024,
660 }));
661
662 test_roundtrip(SnapProtocolMessage::ByteCodes(ByteCodesMessage {
663 request_id: 42,
664 codes: vec![Bytes::from(vec![1, 2, 3])],
665 }));
666
667 test_roundtrip(SnapProtocolMessage::GetBlockAccessLists(GetBlockAccessListsMessage {
668 request_id: 42,
669 block_hashes: vec![b256_from_u64(123), b256_from_u64(456)],
670 response_bytes: 4096,
671 }));
672
673 test_roundtrip(SnapProtocolMessage::BlockAccessLists(BlockAccessListsMessage {
674 request_id: 42,
675 block_access_lists: BlockAccessLists(vec![
676 Some(Bytes::from_static(&[alloy_rlp::EMPTY_LIST_CODE])),
677 Some(Bytes::from_static(&[0xc1, alloy_rlp::EMPTY_LIST_CODE])),
678 ]),
679 }));
680 }
681
682 #[test]
683 fn test_unknown_message_id() {
684 let data = Bytes::from(vec![1, 2, 3, 4]);
686 let mut buf = data.as_ref();
687
688 let result = SnapProtocolMessage::decode(255, &mut buf);
690
691 assert!(result.is_err());
692 if let Err(e) = result {
693 assert_eq!(e.to_string(), "Unknown message ID");
694 }
695 }
696
697 #[test]
698 fn test_snap_v2_message_validity() {
699 let v2 = SnapVersion::V2;
700 for id in 0x00..=0x05 {
702 assert!(v2.supports_message_id(id), "snap/2 should accept {id:#x}");
703 }
704 assert!(!v2.supports_message_id(0x06));
706 assert!(!v2.supports_message_id(0x07));
707 assert!(v2.supports_message_id(SnapMessageId::GetBlockAccessLists as u8));
709 assert!(v2.supports_message_id(SnapMessageId::BlockAccessLists as u8));
710 assert!(!v2.supports_message_id(0x0a));
711 assert!(!v2.supports_message_id(0xff));
712 }
713
714 #[test_case(
715 SnapProtocolMessage::GetAccountRange(GetAccountRangeMessage {
716 request_id: 1, root_hash: B256::ZERO, starting_hash: B256::ZERO,
717 limit_hash: B256::ZERO, response_bytes: 0,
718 }), 1, false ; "get_account_range is a request"
719 )]
720 #[test_case(
721 SnapProtocolMessage::AccountRange(AccountRangeMessage {
722 request_id: 2, accounts: vec![], proof: vec![],
723 }), 2, true ; "account_range is a response"
724 )]
725 #[test_case(
726 SnapProtocolMessage::GetStorageRanges(GetStorageRangesMessage {
727 request_id: 3, root_hash: B256::ZERO, account_hashes: vec![],
728 starting_hash: B256::ZERO.into(), limit_hash: B256::ZERO.into(), response_bytes: 0,
729 }), 3, false ; "get_storage_ranges is a request"
730 )]
731 #[test_case(
732 SnapProtocolMessage::StorageRanges(StorageRangesMessage {
733 request_id: 4, slots: vec![], proof: vec![],
734 }), 4, true ; "storage_ranges is a response"
735 )]
736 #[test_case(
737 SnapProtocolMessage::GetByteCodes(GetByteCodesMessage {
738 request_id: 5, hashes: vec![], response_bytes: 0,
739 }), 5, false ; "get_byte_codes is a request"
740 )]
741 #[test_case(
742 SnapProtocolMessage::ByteCodes(ByteCodesMessage { request_id: 6, codes: vec![] }),
743 6, true ; "byte_codes is a response"
744 )]
745 #[test_case(
746 SnapProtocolMessage::GetBlockAccessLists(GetBlockAccessListsMessage {
747 request_id: 7, block_hashes: vec![], response_bytes: 0,
748 }), 7, false ; "get_block_access_lists is a request"
749 )]
750 #[test_case(
751 SnapProtocolMessage::BlockAccessLists(BlockAccessListsMessage {
752 request_id: 8, block_access_lists: BlockAccessLists(vec![]),
753 }), 8, true ; "block_access_lists is a response"
754 )]
755 fn request_id_and_is_response(msg: SnapProtocolMessage, expected_id: u64, is_response: bool) {
756 assert_eq!(msg.request_id(), expected_id);
757 assert_eq!(msg.is_response(), is_response);
758 }
759
760 #[test_case(
761 SnapProtocolMessage::GetAccountRange(GetAccountRangeMessage {
762 request_id: 1, root_hash: B256::ZERO, starting_hash: B256::ZERO,
763 limit_hash: B256::ZERO, response_bytes: 0,
764 }) ; "get_account_range"
765 )]
766 #[test_case(
767 SnapProtocolMessage::AccountRange(AccountRangeMessage {
768 request_id: 1, accounts: vec![], proof: vec![],
769 }) ; "account_range"
770 )]
771 #[test_case(
772 SnapProtocolMessage::GetStorageRanges(GetStorageRangesMessage {
773 request_id: 1, root_hash: B256::ZERO, account_hashes: vec![],
774 starting_hash: B256::ZERO.into(), limit_hash: B256::ZERO.into(), response_bytes: 0,
775 }) ; "get_storage_ranges"
776 )]
777 #[test_case(
778 SnapProtocolMessage::StorageRanges(StorageRangesMessage {
779 request_id: 1, slots: vec![], proof: vec![],
780 }) ; "storage_ranges"
781 )]
782 #[test_case(
783 SnapProtocolMessage::GetByteCodes(GetByteCodesMessage {
784 request_id: 1, hashes: vec![], response_bytes: 0,
785 }) ; "get_byte_codes"
786 )]
787 #[test_case(
788 SnapProtocolMessage::ByteCodes(ByteCodesMessage { request_id: 1, codes: vec![] }) ;
789 "byte_codes"
790 )]
791 #[test_case(
792 SnapProtocolMessage::GetBlockAccessLists(GetBlockAccessListsMessage {
793 request_id: 1, block_hashes: vec![], response_bytes: 0,
794 }) ; "get_block_access_lists"
795 )]
796 #[test_case(
797 SnapProtocolMessage::BlockAccessLists(BlockAccessListsMessage {
798 request_id: 1, block_access_lists: BlockAccessLists(vec![]),
799 }) ; "block_access_lists"
800 )]
801 fn per_variant_request_id_and_round_trip(mut msg: SnapProtocolMessage) {
802 msg.set_request_id(42);
804 assert_eq!(msg.request_id(), 42);
805
806 let decoded =
808 SnapProtocolMessage::decode_versioned(SnapVersion::V2, &msg.encode()).unwrap();
809 assert_eq!(decoded, msg);
810 }
811
812 #[test]
813 fn decode_versioned_rejects_empty() {
814 assert!(matches!(
816 SnapProtocolMessage::decode_versioned(SnapVersion::V2, &[]),
817 Err(SnapProtocolError::Empty)
818 ));
819 }
820
821 #[test]
822 fn decode_versioned_rejects_trie_node_ids_in_v2() {
823 for id in [0x06u8, 0x07] {
826 assert!(matches!(
827 SnapProtocolMessage::decode_versioned(SnapVersion::V2, &[id]),
828 Err(SnapProtocolError::UnsupportedMessageId(got, SnapVersion::V2)) if got == id
829 ));
830 }
831 }
832
833 #[test]
834 fn decode_versioned_reports_malformed_body() {
835 assert!(matches!(
838 SnapProtocolMessage::decode_versioned(SnapVersion::V2, &[0x08, 0xff]),
839 Err(SnapProtocolError::Rlp(_))
840 ));
841 }
842
843 #[test]
844 fn decode_versioned_rejects_trailing_bytes() {
845 let original = SnapProtocolMessage::GetBlockAccessLists(GetBlockAccessListsMessage {
848 request_id: 7,
849 block_hashes: vec![b256_from_u64(1)],
850 response_bytes: 1024,
851 });
852 let mut framed = original.encode().to_vec();
853 framed.push(0xff);
854 assert!(matches!(
855 SnapProtocolMessage::decode_versioned(SnapVersion::V2, &framed),
856 Err(SnapProtocolError::Rlp(alloy_rlp::Error::UnexpectedLength))
857 ));
858 }
859
860 #[test]
861 fn get_storage_ranges_decodes_geths_empty_origin_and_limit() {
862 let body = alloy_rlp::encode(GethStorageRequest {
863 request_id: 21,
864 root_hash: B256::ZERO,
865 account_hashes: vec![B256::repeat_byte(1), B256::repeat_byte(2)],
866 origin: Bytes::new(),
867 limit: Bytes::new(),
868 response_bytes: 1024,
869 });
870 let mut framed = vec![SnapMessageId::GetStorageRanges as u8];
871 framed.extend_from_slice(&body);
872
873 let decoded = SnapProtocolMessage::decode_versioned(SnapVersion::V2, &framed).unwrap();
874 let SnapProtocolMessage::GetStorageRanges(msg) = decoded else {
875 panic!("expected a GetStorageRanges message");
876 };
877 assert_eq!(msg.starting_hash.unwrap_or(B256::ZERO), B256::ZERO);
878 assert_eq!(msg.limit_hash.unwrap_or(B256::repeat_byte(0xff)), B256::repeat_byte(0xff));
879 }
880
881 fn trie_account(storage_root: B256, code_hash: B256) -> TrieAccount {
882 TrieAccount { nonce: 7, balance: U256::from(42), storage_root, code_hash }
883 }
884
885 #[test]
886 fn slim_body_elides_empty_storage_and_code() {
887 let account = trie_account(EMPTY_ROOT_HASH, KECCAK256_EMPTY);
888 let encoded = AccountData::from_trie_account(B256::repeat_byte(1), &account);
889
890 let body = SlimAccountBody::decode(&mut encoded.body.as_ref()).unwrap();
891 assert!(body.storage_root.is_empty());
892 assert!(body.code_hash.is_empty());
893 assert_eq!(encoded.trie_account().unwrap(), account);
894 }
895
896 #[test]
897 fn slim_body_keeps_non_default_storage_and_code() {
898 let account = trie_account(B256::repeat_byte(2), B256::repeat_byte(3));
899 let encoded = AccountData::from_trie_account(B256::repeat_byte(1), &account);
900
901 let body = SlimAccountBody::decode(&mut encoded.body.as_ref()).unwrap();
902 assert_eq!(body.storage_root.len(), 32);
903 assert_eq!(body.code_hash.len(), 32);
904 assert_eq!(encoded.trie_account().unwrap(), account);
905 }
906
907 #[test]
908 fn slim_body_rejects_field_lengths_the_encoding_never_produces() {
909 let truncated = Bytes::from_static(&[0xaa; 16]);
912
913 assert!(SlimAccountBody::restore(&truncated, EMPTY_ROOT_HASH).is_err());
914 }
915
916 #[test]
917 fn slim_body_rejects_trailing_bytes() {
918 let account = trie_account(EMPTY_ROOT_HASH, KECCAK256_EMPTY);
919 let mut encoded = AccountData::from_trie_account(B256::repeat_byte(1), &account);
920 encoded.body = [encoded.body.as_ref(), &[0x00]].concat().into();
921
922 assert!(encoded.trie_account().is_err());
923 }
924
925 #[test]
926 fn storage_data_carries_the_trie_leaf_encoding() {
927 let value = U256::from(1234);
928 let slot = StorageData::from_value(B256::repeat_byte(4), value);
929
930 assert_eq!(slot.data.as_ref(), alloy_rlp::encode(value));
933 assert_eq!(slot.value().unwrap(), value);
934 }
935
936 #[test]
937 fn storage_data_rejects_trailing_bytes() {
938 let mut slot = StorageData::from_value(B256::repeat_byte(4), U256::from(1));
939 slot.data = [slot.data.as_ref(), &[0x00]].concat().into();
940
941 assert!(slot.value().is_err());
942 }
943}