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 pub fn into_trie_entry(self) -> alloy_rlp::Result<(B256, TrieAccount)> {
146 let account = self.trie_account()?;
147 Ok((self.hash, account))
148 }
149}
150
151#[derive(Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)]
154#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
155#[add_arbitrary_tests(rlp)]
156pub struct AccountRangeMessage {
157 pub request_id: u64,
159 pub accounts: Vec<AccountData>,
161 pub proof: Vec<Bytes>,
163}
164
165#[derive(Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)]
168#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
169#[add_arbitrary_tests(rlp)]
170pub struct GetStorageRangesMessage {
171 pub request_id: u64,
173 pub root_hash: B256,
175 pub account_hashes: Vec<B256>,
177 pub starting_hash: RangeBound,
180 pub limit_hash: RangeBound,
183 pub response_bytes: u64,
185}
186
187#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
191#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
192pub struct RangeBound(Option<B256>);
193
194impl RangeBound {
195 pub const fn unwrap_or(self, default: B256) -> B256 {
197 match self.0 {
198 Some(hash) => hash,
199 None => default,
200 }
201 }
202}
203
204impl From<B256> for RangeBound {
205 fn from(hash: B256) -> Self {
206 Self(Some(hash))
207 }
208}
209
210impl Encodable for RangeBound {
211 fn encode(&self, out: &mut dyn BufMut) {
212 match self.0 {
213 Some(hash) => hash.encode(out),
214 None => Bytes::new().encode(out),
215 }
216 }
217
218 fn length(&self) -> usize {
219 match self.0 {
220 Some(hash) => hash.length(),
221 None => Bytes::new().length(),
222 }
223 }
224}
225
226impl Decodable for RangeBound {
227 fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
228 let bytes = Bytes::decode(buf)?;
229 match bytes.len() {
230 0 => Ok(Self(None)),
231 32 => Ok(Self(Some(B256::from_slice(&bytes)))),
232 _ => Err(alloy_rlp::Error::UnexpectedLength),
233 }
234 }
235}
236
237#[derive(Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)]
239#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
240#[add_arbitrary_tests(rlp)]
241pub struct StorageData {
242 pub hash: B256,
244 pub data: Bytes,
246}
247
248impl StorageData {
249 pub fn from_value(hash: B256, value: U256) -> Self {
251 Self { hash, data: alloy_rlp::encode(value).into() }
252 }
253
254 pub fn value(&self) -> alloy_rlp::Result<U256> {
256 alloy_rlp::decode_exact(&self.data)
257 }
258}
259
260#[derive(Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)]
265#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
266#[add_arbitrary_tests(rlp)]
267pub struct StorageRangesMessage {
268 pub request_id: u64,
270 pub slots: Vec<Vec<StorageData>>,
272 pub proof: Vec<Bytes>,
274}
275
276#[derive(Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)]
279#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
280#[add_arbitrary_tests(rlp)]
281pub struct GetByteCodesMessage {
282 pub request_id: u64,
284 pub hashes: Vec<B256>,
286 pub response_bytes: u64,
288}
289
290#[derive(Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)]
293#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
294#[add_arbitrary_tests(rlp)]
295pub struct ByteCodesMessage {
296 pub request_id: u64,
298 pub codes: Vec<Bytes>,
300}
301
302#[derive(Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)]
304#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
305#[add_arbitrary_tests(rlp)]
306pub struct GetBlockAccessListsMessage {
307 pub request_id: u64,
309 pub block_hashes: Vec<B256>,
311 pub response_bytes: u64,
313}
314
315#[derive(Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)]
317#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
318#[add_arbitrary_tests(rlp)]
319pub struct BlockAccessListsMessage {
320 pub request_id: u64,
322 pub block_access_lists: BlockAccessLists,
324}
325
326#[derive(Debug, Clone, PartialEq, Eq)]
328pub enum SnapProtocolMessage {
329 GetAccountRange(GetAccountRangeMessage),
331 AccountRange(AccountRangeMessage),
333 GetStorageRanges(GetStorageRangesMessage),
335 StorageRanges(StorageRangesMessage),
337 GetByteCodes(GetByteCodesMessage),
339 ByteCodes(ByteCodesMessage),
341 GetBlockAccessLists(GetBlockAccessListsMessage),
343 BlockAccessLists(BlockAccessListsMessage),
345}
346
347#[derive(thiserror::Error, Debug)]
349pub enum SnapProtocolError {
350 #[error("empty snap message")]
352 Empty,
353 #[error("message id {0:#x} is invalid for snap/{1:?}")]
356 UnsupportedMessageId(u8, SnapVersion),
357 #[error("RLP error: {0}")]
359 Rlp(#[from] alloy_rlp::Error),
360}
361
362impl SnapProtocolMessage {
363 pub const fn message_id(&self) -> SnapMessageId {
367 match self {
368 Self::GetAccountRange(_) => SnapMessageId::GetAccountRange,
369 Self::AccountRange(_) => SnapMessageId::AccountRange,
370 Self::GetStorageRanges(_) => SnapMessageId::GetStorageRanges,
371 Self::StorageRanges(_) => SnapMessageId::StorageRanges,
372 Self::GetByteCodes(_) => SnapMessageId::GetByteCodes,
373 Self::ByteCodes(_) => SnapMessageId::ByteCodes,
374 Self::GetBlockAccessLists(_) => SnapMessageId::GetBlockAccessLists,
375 Self::BlockAccessLists(_) => SnapMessageId::BlockAccessLists,
376 }
377 }
378
379 pub const fn request_id(&self) -> u64 {
381 match self {
382 Self::GetAccountRange(m) => m.request_id,
383 Self::AccountRange(m) => m.request_id,
384 Self::GetStorageRanges(m) => m.request_id,
385 Self::StorageRanges(m) => m.request_id,
386 Self::GetByteCodes(m) => m.request_id,
387 Self::ByteCodes(m) => m.request_id,
388 Self::GetBlockAccessLists(m) => m.request_id,
389 Self::BlockAccessLists(m) => m.request_id,
390 }
391 }
392
393 pub const fn is_response(&self) -> bool {
395 matches!(
396 self,
397 Self::AccountRange(_) |
398 Self::StorageRanges(_) |
399 Self::ByteCodes(_) |
400 Self::BlockAccessLists(_)
401 )
402 }
403
404 pub const fn set_request_id(&mut self, request_id: u64) {
407 match self {
408 Self::GetAccountRange(m) => m.request_id = request_id,
409 Self::AccountRange(m) => m.request_id = request_id,
410 Self::GetStorageRanges(m) => m.request_id = request_id,
411 Self::StorageRanges(m) => m.request_id = request_id,
412 Self::GetByteCodes(m) => m.request_id = request_id,
413 Self::ByteCodes(m) => m.request_id = request_id,
414 Self::GetBlockAccessLists(m) => m.request_id = request_id,
415 Self::BlockAccessLists(m) => m.request_id = request_id,
416 }
417 }
418
419 pub fn encode(&self) -> Bytes {
421 let mut buf = Vec::new();
422 buf.push(self.message_id() as u8);
424
425 match self {
427 Self::GetAccountRange(msg) => msg.encode(&mut buf),
428 Self::AccountRange(msg) => msg.encode(&mut buf),
429 Self::GetStorageRanges(msg) => msg.encode(&mut buf),
430 Self::StorageRanges(msg) => msg.encode(&mut buf),
431 Self::GetByteCodes(msg) => msg.encode(&mut buf),
432 Self::ByteCodes(msg) => msg.encode(&mut buf),
433 Self::GetBlockAccessLists(msg) => msg.encode(&mut buf),
434 Self::BlockAccessLists(msg) => msg.encode(&mut buf),
435 }
436
437 Bytes::from(buf)
438 }
439
440 pub fn decode(message_id: u8, buf: &mut &[u8]) -> Result<Self, alloy_rlp::Error> {
442 macro_rules! decode_snap_message_variant {
444 ($message_id:expr, $buf:expr, $id:expr, $variant:ident, $msg_type:ty) => {
445 if $message_id == $id as u8 {
446 return Ok(Self::$variant(<$msg_type>::decode($buf)?));
447 }
448 };
449 }
450
451 decode_snap_message_variant!(
453 message_id,
454 buf,
455 SnapMessageId::GetAccountRange,
456 GetAccountRange,
457 GetAccountRangeMessage
458 );
459 decode_snap_message_variant!(
460 message_id,
461 buf,
462 SnapMessageId::AccountRange,
463 AccountRange,
464 AccountRangeMessage
465 );
466 decode_snap_message_variant!(
467 message_id,
468 buf,
469 SnapMessageId::GetStorageRanges,
470 GetStorageRanges,
471 GetStorageRangesMessage
472 );
473 decode_snap_message_variant!(
474 message_id,
475 buf,
476 SnapMessageId::StorageRanges,
477 StorageRanges,
478 StorageRangesMessage
479 );
480 decode_snap_message_variant!(
481 message_id,
482 buf,
483 SnapMessageId::GetByteCodes,
484 GetByteCodes,
485 GetByteCodesMessage
486 );
487 decode_snap_message_variant!(
488 message_id,
489 buf,
490 SnapMessageId::ByteCodes,
491 ByteCodes,
492 ByteCodesMessage
493 );
494 decode_snap_message_variant!(
495 message_id,
496 buf,
497 SnapMessageId::GetBlockAccessLists,
498 GetBlockAccessLists,
499 GetBlockAccessListsMessage
500 );
501 decode_snap_message_variant!(
502 message_id,
503 buf,
504 SnapMessageId::BlockAccessLists,
505 BlockAccessLists,
506 BlockAccessListsMessage
507 );
508
509 Err(alloy_rlp::Error::Custom("Unknown message ID"))
510 }
511
512 pub fn decode_versioned(version: SnapVersion, bytes: &[u8]) -> Result<Self, SnapProtocolError> {
517 let (&id, mut body) = bytes.split_first().ok_or(SnapProtocolError::Empty)?;
518 if !version.supports_message_id(id) {
519 return Err(SnapProtocolError::UnsupportedMessageId(id, version));
520 }
521 let msg = Self::decode(id, &mut body)?;
522 if !body.is_empty() {
523 return Err(SnapProtocolError::Rlp(alloy_rlp::Error::UnexpectedLength));
524 }
525 Ok(msg)
526 }
527}
528
529#[derive(RlpDecodable)]
531struct SlimAccountBody {
532 nonce: u64,
534 balance: U256,
536 storage_root: Bytes,
538 code_hash: Bytes,
540}
541
542impl SlimAccountBody {
543 fn restore(value: &[u8], empty: B256) -> alloy_rlp::Result<B256> {
545 match value {
546 [] => Ok(empty),
547 _ => B256::try_from(value).map_err(|_| alloy_rlp::Error::UnexpectedLength),
548 }
549 }
550}
551
552#[derive(RlpEncodable)]
554struct SlimAccountBodyRef<'a> {
555 nonce: u64,
557 balance: U256,
559 storage_root: &'a [u8],
561 code_hash: &'a [u8],
563}
564
565impl<'a> SlimAccountBodyRef<'a> {
566 fn shorten(value: &'a B256, empty: B256) -> &'a [u8] {
568 if *value == empty {
569 &[]
570 } else {
571 value.as_slice()
572 }
573 }
574}
575
576#[cfg(test)]
577mod tests {
578 use super::*;
579 use test_case::test_case;
580
581 fn b256_from_u64(value: u64) -> B256 {
583 B256::left_padding_from(&value.to_be_bytes())
584 }
585
586 fn test_roundtrip(original: SnapProtocolMessage) {
588 let encoded = original.encode();
589
590 assert_eq!(encoded[0], original.message_id() as u8);
592
593 let mut buf = &encoded[1..];
594 let decoded = SnapProtocolMessage::decode(encoded[0], &mut buf).unwrap();
595
596 assert_eq!(decoded, original);
598 }
599
600 #[derive(alloy_rlp::RlpEncodable)]
604 struct GethStorageRequest {
605 request_id: u64,
606 root_hash: B256,
607 account_hashes: Vec<B256>,
608 origin: Bytes,
609 limit: Bytes,
610 response_bytes: u64,
611 }
612
613 #[test]
614 fn test_all_message_roundtrips() {
615 assert_eq!(SnapVersion::V2.message_count(), 10);
616
617 test_roundtrip(SnapProtocolMessage::GetAccountRange(GetAccountRangeMessage {
618 request_id: 42,
619 root_hash: b256_from_u64(123),
620 starting_hash: b256_from_u64(456),
621 limit_hash: b256_from_u64(789),
622 response_bytes: 1024,
623 }));
624
625 test_roundtrip(SnapProtocolMessage::AccountRange(AccountRangeMessage {
626 request_id: 42,
627 accounts: vec![AccountData {
628 hash: b256_from_u64(123),
629 body: Bytes::from(vec![1, 2, 3]),
630 }],
631 proof: vec![Bytes::from(vec![4, 5, 6])],
632 }));
633
634 test_roundtrip(SnapProtocolMessage::GetStorageRanges(GetStorageRangesMessage {
635 request_id: 42,
636 root_hash: b256_from_u64(123),
637 account_hashes: vec![b256_from_u64(456)],
638 starting_hash: b256_from_u64(789).into(),
639 limit_hash: b256_from_u64(101112).into(),
640 response_bytes: 2048,
641 }));
642
643 test_roundtrip(SnapProtocolMessage::GetStorageRanges(GetStorageRangesMessage {
645 request_id: 43,
646 root_hash: b256_from_u64(123),
647 account_hashes: vec![b256_from_u64(456), b256_from_u64(789)],
648 starting_hash: RangeBound::default(),
649 limit_hash: RangeBound::default(),
650 response_bytes: 2048,
651 }));
652
653 test_roundtrip(SnapProtocolMessage::StorageRanges(StorageRangesMessage {
654 request_id: 42,
655 slots: vec![vec![StorageData {
656 hash: b256_from_u64(123),
657 data: Bytes::from(vec![1, 2, 3]),
658 }]],
659 proof: vec![Bytes::from(vec![4, 5, 6])],
660 }));
661
662 test_roundtrip(SnapProtocolMessage::GetByteCodes(GetByteCodesMessage {
663 request_id: 42,
664 hashes: vec![b256_from_u64(123)],
665 response_bytes: 1024,
666 }));
667
668 test_roundtrip(SnapProtocolMessage::ByteCodes(ByteCodesMessage {
669 request_id: 42,
670 codes: vec![Bytes::from(vec![1, 2, 3])],
671 }));
672
673 test_roundtrip(SnapProtocolMessage::GetBlockAccessLists(GetBlockAccessListsMessage {
674 request_id: 42,
675 block_hashes: vec![b256_from_u64(123), b256_from_u64(456)],
676 response_bytes: 4096,
677 }));
678
679 test_roundtrip(SnapProtocolMessage::BlockAccessLists(BlockAccessListsMessage {
680 request_id: 42,
681 block_access_lists: BlockAccessLists(vec![
682 Some(Bytes::from_static(&[alloy_rlp::EMPTY_LIST_CODE])),
683 Some(Bytes::from_static(&[0xc1, alloy_rlp::EMPTY_LIST_CODE])),
684 ]),
685 }));
686 }
687
688 #[test]
689 fn test_unknown_message_id() {
690 let data = Bytes::from(vec![1, 2, 3, 4]);
692 let mut buf = data.as_ref();
693
694 let result = SnapProtocolMessage::decode(255, &mut buf);
696
697 assert!(result.is_err());
698 if let Err(e) = result {
699 assert_eq!(e.to_string(), "Unknown message ID");
700 }
701 }
702
703 #[test]
704 fn test_snap_v2_message_validity() {
705 let v2 = SnapVersion::V2;
706 for id in 0x00..=0x05 {
708 assert!(v2.supports_message_id(id), "snap/2 should accept {id:#x}");
709 }
710 assert!(!v2.supports_message_id(0x06));
712 assert!(!v2.supports_message_id(0x07));
713 assert!(v2.supports_message_id(SnapMessageId::GetBlockAccessLists as u8));
715 assert!(v2.supports_message_id(SnapMessageId::BlockAccessLists as u8));
716 assert!(!v2.supports_message_id(0x0a));
717 assert!(!v2.supports_message_id(0xff));
718 }
719
720 #[test_case(
721 SnapProtocolMessage::GetAccountRange(GetAccountRangeMessage {
722 request_id: 1, root_hash: B256::ZERO, starting_hash: B256::ZERO,
723 limit_hash: B256::ZERO, response_bytes: 0,
724 }), 1, false ; "get_account_range is a request"
725 )]
726 #[test_case(
727 SnapProtocolMessage::AccountRange(AccountRangeMessage {
728 request_id: 2, accounts: vec![], proof: vec![],
729 }), 2, true ; "account_range is a response"
730 )]
731 #[test_case(
732 SnapProtocolMessage::GetStorageRanges(GetStorageRangesMessage {
733 request_id: 3, root_hash: B256::ZERO, account_hashes: vec![],
734 starting_hash: B256::ZERO.into(), limit_hash: B256::ZERO.into(), response_bytes: 0,
735 }), 3, false ; "get_storage_ranges is a request"
736 )]
737 #[test_case(
738 SnapProtocolMessage::StorageRanges(StorageRangesMessage {
739 request_id: 4, slots: vec![], proof: vec![],
740 }), 4, true ; "storage_ranges is a response"
741 )]
742 #[test_case(
743 SnapProtocolMessage::GetByteCodes(GetByteCodesMessage {
744 request_id: 5, hashes: vec![], response_bytes: 0,
745 }), 5, false ; "get_byte_codes is a request"
746 )]
747 #[test_case(
748 SnapProtocolMessage::ByteCodes(ByteCodesMessage { request_id: 6, codes: vec![] }),
749 6, true ; "byte_codes is a response"
750 )]
751 #[test_case(
752 SnapProtocolMessage::GetBlockAccessLists(GetBlockAccessListsMessage {
753 request_id: 7, block_hashes: vec![], response_bytes: 0,
754 }), 7, false ; "get_block_access_lists is a request"
755 )]
756 #[test_case(
757 SnapProtocolMessage::BlockAccessLists(BlockAccessListsMessage {
758 request_id: 8, block_access_lists: BlockAccessLists(vec![]),
759 }), 8, true ; "block_access_lists is a response"
760 )]
761 fn request_id_and_is_response(msg: SnapProtocolMessage, expected_id: u64, is_response: bool) {
762 assert_eq!(msg.request_id(), expected_id);
763 assert_eq!(msg.is_response(), is_response);
764 }
765
766 #[test_case(
767 SnapProtocolMessage::GetAccountRange(GetAccountRangeMessage {
768 request_id: 1, root_hash: B256::ZERO, starting_hash: B256::ZERO,
769 limit_hash: B256::ZERO, response_bytes: 0,
770 }) ; "get_account_range"
771 )]
772 #[test_case(
773 SnapProtocolMessage::AccountRange(AccountRangeMessage {
774 request_id: 1, accounts: vec![], proof: vec![],
775 }) ; "account_range"
776 )]
777 #[test_case(
778 SnapProtocolMessage::GetStorageRanges(GetStorageRangesMessage {
779 request_id: 1, root_hash: B256::ZERO, account_hashes: vec![],
780 starting_hash: B256::ZERO.into(), limit_hash: B256::ZERO.into(), response_bytes: 0,
781 }) ; "get_storage_ranges"
782 )]
783 #[test_case(
784 SnapProtocolMessage::StorageRanges(StorageRangesMessage {
785 request_id: 1, slots: vec![], proof: vec![],
786 }) ; "storage_ranges"
787 )]
788 #[test_case(
789 SnapProtocolMessage::GetByteCodes(GetByteCodesMessage {
790 request_id: 1, hashes: vec![], response_bytes: 0,
791 }) ; "get_byte_codes"
792 )]
793 #[test_case(
794 SnapProtocolMessage::ByteCodes(ByteCodesMessage { request_id: 1, codes: vec![] }) ;
795 "byte_codes"
796 )]
797 #[test_case(
798 SnapProtocolMessage::GetBlockAccessLists(GetBlockAccessListsMessage {
799 request_id: 1, block_hashes: vec![], response_bytes: 0,
800 }) ; "get_block_access_lists"
801 )]
802 #[test_case(
803 SnapProtocolMessage::BlockAccessLists(BlockAccessListsMessage {
804 request_id: 1, block_access_lists: BlockAccessLists(vec![]),
805 }) ; "block_access_lists"
806 )]
807 fn per_variant_request_id_and_round_trip(mut msg: SnapProtocolMessage) {
808 msg.set_request_id(42);
810 assert_eq!(msg.request_id(), 42);
811
812 let decoded =
814 SnapProtocolMessage::decode_versioned(SnapVersion::V2, &msg.encode()).unwrap();
815 assert_eq!(decoded, msg);
816 }
817
818 #[test]
819 fn decode_versioned_rejects_empty() {
820 assert!(matches!(
822 SnapProtocolMessage::decode_versioned(SnapVersion::V2, &[]),
823 Err(SnapProtocolError::Empty)
824 ));
825 }
826
827 #[test]
828 fn decode_versioned_rejects_trie_node_ids_in_v2() {
829 for id in [0x06u8, 0x07] {
832 assert!(matches!(
833 SnapProtocolMessage::decode_versioned(SnapVersion::V2, &[id]),
834 Err(SnapProtocolError::UnsupportedMessageId(got, SnapVersion::V2)) if got == id
835 ));
836 }
837 }
838
839 #[test]
840 fn decode_versioned_reports_malformed_body() {
841 assert!(matches!(
844 SnapProtocolMessage::decode_versioned(SnapVersion::V2, &[0x08, 0xff]),
845 Err(SnapProtocolError::Rlp(_))
846 ));
847 }
848
849 #[test]
850 fn decode_versioned_rejects_trailing_bytes() {
851 let original = SnapProtocolMessage::GetBlockAccessLists(GetBlockAccessListsMessage {
854 request_id: 7,
855 block_hashes: vec![b256_from_u64(1)],
856 response_bytes: 1024,
857 });
858 let mut framed = original.encode().to_vec();
859 framed.push(0xff);
860 assert!(matches!(
861 SnapProtocolMessage::decode_versioned(SnapVersion::V2, &framed),
862 Err(SnapProtocolError::Rlp(alloy_rlp::Error::UnexpectedLength))
863 ));
864 }
865
866 #[test]
867 fn get_storage_ranges_decodes_geths_empty_origin_and_limit() {
868 let body = alloy_rlp::encode(GethStorageRequest {
869 request_id: 21,
870 root_hash: B256::ZERO,
871 account_hashes: vec![B256::repeat_byte(1), B256::repeat_byte(2)],
872 origin: Bytes::new(),
873 limit: Bytes::new(),
874 response_bytes: 1024,
875 });
876 let mut framed = vec![SnapMessageId::GetStorageRanges as u8];
877 framed.extend_from_slice(&body);
878
879 let decoded = SnapProtocolMessage::decode_versioned(SnapVersion::V2, &framed).unwrap();
880 let SnapProtocolMessage::GetStorageRanges(msg) = decoded else {
881 panic!("expected a GetStorageRanges message");
882 };
883 assert_eq!(msg.starting_hash.unwrap_or(B256::ZERO), B256::ZERO);
884 assert_eq!(msg.limit_hash.unwrap_or(B256::repeat_byte(0xff)), B256::repeat_byte(0xff));
885 }
886
887 fn trie_account(storage_root: B256, code_hash: B256) -> TrieAccount {
888 TrieAccount { nonce: 7, balance: U256::from(42), storage_root, code_hash }
889 }
890
891 #[test]
892 fn slim_body_elides_empty_storage_and_code() {
893 let account = trie_account(EMPTY_ROOT_HASH, KECCAK256_EMPTY);
894 let hash = B256::repeat_byte(1);
895 let encoded = AccountData::from_trie_account(hash, &account);
896
897 let body = SlimAccountBody::decode(&mut encoded.body.as_ref()).unwrap();
898 assert!(body.storage_root.is_empty());
899 assert!(body.code_hash.is_empty());
900 assert_eq!(encoded.trie_account().unwrap(), account);
901 assert_eq!(encoded.into_trie_entry().unwrap(), (hash, account));
902 }
903
904 #[test]
905 fn slim_body_keeps_non_default_storage_and_code() {
906 let account = trie_account(B256::repeat_byte(2), B256::repeat_byte(3));
907 let encoded = AccountData::from_trie_account(B256::repeat_byte(1), &account);
908
909 let body = SlimAccountBody::decode(&mut encoded.body.as_ref()).unwrap();
910 assert_eq!(body.storage_root.len(), 32);
911 assert_eq!(body.code_hash.len(), 32);
912 assert_eq!(encoded.trie_account().unwrap(), account);
913 }
914
915 #[test]
916 fn slim_body_rejects_field_lengths_the_encoding_never_produces() {
917 let truncated = Bytes::from_static(&[0xaa; 16]);
920
921 assert!(SlimAccountBody::restore(&truncated, EMPTY_ROOT_HASH).is_err());
922 }
923
924 #[test]
925 fn slim_body_rejects_trailing_bytes() {
926 let account = trie_account(EMPTY_ROOT_HASH, KECCAK256_EMPTY);
927 let mut encoded = AccountData::from_trie_account(B256::repeat_byte(1), &account);
928 encoded.body = [encoded.body.as_ref(), &[0x00]].concat().into();
929
930 assert!(encoded.trie_account().is_err());
931 }
932
933 #[test]
934 fn storage_data_carries_the_trie_leaf_encoding() {
935 let value = U256::from(1234);
936 let slot = StorageData::from_value(B256::repeat_byte(4), value);
937
938 assert_eq!(slot.data.as_ref(), alloy_rlp::encode(value));
941 assert_eq!(slot.value().unwrap(), value);
942 }
943
944 #[test]
945 fn storage_data_rejects_trailing_bytes() {
946 let mut slot = StorageData::from_value(B256::repeat_byte(4), U256::from(1));
947 slot.data = [slot.data.as_ref(), &[0x00]].concat().into();
948
949 assert!(slot.value().is_err());
950 }
951}