1use super::{
7 request::{SnapVerifier, VerifyingRequest},
8 VerifiedAccountBatch,
9};
10use alloy_primitives::{B256, U256};
11use futures::Future;
12use reth_eth_wire_types::snap::{
13 GetStorageRangesMessage, RangeBound, StorageData, StorageRangesMessage,
14};
15use reth_network_p2p::{
16 error::RequestError,
17 snap::client::{SnapClient, SnapResponse},
18};
19use reth_network_peers::PeerId;
20use reth_tasks::Runtime;
21use reth_trie_common::{range_proof::verify_range_proof, EMPTY_ROOT_HASH};
22use std::{
23 pin::Pin,
24 task::{Context, Poll},
25};
26use tracing::debug;
27
28const MAX_HASH: B256 = B256::new([0xff; B256::len_bytes()]);
30
31#[derive(Debug)]
33pub struct StorageRangeDownloader<C: SnapClient>(VerifyingRequest<C, StorageRangeVerifier>);
34
35impl<C: SnapClient> StorageRangeDownloader<C> {
36 pub fn new(
46 client: C,
47 request: GetStorageRangesMessage,
48 batch: &VerifiedAccountBatch<'_>,
49 runtime: Runtime,
50 ) -> Result<Self, InvalidStorageRangeRequest> {
51 let origin = request.starting_hash.unwrap_or(B256::ZERO);
52 let limit = request.limit_hash.unwrap_or(MAX_HASH);
53 if origin > limit {
54 return Err(InvalidStorageRangeRequest::ReversedBounds { origin, limit })
55 }
56 if request.account_hashes.is_empty() {
57 return Err(InvalidStorageRangeRequest::NoAccounts)
58 }
59 if request.account_hashes.len() > 1 && origin == B256::ZERO && limit != MAX_HASH {
62 return Err(InvalidStorageRangeRequest::LimitedMultipleAccounts {
63 accounts: request.account_hashes.len(),
64 })
65 }
66 batch.verify_batch(&request)?;
67 let mut storage_roots = Vec::with_capacity(batch.accounts().len());
68 for (index, (account_hash, account)) in batch.accounts().iter().enumerate() {
69 if account.storage_root == EMPTY_ROOT_HASH {
70 return Err(InvalidStorageRangeRequest::EmptyStorageRoot {
71 index,
72 account_hash: *account_hash,
73 })
74 }
75 storage_roots.push(account.storage_root);
76 }
77
78 let verifier = StorageRangeVerifier { request: request.clone(), storage_roots };
79 Ok(Self(VerifyingRequest::new(client, request, verifier, runtime)))
80 }
81}
82
83impl<C> Future for StorageRangeDownloader<C>
84where
85 C: SnapClient + Unpin,
86{
87 type Output = Result<StorageRangeOutcome, RequestError>;
88
89 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
90 self.get_mut().0.poll_verified(cx)
91 }
92}
93
94#[derive(Clone, Debug, PartialEq, Eq)]
96pub enum StorageRangeOutcome {
97 Unavailable {
99 peer_id: PeerId,
101 },
102 Verified(VerifiedStorageRanges),
104}
105
106#[derive(Clone, Debug, PartialEq, Eq)]
110pub struct VerifiedStorageRanges {
111 ranges: Vec<VerifiedStorageRange>,
113 continuation: Option<StorageRangeContinuation>,
115 request: Box<GetStorageRangesMessage>,
117}
118
119impl VerifiedStorageRanges {
120 pub fn into_ranges(self) -> Vec<VerifiedStorageRange> {
122 self.ranges
123 }
124
125 pub fn follow_up<'a>(
131 &self,
132 request_id: u64,
133 batch: VerifiedAccountBatch<'a>,
134 ) -> Result<
135 Option<(GetStorageRangesMessage, VerifiedAccountBatch<'a>)>,
136 InvalidStorageRangeRequest,
137 > {
138 batch.verify_batch(&self.request)?;
139 let Some(continuation) = self.continuation else { return Ok(None) };
140 let (index, starting_hash, limit_hash) = match continuation {
141 StorageRangeContinuation::Partial { account_index: 0, starting_hash, .. } => {
142 (0, starting_hash.into(), self.request.limit_hash)
143 }
144 StorageRangeContinuation::Partial { account_index, starting_hash, .. } => {
145 (account_index, starting_hash.into(), RangeBound::default())
146 }
147 StorageRangeContinuation::NextAccount { account_index, .. } => {
148 (account_index, RangeBound::default(), RangeBound::default())
149 }
150 };
151
152 let request = GetStorageRangesMessage {
153 request_id,
154 account_hashes: self.request.account_hashes[index..].to_vec(),
155 starting_hash,
156 limit_hash,
157 ..(*self.request).clone()
158 };
159 let narrowed = batch.slice(index).expect("checked batch covers the continuation");
160 Ok(Some((request, narrowed)))
161 }
162}
163
164#[derive(Clone, Debug, PartialEq, Eq)]
166pub struct VerifiedStorageRange {
167 pub account_hash: B256,
169 pub slots: Vec<(B256, U256)>,
171}
172
173#[derive(Clone, Copy, Debug, PartialEq, Eq)]
175enum StorageRangeContinuation {
176 Partial {
178 account_index: usize,
180 account_hash: B256,
182 starting_hash: B256,
184 },
185 NextAccount {
187 account_index: usize,
189 account_hash: B256,
191 },
192}
193
194#[derive(Clone, Copy, Debug, PartialEq, Eq, thiserror::Error)]
196pub enum InvalidStorageRangeRequest {
197 #[error(
199 "storage range requests state root {requested}, but accounts are authenticated by {authenticated}"
200 )]
201 StateRootMismatch {
202 requested: B256,
204 authenticated: B256,
206 },
207 #[error("storage range request contains no accounts")]
209 NoAccounts,
210 #[error("limited storage range request contains {accounts} accounts")]
212 LimitedMultipleAccounts {
213 accounts: usize,
215 },
216 #[error("storage range origin {origin} exceeds limit {limit}")]
218 ReversedBounds {
219 origin: B256,
221 limit: B256,
223 },
224 #[error("storage range request has {requested} accounts but {supplied} were supplied")]
226 AccountCount {
227 requested: usize,
229 supplied: usize,
231 },
232 #[error(
234 "storage range account {index} requests {requested}, but authenticated account is {supplied}"
235 )]
236 AccountMismatch {
237 index: usize,
239 requested: B256,
241 supplied: B256,
243 },
244 #[error("storage range account {index} ({account_hash}) has an empty storage root")]
246 EmptyStorageRoot {
247 index: usize,
249 account_hash: B256,
251 },
252}
253
254#[derive(Clone, Debug)]
256struct StorageRangeVerifier {
257 request: GetStorageRangesMessage,
259 storage_roots: Vec<B256>,
261}
262
263impl SnapVerifier for StorageRangeVerifier {
264 type Request = GetStorageRangesMessage;
265 type Output = StorageRangeOutcome;
266
267 fn verify(self, peer_id: PeerId, response: SnapResponse) -> Result<Self::Output, RequestError> {
268 self.verify_response(peer_id, response)
269 }
270}
271
272impl StorageRangeVerifier {
273 fn verify_response(
275 &self,
276 peer_id: PeerId,
277 response: SnapResponse,
278 ) -> Result<StorageRangeOutcome, RequestError> {
279 let Some(response) = self.accepted_response(response)? else {
280 return Ok(StorageRangeOutcome::Unavailable { peer_id })
281 };
282
283 let proof_index =
285 response.slots.len().checked_sub(1).filter(|_| !response.proof.is_empty());
286 let mut ranges = Vec::with_capacity(response.slots.len());
287 let mut bounded_next = None;
288
289 for (index, slots) in response.slots.iter().enumerate() {
290 let proof = if proof_index == Some(index) { response.proof.as_slice() } else { &[] };
291 let verified = self.verify_range(index, slots, proof)?;
292 ranges.push(verified.range);
293 bounded_next = verified.within_limit;
294 }
295
296 let continuation = self.continuation(&ranges, bounded_next);
297 Ok(StorageRangeOutcome::Verified(VerifiedStorageRanges {
298 ranges,
299 continuation,
300 request: Box::new(self.request.clone()),
301 }))
302 }
303
304 fn accepted_response(
307 &self,
308 response: SnapResponse,
309 ) -> Result<Option<StorageRangesMessage>, RequestError> {
310 let SnapResponse::StorageRanges(mut response) = response else {
311 debug!(target: "downloaders::snap", "Expected storage ranges response");
312 return Err(RequestError::BadResponse)
313 };
314 if response.request_id != self.request.request_id {
315 debug!(
316 target: "downloaders::snap",
317 expected = self.request.request_id,
318 got = response.request_id,
319 "Storage ranges response id mismatch"
320 );
321 return Err(RequestError::BadResponse)
322 }
323 if response.slots.len() > self.request.account_hashes.len() {
324 debug!(target: "downloaders::snap", "Storage response contains extra ranges");
325 return Err(RequestError::BadResponse)
326 }
327 if response.slots.is_empty() {
328 if response.proof.is_empty() {
329 return Ok(None)
330 }
331 response.slots.push(Vec::new());
333 }
334 Ok(Some(response))
335 }
336
337 fn verify_range(
339 &self,
340 index: usize,
341 slots: &[StorageData],
342 proof: &[alloy_primitives::Bytes],
343 ) -> Result<VerifiedRange, RequestError> {
344 let account_hash = self.request.account_hashes[index];
345 let (origin, limit) = if index == 0 {
347 (
348 self.request.starting_hash.unwrap_or(B256::ZERO),
349 self.request.limit_hash.unwrap_or(MAX_HASH),
350 )
351 } else {
352 (B256::ZERO, MAX_HASH)
353 };
354
355 if slots.iter().filter(|slot| slot.hash > limit).nth(1).is_some() {
357 debug!(target: "downloaders::snap", %account_hash, "Storage range exceeds limit");
358 return Err(RequestError::BadResponse)
359 }
360
361 let mut decoded = Self::decode_slots(account_hash, origin, slots)?;
362 let leaves = decoded.iter().map(|(hash, value)| (*hash, alloy_rlp::encode(value)));
363 let next = verify_range_proof(self.storage_roots[index], origin, limit, leaves, proof)
364 .map_err(|error| {
365 debug!(
366 target: "downloaders::snap",
367 %account_hash,
368 %error,
369 "Invalid storage range proof"
370 );
371 RequestError::BadResponse
372 })?;
373
374 if next.is_some_and(|next| next <= origin) {
377 debug!(target: "downloaders::snap", %account_hash, "Storage range does not advance");
378 return Err(RequestError::BadResponse)
379 }
380
381 decoded.truncate(decoded.partition_point(|(hash, _)| *hash <= limit));
382 Ok(VerifiedRange {
383 range: VerifiedStorageRange { account_hash, slots: decoded },
384 within_limit: next.filter(|next| *next <= limit),
385 })
386 }
387
388 fn continuation(
391 &self,
392 ranges: &[VerifiedStorageRange],
393 final_next: Option<B256>,
394 ) -> Option<StorageRangeContinuation> {
395 if let Some(starting_hash) = final_next {
396 return Some(StorageRangeContinuation::Partial {
397 account_index: ranges.len() - 1,
398 account_hash: ranges.last().expect("a response range exists").account_hash,
399 starting_hash,
400 })
401 }
402 let account_index = ranges.len();
403 self.request.account_hashes.get(account_index).copied().map(|account_hash| {
404 StorageRangeContinuation::NextAccount { account_index, account_hash }
405 })
406 }
407
408 fn decode_slots(
410 account_hash: B256,
411 origin: B256,
412 slots: &[StorageData],
413 ) -> Result<Vec<(B256, U256)>, RequestError> {
414 let mut decoded = Vec::with_capacity(slots.len());
415 let mut previous = None;
416
417 for slot in slots {
418 if slot.hash < origin || previous.is_some_and(|previous| slot.hash <= previous) {
419 debug!(
420 target: "downloaders::snap",
421 %account_hash,
422 "Storage slots precede origin or are not strictly ordered"
423 );
424 return Err(RequestError::BadResponse)
425 }
426 let value = slot.value().map_err(|error| {
427 debug!(target: "downloaders::snap", %account_hash, %error, "Invalid storage value");
428 RequestError::BadResponse
429 })?;
430 if value.is_zero() {
431 debug!(target: "downloaders::snap", %account_hash, "Storage trie contains zero leaf");
432 return Err(RequestError::BadResponse)
433 }
434 previous = Some(slot.hash);
435 decoded.push((slot.hash, value));
436 }
437 Ok(decoded)
438 }
439}
440
441struct VerifiedRange {
443 range: VerifiedStorageRange,
445 within_limit: Option<B256>,
447}
448
449#[cfg(test)]
450mod tests {
451 use super::{
452 super::{request::MAX_RETRIES, test_utils::TestSnapClient, VerifiedAccountRange},
453 *,
454 };
455 use alloy_primitives::Bytes;
456 use reth_network_p2p::{error::PeerRequestResult, priority::Priority};
457 use reth_network_peers::WithPeerId;
458 use reth_trie_common::{
459 proof::ProofRetainer, HashBuilder, Nibbles, TrieAccount, EMPTY_ROOT_HASH,
460 };
461 use std::sync::Arc;
462
463 fn key(value: u64) -> B256 {
464 B256::left_padding_from(&value.to_be_bytes())
465 }
466
467 fn slots(values: &[(B256, u64)]) -> Vec<(B256, U256)> {
469 values.iter().map(|(hash, value)| (*hash, U256::from(*value))).collect()
470 }
471
472 fn storage_root(slots: &[(B256, U256)], targets: &[B256]) -> (B256, Vec<Bytes>) {
473 let targets = targets.iter().copied().map(Nibbles::unpack).collect();
474 let mut builder = HashBuilder::default().with_proof_retainer(ProofRetainer::new(targets));
475 for (hash, value) in slots {
476 builder.add_leaf(Nibbles::unpack(*hash), &alloy_rlp::encode(value));
477 }
478 let root = builder.root();
479 let proof = builder
480 .take_proof_nodes()
481 .into_nodes_sorted()
482 .into_iter()
483 .map(|(_, node)| node)
484 .collect();
485 (root, proof)
486 }
487
488 const STATE_ROOT: B256 = B256::repeat_byte(0xaa);
490
491 fn verified_range(accounts: &[(B256, TrieAccount)]) -> VerifiedAccountRange {
493 VerifiedAccountRange {
494 state_root: STATE_ROOT,
495 origin: B256::ZERO,
496 accounts: accounts.to_vec(),
497 has_more: false,
498 next: None,
499 }
500 }
501
502 fn account(storage_root: B256) -> TrieAccount {
503 TrieAccount { nonce: 1, balance: U256::from(2), storage_root, ..Default::default() }
504 }
505
506 fn wire_slots(slots: &[(B256, U256)]) -> Vec<StorageData> {
507 slots.iter().map(|(hash, value)| StorageData::from_value(*hash, *value)).collect()
508 }
509
510 fn account_refs(accounts: &[(B256, TrieAccount)]) -> Vec<(B256, &TrieAccount)> {
511 accounts.iter().map(|(hash, account)| (*hash, account)).collect()
512 }
513
514 fn request(accounts: &[(B256, TrieAccount)]) -> GetStorageRangesMessage {
515 GetStorageRangesMessage {
516 request_id: 1,
517 root_hash: STATE_ROOT,
518 account_hashes: accounts.iter().map(|(hash, _)| *hash).collect(),
519 starting_hash: B256::ZERO.into(),
520 limit_hash: MAX_HASH.into(),
521 response_bytes: 512 * 1024,
522 }
523 }
524
525 fn response(
526 peer: PeerId,
527 request_id: u64,
528 slots: Vec<Vec<StorageData>>,
529 proof: Vec<Bytes>,
530 ) -> PeerRequestResult<SnapResponse> {
531 Ok(WithPeerId::new(
532 peer,
533 SnapResponse::StorageRanges(StorageRangesMessage { request_id, slots, proof }),
534 ))
535 }
536
537 fn rejecting_client(
538 response: impl Fn(PeerId) -> PeerRequestResult<SnapResponse>,
539 ) -> Arc<TestSnapClient> {
540 let peer = PeerId::random();
541 Arc::new(TestSnapClient::new((0..=MAX_RETRIES).map(|_| response(peer))))
542 }
543
544 fn downloader<C: SnapClient>(
545 client: C,
546 request: GetStorageRangesMessage,
547 accounts: &[(B256, TrieAccount)],
548 ) -> Result<StorageRangeDownloader<C>, InvalidStorageRangeRequest> {
549 let range = verified_range(accounts);
550 StorageRangeDownloader::new(client, request, &range.batch(), Runtime::test())
551 }
552
553 #[tokio::test]
554 async fn complete_ranges_for_multiple_accounts_are_verified() {
555 let first = slots(&[(key(1), 11), (key(2), 12)]);
556 let second = slots(&[(key(3), 13)]);
557 let (first_root, _) = storage_root(&first, &[]);
558 let (second_root, proof) = storage_root(&second, &[key(3)]);
559 let accounts = vec![(key(100), account(first_root)), (key(200), account(second_root))];
560 let client = Arc::new(TestSnapClient::new([response(
561 PeerId::random(),
562 1,
563 vec![wire_slots(&first), wire_slots(&second)],
564 proof,
565 )]));
566
567 let outcome =
568 downloader(Arc::clone(&client), request(&accounts), &accounts).unwrap().await.unwrap();
569
570 let StorageRangeOutcome::Verified(verified) = outcome else { panic!("verified ranges") };
571 assert_eq!(verified.ranges.len(), 2);
572 assert_eq!(verified.ranges[0].slots, first);
573 assert_eq!(verified.ranges[1].slots, second);
574 assert_eq!(verified.continuation, None);
575 assert!(client.reported().is_empty());
576 }
577
578 #[tokio::test]
579 async fn partial_final_range_reports_its_slot_continuation() {
580 let all = slots(&[(key(1), 11), (key(2), 12), (key(3), 13)]);
581 let (root, proof) = storage_root(&all, &[B256::ZERO, key(1)]);
582 let accounts = vec![(key(100), account(root))];
583 let client = Arc::new(TestSnapClient::new([response(
584 PeerId::random(),
585 1,
586 vec![wire_slots(&all[..1])],
587 proof,
588 )]));
589
590 let outcome =
591 downloader(Arc::clone(&client), request(&accounts), &accounts).unwrap().await.unwrap();
592
593 let StorageRangeOutcome::Verified(verified) = outcome else { panic!("verified ranges") };
594 assert_eq!(verified.ranges[0].slots, all[..1]);
595 assert_eq!(
596 verified.continuation,
597 Some(StorageRangeContinuation::Partial {
598 account_index: 0,
599 account_hash: key(100),
600 starting_hash: key(2),
601 })
602 );
603 assert!(client.reported().is_empty());
604 }
605
606 #[tokio::test]
607 async fn an_omitted_account_resumes_at_the_next_position() {
608 let first = slots(&[(key(1), 11)]);
609 let (first_root, _) = storage_root(&first, &[]);
610 let accounts =
611 vec![(key(100), account(first_root)), (key(200), account(B256::repeat_byte(0xbb)))];
612 let client = Arc::new(TestSnapClient::new([response(
613 PeerId::random(),
614 1,
615 vec![wire_slots(&first)],
616 Vec::new(),
617 )]));
618
619 let outcome =
620 downloader(Arc::clone(&client), request(&accounts), &accounts).unwrap().await.unwrap();
621
622 let StorageRangeOutcome::Verified(verified) = outcome else { panic!("verified ranges") };
623 assert_eq!(verified.ranges.len(), 1);
624 assert_eq!(
625 verified.continuation,
626 Some(StorageRangeContinuation::NextAccount {
627 account_index: 1,
628 account_hash: key(200),
629 })
630 );
631 }
632
633 #[test]
634 fn empty_storage_roots_must_be_filtered_before_submission() {
635 let accounts = vec![
636 (key(100), account(B256::repeat_byte(0xaa))),
637 (key(200), account(EMPTY_ROOT_HASH)),
638 (key(300), account(B256::repeat_byte(0xbb))),
639 ];
640 let client = TestSnapClient::new([response(PeerId::random(), 1, Vec::new(), Vec::new())]);
641
642 assert_eq!(
643 downloader(&client, request(&accounts), &accounts).unwrap_err(),
644 InvalidStorageRangeRequest::EmptyStorageRoot { index: 1, account_hash: key(200) }
645 );
646 assert!(client.priorities().is_empty());
647
648 let range = verified_range(&accounts);
649 let batch = range.storage_batch();
650 let mut storage_request = request(&accounts);
651 storage_request.account_hashes = batch.accounts().iter().map(|(hash, _)| *hash).collect();
652 assert_eq!(storage_request.account_hashes, vec![key(100), key(300)]);
653 assert!(
654 StorageRangeDownloader::new(&client, storage_request, &batch, Runtime::test()).is_ok()
655 );
656 assert_eq!(*client.priorities(), [Priority::Normal]);
657 }
658
659 #[tokio::test]
660 async fn a_response_without_slots_or_proof_reports_the_state_unavailable() {
661 let batches = [
662 vec![(key(100), account(B256::repeat_byte(0xbb)))],
663 vec![
664 (key(100), account(B256::repeat_byte(0xaa))),
665 (key(200), account(B256::repeat_byte(0xbb))),
666 ],
667 ];
668 for accounts in batches {
669 let peer = PeerId::random();
670 let client = Arc::new(TestSnapClient::new([response(peer, 1, Vec::new(), Vec::new())]));
671
672 let outcome = downloader(Arc::clone(&client), request(&accounts), &accounts)
673 .unwrap()
674 .await
675 .unwrap();
676
677 assert_eq!(outcome, StorageRangeOutcome::Unavailable { peer_id: peer });
678 assert!(client.reported().is_empty());
680 }
681 }
682
683 #[tokio::test]
684 async fn a_mismatched_request_id_exhausts_the_retry_budget() {
685 let all = slots(&[(key(1), 11)]);
686 let (root, _) = storage_root(&all, &[]);
687 let accounts = vec![(key(100), account(root))];
688
689 let client = rejecting_client(|peer| response(peer, 9, vec![wire_slots(&all)], Vec::new()));
690 let error = downloader(Arc::clone(&client), request(&accounts), &accounts)
691 .unwrap()
692 .await
693 .unwrap_err();
694
695 assert_eq!(error, RequestError::BadResponse);
696 assert_eq!(client.reported().len(), usize::from(MAX_RETRIES) + 1);
698 }
699
700 #[tokio::test]
701 async fn a_range_proved_against_another_root_is_rejected() {
702 let all = slots(&[(key(1), 11)]);
703 let accounts = vec![(key(100), account(B256::repeat_byte(0xcc)))];
704
705 let client = rejecting_client(|peer| response(peer, 1, vec![wire_slots(&all)], Vec::new()));
706 let error = downloader(Arc::clone(&client), request(&accounts), &accounts)
707 .unwrap()
708 .await
709 .unwrap_err();
710
711 assert_eq!(error, RequestError::BadResponse);
712 assert_eq!(client.reported().len(), usize::from(MAX_RETRIES) + 1);
713 }
714
715 #[tokio::test]
716 async fn slots_that_are_not_strictly_ordered_penalize_the_peer_before_a_retry_succeeds() {
717 let all = slots(&[(key(1), 11), (key(2), 12)]);
718 let (root, _) = storage_root(&all, &[]);
719 let accounts = vec![(key(100), account(root))];
720 let bad_peer = PeerId::random();
721 let good_peer = PeerId::random();
722 let mut reversed = wire_slots(&all);
723 reversed.reverse();
724 let client = Arc::new(TestSnapClient::new([
725 response(bad_peer, 1, vec![reversed], Vec::new()),
726 response(good_peer, 1, vec![wire_slots(&all)], Vec::new()),
727 ]));
728
729 let outcome =
730 downloader(Arc::clone(&client), request(&accounts), &accounts).unwrap().await.unwrap();
731
732 let StorageRangeOutcome::Verified(verified) = outcome else { panic!("verified ranges") };
733 assert_eq!(verified.ranges[0].slots, all);
734 assert_eq!(*client.reported(), [bad_peer]);
735 assert_eq!(*client.priorities(), [Priority::Normal, Priority::High]);
736 }
737
738 #[test]
739 fn a_request_that_does_not_match_its_accounts_is_refused() {
740 let accounts = vec![(key(100), account(EMPTY_ROOT_HASH))];
741 let client = TestSnapClient::new([]);
742
743 let mut mismatched = request(&accounts);
744 mismatched.account_hashes = vec![key(999)];
745 let error = downloader(&client, mismatched, &accounts).unwrap_err();
746 assert_eq!(
747 error,
748 InvalidStorageRangeRequest::AccountMismatch {
749 index: 0,
750 requested: key(999),
751 supplied: key(100),
752 }
753 );
754
755 let mut empty = request(&accounts);
756 empty.account_hashes.clear();
757 assert_eq!(
758 downloader(&client, empty, &accounts).unwrap_err(),
759 InvalidStorageRangeRequest::NoAccounts
760 );
761
762 let mut reversed = request(&accounts);
763 reversed.starting_hash = key(9).into();
764 reversed.limit_hash = key(1).into();
765 assert_eq!(
766 downloader(&client, reversed, &accounts).unwrap_err(),
767 InvalidStorageRangeRequest::ReversedBounds { origin: key(9), limit: key(1) }
768 );
769 }
770
771 #[test]
772 fn a_request_for_another_state_root_is_refused() {
773 let accounts = vec![(key(100), account(EMPTY_ROOT_HASH))];
774 let client = TestSnapClient::new([]);
775
776 let mut other_root = request(&accounts);
777 other_root.root_hash = B256::repeat_byte(0xcc);
778 assert_eq!(
779 downloader(&client, other_root, &accounts).unwrap_err(),
780 InvalidStorageRangeRequest::StateRootMismatch {
781 requested: B256::repeat_byte(0xcc),
782 authenticated: STATE_ROOT,
783 }
784 );
785 }
786
787 #[tokio::test]
788 async fn a_trie_continuing_past_the_limit_needs_no_follow_up() {
789 let all = slots(&[(key(1), 11), (key(2), 12), (key(3), 13)]);
790 let (root, proof) = storage_root(&all, &[B256::ZERO, key(2)]);
791 let accounts = vec![(key(100), account(root))];
792 let client = Arc::new(TestSnapClient::new([response(
793 PeerId::random(),
794 1,
795 vec![wire_slots(&all[..2])],
796 proof,
797 )]));
798 let mut bounded = request(&accounts);
799 bounded.limit_hash = key(2).into();
800
801 let outcome = downloader(Arc::clone(&client), bounded, &accounts).unwrap().await.unwrap();
802
803 let StorageRangeOutcome::Verified(verified) = outcome else { panic!("verified ranges") };
804 let range = verified_range(&accounts);
805 assert_eq!(verified.follow_up(2, range.batch()).unwrap(), None);
806 assert_eq!(verified.into_ranges()[0].slots, all[..2]);
807 }
808
809 #[tokio::test]
810 async fn a_follow_up_keeps_the_limit_only_at_the_first_account() {
811 let all = slots(&[(key(1), 11), (key(2), 12), (key(3), 13)]);
812 let (root, proof) = storage_root(&all, &[B256::ZERO, key(1)]);
813 let accounts = vec![(key(100), account(root))];
814 let client = Arc::new(TestSnapClient::new([response(
815 PeerId::random(),
816 1,
817 vec![wire_slots(&all[..1])],
818 proof,
819 )]));
820 let mut bounded = request(&accounts);
821 bounded.limit_hash = key(5).into();
822
823 let outcome =
824 downloader(Arc::clone(&client), bounded.clone(), &accounts).unwrap().await.unwrap();
825
826 let StorageRangeOutcome::Verified(verified) = outcome else { panic!("verified ranges") };
827 let range = verified_range(&accounts);
828 let (follow_up, narrowed) = verified.follow_up(2, range.batch()).unwrap().unwrap();
829 assert_eq!(narrowed.accounts(), account_refs(&accounts));
830 assert_eq!(follow_up.request_id, 2);
831 assert_eq!(follow_up.root_hash, bounded.root_hash);
832 assert_eq!(follow_up.account_hashes, bounded.account_hashes);
833 assert_eq!(follow_up.starting_hash, key(2).into());
834 assert_eq!(follow_up.limit_hash, key(5).into());
835 }
836
837 #[tokio::test]
840 async fn a_follow_up_past_the_first_account_is_unbounded() {
841 let first = slots(&[(key(1), 11)]);
842 let second = slots(&[(key(1), 21), (key(2), 22)]);
843 let (first_root, _) = storage_root(&first, &[]);
844 let (second_root, proof) = storage_root(&second, &[B256::ZERO, key(1)]);
845 let accounts = vec![(key(100), account(first_root)), (key(200), account(second_root))];
846 let client = Arc::new(TestSnapClient::new([response(
847 PeerId::random(),
848 1,
849 vec![wire_slots(&first), wire_slots(&second[..1])],
850 proof,
851 )]));
852
853 let outcome =
854 downloader(Arc::clone(&client), request(&accounts), &accounts).unwrap().await.unwrap();
855
856 let StorageRangeOutcome::Verified(verified) = outcome else { panic!("verified ranges") };
857 assert_eq!(
858 verified.continuation,
859 Some(StorageRangeContinuation::Partial {
860 account_index: 1,
861 account_hash: key(200),
862 starting_hash: key(2),
863 })
864 );
865
866 let range = verified_range(&accounts);
867 let (follow_up, narrowed) = verified.follow_up(2, range.batch()).unwrap().unwrap();
868 assert_eq!(narrowed.accounts(), account_refs(&accounts[1..]));
869 assert_eq!(follow_up.account_hashes, vec![key(200)]);
870 assert_eq!(follow_up.starting_hash, key(2).into());
871 assert_eq!(follow_up.limit_hash, RangeBound::default());
872 }
873
874 #[tokio::test]
876 async fn resuming_a_partial_range_advances_until_the_trie_is_exhausted() {
877 let all = slots(&[(key(1), 11), (key(2), 12), (key(3), 13)]);
878 let (root, _) = storage_root(&all, &[]);
879 let accounts = vec![(key(100), account(root))];
880 let range = verified_range(&accounts);
881 let mut request = request(&accounts);
882 let mut origins = Vec::new();
883 let mut collected = Vec::new();
884
885 for served in 0..all.len() {
886 let origin = request.starting_hash.unwrap_or(B256::ZERO);
887 origins.push(origin);
888 let (_, proof) = storage_root(&all, &[origin, all[served].0]);
889 let client = Arc::new(TestSnapClient::new([response(
890 PeerId::random(),
891 request.request_id,
892 vec![wire_slots(&all[served..=served])],
893 proof,
894 )]));
895
896 let outcome =
897 downloader(Arc::clone(&client), request.clone(), &accounts).unwrap().await.unwrap();
898 let StorageRangeOutcome::Verified(verified) = outcome else {
899 panic!("verified ranges")
900 };
901 collected.extend(verified.ranges[0].slots.clone());
902
903 let Some((follow_up, _)) =
904 verified.follow_up(request.request_id + 1, range.batch()).unwrap()
905 else {
906 assert_eq!(served, all.len() - 1);
907 break
908 };
909 request = follow_up;
910 }
911
912 assert_eq!(collected, all);
913 assert_eq!(origins, vec![B256::ZERO, key(2), key(3)]);
914 }
915
916 #[test]
919 fn a_zero_origin_limited_request_must_not_carry_several_accounts() {
920 let accounts = vec![
921 (key(100), account(B256::repeat_byte(0xaa))),
922 (key(200), account(B256::repeat_byte(0xbb))),
923 ];
924 let client =
926 TestSnapClient::new([response(PeerId::random(), 1, vec![Vec::new()], Vec::new())]);
927
928 let mut bounded = request(&accounts);
929 bounded.limit_hash = key(5).into();
930 assert_eq!(
931 downloader(&client, bounded.clone(), &accounts).unwrap_err(),
932 InvalidStorageRangeRequest::LimitedMultipleAccounts { accounts: 2 }
933 );
934
935 bounded.starting_hash = key(1).into();
936 assert!(downloader(&client, bounded, &accounts).is_ok());
937 }
938
939 #[tokio::test]
942 async fn consecutive_account_transitions_carry_their_batch_forward() {
943 let served = slots(&[(key(1), 11)]);
944 let (root, _) = storage_root(&served, &[]);
945 let accounts =
946 vec![(key(100), account(root)), (key(200), account(root)), (key(300), account(root))];
947 let range = verified_range(&accounts);
948 let mut request = request(&accounts);
949 let mut batch = range.batch();
950
951 for remaining in (1..accounts.len()).rev() {
952 let client = Arc::new(TestSnapClient::new([response(
953 PeerId::random(),
954 request.request_id,
955 vec![wire_slots(&served)],
956 Vec::new(),
957 )]));
958 let outcome = StorageRangeDownloader::new(
959 Arc::clone(&client),
960 request.clone(),
961 &batch,
962 Runtime::test(),
963 )
964 .unwrap()
965 .await
966 .unwrap();
967
968 let StorageRangeOutcome::Verified(verified) = outcome else {
969 panic!("verified ranges")
970 };
971 let (follow_up, narrowed) =
972 verified.follow_up(request.request_id + 1, batch).unwrap().unwrap();
973 assert_eq!(follow_up.account_hashes.len(), remaining);
974 assert_eq!(narrowed.accounts(), account_refs(&accounts[accounts.len() - remaining..]));
975 request = follow_up;
976 batch = narrowed;
977 }
978
979 assert_eq!(request.account_hashes, vec![key(300)]);
980 }
981
982 #[tokio::test]
983 async fn a_follow_up_refuses_a_batch_from_another_request() {
984 let served = slots(&[(key(1), 11)]);
985 let (root, _) = storage_root(&served, &[]);
986 let accounts = vec![(key(100), account(root)), (key(200), account(root))];
987 let client = Arc::new(TestSnapClient::new([response(
988 PeerId::random(),
989 1,
990 vec![wire_slots(&served)],
991 Vec::new(),
992 )]));
993
994 let outcome =
995 downloader(Arc::clone(&client), request(&accounts), &accounts).unwrap().await.unwrap();
996 let StorageRangeOutcome::Verified(verified) = outcome else { panic!("verified ranges") };
997
998 let others = vec![(key(900), account(root)), (key(901), account(root))];
999 let other_range = verified_range(&others);
1000 assert_eq!(
1001 verified.follow_up(2, other_range.batch()).unwrap_err(),
1002 InvalidStorageRangeRequest::AccountMismatch {
1003 index: 0,
1004 requested: key(100),
1005 supplied: key(900),
1006 }
1007 );
1008 }
1009}