1use crate::schema;
26use codec::{self, Decode, Encode};
27use futures::prelude::*;
28use log::{debug, trace};
29use prost::Message;
30use sc_client_api::{BlockBackend, ProofProvider};
31use sc_network::{
32 config::ProtocolId,
33 request_responses::{IncomingRequest, OutgoingResponse},
34 NetworkBackend, ReputationChange,
35};
36use sc_network_types::PeerId;
37use sp_core::{
38 hexdisplay::HexDisplay,
39 storage::{ChildInfo, ChildType, PrefixedStorageKey},
40};
41use sp_runtime::traits::Block;
42use std::{marker::PhantomData, sync::Arc, time::Duration};
43
44const LOG_TARGET: &str = "light-client-request-handler";
45
46const MAX_LIGHT_REQUEST_QUEUE: usize = 20;
49
50pub struct LightClientRequestHandler<B, Client> {
52 request_receiver: async_channel::Receiver<IncomingRequest>,
53 client: Arc<Client>,
55 execution_timeout: Option<Duration>,
57 _block: PhantomData<B>,
58}
59
60impl<B, Client> LightClientRequestHandler<B, Client>
61where
62 B: Block,
63 Client: BlockBackend<B> + ProofProvider<B> + Send + Sync + 'static,
64{
65 pub fn new<N: NetworkBackend<B, <B as Block>::Hash>>(
67 protocol_id: &ProtocolId,
68 fork_id: Option<&str>,
69 client: Arc<Client>,
70 execution_timeout: Option<Duration>,
71 ) -> (Self, N::RequestResponseProtocolConfig) {
72 let (tx, request_receiver) = async_channel::bounded(MAX_LIGHT_REQUEST_QUEUE);
73
74 let protocol_config = super::generate_protocol_config::<_, B, N>(
75 protocol_id,
76 client
77 .block_hash(0u32.into())
78 .ok()
79 .flatten()
80 .expect("Genesis block exists; qed"),
81 fork_id,
82 tx,
83 );
84
85 (
86 Self { client, request_receiver, execution_timeout, _block: PhantomData::default() },
87 protocol_config,
88 )
89 }
90
91 pub async fn run(mut self) {
97 while let Some(request) = self.request_receiver.next().await {
98 let IncomingRequest { peer, payload, pending_response } = request;
99
100 match self.handle_request(peer, payload) {
101 Ok(response_data) => {
102 let response = OutgoingResponse {
103 result: Ok(response_data),
104 reputation_changes: Vec::new(),
105 sent_feedback: None,
106 };
107
108 match pending_response.send(response) {
109 Ok(()) => trace!(
110 target: LOG_TARGET,
111 "Handled light client request from {}.",
112 peer,
113 ),
114 Err(_) => debug!(
115 target: LOG_TARGET,
116 "Failed to handle light client request from {}: {}",
117 peer,
118 HandleRequestError::SendResponse,
119 ),
120 };
121 },
122 Err(e) => {
123 debug!(
124 target: LOG_TARGET,
125 "Failed to handle light client request from {}: {}", peer, e,
126 );
127
128 let reputation_changes = match e {
129 HandleRequestError::BadRequest(_) => {
130 vec![ReputationChange::new(-(1 << 12), "bad request")]
131 },
132 _ => Vec::new(),
133 };
134
135 let response = OutgoingResponse {
136 result: Err(()),
137 reputation_changes,
138 sent_feedback: None,
139 };
140
141 if pending_response.send(response).is_err() {
142 debug!(
143 target: LOG_TARGET,
144 "Failed to handle light client request from {}: {}",
145 peer,
146 HandleRequestError::SendResponse,
147 );
148 };
149 },
150 }
151 }
152 }
153
154 fn handle_request(
155 &mut self,
156 peer: PeerId,
157 payload: Vec<u8>,
158 ) -> Result<Vec<u8>, HandleRequestError> {
159 let request = schema::v1::light::Request::decode(&payload[..])?;
160
161 let response = match &request.request {
162 Some(schema::v1::light::request::Request::RemoteCallRequest(r)) => {
163 self.on_remote_call_request(&peer, r)?
164 },
165 Some(schema::v1::light::request::Request::RemoteReadRequest(r)) => {
166 self.on_remote_read_request(&peer, r)?
167 },
168 Some(schema::v1::light::request::Request::RemoteReadChildRequest(r)) => {
169 self.on_remote_read_child_request(&peer, r)?
170 },
171 None => {
172 return Err(HandleRequestError::BadRequest("Remote request without request data."))
173 },
174 };
175
176 let mut data = Vec::new();
177 response.encode(&mut data)?;
178
179 Ok(data)
180 }
181
182 fn on_remote_call_request(
183 &mut self,
184 peer: &PeerId,
185 request: &schema::v1::light::RemoteCallRequest,
186 ) -> Result<schema::v1::light::Response, HandleRequestError> {
187 trace!("Remote call request from {} ({} at {:?}).", peer, request.method, request.block,);
188
189 let block = Decode::decode(&mut request.block.as_ref())?;
190
191 let response = match self.client.execution_proof(
192 block,
193 &request.method,
194 &request.data,
195 self.execution_timeout,
196 ) {
197 Ok((_, proof)) => schema::v1::light::RemoteCallResponse { proof: Some(proof.encode()) },
198 Err(e) => {
199 trace!(
200 "remote call request from {} ({} at {:?}) failed with: {}",
201 peer,
202 request.method,
203 request.block,
204 e,
205 );
206 schema::v1::light::RemoteCallResponse { proof: None }
207 },
208 };
209
210 Ok(schema::v1::light::Response {
211 response: Some(schema::v1::light::response::Response::RemoteCallResponse(response)),
212 })
213 }
214
215 fn on_remote_read_request(
216 &mut self,
217 peer: &PeerId,
218 request: &schema::v1::light::RemoteReadRequest,
219 ) -> Result<schema::v1::light::Response, HandleRequestError> {
220 if request.keys.is_empty() {
221 debug!("Invalid remote read request sent by {}.", peer);
222 return Err(HandleRequestError::BadRequest("Remote read request without keys."));
223 }
224
225 trace!(
226 "Remote read request from {} ({} at {:?}).",
227 peer,
228 fmt_keys(request.keys.first(), request.keys.last()),
229 request.block,
230 );
231
232 let block = Decode::decode(&mut request.block.as_ref())?;
233
234 let response =
235 match self.client.read_proof(block, &mut request.keys.iter().map(AsRef::as_ref)) {
236 Ok(proof) => schema::v1::light::RemoteReadResponse { proof: Some(proof.encode()) },
237 Err(error) => {
238 trace!(
239 "remote read request from {} ({} at {:?}) failed with: {}",
240 peer,
241 fmt_keys(request.keys.first(), request.keys.last()),
242 request.block,
243 error,
244 );
245 schema::v1::light::RemoteReadResponse { proof: None }
246 },
247 };
248
249 Ok(schema::v1::light::Response {
250 response: Some(schema::v1::light::response::Response::RemoteReadResponse(response)),
251 })
252 }
253
254 fn on_remote_read_child_request(
255 &mut self,
256 peer: &PeerId,
257 request: &schema::v1::light::RemoteReadChildRequest,
258 ) -> Result<schema::v1::light::Response, HandleRequestError> {
259 if request.keys.is_empty() {
260 debug!("Invalid remote child read request sent by {}.", peer);
261 return Err(HandleRequestError::BadRequest("Remove read child request without keys."));
262 }
263
264 trace!(
265 "Remote read child request from {} ({} {} at {:?}).",
266 peer,
267 HexDisplay::from(&request.storage_key),
268 fmt_keys(request.keys.first(), request.keys.last()),
269 request.block,
270 );
271
272 let block = Decode::decode(&mut request.block.as_ref())?;
273
274 let prefixed_key = PrefixedStorageKey::new_ref(&request.storage_key);
275 let child_info = match ChildType::from_prefixed_key(prefixed_key) {
276 Some((ChildType::ParentKeyId, storage_key)) => Ok(ChildInfo::new_default(storage_key)),
277 None => Err(sp_blockchain::Error::InvalidChildStorageKey),
278 };
279 let response = match child_info.and_then(|child_info| {
280 self.client.read_child_proof(
281 block,
282 &child_info,
283 &mut request.keys.iter().map(AsRef::as_ref),
284 )
285 }) {
286 Ok(proof) => schema::v1::light::RemoteReadResponse { proof: Some(proof.encode()) },
287 Err(error) => {
288 trace!(
289 "remote read child request from {} ({} {} at {:?}) failed with: {}",
290 peer,
291 HexDisplay::from(&request.storage_key),
292 fmt_keys(request.keys.first(), request.keys.last()),
293 request.block,
294 error,
295 );
296 schema::v1::light::RemoteReadResponse { proof: None }
297 },
298 };
299
300 Ok(schema::v1::light::Response {
301 response: Some(schema::v1::light::response::Response::RemoteReadResponse(response)),
302 })
303 }
304}
305
306#[derive(Debug, thiserror::Error)]
307enum HandleRequestError {
308 #[error("Failed to decode request: {0}.")]
309 DecodeProto(#[from] prost::DecodeError),
310 #[error("Failed to encode response: {0}.")]
311 EncodeProto(#[from] prost::EncodeError),
312 #[error("Failed to send response.")]
313 SendResponse,
314 #[error("bad request: {0}")]
316 BadRequest(&'static str),
317 #[error("codec error: {0}")]
319 Codec(#[from] codec::Error),
320}
321
322fn fmt_keys(first: Option<&Vec<u8>>, last: Option<&Vec<u8>>) -> String {
323 if let (Some(first), Some(last)) = (first, last) {
324 if first == last {
325 HexDisplay::from(first).to_string()
326 } else {
327 format!("{}..{}", HexDisplay::from(first), HexDisplay::from(last))
328 }
329 } else {
330 String::from("n/a")
331 }
332}
333
334#[cfg(test)]
335mod tests {
336 use super::*;
337 use sc_network::NetworkWorker;
338 use std::time::Instant;
339 use substrate_test_runtime_client::{
340 runtime::{Block, Hash},
341 TestClient,
342 };
343
344 fn make_handler(
345 execution_timeout: Option<Duration>,
346 ) -> LightClientRequestHandler<Block, TestClient> {
347 let client = Arc::new(substrate_test_runtime_client::new());
348 let (handler, _protocol_config) =
349 LightClientRequestHandler::new::<NetworkWorker<Block, Hash>>(
350 &ProtocolId::from("test"),
351 None,
352 client,
353 execution_timeout,
354 );
355 handler
356 }
357
358 fn remote_call(
360 handler: &mut LightClientRequestHandler<Block, TestClient>,
361 method: &str,
362 ) -> schema::v1::light::RemoteCallResponse {
363 let genesis_hash = handler.client.block_hash(0u32.into()).unwrap().unwrap();
364 let request = schema::v1::light::Request {
365 request: Some(schema::v1::light::request::Request::RemoteCallRequest(
366 schema::v1::light::RemoteCallRequest {
367 block: genesis_hash.encode(),
368 method: method.into(),
369 data: Vec::new(),
370 },
371 )),
372 };
373
374 let response = handler.handle_request(PeerId::random(), request.encode_to_vec()).unwrap();
375 let response = schema::v1::light::Response::decode(&response[..]).unwrap();
376 match response.response {
377 Some(schema::v1::light::response::Response::RemoteCallResponse(response)) => response,
378 response => panic!("unexpected response: {response:?}"),
379 }
380 }
381
382 #[test]
383 fn remote_call_with_generous_timeout_succeeds() {
384 let mut handler = make_handler(Some(Duration::from_secs(60)));
387
388 let response = remote_call(&mut handler, "TestAPI_get_block_number");
389
390 assert!(response.proof.is_some());
391 }
392
393 #[test]
394 fn execution_timeout_interrupts_remote_call() {
395 let timeout = Duration::from_millis(300);
396 let mut handler = make_handler(Some(timeout));
397
398 let started = Instant::now();
399 let response = remote_call(&mut handler, "TestAPI_spin");
400
401 assert!(started.elapsed() >= timeout);
402 assert_eq!(response.proof, None);
403 }
404}