/__w/smoldot/smoldot/repo/full-node/src/json_rpc_service/requests_handler.rs
Line | Count | Source (jump to first uncovered line) |
1 | | // Smoldot |
2 | | // Copyright (C) 2023 Pierre Krieger |
3 | | // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 |
4 | | |
5 | | // This program is free software: you can redistribute it and/or modify |
6 | | // it under the terms of the GNU General Public License as published by |
7 | | // the Free Software Foundation, either version 3 of the License, or |
8 | | // (at your option) any later version. |
9 | | |
10 | | // This program is distributed in the hope that it will be useful, |
11 | | // but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
13 | | // GNU General Public License for more details. |
14 | | |
15 | | // You should have received a copy of the GNU General Public License |
16 | | // along with this program. If not, see <http://www.gnu.org/licenses/>. |
17 | | |
18 | | use futures_lite::future; |
19 | | use smol::stream::StreamExt as _; |
20 | | use smoldot::{ |
21 | | executor, |
22 | | json_rpc::{methods, parse, service}, |
23 | | trie, |
24 | | }; |
25 | | use std::{ |
26 | | future::Future, |
27 | | iter, |
28 | | pin::{self, Pin}, |
29 | | sync::Arc, |
30 | | }; |
31 | | |
32 | | use crate::{ |
33 | | consensus_service, database_thread, |
34 | | json_rpc_service::{legacy_api_subscriptions, runtime_caches_service}, |
35 | | network_service, LogCallback, LogLevel, |
36 | | }; |
37 | | |
38 | | pub struct Config { |
39 | | /// Function that can be used to spawn background tasks. |
40 | | /// |
41 | | /// The tasks passed as parameter must be executed until they shut down. |
42 | | pub tasks_executor: Arc<dyn Fn(Pin<Box<dyn Future<Output = ()> + Send>>) + Send + Sync>, |
43 | | |
44 | | /// Function called in order to notify of something. |
45 | | pub log_callback: Arc<dyn LogCallback + Send + Sync>, |
46 | | |
47 | | pub receiver: async_channel::Receiver<Message>, |
48 | | |
49 | | /// Database to access blocks. |
50 | | pub database: Arc<database_thread::DatabaseThread>, |
51 | | |
52 | | /// Access to the network, and identifier of the chain from the point of view of the network |
53 | | /// service. |
54 | | pub network_service: ( |
55 | | Arc<network_service::NetworkService>, |
56 | | network_service::ChainId, |
57 | | ), |
58 | | |
59 | | /// Name of the chain, as found in the chain specification. |
60 | | pub chain_name: String, |
61 | | |
62 | | /// Type of the chain, as found in the chain specification. |
63 | | pub chain_type: String, |
64 | | |
65 | | /// JSON-encoded properties of the chain, as found in the chain specification. |
66 | | pub chain_properties_json: String, |
67 | | |
68 | | /// Whether the chain is a live network. Found in the chain specification. |
69 | | pub chain_is_live: bool, |
70 | | |
71 | | /// Hash of the genesis block. |
72 | | // TODO: load from database maybe? |
73 | | pub genesis_block_hash: [u8; 32], |
74 | | |
75 | | /// Consensus service of the chain. |
76 | | pub consensus_service: Arc<consensus_service::ConsensusService>, |
77 | | |
78 | | /// Runtime caches service of the JSON-RPC service. |
79 | | pub runtime_caches_service: Arc<runtime_caches_service::RuntimeCachesService>, |
80 | | } |
81 | | |
82 | | pub enum Message { |
83 | | Request(service::RequestProcess), |
84 | | SubscriptionStart(service::SubscriptionStartProcess), |
85 | | } |
86 | | |
87 | 672 | pub fn spawn_requests_handler(config: Config) { |
88 | 672 | let tasks_executor = config.tasks_executor.clone(); |
89 | 672 | tasks_executor(Box::pin(async move { |
90 | 672 | let mut receiver = pin::pin!(config.receiver); |
91 | | loop { |
92 | 1.28k | match receiver.next()695 .await { |
93 | 23 | Some(Message::Request(request)) => match request.request() { |
94 | 0 | methods::MethodCall::rpc_methods {} => { |
95 | 0 | request.respond(methods::Response::rpc_methods(methods::RpcMethods { |
96 | 0 | methods: methods::MethodCall::method_names() |
97 | 0 | .map(|n| n.into()) Unexecuted instantiation: _RNCNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler00B9_ Unexecuted instantiation: _RNCNCNvNtNtCshBwayKnNXDT_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler00B9_ |
98 | 0 | .collect(), |
99 | 0 | })); |
100 | 0 | } |
101 | | |
102 | 1 | methods::MethodCall::chainSpec_v1_chainName {} => { |
103 | 1 | request.respond(methods::Response::chainSpec_v1_chainName( |
104 | 1 | (&config.chain_name).into(), |
105 | 1 | )); |
106 | 1 | } |
107 | 1 | methods::MethodCall::chainSpec_v1_genesisHash {} => { |
108 | 1 | request.respond(methods::Response::chainSpec_v1_genesisHash( |
109 | 1 | methods::HashHexString(config.genesis_block_hash), |
110 | 1 | )); |
111 | 1 | } |
112 | 1 | methods::MethodCall::chainSpec_v1_properties {} => { |
113 | 1 | request.respond(methods::Response::chainSpec_v1_properties( |
114 | 1 | serde_json::from_str(&config.chain_properties_json).unwrap(), |
115 | 1 | )); |
116 | 1 | } |
117 | | |
118 | | methods::MethodCall::chain_getBlockHash { height: Some(0) } => { |
119 | | // In the case where the database was populated through a warp sync, it |
120 | | // might not store block 0 in it. However, the hash of block 0 is |
121 | | // particularly important for JSON-RPC clients, and as such we make sure |
122 | | // to always respond successfully to block 0 requests, even if it isn't |
123 | | // in the database. |
124 | 1 | request.respond(methods::Response::chain_getBlockHash( |
125 | 1 | methods::HashHexString(config.genesis_block_hash), |
126 | 1 | )) |
127 | | } |
128 | 1 | methods::MethodCall::chain_getBlockHash { height } => { |
129 | 1 | let outcome = config |
130 | 1 | .database |
131 | 1 | .with_database(move |database| match height { |
132 | 1 | Some(height) => database.best_block_hash_by_number(height), |
133 | 0 | None => database.best_block_hash().map(Some), |
134 | 1 | }) _RNCNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0s_0B9_ Line | Count | Source | 131 | 1 | .with_database(move |database| match height { | 132 | 1 | Some(height) => database.best_block_hash_by_number(height), | 133 | 0 | None => database.best_block_hash().map(Some), | 134 | 1 | }) |
Unexecuted instantiation: _RNCNCNvNtNtCshBwayKnNXDT_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0s_0B9_ |
135 | 1 | .await; |
136 | 1 | match outcome { |
137 | 0 | Ok(Some(hash)) => request.respond( |
138 | 0 | methods::Response::chain_getBlockHash(methods::HashHexString(hash)), |
139 | 0 | ), |
140 | 1 | Ok(None) => request.respond_null(), |
141 | 0 | Err(error) => { |
142 | 0 | config.log_callback.log(LogLevel::Warn, format!("json-rpc; request=chain_getBlockHash; height={:?}; database_error={}", height, error)); |
143 | 0 | request.fail(parse::ErrorResponse::InternalError) |
144 | | } |
145 | | } |
146 | | } |
147 | 3 | methods::MethodCall::chain_getHeader { hash } => { |
148 | 3 | let hash = match hash { |
149 | 2 | Some(h) => h.0, |
150 | 1 | None => match config |
151 | 1 | .database |
152 | 1 | .with_database(|db| db.best_block_hash()) _RNCNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0s0_0B9_ Line | Count | Source | 152 | 1 | .with_database(|db| db.best_block_hash()) |
Unexecuted instantiation: _RNCNCNvNtNtCshBwayKnNXDT_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0s0_0B9_ |
153 | 1 | .await |
154 | | { |
155 | 1 | Ok(b) => b, |
156 | | Err(_) => { |
157 | 0 | request.fail(service::ErrorResponse::InternalError); |
158 | 0 | continue; |
159 | | } |
160 | | }, |
161 | | }; |
162 | | |
163 | 3 | let result = config |
164 | 3 | .database |
165 | 3 | .with_database(move |db| db.block_scale_encoded_header(&hash)) _RNCNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0s1_0B9_ Line | Count | Source | 165 | 3 | .with_database(move |db| db.block_scale_encoded_header(&hash)) |
Unexecuted instantiation: _RNCNCNvNtNtCshBwayKnNXDT_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0s1_0B9_ |
166 | 3 | .await; |
167 | | |
168 | 3 | match result { |
169 | 2 | Ok(Some(encoded_header)) => { |
170 | 2 | match methods::Header::from_scale_encoded_header( |
171 | 2 | &encoded_header, |
172 | 2 | config.consensus_service.block_number_bytes(), |
173 | 2 | ) { |
174 | 2 | Ok(header) => { |
175 | 2 | request.respond(methods::Response::chain_getHeader(header)) |
176 | | } |
177 | 0 | Err(_) => { |
178 | 0 | request.fail(service::ErrorResponse::InternalError); |
179 | 0 | } |
180 | | } |
181 | | } |
182 | 1 | Ok(None) => { |
183 | 1 | request.respond_null(); |
184 | 1 | } |
185 | 0 | Err(_) => { |
186 | 0 | request.fail(service::ErrorResponse::InternalError); |
187 | 0 | } |
188 | | } |
189 | | } |
190 | | methods::MethodCall::state_getKeysPaged { |
191 | 6 | prefix, |
192 | 6 | count, |
193 | 6 | start_key, |
194 | 6 | hash, |
195 | 6 | } => { |
196 | 6 | // As an undocumented thing, a count strictly superior to 1000 isn't |
197 | 6 | // accepted by Substrate. |
198 | 6 | // See <https://github.com/paritytech/polkadot-sdk/blob/61be78c621ab2fa390cd3bfc79c8307431d0ea90/substrate/client/rpc/src/state/mod.rs#L238>. |
199 | 6 | if count > 1000 { |
200 | 1 | request.fail(service::ErrorResponse::InvalidParams); |
201 | 1 | continue; |
202 | 5 | } |
203 | 5 | |
204 | 5 | // Turn the parameters into a format suitable for the database query. |
205 | 5 | let prefix_nibbles = prefix.map_or(Vec::new(), |p| { |
206 | 5 | trie::bytes_to_nibbles(p.0.iter().copied()) |
207 | 5 | .map(u8::from) |
208 | 5 | .collect() |
209 | 5 | }); _RNCNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0s2_0B9_ Line | Count | Source | 205 | 5 | let prefix_nibbles = prefix.map_or(Vec::new(), |p| { | 206 | 5 | trie::bytes_to_nibbles(p.0.iter().copied()) | 207 | 5 | .map(u8::from) | 208 | 5 | .collect() | 209 | 5 | }); |
Unexecuted instantiation: _RNCNCNvNtNtCshBwayKnNXDT_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0s2_0B9_ |
210 | 5 | let mut start_key_nibbles = start_key.map_or(Vec::new(), |p| { |
211 | 2 | trie::bytes_to_nibbles(p.0.iter().copied()) |
212 | 2 | .map(u8::from) |
213 | 2 | .collect() |
214 | 5 | }); _RNCNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0s3_0B9_ Line | Count | Source | 210 | 2 | let mut start_key_nibbles = start_key.map_or(Vec::new(), |p| { | 211 | 2 | trie::bytes_to_nibbles(p.0.iter().copied()) | 212 | 2 | .map(u8::from) | 213 | 2 | .collect() | 214 | 2 | }); |
Unexecuted instantiation: _RNCNCNvNtNtCshBwayKnNXDT_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0s3_0B9_ |
215 | 5 | |
216 | 5 | // There's a difference of semantics between `state_getKeysPaged` and |
217 | 5 | // the database query we perform below in the situation where `start_key` |
218 | 5 | // isn't within `prefix`: the database request will return nothing while |
219 | 5 | // the JSON-RPC request expects the first key within `prefix`. As such, |
220 | 5 | // we adjust the start key if necessary. |
221 | 5 | // TODO: add documentation and a test in the database code regarding this behavior |
222 | 5 | if start_key_nibbles < prefix_nibbles { |
223 | 1 | start_key_nibbles = prefix_nibbles.clone(); |
224 | 4 | } |
225 | | |
226 | | // Continue in the background. |
227 | 5 | let result = config |
228 | 5 | .database |
229 | 5 | .with_database( |
230 | 5 | move |db| -> Result<_, database_thread::StorageAccessError> { |
231 | 5 | let hash = match hash { |
232 | 1 | Some(h) => h.0, |
233 | 4 | None => db.best_block_hash()?0 , |
234 | | }; |
235 | | |
236 | 5 | let mut out = |
237 | 5 | Vec::with_capacity(usize::try_from(count).unwrap()); |
238 | 5 | |
239 | 5 | let mut key_iter = start_key_nibbles; |
240 | | |
241 | | // The query is performed by repeatedly asking for the next |
242 | | // key. |
243 | 54 | while out.len() < usize::try_from(count).unwrap() { |
244 | 51 | let next_key_nibbles50 = db.block_storage_next_key( |
245 | 51 | &hash, |
246 | 51 | iter::empty::<iter::Empty<_>>(), |
247 | 51 | key_iter.iter().copied(), |
248 | 51 | prefix_nibbles.iter().copied(), |
249 | 51 | false, |
250 | 51 | )?1 ; |
251 | | |
252 | 50 | let Some(next_key_nibbles49 ) = next_key_nibbles else { |
253 | 1 | break; |
254 | | }; |
255 | | |
256 | 49 | out.push(methods::HexString( |
257 | 49 | trie::nibbles_to_bytes_truncate( |
258 | 49 | next_key_nibbles |
259 | 49 | .iter() |
260 | 49 | .copied() |
261 | 4.64k | .map(|n| trie::Nibble::try_from(n).unwrap()), _RNCNCNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0s4_00Bb_ Line | Count | Source | 261 | 4.64k | .map(|n| trie::Nibble::try_from(n).unwrap()), |
Unexecuted instantiation: _RNCNCNCNvNtNtCshBwayKnNXDT_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0s4_00Bb_ |
262 | 49 | ) |
263 | 49 | .collect::<Vec<_>>(), |
264 | 49 | )); |
265 | 49 | |
266 | 49 | // Push an extra nibble as otherwise `block_storage_next_key` |
267 | 49 | // will return the same key again. |
268 | 49 | key_iter = next_key_nibbles; |
269 | 49 | key_iter.push(0); |
270 | | } |
271 | | |
272 | 4 | Ok(out) |
273 | 5 | }, _RNCNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0s4_0B9_ Line | Count | Source | 230 | 5 | move |db| -> Result<_, database_thread::StorageAccessError> { | 231 | 5 | let hash = match hash { | 232 | 1 | Some(h) => h.0, | 233 | 4 | None => db.best_block_hash()?0 , | 234 | | }; | 235 | | | 236 | 5 | let mut out = | 237 | 5 | Vec::with_capacity(usize::try_from(count).unwrap()); | 238 | 5 | | 239 | 5 | let mut key_iter = start_key_nibbles; | 240 | | | 241 | | // The query is performed by repeatedly asking for the next | 242 | | // key. | 243 | 54 | while out.len() < usize::try_from(count).unwrap() { | 244 | 51 | let next_key_nibbles50 = db.block_storage_next_key( | 245 | 51 | &hash, | 246 | 51 | iter::empty::<iter::Empty<_>>(), | 247 | 51 | key_iter.iter().copied(), | 248 | 51 | prefix_nibbles.iter().copied(), | 249 | 51 | false, | 250 | 51 | )?1 ; | 251 | | | 252 | 50 | let Some(next_key_nibbles49 ) = next_key_nibbles else { | 253 | 1 | break; | 254 | | }; | 255 | | | 256 | 49 | out.push(methods::HexString( | 257 | 49 | trie::nibbles_to_bytes_truncate( | 258 | 49 | next_key_nibbles | 259 | 49 | .iter() | 260 | 49 | .copied() | 261 | 49 | .map(|n| trie::Nibble::try_from(n).unwrap()), | 262 | 49 | ) | 263 | 49 | .collect::<Vec<_>>(), | 264 | 49 | )); | 265 | 49 | | 266 | 49 | // Push an extra nibble as otherwise `block_storage_next_key` | 267 | 49 | // will return the same key again. | 268 | 49 | key_iter = next_key_nibbles; | 269 | 49 | key_iter.push(0); | 270 | | } | 271 | | | 272 | 4 | Ok(out) | 273 | 5 | }, |
Unexecuted instantiation: _RNCNCNvNtNtCshBwayKnNXDT_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0s4_0B9_ |
274 | 5 | ) |
275 | 5 | .await; |
276 | | |
277 | | // Send back outcome. |
278 | 1 | match result { |
279 | 4 | Ok(out) => { |
280 | 4 | request.respond(methods::Response::state_getKeysPaged(out)); |
281 | 4 | } |
282 | | Err(database_thread::StorageAccessError::IncompleteStorage) |
283 | 1 | | Err(database_thread::StorageAccessError::UnknownBlock) => { |
284 | 1 | // Note that it is unclear how the function should behave in |
285 | 1 | // that situation. |
286 | 1 | request.fail(service::ErrorResponse::InvalidParams); |
287 | 1 | } |
288 | 0 | Err(database_thread::StorageAccessError::Corrupted(_)) => { |
289 | 0 | request.fail(service::ErrorResponse::InternalError); |
290 | 0 | } |
291 | | } |
292 | | } |
293 | 1 | methods::MethodCall::state_getMetadata { hash } => { |
294 | 1 | let hash = match hash { |
295 | 1 | Some(h) => h.0, |
296 | 0 | None => match config |
297 | 0 | .database |
298 | 0 | .with_database(|db| db.best_block_hash()) Unexecuted instantiation: _RNCNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0s5_0B9_ Unexecuted instantiation: _RNCNCNvNtNtCshBwayKnNXDT_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0s5_0B9_ |
299 | 0 | .await |
300 | | { |
301 | 0 | Ok(b) => b, |
302 | | Err(_) => { |
303 | 0 | request.fail(service::ErrorResponse::InternalError); |
304 | 0 | continue; |
305 | | } |
306 | | }, |
307 | | }; |
308 | | |
309 | 1 | let runtime = match config.runtime_caches_service.get(hash).await { |
310 | 1 | Ok(runtime) => (*runtime).clone(), |
311 | | Err(runtime_caches_service::GetError::UnknownBlock) |
312 | | | Err(runtime_caches_service::GetError::Pruned) => { |
313 | 0 | request.respond_null(); |
314 | 0 | continue; |
315 | | } // TODO: unclear if correct error |
316 | | Err(runtime_caches_service::GetError::InvalidRuntime(_)) |
317 | | | Err(runtime_caches_service::GetError::NoCode) |
318 | | | Err(runtime_caches_service::GetError::InvalidHeapPages) |
319 | | | Err(runtime_caches_service::GetError::CorruptedDatabase) => { |
320 | 0 | request.fail(service::ErrorResponse::InternalError); |
321 | 0 | continue; |
322 | | } |
323 | | }; |
324 | | |
325 | 1 | let mut call = |
326 | 1 | match executor::runtime_call::run(executor::runtime_call::Config { |
327 | 1 | virtual_machine: runtime, |
328 | 1 | function_to_call: "Metadata_metadata", |
329 | 1 | parameter: iter::empty::<&'static [u8]>(), |
330 | 1 | max_log_level: 0, |
331 | 1 | storage_proof_size_behavior: executor::runtime_call::StorageProofSizeBehavior::proof_recording_disabled(), |
332 | 1 | storage_main_trie_changes: Default::default(), |
333 | 1 | calculate_trie_changes: false, |
334 | 1 | }) { |
335 | 1 | Ok(c) => c, |
336 | | Err(_) => { |
337 | 0 | request.fail(service::ErrorResponse::InternalError); |
338 | 0 | continue; |
339 | | } |
340 | | }; |
341 | | |
342 | | loop { |
343 | 1 | match call { |
344 | 1 | executor::runtime_call::RuntimeCall::Finished(Ok(success)) => { |
345 | 1 | match methods::remove_metadata_length_prefix(success.virtual_machine.value().as_ref()) { |
346 | 1 | Ok(m) => request.respond(methods::Response::state_getMetadata(methods::HexString(m.to_vec()))), |
347 | 0 | Err(_) => { |
348 | 0 | request.fail(service::ErrorResponse::InternalError); |
349 | 0 | } |
350 | | } |
351 | 1 | break; |
352 | | } |
353 | | executor::runtime_call::RuntimeCall::Finished(Err(_)) => { |
354 | 0 | request.fail(service::ErrorResponse::InternalError); |
355 | 0 | break; |
356 | | } |
357 | 0 | executor::runtime_call::RuntimeCall::StorageGet(req) => { |
358 | 0 | let parent_paths = req.child_trie().map(|child_trie| { |
359 | 0 | trie::bytes_to_nibbles( |
360 | 0 | b":child_storage:default:".iter().copied(), |
361 | 0 | ) |
362 | 0 | .chain(trie::bytes_to_nibbles( |
363 | 0 | child_trie.as_ref().iter().copied(), |
364 | 0 | )) |
365 | 0 | .map(u8::from) |
366 | 0 | .collect::<Vec<_>>() |
367 | 0 | }); Unexecuted instantiation: _RNCNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0s6_0B9_ Unexecuted instantiation: _RNCNCNvNtNtCshBwayKnNXDT_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0s6_0B9_ |
368 | 0 | let key = |
369 | 0 | trie::bytes_to_nibbles(req.key().as_ref().iter().copied()) |
370 | 0 | .map(u8::from) |
371 | 0 | .collect::<Vec<_>>(); |
372 | 0 | let value = config |
373 | 0 | .database |
374 | 0 | .with_database(move |db| { |
375 | 0 | db.block_storage_get( |
376 | 0 | &hash, |
377 | 0 | parent_paths.into_iter().map(|p| p.into_iter()), Unexecuted instantiation: _RNCNCNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0s7_00Bb_ Unexecuted instantiation: _RNCNCNCNvNtNtCshBwayKnNXDT_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0s7_00Bb_ |
378 | 0 | key.iter().copied(), |
379 | 0 | ) |
380 | 0 | }) Unexecuted instantiation: _RNCNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0s7_0B9_ Unexecuted instantiation: _RNCNCNvNtNtCshBwayKnNXDT_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0s7_0B9_ |
381 | 0 | .await; |
382 | 0 | let Ok(value) = value else { |
383 | 0 | request.fail(service::ErrorResponse::InternalError); |
384 | 0 | break; |
385 | | }; |
386 | 0 | let value = value.as_ref().map(|(val, vers)| { |
387 | 0 | ( |
388 | 0 | iter::once(&val[..]), |
389 | 0 | executor::runtime_call::TrieEntryVersion::try_from(*vers) |
390 | 0 | .expect("corrupted database"), |
391 | 0 | ) |
392 | 0 | }); Unexecuted instantiation: _RNCNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0s8_0B9_ Unexecuted instantiation: _RNCNCNvNtNtCshBwayKnNXDT_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0s8_0B9_ |
393 | 0 |
|
394 | 0 | call = req.inject_value(value); |
395 | | } |
396 | 0 | executor::runtime_call::RuntimeCall::ClosestDescendantMerkleValue(req) => { |
397 | 0 | let parent_paths = req.child_trie().map(|child_trie| { |
398 | 0 | trie::bytes_to_nibbles( |
399 | 0 | b":child_storage:default:".iter().copied(), |
400 | 0 | ) |
401 | 0 | .chain(trie::bytes_to_nibbles( |
402 | 0 | child_trie.as_ref().iter().copied(), |
403 | 0 | )) |
404 | 0 | .map(u8::from) |
405 | 0 | .collect::<Vec<_>>() |
406 | 0 | }); Unexecuted instantiation: _RNCNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0s9_0B9_ Unexecuted instantiation: _RNCNCNvNtNtCshBwayKnNXDT_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0s9_0B9_ |
407 | 0 | let key_nibbles = req.key().map(u8::from).collect::<Vec<_>>(); |
408 | | |
409 | 0 | let merkle_value = config |
410 | 0 | .database |
411 | 0 | .with_database(move |db| { |
412 | 0 | db.block_storage_closest_descendant_merkle_value( |
413 | 0 | &hash, |
414 | 0 | parent_paths.into_iter().map(|p| p.into_iter()), Unexecuted instantiation: _RNCNCNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0sa_00Bb_ Unexecuted instantiation: _RNCNCNCNvNtNtCshBwayKnNXDT_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0sa_00Bb_ |
415 | 0 | key_nibbles.iter().copied(), |
416 | 0 | ) |
417 | 0 | }) Unexecuted instantiation: _RNCNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0sa_0B9_ Unexecuted instantiation: _RNCNCNvNtNtCshBwayKnNXDT_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0sa_0B9_ |
418 | 0 | .await; |
419 | | |
420 | 0 | let Ok(merkle_value) = merkle_value else { |
421 | 0 | request.fail(service::ErrorResponse::InternalError); |
422 | 0 | break; |
423 | | }; |
424 | | |
425 | 0 | call = req |
426 | 0 | .inject_merkle_value(merkle_value.as_ref().map(|v| &v[..])); Unexecuted instantiation: _RNCNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0sb_0B9_ Unexecuted instantiation: _RNCNCNvNtNtCshBwayKnNXDT_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0sb_0B9_ |
427 | | } |
428 | 0 | executor::runtime_call::RuntimeCall::NextKey(req) => { |
429 | 0 | let parent_paths = req.child_trie().map(|child_trie| { |
430 | 0 | trie::bytes_to_nibbles( |
431 | 0 | b":child_storage:default:".iter().copied(), |
432 | 0 | ) |
433 | 0 | .chain(trie::bytes_to_nibbles( |
434 | 0 | child_trie.as_ref().iter().copied(), |
435 | 0 | )) |
436 | 0 | .map(u8::from) |
437 | 0 | .collect::<Vec<_>>() |
438 | 0 | }); Unexecuted instantiation: _RNCNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0sc_0B9_ Unexecuted instantiation: _RNCNCNvNtNtCshBwayKnNXDT_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0sc_0B9_ |
439 | 0 | let key_nibbles = req |
440 | 0 | .key() |
441 | 0 | .map(u8::from) |
442 | 0 | .chain(if req.or_equal() { None } else { Some(0u8) }) |
443 | 0 | .collect::<Vec<_>>(); |
444 | 0 | let prefix_nibbles = |
445 | 0 | req.prefix().map(u8::from).collect::<Vec<_>>(); |
446 | 0 |
|
447 | 0 | let branch_nodes = req.branch_nodes(); |
448 | 0 | let next_key = config |
449 | 0 | .database |
450 | 0 | .with_database(move |db| { |
451 | 0 | db.block_storage_next_key( |
452 | 0 | &hash, |
453 | 0 | parent_paths.into_iter().map(|p| p.into_iter()), Unexecuted instantiation: _RNCNCNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0sd_00Bb_ Unexecuted instantiation: _RNCNCNCNvNtNtCshBwayKnNXDT_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0sd_00Bb_ |
454 | 0 | key_nibbles.iter().copied(), |
455 | 0 | prefix_nibbles.iter().copied(), |
456 | 0 | branch_nodes, |
457 | 0 | ) |
458 | 0 | }) Unexecuted instantiation: _RNCNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0sd_0B9_ Unexecuted instantiation: _RNCNCNvNtNtCshBwayKnNXDT_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0sd_0B9_ |
459 | 0 | .await; |
460 | | |
461 | 0 | let Ok(next_key) = next_key else { |
462 | 0 | request.fail(service::ErrorResponse::InternalError); |
463 | 0 | break; |
464 | | }; |
465 | | |
466 | 0 | call = req.inject_key(next_key.map(|k| { |
467 | 0 | k.into_iter().map(|b| trie::Nibble::try_from(b).unwrap()) Unexecuted instantiation: _RNCNCNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0se_00Bb_ Unexecuted instantiation: _RNCNCNCNvNtNtCshBwayKnNXDT_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0se_00Bb_ |
468 | 0 | })); Unexecuted instantiation: _RNCNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0se_0B9_ Unexecuted instantiation: _RNCNCNvNtNtCshBwayKnNXDT_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0se_0B9_ |
469 | | } |
470 | 0 | executor::runtime_call::RuntimeCall::OffchainStorageSet(req) => { |
471 | 0 | call = req.resume(); |
472 | 0 | } |
473 | 0 | executor::runtime_call::RuntimeCall::SignatureVerification(req) => { |
474 | 0 | call = req.verify_and_resume(); |
475 | 0 | } |
476 | | executor::runtime_call::RuntimeCall::Offchain(_) => { |
477 | 0 | request.fail(service::ErrorResponse::InternalError); |
478 | 0 | break; |
479 | | } |
480 | 0 | executor::runtime_call::RuntimeCall::LogEmit(req) => { |
481 | 0 | // Logs are ignored. |
482 | 0 | call = req.resume(); |
483 | 0 | } |
484 | | } |
485 | | } |
486 | | } |
487 | 1 | methods::MethodCall::state_getRuntimeVersion { at } => { |
488 | 1 | let at = match at { |
489 | 1 | Some(h) => h.0, |
490 | 0 | None => match config |
491 | 0 | .database |
492 | 0 | .with_database(|db| db.best_block_hash()) Unexecuted instantiation: _RNCNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0sf_0B9_ Unexecuted instantiation: _RNCNCNvNtNtCshBwayKnNXDT_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0sf_0B9_ |
493 | 0 | .await |
494 | | { |
495 | 0 | Ok(b) => b, |
496 | | Err(_) => { |
497 | 0 | request.fail(service::ErrorResponse::InternalError); |
498 | 0 | continue; |
499 | | } |
500 | | }, |
501 | | }; |
502 | | |
503 | 1 | match config.runtime_caches_service.get(at).await { |
504 | 1 | Ok(runtime) => { |
505 | 1 | request.respond(methods::Response::state_getRuntimeVersion( |
506 | 1 | convert_runtime_version(runtime.runtime_version()), |
507 | 1 | )); |
508 | 1 | } |
509 | | Err(runtime_caches_service::GetError::UnknownBlock) |
510 | | | Err(runtime_caches_service::GetError::Pruned) => { |
511 | 0 | request.respond_null() |
512 | | } // TODO: unclear if correct error |
513 | | Err(runtime_caches_service::GetError::InvalidRuntime(_)) |
514 | | | Err(runtime_caches_service::GetError::NoCode) |
515 | | | Err(runtime_caches_service::GetError::InvalidHeapPages) |
516 | | | Err(runtime_caches_service::GetError::CorruptedDatabase) => { |
517 | 0 | request.fail(service::ErrorResponse::InternalError) |
518 | | } |
519 | | } |
520 | | } |
521 | 0 | methods::MethodCall::state_queryStorageAt { keys, at } => { |
522 | 0 | // TODO: add a limit to the number of keys? |
523 | 0 |
|
524 | 0 | // Convert the list of keys into a format suitable for the database. |
525 | 0 | let keys_nibbles = keys |
526 | 0 | .iter() |
527 | 0 | .map(|key| { |
528 | 0 | trie::bytes_to_nibbles(key.0.iter().copied()) |
529 | 0 | .map(u8::from) |
530 | 0 | .collect::<Vec<_>>() |
531 | 0 | }) Unexecuted instantiation: _RNCNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0sg_0B9_ Unexecuted instantiation: _RNCNCNvNtNtCshBwayKnNXDT_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0sg_0B9_ |
532 | 0 | .collect::<Vec<_>>(); |
533 | | |
534 | | // The bulk of the request is performed in the database thread. |
535 | 0 | let result = config |
536 | 0 | .database |
537 | 0 | .with_database(move |db| { |
538 | 0 | let at = match at { |
539 | 0 | Some(h) => h.0, |
540 | 0 | None => db.best_block_hash()?, |
541 | | }; |
542 | | |
543 | 0 | let parent = db |
544 | 0 | .block_parent(&at)? |
545 | 0 | .ok_or(database_thread::StorageAccessError::UnknownBlock)?; |
546 | | |
547 | 0 | let mut out = methods::StorageChangeSet { |
548 | 0 | block: methods::HashHexString(at), |
549 | 0 | changes: Vec::with_capacity(keys_nibbles.len()), |
550 | 0 | }; |
551 | | |
552 | 0 | for (key_nibbles, key) in |
553 | 0 | keys_nibbles.into_iter().zip(keys.into_iter()) |
554 | | { |
555 | 0 | let before = match db.block_storage_get( |
556 | 0 | &parent, |
557 | 0 | iter::empty::<iter::Empty<_>>(), |
558 | 0 | key_nibbles.iter().copied(), |
559 | 0 | ) { |
560 | 0 | Ok(v) => v, |
561 | | Err(database_thread::StorageAccessError::UnknownBlock) |
562 | 0 | if parent == [0; 32] => |
563 | 0 | { |
564 | 0 | // In case where `at` is the genesis block, we |
565 | 0 | // assume that its "parent" (which doesn't exist) |
566 | 0 | // has an empty storage. |
567 | 0 | None |
568 | | } |
569 | 0 | Err(err) => return Err(err), |
570 | | }; |
571 | | |
572 | 0 | let after = db.block_storage_get( |
573 | 0 | &at, |
574 | 0 | iter::empty::<iter::Empty<_>>(), |
575 | 0 | key_nibbles.iter().copied(), |
576 | 0 | )?; |
577 | | |
578 | 0 | if before != after { |
579 | 0 | out.changes |
580 | 0 | .push((key, after.map(|(v, _)| methods::HexString(v)))); Unexecuted instantiation: _RNCNCNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0sh_00Bb_ Unexecuted instantiation: _RNCNCNCNvNtNtCshBwayKnNXDT_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0sh_00Bb_ |
581 | 0 | } |
582 | | } |
583 | | |
584 | 0 | Ok(out) |
585 | 0 | }) Unexecuted instantiation: _RNCNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0sh_0B9_ Unexecuted instantiation: _RNCNCNvNtNtCshBwayKnNXDT_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0sh_0B9_ |
586 | 0 | .await; |
587 | | |
588 | | // Send back the response. |
589 | 0 | match result { |
590 | 0 | Ok(out) => { |
591 | 0 | request.respond(methods::Response::state_queryStorageAt(vec![out])); |
592 | 0 | } |
593 | | Err(database_thread::StorageAccessError::IncompleteStorage) |
594 | 0 | | Err(database_thread::StorageAccessError::UnknownBlock) => { |
595 | 0 | // Note that it is unclear how the function should behave in |
596 | 0 | // that situation. |
597 | 0 | request.fail(service::ErrorResponse::InvalidParams); |
598 | 0 | } |
599 | 0 | Err(database_thread::StorageAccessError::Corrupted(_)) => { |
600 | 0 | request.fail(service::ErrorResponse::InternalError); |
601 | 0 | } |
602 | | } |
603 | | } |
604 | 1 | methods::MethodCall::system_chain {} => { |
605 | 1 | request |
606 | 1 | .respond(methods::Response::system_chain((&config.chain_name).into())); |
607 | 1 | } |
608 | 1 | methods::MethodCall::system_chainType {} => { |
609 | 1 | request.respond(methods::Response::system_chainType( |
610 | 1 | (&config.chain_type).into(), |
611 | 1 | )); |
612 | 1 | } |
613 | 1 | methods::MethodCall::system_health {} => { |
614 | 1 | let (is_syncing, peers) = future::zip( |
615 | 1 | config.consensus_service.is_major_syncing_hint(), |
616 | 1 | config.network_service.0.num_peers(config.network_service.1), |
617 | 1 | ) |
618 | 1 | .await; |
619 | | |
620 | 1 | request.respond(methods::Response::system_health(methods::SystemHealth { |
621 | 1 | is_syncing, |
622 | 1 | peers: u64::try_from(peers).unwrap_or(u64::MAX), |
623 | 1 | should_have_peers: config.chain_is_live, |
624 | 1 | })); |
625 | | } |
626 | 1 | methods::MethodCall::system_localPeerId {} => { |
627 | 1 | let peer_id = config.network_service.0.local_peer_id().to_base58(); |
628 | 1 | request.respond(methods::Response::system_localPeerId(peer_id.into())); |
629 | 1 | } |
630 | 1 | methods::MethodCall::system_name {} => { |
631 | 1 | request.respond(methods::Response::system_version( |
632 | 1 | env!("CARGO_PKG_NAME").into(), |
633 | 1 | )); |
634 | 1 | } |
635 | 1 | methods::MethodCall::system_properties {} => { |
636 | 1 | request.respond(methods::Response::system_properties( |
637 | 1 | serde_json::from_str(&config.chain_properties_json).unwrap(), |
638 | 1 | )); |
639 | 1 | } |
640 | 1 | methods::MethodCall::system_version {} => { |
641 | 1 | request.respond(methods::Response::system_version( |
642 | 1 | env!("CARGO_PKG_VERSION").into(), |
643 | 1 | )); |
644 | 1 | } |
645 | | |
646 | 0 | _ => request.fail(service::ErrorResponse::ServerError( |
647 | 0 | -32000, |
648 | 0 | "Not implemented in smoldot yet", |
649 | 0 | )), |
650 | | }, |
651 | 0 | Some(Message::SubscriptionStart(request)) => match request.request() { |
652 | 0 | methods::MethodCall::chain_subscribeAllHeads {} => { |
653 | 0 | let block_number_bytes = config.consensus_service.block_number_bytes(); |
654 | 0 | let mut blocks_to_report = legacy_api_subscriptions::SubscribeAllHeads::new( |
655 | 0 | config.consensus_service.clone(), |
656 | 0 | ); |
657 | 0 |
|
658 | 0 | (config.tasks_executor)(Box::pin(async move { |
659 | 0 | let mut subscription = request.accept(); |
660 | 0 | let subscription_id = subscription.subscription_id().to_owned(); |
661 | | |
662 | | loop { |
663 | 0 | let scale_encoded_header = |
664 | 0 | blocks_to_report.next_scale_encoded_header().await; |
665 | | |
666 | 0 | let json_rpc_header = |
667 | 0 | match methods::Header::from_scale_encoded_header( |
668 | 0 | &scale_encoded_header, |
669 | 0 | block_number_bytes, |
670 | 0 | ) { |
671 | 0 | Ok(h) => h, |
672 | | Err(_) => { |
673 | | // TODO: consider reporting to logs |
674 | 0 | continue; |
675 | | } |
676 | | }; |
677 | | |
678 | 0 | subscription |
679 | 0 | .send_notification(methods::ServerToClient::chain_allHead { |
680 | 0 | subscription: (&subscription_id).into(), |
681 | 0 | result: json_rpc_header.clone(), |
682 | 0 | }) |
683 | 0 | .await Unexecuted instantiation: _RNCNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0si_0B9_ Unexecuted instantiation: _RNCNCNvNtNtCshBwayKnNXDT_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0si_0B9_ |
684 | | } |
685 | 0 | })); |
686 | 0 | } |
687 | | |
688 | 0 | methods::MethodCall::chain_subscribeFinalizedHeads {} => { |
689 | 0 | let block_number_bytes = config.consensus_service.block_number_bytes(); |
690 | 0 | let mut blocks_to_report = |
691 | 0 | legacy_api_subscriptions::SubscribeFinalizedHeads::new( |
692 | 0 | config.consensus_service.clone(), |
693 | 0 | ); |
694 | 0 |
|
695 | 0 | (config.tasks_executor)(Box::pin(async move { |
696 | 0 | let mut subscription = request.accept(); |
697 | 0 | let subscription_id = subscription.subscription_id().to_owned(); |
698 | | |
699 | | loop { |
700 | 0 | let scale_encoded_header = |
701 | 0 | blocks_to_report.next_scale_encoded_header().await; |
702 | | |
703 | 0 | let json_rpc_header = |
704 | 0 | match methods::Header::from_scale_encoded_header( |
705 | 0 | &scale_encoded_header, |
706 | 0 | block_number_bytes, |
707 | 0 | ) { |
708 | 0 | Ok(h) => h, |
709 | | Err(_) => { |
710 | | // TODO: consider reporting to logs |
711 | 0 | continue; |
712 | | } |
713 | | }; |
714 | | |
715 | 0 | subscription |
716 | 0 | .send_notification( |
717 | 0 | methods::ServerToClient::chain_finalizedHead { |
718 | 0 | subscription: (&subscription_id).into(), |
719 | 0 | result: json_rpc_header.clone(), |
720 | 0 | }, |
721 | 0 | ) |
722 | 0 | .await Unexecuted instantiation: _RNCNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0sj_0B9_ Unexecuted instantiation: _RNCNCNvNtNtCshBwayKnNXDT_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0sj_0B9_ |
723 | | } |
724 | 0 | })); |
725 | 0 | } |
726 | | |
727 | 0 | methods::MethodCall::chain_subscribeNewHeads {} => { |
728 | 0 | let block_number_bytes = config.consensus_service.block_number_bytes(); |
729 | 0 | let mut blocks_to_report = legacy_api_subscriptions::SubscribeNewHeads::new( |
730 | 0 | config.consensus_service.clone(), |
731 | 0 | ); |
732 | 0 |
|
733 | 0 | (config.tasks_executor)(Box::pin(async move { |
734 | 0 | let mut subscription = request.accept(); |
735 | 0 | let subscription_id = subscription.subscription_id().to_owned(); |
736 | | |
737 | | loop { |
738 | 0 | let scale_encoded_header = |
739 | 0 | blocks_to_report.next_scale_encoded_header().await; |
740 | | |
741 | 0 | let json_rpc_header = |
742 | 0 | match methods::Header::from_scale_encoded_header( |
743 | 0 | scale_encoded_header, |
744 | 0 | block_number_bytes, |
745 | 0 | ) { |
746 | 0 | Ok(h) => h, |
747 | | Err(_) => { |
748 | | // TODO: consider reporting to logs |
749 | 0 | continue; |
750 | | } |
751 | | }; |
752 | | |
753 | 0 | subscription |
754 | 0 | .send_notification(methods::ServerToClient::chain_newHead { |
755 | 0 | subscription: (&subscription_id).into(), |
756 | 0 | result: json_rpc_header.clone(), |
757 | 0 | }) |
758 | 0 | .await Unexecuted instantiation: _RNCNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0sk_0B9_ Unexecuted instantiation: _RNCNCNvNtNtCshBwayKnNXDT_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0sk_0B9_ |
759 | | } |
760 | 0 | })); |
761 | 0 | } |
762 | | |
763 | 0 | methods::MethodCall::state_subscribeRuntimeVersion {} => { |
764 | 0 | let mut runtime_versions_to_report = |
765 | 0 | legacy_api_subscriptions::SubscribeRuntimeVersion::new( |
766 | 0 | config.consensus_service.clone(), |
767 | 0 | ); |
768 | 0 |
|
769 | 0 | (config.tasks_executor)(Box::pin(async move { |
770 | 0 | let mut subscription = request.accept(); |
771 | 0 | let subscription_id = subscription.subscription_id().to_owned(); |
772 | | |
773 | | loop { |
774 | 0 | let runtime_version = |
775 | 0 | runtime_versions_to_report.next_runtime_version().await; |
776 | | |
777 | 0 | subscription |
778 | 0 | .send_notification( |
779 | 0 | methods::ServerToClient::state_runtimeVersion { |
780 | 0 | subscription: (&subscription_id).into(), |
781 | 0 | result: Some(convert_runtime_version(runtime_version)), |
782 | 0 | }, |
783 | 0 | ) |
784 | 0 | .await Unexecuted instantiation: _RNCNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0sl_0B9_ Unexecuted instantiation: _RNCNCNvNtNtCshBwayKnNXDT_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0sl_0B9_ |
785 | | } |
786 | 0 | })); |
787 | 0 | } |
788 | | |
789 | 0 | methods::MethodCall::state_subscribeStorage { list } => { |
790 | 0 | let mut notifications_to_report = |
791 | 0 | legacy_api_subscriptions::SubscribeStorage::new( |
792 | 0 | config.consensus_service.clone(), |
793 | 0 | config.database.clone(), |
794 | 0 | list.into_iter().map(|item| item.0).collect(), Unexecuted instantiation: _RNCNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0sm_0B9_ Unexecuted instantiation: _RNCNCNvNtNtCshBwayKnNXDT_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0sm_0B9_ |
795 | 0 | ); |
796 | 0 |
|
797 | 0 | (config.tasks_executor)(Box::pin(async move { |
798 | 0 | let mut subscription = request.accept(); |
799 | 0 | let subscription_id = subscription.subscription_id().to_owned(); |
800 | | |
801 | | loop { |
802 | 0 | let (block_hash, storage_changes) = |
803 | 0 | notifications_to_report.next_storage_update().await; |
804 | | |
805 | 0 | subscription |
806 | 0 | .send_notification(methods::ServerToClient::state_storage { |
807 | 0 | subscription: (&subscription_id).into(), |
808 | 0 | result: methods::StorageChangeSet { |
809 | 0 | block: methods::HashHexString(block_hash), |
810 | 0 | changes: storage_changes |
811 | 0 | .map(|(key, value)| { |
812 | 0 | ( |
813 | 0 | methods::HexString(key), |
814 | 0 | value.map(methods::HexString), |
815 | 0 | ) |
816 | 0 | }) Unexecuted instantiation: _RNCNCNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0sn_00Bb_ Unexecuted instantiation: _RNCNCNCNvNtNtCshBwayKnNXDT_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0sn_00Bb_ |
817 | 0 | .collect(), |
818 | 0 | }, |
819 | 0 | }) |
820 | 0 | .await Unexecuted instantiation: _RNCNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0sn_0B9_ Unexecuted instantiation: _RNCNCNvNtNtCshBwayKnNXDT_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0sn_0B9_ |
821 | | } |
822 | 0 | })); |
823 | 0 | } |
824 | | |
825 | 0 | _ => request.fail(service::ErrorResponse::ServerError( |
826 | 0 | -32000, |
827 | 0 | "Not implemented in smoldot yet", |
828 | 0 | )), |
829 | | }, |
830 | 672 | None => return, |
831 | 672 | } |
832 | 672 | } |
833 | 672 | })); _RNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0B7_ Line | Count | Source | 89 | 672 | tasks_executor(Box::pin(async move { | 90 | 672 | let mut receiver = pin::pin!(config.receiver); | 91 | | loop { | 92 | 1.28k | match receiver.next()695 .await { | 93 | 23 | Some(Message::Request(request)) => match request.request() { | 94 | 0 | methods::MethodCall::rpc_methods {} => { | 95 | 0 | request.respond(methods::Response::rpc_methods(methods::RpcMethods { | 96 | 0 | methods: methods::MethodCall::method_names() | 97 | 0 | .map(|n| n.into()) | 98 | 0 | .collect(), | 99 | 0 | })); | 100 | 0 | } | 101 | | | 102 | 1 | methods::MethodCall::chainSpec_v1_chainName {} => { | 103 | 1 | request.respond(methods::Response::chainSpec_v1_chainName( | 104 | 1 | (&config.chain_name).into(), | 105 | 1 | )); | 106 | 1 | } | 107 | 1 | methods::MethodCall::chainSpec_v1_genesisHash {} => { | 108 | 1 | request.respond(methods::Response::chainSpec_v1_genesisHash( | 109 | 1 | methods::HashHexString(config.genesis_block_hash), | 110 | 1 | )); | 111 | 1 | } | 112 | 1 | methods::MethodCall::chainSpec_v1_properties {} => { | 113 | 1 | request.respond(methods::Response::chainSpec_v1_properties( | 114 | 1 | serde_json::from_str(&config.chain_properties_json).unwrap(), | 115 | 1 | )); | 116 | 1 | } | 117 | | | 118 | | methods::MethodCall::chain_getBlockHash { height: Some(0) } => { | 119 | | // In the case where the database was populated through a warp sync, it | 120 | | // might not store block 0 in it. However, the hash of block 0 is | 121 | | // particularly important for JSON-RPC clients, and as such we make sure | 122 | | // to always respond successfully to block 0 requests, even if it isn't | 123 | | // in the database. | 124 | 1 | request.respond(methods::Response::chain_getBlockHash( | 125 | 1 | methods::HashHexString(config.genesis_block_hash), | 126 | 1 | )) | 127 | | } | 128 | 1 | methods::MethodCall::chain_getBlockHash { height } => { | 129 | 1 | let outcome = config | 130 | 1 | .database | 131 | 1 | .with_database(move |database| match height { | 132 | | Some(height) => database.best_block_hash_by_number(height), | 133 | | None => database.best_block_hash().map(Some), | 134 | 1 | }) | 135 | 1 | .await; | 136 | 1 | match outcome { | 137 | 0 | Ok(Some(hash)) => request.respond( | 138 | 0 | methods::Response::chain_getBlockHash(methods::HashHexString(hash)), | 139 | 0 | ), | 140 | 1 | Ok(None) => request.respond_null(), | 141 | 0 | Err(error) => { | 142 | 0 | config.log_callback.log(LogLevel::Warn, format!("json-rpc; request=chain_getBlockHash; height={:?}; database_error={}", height, error)); | 143 | 0 | request.fail(parse::ErrorResponse::InternalError) | 144 | | } | 145 | | } | 146 | | } | 147 | 3 | methods::MethodCall::chain_getHeader { hash } => { | 148 | 3 | let hash = match hash { | 149 | 2 | Some(h) => h.0, | 150 | 1 | None => match config | 151 | 1 | .database | 152 | 1 | .with_database(|db| db.best_block_hash()) | 153 | 1 | .await | 154 | | { | 155 | 1 | Ok(b) => b, | 156 | | Err(_) => { | 157 | 0 | request.fail(service::ErrorResponse::InternalError); | 158 | 0 | continue; | 159 | | } | 160 | | }, | 161 | | }; | 162 | | | 163 | 3 | let result = config | 164 | 3 | .database | 165 | 3 | .with_database(move |db| db.block_scale_encoded_header(&hash)) | 166 | 3 | .await; | 167 | | | 168 | 3 | match result { | 169 | 2 | Ok(Some(encoded_header)) => { | 170 | 2 | match methods::Header::from_scale_encoded_header( | 171 | 2 | &encoded_header, | 172 | 2 | config.consensus_service.block_number_bytes(), | 173 | 2 | ) { | 174 | 2 | Ok(header) => { | 175 | 2 | request.respond(methods::Response::chain_getHeader(header)) | 176 | | } | 177 | 0 | Err(_) => { | 178 | 0 | request.fail(service::ErrorResponse::InternalError); | 179 | 0 | } | 180 | | } | 181 | | } | 182 | 1 | Ok(None) => { | 183 | 1 | request.respond_null(); | 184 | 1 | } | 185 | 0 | Err(_) => { | 186 | 0 | request.fail(service::ErrorResponse::InternalError); | 187 | 0 | } | 188 | | } | 189 | | } | 190 | | methods::MethodCall::state_getKeysPaged { | 191 | 6 | prefix, | 192 | 6 | count, | 193 | 6 | start_key, | 194 | 6 | hash, | 195 | 6 | } => { | 196 | 6 | // As an undocumented thing, a count strictly superior to 1000 isn't | 197 | 6 | // accepted by Substrate. | 198 | 6 | // See <https://github.com/paritytech/polkadot-sdk/blob/61be78c621ab2fa390cd3bfc79c8307431d0ea90/substrate/client/rpc/src/state/mod.rs#L238>. | 199 | 6 | if count > 1000 { | 200 | 1 | request.fail(service::ErrorResponse::InvalidParams); | 201 | 1 | continue; | 202 | 5 | } | 203 | 5 | | 204 | 5 | // Turn the parameters into a format suitable for the database query. | 205 | 5 | let prefix_nibbles = prefix.map_or(Vec::new(), |p| { | 206 | | trie::bytes_to_nibbles(p.0.iter().copied()) | 207 | | .map(u8::from) | 208 | | .collect() | 209 | 5 | }); | 210 | 5 | let mut start_key_nibbles = start_key.map_or(Vec::new(), |p| { | 211 | | trie::bytes_to_nibbles(p.0.iter().copied()) | 212 | | .map(u8::from) | 213 | | .collect() | 214 | 5 | }); | 215 | 5 | | 216 | 5 | // There's a difference of semantics between `state_getKeysPaged` and | 217 | 5 | // the database query we perform below in the situation where `start_key` | 218 | 5 | // isn't within `prefix`: the database request will return nothing while | 219 | 5 | // the JSON-RPC request expects the first key within `prefix`. As such, | 220 | 5 | // we adjust the start key if necessary. | 221 | 5 | // TODO: add documentation and a test in the database code regarding this behavior | 222 | 5 | if start_key_nibbles < prefix_nibbles { | 223 | 1 | start_key_nibbles = prefix_nibbles.clone(); | 224 | 4 | } | 225 | | | 226 | | // Continue in the background. | 227 | 5 | let result = config | 228 | 5 | .database | 229 | 5 | .with_database( | 230 | 5 | move |db| -> Result<_, database_thread::StorageAccessError> { | 231 | | let hash = match hash { | 232 | | Some(h) => h.0, | 233 | | None => db.best_block_hash()?, | 234 | | }; | 235 | | | 236 | | let mut out = | 237 | | Vec::with_capacity(usize::try_from(count).unwrap()); | 238 | | | 239 | | let mut key_iter = start_key_nibbles; | 240 | | | 241 | | // The query is performed by repeatedly asking for the next | 242 | | // key. | 243 | | while out.len() < usize::try_from(count).unwrap() { | 244 | | let next_key_nibbles = db.block_storage_next_key( | 245 | | &hash, | 246 | | iter::empty::<iter::Empty<_>>(), | 247 | | key_iter.iter().copied(), | 248 | | prefix_nibbles.iter().copied(), | 249 | | false, | 250 | | )?; | 251 | | | 252 | | let Some(next_key_nibbles) = next_key_nibbles else { | 253 | | break; | 254 | | }; | 255 | | | 256 | | out.push(methods::HexString( | 257 | | trie::nibbles_to_bytes_truncate( | 258 | | next_key_nibbles | 259 | | .iter() | 260 | | .copied() | 261 | | .map(|n| trie::Nibble::try_from(n).unwrap()), | 262 | | ) | 263 | | .collect::<Vec<_>>(), | 264 | | )); | 265 | | | 266 | | // Push an extra nibble as otherwise `block_storage_next_key` | 267 | | // will return the same key again. | 268 | | key_iter = next_key_nibbles; | 269 | | key_iter.push(0); | 270 | | } | 271 | | | 272 | | Ok(out) | 273 | 5 | }, | 274 | 5 | ) | 275 | 5 | .await; | 276 | | | 277 | | // Send back outcome. | 278 | 1 | match result { | 279 | 4 | Ok(out) => { | 280 | 4 | request.respond(methods::Response::state_getKeysPaged(out)); | 281 | 4 | } | 282 | | Err(database_thread::StorageAccessError::IncompleteStorage) | 283 | 1 | | Err(database_thread::StorageAccessError::UnknownBlock) => { | 284 | 1 | // Note that it is unclear how the function should behave in | 285 | 1 | // that situation. | 286 | 1 | request.fail(service::ErrorResponse::InvalidParams); | 287 | 1 | } | 288 | 0 | Err(database_thread::StorageAccessError::Corrupted(_)) => { | 289 | 0 | request.fail(service::ErrorResponse::InternalError); | 290 | 0 | } | 291 | | } | 292 | | } | 293 | 1 | methods::MethodCall::state_getMetadata { hash } => { | 294 | 1 | let hash = match hash { | 295 | 1 | Some(h) => h.0, | 296 | 0 | None => match config | 297 | 0 | .database | 298 | 0 | .with_database(|db| db.best_block_hash()) | 299 | 0 | .await | 300 | | { | 301 | 0 | Ok(b) => b, | 302 | | Err(_) => { | 303 | 0 | request.fail(service::ErrorResponse::InternalError); | 304 | 0 | continue; | 305 | | } | 306 | | }, | 307 | | }; | 308 | | | 309 | 1 | let runtime = match config.runtime_caches_service.get(hash).await { | 310 | 1 | Ok(runtime) => (*runtime).clone(), | 311 | | Err(runtime_caches_service::GetError::UnknownBlock) | 312 | | | Err(runtime_caches_service::GetError::Pruned) => { | 313 | 0 | request.respond_null(); | 314 | 0 | continue; | 315 | | } // TODO: unclear if correct error | 316 | | Err(runtime_caches_service::GetError::InvalidRuntime(_)) | 317 | | | Err(runtime_caches_service::GetError::NoCode) | 318 | | | Err(runtime_caches_service::GetError::InvalidHeapPages) | 319 | | | Err(runtime_caches_service::GetError::CorruptedDatabase) => { | 320 | 0 | request.fail(service::ErrorResponse::InternalError); | 321 | 0 | continue; | 322 | | } | 323 | | }; | 324 | | | 325 | 1 | let mut call = | 326 | 1 | match executor::runtime_call::run(executor::runtime_call::Config { | 327 | 1 | virtual_machine: runtime, | 328 | 1 | function_to_call: "Metadata_metadata", | 329 | 1 | parameter: iter::empty::<&'static [u8]>(), | 330 | 1 | max_log_level: 0, | 331 | 1 | storage_proof_size_behavior: executor::runtime_call::StorageProofSizeBehavior::proof_recording_disabled(), | 332 | 1 | storage_main_trie_changes: Default::default(), | 333 | 1 | calculate_trie_changes: false, | 334 | 1 | }) { | 335 | 1 | Ok(c) => c, | 336 | | Err(_) => { | 337 | 0 | request.fail(service::ErrorResponse::InternalError); | 338 | 0 | continue; | 339 | | } | 340 | | }; | 341 | | | 342 | | loop { | 343 | 1 | match call { | 344 | 1 | executor::runtime_call::RuntimeCall::Finished(Ok(success)) => { | 345 | 1 | match methods::remove_metadata_length_prefix(success.virtual_machine.value().as_ref()) { | 346 | 1 | Ok(m) => request.respond(methods::Response::state_getMetadata(methods::HexString(m.to_vec()))), | 347 | 0 | Err(_) => { | 348 | 0 | request.fail(service::ErrorResponse::InternalError); | 349 | 0 | } | 350 | | } | 351 | 1 | break; | 352 | | } | 353 | | executor::runtime_call::RuntimeCall::Finished(Err(_)) => { | 354 | 0 | request.fail(service::ErrorResponse::InternalError); | 355 | 0 | break; | 356 | | } | 357 | 0 | executor::runtime_call::RuntimeCall::StorageGet(req) => { | 358 | 0 | let parent_paths = req.child_trie().map(|child_trie| { | 359 | | trie::bytes_to_nibbles( | 360 | | b":child_storage:default:".iter().copied(), | 361 | | ) | 362 | | .chain(trie::bytes_to_nibbles( | 363 | | child_trie.as_ref().iter().copied(), | 364 | | )) | 365 | | .map(u8::from) | 366 | | .collect::<Vec<_>>() | 367 | 0 | }); | 368 | 0 | let key = | 369 | 0 | trie::bytes_to_nibbles(req.key().as_ref().iter().copied()) | 370 | 0 | .map(u8::from) | 371 | 0 | .collect::<Vec<_>>(); | 372 | 0 | let value = config | 373 | 0 | .database | 374 | 0 | .with_database(move |db| { | 375 | | db.block_storage_get( | 376 | | &hash, | 377 | | parent_paths.into_iter().map(|p| p.into_iter()), | 378 | | key.iter().copied(), | 379 | | ) | 380 | 0 | }) | 381 | 0 | .await; | 382 | 0 | let Ok(value) = value else { | 383 | 0 | request.fail(service::ErrorResponse::InternalError); | 384 | 0 | break; | 385 | | }; | 386 | 0 | let value = value.as_ref().map(|(val, vers)| { | 387 | | ( | 388 | | iter::once(&val[..]), | 389 | | executor::runtime_call::TrieEntryVersion::try_from(*vers) | 390 | | .expect("corrupted database"), | 391 | | ) | 392 | 0 | }); | 393 | 0 |
| 394 | 0 | call = req.inject_value(value); | 395 | | } | 396 | 0 | executor::runtime_call::RuntimeCall::ClosestDescendantMerkleValue(req) => { | 397 | 0 | let parent_paths = req.child_trie().map(|child_trie| { | 398 | | trie::bytes_to_nibbles( | 399 | | b":child_storage:default:".iter().copied(), | 400 | | ) | 401 | | .chain(trie::bytes_to_nibbles( | 402 | | child_trie.as_ref().iter().copied(), | 403 | | )) | 404 | | .map(u8::from) | 405 | | .collect::<Vec<_>>() | 406 | 0 | }); | 407 | 0 | let key_nibbles = req.key().map(u8::from).collect::<Vec<_>>(); | 408 | | | 409 | 0 | let merkle_value = config | 410 | 0 | .database | 411 | 0 | .with_database(move |db| { | 412 | | db.block_storage_closest_descendant_merkle_value( | 413 | | &hash, | 414 | | parent_paths.into_iter().map(|p| p.into_iter()), | 415 | | key_nibbles.iter().copied(), | 416 | | ) | 417 | 0 | }) | 418 | 0 | .await; | 419 | | | 420 | 0 | let Ok(merkle_value) = merkle_value else { | 421 | 0 | request.fail(service::ErrorResponse::InternalError); | 422 | 0 | break; | 423 | | }; | 424 | | | 425 | 0 | call = req | 426 | 0 | .inject_merkle_value(merkle_value.as_ref().map(|v| &v[..])); | 427 | | } | 428 | 0 | executor::runtime_call::RuntimeCall::NextKey(req) => { | 429 | 0 | let parent_paths = req.child_trie().map(|child_trie| { | 430 | | trie::bytes_to_nibbles( | 431 | | b":child_storage:default:".iter().copied(), | 432 | | ) | 433 | | .chain(trie::bytes_to_nibbles( | 434 | | child_trie.as_ref().iter().copied(), | 435 | | )) | 436 | | .map(u8::from) | 437 | | .collect::<Vec<_>>() | 438 | 0 | }); | 439 | 0 | let key_nibbles = req | 440 | 0 | .key() | 441 | 0 | .map(u8::from) | 442 | 0 | .chain(if req.or_equal() { None } else { Some(0u8) }) | 443 | 0 | .collect::<Vec<_>>(); | 444 | 0 | let prefix_nibbles = | 445 | 0 | req.prefix().map(u8::from).collect::<Vec<_>>(); | 446 | 0 |
| 447 | 0 | let branch_nodes = req.branch_nodes(); | 448 | 0 | let next_key = config | 449 | 0 | .database | 450 | 0 | .with_database(move |db| { | 451 | | db.block_storage_next_key( | 452 | | &hash, | 453 | | parent_paths.into_iter().map(|p| p.into_iter()), | 454 | | key_nibbles.iter().copied(), | 455 | | prefix_nibbles.iter().copied(), | 456 | | branch_nodes, | 457 | | ) | 458 | 0 | }) | 459 | 0 | .await; | 460 | | | 461 | 0 | let Ok(next_key) = next_key else { | 462 | 0 | request.fail(service::ErrorResponse::InternalError); | 463 | 0 | break; | 464 | | }; | 465 | | | 466 | 0 | call = req.inject_key(next_key.map(|k| { | 467 | | k.into_iter().map(|b| trie::Nibble::try_from(b).unwrap()) | 468 | 0 | })); | 469 | | } | 470 | 0 | executor::runtime_call::RuntimeCall::OffchainStorageSet(req) => { | 471 | 0 | call = req.resume(); | 472 | 0 | } | 473 | 0 | executor::runtime_call::RuntimeCall::SignatureVerification(req) => { | 474 | 0 | call = req.verify_and_resume(); | 475 | 0 | } | 476 | | executor::runtime_call::RuntimeCall::Offchain(_) => { | 477 | 0 | request.fail(service::ErrorResponse::InternalError); | 478 | 0 | break; | 479 | | } | 480 | 0 | executor::runtime_call::RuntimeCall::LogEmit(req) => { | 481 | 0 | // Logs are ignored. | 482 | 0 | call = req.resume(); | 483 | 0 | } | 484 | | } | 485 | | } | 486 | | } | 487 | 1 | methods::MethodCall::state_getRuntimeVersion { at } => { | 488 | 1 | let at = match at { | 489 | 1 | Some(h) => h.0, | 490 | 0 | None => match config | 491 | 0 | .database | 492 | 0 | .with_database(|db| db.best_block_hash()) | 493 | 0 | .await | 494 | | { | 495 | 0 | Ok(b) => b, | 496 | | Err(_) => { | 497 | 0 | request.fail(service::ErrorResponse::InternalError); | 498 | 0 | continue; | 499 | | } | 500 | | }, | 501 | | }; | 502 | | | 503 | 1 | match config.runtime_caches_service.get(at).await { | 504 | 1 | Ok(runtime) => { | 505 | 1 | request.respond(methods::Response::state_getRuntimeVersion( | 506 | 1 | convert_runtime_version(runtime.runtime_version()), | 507 | 1 | )); | 508 | 1 | } | 509 | | Err(runtime_caches_service::GetError::UnknownBlock) | 510 | | | Err(runtime_caches_service::GetError::Pruned) => { | 511 | 0 | request.respond_null() | 512 | | } // TODO: unclear if correct error | 513 | | Err(runtime_caches_service::GetError::InvalidRuntime(_)) | 514 | | | Err(runtime_caches_service::GetError::NoCode) | 515 | | | Err(runtime_caches_service::GetError::InvalidHeapPages) | 516 | | | Err(runtime_caches_service::GetError::CorruptedDatabase) => { | 517 | 0 | request.fail(service::ErrorResponse::InternalError) | 518 | | } | 519 | | } | 520 | | } | 521 | 0 | methods::MethodCall::state_queryStorageAt { keys, at } => { | 522 | 0 | // TODO: add a limit to the number of keys? | 523 | 0 |
| 524 | 0 | // Convert the list of keys into a format suitable for the database. | 525 | 0 | let keys_nibbles = keys | 526 | 0 | .iter() | 527 | 0 | .map(|key| { | 528 | | trie::bytes_to_nibbles(key.0.iter().copied()) | 529 | | .map(u8::from) | 530 | | .collect::<Vec<_>>() | 531 | 0 | }) | 532 | 0 | .collect::<Vec<_>>(); | 533 | | | 534 | | // The bulk of the request is performed in the database thread. | 535 | 0 | let result = config | 536 | 0 | .database | 537 | 0 | .with_database(move |db| { | 538 | | let at = match at { | 539 | | Some(h) => h.0, | 540 | | None => db.best_block_hash()?, | 541 | | }; | 542 | | | 543 | | let parent = db | 544 | | .block_parent(&at)? | 545 | | .ok_or(database_thread::StorageAccessError::UnknownBlock)?; | 546 | | | 547 | | let mut out = methods::StorageChangeSet { | 548 | | block: methods::HashHexString(at), | 549 | | changes: Vec::with_capacity(keys_nibbles.len()), | 550 | | }; | 551 | | | 552 | | for (key_nibbles, key) in | 553 | | keys_nibbles.into_iter().zip(keys.into_iter()) | 554 | | { | 555 | | let before = match db.block_storage_get( | 556 | | &parent, | 557 | | iter::empty::<iter::Empty<_>>(), | 558 | | key_nibbles.iter().copied(), | 559 | | ) { | 560 | | Ok(v) => v, | 561 | | Err(database_thread::StorageAccessError::UnknownBlock) | 562 | | if parent == [0; 32] => | 563 | | { | 564 | | // In case where `at` is the genesis block, we | 565 | | // assume that its "parent" (which doesn't exist) | 566 | | // has an empty storage. | 567 | | None | 568 | | } | 569 | | Err(err) => return Err(err), | 570 | | }; | 571 | | | 572 | | let after = db.block_storage_get( | 573 | | &at, | 574 | | iter::empty::<iter::Empty<_>>(), | 575 | | key_nibbles.iter().copied(), | 576 | | )?; | 577 | | | 578 | | if before != after { | 579 | | out.changes | 580 | | .push((key, after.map(|(v, _)| methods::HexString(v)))); | 581 | | } | 582 | | } | 583 | | | 584 | | Ok(out) | 585 | 0 | }) | 586 | 0 | .await; | 587 | | | 588 | | // Send back the response. | 589 | 0 | match result { | 590 | 0 | Ok(out) => { | 591 | 0 | request.respond(methods::Response::state_queryStorageAt(vec![out])); | 592 | 0 | } | 593 | | Err(database_thread::StorageAccessError::IncompleteStorage) | 594 | 0 | | Err(database_thread::StorageAccessError::UnknownBlock) => { | 595 | 0 | // Note that it is unclear how the function should behave in | 596 | 0 | // that situation. | 597 | 0 | request.fail(service::ErrorResponse::InvalidParams); | 598 | 0 | } | 599 | 0 | Err(database_thread::StorageAccessError::Corrupted(_)) => { | 600 | 0 | request.fail(service::ErrorResponse::InternalError); | 601 | 0 | } | 602 | | } | 603 | | } | 604 | 1 | methods::MethodCall::system_chain {} => { | 605 | 1 | request | 606 | 1 | .respond(methods::Response::system_chain((&config.chain_name).into())); | 607 | 1 | } | 608 | 1 | methods::MethodCall::system_chainType {} => { | 609 | 1 | request.respond(methods::Response::system_chainType( | 610 | 1 | (&config.chain_type).into(), | 611 | 1 | )); | 612 | 1 | } | 613 | 1 | methods::MethodCall::system_health {} => { | 614 | 1 | let (is_syncing, peers) = future::zip( | 615 | 1 | config.consensus_service.is_major_syncing_hint(), | 616 | 1 | config.network_service.0.num_peers(config.network_service.1), | 617 | 1 | ) | 618 | 1 | .await; | 619 | | | 620 | 1 | request.respond(methods::Response::system_health(methods::SystemHealth { | 621 | 1 | is_syncing, | 622 | 1 | peers: u64::try_from(peers).unwrap_or(u64::MAX), | 623 | 1 | should_have_peers: config.chain_is_live, | 624 | 1 | })); | 625 | | } | 626 | 1 | methods::MethodCall::system_localPeerId {} => { | 627 | 1 | let peer_id = config.network_service.0.local_peer_id().to_base58(); | 628 | 1 | request.respond(methods::Response::system_localPeerId(peer_id.into())); | 629 | 1 | } | 630 | 1 | methods::MethodCall::system_name {} => { | 631 | 1 | request.respond(methods::Response::system_version( | 632 | 1 | env!("CARGO_PKG_NAME").into(), | 633 | 1 | )); | 634 | 1 | } | 635 | 1 | methods::MethodCall::system_properties {} => { | 636 | 1 | request.respond(methods::Response::system_properties( | 637 | 1 | serde_json::from_str(&config.chain_properties_json).unwrap(), | 638 | 1 | )); | 639 | 1 | } | 640 | 1 | methods::MethodCall::system_version {} => { | 641 | 1 | request.respond(methods::Response::system_version( | 642 | 1 | env!("CARGO_PKG_VERSION").into(), | 643 | 1 | )); | 644 | 1 | } | 645 | | | 646 | 0 | _ => request.fail(service::ErrorResponse::ServerError( | 647 | 0 | -32000, | 648 | 0 | "Not implemented in smoldot yet", | 649 | 0 | )), | 650 | | }, | 651 | 0 | Some(Message::SubscriptionStart(request)) => match request.request() { | 652 | 0 | methods::MethodCall::chain_subscribeAllHeads {} => { | 653 | 0 | let block_number_bytes = config.consensus_service.block_number_bytes(); | 654 | 0 | let mut blocks_to_report = legacy_api_subscriptions::SubscribeAllHeads::new( | 655 | 0 | config.consensus_service.clone(), | 656 | 0 | ); | 657 | 0 |
| 658 | 0 | (config.tasks_executor)(Box::pin(async move { | 659 | | let mut subscription = request.accept(); | 660 | | let subscription_id = subscription.subscription_id().to_owned(); | 661 | | | 662 | | loop { | 663 | | let scale_encoded_header = | 664 | | blocks_to_report.next_scale_encoded_header().await; | 665 | | | 666 | | let json_rpc_header = | 667 | | match methods::Header::from_scale_encoded_header( | 668 | | &scale_encoded_header, | 669 | | block_number_bytes, | 670 | | ) { | 671 | | Ok(h) => h, | 672 | | Err(_) => { | 673 | | // TODO: consider reporting to logs | 674 | | continue; | 675 | | } | 676 | | }; | 677 | | | 678 | | subscription | 679 | | .send_notification(methods::ServerToClient::chain_allHead { | 680 | | subscription: (&subscription_id).into(), | 681 | | result: json_rpc_header.clone(), | 682 | | }) | 683 | | .await | 684 | | } | 685 | 0 | })); | 686 | 0 | } | 687 | | | 688 | 0 | methods::MethodCall::chain_subscribeFinalizedHeads {} => { | 689 | 0 | let block_number_bytes = config.consensus_service.block_number_bytes(); | 690 | 0 | let mut blocks_to_report = | 691 | 0 | legacy_api_subscriptions::SubscribeFinalizedHeads::new( | 692 | 0 | config.consensus_service.clone(), | 693 | 0 | ); | 694 | 0 |
| 695 | 0 | (config.tasks_executor)(Box::pin(async move { | 696 | | let mut subscription = request.accept(); | 697 | | let subscription_id = subscription.subscription_id().to_owned(); | 698 | | | 699 | | loop { | 700 | | let scale_encoded_header = | 701 | | blocks_to_report.next_scale_encoded_header().await; | 702 | | | 703 | | let json_rpc_header = | 704 | | match methods::Header::from_scale_encoded_header( | 705 | | &scale_encoded_header, | 706 | | block_number_bytes, | 707 | | ) { | 708 | | Ok(h) => h, | 709 | | Err(_) => { | 710 | | // TODO: consider reporting to logs | 711 | | continue; | 712 | | } | 713 | | }; | 714 | | | 715 | | subscription | 716 | | .send_notification( | 717 | | methods::ServerToClient::chain_finalizedHead { | 718 | | subscription: (&subscription_id).into(), | 719 | | result: json_rpc_header.clone(), | 720 | | }, | 721 | | ) | 722 | | .await | 723 | | } | 724 | 0 | })); | 725 | 0 | } | 726 | | | 727 | 0 | methods::MethodCall::chain_subscribeNewHeads {} => { | 728 | 0 | let block_number_bytes = config.consensus_service.block_number_bytes(); | 729 | 0 | let mut blocks_to_report = legacy_api_subscriptions::SubscribeNewHeads::new( | 730 | 0 | config.consensus_service.clone(), | 731 | 0 | ); | 732 | 0 |
| 733 | 0 | (config.tasks_executor)(Box::pin(async move { | 734 | | let mut subscription = request.accept(); | 735 | | let subscription_id = subscription.subscription_id().to_owned(); | 736 | | | 737 | | loop { | 738 | | let scale_encoded_header = | 739 | | blocks_to_report.next_scale_encoded_header().await; | 740 | | | 741 | | let json_rpc_header = | 742 | | match methods::Header::from_scale_encoded_header( | 743 | | scale_encoded_header, | 744 | | block_number_bytes, | 745 | | ) { | 746 | | Ok(h) => h, | 747 | | Err(_) => { | 748 | | // TODO: consider reporting to logs | 749 | | continue; | 750 | | } | 751 | | }; | 752 | | | 753 | | subscription | 754 | | .send_notification(methods::ServerToClient::chain_newHead { | 755 | | subscription: (&subscription_id).into(), | 756 | | result: json_rpc_header.clone(), | 757 | | }) | 758 | | .await | 759 | | } | 760 | 0 | })); | 761 | 0 | } | 762 | | | 763 | 0 | methods::MethodCall::state_subscribeRuntimeVersion {} => { | 764 | 0 | let mut runtime_versions_to_report = | 765 | 0 | legacy_api_subscriptions::SubscribeRuntimeVersion::new( | 766 | 0 | config.consensus_service.clone(), | 767 | 0 | ); | 768 | 0 |
| 769 | 0 | (config.tasks_executor)(Box::pin(async move { | 770 | | let mut subscription = request.accept(); | 771 | | let subscription_id = subscription.subscription_id().to_owned(); | 772 | | | 773 | | loop { | 774 | | let runtime_version = | 775 | | runtime_versions_to_report.next_runtime_version().await; | 776 | | | 777 | | subscription | 778 | | .send_notification( | 779 | | methods::ServerToClient::state_runtimeVersion { | 780 | | subscription: (&subscription_id).into(), | 781 | | result: Some(convert_runtime_version(runtime_version)), | 782 | | }, | 783 | | ) | 784 | | .await | 785 | | } | 786 | 0 | })); | 787 | 0 | } | 788 | | | 789 | 0 | methods::MethodCall::state_subscribeStorage { list } => { | 790 | 0 | let mut notifications_to_report = | 791 | 0 | legacy_api_subscriptions::SubscribeStorage::new( | 792 | 0 | config.consensus_service.clone(), | 793 | 0 | config.database.clone(), | 794 | 0 | list.into_iter().map(|item| item.0).collect(), | 795 | 0 | ); | 796 | 0 |
| 797 | 0 | (config.tasks_executor)(Box::pin(async move { | 798 | | let mut subscription = request.accept(); | 799 | | let subscription_id = subscription.subscription_id().to_owned(); | 800 | | | 801 | | loop { | 802 | | let (block_hash, storage_changes) = | 803 | | notifications_to_report.next_storage_update().await; | 804 | | | 805 | | subscription | 806 | | .send_notification(methods::ServerToClient::state_storage { | 807 | | subscription: (&subscription_id).into(), | 808 | | result: methods::StorageChangeSet { | 809 | | block: methods::HashHexString(block_hash), | 810 | | changes: storage_changes | 811 | | .map(|(key, value)| { | 812 | | ( | 813 | | methods::HexString(key), | 814 | | value.map(methods::HexString), | 815 | | ) | 816 | | }) | 817 | | .collect(), | 818 | | }, | 819 | | }) | 820 | | .await | 821 | | } | 822 | 0 | })); | 823 | 0 | } | 824 | | | 825 | 0 | _ => request.fail(service::ErrorResponse::ServerError( | 826 | 0 | -32000, | 827 | 0 | "Not implemented in smoldot yet", | 828 | 0 | )), | 829 | | }, | 830 | 672 | None => return, | 831 | 672 | } | 832 | 672 | } | 833 | 672 | })); |
Unexecuted instantiation: _RNCNvNtNtCshBwayKnNXDT_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler0B7_ |
834 | 672 | } _RNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler Line | Count | Source | 87 | 672 | pub fn spawn_requests_handler(config: Config) { | 88 | 672 | let tasks_executor = config.tasks_executor.clone(); | 89 | 672 | tasks_executor(Box::pin(async move { | 90 | | let mut receiver = pin::pin!(config.receiver); | 91 | | loop { | 92 | | match receiver.next().await { | 93 | | Some(Message::Request(request)) => match request.request() { | 94 | | methods::MethodCall::rpc_methods {} => { | 95 | | request.respond(methods::Response::rpc_methods(methods::RpcMethods { | 96 | | methods: methods::MethodCall::method_names() | 97 | | .map(|n| n.into()) | 98 | | .collect(), | 99 | | })); | 100 | | } | 101 | | | 102 | | methods::MethodCall::chainSpec_v1_chainName {} => { | 103 | | request.respond(methods::Response::chainSpec_v1_chainName( | 104 | | (&config.chain_name).into(), | 105 | | )); | 106 | | } | 107 | | methods::MethodCall::chainSpec_v1_genesisHash {} => { | 108 | | request.respond(methods::Response::chainSpec_v1_genesisHash( | 109 | | methods::HashHexString(config.genesis_block_hash), | 110 | | )); | 111 | | } | 112 | | methods::MethodCall::chainSpec_v1_properties {} => { | 113 | | request.respond(methods::Response::chainSpec_v1_properties( | 114 | | serde_json::from_str(&config.chain_properties_json).unwrap(), | 115 | | )); | 116 | | } | 117 | | | 118 | | methods::MethodCall::chain_getBlockHash { height: Some(0) } => { | 119 | | // In the case where the database was populated through a warp sync, it | 120 | | // might not store block 0 in it. However, the hash of block 0 is | 121 | | // particularly important for JSON-RPC clients, and as such we make sure | 122 | | // to always respond successfully to block 0 requests, even if it isn't | 123 | | // in the database. | 124 | | request.respond(methods::Response::chain_getBlockHash( | 125 | | methods::HashHexString(config.genesis_block_hash), | 126 | | )) | 127 | | } | 128 | | methods::MethodCall::chain_getBlockHash { height } => { | 129 | | let outcome = config | 130 | | .database | 131 | | .with_database(move |database| match height { | 132 | | Some(height) => database.best_block_hash_by_number(height), | 133 | | None => database.best_block_hash().map(Some), | 134 | | }) | 135 | | .await; | 136 | | match outcome { | 137 | | Ok(Some(hash)) => request.respond( | 138 | | methods::Response::chain_getBlockHash(methods::HashHexString(hash)), | 139 | | ), | 140 | | Ok(None) => request.respond_null(), | 141 | | Err(error) => { | 142 | | config.log_callback.log(LogLevel::Warn, format!("json-rpc; request=chain_getBlockHash; height={:?}; database_error={}", height, error)); | 143 | | request.fail(parse::ErrorResponse::InternalError) | 144 | | } | 145 | | } | 146 | | } | 147 | | methods::MethodCall::chain_getHeader { hash } => { | 148 | | let hash = match hash { | 149 | | Some(h) => h.0, | 150 | | None => match config | 151 | | .database | 152 | | .with_database(|db| db.best_block_hash()) | 153 | | .await | 154 | | { | 155 | | Ok(b) => b, | 156 | | Err(_) => { | 157 | | request.fail(service::ErrorResponse::InternalError); | 158 | | continue; | 159 | | } | 160 | | }, | 161 | | }; | 162 | | | 163 | | let result = config | 164 | | .database | 165 | | .with_database(move |db| db.block_scale_encoded_header(&hash)) | 166 | | .await; | 167 | | | 168 | | match result { | 169 | | Ok(Some(encoded_header)) => { | 170 | | match methods::Header::from_scale_encoded_header( | 171 | | &encoded_header, | 172 | | config.consensus_service.block_number_bytes(), | 173 | | ) { | 174 | | Ok(header) => { | 175 | | request.respond(methods::Response::chain_getHeader(header)) | 176 | | } | 177 | | Err(_) => { | 178 | | request.fail(service::ErrorResponse::InternalError); | 179 | | } | 180 | | } | 181 | | } | 182 | | Ok(None) => { | 183 | | request.respond_null(); | 184 | | } | 185 | | Err(_) => { | 186 | | request.fail(service::ErrorResponse::InternalError); | 187 | | } | 188 | | } | 189 | | } | 190 | | methods::MethodCall::state_getKeysPaged { | 191 | | prefix, | 192 | | count, | 193 | | start_key, | 194 | | hash, | 195 | | } => { | 196 | | // As an undocumented thing, a count strictly superior to 1000 isn't | 197 | | // accepted by Substrate. | 198 | | // See <https://github.com/paritytech/polkadot-sdk/blob/61be78c621ab2fa390cd3bfc79c8307431d0ea90/substrate/client/rpc/src/state/mod.rs#L238>. | 199 | | if count > 1000 { | 200 | | request.fail(service::ErrorResponse::InvalidParams); | 201 | | continue; | 202 | | } | 203 | | | 204 | | // Turn the parameters into a format suitable for the database query. | 205 | | let prefix_nibbles = prefix.map_or(Vec::new(), |p| { | 206 | | trie::bytes_to_nibbles(p.0.iter().copied()) | 207 | | .map(u8::from) | 208 | | .collect() | 209 | | }); | 210 | | let mut start_key_nibbles = start_key.map_or(Vec::new(), |p| { | 211 | | trie::bytes_to_nibbles(p.0.iter().copied()) | 212 | | .map(u8::from) | 213 | | .collect() | 214 | | }); | 215 | | | 216 | | // There's a difference of semantics between `state_getKeysPaged` and | 217 | | // the database query we perform below in the situation where `start_key` | 218 | | // isn't within `prefix`: the database request will return nothing while | 219 | | // the JSON-RPC request expects the first key within `prefix`. As such, | 220 | | // we adjust the start key if necessary. | 221 | | // TODO: add documentation and a test in the database code regarding this behavior | 222 | | if start_key_nibbles < prefix_nibbles { | 223 | | start_key_nibbles = prefix_nibbles.clone(); | 224 | | } | 225 | | | 226 | | // Continue in the background. | 227 | | let result = config | 228 | | .database | 229 | | .with_database( | 230 | | move |db| -> Result<_, database_thread::StorageAccessError> { | 231 | | let hash = match hash { | 232 | | Some(h) => h.0, | 233 | | None => db.best_block_hash()?, | 234 | | }; | 235 | | | 236 | | let mut out = | 237 | | Vec::with_capacity(usize::try_from(count).unwrap()); | 238 | | | 239 | | let mut key_iter = start_key_nibbles; | 240 | | | 241 | | // The query is performed by repeatedly asking for the next | 242 | | // key. | 243 | | while out.len() < usize::try_from(count).unwrap() { | 244 | | let next_key_nibbles = db.block_storage_next_key( | 245 | | &hash, | 246 | | iter::empty::<iter::Empty<_>>(), | 247 | | key_iter.iter().copied(), | 248 | | prefix_nibbles.iter().copied(), | 249 | | false, | 250 | | )?; | 251 | | | 252 | | let Some(next_key_nibbles) = next_key_nibbles else { | 253 | | break; | 254 | | }; | 255 | | | 256 | | out.push(methods::HexString( | 257 | | trie::nibbles_to_bytes_truncate( | 258 | | next_key_nibbles | 259 | | .iter() | 260 | | .copied() | 261 | | .map(|n| trie::Nibble::try_from(n).unwrap()), | 262 | | ) | 263 | | .collect::<Vec<_>>(), | 264 | | )); | 265 | | | 266 | | // Push an extra nibble as otherwise `block_storage_next_key` | 267 | | // will return the same key again. | 268 | | key_iter = next_key_nibbles; | 269 | | key_iter.push(0); | 270 | | } | 271 | | | 272 | | Ok(out) | 273 | | }, | 274 | | ) | 275 | | .await; | 276 | | | 277 | | // Send back outcome. | 278 | | match result { | 279 | | Ok(out) => { | 280 | | request.respond(methods::Response::state_getKeysPaged(out)); | 281 | | } | 282 | | Err(database_thread::StorageAccessError::IncompleteStorage) | 283 | | | Err(database_thread::StorageAccessError::UnknownBlock) => { | 284 | | // Note that it is unclear how the function should behave in | 285 | | // that situation. | 286 | | request.fail(service::ErrorResponse::InvalidParams); | 287 | | } | 288 | | Err(database_thread::StorageAccessError::Corrupted(_)) => { | 289 | | request.fail(service::ErrorResponse::InternalError); | 290 | | } | 291 | | } | 292 | | } | 293 | | methods::MethodCall::state_getMetadata { hash } => { | 294 | | let hash = match hash { | 295 | | Some(h) => h.0, | 296 | | None => match config | 297 | | .database | 298 | | .with_database(|db| db.best_block_hash()) | 299 | | .await | 300 | | { | 301 | | Ok(b) => b, | 302 | | Err(_) => { | 303 | | request.fail(service::ErrorResponse::InternalError); | 304 | | continue; | 305 | | } | 306 | | }, | 307 | | }; | 308 | | | 309 | | let runtime = match config.runtime_caches_service.get(hash).await { | 310 | | Ok(runtime) => (*runtime).clone(), | 311 | | Err(runtime_caches_service::GetError::UnknownBlock) | 312 | | | Err(runtime_caches_service::GetError::Pruned) => { | 313 | | request.respond_null(); | 314 | | continue; | 315 | | } // TODO: unclear if correct error | 316 | | Err(runtime_caches_service::GetError::InvalidRuntime(_)) | 317 | | | Err(runtime_caches_service::GetError::NoCode) | 318 | | | Err(runtime_caches_service::GetError::InvalidHeapPages) | 319 | | | Err(runtime_caches_service::GetError::CorruptedDatabase) => { | 320 | | request.fail(service::ErrorResponse::InternalError); | 321 | | continue; | 322 | | } | 323 | | }; | 324 | | | 325 | | let mut call = | 326 | | match executor::runtime_call::run(executor::runtime_call::Config { | 327 | | virtual_machine: runtime, | 328 | | function_to_call: "Metadata_metadata", | 329 | | parameter: iter::empty::<&'static [u8]>(), | 330 | | max_log_level: 0, | 331 | | storage_proof_size_behavior: executor::runtime_call::StorageProofSizeBehavior::proof_recording_disabled(), | 332 | | storage_main_trie_changes: Default::default(), | 333 | | calculate_trie_changes: false, | 334 | | }) { | 335 | | Ok(c) => c, | 336 | | Err(_) => { | 337 | | request.fail(service::ErrorResponse::InternalError); | 338 | | continue; | 339 | | } | 340 | | }; | 341 | | | 342 | | loop { | 343 | | match call { | 344 | | executor::runtime_call::RuntimeCall::Finished(Ok(success)) => { | 345 | | match methods::remove_metadata_length_prefix(success.virtual_machine.value().as_ref()) { | 346 | | Ok(m) => request.respond(methods::Response::state_getMetadata(methods::HexString(m.to_vec()))), | 347 | | Err(_) => { | 348 | | request.fail(service::ErrorResponse::InternalError); | 349 | | } | 350 | | } | 351 | | break; | 352 | | } | 353 | | executor::runtime_call::RuntimeCall::Finished(Err(_)) => { | 354 | | request.fail(service::ErrorResponse::InternalError); | 355 | | break; | 356 | | } | 357 | | executor::runtime_call::RuntimeCall::StorageGet(req) => { | 358 | | let parent_paths = req.child_trie().map(|child_trie| { | 359 | | trie::bytes_to_nibbles( | 360 | | b":child_storage:default:".iter().copied(), | 361 | | ) | 362 | | .chain(trie::bytes_to_nibbles( | 363 | | child_trie.as_ref().iter().copied(), | 364 | | )) | 365 | | .map(u8::from) | 366 | | .collect::<Vec<_>>() | 367 | | }); | 368 | | let key = | 369 | | trie::bytes_to_nibbles(req.key().as_ref().iter().copied()) | 370 | | .map(u8::from) | 371 | | .collect::<Vec<_>>(); | 372 | | let value = config | 373 | | .database | 374 | | .with_database(move |db| { | 375 | | db.block_storage_get( | 376 | | &hash, | 377 | | parent_paths.into_iter().map(|p| p.into_iter()), | 378 | | key.iter().copied(), | 379 | | ) | 380 | | }) | 381 | | .await; | 382 | | let Ok(value) = value else { | 383 | | request.fail(service::ErrorResponse::InternalError); | 384 | | break; | 385 | | }; | 386 | | let value = value.as_ref().map(|(val, vers)| { | 387 | | ( | 388 | | iter::once(&val[..]), | 389 | | executor::runtime_call::TrieEntryVersion::try_from(*vers) | 390 | | .expect("corrupted database"), | 391 | | ) | 392 | | }); | 393 | | | 394 | | call = req.inject_value(value); | 395 | | } | 396 | | executor::runtime_call::RuntimeCall::ClosestDescendantMerkleValue(req) => { | 397 | | let parent_paths = req.child_trie().map(|child_trie| { | 398 | | trie::bytes_to_nibbles( | 399 | | b":child_storage:default:".iter().copied(), | 400 | | ) | 401 | | .chain(trie::bytes_to_nibbles( | 402 | | child_trie.as_ref().iter().copied(), | 403 | | )) | 404 | | .map(u8::from) | 405 | | .collect::<Vec<_>>() | 406 | | }); | 407 | | let key_nibbles = req.key().map(u8::from).collect::<Vec<_>>(); | 408 | | | 409 | | let merkle_value = config | 410 | | .database | 411 | | .with_database(move |db| { | 412 | | db.block_storage_closest_descendant_merkle_value( | 413 | | &hash, | 414 | | parent_paths.into_iter().map(|p| p.into_iter()), | 415 | | key_nibbles.iter().copied(), | 416 | | ) | 417 | | }) | 418 | | .await; | 419 | | | 420 | | let Ok(merkle_value) = merkle_value else { | 421 | | request.fail(service::ErrorResponse::InternalError); | 422 | | break; | 423 | | }; | 424 | | | 425 | | call = req | 426 | | .inject_merkle_value(merkle_value.as_ref().map(|v| &v[..])); | 427 | | } | 428 | | executor::runtime_call::RuntimeCall::NextKey(req) => { | 429 | | let parent_paths = req.child_trie().map(|child_trie| { | 430 | | trie::bytes_to_nibbles( | 431 | | b":child_storage:default:".iter().copied(), | 432 | | ) | 433 | | .chain(trie::bytes_to_nibbles( | 434 | | child_trie.as_ref().iter().copied(), | 435 | | )) | 436 | | .map(u8::from) | 437 | | .collect::<Vec<_>>() | 438 | | }); | 439 | | let key_nibbles = req | 440 | | .key() | 441 | | .map(u8::from) | 442 | | .chain(if req.or_equal() { None } else { Some(0u8) }) | 443 | | .collect::<Vec<_>>(); | 444 | | let prefix_nibbles = | 445 | | req.prefix().map(u8::from).collect::<Vec<_>>(); | 446 | | | 447 | | let branch_nodes = req.branch_nodes(); | 448 | | let next_key = config | 449 | | .database | 450 | | .with_database(move |db| { | 451 | | db.block_storage_next_key( | 452 | | &hash, | 453 | | parent_paths.into_iter().map(|p| p.into_iter()), | 454 | | key_nibbles.iter().copied(), | 455 | | prefix_nibbles.iter().copied(), | 456 | | branch_nodes, | 457 | | ) | 458 | | }) | 459 | | .await; | 460 | | | 461 | | let Ok(next_key) = next_key else { | 462 | | request.fail(service::ErrorResponse::InternalError); | 463 | | break; | 464 | | }; | 465 | | | 466 | | call = req.inject_key(next_key.map(|k| { | 467 | | k.into_iter().map(|b| trie::Nibble::try_from(b).unwrap()) | 468 | | })); | 469 | | } | 470 | | executor::runtime_call::RuntimeCall::OffchainStorageSet(req) => { | 471 | | call = req.resume(); | 472 | | } | 473 | | executor::runtime_call::RuntimeCall::SignatureVerification(req) => { | 474 | | call = req.verify_and_resume(); | 475 | | } | 476 | | executor::runtime_call::RuntimeCall::Offchain(_) => { | 477 | | request.fail(service::ErrorResponse::InternalError); | 478 | | break; | 479 | | } | 480 | | executor::runtime_call::RuntimeCall::LogEmit(req) => { | 481 | | // Logs are ignored. | 482 | | call = req.resume(); | 483 | | } | 484 | | } | 485 | | } | 486 | | } | 487 | | methods::MethodCall::state_getRuntimeVersion { at } => { | 488 | | let at = match at { | 489 | | Some(h) => h.0, | 490 | | None => match config | 491 | | .database | 492 | | .with_database(|db| db.best_block_hash()) | 493 | | .await | 494 | | { | 495 | | Ok(b) => b, | 496 | | Err(_) => { | 497 | | request.fail(service::ErrorResponse::InternalError); | 498 | | continue; | 499 | | } | 500 | | }, | 501 | | }; | 502 | | | 503 | | match config.runtime_caches_service.get(at).await { | 504 | | Ok(runtime) => { | 505 | | request.respond(methods::Response::state_getRuntimeVersion( | 506 | | convert_runtime_version(runtime.runtime_version()), | 507 | | )); | 508 | | } | 509 | | Err(runtime_caches_service::GetError::UnknownBlock) | 510 | | | Err(runtime_caches_service::GetError::Pruned) => { | 511 | | request.respond_null() | 512 | | } // TODO: unclear if correct error | 513 | | Err(runtime_caches_service::GetError::InvalidRuntime(_)) | 514 | | | Err(runtime_caches_service::GetError::NoCode) | 515 | | | Err(runtime_caches_service::GetError::InvalidHeapPages) | 516 | | | Err(runtime_caches_service::GetError::CorruptedDatabase) => { | 517 | | request.fail(service::ErrorResponse::InternalError) | 518 | | } | 519 | | } | 520 | | } | 521 | | methods::MethodCall::state_queryStorageAt { keys, at } => { | 522 | | // TODO: add a limit to the number of keys? | 523 | | | 524 | | // Convert the list of keys into a format suitable for the database. | 525 | | let keys_nibbles = keys | 526 | | .iter() | 527 | | .map(|key| { | 528 | | trie::bytes_to_nibbles(key.0.iter().copied()) | 529 | | .map(u8::from) | 530 | | .collect::<Vec<_>>() | 531 | | }) | 532 | | .collect::<Vec<_>>(); | 533 | | | 534 | | // The bulk of the request is performed in the database thread. | 535 | | let result = config | 536 | | .database | 537 | | .with_database(move |db| { | 538 | | let at = match at { | 539 | | Some(h) => h.0, | 540 | | None => db.best_block_hash()?, | 541 | | }; | 542 | | | 543 | | let parent = db | 544 | | .block_parent(&at)? | 545 | | .ok_or(database_thread::StorageAccessError::UnknownBlock)?; | 546 | | | 547 | | let mut out = methods::StorageChangeSet { | 548 | | block: methods::HashHexString(at), | 549 | | changes: Vec::with_capacity(keys_nibbles.len()), | 550 | | }; | 551 | | | 552 | | for (key_nibbles, key) in | 553 | | keys_nibbles.into_iter().zip(keys.into_iter()) | 554 | | { | 555 | | let before = match db.block_storage_get( | 556 | | &parent, | 557 | | iter::empty::<iter::Empty<_>>(), | 558 | | key_nibbles.iter().copied(), | 559 | | ) { | 560 | | Ok(v) => v, | 561 | | Err(database_thread::StorageAccessError::UnknownBlock) | 562 | | if parent == [0; 32] => | 563 | | { | 564 | | // In case where `at` is the genesis block, we | 565 | | // assume that its "parent" (which doesn't exist) | 566 | | // has an empty storage. | 567 | | None | 568 | | } | 569 | | Err(err) => return Err(err), | 570 | | }; | 571 | | | 572 | | let after = db.block_storage_get( | 573 | | &at, | 574 | | iter::empty::<iter::Empty<_>>(), | 575 | | key_nibbles.iter().copied(), | 576 | | )?; | 577 | | | 578 | | if before != after { | 579 | | out.changes | 580 | | .push((key, after.map(|(v, _)| methods::HexString(v)))); | 581 | | } | 582 | | } | 583 | | | 584 | | Ok(out) | 585 | | }) | 586 | | .await; | 587 | | | 588 | | // Send back the response. | 589 | | match result { | 590 | | Ok(out) => { | 591 | | request.respond(methods::Response::state_queryStorageAt(vec![out])); | 592 | | } | 593 | | Err(database_thread::StorageAccessError::IncompleteStorage) | 594 | | | Err(database_thread::StorageAccessError::UnknownBlock) => { | 595 | | // Note that it is unclear how the function should behave in | 596 | | // that situation. | 597 | | request.fail(service::ErrorResponse::InvalidParams); | 598 | | } | 599 | | Err(database_thread::StorageAccessError::Corrupted(_)) => { | 600 | | request.fail(service::ErrorResponse::InternalError); | 601 | | } | 602 | | } | 603 | | } | 604 | | methods::MethodCall::system_chain {} => { | 605 | | request | 606 | | .respond(methods::Response::system_chain((&config.chain_name).into())); | 607 | | } | 608 | | methods::MethodCall::system_chainType {} => { | 609 | | request.respond(methods::Response::system_chainType( | 610 | | (&config.chain_type).into(), | 611 | | )); | 612 | | } | 613 | | methods::MethodCall::system_health {} => { | 614 | | let (is_syncing, peers) = future::zip( | 615 | | config.consensus_service.is_major_syncing_hint(), | 616 | | config.network_service.0.num_peers(config.network_service.1), | 617 | | ) | 618 | | .await; | 619 | | | 620 | | request.respond(methods::Response::system_health(methods::SystemHealth { | 621 | | is_syncing, | 622 | | peers: u64::try_from(peers).unwrap_or(u64::MAX), | 623 | | should_have_peers: config.chain_is_live, | 624 | | })); | 625 | | } | 626 | | methods::MethodCall::system_localPeerId {} => { | 627 | | let peer_id = config.network_service.0.local_peer_id().to_base58(); | 628 | | request.respond(methods::Response::system_localPeerId(peer_id.into())); | 629 | | } | 630 | | methods::MethodCall::system_name {} => { | 631 | | request.respond(methods::Response::system_version( | 632 | | env!("CARGO_PKG_NAME").into(), | 633 | | )); | 634 | | } | 635 | | methods::MethodCall::system_properties {} => { | 636 | | request.respond(methods::Response::system_properties( | 637 | | serde_json::from_str(&config.chain_properties_json).unwrap(), | 638 | | )); | 639 | | } | 640 | | methods::MethodCall::system_version {} => { | 641 | | request.respond(methods::Response::system_version( | 642 | | env!("CARGO_PKG_VERSION").into(), | 643 | | )); | 644 | | } | 645 | | | 646 | | _ => request.fail(service::ErrorResponse::ServerError( | 647 | | -32000, | 648 | | "Not implemented in smoldot yet", | 649 | | )), | 650 | | }, | 651 | | Some(Message::SubscriptionStart(request)) => match request.request() { | 652 | | methods::MethodCall::chain_subscribeAllHeads {} => { | 653 | | let block_number_bytes = config.consensus_service.block_number_bytes(); | 654 | | let mut blocks_to_report = legacy_api_subscriptions::SubscribeAllHeads::new( | 655 | | config.consensus_service.clone(), | 656 | | ); | 657 | | | 658 | | (config.tasks_executor)(Box::pin(async move { | 659 | | let mut subscription = request.accept(); | 660 | | let subscription_id = subscription.subscription_id().to_owned(); | 661 | | | 662 | | loop { | 663 | | let scale_encoded_header = | 664 | | blocks_to_report.next_scale_encoded_header().await; | 665 | | | 666 | | let json_rpc_header = | 667 | | match methods::Header::from_scale_encoded_header( | 668 | | &scale_encoded_header, | 669 | | block_number_bytes, | 670 | | ) { | 671 | | Ok(h) => h, | 672 | | Err(_) => { | 673 | | // TODO: consider reporting to logs | 674 | | continue; | 675 | | } | 676 | | }; | 677 | | | 678 | | subscription | 679 | | .send_notification(methods::ServerToClient::chain_allHead { | 680 | | subscription: (&subscription_id).into(), | 681 | | result: json_rpc_header.clone(), | 682 | | }) | 683 | | .await | 684 | | } | 685 | | })); | 686 | | } | 687 | | | 688 | | methods::MethodCall::chain_subscribeFinalizedHeads {} => { | 689 | | let block_number_bytes = config.consensus_service.block_number_bytes(); | 690 | | let mut blocks_to_report = | 691 | | legacy_api_subscriptions::SubscribeFinalizedHeads::new( | 692 | | config.consensus_service.clone(), | 693 | | ); | 694 | | | 695 | | (config.tasks_executor)(Box::pin(async move { | 696 | | let mut subscription = request.accept(); | 697 | | let subscription_id = subscription.subscription_id().to_owned(); | 698 | | | 699 | | loop { | 700 | | let scale_encoded_header = | 701 | | blocks_to_report.next_scale_encoded_header().await; | 702 | | | 703 | | let json_rpc_header = | 704 | | match methods::Header::from_scale_encoded_header( | 705 | | &scale_encoded_header, | 706 | | block_number_bytes, | 707 | | ) { | 708 | | Ok(h) => h, | 709 | | Err(_) => { | 710 | | // TODO: consider reporting to logs | 711 | | continue; | 712 | | } | 713 | | }; | 714 | | | 715 | | subscription | 716 | | .send_notification( | 717 | | methods::ServerToClient::chain_finalizedHead { | 718 | | subscription: (&subscription_id).into(), | 719 | | result: json_rpc_header.clone(), | 720 | | }, | 721 | | ) | 722 | | .await | 723 | | } | 724 | | })); | 725 | | } | 726 | | | 727 | | methods::MethodCall::chain_subscribeNewHeads {} => { | 728 | | let block_number_bytes = config.consensus_service.block_number_bytes(); | 729 | | let mut blocks_to_report = legacy_api_subscriptions::SubscribeNewHeads::new( | 730 | | config.consensus_service.clone(), | 731 | | ); | 732 | | | 733 | | (config.tasks_executor)(Box::pin(async move { | 734 | | let mut subscription = request.accept(); | 735 | | let subscription_id = subscription.subscription_id().to_owned(); | 736 | | | 737 | | loop { | 738 | | let scale_encoded_header = | 739 | | blocks_to_report.next_scale_encoded_header().await; | 740 | | | 741 | | let json_rpc_header = | 742 | | match methods::Header::from_scale_encoded_header( | 743 | | scale_encoded_header, | 744 | | block_number_bytes, | 745 | | ) { | 746 | | Ok(h) => h, | 747 | | Err(_) => { | 748 | | // TODO: consider reporting to logs | 749 | | continue; | 750 | | } | 751 | | }; | 752 | | | 753 | | subscription | 754 | | .send_notification(methods::ServerToClient::chain_newHead { | 755 | | subscription: (&subscription_id).into(), | 756 | | result: json_rpc_header.clone(), | 757 | | }) | 758 | | .await | 759 | | } | 760 | | })); | 761 | | } | 762 | | | 763 | | methods::MethodCall::state_subscribeRuntimeVersion {} => { | 764 | | let mut runtime_versions_to_report = | 765 | | legacy_api_subscriptions::SubscribeRuntimeVersion::new( | 766 | | config.consensus_service.clone(), | 767 | | ); | 768 | | | 769 | | (config.tasks_executor)(Box::pin(async move { | 770 | | let mut subscription = request.accept(); | 771 | | let subscription_id = subscription.subscription_id().to_owned(); | 772 | | | 773 | | loop { | 774 | | let runtime_version = | 775 | | runtime_versions_to_report.next_runtime_version().await; | 776 | | | 777 | | subscription | 778 | | .send_notification( | 779 | | methods::ServerToClient::state_runtimeVersion { | 780 | | subscription: (&subscription_id).into(), | 781 | | result: Some(convert_runtime_version(runtime_version)), | 782 | | }, | 783 | | ) | 784 | | .await | 785 | | } | 786 | | })); | 787 | | } | 788 | | | 789 | | methods::MethodCall::state_subscribeStorage { list } => { | 790 | | let mut notifications_to_report = | 791 | | legacy_api_subscriptions::SubscribeStorage::new( | 792 | | config.consensus_service.clone(), | 793 | | config.database.clone(), | 794 | | list.into_iter().map(|item| item.0).collect(), | 795 | | ); | 796 | | | 797 | | (config.tasks_executor)(Box::pin(async move { | 798 | | let mut subscription = request.accept(); | 799 | | let subscription_id = subscription.subscription_id().to_owned(); | 800 | | | 801 | | loop { | 802 | | let (block_hash, storage_changes) = | 803 | | notifications_to_report.next_storage_update().await; | 804 | | | 805 | | subscription | 806 | | .send_notification(methods::ServerToClient::state_storage { | 807 | | subscription: (&subscription_id).into(), | 808 | | result: methods::StorageChangeSet { | 809 | | block: methods::HashHexString(block_hash), | 810 | | changes: storage_changes | 811 | | .map(|(key, value)| { | 812 | | ( | 813 | | methods::HexString(key), | 814 | | value.map(methods::HexString), | 815 | | ) | 816 | | }) | 817 | | .collect(), | 818 | | }, | 819 | | }) | 820 | | .await | 821 | | } | 822 | | })); | 823 | | } | 824 | | | 825 | | _ => request.fail(service::ErrorResponse::ServerError( | 826 | | -32000, | 827 | | "Not implemented in smoldot yet", | 828 | | )), | 829 | | }, | 830 | | None => return, | 831 | | } | 832 | | } | 833 | 672 | })); | 834 | 672 | } |
Unexecuted instantiation: _RNvNtNtCshBwayKnNXDT_17smoldot_full_node16json_rpc_service16requests_handler22spawn_requests_handler |
835 | | |
836 | 1 | fn convert_runtime_version(runtime_spec: &executor::CoreVersion) -> methods::RuntimeVersion { |
837 | 1 | let runtime_spec = runtime_spec.decode(); |
838 | 1 | methods::RuntimeVersion { |
839 | 1 | spec_name: runtime_spec.spec_name.into(), |
840 | 1 | impl_name: runtime_spec.impl_name.into(), |
841 | 1 | authoring_version: u64::from(runtime_spec.authoring_version), |
842 | 1 | spec_version: u64::from(runtime_spec.spec_version), |
843 | 1 | impl_version: u64::from(runtime_spec.impl_version), |
844 | 1 | transaction_version: runtime_spec.transaction_version.map(u64::from), |
845 | 1 | state_version: runtime_spec.state_version.map(u8::from).map(u64::from), |
846 | 1 | apis: runtime_spec |
847 | 1 | .apis |
848 | 10 | .map(|api| (methods::HexString(api.name_hash.to_vec()), api.version)) _RNCNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler23convert_runtime_version0B7_ Line | Count | Source | 848 | 10 | .map(|api| (methods::HexString(api.name_hash.to_vec()), api.version)) |
Unexecuted instantiation: _RNCNvNtNtCshBwayKnNXDT_17smoldot_full_node16json_rpc_service16requests_handler23convert_runtime_version0B7_ |
849 | 1 | .collect(), |
850 | 1 | } |
851 | 1 | } _RNvNtNtCsiUjFBJteJ7x_17smoldot_full_node16json_rpc_service16requests_handler23convert_runtime_version Line | Count | Source | 836 | 1 | fn convert_runtime_version(runtime_spec: &executor::CoreVersion) -> methods::RuntimeVersion { | 837 | 1 | let runtime_spec = runtime_spec.decode(); | 838 | 1 | methods::RuntimeVersion { | 839 | 1 | spec_name: runtime_spec.spec_name.into(), | 840 | 1 | impl_name: runtime_spec.impl_name.into(), | 841 | 1 | authoring_version: u64::from(runtime_spec.authoring_version), | 842 | 1 | spec_version: u64::from(runtime_spec.spec_version), | 843 | 1 | impl_version: u64::from(runtime_spec.impl_version), | 844 | 1 | transaction_version: runtime_spec.transaction_version.map(u64::from), | 845 | 1 | state_version: runtime_spec.state_version.map(u8::from).map(u64::from), | 846 | 1 | apis: runtime_spec | 847 | 1 | .apis | 848 | 1 | .map(|api| (methods::HexString(api.name_hash.to_vec()), api.version)) | 849 | 1 | .collect(), | 850 | 1 | } | 851 | 1 | } |
Unexecuted instantiation: _RNvNtNtCshBwayKnNXDT_17smoldot_full_node16json_rpc_service16requests_handler23convert_runtime_version |