1use crate::metrics::SnapDownloaderMetrics;
7use futures::FutureExt;
8use reth_eth_wire_types::snap::{
9 GetAccountRangeMessage, GetBlockAccessListsMessage, GetByteCodesMessage,
10 GetStorageRangesMessage,
11};
12use reth_network_p2p::{
13 error::RequestError,
14 priority::Priority,
15 snap::client::{SnapClient, SnapResponse},
16};
17use reth_network_peers::PeerId;
18use reth_tasks::Runtime;
19use std::{
20 fmt,
21 task::{ready, Context, Poll},
22};
23use tracing::debug;
24
25pub(super) const MAX_RETRIES: u8 = 2;
27
28pub(super) struct VerifyingRequest<C: SnapClient, V: SnapVerifier> {
30 client: C,
32 runtime: Runtime,
34 request: V::Request,
36 verifier: V,
38 fut: C::Output,
40 verification: Option<VerificationTask<V::Output>>,
42 retries: u8,
44 metrics: SnapDownloaderMetrics,
46}
47
48impl<C, V> VerifyingRequest<C, V>
49where
50 C: SnapClient,
51 V: SnapVerifier,
52{
53 pub(super) fn new(client: C, request: V::Request, verifier: V, runtime: Runtime) -> Self {
55 let fut = request.send(&client, Priority::Normal);
56 Self {
57 client,
58 runtime,
59 request,
60 verifier,
61 fut,
62 verification: None,
63 retries: 0,
64 metrics: SnapDownloaderMetrics::default(),
65 }
66 }
67
68 pub(super) fn poll_verified(
73 &mut self,
74 cx: &mut Context<'_>,
75 ) -> Poll<Result<V::Output, RequestError>> {
76 loop {
77 if self.verification.is_some() {
78 match ready!(self.poll_verification(cx)) {
79 Ok(Some(output)) => return Poll::Ready(Ok(output)),
80 Ok(None) => {}
81 Err(error) => return Poll::Ready(Err(error)),
82 }
83 }
84
85 match ready!(self.fut.poll_unpin(cx)) {
86 Ok(response) => {
87 let (peer_id, response) = response.split();
88 let verifier = self.verifier.clone();
89 let fut =
90 self.runtime.spawn_blocking(move || verifier.verify(peer_id, response));
91 self.verification = Some(VerificationTask { peer_id, fut });
92 }
93 Err(error) if error.is_retryable() || error == RequestError::BadResponse => {
95 debug!(target: "downloaders::snap", %error, "Snap request failed, retrying");
96 if !self.retry() {
97 return Poll::Ready(Err(error))
98 }
99 }
100 Err(error) => return Poll::Ready(Err(error)),
101 }
102 }
103 }
104
105 fn retry(&mut self) -> bool {
107 if self.retries >= MAX_RETRIES {
108 return false
109 }
110 self.retries += 1;
111 self.fut = self.request.send(&self.client, Priority::High);
112 true
113 }
114
115 fn poll_verification(
117 &mut self,
118 cx: &mut Context<'_>,
119 ) -> Poll<Result<Option<V::Output>, RequestError>> {
120 let verification = self.verification.as_mut().expect("verification task is present");
121 let result = ready!(verification.fut.poll_unpin(cx));
122 let peer_id = verification.peer_id;
123 self.verification = None;
124
125 match result {
126 Ok(Ok(output)) => Poll::Ready(Ok(Some(output))),
127 Ok(Err(error)) => {
128 debug!(target: "downloaders::snap", ?peer_id, %error, "Invalid snap response");
129 self.client.report_bad_message(peer_id);
130 self.metrics.invalid_responses.increment(1);
131 Poll::Ready(self.retry().then_some(None).ok_or(error))
132 }
133 Err(error) => {
135 debug!(target: "downloaders::snap", %error, "Snap verification task failed");
136 Poll::Ready(Err(RequestError::ChannelClosed))
137 }
138 }
139 }
140}
141
142impl<C, V> fmt::Debug for VerifyingRequest<C, V>
144where
145 C: SnapClient,
146 V: SnapVerifier + fmt::Debug,
147 V::Request: fmt::Debug,
148{
149 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
150 f.debug_struct("VerifyingRequest")
151 .field("client", &self.client)
152 .field("request", &self.request)
153 .field("verifier", &self.verifier)
154 .field("verifying", &self.verification.is_some())
155 .field("retries", &self.retries)
156 .finish_non_exhaustive()
157 }
158}
159
160pub(super) trait SnapRequest {
162 fn send<C: SnapClient>(&self, client: &C, priority: Priority) -> C::Output;
164}
165
166impl SnapRequest for GetAccountRangeMessage {
167 fn send<C: SnapClient>(&self, client: &C, priority: Priority) -> C::Output {
168 client.get_account_range_with_priority(self.clone(), priority)
169 }
170}
171
172impl SnapRequest for GetStorageRangesMessage {
173 fn send<C: SnapClient>(&self, client: &C, priority: Priority) -> C::Output {
174 client.get_storage_ranges_with_priority(self.clone(), priority)
175 }
176}
177
178impl SnapRequest for GetBlockAccessListsMessage {
179 fn send<C: SnapClient>(&self, client: &C, priority: Priority) -> C::Output {
180 client.get_block_access_lists_with_priority(self.clone(), priority)
181 }
182}
183
184impl SnapRequest for GetByteCodesMessage {
185 fn send<C: SnapClient>(&self, client: &C, priority: Priority) -> C::Output {
186 client.get_byte_codes_with_priority(self.clone(), priority)
187 }
188}
189
190pub(super) trait SnapVerifier: Clone + Send + 'static {
192 type Request: SnapRequest;
194 type Output: Send + 'static;
196
197 fn verify(self, peer_id: PeerId, response: SnapResponse) -> Result<Self::Output, RequestError>;
199}
200
201struct VerificationTask<O> {
203 peer_id: PeerId,
205 fut: tokio::task::JoinHandle<Result<O, RequestError>>,
207}