1use alloy_primitives::{map::B256Set, B256, KECCAK256_EMPTY};
7use futures::Future;
8use reth_eth_wire_types::snap::{
9 AccountData, AccountRangeMessage, GetAccountRangeMessage, GetStorageRangesMessage,
10};
11use reth_network_p2p::{
12 error::RequestError,
13 snap::client::{SnapClient, SnapResponse},
14};
15use reth_network_peers::PeerId;
16use reth_tasks::Runtime;
17use reth_trie_common::{range_proof::verify_range_proof, TrieAccount, EMPTY_ROOT_HASH};
18use std::{
19 ops::Range,
20 pin::Pin,
21 task::{Context, Poll},
22};
23use tracing::debug;
24
25mod block_access_list;
26mod bytecode;
27mod request;
28mod storage;
29#[cfg(test)]
30mod test_utils;
31
32pub use block_access_list::*;
33pub use bytecode::*;
34use request::{SnapVerifier, VerifyingRequest};
35pub use storage::*;
36
37#[derive(Debug)]
42pub struct AccountRangeDownloader<C: SnapClient>(VerifyingRequest<C, GetAccountRangeMessage>);
43
44impl<C: SnapClient> AccountRangeDownloader<C> {
45 pub fn new(
48 client: C,
49 request: GetAccountRangeMessage,
50 runtime: Runtime,
51 ) -> Result<Self, InvalidAccountRange> {
52 if request.starting_hash > request.limit_hash {
53 return Err(InvalidAccountRange {
54 origin: request.starting_hash,
55 limit: request.limit_hash,
56 })
57 }
58 let verifier = request.clone();
59 Ok(Self(VerifyingRequest::new(client, request, verifier, runtime)))
60 }
61}
62
63impl<C> Future for AccountRangeDownloader<C>
64where
65 C: SnapClient + Unpin,
66{
67 type Output = Result<AccountRangeOutcome, RequestError>;
68
69 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
70 self.get_mut().0.poll_verified(cx)
71 }
72}
73
74#[derive(Clone, Debug, PartialEq, Eq)]
76pub enum AccountRangeOutcome {
77 Unavailable {
79 peer_id: PeerId,
81 },
82 Verified(VerifiedAccountRange),
84}
85
86#[derive(Clone, Debug, PartialEq, Eq)]
88pub struct VerifiedAccountRange {
89 state_root: B256,
92 origin: B256,
94 accounts: Vec<(B256, TrieAccount)>,
96 has_more: bool,
98 next: Option<B256>,
100}
101
102impl VerifiedAccountRange {
103 pub const fn state_root(&self) -> B256 {
105 self.state_root
106 }
107
108 pub const fn origin(&self) -> B256 {
110 self.origin
111 }
112
113 pub fn accounts(&self) -> &[(B256, TrieAccount)] {
115 &self.accounts
116 }
117
118 pub const fn has_more(&self) -> bool {
122 self.has_more
123 }
124
125 pub const fn next(&self) -> Option<B256> {
128 self.next
129 }
130
131 pub fn batch(&self) -> VerifiedAccountBatch<'_> {
133 VerifiedAccountBatch {
134 state_root: self.state_root,
135 accounts: self.accounts.iter().map(|(hash, account)| (*hash, account)).collect(),
136 }
137 }
138
139 pub fn code_hashes(&self) -> Vec<B256> {
143 let mut seen = B256Set::default();
144 self.accounts
145 .iter()
146 .map(|(_, account)| account.code_hash)
147 .filter(|hash| *hash != KECCAK256_EMPTY && seen.insert(*hash))
148 .collect()
149 }
150
151 pub fn storage_batch(&self) -> VerifiedAccountBatch<'_> {
157 VerifiedAccountBatch {
158 state_root: self.state_root,
159 accounts: self
160 .accounts
161 .iter()
162 .filter(|(_, account)| account.storage_root != EMPTY_ROOT_HASH)
163 .map(|(hash, account)| (*hash, account))
164 .collect(),
165 }
166 }
167}
168
169#[derive(Clone, Debug, PartialEq, Eq)]
174pub struct VerifiedAccountBatch<'a> {
175 state_root: B256,
177 accounts: Vec<(B256, &'a TrieAccount)>,
179}
180
181impl<'a> VerifiedAccountBatch<'a> {
182 pub const fn state_root(&self) -> B256 {
184 self.state_root
185 }
186
187 pub fn accounts(&self) -> &[(B256, &'a TrieAccount)] {
189 &self.accounts
190 }
191
192 pub fn range(&self, range: Range<usize>) -> Option<Self> {
197 self.accounts
198 .get(range)
199 .map(|accounts| Self { state_root: self.state_root, accounts: accounts.to_vec() })
200 }
201
202 pub(super) fn verify_batch(
205 &self,
206 request: &GetStorageRangesMessage,
207 ) -> Result<(), InvalidStorageRangeRequest> {
208 if request.root_hash != self.state_root {
209 return Err(InvalidStorageRangeRequest::StateRootMismatch {
210 requested: request.root_hash,
211 authenticated: self.state_root,
212 })
213 }
214 if request.account_hashes.len() != self.accounts.len() {
215 return Err(InvalidStorageRangeRequest::AccountCount {
216 requested: request.account_hashes.len(),
217 supplied: self.accounts.len(),
218 })
219 }
220 for (index, (requested, (supplied, _))) in
221 request.account_hashes.iter().zip(&self.accounts).enumerate()
222 {
223 if requested != supplied {
224 return Err(InvalidStorageRangeRequest::AccountMismatch {
225 index,
226 requested: *requested,
227 supplied: *supplied,
228 })
229 }
230 }
231 Ok(())
232 }
233
234 pub(super) fn slice(mut self, from: usize) -> Option<Self> {
236 (from <= self.accounts.len()).then(|| {
237 self.accounts.drain(..from);
238 self
239 })
240 }
241}
242
243#[derive(Clone, Copy, Debug, PartialEq, Eq, thiserror::Error)]
245#[error("account range origin {origin} exceeds limit {limit}")]
246pub struct InvalidAccountRange {
247 pub origin: B256,
249 pub limit: B256,
251}
252
253impl SnapVerifier for GetAccountRangeMessage {
255 type Request = Self;
256 type Output = AccountRangeOutcome;
257
258 fn verify(self, peer_id: PeerId, response: SnapResponse) -> Result<Self::Output, RequestError> {
259 let SnapResponse::AccountRange(response) = response else {
260 debug!(target: "downloaders::snap", "Expected account range response");
261 return Err(RequestError::BadResponse)
262 };
263 if response.request_id != self.request_id {
264 debug!(
265 target: "downloaders::snap",
266 expected = self.request_id,
267 got = response.request_id,
268 "Account range response id mismatch"
269 );
270 return Err(RequestError::BadResponse)
271 }
272 if response.accounts.is_empty() && response.proof.is_empty() {
273 return if self.root_hash == EMPTY_ROOT_HASH {
274 Ok(AccountRangeOutcome::Verified(VerifiedAccountRange {
275 state_root: self.root_hash,
276 origin: self.starting_hash,
277 accounts: Vec::new(),
278 has_more: false,
279 next: None,
280 }))
281 } else {
282 Ok(AccountRangeOutcome::Unavailable { peer_id })
283 }
284 }
285
286 verify_account_range(&self, response).map(AccountRangeOutcome::Verified)
287 }
288}
289
290fn verify_account_range(
292 request: &GetAccountRangeMessage,
293 response: AccountRangeMessage,
294) -> Result<VerifiedAccountRange, RequestError> {
295 if response.accounts.iter().filter(|data| data.hash > request.limit_hash).nth(1).is_some() {
297 debug!(target: "downloaders::snap", "Account range runs past the requested limit");
298 return Err(RequestError::BadResponse)
299 }
300
301 let mut accounts =
302 response.accounts.into_iter().map(AccountData::into_trie_entry).collect::<Vec<_>>();
303 let next = verify_proof(request, &accounts, &response.proof)?;
304
305 let kept = accounts.partition_point(|(hash, _)| *hash <= request.limit_hash);
308 let next = accounts.get(kept).map(|(hash, _)| *hash).or(next);
309 accounts.truncate(kept);
310 let has_more = next.is_some_and(|next| next <= request.limit_hash);
311
312 Ok(VerifiedAccountRange {
313 state_root: request.root_hash,
314 origin: request.starting_hash,
315 accounts,
316 has_more,
317 next,
318 })
319}
320
321fn verify_proof(
323 request: &GetAccountRangeMessage,
324 accounts: &[(B256, TrieAccount)],
325 proof: &[alloy_primitives::Bytes],
326) -> Result<Option<B256>, RequestError> {
327 let leaves = accounts.iter().map(|(hash, account)| (*hash, alloy_rlp::encode(account)));
328 verify_range_proof(request.root_hash, request.starting_hash, request.limit_hash, leaves, proof)
329 .map_err(|error| {
330 debug!(target: "downloaders::snap", %error, "Invalid account range proof");
331 RequestError::BadResponse
332 })
333}
334
335#[cfg(test)]
336#[allow(clippy::clone_on_copy)]
337mod tests {
338 use super::{request::MAX_RETRIES, test_utils::TestSnapClient, *};
339 use alloy_primitives::{Bytes, U256};
340 use reth_eth_wire_types::snap::ByteCodesMessage;
341 use reth_network_p2p::{error::PeerRequestResult, priority::Priority};
342 use reth_network_peers::WithPeerId;
343 use reth_trie_common::{proof::ProofRetainer, root::state_root, HashBuilder, Nibbles};
344 use std::sync::Arc;
345
346 const MAX_HASH: B256 = B256::new([0xff; B256::len_bytes()]);
347
348 fn key(value: u64) -> B256 {
349 B256::left_padding_from(&value.to_be_bytes())
350 }
351
352 fn account(nonce: u64) -> TrieAccount {
353 TrieAccount { nonce, balance: U256::from(1), ..Default::default() }
354 }
355
356 fn root(accounts: &[(B256, TrieAccount)]) -> B256 {
357 state_root(accounts.to_vec())
358 }
359
360 fn root_and_proof(accounts: &[(B256, TrieAccount)], targets: &[B256]) -> (B256, Vec<Bytes>) {
361 let targets = targets.iter().copied().map(Nibbles::unpack).collect();
362 let mut builder = HashBuilder::default().with_proof_retainer(ProofRetainer::new(targets));
363 for (key, account) in accounts {
364 builder.add_leaf(Nibbles::unpack(*key), &alloy_rlp::encode(account));
365 }
366 let root = builder.root();
367 let proof = builder
368 .take_proof_nodes()
369 .into_nodes_sorted()
370 .into_iter()
371 .map(|(_, node)| node)
372 .collect();
373 (root, proof)
374 }
375
376 fn request(root_hash: B256) -> GetAccountRangeMessage {
377 GetAccountRangeMessage {
378 request_id: 1,
379 root_hash,
380 starting_hash: B256::ZERO,
381 limit_hash: MAX_HASH,
382 response_bytes: 512 * 1024,
383 }
384 }
385
386 fn response(peer: PeerId, message: AccountRangeMessage) -> PeerRequestResult<SnapResponse> {
387 Ok(WithPeerId::new(peer, SnapResponse::AccountRange(message)))
388 }
389
390 fn downloader(
391 client: Arc<TestSnapClient>,
392 request: GetAccountRangeMessage,
393 ) -> Result<AccountRangeDownloader<Arc<TestSnapClient>>, InvalidAccountRange> {
394 AccountRangeDownloader::new(client, request, Runtime::test())
395 }
396
397 #[test]
398 fn verifies_and_decodes_without_an_ambient_runtime() {
399 let accounts = vec![(key(1), account(7)), (key(2), account(8))];
400 let root_hash = root(&accounts);
401 let peer = PeerId::random();
402 let message = AccountRangeMessage {
403 request_id: 1,
404 accounts: accounts
405 .iter()
406 .map(|(key, account)| AccountData::from_trie_account(*key, account))
407 .collect(),
408 proof: Vec::new(),
409 };
410 let client = Arc::new(TestSnapClient::new([response(peer, message)]));
411
412 let downloader = downloader(Arc::clone(&client), request(root_hash)).unwrap();
413 let outcome = futures::executor::block_on(downloader).unwrap();
414
415 assert_eq!(
416 outcome,
417 AccountRangeOutcome::Verified(VerifiedAccountRange {
418 state_root: root_hash,
419 origin: B256::ZERO,
420 accounts,
421 has_more: false,
422 next: None,
423 })
424 );
425 assert!(client.reported().is_empty());
426 assert_eq!(*client.priorities(), [Priority::Normal]);
427 }
428
429 #[tokio::test]
430 async fn invalid_peer_is_reported_and_request_is_retried_at_high_priority() {
431 let accounts = vec![(key(1), account(7))];
432 let root_hash = root(&accounts);
433 let bad_peer = PeerId::random();
434 let good_peer = PeerId::random();
435 let bad = Ok(WithPeerId::new(
436 bad_peer,
437 SnapResponse::ByteCodes(ByteCodesMessage { request_id: 1, codes: Vec::new() }),
438 ));
439 let good = response(
440 good_peer,
441 AccountRangeMessage {
442 request_id: 1,
443 accounts: vec![AccountData::from_trie_account(accounts[0].0, &accounts[0].1)],
444 proof: Vec::new(),
445 },
446 );
447 let client = Arc::new(TestSnapClient::new([bad, good]));
448
449 let outcome = downloader(Arc::clone(&client), request(root_hash)).unwrap().await.unwrap();
450
451 assert!(matches!(outcome, AccountRangeOutcome::Verified(_)));
452 assert_eq!(*client.reported(), [bad_peer]);
453 assert_eq!(*client.priorities(), [Priority::Normal, Priority::High]);
454 }
455
456 #[tokio::test]
457 async fn unavailable_state_is_not_a_bad_peer_response() {
458 let peer = PeerId::random();
459 let message =
460 AccountRangeMessage { request_id: 1, accounts: Vec::new(), proof: Vec::new() };
461 let client = Arc::new(TestSnapClient::new([response(peer, message)]));
462
463 let outcome = downloader(Arc::clone(&client), request(B256::repeat_byte(0x11)))
464 .unwrap()
465 .await
466 .unwrap();
467
468 assert_eq!(outcome, AccountRangeOutcome::Unavailable { peer_id: peer });
469 assert!(client.reported().is_empty());
470 }
471
472 #[test]
473 fn a_subrange_narrows_the_accounts_and_keeps_their_root() {
474 let accounts = vec![(key(1), account(7)), (key(2), account(8)), (key(3), account(9))];
475 let root_hash = root(&accounts);
476 let range = VerifiedAccountRange {
477 state_root: root_hash,
478 origin: B256::ZERO,
479 accounts: accounts.clone(),
480 has_more: false,
481 next: None,
482 };
483
484 let batch = range.batch();
485 let chunk = batch.range(1..3).expect("chunk is inside the batch");
486 let expected =
487 accounts[1..3].iter().map(|(hash, account)| (*hash, account)).collect::<Vec<_>>();
488 assert_eq!(chunk.accounts(), expected);
489 assert_eq!(chunk.state_root(), root_hash);
490
491 assert_eq!(batch.range(2..4), None);
492 }
493
494 #[test]
495 fn code_hashes_are_listed_once_in_the_order_the_accounts_reference_them() {
496 let shared = B256::repeat_byte(0x11);
497 let mut first = account(1);
498 first.code_hash = shared;
499 let mut third = account(3);
500 third.code_hash = shared;
501 let mut fourth = account(4);
502 fourth.code_hash = B256::repeat_byte(0x44);
503 let accounts =
504 vec![(key(1), first), (key(2), account(2)), (key(3), third), (key(4), fourth)];
505 let range = VerifiedAccountRange {
506 state_root: root(&accounts),
507 origin: B256::ZERO,
508 accounts,
509 has_more: false,
510 next: None,
511 };
512
513 assert_eq!(range.code_hashes(), vec![shared, B256::repeat_byte(0x44)]);
514 }
515
516 #[test]
517 fn storage_batch_omits_interleaved_accounts_without_storage() {
518 let mut first = account(1);
519 first.storage_root = B256::repeat_byte(0x11);
520 let empty = account(2);
521 let mut third = account(3);
522 third.storage_root = B256::repeat_byte(0x33);
523 let accounts = vec![(key(1), first), (key(2), empty), (key(3), third)];
524 let root_hash = root(&accounts);
525 let range = VerifiedAccountRange {
526 state_root: root_hash,
527 origin: B256::ZERO,
528 accounts,
529 has_more: false,
530 next: None,
531 };
532
533 let batch = range.storage_batch();
534
535 assert_eq!(batch.state_root(), root_hash);
536 assert_eq!(
537 batch.accounts().iter().map(|(hash, _)| *hash).collect::<Vec<_>>(),
538 vec![key(1), key(3)]
539 );
540
541 let chunk = batch.range(1..2).expect("chunk is inside the batch");
542 assert_eq!(
543 chunk.accounts().iter().map(|(hash, _)| *hash).collect::<Vec<_>>(),
544 vec![key(3)]
545 );
546 assert_eq!(chunk.state_root(), root_hash);
547 }
548
549 #[test]
550 fn invalid_request_range_is_rejected_before_submission() {
551 let client = Arc::new(TestSnapClient::new(std::iter::empty()));
552 let mut request = request(B256::repeat_byte(0x11));
553 request.starting_hash = key(2);
554 request.limit_hash = key(1);
555
556 assert!(matches!(
557 downloader(Arc::clone(&client), request),
558 Err(InvalidAccountRange { .. })
559 ));
560 assert!(client.priorities().is_empty());
561 }
562
563 #[tokio::test]
564 async fn authenticates_then_trims_an_account_past_the_limit() {
565 let accounts = vec![(key(1), account(7)), (key(3), account(8)), (key(4), account(9))];
566 let (root_hash, proof) = root_and_proof(&accounts, &[key(1), key(3)]);
567 let peer = PeerId::random();
568 let message = AccountRangeMessage {
569 request_id: 1,
570 accounts: accounts[..2]
571 .iter()
572 .map(|(key, account)| AccountData::from_trie_account(*key, account))
573 .collect(),
574 proof,
575 };
576 let client = Arc::new(TestSnapClient::new([response(peer, message)]));
577 let mut request = request(root_hash);
578 request.limit_hash = key(2);
579
580 let outcome = downloader(Arc::clone(&client), request).unwrap().await.unwrap();
581
582 assert_eq!(
583 outcome,
584 AccountRangeOutcome::Verified(VerifiedAccountRange {
585 state_root: root_hash,
586 origin: B256::ZERO,
587 accounts: vec![accounts[0].clone()],
588 has_more: false,
589 next: Some(key(3)),
590 })
591 );
592 assert!(client.reported().is_empty());
593 }
594
595 #[tokio::test]
596 async fn range_running_past_the_limit_is_rejected() {
597 let accounts = vec![(key(1), account(7)), (key(3), account(8)), (key(4), account(9))];
598 let (root_hash, proof) = root_and_proof(&accounts, &[key(1), key(4)]);
599 let peer = PeerId::random();
600 let message = AccountRangeMessage {
601 request_id: 1,
602 accounts: accounts
603 .iter()
604 .map(|(key, account)| AccountData::from_trie_account(*key, account))
605 .collect(),
606 proof,
607 };
608 let attempts = usize::from(MAX_RETRIES) + 1;
609 let client = Arc::new(TestSnapClient::new(
610 std::iter::repeat_with(|| response(peer, message.clone())).take(attempts),
611 ));
612 let mut request = request(root_hash);
613 request.limit_hash = key(2);
614
615 let error = downloader(Arc::clone(&client), request).unwrap().await.unwrap_err();
616
617 assert_eq!(error, RequestError::BadResponse);
618 assert_eq!(client.reported().len(), attempts);
619 }
620
621 #[tokio::test]
622 async fn account_at_the_limit_completes_the_requested_interval() {
623 let accounts = vec![(key(1), account(7)), (key(2), account(8)), (key(3), account(9))];
624 let (root_hash, proof) = root_and_proof(&accounts, &[key(1), key(2)]);
625 let peer = PeerId::random();
626 let message = AccountRangeMessage {
627 request_id: 1,
628 accounts: accounts[..2]
629 .iter()
630 .map(|(key, account)| AccountData::from_trie_account(*key, account))
631 .collect(),
632 proof,
633 };
634 let client = Arc::new(TestSnapClient::new([response(peer, message)]));
635 let mut request = request(root_hash);
636 request.limit_hash = key(2);
637
638 let outcome = downloader(Arc::clone(&client), request).unwrap().await.unwrap();
639
640 assert_eq!(
641 outcome,
642 AccountRangeOutcome::Verified(VerifiedAccountRange {
643 state_root: root_hash,
644 origin: B256::ZERO,
645 accounts: accounts[..2].to_vec(),
646 has_more: false,
647 next: Some(key(3)),
648 })
649 );
650 assert!(client.reported().is_empty());
651 }
652
653 #[tokio::test]
655 async fn empty_interval_is_proven_by_the_first_account_after_the_limit() {
656 let accounts = vec![(key(1), account(7)), (key(9), account(8))];
657 let (root_hash, proof) = root_and_proof(&accounts, &[key(3), key(9)]);
658 let peer = PeerId::random();
659 let message = AccountRangeMessage {
660 request_id: 1,
661 accounts: vec![AccountData::from_trie_account(accounts[1].0, &accounts[1].1)],
662 proof,
663 };
664 let client = Arc::new(TestSnapClient::new([response(peer, message)]));
665 let mut request = request(root_hash);
666 request.starting_hash = key(3);
667 request.limit_hash = key(5);
668
669 let outcome = downloader(Arc::clone(&client), request).unwrap().await.unwrap();
670
671 assert_eq!(
672 outcome,
673 AccountRangeOutcome::Verified(VerifiedAccountRange {
674 state_root: root_hash,
675 origin: key(3),
676 accounts: Vec::new(),
677 has_more: false,
678 next: Some(key(9)),
679 })
680 );
681 assert!(client.reported().is_empty());
682 }
683
684 #[tokio::test]
685 async fn empty_interval_is_proven_without_a_boundary_account() {
686 let accounts = vec![(key(1), account(7)), (key(9), account(8))];
687 let (root_hash, proof) = root_and_proof(&accounts, &[key(3), key(5)]);
688 let peer = PeerId::random();
689 let message = AccountRangeMessage { request_id: 1, accounts: Vec::new(), proof };
690 let client = Arc::new(TestSnapClient::new([response(peer, message)]));
691 let mut request = request(root_hash);
692 request.starting_hash = key(3);
693 request.limit_hash = key(5);
694
695 let outcome = downloader(Arc::clone(&client), request).unwrap().await.unwrap();
696
697 assert_eq!(
698 outcome,
699 AccountRangeOutcome::Verified(VerifiedAccountRange {
700 state_root: root_hash,
701 origin: key(3),
702 accounts: Vec::new(),
703 has_more: false,
704 next: Some(key(9)),
705 })
706 );
707 assert!(client.reported().is_empty());
708 }
709
710 #[tokio::test]
712 async fn range_ending_before_the_limit_needs_no_further_request() {
713 let accounts = vec![(key(1), account(7)), (key(9), account(8))];
714 let (root_hash, proof) = root_and_proof(&accounts, &[key(1)]);
715 let peer = PeerId::random();
716 let message = AccountRangeMessage {
717 request_id: 1,
718 accounts: vec![AccountData::from_trie_account(accounts[0].0, &accounts[0].1)],
719 proof,
720 };
721 let client = Arc::new(TestSnapClient::new([response(peer, message)]));
722 let mut request = request(root_hash);
723 request.limit_hash = key(5);
724
725 let outcome = downloader(Arc::clone(&client), request).unwrap().await.unwrap();
726
727 assert_eq!(
728 outcome,
729 AccountRangeOutcome::Verified(VerifiedAccountRange {
730 state_root: root_hash,
731 origin: B256::ZERO,
732 accounts: vec![accounts[0].clone()],
733 has_more: false,
734 next: Some(key(9)),
735 })
736 );
737 }
738
739 #[tokio::test]
740 async fn range_ending_before_a_covered_key_reports_more() {
741 let accounts = vec![(key(1), account(7)), (key(3), account(8))];
742 let (root_hash, proof) = root_and_proof(&accounts, &[key(1)]);
743 let peer = PeerId::random();
744 let message = AccountRangeMessage {
745 request_id: 1,
746 accounts: vec![AccountData::from_trie_account(accounts[0].0, &accounts[0].1)],
747 proof,
748 };
749 let client = Arc::new(TestSnapClient::new([response(peer, message)]));
750 let mut request = request(root_hash);
751 request.limit_hash = key(5);
752
753 let outcome = downloader(Arc::clone(&client), request).unwrap().await.unwrap();
754
755 assert_eq!(
756 outcome,
757 AccountRangeOutcome::Verified(VerifiedAccountRange {
758 state_root: root_hash,
759 origin: B256::ZERO,
760 accounts: vec![accounts[0].clone()],
761 has_more: true,
762 next: Some(key(3)),
763 })
764 );
765 }
766
767 #[tokio::test]
768 async fn request_errors_retry_without_duplicate_peer_penalties() {
769 let accounts = vec![(key(1), account(7))];
770 let root_hash = root(&accounts);
771 let peer = PeerId::random();
772 let good = response(
773 peer,
774 AccountRangeMessage {
775 request_id: 1,
776 accounts: vec![AccountData::from_trie_account(accounts[0].0, &accounts[0].1)],
777 proof: Vec::new(),
778 },
779 );
780 let client = Arc::new(TestSnapClient::new([
781 Err(RequestError::Timeout),
782 Err(RequestError::BadResponse),
783 good,
784 ]));
785
786 downloader(Arc::clone(&client), request(root_hash)).unwrap().await.unwrap();
787
788 assert!(client.reported().is_empty());
789 assert_eq!(*client.priorities(), [Priority::Normal, Priority::High, Priority::High]);
790 }
791
792 #[tokio::test]
793 async fn stops_after_the_retry_budget_is_exhausted() {
794 let peers = [PeerId::random(), PeerId::random(), PeerId::random()];
795 let responses = peers.map(|peer| {
796 Ok(WithPeerId::new(
797 peer,
798 SnapResponse::ByteCodes(ByteCodesMessage { request_id: 1, codes: Vec::new() }),
799 ))
800 });
801 let client = Arc::new(TestSnapClient::new(responses));
802
803 let error = downloader(Arc::clone(&client), request(B256::repeat_byte(0x11)))
804 .unwrap()
805 .await
806 .unwrap_err();
807
808 assert_eq!(error, RequestError::BadResponse);
809 assert_eq!(*client.reported(), peers);
810 assert_eq!(*client.priorities(), [Priority::Normal, Priority::High, Priority::High]);
811 }
812}