Skip to main content

reth_network_p2p/
full_block.rs

1use super::headers::client::HeadersRequest;
2use crate::{
3    block_access_lists::client::{BalRequirement, BlockAccessListsClient},
4    bodies::client::{BodiesClient, SingleBodyRequest},
5    download::DownloadClient,
6    error::PeerRequestResult,
7    headers::client::{HeadersClient, SingleHeaderRequest},
8    priority::Priority,
9    BlockClient,
10};
11use alloy_consensus::BlockHeader;
12use alloy_eip7928::bal::RawBal;
13use alloy_primitives::{Bytes, Sealable, B256};
14use core::marker::PhantomData;
15use futures::FutureExt;
16use reth_consensus::Consensus;
17use reth_eth_wire_types::{
18    BlockAccessLists, EthNetworkPrimitives, HeadersDirection, NetworkPrimitives,
19};
20use reth_network_peers::{PeerId, WithPeerId};
21use reth_primitives_traits::{Block, SealedBlock, SealedBlockWith, SealedHeader};
22use std::{
23    cmp::Reverse,
24    collections::{HashMap, VecDeque},
25    fmt::Debug,
26    hash::Hash,
27    ops::RangeInclusive,
28    pin::Pin,
29    sync::Arc,
30    task::{ready, Context, Poll},
31};
32use tracing::{debug, trace};
33
34/// A sealed block with optional validated raw block access-list data.
35pub type SealedBlockWithAccessList<B> = SealedBlockWith<B, Option<RawBal>>;
36
37/// A Client that can fetch full blocks from the network.
38#[derive(Debug, Clone)]
39pub struct FullBlockClient<Client>
40where
41    Client: BlockClient,
42{
43    client: Client,
44    consensus: Arc<dyn Consensus<Client::Block>>,
45}
46
47impl<Client> FullBlockClient<Client>
48where
49    Client: BlockClient,
50{
51    /// Creates a new instance of `FullBlockClient`.
52    pub fn new(client: Client, consensus: Arc<dyn Consensus<Client::Block>>) -> Self {
53        Self { client, consensus }
54    }
55
56    /// Returns a client with Test consensus
57    #[cfg(any(test, feature = "test-utils"))]
58    pub fn test_client(client: Client) -> Self {
59        Self::new(client, Arc::new(reth_consensus::test_utils::TestConsensus::default()))
60    }
61}
62
63impl<Client> FullBlockClient<Client>
64where
65    Client: BlockClient,
66{
67    /// Returns a future that fetches the [`SealedBlock`] for the given hash.
68    ///
69    /// Note: this future is cancel safe
70    ///
71    /// Caution: This does no validation of body (transactions) response but guarantees that the
72    /// [`SealedHeader`] matches the requested hash.
73    pub fn get_full_block(&self, hash: B256) -> FetchFullBlockFuture<Client> {
74        FetchFullBlockFuture::new(self.client.clone(), self.consensus.clone(), hash)
75    }
76
77    /// Returns a future that fetches [`SealedBlock`]s for the given hash and count.
78    ///
79    /// Note: this future is cancel safe.
80    ///
81    /// Caution: This does no validation of body (transactions) responses but guarantees that
82    /// the headers form a valid range starting at the requested hash, and that the number of
83    /// headers and bodies received matches the requested limit.
84    ///
85    /// The returned future yields bodies in falling order, i.e. with descending block numbers.
86    pub fn get_full_block_range(
87        &self,
88        hash: B256,
89        count: u64,
90    ) -> FetchFullBlockRangeFuture<Client> {
91        let client = self.client.clone();
92        FetchFullBlockRangeFuture {
93            start_hash: hash,
94            count,
95            request: FullBlockRangeRequest {
96                headers: Some(client.get_headers(HeadersRequest::falling(hash.into(), count))),
97                bodies: None,
98            },
99            client,
100            headers: None,
101            pending_headers: VecDeque::new(),
102            bodies: HashMap::default(),
103            consensus: Arc::clone(&self.consensus),
104        }
105    }
106}
107
108impl<Client> FullBlockClient<Client>
109where
110    Client: BlockClient + BlockAccessListsClient,
111{
112    /// Returns a future that fetches the [`SealedBlock`] and optionally its block access list
113    /// for the given hash.
114    ///
115    /// Note: this future is cancel safe
116    ///
117    /// Caution: This does no validation of body (transactions) response but guarantees that the
118    /// [`SealedHeader`] matches the requested hash.
119    pub fn get_full_block_with_access_lists(
120        &self,
121        hash: B256,
122    ) -> FetchFullBlockWithBalFuture<Client> {
123        self.get_full_block_with_access_lists_with_requirement(hash, BalRequirement::default())
124    }
125
126    /// Returns a future that fetches the [`SealedBlock`] and optionally its block access list
127    /// for the given hash using the requested BAL availability policy.
128    ///
129    /// Note: this future is cancel safe
130    ///
131    /// Caution: This does no validation of body (transactions) response but guarantees that the
132    /// [`SealedHeader`] matches the requested hash.
133    pub fn get_full_block_with_access_lists_with_requirement(
134        &self,
135        hash: B256,
136        requirement: BalRequirement,
137    ) -> FetchFullBlockWithBalFuture<Client> {
138        let client = self.client.clone();
139        FetchFullBlockWithBalFuture {
140            block: FetchFullBlockFuture::new(client.clone(), self.consensus.clone(), hash),
141            block_result: None,
142            bal_request_state: BalRequestState::Pending(
143                client.get_block_access_lists_with_requirement(vec![hash], requirement),
144            ),
145        }
146    }
147
148    /// Returns a future that fetches [`SealedBlock`]s and optionally their block access lists for
149    /// the given hash and count.
150    ///
151    /// The block range is always the primary result. Access lists are requested after the block
152    /// range is downloaded. Each block contains access-list data when the optional BAL response
153    /// included the corresponding entry.
154    pub fn get_full_block_range_with_optional_access_lists(
155        &self,
156        hash: B256,
157        count: u64,
158    ) -> FetchFullBlockRangeWithBalFuture<Client> {
159        self.get_full_block_range_with_optional_access_lists_with_requirement(
160            hash,
161            count,
162            BalRequirement::default(),
163        )
164    }
165
166    /// Returns a future that fetches [`SealedBlock`]s and optionally their block access lists for
167    /// the given hash and count using the requested BAL availability policy.
168    ///
169    /// The block range is always the primary result. Access lists are requested after the block
170    /// range is downloaded. Each block contains access-list data when the optional BAL response
171    /// included the corresponding entry.
172    pub fn get_full_block_range_with_optional_access_lists_with_requirement(
173        &self,
174        hash: B256,
175        count: u64,
176        requirement: BalRequirement,
177    ) -> FetchFullBlockRangeWithBalFuture<Client> {
178        let client = self.client.clone();
179        FetchFullBlockRangeWithBalFuture {
180            blocks: self.get_full_block_range(hash, count),
181            client,
182            block_result: None,
183            access_lists: OptionalBlockAccessListsState::WaitingForBlocks { requirement },
184        }
185    }
186}
187
188/// A future that downloads a full block from the network.
189///
190/// This will attempt to fetch both the header and body for the given block hash at the same time.
191/// When both requests succeed, the future will yield the full block.
192#[must_use = "futures do nothing unless polled"]
193pub struct FetchFullBlockFuture<Client>
194where
195    Client: BlockClient,
196{
197    client: Client,
198    consensus: Arc<dyn Consensus<Client::Block>>,
199    hash: B256,
200    request: FullBlockRequest<Client>,
201    header: Option<SealedHeader<Client::Header>>,
202    body: Option<BodyResponse<Client::Body>>,
203}
204
205impl<Client> FetchFullBlockFuture<Client>
206where
207    Client: BlockClient,
208{
209    fn new(client: Client, consensus: Arc<dyn Consensus<Client::Block>>, hash: B256) -> Self {
210        Self {
211            hash,
212            consensus,
213            request: FullBlockRequest {
214                header: Some(client.get_header(hash.into())),
215                body: Some(client.get_block_body(hash)),
216            },
217            client,
218            header: None,
219            body: None,
220        }
221    }
222
223    /// Returns the hash of the block being requested.
224    pub const fn hash(&self) -> &B256 {
225        &self.hash
226    }
227
228    /// If the header request is already complete, this returns the block number
229    pub fn block_number(&self) -> Option<u64> {
230        self.header.as_ref().map(|h| h.number())
231    }
232
233    /// Returns the [`SealedBlock`] if the request is complete and valid.
234    fn take_block(&mut self) -> Option<SealedBlock<Client::Block>> {
235        if self.header.is_none() || self.body.is_none() {
236            return None
237        }
238
239        let header = self.header.take().unwrap();
240        let resp = self.body.take().unwrap();
241        match resp {
242            BodyResponse::Validated(body) => Some(SealedBlock::from_sealed_parts(header, body)),
243            BodyResponse::PendingValidation(resp) => {
244                // ensure the block is valid, else retry
245                if let Err(err) = self.consensus.validate_body_against_header(resp.data(), &header)
246                {
247                    debug!(target: "downloaders", %err, hash=?header.hash(), "Received wrong body");
248                    self.client.report_bad_message(resp.peer_id());
249                    self.header = Some(header);
250                    self.request.body = Some(self.client.get_block_body(self.hash));
251                    return None
252                }
253                Some(SealedBlock::from_sealed_parts(header, resp.into_data()))
254            }
255        }
256    }
257
258    fn on_block_response(&mut self, resp: WithPeerId<Client::Body>) {
259        if let Some(ref header) = self.header {
260            if let Err(err) = self.consensus.validate_body_against_header(resp.data(), header) {
261                debug!(target: "downloaders", %err, hash=?header.hash(), "Received wrong body");
262                self.client.report_bad_message(resp.peer_id());
263                return
264            }
265            self.body = Some(BodyResponse::Validated(resp.into_data()));
266            return
267        }
268        self.body = Some(BodyResponse::PendingValidation(resp));
269    }
270}
271
272impl<Client> Future for FetchFullBlockFuture<Client>
273where
274    Client: BlockClient<Header: BlockHeader + Sealable> + 'static,
275{
276    type Output = SealedBlock<Client::Block>;
277
278    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
279        let this = self.get_mut();
280
281        // preemptive yield point
282        let mut budget = 4;
283
284        loop {
285            match ready!(this.request.poll(cx)) {
286                ResponseResult::Header(res) => {
287                    match res {
288                        Ok(maybe_header) => {
289                            let (peer, maybe_header) =
290                                maybe_header.map(|h| h.map(SealedHeader::seal_slow)).split();
291                            if let Some(header) = maybe_header {
292                                if header.hash() == this.hash {
293                                    this.header = Some(header);
294                                } else {
295                                    debug!(target: "downloaders", expected=?this.hash, received=?header.hash(), "Received wrong header");
296                                    // received a different header than requested
297                                    this.client.report_bad_message(peer)
298                                }
299                            }
300                        }
301                        Err(err) => {
302                            debug!(target: "downloaders", %err, ?this.hash, "Header download failed");
303                        }
304                    }
305
306                    if this.header.is_none() {
307                        // received bad response
308                        this.request.header = Some(this.client.get_header(this.hash.into()));
309                    }
310                }
311                ResponseResult::Body(res) => {
312                    match res {
313                        Ok(maybe_body) => {
314                            if let Some(body) = maybe_body.transpose() {
315                                this.on_block_response(body);
316                            }
317                        }
318                        Err(err) => {
319                            debug!(target: "downloaders", %err, ?this.hash, "Body download failed");
320                        }
321                    }
322                    if this.body.is_none() {
323                        // received bad response
324                        this.request.body = Some(this.client.get_block_body(this.hash));
325                    }
326                }
327            }
328
329            if let Some(res) = this.take_block() {
330                return Poll::Ready(res)
331            }
332
333            // ensure we still have enough budget for another iteration
334            budget -= 1;
335            if budget == 0 {
336                // make sure we're woken up again
337                cx.waker().wake_by_ref();
338                return Poll::Pending
339            }
340        }
341    }
342}
343
344/// A future that downloads a full block and optionally its block access lists from the network.
345///
346/// This composes the existing full block downloader with a block access list request so the
347/// header/body logic stays centralized. Missing access lists do not block returning the full block.
348#[must_use = "futures do nothing unless polled"]
349pub struct FetchFullBlockWithBalFuture<Client>
350where
351    Client: BlockClient + BlockAccessListsClient,
352{
353    block: FetchFullBlockFuture<Client>,
354    block_result: Option<SealedBlock<Client::Block>>,
355    bal_request_state: BalRequestState<<Client as BlockAccessListsClient>::Output>,
356}
357
358impl<Client> FetchFullBlockWithBalFuture<Client>
359where
360    Client: BlockClient<Header: BlockHeader> + BlockAccessListsClient,
361{
362    /// Returns the hash of the block being requested.
363    pub const fn hash(&self) -> &B256 {
364        self.block.hash()
365    }
366}
367
368impl<Client> FetchFullBlockWithBalFuture<Client>
369where
370    Client: BlockClient<Header: BlockHeader + Sealable> + BlockAccessListsClient + 'static,
371{
372    /// If the header request is already complete, this returns the block number.
373    pub fn block_number(&self) -> Option<u64> {
374        self.block_result.as_ref().map(|block| block.number()).or_else(|| self.block.block_number())
375    }
376
377    /// Polls the optional BAL request once and records its final best-effort result.
378    ///
379    /// Exactly one returned BAL entry is preserved. Empty responses, malformed responses, and
380    /// request errors resolve to `None` instead of being retried, so a BAL failure cannot block
381    /// returning the downloaded block.
382    fn poll_bal_request(&mut self, cx: &mut Context<'_>) -> Poll<()> {
383        let res = match &mut self.bal_request_state {
384            BalRequestState::Pending(fut) => ready!(fut.poll_unpin(cx)),
385            BalRequestState::Ready(_) => return Poll::Ready(()),
386        };
387
388        match res {
389            Ok(bal) => {
390                let (peer, access_lists) = bal.split();
391                match access_lists.0.len() {
392                    0 => self.bal_request_state = BalRequestState::Ready(None),
393                    1 => {
394                        let bal = access_lists.0.into_iter().next().expect("len checked");
395                        self.bal_request_state =
396                            BalRequestState::Ready(Some(WithPeerId::new(peer, bal)));
397                    }
398                    received => {
399                        debug!(
400                            target: "downloaders",
401                            hash = ?self.block.hash(),
402                            expected = 1,
403                            received,
404                            "Received wrong access list response",
405                        );
406                        self.block.client.report_bad_message(peer);
407                        self.bal_request_state = BalRequestState::Ready(None);
408                    }
409                }
410            }
411            Err(err) => {
412                debug!(
413                    target: "downloaders",
414                    %err,
415                    hash = ?self.block.hash(),
416                    "Access list download failed",
417                );
418                self.bal_request_state = BalRequestState::Ready(None);
419            }
420        }
421
422        Poll::Ready(())
423    }
424
425    /// Returns the block once the block download and optional BAL lookup have both completed.
426    ///
427    /// The BAL lookup must be ready even when it resolved to `None`, which prevents a fast block
428    /// response from racing a still-pending BAL response and incorrectly dropping available BAL
429    /// data.
430    fn take_block_and_access_lists(&mut self) -> Option<SealedBlockWithAccessList<Client::Block>> {
431        let BalRequestState::Ready(bal) = &mut self.bal_request_state else { return None };
432        let block = self.block_result.take()?;
433        let raw_bal =
434            bal.take().and_then(|bal| match seal_block_access_list_for_block(&block, bal) {
435                Ok(raw_bal) => raw_bal,
436                Err(peer) => {
437                    self.block.client.report_bad_message(peer);
438                    None
439                }
440            });
441        Some(SealedBlockWith::new(block, raw_bal))
442    }
443}
444
445impl<Client> Future for FetchFullBlockWithBalFuture<Client>
446where
447    Client: BlockClient<Header: BlockHeader + Sealable> + BlockAccessListsClient + 'static,
448{
449    type Output = SealedBlockWithAccessList<Client::Block>;
450
451    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
452        let this = self.get_mut();
453
454        if this.block_result.is_none() &&
455            let Poll::Ready(block) = this.block.poll_unpin(cx)
456        {
457            this.block_result = Some(block);
458        }
459
460        ready!(this.poll_bal_request(cx));
461
462        if let Some(res) = this.take_block_and_access_lists() {
463            return Poll::Ready(res)
464        }
465
466        Poll::Pending
467    }
468}
469
470impl<Client> Debug for FetchFullBlockWithBalFuture<Client>
471where
472    Client: BlockClient<Header: BlockHeader> + BlockAccessListsClient,
473{
474    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
475        f.debug_struct("FetchFullBlockWithBalFuture")
476            .field("hash", &self.block.hash())
477            .field("block_ready", &self.block_result.is_some())
478            .field("bal_request_ready", &self.bal_request_state.is_ready())
479            .finish()
480    }
481}
482
483/// Tracks the BAL request and its completed result.
484enum BalRequestState<Req> {
485    Pending(Req),
486    Ready(Option<WithPeerId<Option<Bytes>>>),
487}
488
489impl<Req> BalRequestState<Req> {
490    const fn is_ready(&self) -> bool {
491        matches!(self, Self::Ready(_))
492    }
493}
494
495/// A future that downloads a range of full blocks and optionally their block access lists from the
496/// network.
497///
498/// This composes the existing full block range downloader with a follow-up optional block
499/// access-list request, so callers that only need blocks can keep using
500/// [`FetchFullBlockRangeFuture`].
501#[must_use = "futures do nothing unless polled"]
502#[expect(missing_debug_implementations)]
503pub struct FetchFullBlockRangeWithBalFuture<Client>
504where
505    Client: BlockClient + BlockAccessListsClient,
506{
507    blocks: FetchFullBlockRangeFuture<Client>,
508    client: Client,
509    block_result: Option<Vec<SealedBlock<Client::Block>>>,
510    access_lists: OptionalBlockAccessListsState<<Client as BlockAccessListsClient>::Output>,
511}
512
513impl<Client> FetchFullBlockRangeWithBalFuture<Client>
514where
515    Client: BlockClient<Header: Debug + BlockHeader + Sealable + Clone + Hash + Eq>
516        + BlockAccessListsClient,
517{
518    /// Returns the block hash the requested range starts at (inclusive).
519    pub const fn start_hash(&self) -> B256 {
520        self.blocks.start_hash()
521    }
522
523    /// Returns the number of requested blocks.
524    pub const fn count(&self) -> u64 {
525        self.blocks.count()
526    }
527
528    fn start_access_lists_request_if_possible(&mut self) {
529        let requirement = match &self.access_lists {
530            OptionalBlockAccessListsState::WaitingForBlocks { requirement } => *requirement,
531            OptionalBlockAccessListsState::Pending(_) | OptionalBlockAccessListsState::Ready(_) => {
532                return
533            }
534        };
535
536        // BALs are requested by block hash, so wait until the block range is fully assembled.
537        let Some(blocks) = self.block_result.as_ref() else { return };
538        let hashes = blocks.iter().map(|block| block.hash()).collect::<Vec<_>>();
539        self.access_lists = OptionalBlockAccessListsState::Pending(
540            self.client.get_block_access_lists_with_requirement(hashes, requirement),
541        );
542    }
543
544    /// Starts and polls the optional BAL request once, if it is ready to make progress.
545    fn poll_access_lists(&mut self, cx: &mut Context<'_>) {
546        self.start_access_lists_request_if_possible();
547
548        let poll = match &mut self.access_lists {
549            OptionalBlockAccessListsState::Pending(fut) => fut.poll_unpin(cx),
550            OptionalBlockAccessListsState::WaitingForBlocks { .. } |
551            OptionalBlockAccessListsState::Ready(_) => return,
552        };
553
554        match poll {
555            Poll::Pending => {}
556            Poll::Ready(Ok(access_lists)) => {
557                self.access_lists = OptionalBlockAccessListsState::Ready(Some(access_lists));
558            }
559            Poll::Ready(Err(err)) => {
560                debug!(
561                    target: "downloaders",
562                    %err,
563                    start_hash = ?self.blocks.start_hash(),
564                    "Access list range download failed",
565                );
566
567                // Optional BAL lookup is best-effort: missing eth/71 support or request failures
568                // should not block returning the downloaded block range.
569                self.access_lists = OptionalBlockAccessListsState::Ready(None);
570            }
571        }
572    }
573
574    /// Returns the block range once blocks and the optional BAL lookup are both complete.
575    fn take_response(&mut self) -> Option<Vec<SealedBlockWithAccessList<Client::Block>>> {
576        let OptionalBlockAccessListsState::Ready(access_lists) = &mut self.access_lists else {
577            return None
578        };
579
580        let blocks = self.block_result.take()?;
581
582        Some(seal_blocks_with_access_lists(&self.client, blocks, access_lists.take()))
583    }
584}
585
586impl<Client> Future for FetchFullBlockRangeWithBalFuture<Client>
587where
588    Client: BlockClient<Header: Debug + BlockHeader + Sealable + Clone + Hash + Eq>
589        + BlockAccessListsClient
590        + 'static,
591{
592    type Output = Vec<SealedBlockWithAccessList<Client::Block>>;
593
594    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
595        let this = self.get_mut();
596
597        // Complete the normal block range first, then issue a separate BAL hash-list request.
598        if this.block_result.is_none() &&
599            let Poll::Ready(blocks) = this.blocks.poll_unpin(cx)
600        {
601            this.block_result = Some(blocks);
602        }
603
604        this.poll_access_lists(cx);
605
606        if let Some(response) = this.take_response() {
607            return Poll::Ready(response)
608        }
609
610        Poll::Pending
611    }
612}
613
614/// Tracks an optional BAL range request and its completed result.
615enum OptionalBlockAccessListsState<Req> {
616    /// The block hashes needed for `GetBlockAccessLists` are not known yet.
617    WaitingForBlocks {
618        /// The BAL availability policy for the eventual request.
619        requirement: BalRequirement,
620    },
621    /// A `GetBlockAccessLists` request is in flight.
622    Pending(Req),
623    /// `None` means the block range is available but optional BAL data is not.
624    Ready(Option<WithPeerId<BlockAccessLists>>),
625}
626
627impl<Client> Debug for FetchFullBlockFuture<Client>
628where
629    Client: BlockClient<Header: Debug, Body: Debug>,
630{
631    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
632        f.debug_struct("FetchFullBlockFuture")
633            .field("hash", &self.hash)
634            .field("header", &self.header)
635            .field("body", &self.body)
636            .finish()
637    }
638}
639
640struct FullBlockRequest<Client>
641where
642    Client: BlockClient,
643{
644    header: Option<SingleHeaderRequest<<Client as HeadersClient>::Output>>,
645    body: Option<SingleBodyRequest<<Client as BodiesClient>::Output>>,
646}
647
648impl<Client> FullBlockRequest<Client>
649where
650    Client: BlockClient,
651{
652    fn poll(&mut self, cx: &mut Context<'_>) -> Poll<ResponseResult<Client::Header, Client::Body>> {
653        if let Some(fut) = Pin::new(&mut self.header).as_pin_mut() &&
654            let Poll::Ready(res) = fut.poll(cx)
655        {
656            self.header = None;
657            return Poll::Ready(ResponseResult::Header(res))
658        }
659
660        if let Some(fut) = Pin::new(&mut self.body).as_pin_mut() &&
661            let Poll::Ready(res) = fut.poll(cx)
662        {
663            self.body = None;
664            return Poll::Ready(ResponseResult::Body(res))
665        }
666
667        Poll::Pending
668    }
669}
670
671/// The result of a request for a single header or body. This is yielded by the `FullBlockRequest`
672/// future.
673enum ResponseResult<H, B> {
674    Header(PeerRequestResult<Option<H>>),
675    Body(PeerRequestResult<Option<B>>),
676}
677
678/// The response of a body request.
679#[derive(Debug)]
680enum BodyResponse<B> {
681    /// Already validated against transaction root of header
682    Validated(B),
683    /// Still needs to be validated against header
684    PendingValidation(WithPeerId<B>),
685}
686/// A future that downloads a range of full blocks from the network.
687///
688/// This first fetches the headers for the given range using the inner `Client`. Once the request
689/// is complete, it will fetch the bodies for the headers it received.
690///
691/// Once the bodies request completes, the [`SealedBlock`]s will be assembled and the future will
692/// yield the full block range.
693///
694/// The full block range will be returned with falling block numbers, i.e. in descending order.
695///
696/// NOTE: this assumes that bodies responses are returned by the client in the same order as the
697/// hash array used to request them.
698#[must_use = "futures do nothing unless polled"]
699#[expect(missing_debug_implementations)]
700pub struct FetchFullBlockRangeFuture<Client>
701where
702    Client: BlockClient,
703{
704    /// The client used to fetch headers and bodies.
705    client: Client,
706    /// The consensus instance used to validate the blocks.
707    consensus: Arc<dyn Consensus<Client::Block>>,
708    /// The block hash to start fetching from (inclusive).
709    start_hash: B256,
710    /// How many blocks to fetch: `len([start_hash, ..]) == count`
711    count: u64,
712    /// Requests for headers and bodies that are in progress.
713    request: FullBlockRangeRequest<Client>,
714    /// Fetched headers.
715    headers: Option<Vec<SealedHeader<Client::Header>>>,
716    /// The next headers to request bodies for. This is drained as responses are received.
717    pending_headers: VecDeque<SealedHeader<Client::Header>>,
718    /// The bodies that have been received so far.
719    bodies: HashMap<SealedHeader<Client::Header>, BodyResponse<Client::Body>>,
720}
721
722impl<Client> FetchFullBlockRangeFuture<Client>
723where
724    Client: BlockClient<Header: Debug + BlockHeader + Sealable + Clone + Hash + Eq>,
725{
726    /// Returns whether or not the bodies map is fully populated with requested headers and bodies.
727    fn is_bodies_complete(&self) -> bool {
728        self.bodies.len() == self.count as usize
729    }
730
731    /// Inserts a block body, matching it with the `next_header`.
732    ///
733    /// Note: this assumes the response matches the next header in the queue.
734    fn insert_body(&mut self, body_response: BodyResponse<Client::Body>) {
735        if let Some(header) = self.pending_headers.pop_front() {
736            self.bodies.insert(header, body_response);
737        }
738    }
739
740    /// Inserts multiple block bodies.
741    fn insert_bodies(&mut self, bodies: impl IntoIterator<Item = BodyResponse<Client::Body>>) {
742        for body in bodies {
743            self.insert_body(body);
744        }
745    }
746
747    /// Returns the remaining hashes for the bodies request, based on the headers that still exist
748    /// in the `root_map`.
749    fn remaining_bodies_hashes(&self) -> Vec<B256> {
750        self.pending_headers.iter().map(|h| h.hash()).collect()
751    }
752
753    /// Returns the [`SealedBlock`]s if the request is complete and valid.
754    ///
755    /// The request is complete if the number of blocks requested is equal to the number of blocks
756    /// received. The request is valid if the returned bodies match the roots in the headers.
757    ///
758    /// These are returned in falling order starting with the requested `hash`, i.e. with
759    /// descending block numbers.
760    fn take_blocks(&mut self) -> Option<Vec<SealedBlock<Client::Block>>> {
761        if !self.is_bodies_complete() {
762            // not done with bodies yet
763            return None
764        }
765
766        let headers = self.headers.take()?;
767        let mut needs_retry = false;
768        let mut valid_responses = Vec::new();
769
770        for header in &headers {
771            if let Some(body_resp) = self.bodies.remove(header) {
772                // validate body w.r.t. the hashes in the header, only inserting into the response
773                let body = match body_resp {
774                    BodyResponse::Validated(body) => body,
775                    BodyResponse::PendingValidation(resp) => {
776                        // ensure the block is valid, else retry
777                        if let Err(err) =
778                            self.consensus.validate_body_against_header(resp.data(), header)
779                        {
780                            debug!(target: "downloaders", %err, hash=?header.hash(), "Received wrong body in range response");
781                            self.client.report_bad_message(resp.peer_id());
782
783                            // get body that doesn't match, put back into vecdeque, and retry it
784                            self.pending_headers.push_back(header.clone());
785                            needs_retry = true;
786                            continue
787                        }
788
789                        resp.into_data()
790                    }
791                };
792
793                valid_responses
794                    .push(SealedBlock::<Client::Block>::from_sealed_parts(header.clone(), body));
795            }
796        }
797
798        if needs_retry {
799            // put response hashes back into bodies map since we aren't returning them as a
800            // response
801            for block in valid_responses {
802                let (header, body) = block.split_sealed_header_body();
803                self.bodies.insert(header, BodyResponse::Validated(body));
804            }
805
806            // put headers back since they were `take`n before
807            self.headers = Some(headers);
808
809            // create response for failing bodies
810            let hashes = self.remaining_bodies_hashes();
811            self.request.bodies = Some(self.client.get_block_bodies(hashes));
812            return None
813        }
814
815        Some(valid_responses)
816    }
817
818    fn on_headers_response(&mut self, headers: WithPeerId<Vec<Client::Header>>) {
819        let (peer, mut headers_falling) =
820            headers.map(|h| h.into_iter().map(SealedHeader::seal_slow).collect::<Vec<_>>()).split();
821
822        // fill in the response if it's the correct length
823        if headers_falling.len() == self.count as usize {
824            // sort headers from highest to lowest block number
825            headers_falling.sort_unstable_by_key(|h| Reverse(h.number()));
826
827            // check the starting hash
828            if headers_falling[0].hash() == self.start_hash {
829                let headers_rising = headers_falling.iter().rev().cloned().collect::<Vec<_>>();
830                // check if the downloaded headers are valid
831                if let Err(err) = self.consensus.validate_header_range(&headers_rising) {
832                    debug!(target: "downloaders", %err, ?self.start_hash, "Received bad header response");
833                    self.client.report_bad_message(peer);
834                    return
835                }
836
837                // get the bodies request so it can be polled later
838                let hashes = headers_falling.iter().map(|h| h.hash()).collect::<Vec<_>>();
839
840                // populate the pending headers
841                self.pending_headers = headers_falling.clone().into();
842
843                // set the actual request if it hasn't been started yet
844                if !self.has_bodies_request_started() {
845                    // request the bodies for the downloaded headers
846                    self.request.bodies = Some(self.client.get_block_bodies(hashes));
847                }
848
849                // set the headers response
850                self.headers = Some(headers_falling);
851            } else {
852                // received a different header than requested
853                self.client.report_bad_message(peer);
854            }
855        }
856    }
857
858    /// Returns whether or not a bodies request has been started, returning false if there is no
859    /// pending request.
860    const fn has_bodies_request_started(&self) -> bool {
861        self.request.bodies.is_some()
862    }
863
864    /// Returns the start hash for the request
865    pub const fn start_hash(&self) -> B256 {
866        self.start_hash
867    }
868
869    /// Returns the block count for the request
870    pub const fn count(&self) -> u64 {
871        self.count
872    }
873}
874
875impl<Client> Future for FetchFullBlockRangeFuture<Client>
876where
877    Client: BlockClient<Header: Debug + BlockHeader + Sealable + Clone + Hash + Eq> + 'static,
878{
879    type Output = Vec<SealedBlock<Client::Block>>;
880
881    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
882        let this = self.get_mut();
883
884        loop {
885            match ready!(this.request.poll(cx)) {
886                // This branch handles headers responses from peers - it first ensures that the
887                // starting hash and number of headers matches what we requested.
888                //
889                // If these don't match, we penalize the peer and retry the request.
890                // If they do match, we sort the headers by block number and start the request for
891                // the corresponding block bodies.
892                //
893                // The next result that should be yielded by `poll` is the bodies response.
894                RangeResponseResult::Header(res) => {
895                    match res {
896                        Ok(headers) => {
897                            this.on_headers_response(headers);
898                        }
899                        Err(err) => {
900                            debug!(target: "downloaders", %err, ?this.start_hash, "Header range download failed");
901                        }
902                    }
903
904                    if this.headers.is_none() {
905                        // did not receive a correct response yet, retry
906                        this.request.headers = Some(this.client.get_headers(HeadersRequest {
907                            start: this.start_hash.into(),
908                            limit: this.count,
909                            direction: HeadersDirection::Falling,
910                        }));
911                    }
912                }
913                // This branch handles block body responses from peers - it first inserts the
914                // bodies into the `bodies` map, and then checks if the request is complete.
915                //
916                // If the request is not complete, and we need to request more bodies, we send
917                // a bodies request for the headers we don't yet have bodies for.
918                RangeResponseResult::Body(res) => {
919                    match res {
920                        Ok(bodies_resp) => {
921                            let (peer, new_bodies) = bodies_resp.split();
922
923                            // first insert the received bodies
924                            this.insert_bodies(
925                                new_bodies
926                                    .into_iter()
927                                    .map(|resp| WithPeerId::new(peer, resp))
928                                    .map(BodyResponse::PendingValidation),
929                            );
930
931                            if !this.is_bodies_complete() {
932                                // get remaining hashes so we can send the next request
933                                let req_hashes = this.remaining_bodies_hashes();
934
935                                // set a new request
936                                this.request.bodies = Some(this.client.get_block_bodies(req_hashes))
937                            }
938                        }
939                        Err(err) => {
940                            debug!(target: "downloaders", %err, ?this.start_hash, "Body range download failed");
941                        }
942                    }
943                    if this.request.bodies.is_none() && !this.is_bodies_complete() {
944                        // no pending bodies request (e.g., request error), retry remaining bodies
945                        // TODO: convert this into two futures, one which is a headers range
946                        // future, and one which is a bodies range future.
947                        //
948                        // The headers range future should yield the bodies range future.
949                        // The bodies range future should not have an Option<Vec<B256>>, it should
950                        // have a populated Vec<B256> from the successful headers range future.
951                        //
952                        // This is optimal because we can not send a bodies request without
953                        // first completing the headers request. This way we can get rid of the
954                        // following `if let Some`. A bodies request should never be sent before
955                        // the headers request completes, so this should always be `Some` anyways.
956                        let hashes = this.remaining_bodies_hashes();
957                        if !hashes.is_empty() {
958                            this.request.bodies = Some(this.client.get_block_bodies(hashes));
959                        }
960                    }
961                }
962            }
963
964            if let Some(res) = this.take_blocks() {
965                return Poll::Ready(res)
966            }
967        }
968    }
969}
970
971/// A request for a range of full blocks. Polling this will poll the inner headers and bodies
972/// futures until they return responses. It will return either the header or body result, depending
973/// on which future successfully returned.
974struct FullBlockRangeRequest<Client>
975where
976    Client: BlockClient,
977{
978    headers: Option<<Client as HeadersClient>::Output>,
979    bodies: Option<<Client as BodiesClient>::Output>,
980}
981
982impl<Client> FullBlockRangeRequest<Client>
983where
984    Client: BlockClient,
985{
986    fn poll(
987        &mut self,
988        cx: &mut Context<'_>,
989    ) -> Poll<RangeResponseResult<Client::Header, Client::Body>> {
990        if let Some(fut) = Pin::new(&mut self.headers).as_pin_mut() &&
991            let Poll::Ready(res) = fut.poll(cx)
992        {
993            self.headers = None;
994            return Poll::Ready(RangeResponseResult::Header(res))
995        }
996
997        if let Some(fut) = Pin::new(&mut self.bodies).as_pin_mut() &&
998            let Poll::Ready(res) = fut.poll(cx)
999        {
1000            self.bodies = None;
1001            return Poll::Ready(RangeResponseResult::Body(res))
1002        }
1003
1004        Poll::Pending
1005    }
1006}
1007
1008// The result of a request for headers or block bodies. This is yielded by the
1009// `FullBlockRangeRequest` future.
1010enum RangeResponseResult<H, B> {
1011    Header(PeerRequestResult<Vec<H>>),
1012    Body(PeerRequestResult<Vec<B>>),
1013}
1014
1015/// A headers+bodies client implementation that does nothing.
1016#[derive(Debug, Clone)]
1017#[non_exhaustive]
1018pub struct NoopFullBlockClient<Net = EthNetworkPrimitives>(PhantomData<Net>);
1019
1020/// Implements the `DownloadClient` trait for the `NoopFullBlockClient` struct.
1021impl<Net> DownloadClient for NoopFullBlockClient<Net>
1022where
1023    Net: Debug + Send + Sync,
1024{
1025    /// Reports a bad message received from a peer.
1026    ///
1027    /// # Arguments
1028    ///
1029    /// * `_peer_id` - Identifier for the peer sending the bad message (unused in this
1030    ///   implementation).
1031    fn report_bad_message(&self, _peer_id: PeerId) {}
1032
1033    /// Retrieves the number of connected peers.
1034    ///
1035    /// # Returns
1036    ///
1037    /// The number of connected peers, which is always zero in this implementation.
1038    fn num_connected_peers(&self) -> usize {
1039        0
1040    }
1041}
1042
1043/// Implements the `BodiesClient` trait for the `NoopFullBlockClient` struct.
1044impl<Net> BodiesClient for NoopFullBlockClient<Net>
1045where
1046    Net: NetworkPrimitives,
1047{
1048    type Body = Net::BlockBody;
1049    /// Defines the output type of the function.
1050    type Output = futures::future::Ready<PeerRequestResult<Vec<Self::Body>>>;
1051
1052    /// Retrieves block bodies based on provided hashes and priority.
1053    ///
1054    /// # Arguments
1055    ///
1056    /// * `_hashes` - A vector of block hashes (unused in this implementation).
1057    /// * `_priority` - Priority level for block body retrieval (unused in this implementation).
1058    ///
1059    /// # Returns
1060    ///
1061    /// A future containing an empty vector of block bodies and a randomly generated `PeerId`.
1062    fn get_block_bodies_with_priority_and_range_hint(
1063        &self,
1064        _hashes: Vec<B256>,
1065        _priority: Priority,
1066        _range_hint: Option<RangeInclusive<u64>>,
1067    ) -> Self::Output {
1068        // Create a future that immediately returns an empty vector of block bodies and a random
1069        // PeerId.
1070        futures::future::ready(Ok(WithPeerId::new(PeerId::random(), vec![])))
1071    }
1072}
1073
1074impl<Net> HeadersClient for NoopFullBlockClient<Net>
1075where
1076    Net: NetworkPrimitives,
1077{
1078    type Header = Net::BlockHeader;
1079    /// The output type representing a future containing a peer request result with a vector of
1080    /// headers.
1081    type Output = futures::future::Ready<PeerRequestResult<Vec<Self::Header>>>;
1082
1083    /// Retrieves headers with a specified priority level.
1084    ///
1085    /// This implementation does nothing and returns an empty vector of headers.
1086    ///
1087    /// # Arguments
1088    ///
1089    /// * `_request` - A request for headers (unused in this implementation).
1090    /// * `_priority` - The priority level for the headers request (unused in this implementation).
1091    ///
1092    /// # Returns
1093    ///
1094    /// Always returns a ready future with an empty vector of headers wrapped in a
1095    /// `PeerRequestResult`.
1096    fn get_headers_with_priority(
1097        &self,
1098        _request: HeadersRequest,
1099        _priority: Priority,
1100    ) -> Self::Output {
1101        futures::future::ready(Ok(WithPeerId::new(PeerId::random(), vec![])))
1102    }
1103}
1104
1105impl<Net> BlockClient for NoopFullBlockClient<Net>
1106where
1107    Net: NetworkPrimitives,
1108{
1109    type Block = Net::Block;
1110}
1111
1112impl<Net> BlockAccessListsClient for NoopFullBlockClient<Net>
1113where
1114    Net: NetworkPrimitives,
1115{
1116    type Output = futures::future::Ready<PeerRequestResult<BlockAccessLists>>;
1117
1118    fn get_block_access_lists_with_priority_and_requirement(
1119        &self,
1120        _hashes: Vec<B256>,
1121        _priority: Priority,
1122        _requirement: BalRequirement,
1123    ) -> Self::Output {
1124        futures::future::ready(Ok(WithPeerId::new(PeerId::random(), BlockAccessLists::default())))
1125    }
1126}
1127
1128impl<Net> Default for NoopFullBlockClient<Net> {
1129    fn default() -> Self {
1130        Self(PhantomData::<Net>)
1131    }
1132}
1133
1134/// Validates one raw block access-list entry against the block's access-list hash.
1135///
1136/// Returns `Ok(Some(_))` for a matching entry, `Ok(None)` when the block has no access-list hash
1137/// or the peer returned an unavailable entry, and `Err(peer)` for a hash mismatch that should be
1138/// reported as a bad message.
1139fn seal_block_access_list_for_block<B: Block>(
1140    block: &SealedBlock<B>,
1141    bal: WithPeerId<Option<Bytes>>,
1142) -> Result<Option<RawBal>, PeerId> {
1143    let Some(expected) = block.header().block_access_list_hash() else { return Ok(None) };
1144
1145    let (peer, bal) = bal.split();
1146    let Some(bal) = bal else { return Ok(None) };
1147    let raw_bal = RawBal::new(bal);
1148    raw_bal.ensure_hash(expected).map_err(|error| {
1149        debug!(
1150            target: "downloaders",
1151            block_hash = ?block.hash(),
1152            computed = ?error.computed,
1153            expected = ?error.expected,
1154            "Received block access list with wrong hash",
1155        );
1156        peer
1157    })?;
1158    Ok(Some(raw_bal))
1159}
1160
1161/// Wraps a block range with validated block access-list entries.
1162///
1163/// Short responses are treated as a valid prefix and the remaining blocks receive `None`.
1164/// Responses longer than the requested block range return the full block range without access-list
1165/// data. Non-empty hash mismatches stop accepting access-list data after reporting the peer, so any
1166/// already validated prefix is preserved.
1167fn seal_blocks_with_access_lists<Client>(
1168    client: &Client,
1169    blocks: Vec<SealedBlock<Client::Block>>,
1170    access_lists: Option<WithPeerId<BlockAccessLists>>,
1171) -> Vec<SealedBlockWithAccessList<Client::Block>>
1172where
1173    Client: BlockClient,
1174{
1175    let Some(access_lists) = access_lists else {
1176        return blocks.into_iter().map(SealedBlockWith::from_block).collect()
1177    };
1178
1179    let (peer, access_lists) = access_lists.split();
1180    let expected = blocks.len();
1181    let received = access_lists.0.len();
1182
1183    if received > expected {
1184        trace!(
1185            target: "downloaders",
1186            expected,
1187            received,
1188            "Ignoring overlong access list range response",
1189        );
1190        return blocks.into_iter().map(SealedBlockWith::from_block).collect()
1191    }
1192
1193    let mut access_lists = access_lists.0.into_iter();
1194    let mut blocks = blocks.into_iter();
1195    let mut response = Vec::with_capacity(expected);
1196
1197    for block in blocks.by_ref() {
1198        let Some(bal) = access_lists.next() else {
1199            // Short BAL responses are valid; the current block and all remaining blocks are
1200            // returned without access-list data below.
1201            response.push(SealedBlockWith::from_block(block));
1202            break
1203        };
1204
1205        match seal_block_access_list_for_block(&block, WithPeerId::new(peer, bal)) {
1206            Ok(raw_bal) => response.push(SealedBlockWith::new(block, raw_bal)),
1207            Err(peer) => {
1208                // A hash mismatch means this BAL entry is not for the current block. Stop matching
1209                // later positional entries and return the rest of the range without BAL data.
1210                client.report_bad_message(peer);
1211                response.push(SealedBlockWith::from_block(block));
1212                break
1213            }
1214        }
1215    }
1216
1217    response.extend(blocks.map(SealedBlockWith::from_block));
1218    response
1219}
1220
1221#[cfg(test)]
1222mod tests {
1223    use reth_ethereum_primitives::BlockBody;
1224
1225    use super::*;
1226    use crate::{error::RequestError, test_utils::TestFullBlockClient};
1227    use alloy_consensus::Header;
1228    use alloy_primitives::{keccak256, map::B256Map, Bytes};
1229    use parking_lot::Mutex;
1230    use std::{
1231        ops::Range,
1232        sync::{
1233            atomic::{AtomicBool, AtomicUsize, Ordering},
1234            Arc,
1235        },
1236    };
1237
1238    const EMPTY_LIST_CODE: u8 = 0xc0;
1239    use tokio::time::{timeout, Duration};
1240
1241    fn sealed_header_with_access_list_hash(bal: &Bytes) -> SealedHeader {
1242        let header =
1243            Header { block_access_list_hash: Some(keccak256(bal.as_ref())), ..Default::default() };
1244        SealedHeader::seal_slow(header)
1245    }
1246
1247    fn range_access_lists<B: Block>(
1248        blocks: &[SealedBlockWithAccessList<B>],
1249    ) -> Vec<Option<RawBal>> {
1250        blocks.iter().map(|block| block.data().clone()).collect()
1251    }
1252
1253    #[tokio::test]
1254    async fn download_single_full_block() {
1255        let client = TestFullBlockClient::default();
1256        let header: SealedHeader = SealedHeader::default();
1257        let body = BlockBody::default();
1258        client.insert(header.clone(), body.clone());
1259        let client = FullBlockClient::test_client(client);
1260
1261        let received = client.get_full_block(header.hash()).await;
1262        assert_eq!(received, SealedBlock::from_sealed_parts(header, body));
1263    }
1264
1265    #[tokio::test]
1266    async fn download_single_full_block_range() {
1267        let client = TestFullBlockClient::default();
1268        let header: SealedHeader = SealedHeader::default();
1269        let body = BlockBody::default();
1270        client.insert(header.clone(), body.clone());
1271        let client = FullBlockClient::test_client(client);
1272
1273        let received = client.get_full_block_range(header.hash(), 1).await;
1274        let received = received.first().expect("response should include a block");
1275        assert_eq!(*received, SealedBlock::from_sealed_parts(header, body));
1276    }
1277
1278    #[tokio::test]
1279    async fn download_single_full_block_with_access_lists() {
1280        let client = FullBlockWithAccessListsClient::default();
1281        let body = BlockBody::default();
1282        let bal = Bytes::from_static(&[EMPTY_LIST_CODE]);
1283        let header = sealed_header_with_access_list_hash(&bal);
1284        client.insert(header.clone(), body.clone(), bal.clone());
1285
1286        let request_count = Arc::clone(&client.access_list_requests);
1287        let client = FullBlockClient::test_client(client);
1288
1289        let received = client.get_full_block_with_access_lists(header.hash()).await;
1290        let expected_raw_bal = RawBal::from(bal);
1291
1292        assert_eq!(received.block(), &SealedBlock::from_sealed_parts(header, body));
1293        assert_eq!(received.data().as_ref(), Some(&expected_raw_bal));
1294        assert_eq!(request_count.load(Ordering::SeqCst), 1);
1295    }
1296
1297    #[tokio::test]
1298    async fn download_single_full_block_with_access_lists_uses_requested_requirement() {
1299        let client = FullBlockWithAccessListsClient::default();
1300        let body = BlockBody::default();
1301        let bal = Bytes::from_static(&[EMPTY_LIST_CODE]);
1302        let header = sealed_header_with_access_list_hash(&bal);
1303        client.insert(header.clone(), body.clone(), bal.clone());
1304
1305        let requirement = Arc::clone(&client.last_access_list_requirement);
1306        let client = FullBlockClient::test_client(client);
1307
1308        let received = client
1309            .get_full_block_with_access_lists_with_requirement(
1310                header.hash(),
1311                BalRequirement::Mandatory,
1312            )
1313            .await;
1314
1315        let expected_raw_bal = RawBal::from(bal);
1316        assert_eq!(received.block(), &SealedBlock::from_sealed_parts(header, body));
1317        assert_eq!(received.data().as_ref(), Some(&expected_raw_bal));
1318        assert_eq!(*requirement.lock(), Some(BalRequirement::Mandatory));
1319    }
1320
1321    #[tokio::test]
1322    async fn download_single_full_block_with_access_lists_waits_for_pending_access_lists() {
1323        let client = FullBlockWithAccessListsClient::default();
1324        client.set_access_list_pending_polls(1);
1325
1326        let body = BlockBody::default();
1327        let bal = Bytes::from_static(&[EMPTY_LIST_CODE]);
1328        let header = sealed_header_with_access_list_hash(&bal);
1329        client.insert(header.clone(), body.clone(), bal.clone());
1330
1331        let request_count = Arc::clone(&client.access_list_requests);
1332        let client = FullBlockClient::test_client(client);
1333
1334        let received =
1335            timeout(Duration::from_secs(1), client.get_full_block_with_access_lists(header.hash()))
1336                .await
1337                .expect("access list request should complete");
1338
1339        let expected_raw_bal = RawBal::from(bal);
1340        assert_eq!(received.block(), &SealedBlock::from_sealed_parts(header, body));
1341        assert_eq!(received.data().as_ref(), Some(&expected_raw_bal));
1342        assert_eq!(request_count.load(Ordering::SeqCst), 1);
1343    }
1344
1345    #[tokio::test]
1346    async fn download_single_full_block_with_access_lists_rejects_wrong_hash() {
1347        let client = FullBlockWithAccessListsClient::default();
1348        let body = BlockBody::default();
1349        let expected_bal = Bytes::from_static(&[EMPTY_LIST_CODE]);
1350        let wrong_bal = Bytes::from_static(&[0xc1, 0x01]);
1351        let header = sealed_header_with_access_list_hash(&expected_bal);
1352        client.insert(header.clone(), body.clone(), wrong_bal);
1353
1354        let bad_messages = Arc::clone(&client.bad_messages);
1355        let client = FullBlockClient::test_client(client);
1356
1357        let received =
1358            timeout(Duration::from_secs(1), client.get_full_block_with_access_lists(header.hash()))
1359                .await
1360                .expect("block request should complete without access lists");
1361
1362        assert_eq!(received.block(), &SealedBlock::from_sealed_parts(header, body));
1363        assert!(received.data().is_none());
1364        assert_eq!(bad_messages.load(Ordering::SeqCst), 1);
1365    }
1366
1367    #[tokio::test]
1368    async fn download_single_full_block_with_access_lists_treats_none_as_unavailable() {
1369        let client = FullBlockWithAccessListsClient::default();
1370        let body = BlockBody::default();
1371        let expected_bal = Bytes::from_static(&[0xc1, 0x01]);
1372        let header = sealed_header_with_access_list_hash(&expected_bal);
1373        client.inner.insert(header.clone(), body.clone());
1374
1375        let bad_messages = Arc::clone(&client.bad_messages);
1376        let client = FullBlockClient::test_client(client);
1377
1378        let received =
1379            timeout(Duration::from_secs(1), client.get_full_block_with_access_lists(header.hash()))
1380                .await
1381                .expect("block request should complete without access lists");
1382
1383        assert_eq!(received.block(), &SealedBlock::from_sealed_parts(header, body));
1384        assert!(received.data().is_none());
1385        assert_eq!(bad_messages.load(Ordering::SeqCst), 0);
1386    }
1387
1388    #[tokio::test]
1389    async fn download_single_full_block_with_access_lists_rejects_wrong_empty_list() {
1390        let client = FullBlockWithAccessListsClient::default();
1391        let body = BlockBody::default();
1392        let expected_bal = Bytes::from_static(&[0xc1, 0x01]);
1393        let wrong_empty_bal = Bytes::from_static(&[EMPTY_LIST_CODE]);
1394        let header = sealed_header_with_access_list_hash(&expected_bal);
1395        client.insert(header.clone(), body.clone(), wrong_empty_bal);
1396
1397        let bad_messages = Arc::clone(&client.bad_messages);
1398        let client = FullBlockClient::test_client(client);
1399
1400        let received =
1401            timeout(Duration::from_secs(1), client.get_full_block_with_access_lists(header.hash()))
1402                .await
1403                .expect("block request should complete without access lists");
1404
1405        assert_eq!(received.block(), &SealedBlock::from_sealed_parts(header, body));
1406        assert!(received.data().is_none());
1407        assert_eq!(bad_messages.load(Ordering::SeqCst), 1);
1408    }
1409
1410    #[tokio::test]
1411    async fn download_single_full_block_with_access_lists_returns_none_after_empty_response() {
1412        let client = FullBlockWithAccessListsClient::default();
1413        client.empty_first_response.store(true, Ordering::SeqCst);
1414
1415        let body = BlockBody::default();
1416        let bal = Bytes::from_static(&[EMPTY_LIST_CODE]);
1417        let header = sealed_header_with_access_list_hash(&bal);
1418        client.insert(header.clone(), body.clone(), bal.clone());
1419
1420        let request_count = Arc::clone(&client.access_list_requests);
1421        let bad_messages = Arc::clone(&client.bad_messages);
1422        let client = FullBlockClient::test_client(client);
1423
1424        let received =
1425            timeout(Duration::from_secs(1), client.get_full_block_with_access_lists(header.hash()))
1426                .await
1427                .expect("block request should complete without access lists");
1428
1429        assert_eq!(received.block(), &SealedBlock::from_sealed_parts(header, body));
1430        assert!(received.data().is_none());
1431        assert_eq!(request_count.load(Ordering::SeqCst), 1);
1432        assert_eq!(bad_messages.load(Ordering::SeqCst), 0);
1433    }
1434
1435    #[tokio::test]
1436    async fn download_single_full_block_with_access_lists_returns_block_when_unavailable() {
1437        let client = FullBlockWithAccessListsClient::default();
1438        client.set_access_lists_unsupported(true);
1439
1440        let body = BlockBody::default();
1441        let bal = Bytes::from_static(&[EMPTY_LIST_CODE]);
1442        let header = sealed_header_with_access_list_hash(&bal);
1443        client.insert(header.clone(), body.clone(), bal);
1444
1445        let request_count = Arc::clone(&client.access_list_requests);
1446        let requirement = Arc::clone(&client.last_access_list_requirement);
1447        let client = FullBlockClient::test_client(client);
1448
1449        let received =
1450            timeout(Duration::from_secs(1), client.get_full_block_with_access_lists(header.hash()))
1451                .await
1452                .expect("block request should complete without access lists");
1453
1454        assert_eq!(received.block(), &SealedBlock::from_sealed_parts(header, body));
1455        assert!(received.data().is_none());
1456        assert_eq!(request_count.load(Ordering::SeqCst), 1);
1457        assert_eq!(
1458            *requirement.lock(),
1459            Some(BalRequirement::Optional),
1460            "single block BAL lookup should be best-effort"
1461        );
1462    }
1463
1464    /// Inserts headers and returns the last header and block body.
1465    fn insert_headers_into_client(
1466        client: &TestFullBlockClient,
1467        range: Range<usize>,
1468    ) -> (SealedHeader, BlockBody) {
1469        let mut sealed_header: SealedHeader = SealedHeader::default();
1470        let body = BlockBody::default();
1471        for _ in range {
1472            let (mut header, hash) = sealed_header.split();
1473            // update to the next header
1474            header.parent_hash = hash;
1475            header.number += 1;
1476
1477            sealed_header = SealedHeader::seal_slow(header);
1478
1479            client.insert(sealed_header.clone(), body.clone());
1480        }
1481
1482        (sealed_header, body)
1483    }
1484
1485    #[derive(Clone, Debug)]
1486    struct FullBlockWithAccessListsClient {
1487        inner: TestFullBlockClient,
1488        access_lists: Arc<Mutex<B256Map<Bytes>>>,
1489        access_list_requests: Arc<AtomicUsize>,
1490        access_list_soft_limit: Arc<AtomicUsize>,
1491        access_list_pending_polls: Arc<AtomicUsize>,
1492        extra_access_list_entries: Arc<AtomicUsize>,
1493        unsupported_access_lists: Arc<AtomicBool>,
1494        last_access_list_requirement: Arc<Mutex<Option<BalRequirement>>>,
1495        bad_messages: Arc<AtomicUsize>,
1496        empty_first_response: Arc<AtomicBool>,
1497    }
1498
1499    impl Default for FullBlockWithAccessListsClient {
1500        fn default() -> Self {
1501            Self {
1502                inner: TestFullBlockClient::default(),
1503                access_lists: Arc::new(Mutex::new(B256Map::default())),
1504                access_list_requests: Arc::new(AtomicUsize::new(0)),
1505                access_list_soft_limit: Arc::new(AtomicUsize::new(usize::MAX)),
1506                access_list_pending_polls: Arc::new(AtomicUsize::new(0)),
1507                extra_access_list_entries: Arc::new(AtomicUsize::new(0)),
1508                unsupported_access_lists: Arc::new(AtomicBool::new(false)),
1509                last_access_list_requirement: Arc::new(Mutex::new(None)),
1510                bad_messages: Arc::new(AtomicUsize::new(0)),
1511                empty_first_response: Arc::new(AtomicBool::new(false)),
1512            }
1513        }
1514    }
1515
1516    impl FullBlockWithAccessListsClient {
1517        fn insert(&self, header: SealedHeader, body: BlockBody, bal: Bytes) {
1518            self.inner.insert(header.clone(), body);
1519            self.access_lists.lock().insert(header.hash(), bal);
1520        }
1521
1522        fn set_access_list_soft_limit(&self, limit: usize) {
1523            self.access_list_soft_limit.store(limit, Ordering::SeqCst);
1524        }
1525
1526        fn set_access_list_pending_polls(&self, polls: usize) {
1527            self.access_list_pending_polls.store(polls, Ordering::SeqCst);
1528        }
1529
1530        fn set_extra_access_list_entries(&self, count: usize) {
1531            self.extra_access_list_entries.store(count, Ordering::SeqCst);
1532        }
1533
1534        fn set_access_lists_unsupported(&self, unsupported: bool) {
1535            self.unsupported_access_lists.store(unsupported, Ordering::SeqCst);
1536        }
1537    }
1538
1539    /// Inserts headers with block access lists and returns the last header and block body.
1540    fn insert_headers_with_access_lists_into_client(
1541        client: &FullBlockWithAccessListsClient,
1542        range: Range<usize>,
1543    ) -> (SealedHeader, BlockBody) {
1544        let mut sealed_header: SealedHeader = SealedHeader::default();
1545        let body = BlockBody::default();
1546        for block_idx in range {
1547            let (mut header, hash) = sealed_header.split();
1548            header.parent_hash = hash;
1549            header.number += 1;
1550            let bal = Bytes::from(vec![0xc1, block_idx as u8]);
1551            header.block_access_list_hash = Some(keccak256(bal.as_ref()));
1552
1553            sealed_header = SealedHeader::seal_slow(header);
1554
1555            client.insert(sealed_header.clone(), body.clone(), bal);
1556        }
1557
1558        (sealed_header, body)
1559    }
1560
1561    impl DownloadClient for FullBlockWithAccessListsClient {
1562        fn report_bad_message(&self, peer_id: PeerId) {
1563            self.bad_messages.fetch_add(1, Ordering::SeqCst);
1564            self.inner.report_bad_message(peer_id);
1565        }
1566
1567        fn num_connected_peers(&self) -> usize {
1568            self.inner.num_connected_peers()
1569        }
1570    }
1571
1572    impl HeadersClient for FullBlockWithAccessListsClient {
1573        type Header = <TestFullBlockClient as HeadersClient>::Header;
1574        type Output = <TestFullBlockClient as HeadersClient>::Output;
1575
1576        fn get_headers_with_priority(
1577            &self,
1578            request: HeadersRequest,
1579            priority: Priority,
1580        ) -> Self::Output {
1581            self.inner.get_headers_with_priority(request, priority)
1582        }
1583    }
1584
1585    impl BodiesClient for FullBlockWithAccessListsClient {
1586        type Body = <TestFullBlockClient as BodiesClient>::Body;
1587        type Output = <TestFullBlockClient as BodiesClient>::Output;
1588
1589        fn get_block_bodies_with_priority_and_range_hint(
1590            &self,
1591            hashes: Vec<B256>,
1592            priority: Priority,
1593            range_hint: Option<RangeInclusive<u64>>,
1594        ) -> Self::Output {
1595            self.inner.get_block_bodies_with_priority_and_range_hint(hashes, priority, range_hint)
1596        }
1597    }
1598
1599    struct MaybePendingAccessLists {
1600        response: Option<PeerRequestResult<BlockAccessLists>>,
1601        pending_polls: usize,
1602    }
1603
1604    impl MaybePendingAccessLists {
1605        const fn new(response: PeerRequestResult<BlockAccessLists>, pending_polls: usize) -> Self {
1606            Self { response: Some(response), pending_polls }
1607        }
1608    }
1609
1610    impl std::future::Future for MaybePendingAccessLists {
1611        type Output = PeerRequestResult<BlockAccessLists>;
1612
1613        fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
1614            if self.pending_polls > 0 {
1615                self.pending_polls -= 1;
1616                cx.waker().wake_by_ref();
1617                return Poll::Pending
1618            }
1619
1620            Poll::Ready(self.response.take().expect("future polled after completion"))
1621        }
1622    }
1623
1624    impl BlockAccessListsClient for FullBlockWithAccessListsClient {
1625        type Output = MaybePendingAccessLists;
1626
1627        fn get_block_access_lists_with_priority_and_requirement(
1628            &self,
1629            hashes: Vec<B256>,
1630            _priority: Priority,
1631            requirement: BalRequirement,
1632        ) -> Self::Output {
1633            self.access_list_requests.fetch_add(1, Ordering::SeqCst);
1634            *self.last_access_list_requirement.lock() = Some(requirement);
1635            let pending_polls = self.access_list_pending_polls.swap(0, Ordering::SeqCst);
1636
1637            if self.unsupported_access_lists.load(Ordering::SeqCst) {
1638                return MaybePendingAccessLists::new(
1639                    Err(RequestError::UnsupportedCapability),
1640                    pending_polls,
1641                )
1642            }
1643
1644            if self.empty_first_response.swap(false, Ordering::SeqCst) {
1645                return MaybePendingAccessLists::new(
1646                    Ok(WithPeerId::new(PeerId::random(), BlockAccessLists(Vec::new()))),
1647                    pending_polls,
1648                )
1649            }
1650
1651            let mut access_lists: Vec<_> = hashes
1652                .into_iter()
1653                .take(self.access_list_soft_limit.load(Ordering::SeqCst))
1654                .map(|hash| self.access_lists.lock().get(&hash).cloned())
1655                .collect();
1656            for _ in 0..self.extra_access_list_entries.load(Ordering::SeqCst) {
1657                access_lists.push(None);
1658            }
1659
1660            MaybePendingAccessLists::new(
1661                Ok(WithPeerId::new(PeerId::random(), BlockAccessLists(access_lists))),
1662                pending_polls,
1663            )
1664        }
1665    }
1666
1667    impl BlockClient for FullBlockWithAccessListsClient {
1668        type Block = reth_ethereum_primitives::Block;
1669    }
1670
1671    #[derive(Clone, Debug)]
1672    struct FailingBodiesClient {
1673        inner: TestFullBlockClient,
1674        fail_on: usize,
1675        body_requests: Arc<AtomicUsize>,
1676    }
1677
1678    impl FailingBodiesClient {
1679        fn new(inner: TestFullBlockClient, fail_on: usize) -> Self {
1680            Self { inner, fail_on, body_requests: Arc::new(AtomicUsize::new(0)) }
1681        }
1682    }
1683
1684    impl DownloadClient for FailingBodiesClient {
1685        fn report_bad_message(&self, peer_id: PeerId) {
1686            self.inner.report_bad_message(peer_id);
1687        }
1688
1689        fn num_connected_peers(&self) -> usize {
1690            self.inner.num_connected_peers()
1691        }
1692    }
1693
1694    impl HeadersClient for FailingBodiesClient {
1695        type Header = <TestFullBlockClient as HeadersClient>::Header;
1696        type Output = <TestFullBlockClient as HeadersClient>::Output;
1697
1698        fn get_headers_with_priority(
1699            &self,
1700            request: HeadersRequest,
1701            priority: Priority,
1702        ) -> Self::Output {
1703            self.inner.get_headers_with_priority(request, priority)
1704        }
1705    }
1706
1707    impl BodiesClient for FailingBodiesClient {
1708        type Body = <TestFullBlockClient as BodiesClient>::Body;
1709        type Output = <TestFullBlockClient as BodiesClient>::Output;
1710
1711        fn get_block_bodies_with_priority_and_range_hint(
1712            &self,
1713            hashes: Vec<B256>,
1714            priority: Priority,
1715            range_hint: Option<RangeInclusive<u64>>,
1716        ) -> Self::Output {
1717            let attempt = self.body_requests.fetch_add(1, Ordering::SeqCst);
1718            if attempt == self.fail_on {
1719                return futures::future::ready(Err(RequestError::Timeout))
1720            }
1721
1722            self.inner.get_block_bodies_with_priority_and_range_hint(hashes, priority, range_hint)
1723        }
1724    }
1725
1726    impl BlockClient for FailingBodiesClient {
1727        type Block = reth_ethereum_primitives::Block;
1728    }
1729
1730    #[tokio::test]
1731    async fn download_full_block_range() {
1732        let client = TestFullBlockClient::default();
1733        let (header, body) = insert_headers_into_client(&client, 0..50);
1734        let client = FullBlockClient::test_client(client);
1735
1736        let received = client.get_full_block_range(header.hash(), 1).await;
1737        let received = received.first().expect("response should include a block");
1738        assert_eq!(*received, SealedBlock::from_sealed_parts(header.clone(), body));
1739
1740        let received = client.get_full_block_range(header.hash(), 10).await;
1741        assert_eq!(received.len(), 10);
1742        for (i, block) in received.iter().enumerate() {
1743            let expected_number = header.number - i as u64;
1744            assert_eq!(block.number, expected_number);
1745        }
1746    }
1747
1748    #[tokio::test]
1749    async fn download_full_block_range_over_soft_limit() {
1750        // default soft limit is 20, so we will request 50 blocks
1751        let client = TestFullBlockClient::default();
1752        let (header, body) = insert_headers_into_client(&client, 0..50);
1753        let client = FullBlockClient::test_client(client);
1754
1755        let received = client.get_full_block_range(header.hash(), 1).await;
1756        let received = received.first().expect("response should include a block");
1757        assert_eq!(*received, SealedBlock::from_sealed_parts(header.clone(), body));
1758
1759        let received = client.get_full_block_range(header.hash(), 50).await;
1760        assert_eq!(received.len(), 50);
1761        for (i, block) in received.iter().enumerate() {
1762            let expected_number = header.number - i as u64;
1763            assert_eq!(block.number, expected_number);
1764        }
1765    }
1766
1767    #[tokio::test]
1768    async fn download_full_block_range_retries_after_body_error() {
1769        let mut client = TestFullBlockClient::default();
1770        client.set_soft_limit(2);
1771        let (header, _) = insert_headers_into_client(&client, 0..3);
1772
1773        let client = FailingBodiesClient::new(client, 1);
1774        let body_requests = Arc::clone(&client.body_requests);
1775        let client = FullBlockClient::test_client(client);
1776
1777        let received =
1778            timeout(Duration::from_secs(1), client.get_full_block_range(header.hash(), 3))
1779                .await
1780                .expect("body request retry should complete");
1781
1782        assert_eq!(received.len(), 3);
1783        assert_eq!(body_requests.load(Ordering::SeqCst), 3);
1784    }
1785
1786    #[tokio::test]
1787    async fn download_full_block_range_with_access_lists() {
1788        let client = FullBlockWithAccessListsClient::default();
1789        let (header, _) = insert_headers_with_access_lists_into_client(&client, 0..3);
1790
1791        let access_lists = Arc::clone(&client.access_lists);
1792        let request_count = Arc::clone(&client.access_list_requests);
1793        let requirement = Arc::clone(&client.last_access_list_requirement);
1794        let client = FullBlockClient::test_client(client);
1795
1796        let response = timeout(
1797            Duration::from_secs(1),
1798            client.get_full_block_range_with_optional_access_lists(header.hash(), 3),
1799        )
1800        .await
1801        .expect("range request should complete");
1802
1803        let blocks = response;
1804        assert_eq!(blocks.len(), 3);
1805        let expected = {
1806            let bals = access_lists.lock();
1807            blocks
1808                .iter()
1809                .map(|block| {
1810                    let bal = bals.get(&block.block().hash()).cloned().expect("access list exists");
1811                    Some(RawBal::from(bal))
1812                })
1813                .collect::<Vec<_>>()
1814        };
1815        assert_eq!(range_access_lists(&blocks), expected);
1816        assert_eq!(request_count.load(Ordering::SeqCst), 1);
1817        assert_eq!(*requirement.lock(), Some(BalRequirement::Optional));
1818    }
1819
1820    #[tokio::test]
1821    async fn download_full_block_range_with_access_lists_returns_none_for_empty_response() {
1822        let client = FullBlockWithAccessListsClient::default();
1823        client.empty_first_response.store(true, Ordering::SeqCst);
1824        let (header, _) = insert_headers_with_access_lists_into_client(&client, 0..3);
1825
1826        let request_count = Arc::clone(&client.access_list_requests);
1827        let client = FullBlockClient::test_client(client);
1828
1829        let response = timeout(
1830            Duration::from_secs(1),
1831            client.get_full_block_range_with_optional_access_lists(header.hash(), 3),
1832        )
1833        .await
1834        .expect("range request should complete without access lists");
1835
1836        let blocks = response;
1837        assert_eq!(blocks.len(), 3);
1838        assert_eq!(range_access_lists(&blocks), vec![None; blocks.len()]);
1839        assert_eq!(request_count.load(Ordering::SeqCst), 1);
1840    }
1841
1842    #[tokio::test]
1843    async fn download_full_block_range_with_access_lists_uses_requested_requirement() {
1844        let client = FullBlockWithAccessListsClient::default();
1845        let (header, _) = insert_headers_with_access_lists_into_client(&client, 0..3);
1846
1847        let requirement = Arc::clone(&client.last_access_list_requirement);
1848        let client = FullBlockClient::test_client(client);
1849
1850        let blocks = timeout(
1851            Duration::from_secs(1),
1852            client.get_full_block_range_with_optional_access_lists_with_requirement(
1853                header.hash(),
1854                3,
1855                BalRequirement::Mandatory,
1856            ),
1857        )
1858        .await
1859        .expect("range request should complete");
1860
1861        assert_eq!(blocks.len(), 3);
1862        assert_eq!(*requirement.lock(), Some(BalRequirement::Mandatory));
1863    }
1864
1865    #[tokio::test]
1866    async fn download_full_block_range_with_access_lists_preserves_short_response() {
1867        let client = FullBlockWithAccessListsClient::default();
1868        client.set_access_list_soft_limit(2);
1869        let (header, _) = insert_headers_with_access_lists_into_client(&client, 0..5);
1870
1871        let access_lists = Arc::clone(&client.access_lists);
1872        let request_count = Arc::clone(&client.access_list_requests);
1873        let client = FullBlockClient::test_client(client);
1874
1875        let blocks = timeout(
1876            Duration::from_secs(1),
1877            client.get_full_block_range_with_optional_access_lists(header.hash(), 5),
1878        )
1879        .await
1880        .expect("range request should complete without access lists");
1881
1882        assert_eq!(blocks.len(), 5);
1883        let expected = {
1884            let bals = access_lists.lock();
1885            blocks
1886                .iter()
1887                .enumerate()
1888                .map(|(idx, block)| {
1889                    if idx >= 2 {
1890                        return None
1891                    }
1892
1893                    let bal = bals.get(&block.block().hash()).cloned().expect("access list exists");
1894                    Some(RawBal::from(bal))
1895                })
1896                .collect::<Vec<_>>()
1897        };
1898        assert_eq!(range_access_lists(&blocks), expected);
1899        assert_eq!(request_count.load(Ordering::SeqCst), 1);
1900    }
1901
1902    #[tokio::test]
1903    async fn download_full_block_range_with_access_lists_preserves_unavailable_entries() {
1904        let client = FullBlockWithAccessListsClient::default();
1905        let (header, _) = insert_headers_with_access_lists_into_client(&client, 0..3);
1906        client.access_lists.lock().remove(&header.hash());
1907
1908        let access_lists = Arc::clone(&client.access_lists);
1909        let bad_messages = Arc::clone(&client.bad_messages);
1910        let client = FullBlockClient::test_client(client);
1911
1912        let blocks = timeout(
1913            Duration::from_secs(1),
1914            client.get_full_block_range_with_optional_access_lists(header.hash(), 3),
1915        )
1916        .await
1917        .expect("range request should complete");
1918
1919        assert_eq!(blocks.len(), 3);
1920        let expected = {
1921            let bals = access_lists.lock();
1922            blocks
1923                .iter()
1924                .map(|block| {
1925                    if block.block().hash() == header.hash() {
1926                        return None
1927                    }
1928
1929                    let bal = bals.get(&block.block().hash()).cloned().expect("access list exists");
1930                    Some(RawBal::from(bal))
1931                })
1932                .collect::<Vec<_>>()
1933        };
1934        assert_eq!(range_access_lists(&blocks), expected);
1935        assert_eq!(bad_messages.load(Ordering::SeqCst), 0);
1936    }
1937
1938    #[tokio::test]
1939    async fn download_full_block_range_with_access_lists_returns_none_when_unavailable() {
1940        let client = FullBlockWithAccessListsClient::default();
1941        client.set_access_lists_unsupported(true);
1942        let (header, _) = insert_headers_with_access_lists_into_client(&client, 0..3);
1943
1944        let request_count = Arc::clone(&client.access_list_requests);
1945        let client = FullBlockClient::test_client(client);
1946
1947        let blocks = timeout(
1948            Duration::from_secs(1),
1949            client.get_full_block_range_with_optional_access_lists(header.hash(), 3),
1950        )
1951        .await
1952        .expect("range request should complete without access lists");
1953
1954        assert_eq!(blocks.len(), 3);
1955        assert_eq!(range_access_lists(&blocks), vec![None; blocks.len()]);
1956        assert_eq!(request_count.load(Ordering::SeqCst), 1);
1957    }
1958
1959    #[tokio::test]
1960    async fn download_full_block_range_with_access_lists_ignores_long_response() {
1961        let client = FullBlockWithAccessListsClient::default();
1962        client.set_extra_access_list_entries(1);
1963        let (header, _) = insert_headers_with_access_lists_into_client(&client, 0..3);
1964
1965        let request_count = Arc::clone(&client.access_list_requests);
1966        let bad_messages = Arc::clone(&client.bad_messages);
1967        let client = FullBlockClient::test_client(client);
1968
1969        let blocks = timeout(
1970            Duration::from_secs(1),
1971            client.get_full_block_range_with_optional_access_lists(header.hash(), 3),
1972        )
1973        .await
1974        .expect("range request should complete without access lists");
1975
1976        assert_eq!(blocks.len(), 3);
1977        assert_eq!(range_access_lists(&blocks), vec![None; blocks.len()]);
1978        assert_eq!(request_count.load(Ordering::SeqCst), 1);
1979        assert_eq!(bad_messages.load(Ordering::SeqCst), 0);
1980    }
1981
1982    #[tokio::test]
1983    async fn download_full_block_range_with_access_lists_rejects_wrong_hash() {
1984        let client = FullBlockWithAccessListsClient::default();
1985        let (header, _) = insert_headers_with_access_lists_into_client(&client, 0..3);
1986        client.access_lists.lock().insert(header.hash(), Bytes::from_static(&[0xc1, 0x7f]));
1987
1988        let bad_messages = Arc::clone(&client.bad_messages);
1989        let client = FullBlockClient::test_client(client);
1990
1991        let blocks = timeout(
1992            Duration::from_secs(1),
1993            client.get_full_block_range_with_optional_access_lists(header.hash(), 3),
1994        )
1995        .await
1996        .expect("range request should complete without access lists");
1997
1998        assert_eq!(blocks.len(), 3);
1999        assert_eq!(range_access_lists(&blocks), vec![None; blocks.len()]);
2000        assert_eq!(bad_messages.load(Ordering::SeqCst), 1);
2001    }
2002
2003    #[tokio::test]
2004    async fn download_full_block_range_with_access_lists_preserves_valid_prefix_until_wrong_hash() {
2005        let client = FullBlockWithAccessListsClient::default();
2006        let (header, _) = insert_headers_with_access_lists_into_client(&client, 0..3);
2007        let first_bal =
2008            client.access_lists.lock().get(&header.hash()).cloned().expect("access list exists");
2009        let second_hash = header.parent_hash;
2010        client.access_lists.lock().insert(second_hash, Bytes::from_static(&[0xc1, 0x7f]));
2011
2012        let bad_messages = Arc::clone(&client.bad_messages);
2013        let client = FullBlockClient::test_client(client);
2014
2015        let blocks = timeout(
2016            Duration::from_secs(1),
2017            client.get_full_block_range_with_optional_access_lists(header.hash(), 3),
2018        )
2019        .await
2020        .expect("range request should complete without unvalidated access lists");
2021
2022        assert_eq!(blocks.len(), 3);
2023        assert_eq!(blocks[1].block().hash(), second_hash);
2024        assert_eq!(range_access_lists(&blocks), vec![Some(RawBal::from(first_bal)), None, None]);
2025        assert_eq!(bad_messages.load(Ordering::SeqCst), 1);
2026    }
2027
2028    #[tokio::test]
2029    async fn download_full_block_range_rejects_invalid_headers() {
2030        let client = TestFullBlockClient::default();
2031        let (header, _) = insert_headers_into_client(&client, 0..3);
2032        let consensus = reth_consensus::test_utils::TestConsensus::default();
2033        consensus.set_fail_validation(true);
2034        let client = FullBlockClient::new(client, Arc::new(consensus));
2035        let mut request = client.get_full_block_range(header.hash(), 3);
2036        let headers = client
2037            .client
2038            .get_headers(HeadersRequest::falling(header.hash().into(), 3))
2039            .await
2040            .unwrap();
2041
2042        request.on_headers_response(headers);
2043
2044        assert!(request.headers.is_none());
2045        assert!(request.pending_headers.is_empty());
2046        assert!(request.request.bodies.is_none());
2047    }
2048}