Skip to main content

reth_network_p2p/snap/
client.rs

1use crate::{
2    download::DownloadClient,
3    error::{PeerRequestResult, RequestError},
4    full_block::NoopFullBlockClient,
5    priority::Priority,
6};
7use futures::Future;
8use reth_eth_wire_types::{
9    snap::{
10        AccountRangeMessage, BlockAccessListsMessage, ByteCodesMessage, GetAccountRangeMessage,
11        GetBlockAccessListsMessage, GetByteCodesMessage, GetStorageRangesMessage,
12        SnapProtocolMessage, StorageRangesMessage,
13    },
14    NetworkPrimitives,
15};
16
17/// Response types for snap sync requests
18#[derive(Debug, Clone, PartialEq, Eq)]
19pub enum SnapResponse {
20    /// Response containing account range data
21    AccountRange(AccountRangeMessage),
22    /// Response containing storage ranges data
23    StorageRanges(StorageRangesMessage),
24    /// Response containing bytecode data
25    ByteCodes(ByteCodesMessage),
26    /// Response containing block access lists.
27    ///
28    /// Only valid for `snap/2` (EIP-8189).
29    BlockAccessLists(BlockAccessListsMessage),
30}
31
32impl TryFrom<SnapProtocolMessage> for SnapResponse {
33    /// The original message, returned unchanged when it is a request rather than a response.
34    type Error = SnapProtocolMessage;
35
36    fn try_from(msg: SnapProtocolMessage) -> Result<Self, Self::Error> {
37        match msg {
38            SnapProtocolMessage::AccountRange(m) => Ok(Self::AccountRange(m)),
39            SnapProtocolMessage::StorageRanges(m) => Ok(Self::StorageRanges(m)),
40            SnapProtocolMessage::ByteCodes(m) => Ok(Self::ByteCodes(m)),
41            SnapProtocolMessage::BlockAccessLists(m) => Ok(Self::BlockAccessLists(m)),
42            request => Err(request),
43        }
44    }
45}
46
47impl From<SnapResponse> for SnapProtocolMessage {
48    fn from(response: SnapResponse) -> Self {
49        match response {
50            SnapResponse::AccountRange(m) => Self::AccountRange(m),
51            SnapResponse::StorageRanges(m) => Self::StorageRanges(m),
52            SnapResponse::ByteCodes(m) => Self::ByteCodes(m),
53            SnapResponse::BlockAccessLists(m) => Self::BlockAccessLists(m),
54        }
55    }
56}
57
58/// The snap sync downloader client
59#[auto_impl::auto_impl(&, Arc, Box)]
60pub trait SnapClient: DownloadClient {
61    /// The output future type for snap requests
62    type Output: Future<Output = PeerRequestResult<SnapResponse>> + Send + Sync + Unpin;
63
64    /// Sends the account range request to the p2p network and returns the account range
65    /// response received from a peer.
66    fn get_account_range(&self, request: GetAccountRangeMessage) -> Self::Output {
67        self.get_account_range_with_priority(request, Priority::Normal)
68    }
69
70    /// Sends the account range request to the p2p network with priority set and returns
71    /// the account range response received from a peer.
72    fn get_account_range_with_priority(
73        &self,
74        request: GetAccountRangeMessage,
75        priority: Priority,
76    ) -> Self::Output;
77
78    /// Sends the storage ranges request to the p2p network and returns the storage ranges
79    /// response received from a peer.
80    fn get_storage_ranges(&self, request: GetStorageRangesMessage) -> Self::Output;
81
82    /// Sends the storage ranges request to the p2p network with priority set and returns
83    /// the storage ranges response received from a peer.
84    fn get_storage_ranges_with_priority(
85        &self,
86        request: GetStorageRangesMessage,
87        priority: Priority,
88    ) -> Self::Output;
89
90    /// Sends the byte codes request to the p2p network and returns the byte codes
91    /// response received from a peer.
92    fn get_byte_codes(&self, request: GetByteCodesMessage) -> Self::Output;
93
94    /// Sends the byte codes request to the p2p network with priority set and returns
95    /// the byte codes response received from a peer.
96    fn get_byte_codes_with_priority(
97        &self,
98        request: GetByteCodesMessage,
99        priority: Priority,
100    ) -> Self::Output;
101
102    /// Sends the block access lists request to the p2p network and returns the block
103    /// access lists response received from a peer.
104    ///
105    /// Only valid for `snap/2` (EIP-8189).
106    fn get_block_access_lists(&self, request: GetBlockAccessListsMessage) -> Self::Output {
107        self.get_block_access_lists_with_priority(request, Priority::Normal)
108    }
109
110    /// Sends the block access lists request to the p2p network with priority set and returns
111    /// the block access lists response received from a peer.
112    ///
113    /// Only valid for `snap/2` (EIP-8189).
114    fn get_block_access_lists_with_priority(
115        &self,
116        request: GetBlockAccessListsMessage,
117        priority: Priority,
118    ) -> Self::Output;
119}
120
121/// Fails every snap request with [`RequestError::UnsupportedCapability`], so the noop client can
122/// stand in wherever a [`SnapClient`] bound is required but snap is not served.
123impl<Net> SnapClient for NoopFullBlockClient<Net>
124where
125    Net: NetworkPrimitives,
126{
127    type Output = futures::future::Ready<PeerRequestResult<SnapResponse>>;
128
129    /// Fails the account range request as unsupported.
130    fn get_account_range_with_priority(
131        &self,
132        _request: GetAccountRangeMessage,
133        _priority: Priority,
134    ) -> Self::Output {
135        unsupported()
136    }
137
138    /// Fails the storage ranges request as unsupported.
139    fn get_storage_ranges(&self, _request: GetStorageRangesMessage) -> Self::Output {
140        unsupported()
141    }
142
143    /// Fails the prioritized storage ranges request as unsupported.
144    fn get_storage_ranges_with_priority(
145        &self,
146        _request: GetStorageRangesMessage,
147        _priority: Priority,
148    ) -> Self::Output {
149        unsupported()
150    }
151
152    /// Fails the bytecode request as unsupported.
153    fn get_byte_codes(&self, _request: GetByteCodesMessage) -> Self::Output {
154        unsupported()
155    }
156
157    /// Fails the prioritized bytecode request as unsupported.
158    fn get_byte_codes_with_priority(
159        &self,
160        _request: GetByteCodesMessage,
161        _priority: Priority,
162    ) -> Self::Output {
163        unsupported()
164    }
165
166    /// Fails the block access lists request as unsupported.
167    fn get_block_access_lists_with_priority(
168        &self,
169        _request: GetBlockAccessListsMessage,
170        _priority: Priority,
171    ) -> Self::Output {
172        unsupported()
173    }
174}
175
176/// The noop answer to any snap request: immediately ready, no capability.
177fn unsupported() -> futures::future::Ready<PeerRequestResult<SnapResponse>> {
178    futures::future::ready(Err(RequestError::UnsupportedCapability))
179}
180
181#[cfg(test)]
182mod tests {
183    use super::*;
184    use reth_eth_wire_types::BlockAccessLists;
185    use test_case::test_case;
186
187    #[test_case(
188        SnapProtocolMessage::GetAccountRange(GetAccountRangeMessage {
189            request_id: 1, root_hash: Default::default(), starting_hash: Default::default(),
190            limit_hash: Default::default(), response_bytes: 0,
191        }), false ; "account range request is not a response"
192    )]
193    #[test_case(
194        SnapProtocolMessage::GetBlockAccessLists(GetBlockAccessListsMessage {
195            request_id: 1, block_hashes: vec![], response_bytes: 0,
196        }), false ; "block access lists request is not a response"
197    )]
198    #[test_case(
199        SnapProtocolMessage::AccountRange(AccountRangeMessage {
200            request_id: 1, accounts: vec![], proof: vec![],
201        }), true ; "account range response converts"
202    )]
203    #[test_case(
204        SnapProtocolMessage::ByteCodes(ByteCodesMessage { request_id: 1, codes: vec![] }),
205        true ; "byte codes response converts"
206    )]
207    #[test_case(
208        SnapProtocolMessage::BlockAccessLists(BlockAccessListsMessage {
209            request_id: 1, block_access_lists: BlockAccessLists(vec![]),
210        }), true ; "block access lists response converts"
211    )]
212    fn try_from_snap_message(msg: SnapProtocolMessage, is_response: bool) {
213        let original = msg.clone();
214        match SnapResponse::try_from(msg) {
215            Ok(_) => assert!(is_response),
216            // requests are returned unchanged
217            Err(returned) => {
218                assert!(!is_response);
219                assert_eq!(returned, original);
220            }
221        }
222    }
223}