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