Skip to main content

sc_network_light/light_client_requests/
handler.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
5
6// This program is free software: you can redistribute it and/or modify
7// it under the terms of the GNU General Public License as published by
8// the Free Software Foundation, either version 3 of the License, or
9// (at your option) any later version.
10
11// This program is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14// GNU General Public License for more details.
15
16// You should have received a copy of the GNU General Public License
17// along with this program. If not, see <https://www.gnu.org/licenses/>.
18
19//! Helper for incoming light client requests.
20//!
21//! Handle (i.e. answer) incoming light client requests from a remote peer received via
22//! `crate::request_responses::RequestResponsesBehaviour` with
23//! [`LightClientRequestHandler`](handler::LightClientRequestHandler).
24
25use 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};
43
44const LOG_TARGET: &str = "light-client-request-handler";
45
46/// Incoming requests bounded queue size. For now due to lack of data on light client request
47/// handling in production systems, this value is chosen to match the block request limit.
48const MAX_LIGHT_REQUEST_QUEUE: usize = 20;
49
50/// Handler for incoming light client requests from a remote peer.
51pub struct LightClientRequestHandler<B, Client> {
52	request_receiver: async_channel::Receiver<IncomingRequest>,
53	/// Blockchain client.
54	client: Arc<Client>,
55	_block: PhantomData<B>,
56}
57
58impl<B, Client> LightClientRequestHandler<B, Client>
59where
60	B: Block,
61	Client: BlockBackend<B> + ProofProvider<B> + Send + Sync + 'static,
62{
63	/// Create a new [`LightClientRequestHandler`].
64	pub fn new<N: NetworkBackend<B, <B as Block>::Hash>>(
65		protocol_id: &ProtocolId,
66		fork_id: Option<&str>,
67		client: Arc<Client>,
68	) -> (Self, N::RequestResponseProtocolConfig) {
69		let (tx, request_receiver) = async_channel::bounded(MAX_LIGHT_REQUEST_QUEUE);
70
71		let protocol_config = super::generate_protocol_config::<_, B, N>(
72			protocol_id,
73			client
74				.block_hash(0u32.into())
75				.ok()
76				.flatten()
77				.expect("Genesis block exists; qed"),
78			fork_id,
79			tx,
80		);
81
82		(Self { client, request_receiver, _block: PhantomData::default() }, protocol_config)
83	}
84
85	/// Run [`LightClientRequestHandler`].
86	pub async fn run(mut self) {
87		while let Some(request) = self.request_receiver.next().await {
88			let IncomingRequest { peer, payload, pending_response } = request;
89
90			match self.handle_request(peer, payload) {
91				Ok(response_data) => {
92					let response = OutgoingResponse {
93						result: Ok(response_data),
94						reputation_changes: Vec::new(),
95						sent_feedback: None,
96					};
97
98					match pending_response.send(response) {
99						Ok(()) => trace!(
100							target: LOG_TARGET,
101							"Handled light client request from {}.",
102							peer,
103						),
104						Err(_) => debug!(
105							target: LOG_TARGET,
106							"Failed to handle light client request from {}: {}",
107							peer,
108							HandleRequestError::SendResponse,
109						),
110					};
111				},
112				Err(e) => {
113					debug!(
114						target: LOG_TARGET,
115						"Failed to handle light client request from {}: {}", peer, e,
116					);
117
118					let reputation_changes = match e {
119						HandleRequestError::BadRequest(_) => {
120							vec![ReputationChange::new(-(1 << 12), "bad request")]
121						},
122						_ => Vec::new(),
123					};
124
125					let response = OutgoingResponse {
126						result: Err(()),
127						reputation_changes,
128						sent_feedback: None,
129					};
130
131					if pending_response.send(response).is_err() {
132						debug!(
133							target: LOG_TARGET,
134							"Failed to handle light client request from {}: {}",
135							peer,
136							HandleRequestError::SendResponse,
137						);
138					};
139				},
140			}
141		}
142	}
143
144	fn handle_request(
145		&mut self,
146		peer: PeerId,
147		payload: Vec<u8>,
148	) -> Result<Vec<u8>, HandleRequestError> {
149		let request = schema::v1::light::Request::decode(&payload[..])?;
150
151		let response = match &request.request {
152			Some(schema::v1::light::request::Request::RemoteCallRequest(r)) => {
153				self.on_remote_call_request(&peer, r)?
154			},
155			Some(schema::v1::light::request::Request::RemoteReadRequest(r)) => {
156				self.on_remote_read_request(&peer, r)?
157			},
158			Some(schema::v1::light::request::Request::RemoteReadChildRequest(r)) => {
159				self.on_remote_read_child_request(&peer, r)?
160			},
161			None => {
162				return Err(HandleRequestError::BadRequest("Remote request without request data."))
163			},
164		};
165
166		let mut data = Vec::new();
167		response.encode(&mut data)?;
168
169		Ok(data)
170	}
171
172	fn on_remote_call_request(
173		&mut self,
174		peer: &PeerId,
175		request: &schema::v1::light::RemoteCallRequest,
176	) -> Result<schema::v1::light::Response, HandleRequestError> {
177		trace!("Remote call request from {} ({} at {:?}).", peer, request.method, request.block,);
178
179		let block = Decode::decode(&mut request.block.as_ref())?;
180
181		let response = match self.client.execution_proof(block, &request.method, &request.data) {
182			Ok((_, proof)) => schema::v1::light::RemoteCallResponse { proof: Some(proof.encode()) },
183			Err(e) => {
184				trace!(
185					"remote call request from {} ({} at {:?}) failed with: {}",
186					peer,
187					request.method,
188					request.block,
189					e,
190				);
191				schema::v1::light::RemoteCallResponse { proof: None }
192			},
193		};
194
195		Ok(schema::v1::light::Response {
196			response: Some(schema::v1::light::response::Response::RemoteCallResponse(response)),
197		})
198	}
199
200	fn on_remote_read_request(
201		&mut self,
202		peer: &PeerId,
203		request: &schema::v1::light::RemoteReadRequest,
204	) -> Result<schema::v1::light::Response, HandleRequestError> {
205		if request.keys.is_empty() {
206			debug!("Invalid remote read request sent by {}.", peer);
207			return Err(HandleRequestError::BadRequest("Remote read request without keys."));
208		}
209
210		trace!(
211			"Remote read request from {} ({} at {:?}).",
212			peer,
213			fmt_keys(request.keys.first(), request.keys.last()),
214			request.block,
215		);
216
217		let block = Decode::decode(&mut request.block.as_ref())?;
218
219		let response =
220			match self.client.read_proof(block, &mut request.keys.iter().map(AsRef::as_ref)) {
221				Ok(proof) => schema::v1::light::RemoteReadResponse { proof: Some(proof.encode()) },
222				Err(error) => {
223					trace!(
224						"remote read request from {} ({} at {:?}) failed with: {}",
225						peer,
226						fmt_keys(request.keys.first(), request.keys.last()),
227						request.block,
228						error,
229					);
230					schema::v1::light::RemoteReadResponse { proof: None }
231				},
232			};
233
234		Ok(schema::v1::light::Response {
235			response: Some(schema::v1::light::response::Response::RemoteReadResponse(response)),
236		})
237	}
238
239	fn on_remote_read_child_request(
240		&mut self,
241		peer: &PeerId,
242		request: &schema::v1::light::RemoteReadChildRequest,
243	) -> Result<schema::v1::light::Response, HandleRequestError> {
244		if request.keys.is_empty() {
245			debug!("Invalid remote child read request sent by {}.", peer);
246			return Err(HandleRequestError::BadRequest("Remove read child request without keys."));
247		}
248
249		trace!(
250			"Remote read child request from {} ({} {} at {:?}).",
251			peer,
252			HexDisplay::from(&request.storage_key),
253			fmt_keys(request.keys.first(), request.keys.last()),
254			request.block,
255		);
256
257		let block = Decode::decode(&mut request.block.as_ref())?;
258
259		let prefixed_key = PrefixedStorageKey::new_ref(&request.storage_key);
260		let child_info = match ChildType::from_prefixed_key(prefixed_key) {
261			Some((ChildType::ParentKeyId, storage_key)) => Ok(ChildInfo::new_default(storage_key)),
262			None => Err(sp_blockchain::Error::InvalidChildStorageKey),
263		};
264		let response = match child_info.and_then(|child_info| {
265			self.client.read_child_proof(
266				block,
267				&child_info,
268				&mut request.keys.iter().map(AsRef::as_ref),
269			)
270		}) {
271			Ok(proof) => schema::v1::light::RemoteReadResponse { proof: Some(proof.encode()) },
272			Err(error) => {
273				trace!(
274					"remote read child request from {} ({} {} at {:?}) failed with: {}",
275					peer,
276					HexDisplay::from(&request.storage_key),
277					fmt_keys(request.keys.first(), request.keys.last()),
278					request.block,
279					error,
280				);
281				schema::v1::light::RemoteReadResponse { proof: None }
282			},
283		};
284
285		Ok(schema::v1::light::Response {
286			response: Some(schema::v1::light::response::Response::RemoteReadResponse(response)),
287		})
288	}
289}
290
291#[derive(Debug, thiserror::Error)]
292enum HandleRequestError {
293	#[error("Failed to decode request: {0}.")]
294	DecodeProto(#[from] prost::DecodeError),
295	#[error("Failed to encode response: {0}.")]
296	EncodeProto(#[from] prost::EncodeError),
297	#[error("Failed to send response.")]
298	SendResponse,
299	/// A bad request has been received.
300	#[error("bad request: {0}")]
301	BadRequest(&'static str),
302	/// Encoding or decoding of some data failed.
303	#[error("codec error: {0}")]
304	Codec(#[from] codec::Error),
305}
306
307fn fmt_keys(first: Option<&Vec<u8>>, last: Option<&Vec<u8>>) -> String {
308	if let (Some(first), Some(last)) = (first, last) {
309		if first == last {
310			HexDisplay::from(first).to_string()
311		} else {
312			format!("{}..{}", HexDisplay::from(first), HexDisplay::from(last))
313		}
314	} else {
315		String::from("n/a")
316	}
317}