soil_network/light/light_client_requests/
handler.rs1use crate::light::schema;
14use crate::types::PeerId;
15use crate::{
16 config::ProtocolId,
17 request_responses::{IncomingRequest, OutgoingResponse},
18 NetworkBackend, ReputationChange,
19};
20use codec::{self, Decode, Encode};
21use futures::prelude::*;
22use log::{debug, trace};
23use prost::Message;
24use soil_client::client_api::{BlockBackend, ProofProvider};
25use std::{marker::PhantomData, sync::Arc};
26use subsoil::core::{
27 hexdisplay::HexDisplay,
28 storage::{ChildInfo, ChildType, PrefixedStorageKey},
29};
30use subsoil::runtime::traits::Block;
31
32const LOG_TARGET: &str = "light-client-request-handler";
33
34const MAX_LIGHT_REQUEST_QUEUE: usize = 20;
37
38pub struct LightClientRequestHandler<B, Client> {
40 request_receiver: async_channel::Receiver<IncomingRequest>,
41 client: Arc<Client>,
43 _block: PhantomData<B>,
44}
45
46impl<B, Client> LightClientRequestHandler<B, Client>
47where
48 B: Block,
49 Client: BlockBackend<B> + ProofProvider<B> + Send + Sync + 'static,
50{
51 pub fn new<N: NetworkBackend<B, <B as Block>::Hash>>(
53 protocol_id: &ProtocolId,
54 fork_id: Option<&str>,
55 client: Arc<Client>,
56 ) -> (Self, N::RequestResponseProtocolConfig) {
57 let (tx, request_receiver) = async_channel::bounded(MAX_LIGHT_REQUEST_QUEUE);
58
59 let protocol_config = super::generate_protocol_config::<_, B, N>(
60 protocol_id,
61 client
62 .block_hash(0u32.into())
63 .ok()
64 .flatten()
65 .expect("Genesis block exists; qed"),
66 fork_id,
67 tx,
68 );
69
70 (Self { client, request_receiver, _block: PhantomData::default() }, protocol_config)
71 }
72
73 pub async fn run(mut self) {
75 while let Some(request) = self.request_receiver.next().await {
76 let IncomingRequest { peer, payload, pending_response } = request;
77
78 match self.handle_request(peer, payload) {
79 Ok(response_data) => {
80 let response = OutgoingResponse {
81 result: Ok(response_data),
82 reputation_changes: Vec::new(),
83 sent_feedback: None,
84 };
85
86 match pending_response.send(response) {
87 Ok(()) => trace!(
88 target: LOG_TARGET,
89 "Handled light client request from {}.",
90 peer,
91 ),
92 Err(_) => debug!(
93 target: LOG_TARGET,
94 "Failed to handle light client request from {}: {}",
95 peer,
96 HandleRequestError::SendResponse,
97 ),
98 };
99 },
100 Err(e) => {
101 debug!(
102 target: LOG_TARGET,
103 "Failed to handle light client request from {}: {}", peer, e,
104 );
105
106 let reputation_changes = match e {
107 HandleRequestError::BadRequest(_) => {
108 vec![ReputationChange::new(-(1 << 12), "bad request")]
109 },
110 _ => Vec::new(),
111 };
112
113 let response = OutgoingResponse {
114 result: Err(()),
115 reputation_changes,
116 sent_feedback: None,
117 };
118
119 if pending_response.send(response).is_err() {
120 debug!(
121 target: LOG_TARGET,
122 "Failed to handle light client request from {}: {}",
123 peer,
124 HandleRequestError::SendResponse,
125 );
126 };
127 },
128 }
129 }
130 }
131
132 fn handle_request(
133 &mut self,
134 peer: PeerId,
135 payload: Vec<u8>,
136 ) -> Result<Vec<u8>, HandleRequestError> {
137 let request = schema::v1::light::Request::decode(&payload[..])?;
138
139 let response = match &request.request {
140 Some(schema::v1::light::request::Request::RemoteCallRequest(r)) => {
141 self.on_remote_call_request(&peer, r)?
142 },
143 Some(schema::v1::light::request::Request::RemoteReadRequest(r)) => {
144 self.on_remote_read_request(&peer, r)?
145 },
146 Some(schema::v1::light::request::Request::RemoteReadChildRequest(r)) => {
147 self.on_remote_read_child_request(&peer, r)?
148 },
149 None => {
150 return Err(HandleRequestError::BadRequest("Remote request without request data."))
151 },
152 };
153
154 let mut data = Vec::new();
155 response.encode(&mut data)?;
156
157 Ok(data)
158 }
159
160 fn on_remote_call_request(
161 &mut self,
162 peer: &PeerId,
163 request: &schema::v1::light::RemoteCallRequest,
164 ) -> Result<schema::v1::light::Response, HandleRequestError> {
165 trace!("Remote call request from {} ({} at {:?}).", peer, request.method, request.block,);
166
167 let block = Decode::decode(&mut request.block.as_ref())?;
168
169 let response = match self.client.execution_proof(block, &request.method, &request.data) {
170 Ok((_, proof)) => schema::v1::light::RemoteCallResponse { proof: Some(proof.encode()) },
171 Err(e) => {
172 trace!(
173 "remote call request from {} ({} at {:?}) failed with: {}",
174 peer,
175 request.method,
176 request.block,
177 e,
178 );
179 schema::v1::light::RemoteCallResponse { proof: None }
180 },
181 };
182
183 Ok(schema::v1::light::Response {
184 response: Some(schema::v1::light::response::Response::RemoteCallResponse(response)),
185 })
186 }
187
188 fn on_remote_read_request(
189 &mut self,
190 peer: &PeerId,
191 request: &schema::v1::light::RemoteReadRequest,
192 ) -> Result<schema::v1::light::Response, HandleRequestError> {
193 if request.keys.is_empty() {
194 debug!("Invalid remote read request sent by {}.", peer);
195 return Err(HandleRequestError::BadRequest("Remote read request without keys."));
196 }
197
198 trace!(
199 "Remote read request from {} ({} at {:?}).",
200 peer,
201 fmt_keys(request.keys.first(), request.keys.last()),
202 request.block,
203 );
204
205 let block = Decode::decode(&mut request.block.as_ref())?;
206
207 let response =
208 match self.client.read_proof(block, &mut request.keys.iter().map(AsRef::as_ref)) {
209 Ok(proof) => schema::v1::light::RemoteReadResponse { proof: Some(proof.encode()) },
210 Err(error) => {
211 trace!(
212 "remote read request from {} ({} at {:?}) failed with: {}",
213 peer,
214 fmt_keys(request.keys.first(), request.keys.last()),
215 request.block,
216 error,
217 );
218 schema::v1::light::RemoteReadResponse { proof: None }
219 },
220 };
221
222 Ok(schema::v1::light::Response {
223 response: Some(schema::v1::light::response::Response::RemoteReadResponse(response)),
224 })
225 }
226
227 fn on_remote_read_child_request(
228 &mut self,
229 peer: &PeerId,
230 request: &schema::v1::light::RemoteReadChildRequest,
231 ) -> Result<schema::v1::light::Response, HandleRequestError> {
232 if request.keys.is_empty() {
233 debug!("Invalid remote child read request sent by {}.", peer);
234 return Err(HandleRequestError::BadRequest("Remove read child request without keys."));
235 }
236
237 trace!(
238 "Remote read child request from {} ({} {} at {:?}).",
239 peer,
240 HexDisplay::from(&request.storage_key),
241 fmt_keys(request.keys.first(), request.keys.last()),
242 request.block,
243 );
244
245 let block = Decode::decode(&mut request.block.as_ref())?;
246
247 let prefixed_key = PrefixedStorageKey::new_ref(&request.storage_key);
248 let child_info = match ChildType::from_prefixed_key(prefixed_key) {
249 Some((ChildType::ParentKeyId, storage_key)) => Ok(ChildInfo::new_default(storage_key)),
250 None => Err(soil_client::blockchain::Error::InvalidChildStorageKey),
251 };
252 let response = match child_info.and_then(|child_info| {
253 self.client.read_child_proof(
254 block,
255 &child_info,
256 &mut request.keys.iter().map(AsRef::as_ref),
257 )
258 }) {
259 Ok(proof) => schema::v1::light::RemoteReadResponse { proof: Some(proof.encode()) },
260 Err(error) => {
261 trace!(
262 "remote read child request from {} ({} {} at {:?}) failed with: {}",
263 peer,
264 HexDisplay::from(&request.storage_key),
265 fmt_keys(request.keys.first(), request.keys.last()),
266 request.block,
267 error,
268 );
269 schema::v1::light::RemoteReadResponse { proof: None }
270 },
271 };
272
273 Ok(schema::v1::light::Response {
274 response: Some(schema::v1::light::response::Response::RemoteReadResponse(response)),
275 })
276 }
277}
278
279#[derive(Debug, thiserror::Error)]
280enum HandleRequestError {
281 #[error("Failed to decode request: {0}.")]
282 DecodeProto(#[from] prost::DecodeError),
283 #[error("Failed to encode response: {0}.")]
284 EncodeProto(#[from] prost::EncodeError),
285 #[error("Failed to send response.")]
286 SendResponse,
287 #[error("bad request: {0}")]
289 BadRequest(&'static str),
290 #[error("codec error: {0}")]
292 Codec(#[from] codec::Error),
293}
294
295fn fmt_keys(first: Option<&Vec<u8>>, last: Option<&Vec<u8>>) -> String {
296 if let (Some(first), Some(last)) = (first, last) {
297 if first == last {
298 HexDisplay::from(first).to_string()
299 } else {
300 format!("{}..{}", HexDisplay::from(first), HexDisplay::from(last))
301 }
302 } else {
303 String::from("n/a")
304 }
305}