/__w/smoldot/smoldot/repo/light-base/src/json_rpc_service/background.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 crate::{ |
19 | | log, network_service, |
20 | | platform::PlatformRef, |
21 | | runtime_service, sync_service, transactions_service, |
22 | | util::{self, SipHasherBuild}, |
23 | | }; |
24 | | |
25 | | use alloc::{ |
26 | | borrow::{Cow, ToOwned as _}, |
27 | | boxed::Box, |
28 | | collections::{BTreeSet, VecDeque}, |
29 | | format, |
30 | | string::{String, ToString as _}, |
31 | | sync::Arc, |
32 | | vec, |
33 | | vec::Vec, |
34 | | }; |
35 | | use core::{ |
36 | | iter, mem, |
37 | | num::{NonZeroU32, NonZeroUsize}, |
38 | | pin::Pin, |
39 | | time::Duration, |
40 | | }; |
41 | | use futures_lite::{FutureExt as _, StreamExt as _}; |
42 | | use futures_util::{future, stream}; |
43 | | use rand_chacha::{ |
44 | | rand_core::{RngCore as _, SeedableRng as _}, |
45 | | ChaCha20Rng, |
46 | | }; |
47 | | use smoldot::{ |
48 | | header, |
49 | | informant::HashDisplay, |
50 | | json_rpc::{self, methods, parse}, |
51 | | libp2p::{multiaddr, PeerId}, |
52 | | network::codec, |
53 | | }; |
54 | | |
55 | | /// Configuration for a JSON-RPC service. |
56 | | pub(super) struct Config<TPlat: PlatformRef> { |
57 | | /// Access to the platform's capabilities. |
58 | | // TODO: redundant with Config above? |
59 | | pub platform: TPlat, |
60 | | |
61 | | /// Access to the network, and identifier of the chain from the point of view of the network |
62 | | /// service. |
63 | | pub network_service: Arc<network_service::NetworkServiceChain<TPlat>>, |
64 | | |
65 | | /// Service responsible for synchronizing the chain. |
66 | | pub sync_service: Arc<sync_service::SyncService<TPlat>>, |
67 | | |
68 | | /// Service responsible for emitting transactions and tracking their state. |
69 | | pub transactions_service: Arc<transactions_service::TransactionsService<TPlat>>, |
70 | | |
71 | | /// Service that provides a ready-to-be-called runtime for the current best block. |
72 | | pub runtime_service: Arc<runtime_service::RuntimeService<TPlat>>, |
73 | | |
74 | | /// Name of the chain, as found in the chain specification. |
75 | | pub chain_name: String, |
76 | | /// Type of chain, as found in the chain specification. |
77 | | pub chain_ty: String, |
78 | | /// JSON-encoded properties of the chain, as found in the chain specification. |
79 | | pub chain_properties_json: String, |
80 | | /// Whether the chain is a live network. Found in the chain specification. |
81 | | pub chain_is_live: bool, |
82 | | |
83 | | /// Value to return when the `system_name` RPC is called. Should be set to the name of the |
84 | | /// final executable. |
85 | | pub system_name: String, |
86 | | |
87 | | /// Value to return when the `system_version` RPC is called. Should be set to the version of |
88 | | /// the final executable. |
89 | | pub system_version: String, |
90 | | |
91 | | /// Hash of the genesis block of the chain. |
92 | | pub genesis_block_hash: [u8; 32], |
93 | | } |
94 | | |
95 | | /// Fields used to process JSON-RPC requests in the background. |
96 | | struct Background<TPlat: PlatformRef> { |
97 | | /// Target to use for all the logs. |
98 | | log_target: String, |
99 | | |
100 | | /// Access to the platform's capabilities. |
101 | | platform: TPlat, |
102 | | |
103 | | /// Name of the chain, as found in the chain specification. |
104 | | chain_name: String, |
105 | | /// Type of chain, as found in the chain specification. |
106 | | chain_ty: String, |
107 | | /// JSON-encoded properties of the chain, as found in the chain specification. |
108 | | chain_properties_json: String, |
109 | | /// Whether the chain is a live network. Found in the chain specification. |
110 | | chain_is_live: bool, |
111 | | /// Value to return when the `system_name` RPC is called. |
112 | | system_name: String, |
113 | | /// Value to return when the `system_version` RPC is called. |
114 | | system_version: String, |
115 | | /// Hash of the genesis block. |
116 | | /// Keeping the genesis block is important, as the genesis block hash is included in |
117 | | /// transaction signatures, and must therefore be queried by upper-level UIs. |
118 | | genesis_block_hash: [u8; 32], |
119 | | |
120 | | /// Randomness used for various purposes, such as generating subscription IDs. |
121 | | randomness: ChaCha20Rng, |
122 | | |
123 | | /// See [`Config::network_service`]. |
124 | | network_service: Arc<network_service::NetworkServiceChain<TPlat>>, |
125 | | /// See [`Config::sync_service`]. |
126 | | sync_service: Arc<sync_service::SyncService<TPlat>>, |
127 | | /// See [`Config::runtime_service`]. |
128 | | runtime_service: Arc<runtime_service::RuntimeService<TPlat>>, |
129 | | /// See [`Config::transactions_service`]. |
130 | | transactions_service: Arc<transactions_service::TransactionsService<TPlat>>, |
131 | | |
132 | | /// Tasks that are spawned by the service and running in the background. |
133 | | background_tasks: |
134 | | stream::FuturesUnordered<Pin<Box<dyn future::Future<Output = Event<TPlat>> + Send>>>, |
135 | | |
136 | | /// Channel where serialized JSON-RPC requests are pulled from. |
137 | | requests_rx: Pin<Box<async_channel::Receiver<String>>>, |
138 | | /// Channel to send serialized JSON-RPC responses and notifications to the foreground. |
139 | | responses_tx: async_channel::Sender<String>, |
140 | | |
141 | | /// State of each `chainHead_follow` subscription indexed by its ID. |
142 | | chain_head_follow_subscriptions: |
143 | | hashbrown::HashMap<String, ChainHeadFollow, fnv::FnvBuildHasher>, |
144 | | |
145 | | /// If `true`, we have already printed a warning about usage of the legacy JSON-RPC API. This |
146 | | /// flag prevents printing this message multiple times. |
147 | | printed_legacy_json_rpc_warning: bool, |
148 | | |
149 | | /// Next time to do some memory reclaims. |
150 | | next_garbage_collection: Pin<Box<TPlat::Delay>>, |
151 | | |
152 | | /// State of the runtime service subscription. Used for legacy JSON-RPC API subscriptions. |
153 | | runtime_service_subscription: RuntimeServiceSubscription<TPlat>, |
154 | | /// List of all active `chain_subscribeAllHeads` subscriptions, indexed by the subscription ID. |
155 | | all_heads_subscriptions: hashbrown::HashSet<String, fnv::FnvBuildHasher>, |
156 | | /// List of all active `chain_subscribeNewHeads` subscriptions, indexed by the subscription ID. |
157 | | new_heads_subscriptions: hashbrown::HashSet<String, fnv::FnvBuildHasher>, |
158 | | /// List of all active `chain_subscribeFinalizedHeads` subscriptions, indexed by the |
159 | | /// subscription ID. |
160 | | finalized_heads_subscriptions: hashbrown::HashSet<String, fnv::FnvBuildHasher>, |
161 | | /// List of all active `state_subscribeRuntimeVersion` subscriptions, indexed by the |
162 | | /// subscription ID. |
163 | | runtime_version_subscriptions: hashbrown::HashSet<String, fnv::FnvBuildHasher>, |
164 | | /// List of all active `author_submitAndWatchExtrinsic`, `transaction_v1_broadcast`, and |
165 | | /// `transactionWatch_v1_submitAndWatch` subscriptions, indexed by the subscription ID. |
166 | | /// When it comes to `author_submitAndWatchExtrinsic` and |
167 | | /// `transactionWatch_v1_submitAndWatch`, transactions are removed from this list when |
168 | | /// they are dropped from the transactions service. When it comes |
169 | | /// to `transaction_v1_broadcast`, transactions are left forever until the API user |
170 | | /// unsubscribes. |
171 | | transactions_subscriptions: hashbrown::HashMap<String, TransactionWatch, fnv::FnvBuildHasher>, |
172 | | |
173 | | /// List of all active `state_subscribeStorage` subscriptions, indexed by the subscription ID. |
174 | | /// Values are the list of keys requested by this subscription. |
175 | | legacy_api_storage_subscriptions: BTreeSet<(Arc<str>, Vec<u8>)>, |
176 | | /// Identical to [`Background::legacy_api_storage_subscriptions`] but indexed by requested key. |
177 | | legacy_api_storage_subscriptions_by_key: BTreeSet<(Vec<u8>, Arc<str>)>, |
178 | | /// List of storage subscriptions whose latest sent notification isn't about the current |
179 | | /// best block. |
180 | | legacy_api_stale_storage_subscriptions: hashbrown::HashSet<Arc<str>, fnv::FnvBuildHasher>, |
181 | | /// `true` if there exists a background task in [`Background::background_tasks`] currently |
182 | | /// fetching storage items for storage subscriptions. |
183 | | legacy_api_storage_query_in_progress: bool, |
184 | | |
185 | | /// List of multi-stage requests (i.e. JSON-RPC requests that require multiple asynchronous |
186 | | /// operations) that are ready to make progress. |
187 | | multistage_requests_to_advance: VecDeque<(String, MultiStageRequestStage, MultiStageRequestTy)>, |
188 | | /// Multi-stage requests that are waiting for the best block hash to be known in order |
189 | | /// to progress. |
190 | | best_block_hash_pending: Vec<(String, MultiStageRequestTy)>, |
191 | | /// List of request IDs of `chain_getFinalizedHash` requests that are waiting for the |
192 | | /// finalized block hash to be known. |
193 | | pending_get_finalized_head: Vec<String>, |
194 | | /// Requests for blocks headers, state root hash and numbers that are still in progress. |
195 | | /// For each block hash, contains a list of multi-stage requests that are interested in the |
196 | | /// response. Once the operation has been finished, the value is inserted in |
197 | | /// [`Background::block_headers_cache`]. |
198 | | block_headers_pending: |
199 | | hashbrown::HashMap<[u8; 32], Vec<(String, MultiStageRequestTy)>, fnv::FnvBuildHasher>, |
200 | | /// Requests for block runtimes that are still in progress. |
201 | | /// For each block hash, contains a list of requests that are interested in the response. |
202 | | /// Once the operation has been finished, the value is inserted in |
203 | | /// [`Background::block_runtimes_cache`]. |
204 | | block_runtimes_pending: |
205 | | hashbrown::HashMap<[u8; 32], Vec<(String, MultiStageRequestTy)>, fnv::FnvBuildHasher>, |
206 | | |
207 | | /// Cache of known headers, state trie root hashes and numbers of blocks. Used only for the |
208 | | /// legacy JSON-RPC API. |
209 | | /// |
210 | | /// Can also be an `Err` if the header is in an invalid format. |
211 | | block_headers_cache: lru::LruCache< |
212 | | [u8; 32], |
213 | | Result<(Vec<u8>, [u8; 32], u64), header::Error>, |
214 | | fnv::FnvBuildHasher, |
215 | | >, |
216 | | /// Cache of known runtimes of blocks. Used only for the legacy JSON-RPC API. |
217 | | /// |
218 | | /// Note that runtimes that have failed to compile can be found here as well. |
219 | | block_runtimes_cache: |
220 | | lru::LruCache<[u8; 32], runtime_service::PinnedRuntime, fnv::FnvBuildHasher>, |
221 | | /// When `state_getKeysPaged` is called and the response is truncated, the response is |
222 | | /// inserted in this cache. The API user is likely to call `state_getKeysPaged` again with |
223 | | /// the same parameters, in which case we hit the cache and avoid the networking requests. |
224 | | /// The values are list of keys. |
225 | | state_get_keys_paged_cache: |
226 | | lru::LruCache<GetKeysPagedCacheKey, Vec<Vec<u8>>, util::SipHasherBuild>, |
227 | | } |
228 | | |
229 | | /// State of the subscription towards the runtime service. |
230 | | /// See [`Background::runtime_service_subscription`]. |
231 | | enum RuntimeServiceSubscription<TPlat: PlatformRef> { |
232 | | /// Subscription is active. |
233 | | Active { |
234 | | /// Object representing the subscription. |
235 | | subscription: runtime_service::Subscription<TPlat>, |
236 | | |
237 | | /// Hash of the current best block. Guaranteed to be in |
238 | | /// [`RuntimeServiceSubscription::Active::pinned_blocks`]. |
239 | | current_best_block: [u8; 32], |
240 | | |
241 | | /// If `Some`, the new heads and runtime version subscriptions haven't been updated about |
242 | | /// the new current best block yet. Contains the previous best block that the |
243 | | /// subscriptions are aware of. The previous best block is guaranteed to be in |
244 | | /// [`RuntimeServiceSubscription::Active::pinned_blocks`]. |
245 | | new_heads_and_runtime_subscriptions_stale: Option<Option<[u8; 32]>>, |
246 | | |
247 | | /// Hash of the current finalized block. Guaranteed to be in |
248 | | /// [`RuntimeServiceSubscription::Active::pinned_blocks`]. |
249 | | current_finalized_block: [u8; 32], |
250 | | |
251 | | /// If `true`, the finalized heads subscriptions haven't been updated about the new |
252 | | /// current finalized block yet. |
253 | | finalized_heads_subscriptions_stale: bool, |
254 | | |
255 | | /// When the runtime service reports a new block, it is kept pinned and inserted in this |
256 | | /// list. |
257 | | /// |
258 | | /// Blocks are removed from this container and unpinned when they leave |
259 | | /// [`RuntimeServiceSubscription::Active::finalized_and_pruned_lru`]. |
260 | | /// |
261 | | /// JSON-RPC clients are more likely to ask for information about recent blocks and |
262 | | /// perform calls on them, hence a cache of recent blocks. |
263 | | pinned_blocks: hashbrown::HashMap<[u8; 32], RecentBlock, fnv::FnvBuildHasher>, |
264 | | |
265 | | /// When a block is finalized or pruned, it is inserted into this LRU cache. The least |
266 | | /// recently used blocks are removed and unpinned. |
267 | | finalized_and_pruned_lru: lru::LruCache<[u8; 32], (), fnv::FnvBuildHasher>, |
268 | | }, |
269 | | |
270 | | /// Waiting for the runtime service to start the subscription. Can potentially take a long |
271 | | /// time. |
272 | | Pending(Pin<Box<dyn future::Future<Output = runtime_service::SubscribeAll<TPlat>> + Send>>), |
273 | | |
274 | | /// Subscription not requested yet. Should transition to |
275 | | /// [`RuntimeServiceSubscription::Pending`] as soon as possible. |
276 | | NotCreated, |
277 | | } |
278 | | |
279 | | struct RecentBlock { |
280 | | scale_encoded_header: Vec<u8>, |
281 | | // TODO: do we really need to keep the runtime version here, given that the block is still pinned in the runtime service? |
282 | | runtime_version: Arc<Result<smoldot::executor::CoreVersion, runtime_service::RuntimeError>>, |
283 | | } |
284 | | |
285 | | struct ChainHeadFollow { |
286 | | /// For each pinned block hash, the SCALE-encoded header of the block. |
287 | | pinned_blocks_headers: hashbrown::HashMap<[u8; 32], Vec<u8>, fnv::FnvBuildHasher>, |
288 | | |
289 | | /// List of body/call/storage operations currently in progress. Keys are operation IDs. |
290 | | operations_in_progress: hashbrown::HashMap<String, ChainHeadOperation, fnv::FnvBuildHasher>, |
291 | | |
292 | | /// Remaining number of operation slots that the JSON-RPC client can occupy. |
293 | | available_operation_slots: u32, |
294 | | |
295 | | /// If the subscription was created with `withRuntime: true`, contains the subscription ID |
296 | | /// according to the runtime service. |
297 | | /// |
298 | | /// Contains `None` if `withRuntime` was `false`, or if the subscription hasn't been |
299 | | /// initialized yet. |
300 | | runtime_service_subscription_id: Option<runtime_service::SubscriptionId>, |
301 | | } |
302 | | |
303 | | struct ChainHeadOperation { |
304 | | /// Number of slots that this operation occupies. |
305 | | /// See [`ChainHeadFollow::available_operation_slots`]. |
306 | | occupied_slots: u32, |
307 | | |
308 | | /// Event connected to the background task in [`Background::background_tasks`] that is |
309 | | /// currently executing the operation. Cancels the task when notified. |
310 | | interrupt: event_listener::Event, |
311 | | } |
312 | | |
313 | | enum MultiStageRequestStage { |
314 | | BlockHashNotKnown, |
315 | | BlockHashKnown { |
316 | | block_hash: [u8; 32], |
317 | | }, |
318 | | BlockInfoKnown { |
319 | | block_hash: [u8; 32], |
320 | | block_state_trie_root_hash: [u8; 32], |
321 | | block_number: u64, |
322 | | }, |
323 | | } |
324 | | |
325 | | enum MultiStageRequestTy { |
326 | | ChainGetBestBlockHash, |
327 | | ChainGetBlock, |
328 | | ChainGetHeader, |
329 | | StateCall { |
330 | | name: String, |
331 | | parameters: Vec<u8>, |
332 | | }, |
333 | | StateGetKeys { |
334 | | prefix: Vec<u8>, |
335 | | }, |
336 | | StateGetKeysPaged { |
337 | | prefix: Vec<u8>, |
338 | | count: u32, |
339 | | start_key: Option<Vec<u8>>, |
340 | | }, |
341 | | StateQueryStorageAt { |
342 | | keys: Vec<methods::HexString>, |
343 | | }, |
344 | | StateGetMetadata, |
345 | | StateGetStorage { |
346 | | key: Vec<u8>, |
347 | | }, |
348 | | StateGetRuntimeVersion, |
349 | | PaymentQueryInfo { |
350 | | extrinsic: Vec<u8>, |
351 | | }, |
352 | | SystemAccountNextIndex { |
353 | | account_id: Vec<u8>, |
354 | | }, |
355 | | } |
356 | | |
357 | | enum StorageRequestInProgress { |
358 | | StateGetKeys { |
359 | | in_progress_results: Vec<methods::HexString>, |
360 | | }, |
361 | | StateGetKeysPaged { |
362 | | block_hash: [u8; 32], |
363 | | prefix: Vec<u8>, |
364 | | count: u32, |
365 | | start_key: Option<Vec<u8>>, |
366 | | in_progress_results: Vec<Vec<u8>>, |
367 | | }, |
368 | | StateQueryStorageAt { |
369 | | block_hash: [u8; 32], |
370 | | in_progress_results: Vec<(methods::HexString, Option<methods::HexString>)>, |
371 | | }, |
372 | | StateGetStorage, |
373 | | } |
374 | | |
375 | | enum RuntimeCallRequestInProgress { |
376 | | StateCall, |
377 | | StateGetMetadata, |
378 | | PaymentQueryInfo, |
379 | | SystemAccountNextIndex, |
380 | | } |
381 | | |
382 | | /// Event generated by a task in [`Background::background_tasks`] when it finishes. |
383 | | enum Event<TPlat: PlatformRef> { |
384 | | TransactionEvent { |
385 | | subscription_id: String, |
386 | | event: transactions_service::TransactionStatus, |
387 | | watcher: Pin<Box<transactions_service::TransactionWatcher>>, |
388 | | }, |
389 | | ChainGetBlockResult { |
390 | | request_id_json: String, |
391 | | result: Result<codec::BlockData, ()>, |
392 | | expected_block_hash: [u8; 32], |
393 | | }, |
394 | | ChainHeadSubscriptionWithRuntimeReady { |
395 | | subscription_id: String, |
396 | | subscription: runtime_service::SubscribeAll<TPlat>, |
397 | | }, |
398 | | ChainHeadSubscriptionWithRuntimeNotification { |
399 | | subscription_id: String, |
400 | | notification: runtime_service::Notification, |
401 | | stream: runtime_service::Subscription<TPlat>, |
402 | | }, |
403 | | ChainHeadSubscriptionWithoutRuntimeReady { |
404 | | subscription_id: String, |
405 | | subscription: sync_service::SubscribeAll, |
406 | | }, |
407 | | ChainHeadSubscriptionWithoutRuntimeNotification { |
408 | | subscription_id: String, |
409 | | notification: sync_service::Notification, |
410 | | stream: Pin<Box<async_channel::Receiver<sync_service::Notification>>>, |
411 | | }, |
412 | | ChainHeadSubscriptionDeadSubcription { |
413 | | subscription_id: String, |
414 | | }, |
415 | | ChainHeadStorageOperationProgress { |
416 | | subscription_id: String, |
417 | | operation_id: String, |
418 | | progress: sync_service::StorageQueryProgress<TPlat>, |
419 | | }, |
420 | | ChainHeadCallOperationDone { |
421 | | subscription_id: String, |
422 | | operation_id: String, |
423 | | result: Result<runtime_service::RuntimeCallSuccess, runtime_service::RuntimeCallError>, |
424 | | }, |
425 | | ChainHeadBodyOperationDone { |
426 | | subscription_id: String, |
427 | | operation_id: String, |
428 | | expected_extrinsics_root: [u8; 32], |
429 | | result: Result<codec::BlockData, ()>, |
430 | | }, |
431 | | ChainHeadOperationCancelled, |
432 | | BlockInfoRetrieved { |
433 | | block_hash: [u8; 32], |
434 | | result: Result<Result<(Vec<u8>, [u8; 32], u64), header::Error>, ()>, |
435 | | }, |
436 | | RuntimeDownloaded { |
437 | | block_hash: [u8; 32], |
438 | | result: Result<runtime_service::PinnedRuntime, String>, |
439 | | }, |
440 | | LegacyApiFunctionStorageRequestProgress { |
441 | | request_id_json: String, |
442 | | request: StorageRequestInProgress, |
443 | | progress: sync_service::StorageQueryProgress<TPlat>, |
444 | | }, |
445 | | LegacyApiFunctionRuntimeCallResult { |
446 | | request_id_json: String, |
447 | | request: RuntimeCallRequestInProgress, |
448 | | result: Result<runtime_service::RuntimeCallSuccess, runtime_service::RuntimeCallError>, |
449 | | }, |
450 | | LegacyApiStorageSubscriptionsUpdate { |
451 | | block_hash: [u8; 32], |
452 | | result: Result<Vec<sync_service::StorageResultItem>, sync_service::StorageQueryError>, |
453 | | }, |
454 | | } |
455 | | |
456 | | struct TransactionWatch { |
457 | | included_block: Option<[u8; 32]>, |
458 | | num_broadcasted_peers: usize, |
459 | | ty: TransactionWatchTy, |
460 | | } |
461 | | |
462 | | enum TransactionWatchTy { |
463 | | /// `author_submitAndWatchExtrinsic`. |
464 | | Legacy, |
465 | | /// `transaction_v1_broadcast`. |
466 | | NewApi { |
467 | | /// A copy of the body of the transaction is kept, as it might be necessary to re-insert |
468 | | /// it in the transactions service later, for example if it reports having crashed. |
469 | | transaction_bytes: Vec<u8>, |
470 | | }, |
471 | | /// `transactionWatch_v1_submitAndWatch`. |
472 | | NewApiWatch, |
473 | | } |
474 | | |
475 | | /// See [`Background::state_get_keys_paged_cache`]. |
476 | | #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
477 | | struct GetKeysPagedCacheKey { |
478 | | /// Value of the `hash` parameter of the call to `state_getKeysPaged`. |
479 | | hash: [u8; 32], |
480 | | /// Value of the `prefix` parameter of the call to `state_getKeysPaged`. |
481 | | prefix: Vec<u8>, |
482 | | } |
483 | | |
484 | 0 | pub(super) async fn run<TPlat: PlatformRef>( |
485 | 0 | log_target: String, |
486 | 0 | config: Config<TPlat>, |
487 | 0 | requests_rx: async_channel::Receiver<String>, |
488 | 0 | responses_tx: async_channel::Sender<String>, |
489 | 0 | ) { Unexecuted instantiation: _RINvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background3runpEB6_ Unexecuted instantiation: _RINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefEB1b_ Unexecuted instantiation: _RINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runpEB6_ |
490 | 0 | let mut me = Background { |
491 | 0 | log_target, |
492 | 0 | chain_name: config.chain_name, |
493 | 0 | chain_ty: config.chain_ty, |
494 | 0 | chain_is_live: config.chain_is_live, |
495 | 0 | chain_properties_json: config.chain_properties_json, |
496 | 0 | system_name: config.system_name.clone(), |
497 | 0 | system_version: config.system_version.clone(), |
498 | 0 | randomness: ChaCha20Rng::from_seed({ |
499 | 0 | let mut seed = [0; 32]; |
500 | 0 | config.platform.fill_random_bytes(&mut seed); |
501 | 0 | seed |
502 | 0 | }), |
503 | 0 | next_garbage_collection: Box::pin(config.platform.sleep(Duration::new(0, 0))), |
504 | 0 | network_service: config.network_service.clone(), |
505 | 0 | sync_service: config.sync_service.clone(), |
506 | 0 | runtime_service: config.runtime_service.clone(), |
507 | 0 | transactions_service: config.transactions_service.clone(), |
508 | 0 | background_tasks: stream::FuturesUnordered::new(), |
509 | 0 | runtime_service_subscription: RuntimeServiceSubscription::NotCreated, |
510 | 0 | all_heads_subscriptions: hashbrown::HashSet::with_capacity_and_hasher( |
511 | 0 | 2, |
512 | 0 | Default::default(), |
513 | 0 | ), |
514 | 0 | new_heads_subscriptions: hashbrown::HashSet::with_capacity_and_hasher( |
515 | 0 | 2, |
516 | 0 | Default::default(), |
517 | 0 | ), |
518 | 0 | finalized_heads_subscriptions: hashbrown::HashSet::with_capacity_and_hasher( |
519 | 0 | 2, |
520 | 0 | Default::default(), |
521 | 0 | ), |
522 | 0 | runtime_version_subscriptions: hashbrown::HashSet::with_capacity_and_hasher( |
523 | 0 | 2, |
524 | 0 | Default::default(), |
525 | 0 | ), |
526 | 0 | transactions_subscriptions: hashbrown::HashMap::with_capacity_and_hasher( |
527 | 0 | 2, |
528 | 0 | Default::default(), |
529 | 0 | ), |
530 | 0 | chain_head_follow_subscriptions: hashbrown::HashMap::with_hasher(Default::default()), |
531 | 0 | legacy_api_storage_subscriptions: BTreeSet::new(), |
532 | 0 | legacy_api_storage_subscriptions_by_key: BTreeSet::new(), |
533 | 0 | legacy_api_stale_storage_subscriptions: hashbrown::HashSet::with_capacity_and_hasher( |
534 | 0 | 0, |
535 | 0 | Default::default(), |
536 | 0 | ), |
537 | 0 | legacy_api_storage_query_in_progress: false, |
538 | 0 | requests_rx: Box::pin(requests_rx), |
539 | 0 | responses_tx, |
540 | 0 | multistage_requests_to_advance: VecDeque::new(), |
541 | 0 | block_headers_cache: lru::LruCache::with_hasher( |
542 | 0 | NonZeroUsize::new(32).unwrap_or_else(|| unreachable!()), Unexecuted instantiation: _RNCNCINvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background3runpE00Ba_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE00B1f_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runpE00Ba_ |
543 | 0 | Default::default(), |
544 | 0 | ), |
545 | 0 | best_block_hash_pending: Vec::new(), |
546 | 0 | pending_get_finalized_head: Vec::new(), |
547 | 0 | block_headers_pending: hashbrown::HashMap::with_capacity_and_hasher(0, Default::default()), |
548 | 0 | block_runtimes_cache: lru::LruCache::with_hasher( |
549 | 0 | NonZeroUsize::new(32).unwrap_or_else(|| unreachable!()), Unexecuted instantiation: _RNCNCINvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background3runpE0s_0Ba_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0s_0B1f_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runpE0s_0Ba_ |
550 | 0 | Default::default(), |
551 | 0 | ), |
552 | 0 | block_runtimes_pending: hashbrown::HashMap::with_capacity_and_hasher(0, Default::default()), |
553 | 0 | state_get_keys_paged_cache: lru::LruCache::with_hasher( |
554 | 0 | NonZeroUsize::new(2).unwrap(), |
555 | 0 | util::SipHasherBuild::new({ |
556 | 0 | let mut seed = [0; 16]; |
557 | 0 | config.platform.fill_random_bytes(&mut seed); |
558 | 0 | seed |
559 | 0 | }), |
560 | 0 | ), |
561 | 0 | genesis_block_hash: config.genesis_block_hash, |
562 | 0 | printed_legacy_json_rpc_warning: false, |
563 | 0 | platform: config.platform, |
564 | 0 | }; |
565 | | |
566 | | loop { |
567 | | // Yield at every loop in order to provide better tasks granularity. |
568 | 0 | futures_lite::future::yield_now().await; |
569 | | |
570 | | enum WakeUpReason<'a, TPlat: PlatformRef> { |
571 | | ForegroundDead, |
572 | | GarbageCollection, |
573 | | IncomingJsonRpcRequest(String), |
574 | | AdvanceMultiStageRequest { |
575 | | request_id_json: String, |
576 | | stage: MultiStageRequestStage, |
577 | | request_ty: MultiStageRequestTy, |
578 | | }, |
579 | | Event(Event<TPlat>), |
580 | | RuntimeServiceSubscriptionReady(runtime_service::SubscribeAll<TPlat>), |
581 | | RuntimeServiceSubscriptionNotification { |
582 | | notification: runtime_service::Notification, |
583 | | subscription: &'a mut runtime_service::Subscription<TPlat>, |
584 | | pinned_blocks: |
585 | | &'a mut hashbrown::HashMap<[u8; 32], RecentBlock, fnv::FnvBuildHasher>, |
586 | | finalized_and_pruned_lru: &'a mut lru::LruCache<[u8; 32], (), fnv::FnvBuildHasher>, |
587 | | current_best_block: &'a mut [u8; 32], |
588 | | new_heads_and_runtime_subscriptions_stale: &'a mut Option<Option<[u8; 32]>>, |
589 | | current_finalized_block: &'a mut [u8; 32], |
590 | | finalized_heads_subscriptions_stale: &'a mut bool, |
591 | | }, |
592 | | RuntimeServiceSubscriptionDead, |
593 | | StartStorageSubscriptionsUpdates, |
594 | | NotifyFinalizedHeads, |
595 | | NotifyNewHeadsRuntimeSubscriptions(Option<[u8; 32]>), |
596 | | } |
597 | | |
598 | | // Wait until there is something to do. |
599 | 0 | let wake_up_reason = { |
600 | 0 | async { |
601 | 0 | match &mut me.runtime_service_subscription { |
602 | | RuntimeServiceSubscription::NotCreated => { |
603 | | // TODO: only do this if there is a need for the subscription |
604 | 0 | WakeUpReason::RuntimeServiceSubscriptionDead |
605 | | } |
606 | | RuntimeServiceSubscription::Active { |
607 | 0 | subscription, |
608 | 0 | pinned_blocks, |
609 | 0 | finalized_and_pruned_lru, |
610 | 0 | current_best_block, |
611 | 0 | new_heads_and_runtime_subscriptions_stale, |
612 | 0 | current_finalized_block, |
613 | 0 | finalized_heads_subscriptions_stale, |
614 | 0 | } => { |
615 | 0 | if !me.legacy_api_storage_query_in_progress |
616 | 0 | && !me.legacy_api_stale_storage_subscriptions.is_empty() |
617 | | { |
618 | 0 | return WakeUpReason::StartStorageSubscriptionsUpdates; |
619 | 0 | } |
620 | 0 |
|
621 | 0 | if *finalized_heads_subscriptions_stale { |
622 | 0 | return WakeUpReason::NotifyFinalizedHeads; |
623 | 0 | } |
624 | | |
625 | 0 | if let Some(previous_best_block) = |
626 | 0 | new_heads_and_runtime_subscriptions_stale.take() |
627 | | { |
628 | 0 | return WakeUpReason::NotifyNewHeadsRuntimeSubscriptions( |
629 | 0 | previous_best_block, |
630 | 0 | ); |
631 | 0 | } |
632 | 0 |
|
633 | 0 | match subscription.next().await { |
634 | 0 | Some(notification) => { |
635 | 0 | WakeUpReason::RuntimeServiceSubscriptionNotification { |
636 | 0 | notification, |
637 | 0 | subscription, |
638 | 0 | pinned_blocks, |
639 | 0 | finalized_and_pruned_lru, |
640 | 0 | current_best_block, |
641 | 0 | new_heads_and_runtime_subscriptions_stale, |
642 | 0 | current_finalized_block, |
643 | 0 | finalized_heads_subscriptions_stale, |
644 | 0 | } |
645 | | } |
646 | 0 | None => WakeUpReason::RuntimeServiceSubscriptionDead, |
647 | | } |
648 | | } |
649 | 0 | RuntimeServiceSubscription::Pending(pending) => { |
650 | 0 | WakeUpReason::RuntimeServiceSubscriptionReady(pending.await) |
651 | | } |
652 | | } |
653 | 0 | } Unexecuted instantiation: _RNCNCINvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background3runpE0s0_0Ba_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0s0_0B1f_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runpE0s0_0Ba_ |
654 | 0 | .or(async { |
655 | 0 | if let Some((request_id_json, stage, request_ty)) = |
656 | 0 | me.multistage_requests_to_advance.pop_front() |
657 | | { |
658 | 0 | WakeUpReason::AdvanceMultiStageRequest { |
659 | 0 | request_id_json, |
660 | 0 | stage, |
661 | 0 | request_ty, |
662 | 0 | } |
663 | | } else { |
664 | 0 | future::pending().await |
665 | | } |
666 | 0 | }) Unexecuted instantiation: _RNCNCINvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background3runpE0s1_0Ba_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0s1_0B1f_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runpE0s1_0Ba_ |
667 | 0 | .or(async { |
668 | 0 | if let Some(event) = me.background_tasks.next().await { |
669 | 0 | WakeUpReason::Event(event) |
670 | | } else { |
671 | 0 | future::pending().await |
672 | | } |
673 | 0 | }) Unexecuted instantiation: _RNCNCINvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background3runpE0s2_0Ba_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0s2_0B1f_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runpE0s2_0Ba_ |
674 | 0 | .or(async { |
675 | 0 | // Pulling new requests is one of the lowest priority tasks, in order to avoid |
676 | 0 | // doing so if the task is overloaded. |
677 | 0 | me.requests_rx.next().await.map_or( |
678 | 0 | WakeUpReason::ForegroundDead, |
679 | 0 | WakeUpReason::IncomingJsonRpcRequest, |
680 | 0 | ) |
681 | 0 | }) Unexecuted instantiation: _RNCNCINvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background3runpE0s3_0Ba_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0s3_0B1f_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runpE0s3_0Ba_ |
682 | 0 | .or(async { |
683 | 0 | (&mut me.next_garbage_collection).await; |
684 | 0 | me.next_garbage_collection = Box::pin(me.platform.sleep(Duration::from_secs(10))); |
685 | 0 | WakeUpReason::GarbageCollection |
686 | 0 | }) Unexecuted instantiation: _RNCNCINvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background3runpE0s4_0Ba_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0s4_0B1f_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runpE0s4_0Ba_ |
687 | 0 | .await |
688 | | }; |
689 | | |
690 | 0 | match wake_up_reason { |
691 | | WakeUpReason::ForegroundDead => { |
692 | | // Service foreground has been destroyed. Stop the background task. |
693 | 0 | return; |
694 | | } |
695 | | |
696 | 0 | WakeUpReason::GarbageCollection => { |
697 | 0 | // Periodically shrink all the shrinkable containers, in order to make sure that |
698 | 0 | // a temporary peak in memory usage doesn't keep memory allocated forever. |
699 | 0 | me.chain_head_follow_subscriptions.shrink_to_fit(); |
700 | 0 | me.all_heads_subscriptions.shrink_to_fit(); |
701 | 0 | me.new_heads_subscriptions.shrink_to_fit(); |
702 | 0 | me.finalized_heads_subscriptions.shrink_to_fit(); |
703 | 0 | me.runtime_version_subscriptions.shrink_to_fit(); |
704 | 0 | me.transactions_subscriptions.shrink_to_fit(); |
705 | 0 | me.legacy_api_stale_storage_subscriptions.shrink_to_fit(); |
706 | 0 | me.multistage_requests_to_advance.shrink_to_fit(); |
707 | 0 | me.block_headers_pending.shrink_to_fit(); |
708 | 0 | me.block_runtimes_pending.shrink_to_fit(); |
709 | 0 | } |
710 | | |
711 | 0 | WakeUpReason::IncomingJsonRpcRequest(request_json) => { |
712 | | // New JSON-RPC request pulled from the channel. |
713 | 0 | let Ok((request_id_json, request_parsed)) = |
714 | 0 | methods::parse_jsonrpc_client_to_server(&request_json) |
715 | | else { |
716 | | // Request has failed to parse. Immediately return an answer. |
717 | 0 | let _ = me |
718 | 0 | .responses_tx |
719 | 0 | .send(parse::build_parse_error_response()) |
720 | 0 | .await; |
721 | 0 | continue; |
722 | | }; |
723 | | |
724 | | // Print a warning for legacy JSON-RPC API functions. |
725 | 0 | match request_parsed { |
726 | | // Legacy API functions. |
727 | | methods::MethodCall::account_nextIndex { .. } |
728 | | | methods::MethodCall::author_hasKey { .. } |
729 | | | methods::MethodCall::author_hasSessionKeys { .. } |
730 | | | methods::MethodCall::author_insertKey { .. } |
731 | | | methods::MethodCall::author_pendingExtrinsics { .. } |
732 | | | methods::MethodCall::author_removeExtrinsic { .. } |
733 | | | methods::MethodCall::author_rotateKeys { .. } |
734 | | | methods::MethodCall::author_submitAndWatchExtrinsic { .. } |
735 | | | methods::MethodCall::author_submitExtrinsic { .. } |
736 | | | methods::MethodCall::author_unwatchExtrinsic { .. } |
737 | | | methods::MethodCall::babe_epochAuthorship { .. } |
738 | | | methods::MethodCall::chain_getBlock { .. } |
739 | | | methods::MethodCall::chain_getBlockHash { .. } |
740 | | | methods::MethodCall::chain_getFinalizedHead { .. } |
741 | | | methods::MethodCall::chain_getHeader { .. } |
742 | | | methods::MethodCall::chain_subscribeAllHeads { .. } |
743 | | | methods::MethodCall::chain_subscribeFinalizedHeads { .. } |
744 | | | methods::MethodCall::chain_subscribeNewHeads { .. } |
745 | | | methods::MethodCall::chain_unsubscribeAllHeads { .. } |
746 | | | methods::MethodCall::chain_unsubscribeFinalizedHeads { .. } |
747 | | | methods::MethodCall::chain_unsubscribeNewHeads { .. } |
748 | | | methods::MethodCall::childstate_getKeys { .. } |
749 | | | methods::MethodCall::childstate_getStorage { .. } |
750 | | | methods::MethodCall::childstate_getStorageHash { .. } |
751 | | | methods::MethodCall::childstate_getStorageSize { .. } |
752 | | | methods::MethodCall::grandpa_roundState { .. } |
753 | | | methods::MethodCall::offchain_localStorageGet { .. } |
754 | | | methods::MethodCall::offchain_localStorageSet { .. } |
755 | | | methods::MethodCall::payment_queryInfo { .. } |
756 | | | methods::MethodCall::state_call { .. } |
757 | | | methods::MethodCall::state_getKeys { .. } |
758 | | | methods::MethodCall::state_getKeysPaged { .. } |
759 | | | methods::MethodCall::state_getMetadata { .. } |
760 | | | methods::MethodCall::state_getPairs { .. } |
761 | | | methods::MethodCall::state_getReadProof { .. } |
762 | | | methods::MethodCall::state_getRuntimeVersion { .. } |
763 | | | methods::MethodCall::state_getStorage { .. } |
764 | | | methods::MethodCall::state_getStorageHash { .. } |
765 | | | methods::MethodCall::state_getStorageSize { .. } |
766 | | | methods::MethodCall::state_queryStorage { .. } |
767 | | | methods::MethodCall::state_queryStorageAt { .. } |
768 | | | methods::MethodCall::state_subscribeRuntimeVersion { .. } |
769 | | | methods::MethodCall::state_subscribeStorage { .. } |
770 | | | methods::MethodCall::state_unsubscribeRuntimeVersion { .. } |
771 | | | methods::MethodCall::state_unsubscribeStorage { .. } |
772 | | | methods::MethodCall::system_accountNextIndex { .. } |
773 | | | methods::MethodCall::system_addReservedPeer { .. } |
774 | | | methods::MethodCall::system_chain { .. } |
775 | | | methods::MethodCall::system_chainType { .. } |
776 | | | methods::MethodCall::system_dryRun { .. } |
777 | | | methods::MethodCall::system_health { .. } |
778 | | | methods::MethodCall::system_localListenAddresses { .. } |
779 | | | methods::MethodCall::system_localPeerId { .. } |
780 | | | methods::MethodCall::system_name { .. } |
781 | | | methods::MethodCall::system_networkState { .. } |
782 | | | methods::MethodCall::system_nodeRoles { .. } |
783 | | | methods::MethodCall::system_peers { .. } |
784 | | | methods::MethodCall::system_properties { .. } |
785 | | | methods::MethodCall::system_removeReservedPeer { .. } |
786 | | | methods::MethodCall::system_version { .. } => { |
787 | 0 | if !me.printed_legacy_json_rpc_warning { |
788 | 0 | me.printed_legacy_json_rpc_warning = true; |
789 | 0 | log!( |
790 | 0 | &me.platform, |
791 | 0 | Warn, |
792 | 0 | &me.log_target, |
793 | 0 | format!( |
794 | 0 | "The JSON-RPC client has just called a JSON-RPC function from \ |
795 | 0 | the legacy JSON-RPC API ({}). Legacy JSON-RPC functions have \ |
796 | 0 | loose semantics and cannot be properly implemented on a light \ |
797 | 0 | client. You are encouraged to use the new JSON-RPC API \ |
798 | 0 | <https://github.com/paritytech/json-rpc-interface-spec/> \ |
799 | 0 | instead. The legacy JSON-RPC API functions will be deprecated \ |
800 | 0 | and removed in the distant future.", |
801 | 0 | request_parsed.name() |
802 | 0 | ) |
803 | 0 | ) |
804 | 0 | } |
805 | | } |
806 | | |
807 | | // Non-legacy-API functions. |
808 | | methods::MethodCall::chainHead_v1_body { .. } |
809 | | | methods::MethodCall::chainHead_v1_call { .. } |
810 | | | methods::MethodCall::chainHead_v1_continue { .. } |
811 | | | methods::MethodCall::chainHead_v1_follow { .. } |
812 | | | methods::MethodCall::chainHead_v1_header { .. } |
813 | | | methods::MethodCall::chainHead_v1_stopOperation { .. } |
814 | | | methods::MethodCall::chainHead_v1_storage { .. } |
815 | | | methods::MethodCall::chainHead_v1_unfollow { .. } |
816 | | | methods::MethodCall::chainHead_v1_unpin { .. } |
817 | | | methods::MethodCall::chainSpec_v1_chainName { .. } |
818 | | | methods::MethodCall::chainSpec_v1_genesisHash { .. } |
819 | | | methods::MethodCall::chainSpec_v1_properties { .. } |
820 | | | methods::MethodCall::rpc_methods { .. } |
821 | | | methods::MethodCall::sudo_unstable_p2pDiscover { .. } |
822 | | | methods::MethodCall::sudo_unstable_version { .. } |
823 | | | methods::MethodCall::transaction_v1_broadcast { .. } |
824 | | | methods::MethodCall::transaction_v1_stop { .. } |
825 | | | methods::MethodCall::transactionWatch_v1_submitAndWatch { .. } |
826 | | | methods::MethodCall::transactionWatch_v1_unwatch { .. } |
827 | | | methods::MethodCall::sudo_network_unstable_watch { .. } |
828 | | | methods::MethodCall::sudo_network_unstable_unwatch { .. } |
829 | 0 | | methods::MethodCall::chainHead_unstable_finalizedDatabase { .. } => {} |
830 | | } |
831 | | |
832 | | // Actual requests handler. |
833 | 0 | match request_parsed { |
834 | | methods::MethodCall::author_pendingExtrinsics {} => { |
835 | | // Because multiple different chains ("chain" in the context of the |
836 | | // public API of smoldot) might share the same transactions service, it |
837 | | // could be possible for chain A to submit a transaction and then for |
838 | | // chain B to read it by calling `author_pendingExtrinsics`. This would |
839 | | // make it possible for the API user of chain A to be able to communicate |
840 | | // with the API user of chain B. While the implications of permitting |
841 | | // this are unclear, it is not a bad idea to prevent this communication |
842 | | // from happening. Consequently, we always return an empty list of |
843 | | // pending extrinsics. |
844 | | // TODO: could store the list of pending transactions in the JSON-RPC service instead |
845 | 0 | let _ = me |
846 | 0 | .responses_tx |
847 | 0 | .send( |
848 | 0 | methods::Response::author_pendingExtrinsics(Vec::new()) |
849 | 0 | .to_json_response(request_id_json), |
850 | 0 | ) |
851 | 0 | .await; |
852 | | } |
853 | | |
854 | 0 | methods::MethodCall::author_submitExtrinsic { transaction } => { |
855 | 0 | // Note that this function is misnamed. It should really be called |
856 | 0 | // "author_submitTransaction". |
857 | 0 |
|
858 | 0 | // In Substrate, `author_submitExtrinsic` returns the hash of the |
859 | 0 | // transaction. It is unclear whether it has to actually be the hash of |
860 | 0 | // the transaction or if it could be any opaque value. Additionally, there |
861 | 0 | // isn't any other JSON-RPC method that accepts as parameter the value |
862 | 0 | // returned here. When in doubt, we return the hash as well. |
863 | 0 |
|
864 | 0 | let mut hash_context = blake2_rfc::blake2b::Blake2b::new(32); |
865 | 0 | hash_context.update(&transaction.0); |
866 | 0 | let mut transaction_hash: [u8; 32] = Default::default(); |
867 | 0 | transaction_hash.copy_from_slice(hash_context.finalize().as_bytes()); |
868 | 0 | me.transactions_service |
869 | 0 | .submit_transaction(transaction.0) |
870 | 0 | .await; |
871 | 0 | let _ = me |
872 | 0 | .responses_tx |
873 | 0 | .send( |
874 | 0 | methods::Response::author_submitExtrinsic(methods::HashHexString( |
875 | 0 | transaction_hash, |
876 | 0 | )) |
877 | 0 | .to_json_response(request_id_json), |
878 | 0 | ) |
879 | 0 | .await; |
880 | | } |
881 | | |
882 | 0 | methods::MethodCall::author_submitAndWatchExtrinsic { transaction } => { |
883 | 0 | let subscription_id = { |
884 | 0 | let mut subscription_id = [0u8; 32]; |
885 | 0 | me.randomness.fill_bytes(&mut subscription_id); |
886 | 0 | bs58::encode(subscription_id).into_string() |
887 | | }; |
888 | | |
889 | 0 | let mut transaction_updates = Box::pin( |
890 | 0 | me.transactions_service |
891 | 0 | .submit_and_watch_transaction(transaction.0, 16, true) |
892 | 0 | .await, |
893 | | ); |
894 | | |
895 | 0 | let _prev_value = me.transactions_subscriptions.insert( |
896 | 0 | subscription_id.clone(), |
897 | 0 | TransactionWatch { |
898 | 0 | included_block: None, |
899 | 0 | num_broadcasted_peers: 0, |
900 | 0 | ty: TransactionWatchTy::Legacy, |
901 | 0 | }, |
902 | 0 | ); |
903 | 0 | debug_assert!(_prev_value.is_none()); |
904 | | |
905 | 0 | let _ = me |
906 | 0 | .responses_tx |
907 | 0 | .send( |
908 | 0 | methods::Response::author_submitAndWatchExtrinsic(Cow::Borrowed( |
909 | 0 | &subscription_id, |
910 | 0 | )) |
911 | 0 | .to_json_response(request_id_json), |
912 | 0 | ) |
913 | 0 | .await; |
914 | | |
915 | | // Push a task that will generate an event whenever the transactions |
916 | | // service sends a notification. |
917 | 0 | me.background_tasks.push(Box::pin(async move { |
918 | 0 | let Some(status) = transaction_updates.as_mut().next().await else { |
919 | 0 | unreachable!() |
920 | | }; |
921 | 0 | Event::TransactionEvent { |
922 | 0 | subscription_id, |
923 | 0 | event: status, |
924 | 0 | watcher: transaction_updates, |
925 | 0 | } |
926 | 0 | })); Unexecuted instantiation: _RNCNCINvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background3runpE0s5_0Ba_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0s5_0B1f_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runpE0s5_0Ba_ |
927 | | } |
928 | | |
929 | 0 | methods::MethodCall::author_unwatchExtrinsic { subscription } => { |
930 | 0 | let exists = me |
931 | 0 | .transactions_subscriptions |
932 | 0 | .get(&*subscription) |
933 | 0 | .map_or(false, |sub| matches!(sub.ty, TransactionWatchTy::Legacy)); Unexecuted instantiation: _RNCNCINvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background3runpE0s6_0Ba_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0s6_0B1f_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runpE0s6_0Ba_ |
934 | 0 | if exists { |
935 | 0 | me.transactions_subscriptions.remove(&*subscription); |
936 | 0 | } |
937 | 0 | let _ = me |
938 | 0 | .responses_tx |
939 | 0 | .send( |
940 | 0 | methods::Response::author_unwatchExtrinsic(exists) |
941 | 0 | .to_json_response(request_id_json), |
942 | 0 | ) |
943 | 0 | .await; |
944 | | |
945 | | // Note that this doesn't remove the transaction from the transactions |
946 | | // service. |
947 | | // We don't cancel the task in `background_tasks` that will |
948 | | // generate events about this transaction. Instead, the task will stop |
949 | | // renewing itself the next time it generates a notification. |
950 | | } |
951 | | |
952 | 0 | methods::MethodCall::chain_getBlock { hash } => { |
953 | 0 | // Because this request requires asynchronous operations, we push it |
954 | 0 | // to a list of "multi-stage requests" that are processed later. |
955 | 0 | me.multistage_requests_to_advance.push_back(( |
956 | 0 | request_id_json.to_owned(), |
957 | 0 | match hash { |
958 | 0 | Some(methods::HashHexString(block_hash)) => { |
959 | 0 | MultiStageRequestStage::BlockHashKnown { block_hash } |
960 | | } |
961 | 0 | None => MultiStageRequestStage::BlockHashNotKnown, |
962 | | }, |
963 | 0 | MultiStageRequestTy::ChainGetBlock, |
964 | | )); |
965 | | } |
966 | | |
967 | 0 | methods::MethodCall::chain_getBlockHash { height } => { |
968 | 0 | // TODO: maybe store values in cache? |
969 | 0 | match height { |
970 | | Some(0) => { |
971 | | // Block 0 is important for the JSON-RPC client to be able |
972 | | // to generate transactions for the chain. Make sure that it is |
973 | | // always queriable |
974 | 0 | let _ = me |
975 | 0 | .responses_tx |
976 | 0 | .send( |
977 | 0 | methods::Response::chain_getBlockHash( |
978 | 0 | methods::HashHexString(me.genesis_block_hash), |
979 | 0 | ) |
980 | 0 | .to_json_response(request_id_json), |
981 | 0 | ) |
982 | 0 | .await; |
983 | | } |
984 | 0 | None => { |
985 | 0 | // Because finding the best block might require an asynchronous |
986 | 0 | // operation, we push it to a list of "multi-stage requests" |
987 | 0 | // that are processed later. |
988 | 0 | me.multistage_requests_to_advance.push_back(( |
989 | 0 | request_id_json.to_owned(), |
990 | 0 | MultiStageRequestStage::BlockHashNotKnown, |
991 | 0 | MultiStageRequestTy::ChainGetBestBlockHash, |
992 | 0 | )); |
993 | 0 | } |
994 | | Some(_) => { |
995 | | // TODO: look into some list of known blocks |
996 | | |
997 | | // While could ask a full node for the block with a specific |
998 | | // number, there is absolutely no way to verify the answer of |
999 | | // the full node. |
1000 | 0 | let _ = me |
1001 | 0 | .responses_tx |
1002 | 0 | .send(parse::build_success_response(request_id_json, "null")) |
1003 | 0 | .await; |
1004 | | } |
1005 | | } |
1006 | | } |
1007 | | |
1008 | | methods::MethodCall::chain_getFinalizedHead {} => { |
1009 | | if let RuntimeServiceSubscription::Active { |
1010 | 0 | current_finalized_block, |
1011 | | .. |
1012 | 0 | } = &me.runtime_service_subscription |
1013 | | { |
1014 | | // The finalized block hash is known. Send back an answer immediately. |
1015 | 0 | let _ = me |
1016 | 0 | .responses_tx |
1017 | 0 | .send( |
1018 | 0 | methods::Response::chain_getFinalizedHead( |
1019 | 0 | methods::HashHexString(*current_finalized_block), |
1020 | 0 | ) |
1021 | 0 | .to_json_response(request_id_json), |
1022 | 0 | ) |
1023 | 0 | .await; |
1024 | 0 | } else { |
1025 | 0 | // Finalized block hash not known yet. Push the request to a list of |
1026 | 0 | // requests that will be answered once it is known. |
1027 | 0 | me.pending_get_finalized_head |
1028 | 0 | .push(request_id_json.to_owned()); |
1029 | 0 | } |
1030 | | } |
1031 | | |
1032 | 0 | methods::MethodCall::chain_getHeader { hash } => { |
1033 | 0 | // Because this request requires asynchronous operations, we push it |
1034 | 0 | // to a list of "multi-stage requests" that are processed later. |
1035 | 0 | me.multistage_requests_to_advance.push_back(( |
1036 | 0 | request_id_json.to_owned(), |
1037 | 0 | match hash { |
1038 | 0 | Some(methods::HashHexString(block_hash)) => { |
1039 | 0 | MultiStageRequestStage::BlockHashKnown { block_hash } |
1040 | | } |
1041 | 0 | None => MultiStageRequestStage::BlockHashNotKnown, |
1042 | | }, |
1043 | 0 | MultiStageRequestTy::ChainGetHeader, |
1044 | | )); |
1045 | | } |
1046 | | |
1047 | | methods::MethodCall::chain_subscribeAllHeads {} => { |
1048 | 0 | let subscription_id = { |
1049 | 0 | let mut subscription_id = [0u8; 32]; |
1050 | 0 | me.randomness.fill_bytes(&mut subscription_id); |
1051 | 0 | bs58::encode(subscription_id).into_string() |
1052 | 0 | }; |
1053 | 0 |
|
1054 | 0 | let _ = me |
1055 | 0 | .responses_tx |
1056 | 0 | .send( |
1057 | 0 | methods::Response::chain_subscribeAllHeads(Cow::Borrowed( |
1058 | 0 | &subscription_id, |
1059 | 0 | )) |
1060 | 0 | .to_json_response(request_id_json), |
1061 | 0 | ) |
1062 | 0 | .await; |
1063 | | |
1064 | 0 | let _was_inserted = me.all_heads_subscriptions.insert(subscription_id); |
1065 | 0 | debug_assert!(_was_inserted); |
1066 | | |
1067 | | // Note that, contrary to other similar subscriptions, we don't send |
1068 | | // any notification immediately. |
1069 | | } |
1070 | | |
1071 | | methods::MethodCall::chain_subscribeFinalizedHeads {} => { |
1072 | 0 | let subscription_id = { |
1073 | 0 | let mut subscription_id = [0u8; 32]; |
1074 | 0 | me.randomness.fill_bytes(&mut subscription_id); |
1075 | 0 | bs58::encode(subscription_id).into_string() |
1076 | 0 | }; |
1077 | 0 |
|
1078 | 0 | let _ = me |
1079 | 0 | .responses_tx |
1080 | 0 | .send( |
1081 | 0 | methods::Response::chain_subscribeFinalizedHeads(Cow::Borrowed( |
1082 | 0 | &subscription_id, |
1083 | 0 | )) |
1084 | 0 | .to_json_response(request_id_json), |
1085 | 0 | ) |
1086 | 0 | .await; |
1087 | | |
1088 | | // If the finalized block hash is known, send a notification immediately. |
1089 | | // Otherwise, one will be sent once the finalized block hash is known. |
1090 | | if let RuntimeServiceSubscription::Active { |
1091 | 0 | current_finalized_block, |
1092 | 0 | pinned_blocks, |
1093 | | .. |
1094 | 0 | } = &me.runtime_service_subscription |
1095 | | { |
1096 | 0 | match methods::Header::from_scale_encoded_header( |
1097 | 0 | &pinned_blocks |
1098 | 0 | .get(current_finalized_block) |
1099 | 0 | .unwrap() |
1100 | 0 | .scale_encoded_header, |
1101 | 0 | me.runtime_service.block_number_bytes(), |
1102 | 0 | ) { |
1103 | 0 | Ok(h) => { |
1104 | 0 | let _ = me |
1105 | 0 | .responses_tx |
1106 | 0 | .send( |
1107 | 0 | methods::ServerToClient::chain_finalizedHead { |
1108 | 0 | subscription: Cow::Borrowed(&subscription_id), |
1109 | 0 | result: h, |
1110 | 0 | } |
1111 | 0 | .to_json_request_object_parameters(None), |
1112 | 0 | ) |
1113 | 0 | .await; |
1114 | | } |
1115 | 0 | Err(error) => { |
1116 | 0 | log!( |
1117 | 0 | &me.platform, |
1118 | 0 | Warn, |
1119 | 0 | &me.log_target, |
1120 | 0 | format!( |
1121 | 0 | "`chain_subscribeFinalizedHeads` subscription has \ |
1122 | 0 | skipped block due to undecodable header. Hash: {}. \ |
1123 | 0 | Error: {}", |
1124 | 0 | HashDisplay(current_finalized_block), |
1125 | 0 | error |
1126 | 0 | ) |
1127 | 0 | ); |
1128 | 0 | } |
1129 | | } |
1130 | 0 | } |
1131 | | |
1132 | 0 | let _was_inserted = |
1133 | 0 | me.finalized_heads_subscriptions.insert(subscription_id); |
1134 | 0 | debug_assert!(_was_inserted); |
1135 | | } |
1136 | | |
1137 | | methods::MethodCall::chain_subscribeNewHeads {} => { |
1138 | 0 | let subscription_id = { |
1139 | 0 | let mut subscription_id = [0u8; 32]; |
1140 | 0 | me.randomness.fill_bytes(&mut subscription_id); |
1141 | 0 | bs58::encode(subscription_id).into_string() |
1142 | 0 | }; |
1143 | 0 |
|
1144 | 0 | let _ = me |
1145 | 0 | .responses_tx |
1146 | 0 | .send( |
1147 | 0 | methods::Response::chain_subscribeNewHeads(Cow::Borrowed( |
1148 | 0 | &subscription_id, |
1149 | 0 | )) |
1150 | 0 | .to_json_response(request_id_json), |
1151 | 0 | ) |
1152 | 0 | .await; |
1153 | | |
1154 | | // If the best block hash is known, send a notification immediately. |
1155 | | // Otherwise, one will be sent once the best block hash is known. |
1156 | | if let RuntimeServiceSubscription::Active { |
1157 | 0 | current_best_block, |
1158 | 0 | pinned_blocks, |
1159 | | .. |
1160 | 0 | } = &me.runtime_service_subscription |
1161 | | { |
1162 | 0 | match methods::Header::from_scale_encoded_header( |
1163 | 0 | &pinned_blocks |
1164 | 0 | .get(current_best_block) |
1165 | 0 | .unwrap() |
1166 | 0 | .scale_encoded_header, |
1167 | 0 | me.runtime_service.block_number_bytes(), |
1168 | 0 | ) { |
1169 | 0 | Ok(h) => { |
1170 | 0 | let _ = me |
1171 | 0 | .responses_tx |
1172 | 0 | .send( |
1173 | 0 | methods::ServerToClient::chain_newHead { |
1174 | 0 | subscription: Cow::Borrowed(&subscription_id), |
1175 | 0 | result: h, |
1176 | 0 | } |
1177 | 0 | .to_json_request_object_parameters(None), |
1178 | 0 | ) |
1179 | 0 | .await; |
1180 | | } |
1181 | 0 | Err(error) => { |
1182 | 0 | log!( |
1183 | 0 | &me.platform, |
1184 | 0 | Warn, |
1185 | 0 | &me.log_target, |
1186 | 0 | format!( |
1187 | 0 | "`chain_subscribeNewHeads` subscription has \ |
1188 | 0 | skipped block due to undecodable header. Hash: {}. \ |
1189 | 0 | Error: {}", |
1190 | 0 | HashDisplay(current_best_block), |
1191 | 0 | error |
1192 | 0 | ) |
1193 | 0 | ); |
1194 | 0 | } |
1195 | | } |
1196 | 0 | } |
1197 | | |
1198 | 0 | let _was_inserted = me.new_heads_subscriptions.insert(subscription_id); |
1199 | 0 | debug_assert!(_was_inserted); |
1200 | | } |
1201 | | |
1202 | 0 | methods::MethodCall::chain_unsubscribeAllHeads { subscription } => { |
1203 | 0 | let exists = me.all_heads_subscriptions.remove(&subscription); |
1204 | 0 | let _ = me |
1205 | 0 | .responses_tx |
1206 | 0 | .send( |
1207 | 0 | methods::Response::chain_unsubscribeAllHeads(exists) |
1208 | 0 | .to_json_response(request_id_json), |
1209 | 0 | ) |
1210 | 0 | .await; |
1211 | | } |
1212 | | |
1213 | 0 | methods::MethodCall::chain_unsubscribeFinalizedHeads { subscription } => { |
1214 | 0 | let exists = me.finalized_heads_subscriptions.remove(&subscription); |
1215 | 0 | let _ = me |
1216 | 0 | .responses_tx |
1217 | 0 | .send( |
1218 | 0 | methods::Response::chain_unsubscribeFinalizedHeads(exists) |
1219 | 0 | .to_json_response(request_id_json), |
1220 | 0 | ) |
1221 | 0 | .await; |
1222 | | } |
1223 | | |
1224 | 0 | methods::MethodCall::chain_unsubscribeNewHeads { subscription } => { |
1225 | 0 | let exists = me.new_heads_subscriptions.remove(&subscription); |
1226 | 0 | let _ = me |
1227 | 0 | .responses_tx |
1228 | 0 | .send( |
1229 | 0 | methods::Response::chain_unsubscribeNewHeads(exists) |
1230 | 0 | .to_json_response(request_id_json), |
1231 | 0 | ) |
1232 | 0 | .await; |
1233 | | } |
1234 | | |
1235 | | methods::MethodCall::payment_queryInfo { |
1236 | 0 | extrinsic: methods::HexString(extrinsic), |
1237 | 0 | hash, |
1238 | 0 | } => { |
1239 | 0 | // Because this request requires asynchronous operations, we push it |
1240 | 0 | // to a list of "multi-stage requests" that are processed later. |
1241 | 0 | me.multistage_requests_to_advance.push_back(( |
1242 | 0 | request_id_json.to_owned(), |
1243 | 0 | match hash { |
1244 | 0 | Some(methods::HashHexString(block_hash)) => { |
1245 | 0 | MultiStageRequestStage::BlockHashKnown { block_hash } |
1246 | | } |
1247 | 0 | None => MultiStageRequestStage::BlockHashNotKnown, |
1248 | | }, |
1249 | 0 | MultiStageRequestTy::PaymentQueryInfo { extrinsic }, |
1250 | | )); |
1251 | | } |
1252 | | |
1253 | | methods::MethodCall::rpc_methods {} => { |
1254 | 0 | let _ = me |
1255 | 0 | .responses_tx |
1256 | 0 | .send( |
1257 | 0 | methods::Response::rpc_methods(methods::RpcMethods { |
1258 | 0 | methods: methods::MethodCall::method_names() |
1259 | 0 | .map(|n| n.into()) Unexecuted instantiation: _RNCNCINvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background3runpE0s7_0Ba_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0s7_0B1f_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runpE0s7_0Ba_ |
1260 | 0 | .collect(), |
1261 | 0 | }) |
1262 | 0 | .to_json_response(request_id_json), |
1263 | 0 | ) |
1264 | 0 | .await; |
1265 | | } |
1266 | | |
1267 | | methods::MethodCall::state_call { |
1268 | 0 | name, |
1269 | 0 | parameters, |
1270 | 0 | hash, |
1271 | 0 | } => { |
1272 | 0 | // Because this request requires asynchronous operations, we push it |
1273 | 0 | // to a list of "multi-stage requests" that are processed later. |
1274 | 0 | me.multistage_requests_to_advance.push_back(( |
1275 | 0 | request_id_json.to_owned(), |
1276 | 0 | match hash { |
1277 | 0 | Some(methods::HashHexString(block_hash)) => { |
1278 | 0 | MultiStageRequestStage::BlockHashKnown { block_hash } |
1279 | | } |
1280 | 0 | None => MultiStageRequestStage::BlockHashNotKnown, |
1281 | | }, |
1282 | 0 | MultiStageRequestTy::StateCall { |
1283 | 0 | name: name.into_owned(), |
1284 | 0 | parameters: parameters.0, |
1285 | 0 | }, |
1286 | | )); |
1287 | | } |
1288 | | |
1289 | | methods::MethodCall::state_getKeys { |
1290 | 0 | prefix: methods::HexString(prefix), |
1291 | 0 | hash, |
1292 | 0 | } => { |
1293 | 0 | // Because this request requires asynchronous operations, we push it |
1294 | 0 | // to a list of "multi-stage requests" that are processed later. |
1295 | 0 | me.multistage_requests_to_advance.push_back(( |
1296 | 0 | request_id_json.to_owned(), |
1297 | 0 | match hash { |
1298 | 0 | Some(methods::HashHexString(block_hash)) => { |
1299 | 0 | MultiStageRequestStage::BlockHashKnown { block_hash } |
1300 | | } |
1301 | 0 | None => MultiStageRequestStage::BlockHashNotKnown, |
1302 | | }, |
1303 | 0 | MultiStageRequestTy::StateGetKeys { prefix }, |
1304 | | )); |
1305 | | } |
1306 | | |
1307 | | methods::MethodCall::state_getKeysPaged { |
1308 | 0 | prefix, |
1309 | 0 | count, |
1310 | 0 | start_key, |
1311 | 0 | hash, |
1312 | 0 | } => { |
1313 | 0 | // Because this request requires asynchronous operations, we push it |
1314 | 0 | // to a list of "multi-stage requests" that are processed later. |
1315 | 0 | me.multistage_requests_to_advance.push_back(( |
1316 | 0 | request_id_json.to_owned(), |
1317 | 0 | match hash { |
1318 | 0 | Some(methods::HashHexString(block_hash)) => { |
1319 | 0 | MultiStageRequestStage::BlockHashKnown { block_hash } |
1320 | | } |
1321 | 0 | None => MultiStageRequestStage::BlockHashNotKnown, |
1322 | | }, |
1323 | 0 | MultiStageRequestTy::StateGetKeysPaged { |
1324 | 0 | prefix: prefix.map_or(Vec::new(), |p| p.0), Unexecuted instantiation: _RNCNCINvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background3runpE0s8_0Ba_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0s8_0B1f_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runpE0s8_0Ba_ |
1325 | 0 | count, |
1326 | 0 | start_key: start_key.map(|p| p.0), Unexecuted instantiation: _RNCNCINvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background3runpE0s9_0Ba_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0s9_0B1f_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runpE0s9_0Ba_ |
1327 | 0 | }, |
1328 | | )); |
1329 | | } |
1330 | | |
1331 | 0 | methods::MethodCall::state_queryStorageAt { keys, at } => { |
1332 | 0 | // Because this request requires asynchronous operations, we push it |
1333 | 0 | // to a list of "multi-stage requests" that are processed later. |
1334 | 0 | me.multistage_requests_to_advance.push_back(( |
1335 | 0 | request_id_json.to_owned(), |
1336 | 0 | match at { |
1337 | 0 | Some(methods::HashHexString(block_hash)) => { |
1338 | 0 | MultiStageRequestStage::BlockHashKnown { block_hash } |
1339 | | } |
1340 | 0 | None => MultiStageRequestStage::BlockHashNotKnown, |
1341 | | }, |
1342 | 0 | MultiStageRequestTy::StateQueryStorageAt { keys }, |
1343 | | )); |
1344 | | } |
1345 | | |
1346 | 0 | methods::MethodCall::state_getMetadata { hash } => { |
1347 | 0 | // Because this request requires asynchronous operations, we push it |
1348 | 0 | // to a list of "multi-stage requests" that are processed later. |
1349 | 0 | me.multistage_requests_to_advance.push_back(( |
1350 | 0 | request_id_json.to_owned(), |
1351 | 0 | match hash { |
1352 | 0 | Some(methods::HashHexString(block_hash)) => { |
1353 | 0 | MultiStageRequestStage::BlockHashKnown { block_hash } |
1354 | | } |
1355 | 0 | None => MultiStageRequestStage::BlockHashNotKnown, |
1356 | | }, |
1357 | 0 | MultiStageRequestTy::StateGetMetadata, |
1358 | | )); |
1359 | | } |
1360 | | |
1361 | 0 | methods::MethodCall::state_getStorage { key, hash } => { |
1362 | 0 | // Because this request requires asynchronous operations, we push it |
1363 | 0 | // to a list of "multi-stage requests" that are processed later. |
1364 | 0 | me.multistage_requests_to_advance.push_back(( |
1365 | 0 | request_id_json.to_owned(), |
1366 | 0 | match hash { |
1367 | 0 | Some(methods::HashHexString(block_hash)) => { |
1368 | 0 | MultiStageRequestStage::BlockHashKnown { block_hash } |
1369 | | } |
1370 | 0 | None => MultiStageRequestStage::BlockHashNotKnown, |
1371 | | }, |
1372 | 0 | MultiStageRequestTy::StateGetStorage { key: key.0 }, |
1373 | | )); |
1374 | | } |
1375 | | |
1376 | 0 | methods::MethodCall::state_getRuntimeVersion { at } => { |
1377 | 0 | // Because this request requires asynchronous operations, we push it |
1378 | 0 | // to a list of "multi-stage requests" that are processed later. |
1379 | 0 | me.multistage_requests_to_advance.push_back(( |
1380 | 0 | request_id_json.to_owned(), |
1381 | 0 | match at { |
1382 | 0 | Some(methods::HashHexString(block_hash)) => { |
1383 | 0 | MultiStageRequestStage::BlockHashKnown { block_hash } |
1384 | | } |
1385 | 0 | None => MultiStageRequestStage::BlockHashNotKnown, |
1386 | | }, |
1387 | 0 | MultiStageRequestTy::StateGetRuntimeVersion, |
1388 | | )); |
1389 | | } |
1390 | | |
1391 | | methods::MethodCall::state_subscribeRuntimeVersion {} => { |
1392 | 0 | let subscription_id = { |
1393 | 0 | let mut subscription_id = [0u8; 32]; |
1394 | 0 | me.randomness.fill_bytes(&mut subscription_id); |
1395 | 0 | bs58::encode(subscription_id).into_string() |
1396 | 0 | }; |
1397 | 0 |
|
1398 | 0 | let _ = me |
1399 | 0 | .responses_tx |
1400 | 0 | .send( |
1401 | 0 | methods::Response::state_subscribeRuntimeVersion(Cow::Borrowed( |
1402 | 0 | &subscription_id, |
1403 | 0 | )) |
1404 | 0 | .to_json_response(request_id_json), |
1405 | 0 | ) |
1406 | 0 | .await; |
1407 | | |
1408 | | // If the finalized block runtime is known, immediately send back |
1409 | | // a notification. |
1410 | | if let RuntimeServiceSubscription::Active { |
1411 | 0 | current_best_block, |
1412 | 0 | pinned_blocks, |
1413 | | .. |
1414 | 0 | } = &me.runtime_service_subscription |
1415 | | { |
1416 | | // TODO: we don't send None in case of error; remove the Option altogether |
1417 | 0 | if let Ok(runtime_version) = &*pinned_blocks |
1418 | 0 | .get(current_best_block) |
1419 | 0 | .unwrap() |
1420 | 0 | .runtime_version |
1421 | | { |
1422 | 0 | let _ = me |
1423 | 0 | .responses_tx |
1424 | 0 | .send( |
1425 | 0 | methods::ServerToClient::state_runtimeVersion { |
1426 | 0 | subscription: Cow::Borrowed(&subscription_id), |
1427 | 0 | result: Some(convert_runtime_version_legacy( |
1428 | 0 | runtime_version, |
1429 | 0 | )), |
1430 | 0 | } |
1431 | 0 | .to_json_request_object_parameters(None), |
1432 | 0 | ) |
1433 | 0 | .await; |
1434 | 0 | } |
1435 | 0 | } |
1436 | | |
1437 | 0 | let _was_inserted = |
1438 | 0 | me.runtime_version_subscriptions.insert(subscription_id); |
1439 | 0 | debug_assert!(_was_inserted); |
1440 | | } |
1441 | | |
1442 | 0 | methods::MethodCall::state_subscribeStorage { list } => { |
1443 | 0 | // TODO: limit the size of `list` to avoid DoS attacks; this is out of scope of this module and should be done "externally" |
1444 | 0 | if list.is_empty() { |
1445 | | // When the list of keys is empty, that means we want to subscribe |
1446 | | // to *all* storage changes. It is not possible to reasonably |
1447 | | // implement this in a light client. |
1448 | 0 | let _ = me |
1449 | 0 | .responses_tx |
1450 | 0 | .send(parse::build_error_response( |
1451 | 0 | request_id_json, |
1452 | 0 | parse::ErrorResponse::ServerError( |
1453 | 0 | -32000, |
1454 | 0 | "Subscribing to all storage changes isn't supported", |
1455 | 0 | ), |
1456 | 0 | None, |
1457 | 0 | )) |
1458 | 0 | .await; |
1459 | 0 | continue; |
1460 | 0 | } |
1461 | 0 |
|
1462 | 0 | let subscription_id = Arc::<str>::from({ |
1463 | 0 | let mut subscription_id = [0u8; 32]; |
1464 | 0 | me.randomness.fill_bytes(&mut subscription_id); |
1465 | 0 | bs58::encode(subscription_id).into_string() |
1466 | 0 | }); |
1467 | | |
1468 | 0 | for key in list { |
1469 | 0 | let _was_inserted = me |
1470 | 0 | .legacy_api_storage_subscriptions_by_key |
1471 | 0 | .insert((key.0.clone(), subscription_id.clone())); |
1472 | 0 | debug_assert!(_was_inserted); |
1473 | 0 | let _was_inserted = me |
1474 | 0 | .legacy_api_storage_subscriptions |
1475 | 0 | .insert((subscription_id.clone(), key.0)); |
1476 | 0 | debug_assert!(_was_inserted); |
1477 | | } |
1478 | | |
1479 | | // The subscription is inserted in a list of "stale" subscriptions. |
1480 | | // It will be picked up as soon as possible. |
1481 | 0 | let _was_inserted = me |
1482 | 0 | .legacy_api_stale_storage_subscriptions |
1483 | 0 | .insert(subscription_id.clone()); |
1484 | 0 | debug_assert!(_was_inserted); |
1485 | | |
1486 | 0 | let _ = me |
1487 | 0 | .responses_tx |
1488 | 0 | .send( |
1489 | 0 | methods::Response::state_subscribeStorage(Cow::Borrowed( |
1490 | 0 | &*subscription_id, |
1491 | 0 | )) |
1492 | 0 | .to_json_response(request_id_json), |
1493 | 0 | ) |
1494 | 0 | .await; |
1495 | | } |
1496 | | |
1497 | 0 | methods::MethodCall::state_unsubscribeRuntimeVersion { subscription } => { |
1498 | 0 | let exists = me.runtime_version_subscriptions.remove(&*subscription); |
1499 | 0 | let _ = me |
1500 | 0 | .responses_tx |
1501 | 0 | .send( |
1502 | 0 | methods::Response::state_unsubscribeRuntimeVersion(exists) |
1503 | 0 | .to_json_response(request_id_json), |
1504 | 0 | ) |
1505 | 0 | .await; |
1506 | | } |
1507 | | |
1508 | 0 | methods::MethodCall::state_unsubscribeStorage { subscription } => { |
1509 | 0 | let subscription = Arc::<str>::from(&*subscription); |
1510 | | |
1511 | | // Remove the subscription from the state. This is a bit complicated due |
1512 | | // to the use of `BTreeSet`s. |
1513 | 0 | let subscribed_keys = { |
1514 | 0 | let mut after = me |
1515 | 0 | .legacy_api_storage_subscriptions |
1516 | 0 | .split_off(&(subscription.clone(), Vec::new())); |
1517 | 0 | if let Some(first_entry_after) = |
1518 | 0 | after.iter().find(|(s, _)| *s != subscription).cloned() Unexecuted instantiation: _RNCNCINvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background3runpE0sa_0Ba_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sa_0B1f_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runpE0sa_0Ba_ |
1519 | | // TODO: O(n) ^ |
1520 | 0 | { |
1521 | 0 | me.legacy_api_storage_subscriptions |
1522 | 0 | .append(&mut after.split_off(&first_entry_after)); |
1523 | 0 | } |
1524 | 0 | after |
1525 | 0 | }; |
1526 | 0 | let exists = !subscribed_keys.is_empty(); |
1527 | 0 | for (_, key) in subscribed_keys { |
1528 | 0 | let _was_removed = me |
1529 | 0 | .legacy_api_storage_subscriptions_by_key |
1530 | 0 | .remove(&(key, subscription.clone())); |
1531 | 0 | debug_assert!(_was_removed); |
1532 | | } |
1533 | | |
1534 | 0 | let _ = me |
1535 | 0 | .legacy_api_stale_storage_subscriptions |
1536 | 0 | .remove(&subscription); |
1537 | 0 |
|
1538 | 0 | let _ = me |
1539 | 0 | .responses_tx |
1540 | 0 | .send( |
1541 | 0 | methods::Response::state_unsubscribeStorage(exists) |
1542 | 0 | .to_json_response(request_id_json), |
1543 | 0 | ) |
1544 | 0 | .await; |
1545 | | } |
1546 | | |
1547 | 0 | methods::MethodCall::system_accountNextIndex { account } => { |
1548 | 0 | // Because this request requires asynchronous operations, we push it |
1549 | 0 | // to a list of "multi-stage requests" that are processed later. |
1550 | 0 | me.multistage_requests_to_advance.push_back(( |
1551 | 0 | request_id_json.to_owned(), |
1552 | 0 | MultiStageRequestStage::BlockHashNotKnown, |
1553 | 0 | MultiStageRequestTy::SystemAccountNextIndex { |
1554 | 0 | account_id: account.0, |
1555 | 0 | }, |
1556 | 0 | )); |
1557 | 0 | } |
1558 | | |
1559 | | methods::MethodCall::system_chain {} => { |
1560 | 0 | let _ = me |
1561 | 0 | .responses_tx |
1562 | 0 | .send( |
1563 | 0 | methods::Response::system_chain((&me.chain_name).into()) |
1564 | 0 | .to_json_response(request_id_json), |
1565 | 0 | ) |
1566 | 0 | .await; |
1567 | | } |
1568 | | |
1569 | | methods::MethodCall::system_chainType {} => { |
1570 | 0 | let _ = me |
1571 | 0 | .responses_tx |
1572 | 0 | .send( |
1573 | 0 | methods::Response::system_chainType((&me.chain_ty).into()) |
1574 | 0 | .to_json_response(request_id_json), |
1575 | 0 | ) |
1576 | 0 | .await; |
1577 | | } |
1578 | | |
1579 | | methods::MethodCall::system_health {} => { |
1580 | 0 | let _ = me |
1581 | 0 | .responses_tx |
1582 | 0 | .send( |
1583 | 0 | methods::Response::system_health(methods::SystemHealth { |
1584 | 0 | is_syncing: !me |
1585 | 0 | .runtime_service |
1586 | 0 | .is_near_head_of_chain_heuristic() |
1587 | 0 | .await, |
1588 | | peers: u64::try_from( |
1589 | 0 | me.sync_service.syncing_peers().await.len(), |
1590 | 0 | ) |
1591 | 0 | .unwrap_or(u64::MAX), |
1592 | 0 | should_have_peers: me.chain_is_live, |
1593 | 0 | }) |
1594 | 0 | .to_json_response(request_id_json), |
1595 | | ) |
1596 | 0 | .await; |
1597 | | } |
1598 | | |
1599 | | methods::MethodCall::system_localListenAddresses {} => { |
1600 | | // Light client never listens on any address. |
1601 | 0 | let _ = me |
1602 | 0 | .responses_tx |
1603 | 0 | .send( |
1604 | 0 | methods::Response::system_localListenAddresses(Vec::new()) |
1605 | 0 | .to_json_response(request_id_json), |
1606 | 0 | ) |
1607 | 0 | .await; |
1608 | | } |
1609 | | |
1610 | | methods::MethodCall::system_name {} => { |
1611 | 0 | let _ = me |
1612 | 0 | .responses_tx |
1613 | 0 | .send( |
1614 | 0 | methods::Response::system_name((&me.system_name).into()) |
1615 | 0 | .to_json_response(request_id_json), |
1616 | 0 | ) |
1617 | 0 | .await; |
1618 | | } |
1619 | | |
1620 | | methods::MethodCall::system_nodeRoles {} => { |
1621 | 0 | let _ = me |
1622 | 0 | .responses_tx |
1623 | 0 | .send( |
1624 | 0 | methods::Response::system_nodeRoles(Cow::Borrowed(&[ |
1625 | 0 | methods::NodeRole::Light, |
1626 | 0 | ])) |
1627 | 0 | .to_json_response(request_id_json), |
1628 | 0 | ) |
1629 | 0 | .await; |
1630 | | } |
1631 | | |
1632 | | methods::MethodCall::system_peers {} => { |
1633 | 0 | let _ = me |
1634 | 0 | .responses_tx |
1635 | 0 | .send( |
1636 | 0 | methods::Response::system_peers( |
1637 | 0 | me.sync_service |
1638 | 0 | .syncing_peers() |
1639 | 0 | .await |
1640 | 0 | .map(|(peer_id, role, best_number, best_hash)| { |
1641 | 0 | methods::SystemPeer { |
1642 | 0 | peer_id: peer_id.to_string(), |
1643 | 0 | roles: match role { |
1644 | | sync_service::Role::Authority => { |
1645 | 0 | methods::SystemPeerRole::Authority |
1646 | | } |
1647 | | sync_service::Role::Full => { |
1648 | 0 | methods::SystemPeerRole::Full |
1649 | | } |
1650 | | sync_service::Role::Light => { |
1651 | 0 | methods::SystemPeerRole::Light |
1652 | | } |
1653 | | }, |
1654 | 0 | best_hash: methods::HashHexString(best_hash), |
1655 | 0 | best_number, |
1656 | 0 | } |
1657 | 0 | }) Unexecuted instantiation: _RNCNCINvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background3runpE0sb_0Ba_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sb_0B1f_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runpE0sb_0Ba_ |
1658 | 0 | .collect(), |
1659 | 0 | ) |
1660 | 0 | .to_json_response(request_id_json), |
1661 | | ) |
1662 | 0 | .await; |
1663 | | } |
1664 | | |
1665 | | methods::MethodCall::system_properties {} => { |
1666 | 0 | let _ = me |
1667 | 0 | .responses_tx |
1668 | 0 | .send( |
1669 | 0 | methods::Response::system_properties( |
1670 | 0 | serde_json::from_str(&me.chain_properties_json).unwrap(), |
1671 | 0 | ) |
1672 | 0 | .to_json_response(request_id_json), |
1673 | 0 | ) |
1674 | 0 | .await; |
1675 | | } |
1676 | | |
1677 | | methods::MethodCall::system_version {} => { |
1678 | 0 | let _ = me |
1679 | 0 | .responses_tx |
1680 | 0 | .send( |
1681 | 0 | methods::Response::system_version((&me.system_version).into()) |
1682 | 0 | .to_json_response(request_id_json), |
1683 | 0 | ) |
1684 | 0 | .await; |
1685 | | } |
1686 | | |
1687 | | methods::MethodCall::chainHead_v1_body { |
1688 | 0 | follow_subscription, |
1689 | 0 | hash, |
1690 | | } => { |
1691 | 0 | let Some(subscription) = me |
1692 | 0 | .chain_head_follow_subscriptions |
1693 | 0 | .get_mut(&*follow_subscription) |
1694 | | else { |
1695 | | // Subscription doesn't exist. |
1696 | 0 | let _ = me |
1697 | 0 | .responses_tx |
1698 | 0 | .send( |
1699 | 0 | methods::Response::chainHead_v1_body( |
1700 | 0 | methods::ChainHeadBodyCallReturn::LimitReached {}, |
1701 | 0 | ) |
1702 | 0 | .to_json_response(request_id_json), |
1703 | 0 | ) |
1704 | 0 | .await; |
1705 | 0 | continue; |
1706 | | }; |
1707 | | |
1708 | | // Determine whether the requested block hash is valid, and if yes its |
1709 | | // number and extrinsics trie root. The extrinsics trie root is used to |
1710 | | // verify whether the body we download is correct. |
1711 | 0 | let (block_number, extrinsics_root) = { |
1712 | 0 | if let Some(header) = subscription.pinned_blocks_headers.get(&hash.0) { |
1713 | 0 | let decoded = |
1714 | 0 | header::decode(header, me.sync_service.block_number_bytes()) |
1715 | 0 | .unwrap(); // TODO: unwrap? |
1716 | 0 | (decoded.number, *decoded.extrinsics_root) |
1717 | | } else { |
1718 | | // Block isn't pinned. Request is invalid. |
1719 | 0 | let _ = me |
1720 | 0 | .responses_tx |
1721 | 0 | .send(parse::build_error_response( |
1722 | 0 | request_id_json, |
1723 | 0 | parse::ErrorResponse::ApplicationDefined( |
1724 | 0 | -32801, |
1725 | 0 | "unknown or unpinned block", |
1726 | 0 | ), |
1727 | 0 | None, |
1728 | 0 | )) |
1729 | 0 | .await; |
1730 | 0 | continue; |
1731 | | } |
1732 | | }; |
1733 | | |
1734 | | // Check whether there is an operation slot available. |
1735 | | subscription.available_operation_slots = |
1736 | 0 | match subscription.available_operation_slots.checked_sub(1) { |
1737 | 0 | Some(s) => s, |
1738 | | None => { |
1739 | 0 | let _ = me |
1740 | 0 | .responses_tx |
1741 | 0 | .send( |
1742 | 0 | methods::Response::chainHead_v1_body( |
1743 | 0 | methods::ChainHeadBodyCallReturn::LimitReached {}, |
1744 | 0 | ) |
1745 | 0 | .to_json_response(request_id_json), |
1746 | 0 | ) |
1747 | 0 | .await; |
1748 | 0 | continue; |
1749 | | } |
1750 | | }; |
1751 | | |
1752 | | // Build the future that will grab the block body. |
1753 | 0 | let body_download_future = me.sync_service.clone().block_query( |
1754 | 0 | block_number, |
1755 | 0 | hash.0, |
1756 | 0 | codec::BlocksRequestFields { |
1757 | 0 | header: false, |
1758 | 0 | body: true, |
1759 | 0 | justifications: false, |
1760 | 0 | }, |
1761 | 0 | 3, |
1762 | 0 | Duration::from_secs(20), |
1763 | 0 | NonZeroU32::new(2).unwrap(), |
1764 | 0 | ); |
1765 | 0 |
|
1766 | 0 | // Allocate an operation ID, update the local state, and notify the |
1767 | 0 | // JSON-RPC client. |
1768 | 0 | let operation_id = { |
1769 | 0 | let mut operation_id = [0u8; 32]; |
1770 | 0 | me.randomness.fill_bytes(&mut operation_id); |
1771 | 0 | bs58::encode(operation_id).into_string() |
1772 | 0 | }; |
1773 | 0 | let interrupt = event_listener::Event::new(); |
1774 | 0 | let on_interrupt = interrupt.listen(); |
1775 | 0 | let _was_in = subscription.operations_in_progress.insert( |
1776 | 0 | operation_id.clone(), |
1777 | 0 | ChainHeadOperation { |
1778 | 0 | occupied_slots: 1, |
1779 | 0 | interrupt, |
1780 | 0 | }, |
1781 | 0 | ); |
1782 | 0 | debug_assert!(_was_in.is_none()); |
1783 | 0 | let _ = me |
1784 | 0 | .responses_tx |
1785 | 0 | .send( |
1786 | 0 | methods::Response::chainHead_v1_body( |
1787 | 0 | methods::ChainHeadBodyCallReturn::Started { |
1788 | 0 | operation_id: (&operation_id).into(), |
1789 | 0 | }, |
1790 | 0 | ) |
1791 | 0 | .to_json_response(request_id_json), |
1792 | 0 | ) |
1793 | 0 | .await; |
1794 | | |
1795 | | // Finish the download asynchronously. |
1796 | 0 | let subscription_id = follow_subscription.into_owned(); |
1797 | 0 | me.background_tasks.push(Box::pin(async move { |
1798 | 0 | async move { |
1799 | 0 | on_interrupt.await; |
1800 | | // This event is necessary only because tasks can't finish without |
1801 | | // generating an event. |
1802 | 0 | Event::ChainHeadOperationCancelled |
1803 | 0 | } Unexecuted instantiation: _RNCNCNCINvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background3runpE0sc_00Bc_ Unexecuted instantiation: _RNCNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sc_00B1h_ Unexecuted instantiation: _RNCNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runpE0sc_00Bc_ |
1804 | 0 | .or(async move { |
1805 | 0 | Event::ChainHeadBodyOperationDone { |
1806 | 0 | subscription_id, |
1807 | 0 | operation_id, |
1808 | 0 | expected_extrinsics_root: extrinsics_root, |
1809 | 0 | result: body_download_future.await, |
1810 | | } |
1811 | 0 | }) Unexecuted instantiation: _RNCNCNCINvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background3runpE0sc_0s_0Bc_ Unexecuted instantiation: _RNCNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sc_0s_0B1h_ Unexecuted instantiation: _RNCNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runpE0sc_0s_0Bc_ |
1812 | 0 | .await |
1813 | 0 | })); Unexecuted instantiation: _RNCNCINvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background3runpE0sc_0Ba_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sc_0B1f_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runpE0sc_0Ba_ |
1814 | | } |
1815 | | |
1816 | | methods::MethodCall::chainHead_v1_call { |
1817 | 0 | follow_subscription, |
1818 | 0 | hash, |
1819 | 0 | function, |
1820 | 0 | call_parameters: methods::HexString(call_parameters), |
1821 | | } => { |
1822 | 0 | let Some(subscription) = me |
1823 | 0 | .chain_head_follow_subscriptions |
1824 | 0 | .get_mut(&*follow_subscription) |
1825 | | else { |
1826 | | // Subscription doesn't exist. |
1827 | 0 | let _ = me |
1828 | 0 | .responses_tx |
1829 | 0 | .send( |
1830 | 0 | methods::Response::chainHead_v1_call( |
1831 | 0 | methods::ChainHeadBodyCallReturn::LimitReached {}, |
1832 | 0 | ) |
1833 | 0 | .to_json_response(request_id_json), |
1834 | 0 | ) |
1835 | 0 | .await; |
1836 | 0 | continue; |
1837 | | }; |
1838 | | |
1839 | | // Determine whether the requested block hash is valid. |
1840 | 0 | if !subscription.pinned_blocks_headers.contains_key(&hash.0) { |
1841 | | // Block isn't pinned. Request is invalid. |
1842 | 0 | let _ = me |
1843 | 0 | .responses_tx |
1844 | 0 | .send(parse::build_error_response( |
1845 | 0 | request_id_json, |
1846 | 0 | parse::ErrorResponse::ApplicationDefined( |
1847 | 0 | -32801, |
1848 | 0 | "unknown or unpinned block", |
1849 | 0 | ), |
1850 | 0 | None, |
1851 | 0 | )) |
1852 | 0 | .await; |
1853 | 0 | continue; |
1854 | 0 | } |
1855 | 0 |
|
1856 | 0 | // Check whether there is an operation slot available. |
1857 | 0 | subscription.available_operation_slots = |
1858 | 0 | match subscription.available_operation_slots.checked_sub(1) { |
1859 | 0 | Some(s) => s, |
1860 | | None => { |
1861 | 0 | let _ = me |
1862 | 0 | .responses_tx |
1863 | 0 | .send( |
1864 | 0 | methods::Response::chainHead_v1_call( |
1865 | 0 | methods::ChainHeadBodyCallReturn::LimitReached {}, |
1866 | 0 | ) |
1867 | 0 | .to_json_response(request_id_json), |
1868 | 0 | ) |
1869 | 0 | .await; |
1870 | 0 | continue; |
1871 | | } |
1872 | | }; |
1873 | | |
1874 | | // Make sure that the subscription is `withRuntime: true`. |
1875 | 0 | let Some(runtime_service_subscription_id) = |
1876 | 0 | subscription.runtime_service_subscription_id |
1877 | | else { |
1878 | | // Subscription is "without runtime". |
1879 | | // This path is in principle also reachable if the subscription isn't |
1880 | | // initialized yet, but in that case the block hash can't possibly be |
1881 | | // pinned. |
1882 | 0 | let _ = me |
1883 | 0 | .responses_tx |
1884 | 0 | .send(parse::build_error_response( |
1885 | 0 | request_id_json, |
1886 | 0 | parse::ErrorResponse::InvalidParams, |
1887 | 0 | None, |
1888 | 0 | )) |
1889 | 0 | .await; |
1890 | 0 | continue; |
1891 | | }; |
1892 | | |
1893 | | // Extract information about the block. |
1894 | 0 | let (pinned_runtime, block_state_trie_root_hash, block_number) = match me |
1895 | 0 | .runtime_service |
1896 | 0 | .pin_pinned_block_runtime(runtime_service_subscription_id, hash.0) |
1897 | 0 | .await |
1898 | | { |
1899 | 0 | Ok(info) => info, |
1900 | | Err(runtime_service::PinPinnedBlockRuntimeError::BlockNotPinned) => { |
1901 | | // This has been verified above. |
1902 | 0 | unreachable!() |
1903 | | } |
1904 | | Err( |
1905 | | runtime_service::PinPinnedBlockRuntimeError::ObsoleteSubscription, |
1906 | | ) => { |
1907 | | // The runtime service subscription is dead. |
1908 | 0 | let _ = me |
1909 | 0 | .responses_tx |
1910 | 0 | .send( |
1911 | 0 | methods::Response::chainHead_v1_call( |
1912 | 0 | methods::ChainHeadBodyCallReturn::LimitReached {}, |
1913 | 0 | ) |
1914 | 0 | .to_json_response(request_id_json), |
1915 | 0 | ) |
1916 | 0 | .await; |
1917 | 0 | continue; |
1918 | | } |
1919 | | }; |
1920 | | |
1921 | | // Create a future that will perform the runtime call. |
1922 | 0 | let runtime_call_future = { |
1923 | 0 | let runtime_service = me.runtime_service.clone(); |
1924 | 0 | let function = function.into_owned(); |
1925 | 0 | async move { |
1926 | 0 | runtime_service |
1927 | 0 | .clone() |
1928 | 0 | .runtime_call( |
1929 | 0 | pinned_runtime, |
1930 | 0 | hash.0, |
1931 | 0 | block_number, |
1932 | 0 | block_state_trie_root_hash, |
1933 | 0 | function, |
1934 | 0 | None, |
1935 | 0 | call_parameters, |
1936 | 0 | 3, |
1937 | 0 | Duration::from_secs(20), |
1938 | 0 | NonZeroU32::new(2).unwrap(), |
1939 | 0 | ) |
1940 | 0 | .await |
1941 | 0 | } Unexecuted instantiation: _RNCNCINvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background3runpE0sd_0Ba_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sd_0B1f_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runpE0sd_0Ba_ |
1942 | | }; |
1943 | | |
1944 | | // Allocate a new operation ID, update the local state, and send the |
1945 | | // confirmation to the JSON-RPC client. |
1946 | 0 | let operation_id = { |
1947 | 0 | let mut operation_id = [0u8; 32]; |
1948 | 0 | me.randomness.fill_bytes(&mut operation_id); |
1949 | 0 | bs58::encode(operation_id).into_string() |
1950 | 0 | }; |
1951 | 0 | let interrupt = event_listener::Event::new(); |
1952 | 0 | let on_interrupt = interrupt.listen(); |
1953 | 0 | let _was_in = subscription.operations_in_progress.insert( |
1954 | 0 | operation_id.clone(), |
1955 | 0 | ChainHeadOperation { |
1956 | 0 | occupied_slots: 1, |
1957 | 0 | interrupt, |
1958 | 0 | }, |
1959 | 0 | ); |
1960 | 0 | debug_assert!(_was_in.is_none()); |
1961 | 0 | let _ = me |
1962 | 0 | .responses_tx |
1963 | 0 | .send( |
1964 | 0 | methods::Response::chainHead_v1_call( |
1965 | 0 | methods::ChainHeadBodyCallReturn::Started { |
1966 | 0 | operation_id: (&operation_id).into(), |
1967 | 0 | }, |
1968 | 0 | ) |
1969 | 0 | .to_json_response(request_id_json), |
1970 | 0 | ) |
1971 | 0 | .await; |
1972 | | |
1973 | | // Finish the call asynchronously. |
1974 | 0 | let subscription_id = follow_subscription.into_owned(); |
1975 | 0 | me.background_tasks.push(Box::pin(async move { |
1976 | 0 | async move { |
1977 | 0 | on_interrupt.await; |
1978 | | // This event is necessary only because tasks can't finish without |
1979 | | // generating an event. |
1980 | 0 | Event::ChainHeadOperationCancelled |
1981 | 0 | } Unexecuted instantiation: _RNCNCNCINvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background3runpE0se_00Bc_ Unexecuted instantiation: _RNCNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0se_00B1h_ Unexecuted instantiation: _RNCNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runpE0se_00Bc_ |
1982 | 0 | .or(async move { |
1983 | 0 | Event::ChainHeadCallOperationDone { |
1984 | 0 | subscription_id, |
1985 | 0 | operation_id, |
1986 | 0 | result: runtime_call_future.await, |
1987 | | } |
1988 | 0 | }) Unexecuted instantiation: _RNCNCNCINvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background3runpE0se_0s_0Bc_ Unexecuted instantiation: _RNCNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0se_0s_0B1h_ Unexecuted instantiation: _RNCNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runpE0se_0s_0Bc_ |
1989 | 0 | .await |
1990 | 0 | })); Unexecuted instantiation: _RNCNCINvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background3runpE0se_0Ba_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0se_0B1f_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runpE0se_0Ba_ |
1991 | | } |
1992 | | |
1993 | | methods::MethodCall::chainHead_v1_continue { .. } => { |
1994 | | // TODO: not implemented properly |
1995 | 0 | let _ = me |
1996 | 0 | .responses_tx |
1997 | 0 | .send( |
1998 | 0 | methods::Response::chainHead_v1_continue(()) |
1999 | 0 | .to_json_response(request_id_json), |
2000 | 0 | ) |
2001 | 0 | .await; |
2002 | | } |
2003 | | |
2004 | | methods::MethodCall::chainHead_v1_storage { |
2005 | 0 | follow_subscription, |
2006 | 0 | hash, |
2007 | 0 | items, |
2008 | 0 | child_trie, |
2009 | | } => { |
2010 | 0 | let Some(subscription) = me |
2011 | 0 | .chain_head_follow_subscriptions |
2012 | 0 | .get_mut(&*follow_subscription) |
2013 | | else { |
2014 | | // Subscription doesn't exist. |
2015 | 0 | let _ = me |
2016 | 0 | .responses_tx |
2017 | 0 | .send( |
2018 | 0 | methods::Response::chainHead_v1_storage( |
2019 | 0 | methods::ChainHeadStorageReturn::LimitReached {}, |
2020 | 0 | ) |
2021 | 0 | .to_json_response(request_id_json), |
2022 | 0 | ) |
2023 | 0 | .await; |
2024 | 0 | continue; |
2025 | | }; |
2026 | | |
2027 | | // Determine whether the requested block hash is valid, and if yes its |
2028 | | // number and state trie root. The extrinsics trie root is used to |
2029 | | // verify whether the body we download is correct. |
2030 | 0 | let (block_number, block_state_trie_root) = { |
2031 | 0 | if let Some(header) = subscription.pinned_blocks_headers.get(&hash.0) { |
2032 | 0 | let decoded = |
2033 | 0 | header::decode(header, me.sync_service.block_number_bytes()) |
2034 | 0 | .unwrap(); // TODO: unwrap? |
2035 | 0 | (decoded.number, *decoded.state_root) |
2036 | | } else { |
2037 | | // Block isn't pinned. Request is invalid. |
2038 | 0 | let _ = me |
2039 | 0 | .responses_tx |
2040 | 0 | .send(parse::build_error_response( |
2041 | 0 | request_id_json, |
2042 | 0 | parse::ErrorResponse::ApplicationDefined( |
2043 | 0 | -32801, |
2044 | 0 | "unknown or unpinned block", |
2045 | 0 | ), |
2046 | 0 | None, |
2047 | 0 | )) |
2048 | 0 | .await; |
2049 | 0 | continue; |
2050 | | } |
2051 | | }; |
2052 | | |
2053 | 0 | if child_trie.is_some() { |
2054 | | // TODO: implement this |
2055 | 0 | let _ = me |
2056 | 0 | .responses_tx |
2057 | 0 | .send(parse::build_error_response( |
2058 | 0 | request_id_json, |
2059 | 0 | parse::ErrorResponse::ServerError( |
2060 | 0 | -32000, |
2061 | 0 | "Child key storage queries not supported yet", |
2062 | 0 | ), |
2063 | 0 | None, |
2064 | 0 | )) |
2065 | 0 | .await; |
2066 | 0 | log!( |
2067 | 0 | &me.platform, |
2068 | 0 | Warn, |
2069 | 0 | &me.log_target, |
2070 | 0 | "chainHead_v1_storage has been called with a non-null childTrie. \ |
2071 | 0 | This isn't supported by smoldot yet." |
2072 | 0 | ); |
2073 | 0 | continue; |
2074 | 0 | } |
2075 | 0 |
|
2076 | 0 | // Build the list of storage operations that are effectively started. |
2077 | 0 | // This reads from the list that the API user requests, and stops if there |
2078 | 0 | // is no available operation slot. |
2079 | 0 | let mut storage_operations = Vec::with_capacity(items.len()); |
2080 | 0 | let mut items = items.into_iter(); |
2081 | | loop { |
2082 | 0 | if subscription.available_operation_slots == 0 { |
2083 | 0 | break; |
2084 | 0 | } |
2085 | 0 | let Some(item) = items.next() else { break }; |
2086 | 0 | subscription.available_operation_slots -= 1; |
2087 | 0 | storage_operations.push(sync_service::StorageRequestItem { |
2088 | 0 | key: item.key.0, |
2089 | 0 | ty: match item.ty { |
2090 | | methods::ChainHeadStorageType::Value => { |
2091 | 0 | sync_service::StorageRequestItemTy::Value |
2092 | | } |
2093 | | methods::ChainHeadStorageType::Hash => { |
2094 | 0 | sync_service::StorageRequestItemTy::Hash |
2095 | | } |
2096 | | methods::ChainHeadStorageType::ClosestDescendantMerkleValue => { |
2097 | 0 | sync_service::StorageRequestItemTy::ClosestDescendantMerkleValue |
2098 | | } |
2099 | | methods::ChainHeadStorageType::DescendantsValues => { |
2100 | 0 | sync_service::StorageRequestItemTy::DescendantsValues |
2101 | | } |
2102 | | methods::ChainHeadStorageType::DescendantsHashes => { |
2103 | 0 | sync_service::StorageRequestItemTy::DescendantsHashes |
2104 | | } |
2105 | | }, |
2106 | | }); |
2107 | | } |
2108 | | |
2109 | | // Abort immediately if nothing was started. |
2110 | 0 | if storage_operations.is_empty() { |
2111 | 0 | let _ = me |
2112 | 0 | .responses_tx |
2113 | 0 | .send( |
2114 | 0 | methods::Response::chainHead_v1_storage( |
2115 | 0 | methods::ChainHeadStorageReturn::LimitReached {}, |
2116 | 0 | ) |
2117 | 0 | .to_json_response(request_id_json), |
2118 | 0 | ) |
2119 | 0 | .await; |
2120 | 0 | continue; |
2121 | 0 | } |
2122 | 0 |
|
2123 | 0 | // Initialize the storage query operation. |
2124 | 0 | let fetch_operation = me.sync_service.clone().storage_query( |
2125 | 0 | block_number, |
2126 | 0 | hash.0, |
2127 | 0 | block_state_trie_root, |
2128 | 0 | storage_operations.into_iter(), |
2129 | 0 | 3, |
2130 | 0 | Duration::from_secs(20), |
2131 | 0 | NonZeroU32::new(2).unwrap(), |
2132 | 0 | ); |
2133 | 0 |
|
2134 | 0 | let operation_id = { |
2135 | 0 | let mut operation_id = [0u8; 32]; |
2136 | 0 | me.randomness.fill_bytes(&mut operation_id); |
2137 | 0 | bs58::encode(operation_id).into_string() |
2138 | 0 | }; |
2139 | 0 |
|
2140 | 0 | let interrupt = event_listener::Event::new(); |
2141 | 0 | let on_interrupt = interrupt.listen(); |
2142 | 0 |
|
2143 | 0 | let _was_in = subscription.operations_in_progress.insert( |
2144 | 0 | operation_id.clone(), |
2145 | 0 | ChainHeadOperation { |
2146 | 0 | occupied_slots: 1, |
2147 | 0 | interrupt, |
2148 | 0 | }, |
2149 | 0 | ); |
2150 | 0 | debug_assert!(_was_in.is_none()); |
2151 | 0 | let _ = me |
2152 | 0 | .responses_tx |
2153 | 0 | .send( |
2154 | 0 | methods::Response::chainHead_v1_storage( |
2155 | 0 | methods::ChainHeadStorageReturn::Started { |
2156 | 0 | operation_id: (&operation_id).into(), |
2157 | 0 | discarded_items: items.len(), |
2158 | 0 | }, |
2159 | 0 | ) |
2160 | 0 | .to_json_response(request_id_json), |
2161 | 0 | ) |
2162 | 0 | .await; |
2163 | | |
2164 | 0 | let subscription_id = follow_subscription.into_owned(); |
2165 | 0 | me.background_tasks.push(Box::pin(async move { |
2166 | 0 | async { |
2167 | 0 | on_interrupt.await; |
2168 | | // This event is necessary only because tasks can't finish without |
2169 | | // generating an event. |
2170 | 0 | Event::ChainHeadOperationCancelled |
2171 | 0 | } Unexecuted instantiation: _RNCNCNCINvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background3runpE0sf_00Bc_ Unexecuted instantiation: _RNCNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sf_00B1h_ Unexecuted instantiation: _RNCNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runpE0sf_00Bc_ |
2172 | 0 | .or(async { |
2173 | 0 | Event::ChainHeadStorageOperationProgress { |
2174 | 0 | subscription_id, |
2175 | 0 | operation_id, |
2176 | 0 | progress: fetch_operation.advance().await, |
2177 | | } |
2178 | 0 | }) Unexecuted instantiation: _RNCNCNCINvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background3runpE0sf_0s_0Bc_ Unexecuted instantiation: _RNCNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sf_0s_0B1h_ Unexecuted instantiation: _RNCNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runpE0sf_0s_0Bc_ |
2179 | 0 | .await |
2180 | 0 | })); Unexecuted instantiation: _RNCNCINvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background3runpE0sf_0Ba_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sf_0B1f_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runpE0sf_0Ba_ |
2181 | | } |
2182 | | |
2183 | | methods::MethodCall::chainHead_v1_stopOperation { |
2184 | 0 | follow_subscription, |
2185 | 0 | operation_id, |
2186 | | } => { |
2187 | | // Stopping an operation is done by notifying an event so that the |
2188 | | // background task stops. |
2189 | 0 | if let Some(subscription) = me |
2190 | 0 | .chain_head_follow_subscriptions |
2191 | 0 | .get_mut(&*follow_subscription) |
2192 | | { |
2193 | 0 | if let Some(operation) = |
2194 | 0 | subscription.operations_in_progress.remove(&*operation_id) |
2195 | 0 | { |
2196 | 0 | operation.interrupt.notify(usize::MAX); |
2197 | 0 | subscription.available_operation_slots += operation.occupied_slots; |
2198 | 0 | } |
2199 | 0 | } |
2200 | | |
2201 | 0 | let _ = me |
2202 | 0 | .responses_tx |
2203 | 0 | .send( |
2204 | 0 | methods::Response::chainHead_v1_stopOperation(()) |
2205 | 0 | .to_json_response(request_id_json), |
2206 | 0 | ) |
2207 | 0 | .await; |
2208 | | } |
2209 | | |
2210 | 0 | methods::MethodCall::chainHead_v1_follow { with_runtime } => { |
2211 | 0 | // Check that the number of existing subscriptions is below the limit. |
2212 | 0 | // TODO: configurable limit |
2213 | 0 | if me.chain_head_follow_subscriptions.len() >= 2 { |
2214 | 0 | let _ = me |
2215 | 0 | .responses_tx |
2216 | 0 | .send(parse::build_error_response( |
2217 | 0 | request_id_json, |
2218 | 0 | parse::ErrorResponse::ApplicationDefined( |
2219 | 0 | -32800, |
2220 | 0 | "too many active follow subscriptions", |
2221 | 0 | ), |
2222 | 0 | None, |
2223 | 0 | )) |
2224 | 0 | .await; |
2225 | 0 | continue; |
2226 | 0 | } |
2227 | 0 |
|
2228 | 0 | let subscription_id = { |
2229 | 0 | let mut subscription_id = [0u8; 32]; |
2230 | 0 | me.randomness.fill_bytes(&mut subscription_id); |
2231 | 0 | bs58::encode(subscription_id).into_string() |
2232 | 0 | }; |
2233 | 0 |
|
2234 | 0 | let _prev_value = me.chain_head_follow_subscriptions.insert( |
2235 | 0 | subscription_id.clone(), |
2236 | 0 | ChainHeadFollow { |
2237 | 0 | pinned_blocks_headers: hashbrown::HashMap::with_capacity_and_hasher( |
2238 | 0 | 0, |
2239 | 0 | Default::default(), |
2240 | 0 | ), // TODO: capacity? |
2241 | 0 | operations_in_progress: |
2242 | 0 | hashbrown::HashMap::with_capacity_and_hasher( |
2243 | 0 | 32, |
2244 | 0 | Default::default(), |
2245 | 0 | ), |
2246 | 0 | available_operation_slots: 32, // TODO: make configurable? adjust dynamically? |
2247 | 0 | runtime_service_subscription_id: None, |
2248 | 0 | }, |
2249 | 0 | ); |
2250 | 0 | debug_assert!(_prev_value.is_none()); |
2251 | | |
2252 | 0 | let _ = me |
2253 | 0 | .responses_tx |
2254 | 0 | .send( |
2255 | 0 | methods::Response::chainHead_v1_follow(Cow::Borrowed( |
2256 | 0 | &subscription_id, |
2257 | 0 | )) |
2258 | 0 | .to_json_response(request_id_json), |
2259 | 0 | ) |
2260 | 0 | .await; |
2261 | | |
2262 | | // Subscription asynchronously either to the runtime service or the |
2263 | | // sync service. |
2264 | 0 | if with_runtime { |
2265 | 0 | let runtime_service = me.runtime_service.clone(); |
2266 | 0 | me.background_tasks.push(Box::pin(async move { |
2267 | 0 | Event::ChainHeadSubscriptionWithRuntimeReady { |
2268 | 0 | subscription_id, |
2269 | 0 | subscription: runtime_service |
2270 | 0 | .subscribe_all( |
2271 | 0 | 32, |
2272 | 0 | NonZeroUsize::new(32).unwrap_or_else(|| unreachable!()), Unexecuted instantiation: _RNCNCNCINvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background3runpE0sg_00Bc_ Unexecuted instantiation: _RNCNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sg_00B1h_ Unexecuted instantiation: _RNCNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runpE0sg_00Bc_ |
2273 | 0 | ) |
2274 | 0 | .await, |
2275 | | } |
2276 | 0 | })) Unexecuted instantiation: _RNCNCINvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background3runpE0sg_0Ba_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sg_0B1f_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runpE0sg_0Ba_ |
2277 | | } else { |
2278 | 0 | let sync_service = me.sync_service.clone(); |
2279 | 0 | me.background_tasks.push(Box::pin(async move { |
2280 | 0 | Event::ChainHeadSubscriptionWithoutRuntimeReady { |
2281 | 0 | subscription_id, |
2282 | 0 | subscription: sync_service.subscribe_all(32, false).await, |
2283 | | } |
2284 | 0 | })) Unexecuted instantiation: _RNCNCINvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background3runpE0sh_0Ba_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sh_0B1f_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runpE0sh_0Ba_ |
2285 | | } |
2286 | | } |
2287 | | |
2288 | | methods::MethodCall::chainHead_v1_unfollow { |
2289 | 0 | follow_subscription, |
2290 | | } => { |
2291 | 0 | if let Some(subscription) = me |
2292 | 0 | .chain_head_follow_subscriptions |
2293 | 0 | .remove(&*follow_subscription) |
2294 | | { |
2295 | 0 | for (_, operation) in subscription.operations_in_progress { |
2296 | 0 | operation.interrupt.notify(usize::MAX); |
2297 | 0 | } |
2298 | 0 | }; |
2299 | | |
2300 | 0 | let _ = me |
2301 | 0 | .responses_tx |
2302 | 0 | .send( |
2303 | 0 | methods::Response::chainHead_v1_unfollow(()) |
2304 | 0 | .to_json_response(request_id_json), |
2305 | 0 | ) |
2306 | 0 | .await; |
2307 | | } |
2308 | | |
2309 | | methods::MethodCall::chainHead_v1_header { |
2310 | 0 | follow_subscription, |
2311 | 0 | hash, |
2312 | | } => { |
2313 | 0 | let Some(subscription) = me |
2314 | 0 | .chain_head_follow_subscriptions |
2315 | 0 | .get_mut(&*follow_subscription) |
2316 | | else { |
2317 | | // Subscription doesn't exist. |
2318 | 0 | let _ = me |
2319 | 0 | .responses_tx |
2320 | 0 | .send( |
2321 | 0 | methods::Response::chainHead_v1_header(None) |
2322 | 0 | .to_json_response(request_id_json), |
2323 | 0 | ) |
2324 | 0 | .await; |
2325 | 0 | continue; |
2326 | | }; |
2327 | | |
2328 | 0 | let Some(block) = subscription.pinned_blocks_headers.get(&hash.0) else { |
2329 | | // Block isn't pinned. |
2330 | 0 | let _ = me |
2331 | 0 | .responses_tx |
2332 | 0 | .send(parse::build_error_response( |
2333 | 0 | request_id_json, |
2334 | 0 | parse::ErrorResponse::ApplicationDefined( |
2335 | 0 | -32801, |
2336 | 0 | "unknown or unpinned block", |
2337 | 0 | ), |
2338 | 0 | None, |
2339 | 0 | )) |
2340 | 0 | .await; |
2341 | 0 | continue; |
2342 | | }; |
2343 | | |
2344 | 0 | let _ = me |
2345 | 0 | .responses_tx |
2346 | 0 | .send( |
2347 | 0 | methods::Response::chainHead_v1_header(Some(methods::HexString( |
2348 | 0 | block.clone(), |
2349 | 0 | ))) |
2350 | 0 | .to_json_response(request_id_json), |
2351 | 0 | ) |
2352 | 0 | .await; |
2353 | | } |
2354 | | |
2355 | | methods::MethodCall::chainHead_v1_unpin { |
2356 | 0 | follow_subscription, |
2357 | 0 | hash_or_hashes, |
2358 | | } => { |
2359 | 0 | let Some(subscription) = me |
2360 | 0 | .chain_head_follow_subscriptions |
2361 | 0 | .get_mut(&*follow_subscription) |
2362 | | else { |
2363 | | // Subscription doesn't exist. |
2364 | 0 | let _ = me |
2365 | 0 | .responses_tx |
2366 | 0 | .send( |
2367 | 0 | methods::Response::chainHead_v1_unpin(()) |
2368 | 0 | .to_json_response(request_id_json), |
2369 | 0 | ) |
2370 | 0 | .await; |
2371 | 0 | continue; |
2372 | | }; |
2373 | | |
2374 | 0 | let all_hashes = match &hash_or_hashes { |
2375 | 0 | methods::HashHexStringSingleOrArray::Single(hash) => { |
2376 | 0 | either::Left(iter::once(&hash.0)) |
2377 | | } |
2378 | 0 | methods::HashHexStringSingleOrArray::Array(hashes) => { |
2379 | 0 | either::Right(hashes.iter().map(|h| &h.0)) Unexecuted instantiation: _RNCNCINvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background3runpE0si_0Ba_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0si_0B1f_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runpE0si_0Ba_ |
2380 | | } |
2381 | | }; |
2382 | | |
2383 | 0 | let checks_passed = { |
2384 | 0 | let mut dedup_check = hashbrown::HashSet::with_capacity_and_hasher( |
2385 | 0 | 0, |
2386 | 0 | SipHasherBuild::new({ |
2387 | 0 | let mut seed = [0; 16]; |
2388 | 0 | me.randomness.fill_bytes(&mut seed); |
2389 | 0 | seed |
2390 | 0 | }), |
2391 | 0 | ); |
2392 | 0 | let mut all_hashes = all_hashes.clone(); |
2393 | | |
2394 | | loop { |
2395 | 0 | let Some(hash) = all_hashes.next() else { |
2396 | 0 | break true; |
2397 | | }; |
2398 | | |
2399 | 0 | if !dedup_check.insert(hash) { |
2400 | 0 | let _ = me |
2401 | 0 | .responses_tx |
2402 | 0 | .send(parse::build_error_response( |
2403 | 0 | request_id_json, |
2404 | 0 | parse::ErrorResponse::ApplicationDefined( |
2405 | 0 | -32804, |
2406 | 0 | "duplicate block hash", |
2407 | 0 | ), |
2408 | 0 | None, |
2409 | 0 | )) |
2410 | 0 | .await; |
2411 | 0 | break false; |
2412 | 0 | } |
2413 | 0 |
|
2414 | 0 | if !subscription.pinned_blocks_headers.contains_key(hash) { |
2415 | 0 | let _ = me |
2416 | 0 | .responses_tx |
2417 | 0 | .send(parse::build_error_response( |
2418 | 0 | request_id_json, |
2419 | 0 | parse::ErrorResponse::InvalidParams, |
2420 | 0 | None, |
2421 | 0 | )) |
2422 | 0 | .await; |
2423 | 0 | break false; |
2424 | 0 | } |
2425 | | } |
2426 | | }; |
2427 | | |
2428 | 0 | if !checks_passed { |
2429 | 0 | continue; |
2430 | 0 | } |
2431 | | |
2432 | | // The logic below assumes that all hashes are unique, which is ensured |
2433 | | // above. |
2434 | 0 | for hash in all_hashes { |
2435 | 0 | subscription.pinned_blocks_headers.remove(hash); |
2436 | 0 | if let Some(subscription_id) = |
2437 | 0 | subscription.runtime_service_subscription_id |
2438 | | { |
2439 | 0 | me.runtime_service.unpin_block(subscription_id, *hash).await; |
2440 | 0 | } |
2441 | | } |
2442 | | |
2443 | 0 | let _ = me |
2444 | 0 | .responses_tx |
2445 | 0 | .send( |
2446 | 0 | methods::Response::chainHead_v1_unpin(()) |
2447 | 0 | .to_json_response(request_id_json), |
2448 | 0 | ) |
2449 | 0 | .await; |
2450 | | } |
2451 | | |
2452 | | methods::MethodCall::chainHead_unstable_finalizedDatabase { |
2453 | 0 | max_size_bytes, |
2454 | | } => { |
2455 | 0 | let response = crate::database::encode_database( |
2456 | 0 | &me.network_service, |
2457 | 0 | &me.sync_service, |
2458 | 0 | &me.runtime_service, |
2459 | 0 | &me.genesis_block_hash, |
2460 | 0 | usize::try_from(max_size_bytes.unwrap_or(u64::MAX)) |
2461 | 0 | .unwrap_or(usize::MAX), |
2462 | 0 | ) |
2463 | 0 | .await; |
2464 | | |
2465 | 0 | let _ = me |
2466 | 0 | .responses_tx |
2467 | 0 | .send( |
2468 | 0 | methods::Response::chainHead_unstable_finalizedDatabase( |
2469 | 0 | response.into(), |
2470 | 0 | ) |
2471 | 0 | .to_json_response(request_id_json), |
2472 | 0 | ) |
2473 | 0 | .await; |
2474 | | } |
2475 | | |
2476 | | methods::MethodCall::chainSpec_v1_chainName {} => { |
2477 | 0 | let _ = me |
2478 | 0 | .responses_tx |
2479 | 0 | .send( |
2480 | 0 | methods::Response::chainSpec_v1_chainName((&me.chain_name).into()) |
2481 | 0 | .to_json_response(request_id_json), |
2482 | 0 | ) |
2483 | 0 | .await; |
2484 | | } |
2485 | | |
2486 | | methods::MethodCall::chainSpec_v1_genesisHash {} => { |
2487 | 0 | let _ = me |
2488 | 0 | .responses_tx |
2489 | 0 | .send( |
2490 | 0 | methods::Response::chainSpec_v1_genesisHash( |
2491 | 0 | methods::HashHexString(me.genesis_block_hash), |
2492 | 0 | ) |
2493 | 0 | .to_json_response(request_id_json), |
2494 | 0 | ) |
2495 | 0 | .await; |
2496 | | } |
2497 | | |
2498 | | methods::MethodCall::chainSpec_v1_properties {} => { |
2499 | 0 | let _ = me |
2500 | 0 | .responses_tx |
2501 | 0 | .send( |
2502 | 0 | methods::Response::chainSpec_v1_properties( |
2503 | 0 | serde_json::from_str(&me.chain_properties_json).unwrap(), |
2504 | 0 | ) |
2505 | 0 | .to_json_response(request_id_json), |
2506 | 0 | ) |
2507 | 0 | .await; |
2508 | | } |
2509 | | |
2510 | 0 | methods::MethodCall::sudo_unstable_p2pDiscover { multiaddr } => { |
2511 | 0 | match multiaddr.parse::<multiaddr::Multiaddr>() { |
2512 | 0 | Ok(mut addr) |
2513 | 0 | if matches!( |
2514 | 0 | addr.iter().last(), |
2515 | | Some(multiaddr::Protocol::P2p(_)) |
2516 | | ) => |
2517 | | { |
2518 | 0 | let peer_id_bytes = match addr.iter().last() { |
2519 | 0 | Some(multiaddr::Protocol::P2p(peer_id)) => { |
2520 | 0 | peer_id.into_bytes().to_owned() |
2521 | | } |
2522 | 0 | _ => unreachable!(), |
2523 | | }; |
2524 | 0 | addr.pop(); |
2525 | 0 |
|
2526 | 0 | match PeerId::from_bytes(peer_id_bytes) { |
2527 | 0 | Ok(peer_id) => { |
2528 | 0 | me.network_service |
2529 | 0 | .discover( |
2530 | 0 | iter::once((peer_id, iter::once(addr))), |
2531 | 0 | false, |
2532 | 0 | ) |
2533 | 0 | .await; |
2534 | 0 | let _ = me |
2535 | 0 | .responses_tx |
2536 | 0 | .send( |
2537 | 0 | methods::Response::sudo_unstable_p2pDiscover(()) |
2538 | 0 | .to_json_response(request_id_json), |
2539 | 0 | ) |
2540 | 0 | .await; |
2541 | | } |
2542 | | Err(_) => { |
2543 | 0 | let _ = me |
2544 | 0 | .responses_tx |
2545 | 0 | .send(parse::build_error_response( |
2546 | 0 | request_id_json, |
2547 | 0 | parse::ErrorResponse::InvalidParams, |
2548 | 0 | Some( |
2549 | 0 | &serde_json::to_string( |
2550 | 0 | "multiaddr doesn't end with /p2p", |
2551 | 0 | ) |
2552 | 0 | .unwrap_or_else(|_| unreachable!()), Unexecuted instantiation: _RNCNCINvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background3runpE0sj_0Ba_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sj_0B1f_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runpE0sj_0Ba_ |
2553 | 0 | ), |
2554 | 0 | )) |
2555 | 0 | .await; |
2556 | | } |
2557 | | } |
2558 | | } |
2559 | | Ok(_) => { |
2560 | 0 | let _ = me |
2561 | 0 | .responses_tx |
2562 | 0 | .send(parse::build_error_response( |
2563 | 0 | request_id_json, |
2564 | 0 | parse::ErrorResponse::InvalidParams, |
2565 | 0 | Some( |
2566 | 0 | &serde_json::to_string( |
2567 | 0 | "multiaddr doesn't end with /p2p", |
2568 | 0 | ) |
2569 | 0 | .unwrap_or_else(|_| unreachable!()), Unexecuted instantiation: _RNCNCINvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background3runpE0sk_0Ba_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sk_0B1f_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runpE0sk_0Ba_ |
2570 | 0 | ), |
2571 | 0 | )) |
2572 | 0 | .await; |
2573 | | } |
2574 | 0 | Err(err) => { |
2575 | 0 | let _ = me |
2576 | 0 | .responses_tx |
2577 | 0 | .send(parse::build_error_response( |
2578 | 0 | request_id_json, |
2579 | 0 | parse::ErrorResponse::InvalidParams, |
2580 | 0 | Some( |
2581 | 0 | &serde_json::to_string(&err.to_string()) |
2582 | 0 | .unwrap_or_else(|_| unreachable!()), Unexecuted instantiation: _RNCNCINvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background3runpE0sl_0Ba_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sl_0B1f_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runpE0sl_0Ba_ |
2583 | 0 | ), |
2584 | 0 | )) |
2585 | 0 | .await; |
2586 | | } |
2587 | | } |
2588 | | } |
2589 | | |
2590 | | methods::MethodCall::sudo_unstable_version {} => { |
2591 | 0 | let _ = me |
2592 | 0 | .responses_tx |
2593 | 0 | .send( |
2594 | 0 | methods::Response::sudo_unstable_version( |
2595 | 0 | format!("{} {}", me.system_name, me.system_version).into(), |
2596 | 0 | ) |
2597 | 0 | .to_json_response(request_id_json), |
2598 | 0 | ) |
2599 | 0 | .await; |
2600 | | } |
2601 | | |
2602 | 0 | request_parsed @ (methods::MethodCall::transaction_v1_broadcast { .. } |
2603 | | | methods::MethodCall::transactionWatch_v1_submitAndWatch { |
2604 | | .. |
2605 | | }) => { |
2606 | 0 | let (transaction, watched) = match request_parsed { |
2607 | | methods::MethodCall::transaction_v1_broadcast { |
2608 | 0 | transaction: methods::HexString(transaction), |
2609 | 0 | } => (transaction, false), |
2610 | | methods::MethodCall::transactionWatch_v1_submitAndWatch { |
2611 | 0 | transaction: methods::HexString(transaction), |
2612 | 0 | } => (transaction, true), |
2613 | 0 | _ => unreachable!(), |
2614 | | }; |
2615 | | |
2616 | 0 | let subscription_id = { |
2617 | 0 | let mut subscription_id = [0u8; 32]; |
2618 | 0 | me.randomness.fill_bytes(&mut subscription_id); |
2619 | 0 | bs58::encode(subscription_id).into_string() |
2620 | | }; |
2621 | | |
2622 | 0 | let _prev_value = me.transactions_subscriptions.insert( |
2623 | 0 | subscription_id.clone(), |
2624 | 0 | TransactionWatch { |
2625 | 0 | included_block: None, |
2626 | 0 | num_broadcasted_peers: 0, |
2627 | 0 | ty: if watched { |
2628 | 0 | TransactionWatchTy::NewApiWatch |
2629 | | } else { |
2630 | 0 | TransactionWatchTy::NewApi { |
2631 | 0 | transaction_bytes: transaction.clone(), |
2632 | 0 | } |
2633 | | }, |
2634 | | }, |
2635 | | ); |
2636 | 0 | debug_assert!(_prev_value.is_none()); |
2637 | | |
2638 | 0 | let mut transaction_updates = Box::pin( |
2639 | 0 | me.transactions_service |
2640 | 0 | .submit_and_watch_transaction(transaction, 16, watched) |
2641 | 0 | .await, |
2642 | | ); |
2643 | | |
2644 | 0 | let _ = me |
2645 | 0 | .responses_tx |
2646 | 0 | .send( |
2647 | 0 | if watched { |
2648 | 0 | methods::Response::transactionWatch_v1_submitAndWatch( |
2649 | 0 | Cow::Borrowed(&subscription_id), |
2650 | 0 | ) |
2651 | | } else { |
2652 | 0 | methods::Response::transaction_v1_broadcast(Cow::Borrowed( |
2653 | 0 | &subscription_id, |
2654 | 0 | )) |
2655 | | } |
2656 | 0 | .to_json_response(request_id_json), |
2657 | | ) |
2658 | 0 | .await; |
2659 | | |
2660 | | // A task is started that will yield when the transactions service |
2661 | | // generates a notification. |
2662 | | // Note that we do that even for `transaction_v1_broadcast`, as it is |
2663 | | // important to pull notifications from the channel in order to not |
2664 | | // clog it. |
2665 | 0 | me.background_tasks.push(Box::pin(async move { |
2666 | 0 | let Some(status) = transaction_updates.as_mut().next().await else { |
2667 | 0 | unreachable!() |
2668 | | }; |
2669 | 0 | Event::TransactionEvent { |
2670 | 0 | subscription_id, |
2671 | 0 | event: status, |
2672 | 0 | watcher: transaction_updates, |
2673 | 0 | } |
2674 | 0 | })); Unexecuted instantiation: _RNCNCINvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background3runpE0sm_0Ba_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sm_0B1f_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runpE0sm_0Ba_ |
2675 | | } |
2676 | | |
2677 | 0 | methods::MethodCall::transaction_v1_stop { operation_id } => { |
2678 | 0 | let exists = me |
2679 | 0 | .transactions_subscriptions |
2680 | 0 | .get(&*operation_id) |
2681 | 0 | .map_or(false, |sub| { |
2682 | 0 | matches!(sub.ty, TransactionWatchTy::NewApi { .. }) |
2683 | 0 | }); Unexecuted instantiation: _RNCNCINvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background3runpE0sn_0Ba_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sn_0B1f_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runpE0sn_0Ba_ |
2684 | 0 | if exists { |
2685 | 0 | me.transactions_subscriptions.remove(&*operation_id); |
2686 | 0 | let _ = me |
2687 | 0 | .responses_tx |
2688 | 0 | .send( |
2689 | 0 | methods::Response::transaction_v1_stop(()) |
2690 | 0 | .to_json_response(request_id_json), |
2691 | 0 | ) |
2692 | 0 | .await; |
2693 | | } else { |
2694 | 0 | let _ = me |
2695 | 0 | .responses_tx |
2696 | 0 | .send(parse::build_error_response( |
2697 | 0 | request_id_json, |
2698 | 0 | json_rpc::parse::ErrorResponse::InvalidParams, |
2699 | 0 | None, |
2700 | 0 | )) |
2701 | 0 | .await; |
2702 | | } |
2703 | | } |
2704 | | |
2705 | 0 | methods::MethodCall::transactionWatch_v1_unwatch { subscription } => { |
2706 | 0 | let exists = me |
2707 | 0 | .transactions_subscriptions |
2708 | 0 | .get(&*subscription) |
2709 | 0 | .map_or(false, |sub| { |
2710 | 0 | matches!(sub.ty, TransactionWatchTy::NewApiWatch) |
2711 | 0 | }); Unexecuted instantiation: _RNCNCINvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background3runpE0so_0Ba_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0so_0B1f_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runpE0so_0Ba_ |
2712 | 0 | if exists { |
2713 | 0 | me.transactions_subscriptions.remove(&*subscription); |
2714 | 0 | } |
2715 | 0 | let _ = me |
2716 | 0 | .responses_tx |
2717 | 0 | .send( |
2718 | 0 | methods::Response::transactionWatch_v1_unwatch(()) |
2719 | 0 | .to_json_response(request_id_json), |
2720 | 0 | ) |
2721 | 0 | .await; |
2722 | | } |
2723 | | |
2724 | 0 | _method @ (methods::MethodCall::account_nextIndex { .. } |
2725 | | | methods::MethodCall::author_hasKey { .. } |
2726 | | | methods::MethodCall::author_hasSessionKeys { .. } |
2727 | | | methods::MethodCall::author_insertKey { .. } |
2728 | | | methods::MethodCall::author_removeExtrinsic { .. } |
2729 | | | methods::MethodCall::author_rotateKeys { .. } |
2730 | | | methods::MethodCall::babe_epochAuthorship { .. } |
2731 | | | methods::MethodCall::childstate_getKeys { .. } |
2732 | | | methods::MethodCall::childstate_getStorage { .. } |
2733 | | | methods::MethodCall::childstate_getStorageHash { .. } |
2734 | | | methods::MethodCall::childstate_getStorageSize { .. } |
2735 | | | methods::MethodCall::grandpa_roundState { .. } |
2736 | | | methods::MethodCall::offchain_localStorageGet { .. } |
2737 | | | methods::MethodCall::offchain_localStorageSet { .. } |
2738 | | | methods::MethodCall::state_getPairs { .. } |
2739 | | | methods::MethodCall::state_getReadProof { .. } |
2740 | | | methods::MethodCall::state_getStorageHash { .. } |
2741 | | | methods::MethodCall::state_getStorageSize { .. } |
2742 | | | methods::MethodCall::state_queryStorage { .. } |
2743 | | | methods::MethodCall::system_addReservedPeer { .. } |
2744 | | | methods::MethodCall::system_dryRun { .. } |
2745 | | | methods::MethodCall::system_localPeerId { .. } |
2746 | | | methods::MethodCall::system_networkState { .. } |
2747 | | | methods::MethodCall::system_removeReservedPeer { .. } |
2748 | | | methods::MethodCall::sudo_network_unstable_watch { .. } |
2749 | | | methods::MethodCall::sudo_network_unstable_unwatch { .. }) => { |
2750 | | // TODO: implement the ones that make sense to implement ^ |
2751 | 0 | log!( |
2752 | 0 | &me.platform, |
2753 | 0 | Warn, |
2754 | 0 | &me.log_target, |
2755 | 0 | format!("JSON-RPC call not supported yet: {:?}", _method) |
2756 | 0 | ); |
2757 | 0 | let _ = me |
2758 | 0 | .responses_tx |
2759 | 0 | .send(parse::build_error_response( |
2760 | 0 | request_id_json, |
2761 | 0 | json_rpc::parse::ErrorResponse::ServerError( |
2762 | 0 | -32000, |
2763 | 0 | "Not implemented in smoldot yet", |
2764 | 0 | ), |
2765 | 0 | None, |
2766 | 0 | )) |
2767 | 0 | .await; |
2768 | | } |
2769 | | } |
2770 | | } |
2771 | | |
2772 | | WakeUpReason::AdvanceMultiStageRequest { |
2773 | 0 | request_id_json, |
2774 | 0 | stage: MultiStageRequestStage::BlockHashNotKnown, |
2775 | 0 | request_ty, |
2776 | 0 | } => { |
2777 | 0 | // A "multi-stage" request needs to know the best block hash. |
2778 | 0 | // If it is known, we switch it to the next stage, otherwise we push it in a |
2779 | 0 | // different list. |
2780 | 0 | match me.runtime_service_subscription { |
2781 | | RuntimeServiceSubscription::Pending { .. } |
2782 | 0 | | RuntimeServiceSubscription::NotCreated => { |
2783 | 0 | me.best_block_hash_pending |
2784 | 0 | .push((request_id_json, request_ty)); |
2785 | 0 | } |
2786 | | RuntimeServiceSubscription::Active { |
2787 | 0 | current_best_block, .. |
2788 | | } => { |
2789 | | // We special-case `state_getKeysPaged` as the results of previous similar |
2790 | | // requests are put in a cache. Perform a cache lookup now. |
2791 | | if let MultiStageRequestTy::StateGetKeysPaged { |
2792 | 0 | prefix, |
2793 | 0 | start_key, |
2794 | 0 | count, |
2795 | 0 | } = &request_ty |
2796 | | { |
2797 | 0 | if let Some(cache_entry) = |
2798 | 0 | me.state_get_keys_paged_cache.get(&GetKeysPagedCacheKey { |
2799 | 0 | hash: current_best_block, |
2800 | 0 | prefix: prefix.clone(), |
2801 | 0 | }) |
2802 | | { |
2803 | | // Cache hit! |
2804 | | // Filter by start key and count. |
2805 | 0 | let results_to_client = cache_entry |
2806 | 0 | .iter() |
2807 | 0 | .cloned() |
2808 | 0 | .filter(|k| start_key.as_ref().map_or(true, |s| *k > *s)) Unexecuted instantiation: _RNCNCINvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background3runpE0sp_0Ba_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sp_0B1f_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runpE0sp_0Ba_ Unexecuted instantiation: _RNCNCNCINvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background3runpE0sp_00Bc_ Unexecuted instantiation: _RNCNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sp_00B1h_ Unexecuted instantiation: _RNCNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runpE0sp_00Bc_ |
2809 | 0 | .map(methods::HexString) |
2810 | 0 | .take(usize::try_from(*count).unwrap_or(usize::MAX)) |
2811 | 0 | .collect::<Vec<_>>(); |
2812 | 0 | let _ = me |
2813 | 0 | .responses_tx |
2814 | 0 | .send( |
2815 | 0 | methods::Response::state_getKeysPaged(results_to_client) |
2816 | 0 | .to_json_response(&request_id_json), |
2817 | 0 | ) |
2818 | 0 | .await; |
2819 | 0 | continue; |
2820 | 0 | } |
2821 | 0 | } |
2822 | | |
2823 | 0 | me.multistage_requests_to_advance.push_back(( |
2824 | 0 | request_id_json, |
2825 | 0 | MultiStageRequestStage::BlockHashKnown { |
2826 | 0 | block_hash: current_best_block, |
2827 | 0 | }, |
2828 | 0 | request_ty, |
2829 | 0 | )); |
2830 | | } |
2831 | | } |
2832 | | } |
2833 | | |
2834 | | WakeUpReason::AdvanceMultiStageRequest { |
2835 | 0 | request_id_json, |
2836 | 0 | stage: |
2837 | 0 | MultiStageRequestStage::BlockHashKnown { block_hash, .. } |
2838 | 0 | | MultiStageRequestStage::BlockInfoKnown { block_hash, .. }, |
2839 | | request_ty: MultiStageRequestTy::ChainGetBestBlockHash, |
2840 | | } => { |
2841 | | // Handling `chain_getBlockHash` for the best block. |
2842 | 0 | let _ = me |
2843 | 0 | .responses_tx |
2844 | 0 | .send( |
2845 | 0 | methods::Response::chain_getBlockHash(methods::HashHexString(block_hash)) |
2846 | 0 | .to_json_response(&request_id_json), |
2847 | 0 | ) |
2848 | 0 | .await; |
2849 | | } |
2850 | | |
2851 | | WakeUpReason::AdvanceMultiStageRequest { |
2852 | 0 | request_id_json, |
2853 | 0 | stage: |
2854 | 0 | MultiStageRequestStage::BlockHashKnown { block_hash, .. } |
2855 | 0 | | MultiStageRequestStage::BlockInfoKnown { block_hash, .. }, |
2856 | | request_ty: MultiStageRequestTy::ChainGetBlock, |
2857 | 0 | } => { |
2858 | 0 | // Handling `chain_getBlock`. |
2859 | 0 |
|
2860 | 0 | // Try to determine the block number by looking for the block in cache. |
2861 | 0 | // The request can be fulfilled no matter whether the block number is |
2862 | 0 | // known or not, but knowing it will lead to a better selection of peers, |
2863 | 0 | // and thus increase the chances of the requests succeeding. |
2864 | 0 | let block_number = me |
2865 | 0 | .block_headers_cache |
2866 | 0 | .get(&block_hash) |
2867 | 0 | .and_then(|result| result.as_ref().ok().map(|(_, _, n)| *n)); Unexecuted instantiation: _RNCNCINvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background3runpE0sq_0Ba_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sq_0B1f_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runpE0sq_0Ba_ Unexecuted instantiation: _RNCNCNCINvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background3runpE0sq_00Bc_ Unexecuted instantiation: _RNCNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sq_00B1h_ Unexecuted instantiation: _RNCNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runpE0sq_00Bc_ |
2868 | 0 |
|
2869 | 0 | // Block bodies and headers aren't stored locally. Ask the network. |
2870 | 0 | me.background_tasks.push({ |
2871 | 0 | let sync_service = me.sync_service.clone(); |
2872 | 0 | let request_id_json = request_id_json.to_owned(); |
2873 | 0 | Box::pin(async move { |
2874 | 0 | let result = if let Some(block_number) = block_number { |
2875 | 0 | sync_service |
2876 | 0 | .block_query( |
2877 | 0 | block_number, |
2878 | 0 | block_hash, |
2879 | 0 | codec::BlocksRequestFields { |
2880 | 0 | header: true, |
2881 | 0 | body: true, |
2882 | 0 | justifications: false, |
2883 | 0 | }, |
2884 | 0 | 3, |
2885 | 0 | Duration::from_secs(8), |
2886 | 0 | NonZeroU32::new(1).unwrap(), |
2887 | 0 | ) |
2888 | 0 | .await |
2889 | | } else { |
2890 | 0 | sync_service |
2891 | 0 | .block_query_unknown_number( |
2892 | 0 | block_hash, |
2893 | 0 | codec::BlocksRequestFields { |
2894 | 0 | header: true, |
2895 | 0 | body: true, |
2896 | 0 | justifications: false, |
2897 | 0 | }, |
2898 | 0 | 3, |
2899 | 0 | Duration::from_secs(8), |
2900 | 0 | NonZeroU32::new(1).unwrap(), |
2901 | 0 | ) |
2902 | 0 | .await |
2903 | | }; |
2904 | 0 | Event::ChainGetBlockResult { |
2905 | 0 | request_id_json, |
2906 | 0 | result, |
2907 | 0 | expected_block_hash: block_hash, |
2908 | 0 | } |
2909 | 0 | }) Unexecuted instantiation: _RNCNCINvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background3runpE0sr_0Ba_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sr_0B1f_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runpE0sr_0Ba_ |
2910 | 0 | }); |
2911 | 0 | } |
2912 | | |
2913 | | WakeUpReason::AdvanceMultiStageRequest { |
2914 | 0 | request_id_json: request_id, |
2915 | 0 | stage: MultiStageRequestStage::BlockHashKnown { block_hash }, |
2916 | 0 | request_ty, |
2917 | | } => { |
2918 | | // A "multi-stage request" needs to know the information about the block with |
2919 | | // the given hash. |
2920 | | |
2921 | | // If the block's information is available in cache, switch it to the next stage. |
2922 | 0 | if let Some(in_cache) = me.block_headers_cache.get(&block_hash) { |
2923 | 0 | let &Ok((ref scale_encoded_header, block_state_trie_root_hash, block_number)) = |
2924 | 0 | in_cache |
2925 | | else { |
2926 | | // Block is known to not be decodable. |
2927 | 0 | let _ = me |
2928 | 0 | .responses_tx |
2929 | 0 | .send(parse::build_error_response( |
2930 | 0 | &request_id, |
2931 | 0 | parse::ErrorResponse::ServerError(-32000, "invalid block header"), |
2932 | 0 | None, |
2933 | 0 | )) |
2934 | 0 | .await; |
2935 | 0 | continue; |
2936 | | }; |
2937 | | |
2938 | | // Special-case `chain_getHeader`, as it is only needs to know the header |
2939 | | // of the block and doesn't need to be switched to a next stage. |
2940 | 0 | if matches!(request_ty, MultiStageRequestTy::ChainGetHeader) { |
2941 | 0 | match methods::Header::from_scale_encoded_header( |
2942 | 0 | scale_encoded_header, |
2943 | 0 | me.runtime_service.block_number_bytes(), |
2944 | 0 | ) { |
2945 | 0 | Ok(header) => { |
2946 | 0 | let _ = me |
2947 | 0 | .responses_tx |
2948 | 0 | .send( |
2949 | 0 | methods::Response::chain_getHeader(header) |
2950 | 0 | .to_json_response(&request_id), |
2951 | 0 | ) |
2952 | 0 | .await; |
2953 | | } |
2954 | 0 | Err(error) => { |
2955 | 0 | let _ = me |
2956 | 0 | .responses_tx |
2957 | 0 | .send(parse::build_error_response( |
2958 | 0 | &request_id, |
2959 | 0 | json_rpc::parse::ErrorResponse::ServerError( |
2960 | 0 | -32000, |
2961 | 0 | &format!("Failed to decode block header: {error}"), |
2962 | 0 | ), |
2963 | 0 | None, |
2964 | 0 | )) |
2965 | 0 | .await; |
2966 | | } |
2967 | | } |
2968 | 0 | continue; |
2969 | 0 | } |
2970 | 0 |
|
2971 | 0 | me.multistage_requests_to_advance.push_back(( |
2972 | 0 | request_id, |
2973 | 0 | MultiStageRequestStage::BlockInfoKnown { |
2974 | 0 | block_hash, |
2975 | 0 | block_state_trie_root_hash, |
2976 | 0 | block_number, |
2977 | 0 | }, |
2978 | 0 | request_ty, |
2979 | 0 | )); |
2980 | 0 | continue; |
2981 | 0 | } |
2982 | 0 |
|
2983 | 0 | // Value is not available in cache. |
2984 | 0 | match me.block_headers_pending.entry(block_hash) { |
2985 | 0 | hashbrown::hash_map::Entry::Occupied(entry) => { |
2986 | 0 | // We are already in the process of asking the networking service for |
2987 | 0 | // the block information. |
2988 | 0 | // Keep track of the request. |
2989 | 0 | debug_assert!(!entry.get().is_empty()); |
2990 | 0 | entry.into_mut().push((request_id, request_ty)); |
2991 | | } |
2992 | 0 | hashbrown::hash_map::Entry::Vacant(entry) => { |
2993 | 0 | // No network request is in progress yet. Start one. |
2994 | 0 | me.background_tasks.push({ |
2995 | 0 | let block_info_retrieve_future = |
2996 | 0 | me.sync_service.clone().block_query_unknown_number( |
2997 | 0 | block_hash, |
2998 | 0 | codec::BlocksRequestFields { |
2999 | 0 | header: true, |
3000 | 0 | body: false, |
3001 | 0 | justifications: false, |
3002 | 0 | }, |
3003 | 0 | 3, |
3004 | 0 | Duration::from_secs(5), |
3005 | 0 | NonZeroU32::new(1).unwrap_or_else(|| unreachable!()), Unexecuted instantiation: _RNCNCINvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background3runpE0ss_0Ba_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0ss_0B1f_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runpE0ss_0Ba_ |
3006 | 0 | ); |
3007 | 0 | let block_number_bytes = me.runtime_service.block_number_bytes(); |
3008 | 0 | Box::pin(async move { |
3009 | 0 | let result = match block_info_retrieve_future.await { |
3010 | 0 | Ok(result) => match result.header { |
3011 | 0 | Some(scale_header) => { |
3012 | 0 | if header::hash_from_scale_encoded_header(&scale_header) |
3013 | 0 | == block_hash |
3014 | | { |
3015 | 0 | Ok(header::decode( |
3016 | 0 | &scale_header, |
3017 | 0 | block_number_bytes, |
3018 | 0 | ) |
3019 | 0 | .map(|header| { |
3020 | 0 | ( |
3021 | 0 | scale_header.clone(), |
3022 | 0 | *header.state_root, |
3023 | 0 | header.number, |
3024 | 0 | ) |
3025 | 0 | })) Unexecuted instantiation: _RNCNCNCINvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background3runpE0st_00Bc_ Unexecuted instantiation: _RNCNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0st_00B1h_ Unexecuted instantiation: _RNCNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runpE0st_00Bc_ |
3026 | | } else { |
3027 | 0 | Err(()) |
3028 | | } |
3029 | | } |
3030 | 0 | None => Err(()), |
3031 | | }, |
3032 | 0 | Err(()) => Err(()), |
3033 | | }; |
3034 | 0 | Event::BlockInfoRetrieved { block_hash, result } |
3035 | 0 | }) Unexecuted instantiation: _RNCNCINvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background3runpE0st_0Ba_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0st_0B1f_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runpE0st_0Ba_ |
3036 | 0 | }); |
3037 | 0 |
|
3038 | 0 | // Keep track of the request. |
3039 | 0 | let mut list = Vec::with_capacity(4); |
3040 | 0 | list.push((request_id, request_ty)); |
3041 | 0 | entry.insert(list); |
3042 | 0 | } |
3043 | | } |
3044 | | } |
3045 | | |
3046 | | WakeUpReason::AdvanceMultiStageRequest { |
3047 | | stage: MultiStageRequestStage::BlockInfoKnown { .. }, |
3048 | | request_ty: MultiStageRequestTy::ChainGetHeader, |
3049 | | .. |
3050 | | } => { |
3051 | | // `chain_getHeader` should never reach this stage. |
3052 | 0 | unreachable!() |
3053 | | } |
3054 | | |
3055 | | WakeUpReason::AdvanceMultiStageRequest { |
3056 | 0 | request_id_json, |
3057 | | stage: |
3058 | | MultiStageRequestStage::BlockInfoKnown { |
3059 | 0 | block_hash, |
3060 | 0 | block_state_trie_root_hash, |
3061 | 0 | block_number, |
3062 | | }, |
3063 | | request_ty: |
3064 | 0 | request_ty @ (MultiStageRequestTy::StateGetRuntimeVersion |
3065 | | | MultiStageRequestTy::StateCall { .. } |
3066 | | | MultiStageRequestTy::StateGetMetadata |
3067 | | | MultiStageRequestTy::PaymentQueryInfo { .. } |
3068 | | | MultiStageRequestTy::SystemAccountNextIndex { .. }), |
3069 | | } => { |
3070 | | // A runtime-related JSON-RPC function needs access to the runtime of the |
3071 | | // given block. |
3072 | | |
3073 | | // If the value is available in cache, do the runtime call. |
3074 | 0 | if let Some(in_cache) = me.block_runtimes_cache.get(&block_hash) { |
3075 | | // Special-case `state_getRuntimeVersion` as it only needs access to the |
3076 | | // runtime but not do any call. |
3077 | 0 | if let MultiStageRequestTy::StateGetRuntimeVersion = &request_ty { |
3078 | 0 | match me |
3079 | 0 | .runtime_service |
3080 | 0 | .pinned_runtime_specification(in_cache.clone()) |
3081 | 0 | .await |
3082 | | { |
3083 | 0 | Ok(spec) => { |
3084 | 0 | let _ = me |
3085 | 0 | .responses_tx |
3086 | 0 | .send( |
3087 | 0 | methods::Response::state_getRuntimeVersion( |
3088 | 0 | convert_runtime_version_legacy(&spec), |
3089 | 0 | ) |
3090 | 0 | .to_json_response(&request_id_json), |
3091 | 0 | ) |
3092 | 0 | .await; |
3093 | | } |
3094 | 0 | Err(error) => { |
3095 | 0 | let _ = me |
3096 | 0 | .responses_tx |
3097 | 0 | .send(parse::build_error_response( |
3098 | 0 | &request_id_json, |
3099 | 0 | json_rpc::parse::ErrorResponse::ServerError( |
3100 | 0 | -32000, |
3101 | 0 | &error.to_string(), |
3102 | 0 | ), |
3103 | 0 | None, |
3104 | 0 | )) |
3105 | 0 | .await; |
3106 | | } |
3107 | | } |
3108 | 0 | continue; |
3109 | 0 | } |
3110 | | |
3111 | | // Determine the parameters of the runtime call to start. |
3112 | 0 | let (function_name, required_api_version, parameters_vectored, request_update) = |
3113 | 0 | match request_ty { |
3114 | 0 | MultiStageRequestTy::StateCall { name, parameters } => ( |
3115 | 0 | name, |
3116 | 0 | None, |
3117 | 0 | parameters, |
3118 | 0 | RuntimeCallRequestInProgress::StateCall, |
3119 | 0 | ), |
3120 | 0 | MultiStageRequestTy::StateGetMetadata => ( |
3121 | 0 | "Metadata_metadata".to_owned(), |
3122 | 0 | Some(("Metadata".to_owned(), 1..=2)), |
3123 | 0 | Vec::new(), |
3124 | 0 | RuntimeCallRequestInProgress::StateGetMetadata, |
3125 | 0 | ), |
3126 | 0 | MultiStageRequestTy::PaymentQueryInfo { extrinsic } => { |
3127 | 0 | ( |
3128 | 0 | json_rpc::payment_info::PAYMENT_FEES_FUNCTION_NAME.to_owned(), |
3129 | 0 | Some(("TransactionPaymentApi".to_owned(), 1..=2)), |
3130 | 0 | json_rpc::payment_info::payment_info_parameters(&extrinsic) |
3131 | 0 | .fold(Vec::new(), |mut a, b| { |
3132 | 0 | a.extend_from_slice(b.as_ref()); |
3133 | 0 | a |
3134 | 0 | }), Unexecuted instantiation: _RNCNCINvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background3runpE0su_0Ba_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0su_0B1f_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runpE0su_0Ba_ |
3135 | 0 | RuntimeCallRequestInProgress::PaymentQueryInfo, |
3136 | 0 | ) |
3137 | | } |
3138 | 0 | MultiStageRequestTy::SystemAccountNextIndex { account_id } => ( |
3139 | 0 | "AccountNonceApi_account_nonce".to_owned(), |
3140 | 0 | Some(("AccountNonceApi".to_owned(), 1..=1)), |
3141 | 0 | account_id, |
3142 | 0 | RuntimeCallRequestInProgress::SystemAccountNextIndex, |
3143 | 0 | ), |
3144 | 0 | _ => unreachable!(), |
3145 | | }; |
3146 | | |
3147 | | // Start the runtime call in the background. |
3148 | 0 | let runtime_service = me.runtime_service.clone(); |
3149 | 0 | let in_cache = in_cache.clone(); |
3150 | 0 | me.background_tasks.push(Box::pin(async move { |
3151 | 0 | Event::LegacyApiFunctionRuntimeCallResult { |
3152 | 0 | request_id_json, |
3153 | 0 | request: request_update, |
3154 | 0 | result: runtime_service |
3155 | 0 | .runtime_call( |
3156 | 0 | in_cache, |
3157 | 0 | block_hash, |
3158 | 0 | block_number, |
3159 | 0 | block_state_trie_root_hash, |
3160 | 0 | function_name, |
3161 | 0 | required_api_version, |
3162 | 0 | parameters_vectored, |
3163 | 0 | 3, |
3164 | 0 | Duration::from_secs(5), |
3165 | 0 | NonZeroU32::new(1).unwrap_or_else(|| unreachable!()), Unexecuted instantiation: _RNCNCNCINvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background3runpE0sv_00Bc_ Unexecuted instantiation: _RNCNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sv_00B1h_ Unexecuted instantiation: _RNCNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runpE0sv_00Bc_ |
3166 | 0 | ) |
3167 | 0 | .await, |
3168 | | } |
3169 | 0 | })); Unexecuted instantiation: _RNCNCINvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background3runpE0sv_0Ba_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sv_0B1f_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runpE0sv_0Ba_ |
3170 | 0 | continue; |
3171 | 0 | } |
3172 | 0 |
|
3173 | 0 | // Runtime is not available in cache. Download it from the network. |
3174 | 0 | match me.block_runtimes_pending.entry(block_hash) { |
3175 | 0 | hashbrown::hash_map::Entry::Occupied(entry) => { |
3176 | 0 | // We are already in the process of asking the networking service for |
3177 | 0 | // the runtime. |
3178 | 0 | // Keep track of the request. |
3179 | 0 | debug_assert!(!entry.get().is_empty()); |
3180 | 0 | entry.into_mut().push((request_id_json, request_ty)); |
3181 | | } |
3182 | 0 | hashbrown::hash_map::Entry::Vacant(entry) => { |
3183 | 0 | // No network request is in progress yet. Start one. |
3184 | 0 | me.background_tasks.push(Box::pin({ |
3185 | 0 | let sync_service = me.sync_service.clone(); |
3186 | 0 | let runtime_service = me.runtime_service.clone(); |
3187 | 0 | // TODO: move to separate function |
3188 | 0 | async move { |
3189 | | let ( |
3190 | 0 | storage_code, |
3191 | 0 | storage_heap_pages, |
3192 | 0 | code_merkle_value, |
3193 | 0 | code_closest_ancestor_excluding, |
3194 | | ) = { |
3195 | 0 | let mut storage_code = None; |
3196 | 0 | let mut storage_heap_pages = None; |
3197 | 0 | let mut code_merkle_value = None; |
3198 | 0 | let mut code_closest_ancestor_excluding = None; |
3199 | | |
3200 | 0 | let mut query = |
3201 | 0 | sync_service |
3202 | 0 | .storage_query( |
3203 | 0 | block_number, |
3204 | 0 | block_hash, |
3205 | 0 | block_state_trie_root_hash, |
3206 | 0 | [ |
3207 | 0 | sync_service::StorageRequestItem { |
3208 | 0 | key: b":code".to_vec(), |
3209 | 0 | ty: sync_service::StorageRequestItemTy::ClosestDescendantMerkleValue, |
3210 | 0 | }, |
3211 | 0 | sync_service::StorageRequestItem { |
3212 | 0 | key: b":code".to_vec(), |
3213 | 0 | ty: sync_service::StorageRequestItemTy::Value, |
3214 | 0 | }, |
3215 | 0 | sync_service::StorageRequestItem { |
3216 | 0 | key: b":heappages".to_vec(), |
3217 | 0 | ty: sync_service::StorageRequestItemTy::Value, |
3218 | 0 | }, |
3219 | 0 | ] |
3220 | 0 | .into_iter(), |
3221 | 0 | 3, |
3222 | 0 | Duration::from_secs(20), |
3223 | 0 | NonZeroU32::new(1).unwrap(), |
3224 | 0 | ) |
3225 | 0 | .advance() |
3226 | 0 | .await; |
3227 | | |
3228 | | loop { |
3229 | 0 | match query { |
3230 | | sync_service::StorageQueryProgress::Finished => { |
3231 | 0 | break ( |
3232 | 0 | storage_code, |
3233 | 0 | storage_heap_pages, |
3234 | 0 | code_merkle_value, |
3235 | 0 | code_closest_ancestor_excluding, |
3236 | 0 | ) |
3237 | | } |
3238 | | sync_service::StorageQueryProgress::Progress { |
3239 | | request_index: 0, |
3240 | | item: |
3241 | | sync_service::StorageResultItem::ClosestDescendantMerkleValue { |
3242 | 0 | closest_descendant_merkle_value, |
3243 | 0 | found_closest_ancestor_excluding, |
3244 | 0 | .. |
3245 | 0 | }, |
3246 | 0 | query: next, |
3247 | 0 | } => { |
3248 | 0 | code_merkle_value = closest_descendant_merkle_value; |
3249 | 0 | code_closest_ancestor_excluding = found_closest_ancestor_excluding; |
3250 | 0 | query = next.advance().await; |
3251 | | } |
3252 | | sync_service::StorageQueryProgress::Progress { |
3253 | | request_index: 1, |
3254 | 0 | item: sync_service::StorageResultItem::Value { value, .. }, |
3255 | 0 | query: next, |
3256 | 0 | } => { |
3257 | 0 | storage_code = value; |
3258 | 0 | query = next.advance().await; |
3259 | | } |
3260 | | sync_service::StorageQueryProgress::Progress { |
3261 | | request_index: 2, |
3262 | 0 | item: sync_service::StorageResultItem::Value { value, .. }, |
3263 | 0 | query: next, |
3264 | 0 | } => { |
3265 | 0 | storage_heap_pages = value; |
3266 | 0 | query = next.advance().await; |
3267 | | } |
3268 | 0 | sync_service::StorageQueryProgress::Progress { .. } => unreachable!(), |
3269 | 0 | sync_service::StorageQueryProgress::Error(error) => { |
3270 | 0 | return Event::RuntimeDownloaded { |
3271 | 0 | block_hash, |
3272 | 0 | result: Err(error.to_string()), |
3273 | 0 | } |
3274 | | } |
3275 | | } |
3276 | | } |
3277 | | }; |
3278 | | |
3279 | | // Give the code and heap pages to the runtime service. The runtime service will |
3280 | | // try to find any similar runtime it might have, and if not will compile it. |
3281 | 0 | let pinned_runtime = runtime_service |
3282 | 0 | .compile_and_pin_runtime( |
3283 | 0 | storage_code, |
3284 | 0 | storage_heap_pages, |
3285 | 0 | code_merkle_value, |
3286 | 0 | code_closest_ancestor_excluding, |
3287 | 0 | ) |
3288 | 0 | .await; |
3289 | | |
3290 | 0 | Event::RuntimeDownloaded { |
3291 | 0 | block_hash, |
3292 | 0 | result: pinned_runtime.map_err(|error| error.to_string()), Unexecuted instantiation: _RNCNCNCINvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background3runpE0sw_00Bc_ Unexecuted instantiation: _RNCNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sw_00B1h_ Unexecuted instantiation: _RNCNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runpE0sw_00Bc_ |
3293 | 0 | } |
3294 | 0 | } Unexecuted instantiation: _RNCNCINvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background3runpE0sw_0Ba_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sw_0B1f_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runpE0sw_0Ba_ |
3295 | 0 | })); |
3296 | 0 |
|
3297 | 0 | // Keep track of the request. |
3298 | 0 | let mut list = Vec::with_capacity(4); |
3299 | 0 | list.push((request_id_json, request_ty)); |
3300 | 0 | entry.insert(list); |
3301 | 0 | } |
3302 | | } |
3303 | | } |
3304 | | |
3305 | | WakeUpReason::Event(Event::LegacyApiFunctionRuntimeCallResult { |
3306 | 0 | request_id_json, |
3307 | 0 | request, |
3308 | 0 | result, |
3309 | 0 | }) => { |
3310 | 0 | // A runtime-call-related JSON-RPC API has finished its runtime call. |
3311 | 0 | match (result, request) { |
3312 | 0 | (Ok(result), RuntimeCallRequestInProgress::StateCall) => { |
3313 | 0 | let _ = me |
3314 | 0 | .responses_tx |
3315 | 0 | .send( |
3316 | 0 | methods::Response::state_call(methods::HexString(result.output)) |
3317 | 0 | .to_json_response(&request_id_json), |
3318 | 0 | ) |
3319 | 0 | .await; |
3320 | | } |
3321 | 0 | (Ok(result), RuntimeCallRequestInProgress::StateGetMetadata) => { |
3322 | 0 | match methods::remove_metadata_length_prefix(&result.output) { |
3323 | 0 | Ok(metadata) => { |
3324 | 0 | let _ = me |
3325 | 0 | .responses_tx |
3326 | 0 | .send( |
3327 | 0 | methods::Response::state_getMetadata(methods::HexString( |
3328 | 0 | metadata.to_owned(), |
3329 | 0 | )) |
3330 | 0 | .to_json_response(&request_id_json), |
3331 | 0 | ) |
3332 | 0 | .await; |
3333 | | } |
3334 | 0 | Err(error) => { |
3335 | 0 | log!( |
3336 | 0 | &me.platform, |
3337 | 0 | Warn, |
3338 | 0 | &me.log_target, |
3339 | 0 | format!("Failed to decode metadata. Error: {error}") |
3340 | 0 | ); |
3341 | 0 | let _ = me |
3342 | 0 | .responses_tx |
3343 | 0 | .send(parse::build_error_response( |
3344 | 0 | &request_id_json, |
3345 | 0 | parse::ErrorResponse::ServerError( |
3346 | 0 | -32000, |
3347 | 0 | &error.to_string(), |
3348 | 0 | ), |
3349 | 0 | None, |
3350 | 0 | )) |
3351 | 0 | .await; |
3352 | | } |
3353 | | } |
3354 | | } |
3355 | 0 | (Ok(result), RuntimeCallRequestInProgress::PaymentQueryInfo) => { |
3356 | 0 | match json_rpc::payment_info::decode_payment_info( |
3357 | 0 | &result.output, |
3358 | 0 | // `api_version` is guaranteed to be `Some` if we passed an API |
3359 | 0 | // requirement when calling `runtime_call`, which we always do. |
3360 | 0 | result.api_version.unwrap(), |
3361 | 0 | ) { |
3362 | 0 | Ok(info) => { |
3363 | 0 | let _ = me |
3364 | 0 | .responses_tx |
3365 | 0 | .send( |
3366 | 0 | methods::Response::payment_queryInfo(info) |
3367 | 0 | .to_json_response(&request_id_json), |
3368 | 0 | ) |
3369 | 0 | .await; |
3370 | | } |
3371 | 0 | Err(error) => { |
3372 | 0 | let _ = me |
3373 | 0 | .responses_tx |
3374 | 0 | .send(parse::build_error_response( |
3375 | 0 | &request_id_json, |
3376 | 0 | parse::ErrorResponse::ServerError( |
3377 | 0 | -32000, |
3378 | 0 | &format!("Failed to decode runtime output: {error}"), |
3379 | 0 | ), |
3380 | 0 | None, |
3381 | 0 | )) |
3382 | 0 | .await; |
3383 | | } |
3384 | | } |
3385 | | } |
3386 | 0 | (Ok(result), RuntimeCallRequestInProgress::SystemAccountNextIndex) => { |
3387 | 0 | // TODO: we get a u32 when expecting a u64; figure out problem |
3388 | 0 | match <[u8; 4]>::try_from(&result.output[..]) { |
3389 | 0 | Ok(index) => { |
3390 | 0 | let _ = me |
3391 | 0 | .responses_tx |
3392 | 0 | .send( |
3393 | 0 | methods::Response::system_accountNextIndex(u64::from( |
3394 | 0 | u32::from_le_bytes(index), |
3395 | 0 | )) |
3396 | 0 | .to_json_response(&request_id_json), |
3397 | 0 | ) |
3398 | 0 | .await; |
3399 | | } |
3400 | | Err(_) => { |
3401 | 0 | let _ = me |
3402 | 0 | .responses_tx |
3403 | 0 | .send(parse::build_error_response( |
3404 | 0 | &request_id_json, |
3405 | 0 | parse::ErrorResponse::ServerError( |
3406 | 0 | -32000, |
3407 | 0 | &format!("Failed to decode runtime output"), |
3408 | 0 | ), |
3409 | 0 | None, |
3410 | 0 | )) |
3411 | 0 | .await; |
3412 | | } |
3413 | | } |
3414 | | } |
3415 | 0 | (Err(error), request) => { |
3416 | 0 | if matches!(request, RuntimeCallRequestInProgress::StateGetMetadata) { |
3417 | 0 | log!( |
3418 | 0 | &me.platform, |
3419 | 0 | Warn, |
3420 | 0 | &me.log_target, |
3421 | 0 | format!( |
3422 | 0 | "Returning error from `state_getMetadata`. API user might \ |
3423 | 0 | not function properly. Error: {error}" |
3424 | 0 | ) |
3425 | 0 | ); |
3426 | 0 | } |
3427 | | |
3428 | 0 | let _ = me |
3429 | 0 | .responses_tx |
3430 | 0 | .send(parse::build_error_response( |
3431 | 0 | &request_id_json, |
3432 | 0 | parse::ErrorResponse::ServerError(-32000, &error.to_string()), |
3433 | 0 | None, |
3434 | 0 | )) |
3435 | 0 | .await; |
3436 | | } |
3437 | | } |
3438 | | } |
3439 | | |
3440 | | WakeUpReason::AdvanceMultiStageRequest { |
3441 | 0 | request_id_json, |
3442 | | stage: |
3443 | | MultiStageRequestStage::BlockInfoKnown { |
3444 | 0 | block_hash, |
3445 | 0 | block_state_trie_root_hash, |
3446 | 0 | block_number, |
3447 | | }, |
3448 | | request_ty: |
3449 | 0 | request_ty @ (MultiStageRequestTy::StateGetKeys { .. } |
3450 | | | MultiStageRequestTy::StateGetKeysPaged { .. } |
3451 | | | MultiStageRequestTy::StateQueryStorageAt { .. } |
3452 | | | MultiStageRequestTy::StateGetStorage { .. }), |
3453 | 0 | } => { |
3454 | 0 | // A storage-related JSON-RPC function can make progress. |
3455 | 0 | // Build and start a background task that performs the actual storage request. |
3456 | 0 | let (request, storage_request) = match request_ty { |
3457 | 0 | MultiStageRequestTy::StateGetKeys { prefix } => ( |
3458 | 0 | StorageRequestInProgress::StateGetKeys { |
3459 | 0 | in_progress_results: Vec::with_capacity(32), |
3460 | 0 | }, |
3461 | 0 | either::Left(iter::once(sync_service::StorageRequestItem { |
3462 | 0 | key: prefix.clone(), |
3463 | 0 | ty: sync_service::StorageRequestItemTy::DescendantsHashes, |
3464 | 0 | })), |
3465 | 0 | ), |
3466 | | MultiStageRequestTy::StateGetKeysPaged { |
3467 | 0 | prefix, |
3468 | 0 | start_key, |
3469 | 0 | count, |
3470 | 0 | } => ( |
3471 | 0 | StorageRequestInProgress::StateGetKeysPaged { |
3472 | 0 | in_progress_results: Vec::with_capacity(32), |
3473 | 0 | block_hash, |
3474 | 0 | prefix: prefix.clone(), |
3475 | 0 | start_key, |
3476 | 0 | count, |
3477 | 0 | }, |
3478 | 0 | either::Left(iter::once(sync_service::StorageRequestItem { |
3479 | 0 | key: prefix, |
3480 | 0 | ty: sync_service::StorageRequestItemTy::DescendantsHashes, |
3481 | 0 | })), |
3482 | 0 | ), |
3483 | 0 | MultiStageRequestTy::StateQueryStorageAt { keys } => ( |
3484 | 0 | StorageRequestInProgress::StateQueryStorageAt { |
3485 | 0 | block_hash, |
3486 | 0 | in_progress_results: Vec::with_capacity(keys.len()), |
3487 | 0 | }, |
3488 | 0 | either::Right(keys.into_iter().map(|key| { |
3489 | 0 | sync_service::StorageRequestItem { |
3490 | 0 | key: key.0, |
3491 | 0 | ty: sync_service::StorageRequestItemTy::Value, |
3492 | 0 | } |
3493 | 0 | })), Unexecuted instantiation: _RNCNCINvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background3runpE0sx_0Ba_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sx_0B1f_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runpE0sx_0Ba_ |
3494 | 0 | ), |
3495 | 0 | MultiStageRequestTy::StateGetStorage { key } => ( |
3496 | 0 | StorageRequestInProgress::StateGetStorage {}, |
3497 | 0 | either::Left(iter::once(sync_service::StorageRequestItem { |
3498 | 0 | key, |
3499 | 0 | ty: sync_service::StorageRequestItemTy::Value, |
3500 | 0 | })), |
3501 | 0 | ), |
3502 | 0 | _ => unreachable!(), |
3503 | | }; |
3504 | | |
3505 | 0 | let storage_query = me.sync_service.clone().storage_query( |
3506 | 0 | block_number, |
3507 | 0 | block_hash, |
3508 | 0 | block_state_trie_root_hash, |
3509 | 0 | storage_request, |
3510 | 0 | 3, |
3511 | 0 | Duration::from_secs(10), |
3512 | 0 | NonZeroU32::new(1).unwrap_or_else(|| unreachable!()), Unexecuted instantiation: _RNCNCINvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background3runpE0sy_0Ba_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sy_0B1f_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runpE0sy_0Ba_ |
3513 | 0 | ); |
3514 | 0 |
|
3515 | 0 | me.background_tasks.push(Box::pin(async move { |
3516 | 0 | Event::LegacyApiFunctionStorageRequestProgress { |
3517 | 0 | request_id_json, |
3518 | 0 | request, |
3519 | 0 | progress: storage_query.advance().await, |
3520 | | } |
3521 | 0 | })); Unexecuted instantiation: _RNCNCINvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background3runpE0sz_0Ba_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sz_0B1f_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runpE0sz_0Ba_ |
3522 | | } |
3523 | | |
3524 | | WakeUpReason::Event(Event::LegacyApiFunctionStorageRequestProgress { |
3525 | 0 | request_id_json, |
3526 | 0 | request, |
3527 | 0 | progress, |
3528 | 0 | }) => { |
3529 | 0 | // The background task of a storage-related JSON-RPC function has made progress. |
3530 | 0 | match (progress, request) { |
3531 | | ( |
3532 | | sync_service::StorageQueryProgress::Progress { |
3533 | 0 | item: sync_service::StorageResultItem::DescendantHash { key, .. }, |
3534 | 0 | query: next, |
3535 | 0 | .. |
3536 | 0 | }, |
3537 | 0 | StorageRequestInProgress::StateGetKeys { |
3538 | 0 | mut in_progress_results, |
3539 | 0 | }, |
3540 | 0 | ) => { |
3541 | 0 | // Continue finding descendants. |
3542 | 0 | in_progress_results.push(methods::HexString(key)); |
3543 | 0 | me.background_tasks.push(Box::pin(async move { |
3544 | 0 | Event::LegacyApiFunctionStorageRequestProgress { |
3545 | 0 | request_id_json, |
3546 | 0 | request: StorageRequestInProgress::StateGetKeys { |
3547 | 0 | in_progress_results, |
3548 | 0 | }, |
3549 | 0 | progress: next.advance().await, |
3550 | | } |
3551 | 0 | })); Unexecuted instantiation: _RNCNCINvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background3runpE0sA_0Ba_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sA_0B1f_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runpE0sA_0Ba_ |
3552 | 0 | } |
3553 | | ( |
3554 | | sync_service::StorageQueryProgress::Finished, |
3555 | | StorageRequestInProgress::StateGetKeys { |
3556 | 0 | in_progress_results, |
3557 | 0 | }, |
3558 | 0 | ) => { |
3559 | 0 | // Finished. |
3560 | 0 | let _ = me |
3561 | 0 | .responses_tx |
3562 | 0 | .send( |
3563 | 0 | methods::Response::state_getKeys(in_progress_results) |
3564 | 0 | .to_json_response(&request_id_json), |
3565 | 0 | ) |
3566 | 0 | .await; |
3567 | | } |
3568 | | ( |
3569 | | sync_service::StorageQueryProgress::Progress { |
3570 | 0 | item: sync_service::StorageResultItem::DescendantHash { key, .. }, |
3571 | 0 | query: next, |
3572 | 0 | .. |
3573 | 0 | }, |
3574 | 0 | StorageRequestInProgress::StateGetKeysPaged { |
3575 | 0 | mut in_progress_results, |
3576 | 0 | block_hash, |
3577 | 0 | prefix, |
3578 | 0 | start_key, |
3579 | 0 | count, |
3580 | 0 | }, |
3581 | 0 | ) => { |
3582 | 0 | // Continue finding descendants. |
3583 | 0 | in_progress_results.push(key); |
3584 | 0 | me.background_tasks.push(Box::pin(async move { |
3585 | 0 | Event::LegacyApiFunctionStorageRequestProgress { |
3586 | 0 | request_id_json, |
3587 | 0 | request: StorageRequestInProgress::StateGetKeysPaged { |
3588 | 0 | in_progress_results, |
3589 | 0 | block_hash, |
3590 | 0 | prefix, |
3591 | 0 | start_key, |
3592 | 0 | count, |
3593 | 0 | }, |
3594 | 0 | progress: next.advance().await, |
3595 | | } |
3596 | 0 | })); Unexecuted instantiation: _RNCNCINvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background3runpE0sB_0Ba_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sB_0B1f_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runpE0sB_0Ba_ |
3597 | 0 | } |
3598 | | ( |
3599 | | sync_service::StorageQueryProgress::Finished, |
3600 | | StorageRequestInProgress::StateGetKeysPaged { |
3601 | 0 | block_hash, |
3602 | 0 | in_progress_results: final_results, |
3603 | 0 | prefix, |
3604 | 0 | start_key, |
3605 | 0 | count, |
3606 | 0 | }, |
3607 | 0 | ) => { |
3608 | 0 | // Finished. |
3609 | 0 |
|
3610 | 0 | // Filter by start key and count. |
3611 | 0 | let results_to_client = final_results |
3612 | 0 | .iter() |
3613 | 0 | .cloned() |
3614 | 0 | .filter(|k| start_key.as_ref().map_or(true, |s| *k > *s)) Unexecuted instantiation: _RNCNCINvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background3runpE0sC_0Ba_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sC_0B1f_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runpE0sC_0Ba_ Unexecuted instantiation: _RNCNCNCINvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background3runpE0sC_00Bc_ Unexecuted instantiation: _RNCNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sC_00B1h_ Unexecuted instantiation: _RNCNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runpE0sC_00Bc_ |
3615 | 0 | .map(methods::HexString) |
3616 | 0 | .take(usize::try_from(count).unwrap_or(usize::MAX)) |
3617 | 0 | .collect::<Vec<_>>(); |
3618 | 0 |
|
3619 | 0 | // If the returned response is somehow truncated, it is very likely that the |
3620 | 0 | // JSON-RPC client will call the function again with the exact same parameters. |
3621 | 0 | // Thus, store the results in a cache. |
3622 | 0 | if results_to_client.len() != final_results.len() { |
3623 | 0 | me.state_get_keys_paged_cache.push( |
3624 | 0 | GetKeysPagedCacheKey { |
3625 | 0 | hash: block_hash, |
3626 | 0 | prefix, |
3627 | 0 | }, |
3628 | 0 | final_results, |
3629 | 0 | ); |
3630 | 0 | } |
3631 | | |
3632 | 0 | let _ = me |
3633 | 0 | .responses_tx |
3634 | 0 | .send( |
3635 | 0 | methods::Response::state_getKeysPaged(results_to_client) |
3636 | 0 | .to_json_response(&request_id_json), |
3637 | 0 | ) |
3638 | 0 | .await; |
3639 | | } |
3640 | | ( |
3641 | | sync_service::StorageQueryProgress::Progress { |
3642 | 0 | item: sync_service::StorageResultItem::Value { key, value }, |
3643 | 0 | query: next, |
3644 | 0 | .. |
3645 | 0 | }, |
3646 | 0 | StorageRequestInProgress::StateQueryStorageAt { |
3647 | 0 | block_hash, |
3648 | 0 | mut in_progress_results, |
3649 | 0 | }, |
3650 | 0 | ) => { |
3651 | 0 | // Continue finding keys. |
3652 | 0 | in_progress_results |
3653 | 0 | .push((methods::HexString(key), value.map(methods::HexString))); |
3654 | 0 | me.background_tasks.push(Box::pin(async move { |
3655 | 0 | Event::LegacyApiFunctionStorageRequestProgress { |
3656 | 0 | request_id_json, |
3657 | 0 | request: StorageRequestInProgress::StateQueryStorageAt { |
3658 | 0 | block_hash, |
3659 | 0 | in_progress_results, |
3660 | 0 | }, |
3661 | 0 | progress: next.advance().await, |
3662 | | } |
3663 | 0 | })); Unexecuted instantiation: _RNCNCINvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background3runpE0sD_0Ba_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sD_0B1f_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runpE0sD_0Ba_ |
3664 | 0 | } |
3665 | | ( |
3666 | | sync_service::StorageQueryProgress::Finished, |
3667 | | StorageRequestInProgress::StateQueryStorageAt { |
3668 | 0 | block_hash, |
3669 | 0 | in_progress_results, |
3670 | 0 | }, |
3671 | 0 | ) => { |
3672 | 0 | // Finished. |
3673 | 0 | let _ = me |
3674 | 0 | .responses_tx |
3675 | 0 | .send( |
3676 | 0 | methods::Response::state_queryStorageAt(vec![ |
3677 | 0 | methods::StorageChangeSet { |
3678 | 0 | block: methods::HashHexString(block_hash), |
3679 | 0 | changes: in_progress_results, |
3680 | 0 | }, |
3681 | 0 | ]) |
3682 | 0 | .to_json_response(&request_id_json), |
3683 | 0 | ) |
3684 | 0 | .await; |
3685 | | } |
3686 | | ( |
3687 | | sync_service::StorageQueryProgress::Progress { |
3688 | | item: |
3689 | | sync_service::StorageResultItem::Value { |
3690 | 0 | value: Some(value), .. |
3691 | 0 | }, |
3692 | 0 | .. |
3693 | 0 | }, |
3694 | 0 | StorageRequestInProgress::StateGetStorage {}, |
3695 | 0 | ) => { |
3696 | 0 | // Finished. We throw away the object that continues the request, as we |
3697 | 0 | // know that nothing else will come after. |
3698 | 0 | let _ = me |
3699 | 0 | .responses_tx |
3700 | 0 | .send( |
3701 | 0 | methods::Response::state_getStorage(methods::HexString(value)) |
3702 | 0 | .to_json_response(&request_id_json), |
3703 | 0 | ) |
3704 | 0 | .await; |
3705 | | } |
3706 | | ( |
3707 | | sync_service::StorageQueryProgress::Progress { |
3708 | | item: sync_service::StorageResultItem::Value { value: None, .. }, |
3709 | | .. |
3710 | | }, |
3711 | | StorageRequestInProgress::StateGetStorage {}, |
3712 | | ) => { |
3713 | | // Finished. We throw away the object that continues the request, as we |
3714 | | // know that nothing else will come after. |
3715 | 0 | let _ = me |
3716 | 0 | .responses_tx |
3717 | 0 | .send(parse::build_success_response(&request_id_json, "null")) |
3718 | 0 | .await; |
3719 | | } |
3720 | 0 | (sync_service::StorageQueryProgress::Error(error), _) => { |
3721 | 0 | // All errors are sent back the same way. |
3722 | 0 | let _ = me |
3723 | 0 | .responses_tx |
3724 | 0 | .send(parse::build_error_response( |
3725 | 0 | &request_id_json, |
3726 | 0 | parse::ErrorResponse::ServerError(-32000, &error.to_string()), |
3727 | 0 | None, |
3728 | 0 | )) |
3729 | 0 | .await; |
3730 | | } |
3731 | 0 | _ => unreachable!(), |
3732 | | } |
3733 | | } |
3734 | | |
3735 | | WakeUpReason::Event( |
3736 | 0 | event @ (Event::ChainHeadSubscriptionWithRuntimeReady { .. } |
3737 | | | Event::ChainHeadSubscriptionWithoutRuntimeReady { .. }), |
3738 | | ) => { |
3739 | | // A `chainHead_follow` subscription has finished subscribing to either the |
3740 | | // runtime service or the sync service. |
3741 | | |
3742 | | // Both "with runtime" and "without runtime" events are handled together here, |
3743 | | // but they use different types. |
3744 | | // Extract the event information. |
3745 | | let ( |
3746 | 0 | subscription_id, |
3747 | 0 | new_blocks, |
3748 | 0 | finalized_block_scale_encoded_header, |
3749 | 0 | finalized_block_runtime, |
3750 | 0 | non_finalized_blocks_ancestry_order, |
3751 | 0 | ) = match event { |
3752 | | Event::ChainHeadSubscriptionWithRuntimeReady { |
3753 | 0 | subscription_id, |
3754 | 0 | subscription, |
3755 | 0 | } => ( |
3756 | 0 | subscription_id, |
3757 | 0 | either::Left(subscription.new_blocks), |
3758 | 0 | subscription.finalized_block_scale_encoded_header, |
3759 | 0 | Some(subscription.finalized_block_runtime), |
3760 | 0 | either::Left( |
3761 | 0 | subscription |
3762 | 0 | .non_finalized_blocks_ancestry_order |
3763 | 0 | .into_iter() |
3764 | 0 | .map(either::Left), |
3765 | 0 | ), |
3766 | 0 | ), |
3767 | | Event::ChainHeadSubscriptionWithoutRuntimeReady { |
3768 | 0 | subscription_id, |
3769 | 0 | subscription, |
3770 | 0 | } => ( |
3771 | 0 | subscription_id, |
3772 | 0 | either::Right(Box::pin(subscription.new_blocks)), |
3773 | 0 | subscription.finalized_block_scale_encoded_header, |
3774 | 0 | None, |
3775 | 0 | either::Right( |
3776 | 0 | subscription |
3777 | 0 | .non_finalized_blocks_ancestry_order |
3778 | 0 | .into_iter() |
3779 | 0 | .map(either::Right), |
3780 | 0 | ), |
3781 | 0 | ), |
3782 | 0 | _ => unreachable!(), |
3783 | | }; |
3784 | | |
3785 | | // It might be that the JSON-RPC client has unsubscribed before the subscription |
3786 | | // was initialized. |
3787 | 0 | let Some(subscription_info) = |
3788 | 0 | me.chain_head_follow_subscriptions.get_mut(&subscription_id) |
3789 | | else { |
3790 | 0 | continue; |
3791 | | }; |
3792 | | |
3793 | | // Store the subscription ID in the subscription. |
3794 | 0 | if let either::Left(new_blocks) = &new_blocks { |
3795 | 0 | subscription_info.runtime_service_subscription_id = Some(new_blocks.id()); |
3796 | 0 | } |
3797 | | |
3798 | | // Send the `initialized` event and pin the finalized block. |
3799 | 0 | let finalized_block_hash = |
3800 | 0 | header::hash_from_scale_encoded_header(&finalized_block_scale_encoded_header); // TODO: indicate hash in subscription? |
3801 | 0 | let _ = me |
3802 | 0 | .responses_tx |
3803 | 0 | .send( |
3804 | 0 | methods::ServerToClient::chainHead_v1_followEvent { |
3805 | 0 | subscription: Cow::Borrowed(&subscription_id), |
3806 | 0 | result: methods::FollowEvent::Initialized { |
3807 | 0 | finalized_block_hashes: vec![methods::HashHexString( |
3808 | 0 | finalized_block_hash, |
3809 | 0 | )], |
3810 | 0 | finalized_block_runtime: finalized_block_runtime.as_ref().map( |
3811 | 0 | |runtime| match runtime { |
3812 | 0 | Ok(rt) => methods::MaybeRuntimeSpec::Valid { |
3813 | 0 | spec: convert_runtime_version(&rt), |
3814 | 0 | }, |
3815 | 0 | Err(error) => methods::MaybeRuntimeSpec::Invalid { |
3816 | 0 | error: error.to_string(), |
3817 | 0 | }, |
3818 | 0 | }, Unexecuted instantiation: _RNCNCINvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background3runpE0sE_0Ba_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sE_0B1f_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runpE0sE_0Ba_ |
3819 | 0 | ), |
3820 | 0 | }, |
3821 | 0 | } |
3822 | 0 | .to_json_request_object_parameters(None), |
3823 | 0 | ) |
3824 | 0 | .await; |
3825 | 0 | subscription_info |
3826 | 0 | .pinned_blocks_headers |
3827 | 0 | .insert(finalized_block_hash, finalized_block_scale_encoded_header); |
3828 | | |
3829 | | // Send an event for each non-finalized block. |
3830 | 0 | for block in non_finalized_blocks_ancestry_order { |
3831 | 0 | let parent_block_hash = match &block { |
3832 | 0 | either::Left(b) => b.parent_hash, |
3833 | 0 | either::Right(b) => b.parent_hash, |
3834 | | }; |
3835 | 0 | let hash = header::hash_from_scale_encoded_header(match &block { |
3836 | 0 | either::Left(b) => &b.scale_encoded_header, |
3837 | 0 | either::Right(b) => &b.scale_encoded_header, |
3838 | | }); // TODO: indicate hash in subscription? |
3839 | 0 | let _ = me |
3840 | 0 | .responses_tx |
3841 | 0 | .send( |
3842 | 0 | methods::ServerToClient::chainHead_v1_followEvent { |
3843 | 0 | subscription: Cow::Borrowed(&subscription_id), |
3844 | 0 | result: methods::FollowEvent::NewBlock { |
3845 | 0 | block_hash: methods::HashHexString(hash), |
3846 | 0 | parent_block_hash: methods::HashHexString(parent_block_hash), |
3847 | 0 | new_runtime: if let either::Left(block) = &block { |
3848 | 0 | if let Some(rt) = &block.new_runtime { |
3849 | 0 | match rt { |
3850 | 0 | Ok(spec) => { |
3851 | 0 | Some(methods::MaybeRuntimeSpec::Valid { |
3852 | 0 | spec: convert_runtime_version(spec), |
3853 | 0 | }) |
3854 | | } |
3855 | 0 | Err(error) => { |
3856 | 0 | Some(methods::MaybeRuntimeSpec::Invalid { |
3857 | 0 | error: error.to_string(), |
3858 | 0 | }) |
3859 | | } |
3860 | | } |
3861 | | } else { |
3862 | 0 | None |
3863 | | } |
3864 | | } else { |
3865 | 0 | None |
3866 | | }, |
3867 | | }, |
3868 | | } |
3869 | 0 | .to_json_request_object_parameters(None), |
3870 | | ) |
3871 | 0 | .await; |
3872 | 0 | if match &block { |
3873 | 0 | either::Left(b) => b.is_new_best, |
3874 | 0 | either::Right(b) => b.is_new_best, |
3875 | | } { |
3876 | 0 | let _ = me |
3877 | 0 | .responses_tx |
3878 | 0 | .send( |
3879 | 0 | methods::ServerToClient::chainHead_v1_followEvent { |
3880 | 0 | subscription: Cow::Borrowed(&subscription_id), |
3881 | 0 | result: methods::FollowEvent::BestBlockChanged { |
3882 | 0 | best_block_hash: methods::HashHexString(hash), |
3883 | 0 | }, |
3884 | 0 | } |
3885 | 0 | .to_json_request_object_parameters(None), |
3886 | 0 | ) |
3887 | 0 | .await; |
3888 | 0 | } |
3889 | 0 | subscription_info.pinned_blocks_headers.insert( |
3890 | 0 | hash, |
3891 | 0 | match block { |
3892 | 0 | either::Left(b) => b.scale_encoded_header, |
3893 | 0 | either::Right(b) => b.scale_encoded_header, |
3894 | | }, |
3895 | | ); |
3896 | | } |
3897 | | |
3898 | | // Push a new background task that will yield an event when the newly-created |
3899 | | // susbcription generates its first event. |
3900 | 0 | me.background_tasks.push({ |
3901 | 0 | match new_blocks { |
3902 | 0 | either::Left(mut new_blocks) => Box::pin(async move { |
3903 | 0 | if let Some(notification) = new_blocks.next().await { |
3904 | 0 | Event::ChainHeadSubscriptionWithRuntimeNotification { |
3905 | 0 | subscription_id, |
3906 | 0 | notification, |
3907 | 0 | stream: new_blocks, |
3908 | 0 | } |
3909 | | } else { |
3910 | 0 | Event::ChainHeadSubscriptionDeadSubcription { subscription_id } |
3911 | | } |
3912 | 0 | }), Unexecuted instantiation: _RNCNCINvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background3runpE0sF_0Ba_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sF_0B1f_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runpE0sF_0Ba_ |
3913 | 0 | either::Right(mut new_blocks) => Box::pin(async move { |
3914 | 0 | if let Some(notification) = new_blocks.next().await { |
3915 | 0 | Event::ChainHeadSubscriptionWithoutRuntimeNotification { |
3916 | 0 | subscription_id, |
3917 | 0 | notification, |
3918 | 0 | stream: new_blocks, |
3919 | 0 | } |
3920 | | } else { |
3921 | 0 | Event::ChainHeadSubscriptionDeadSubcription { subscription_id } |
3922 | | } |
3923 | 0 | }), Unexecuted instantiation: _RNCNCINvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background3runpE0sG_0Ba_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sG_0B1f_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runpE0sG_0Ba_ |
3924 | | } |
3925 | | }); |
3926 | | } |
3927 | | |
3928 | | WakeUpReason::Event(Event::ChainHeadSubscriptionWithRuntimeNotification { |
3929 | 0 | subscription_id, |
3930 | 0 | notification, |
3931 | 0 | mut stream, |
3932 | | }) => { |
3933 | | // It might be that the JSON-RPC client has unsubscribed. |
3934 | 0 | let Some(subscription_info) = |
3935 | 0 | me.chain_head_follow_subscriptions.get_mut(&subscription_id) |
3936 | | else { |
3937 | 0 | continue; |
3938 | | }; |
3939 | | |
3940 | 0 | match notification { |
3941 | | runtime_service::Notification::Finalized { |
3942 | 0 | hash, |
3943 | 0 | best_block_hash_if_changed, |
3944 | 0 | pruned_blocks, |
3945 | | } => { |
3946 | 0 | if let Some(new_best_block_hash) = best_block_hash_if_changed { |
3947 | 0 | let _ = me |
3948 | 0 | .responses_tx |
3949 | 0 | .send( |
3950 | 0 | methods::ServerToClient::chainHead_v1_followEvent { |
3951 | 0 | subscription: Cow::Borrowed(&subscription_id), |
3952 | 0 | result: methods::FollowEvent::BestBlockChanged { |
3953 | 0 | best_block_hash: methods::HashHexString( |
3954 | 0 | new_best_block_hash, |
3955 | 0 | ), |
3956 | 0 | }, |
3957 | 0 | } |
3958 | 0 | .to_json_request_object_parameters(None), |
3959 | 0 | ) |
3960 | 0 | .await; |
3961 | 0 | } |
3962 | | |
3963 | 0 | let _ = me |
3964 | 0 | .responses_tx |
3965 | 0 | .send( |
3966 | 0 | methods::ServerToClient::chainHead_v1_followEvent { |
3967 | 0 | subscription: Cow::Borrowed(&subscription_id), |
3968 | 0 | result: methods::FollowEvent::Finalized { |
3969 | 0 | finalized_blocks_hashes: vec![methods::HashHexString(hash)], |
3970 | 0 | pruned_blocks_hashes: pruned_blocks |
3971 | 0 | .into_iter() |
3972 | 0 | .map(methods::HashHexString) |
3973 | 0 | .collect(), |
3974 | 0 | }, |
3975 | 0 | } |
3976 | 0 | .to_json_request_object_parameters(None), |
3977 | 0 | ) |
3978 | 0 | .await; |
3979 | | } |
3980 | 0 | runtime_service::Notification::BestBlockChanged { hash } => { |
3981 | 0 | let _ = me |
3982 | 0 | .responses_tx |
3983 | 0 | .send( |
3984 | 0 | methods::ServerToClient::chainHead_v1_followEvent { |
3985 | 0 | subscription: Cow::Borrowed(&subscription_id), |
3986 | 0 | result: methods::FollowEvent::BestBlockChanged { |
3987 | 0 | best_block_hash: methods::HashHexString(hash), |
3988 | 0 | }, |
3989 | 0 | } |
3990 | 0 | .to_json_request_object_parameters(None), |
3991 | 0 | ) |
3992 | 0 | .await; |
3993 | | } |
3994 | 0 | runtime_service::Notification::Block(block) => { |
3995 | 0 | // TODO: pass hash through notification |
3996 | 0 | let block_hash = |
3997 | 0 | header::hash_from_scale_encoded_header(&block.scale_encoded_header); |
3998 | 0 | subscription_info |
3999 | 0 | .pinned_blocks_headers |
4000 | 0 | .insert(block_hash, block.scale_encoded_header); |
4001 | 0 |
|
4002 | 0 | let _ = me |
4003 | 0 | .responses_tx |
4004 | 0 | .send( |
4005 | 0 | methods::ServerToClient::chainHead_v1_followEvent { |
4006 | 0 | subscription: Cow::Borrowed(&subscription_id), |
4007 | 0 | result: methods::FollowEvent::NewBlock { |
4008 | 0 | block_hash: methods::HashHexString(block_hash), |
4009 | 0 | parent_block_hash: methods::HashHexString( |
4010 | 0 | block.parent_hash, |
4011 | 0 | ), |
4012 | 0 | new_runtime: match &block.new_runtime { |
4013 | 0 | Some(Ok(rt)) => { |
4014 | 0 | Some(methods::MaybeRuntimeSpec::Valid { |
4015 | 0 | spec: convert_runtime_version(&rt), |
4016 | 0 | }) |
4017 | | } |
4018 | 0 | Some(Err(error)) => { |
4019 | 0 | Some(methods::MaybeRuntimeSpec::Invalid { |
4020 | 0 | error: error.to_string(), |
4021 | 0 | }) |
4022 | | } |
4023 | 0 | None => None, |
4024 | | }, |
4025 | | }, |
4026 | | } |
4027 | 0 | .to_json_request_object_parameters(None), |
4028 | | ) |
4029 | 0 | .await; |
4030 | | |
4031 | 0 | if block.is_new_best { |
4032 | 0 | let _ = me |
4033 | 0 | .responses_tx |
4034 | 0 | .send( |
4035 | 0 | methods::ServerToClient::chainHead_v1_followEvent { |
4036 | 0 | subscription: Cow::Borrowed(&subscription_id), |
4037 | 0 | result: methods::FollowEvent::BestBlockChanged { |
4038 | 0 | best_block_hash: methods::HashHexString(block_hash), |
4039 | 0 | }, |
4040 | 0 | } |
4041 | 0 | .to_json_request_object_parameters(None), |
4042 | 0 | ) |
4043 | 0 | .await; |
4044 | 0 | } |
4045 | | } |
4046 | | } |
4047 | | |
4048 | | // Push a new task that will yield when the runtime service subscription generates |
4049 | | // the next notification. |
4050 | 0 | me.background_tasks.push(Box::pin(async move { |
4051 | 0 | if let Some(notification) = stream.next().await { |
4052 | 0 | Event::ChainHeadSubscriptionWithRuntimeNotification { |
4053 | 0 | subscription_id, |
4054 | 0 | notification, |
4055 | 0 | stream, |
4056 | 0 | } |
4057 | | } else { |
4058 | 0 | Event::ChainHeadSubscriptionDeadSubcription { subscription_id } |
4059 | | } |
4060 | 0 | })) Unexecuted instantiation: _RNCNCINvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background3runpE0sH_0Ba_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sH_0B1f_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runpE0sH_0Ba_ |
4061 | | } |
4062 | | |
4063 | | WakeUpReason::Event(Event::ChainHeadSubscriptionWithoutRuntimeNotification { |
4064 | 0 | subscription_id, |
4065 | 0 | notification, |
4066 | 0 | mut stream, |
4067 | | }) => { |
4068 | | // It might be that the JSON-RPC client has unsubscribed. |
4069 | 0 | let Some(subscription_info) = |
4070 | 0 | me.chain_head_follow_subscriptions.get_mut(&subscription_id) |
4071 | | else { |
4072 | 0 | continue; |
4073 | | }; |
4074 | | |
4075 | 0 | match notification { |
4076 | | sync_service::Notification::Finalized { |
4077 | 0 | hash, |
4078 | 0 | best_block_hash_if_changed, |
4079 | 0 | pruned_blocks, |
4080 | | } => { |
4081 | 0 | if let Some(new_best_block_hash) = best_block_hash_if_changed { |
4082 | 0 | let _ = me |
4083 | 0 | .responses_tx |
4084 | 0 | .send( |
4085 | 0 | methods::ServerToClient::chainHead_v1_followEvent { |
4086 | 0 | subscription: Cow::Borrowed(&subscription_id), |
4087 | 0 | result: methods::FollowEvent::BestBlockChanged { |
4088 | 0 | best_block_hash: methods::HashHexString( |
4089 | 0 | new_best_block_hash, |
4090 | 0 | ), |
4091 | 0 | }, |
4092 | 0 | } |
4093 | 0 | .to_json_request_object_parameters(None), |
4094 | 0 | ) |
4095 | 0 | .await; |
4096 | 0 | } |
4097 | | |
4098 | 0 | let _ = me |
4099 | 0 | .responses_tx |
4100 | 0 | .send( |
4101 | 0 | methods::ServerToClient::chainHead_v1_followEvent { |
4102 | 0 | subscription: Cow::Borrowed(&subscription_id), |
4103 | 0 | result: methods::FollowEvent::Finalized { |
4104 | 0 | finalized_blocks_hashes: vec![methods::HashHexString(hash)], |
4105 | 0 | pruned_blocks_hashes: pruned_blocks |
4106 | 0 | .into_iter() |
4107 | 0 | .map(methods::HashHexString) |
4108 | 0 | .collect(), |
4109 | 0 | }, |
4110 | 0 | } |
4111 | 0 | .to_json_request_object_parameters(None), |
4112 | 0 | ) |
4113 | 0 | .await; |
4114 | | } |
4115 | 0 | sync_service::Notification::BestBlockChanged { hash } => { |
4116 | 0 | let _ = me |
4117 | 0 | .responses_tx |
4118 | 0 | .send( |
4119 | 0 | methods::ServerToClient::chainHead_v1_followEvent { |
4120 | 0 | subscription: Cow::Borrowed(&subscription_id), |
4121 | 0 | result: methods::FollowEvent::BestBlockChanged { |
4122 | 0 | best_block_hash: methods::HashHexString(hash), |
4123 | 0 | }, |
4124 | 0 | } |
4125 | 0 | .to_json_request_object_parameters(None), |
4126 | 0 | ) |
4127 | 0 | .await; |
4128 | | } |
4129 | 0 | sync_service::Notification::Block(block) => { |
4130 | 0 | // TODO: pass hash through notification |
4131 | 0 | let block_hash = |
4132 | 0 | header::hash_from_scale_encoded_header(&block.scale_encoded_header); |
4133 | 0 | subscription_info |
4134 | 0 | .pinned_blocks_headers |
4135 | 0 | .insert(block_hash, block.scale_encoded_header); |
4136 | 0 |
|
4137 | 0 | let _ = me |
4138 | 0 | .responses_tx |
4139 | 0 | .send( |
4140 | 0 | methods::ServerToClient::chainHead_v1_followEvent { |
4141 | 0 | subscription: Cow::Borrowed(&subscription_id), |
4142 | 0 | result: methods::FollowEvent::NewBlock { |
4143 | 0 | block_hash: methods::HashHexString(block_hash), |
4144 | 0 | parent_block_hash: methods::HashHexString( |
4145 | 0 | block.parent_hash, |
4146 | 0 | ), |
4147 | 0 | new_runtime: None, |
4148 | 0 | }, |
4149 | 0 | } |
4150 | 0 | .to_json_request_object_parameters(None), |
4151 | 0 | ) |
4152 | 0 | .await; |
4153 | | |
4154 | 0 | if block.is_new_best { |
4155 | 0 | let _ = me |
4156 | 0 | .responses_tx |
4157 | 0 | .send( |
4158 | 0 | methods::ServerToClient::chainHead_v1_followEvent { |
4159 | 0 | subscription: Cow::Borrowed(&subscription_id), |
4160 | 0 | result: methods::FollowEvent::BestBlockChanged { |
4161 | 0 | best_block_hash: methods::HashHexString(block_hash), |
4162 | 0 | }, |
4163 | 0 | } |
4164 | 0 | .to_json_request_object_parameters(None), |
4165 | 0 | ) |
4166 | 0 | .await; |
4167 | 0 | } |
4168 | | } |
4169 | | } |
4170 | | |
4171 | | // Push a new task that will yield when the sync service subscription generates |
4172 | | // the next notification. |
4173 | 0 | me.background_tasks.push(Box::pin(async move { |
4174 | 0 | if let Some(notification) = stream.next().await { |
4175 | 0 | Event::ChainHeadSubscriptionWithoutRuntimeNotification { |
4176 | 0 | subscription_id, |
4177 | 0 | notification, |
4178 | 0 | stream, |
4179 | 0 | } |
4180 | | } else { |
4181 | 0 | Event::ChainHeadSubscriptionDeadSubcription { subscription_id } |
4182 | | } |
4183 | 0 | })) Unexecuted instantiation: _RNCNCINvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background3runpE0sI_0Ba_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sI_0B1f_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runpE0sI_0Ba_ |
4184 | | } |
4185 | | |
4186 | | WakeUpReason::Event(Event::ChainHeadCallOperationDone { |
4187 | 0 | subscription_id, |
4188 | 0 | operation_id, |
4189 | 0 | result, |
4190 | | }) => { |
4191 | | // A `chainHead_call` operation has finished. |
4192 | | |
4193 | 0 | let Some(subscription_info) = |
4194 | 0 | me.chain_head_follow_subscriptions.get_mut(&subscription_id) |
4195 | | else { |
4196 | 0 | unreachable!() |
4197 | | }; |
4198 | 0 | let Some(operation_info) = subscription_info |
4199 | 0 | .operations_in_progress |
4200 | 0 | .remove(&operation_id) |
4201 | | else { |
4202 | | // If the operation was cancelled, then a `ChainHeadOperationCancelled` |
4203 | | // event should have been generated instead. |
4204 | 0 | unreachable!() |
4205 | | }; |
4206 | | |
4207 | 0 | subscription_info.available_operation_slots += operation_info.occupied_slots; |
4208 | | |
4209 | 0 | let result = match result { |
4210 | 0 | Ok(success) => methods::FollowEvent::OperationCallDone { |
4211 | 0 | operation_id: operation_id.clone().into(), |
4212 | 0 | output: methods::HexString(success.output), |
4213 | 0 | }, |
4214 | 0 | Err(runtime_service::RuntimeCallError::InvalidRuntime(error)) => { |
4215 | 0 | methods::FollowEvent::OperationError { |
4216 | 0 | operation_id: operation_id.clone().into(), |
4217 | 0 | error: error.to_string().into(), |
4218 | 0 | } |
4219 | | } |
4220 | | Err(runtime_service::RuntimeCallError::ApiVersionRequirementUnfulfilled) => { |
4221 | | // We pass `None` for the API requirement, thus this error can never happen. |
4222 | 0 | unreachable!() |
4223 | | } |
4224 | | Err( |
4225 | | runtime_service::RuntimeCallError::Crash |
4226 | | | runtime_service::RuntimeCallError::Inaccessible(_), |
4227 | 0 | ) => methods::FollowEvent::OperationInaccessible { |
4228 | 0 | operation_id: operation_id.clone().into(), |
4229 | 0 | }, |
4230 | | Err(runtime_service::RuntimeCallError::Execution( |
4231 | | runtime_service::RuntimeCallExecutionError::ForbiddenHostFunction, |
4232 | 0 | )) => methods::FollowEvent::OperationError { |
4233 | 0 | operation_id: operation_id.clone().into(), |
4234 | 0 | error: "Runtime has called an offchain host function" |
4235 | 0 | .to_string() |
4236 | 0 | .into(), |
4237 | 0 | }, |
4238 | | Err(runtime_service::RuntimeCallError::Execution( |
4239 | 0 | runtime_service::RuntimeCallExecutionError::Start(error), |
4240 | 0 | )) => methods::FollowEvent::OperationError { |
4241 | 0 | operation_id: operation_id.clone().into(), |
4242 | 0 | error: error.to_string().into(), |
4243 | 0 | }, |
4244 | | Err(runtime_service::RuntimeCallError::Execution( |
4245 | 0 | runtime_service::RuntimeCallExecutionError::Execution(error), |
4246 | 0 | )) => methods::FollowEvent::OperationError { |
4247 | 0 | operation_id: operation_id.clone().into(), |
4248 | 0 | error: error.to_string().into(), |
4249 | 0 | }, |
4250 | | }; |
4251 | | |
4252 | 0 | let _ = me |
4253 | 0 | .responses_tx |
4254 | 0 | .send( |
4255 | 0 | methods::ServerToClient::chainHead_v1_followEvent { |
4256 | 0 | subscription: Cow::Borrowed(&subscription_id), |
4257 | 0 | result, |
4258 | 0 | } |
4259 | 0 | .to_json_request_object_parameters(None), |
4260 | 0 | ) |
4261 | 0 | .await; |
4262 | | } |
4263 | | |
4264 | | WakeUpReason::Event(Event::ChainHeadBodyOperationDone { |
4265 | 0 | subscription_id, |
4266 | 0 | operation_id, |
4267 | 0 | expected_extrinsics_root, |
4268 | 0 | result, |
4269 | | }) => { |
4270 | | // A `chainHead_body` operation has finished. |
4271 | | |
4272 | 0 | let Some(subscription_info) = |
4273 | 0 | me.chain_head_follow_subscriptions.get_mut(&subscription_id) |
4274 | | else { |
4275 | 0 | unreachable!() |
4276 | | }; |
4277 | 0 | let Some(operation_info) = subscription_info |
4278 | 0 | .operations_in_progress |
4279 | 0 | .remove(&operation_id) |
4280 | | else { |
4281 | | // If the operation was cancelled, then a `ChainHeadOperationCancelled` |
4282 | | // event should have been generated instead. |
4283 | 0 | unreachable!() |
4284 | | }; |
4285 | | |
4286 | 0 | subscription_info.available_operation_slots += operation_info.occupied_slots; |
4287 | | |
4288 | | // We must check whether the body is present in the response and valid. |
4289 | | // TODO: should try the request again with a different peer instead of failing immediately |
4290 | 0 | let body = match result { |
4291 | 0 | Ok(result) => { |
4292 | 0 | if let Some(body) = result.body { |
4293 | 0 | if header::extrinsics_root(&body) == expected_extrinsics_root { |
4294 | 0 | Ok(body) |
4295 | | } else { |
4296 | 0 | Err(()) |
4297 | | } |
4298 | | } else { |
4299 | 0 | Err(()) |
4300 | | } |
4301 | | } |
4302 | 0 | Err(err) => Err(err), |
4303 | | }; |
4304 | | |
4305 | | // Send back the response. |
4306 | 0 | match body { |
4307 | 0 | Ok(body) => { |
4308 | 0 | let _ = me |
4309 | 0 | .responses_tx |
4310 | 0 | .send( |
4311 | 0 | methods::ServerToClient::chainHead_v1_followEvent { |
4312 | 0 | subscription: Cow::Borrowed(&subscription_id), |
4313 | 0 | result: methods::FollowEvent::OperationBodyDone { |
4314 | 0 | operation_id: operation_id.clone().into(), |
4315 | 0 | value: body.into_iter().map(methods::HexString).collect(), |
4316 | 0 | }, |
4317 | 0 | } |
4318 | 0 | .to_json_request_object_parameters(None), |
4319 | 0 | ) |
4320 | 0 | .await; |
4321 | | } |
4322 | | Err(()) => { |
4323 | 0 | let _ = me |
4324 | 0 | .responses_tx |
4325 | 0 | .send( |
4326 | 0 | methods::ServerToClient::chainHead_v1_followEvent { |
4327 | 0 | subscription: Cow::Borrowed(&subscription_id), |
4328 | 0 | result: methods::FollowEvent::OperationInaccessible { |
4329 | 0 | operation_id: operation_id.clone().into(), |
4330 | 0 | }, |
4331 | 0 | } |
4332 | 0 | .to_json_request_object_parameters(None), |
4333 | 0 | ) |
4334 | 0 | .await; |
4335 | | } |
4336 | | } |
4337 | | } |
4338 | | |
4339 | | WakeUpReason::Event(Event::ChainHeadStorageOperationProgress { |
4340 | 0 | subscription_id, |
4341 | 0 | operation_id, |
4342 | 0 | progress: |
4343 | 0 | sync_service::StorageQueryProgress::Progress { |
4344 | 0 | request_index, |
4345 | 0 | item, |
4346 | 0 | mut query, |
4347 | 0 | }, |
4348 | 0 | }) => { |
4349 | 0 | // A `chainHead_storage` operation has made progress. |
4350 | 0 | let mut items_chunk = Vec::with_capacity(16); |
4351 | | |
4352 | 0 | for (_, item) in |
4353 | 0 | iter::once((request_index, item)).chain(iter::from_fn(|| query.try_advance())) Unexecuted instantiation: _RNCNCINvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background3runpE0sJ_0Ba_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sJ_0B1f_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runpE0sJ_0Ba_ |
4354 | | { |
4355 | | // Perform some API conversion. |
4356 | 0 | let item = match item { |
4357 | | sync_service::StorageResultItem::Value { |
4358 | 0 | key, |
4359 | 0 | value: Some(value), |
4360 | 0 | } => Some(methods::ChainHeadStorageResponseItem { |
4361 | 0 | key: methods::HexString(key), |
4362 | 0 | value: Some(methods::HexString(value)), |
4363 | 0 | hash: None, |
4364 | 0 | closest_descendant_merkle_value: None, |
4365 | 0 | }), |
4366 | 0 | sync_service::StorageResultItem::Value { value: None, .. } => None, |
4367 | | sync_service::StorageResultItem::Hash { |
4368 | 0 | key, |
4369 | 0 | hash: Some(hash), |
4370 | 0 | } => Some(methods::ChainHeadStorageResponseItem { |
4371 | 0 | key: methods::HexString(key), |
4372 | 0 | value: None, |
4373 | 0 | hash: Some(methods::HexString(hash.to_vec())), |
4374 | 0 | closest_descendant_merkle_value: None, |
4375 | 0 | }), |
4376 | 0 | sync_service::StorageResultItem::Hash { hash: None, .. } => None, |
4377 | 0 | sync_service::StorageResultItem::DescendantValue { key, value, .. } => { |
4378 | 0 | Some(methods::ChainHeadStorageResponseItem { |
4379 | 0 | key: methods::HexString(key), |
4380 | 0 | value: Some(methods::HexString(value)), |
4381 | 0 | hash: None, |
4382 | 0 | closest_descendant_merkle_value: None, |
4383 | 0 | }) |
4384 | | } |
4385 | 0 | sync_service::StorageResultItem::DescendantHash { key, hash, .. } => { |
4386 | 0 | Some(methods::ChainHeadStorageResponseItem { |
4387 | 0 | key: methods::HexString(key), |
4388 | 0 | value: None, |
4389 | 0 | hash: Some(methods::HexString(hash.to_vec())), |
4390 | 0 | closest_descendant_merkle_value: None, |
4391 | 0 | }) |
4392 | | } |
4393 | | sync_service::StorageResultItem::ClosestDescendantMerkleValue { |
4394 | 0 | requested_key, |
4395 | 0 | closest_descendant_merkle_value: Some(merkle_value), |
4396 | 0 | .. |
4397 | 0 | } => Some(methods::ChainHeadStorageResponseItem { |
4398 | 0 | key: methods::HexString(requested_key), |
4399 | 0 | value: None, |
4400 | 0 | hash: None, |
4401 | 0 | closest_descendant_merkle_value: Some(methods::HexString(merkle_value)), |
4402 | 0 | }), |
4403 | | sync_service::StorageResultItem::ClosestDescendantMerkleValue { |
4404 | | closest_descendant_merkle_value: None, |
4405 | | .. |
4406 | 0 | } => None, |
4407 | | }; |
4408 | | |
4409 | 0 | if let Some(item) = item { |
4410 | 0 | items_chunk.push(item); |
4411 | 0 | } |
4412 | | } |
4413 | | |
4414 | | // Send the gathered items to the JSON-RPC client. |
4415 | 0 | if !items_chunk.is_empty() { |
4416 | 0 | let _ = me |
4417 | 0 | .responses_tx |
4418 | 0 | .send( |
4419 | 0 | methods::ServerToClient::chainHead_v1_followEvent { |
4420 | 0 | subscription: Cow::Borrowed(&subscription_id), |
4421 | 0 | result: methods::FollowEvent::OperationStorageItems { |
4422 | 0 | operation_id: Cow::Borrowed(&operation_id), |
4423 | 0 | items: items_chunk, |
4424 | 0 | }, |
4425 | 0 | } |
4426 | 0 | .to_json_request_object_parameters(None), |
4427 | 0 | ) |
4428 | 0 | .await; |
4429 | 0 | } |
4430 | | |
4431 | | // TODO: generate a waitingForContinue here and wait for user to continue |
4432 | | |
4433 | | // Re-queue the operation for the follow-up items. |
4434 | 0 | let on_interrupt = me |
4435 | 0 | .chain_head_follow_subscriptions |
4436 | 0 | .get(&subscription_id) |
4437 | 0 | .unwrap_or_else(|| unreachable!()) Unexecuted instantiation: _RNCNCINvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background3runpE0sK_0Ba_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sK_0B1f_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runpE0sK_0Ba_ |
4438 | 0 | .operations_in_progress |
4439 | 0 | .get(&operation_id) |
4440 | 0 | .unwrap_or_else(|| unreachable!()) Unexecuted instantiation: _RNCNCINvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background3runpE0sL_0Ba_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sL_0B1f_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runpE0sL_0Ba_ |
4441 | 0 | .interrupt |
4442 | 0 | .listen(); |
4443 | 0 | me.background_tasks.push(Box::pin(async move { |
4444 | 0 | async { |
4445 | 0 | on_interrupt.await; |
4446 | | // This event is necessary only because tasks can't finish without |
4447 | | // generating an event. |
4448 | 0 | Event::ChainHeadOperationCancelled |
4449 | 0 | } Unexecuted instantiation: _RNCNCNCINvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background3runpE0sM_00Bc_ Unexecuted instantiation: _RNCNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sM_00B1h_ Unexecuted instantiation: _RNCNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runpE0sM_00Bc_ |
4450 | 0 | .or(async { |
4451 | 0 | Event::ChainHeadStorageOperationProgress { |
4452 | 0 | subscription_id, |
4453 | 0 | operation_id, |
4454 | 0 | progress: query.advance().await, |
4455 | | } |
4456 | 0 | }) Unexecuted instantiation: _RNCNCNCINvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background3runpE0sM_0s_0Bc_ Unexecuted instantiation: _RNCNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sM_0s_0B1h_ Unexecuted instantiation: _RNCNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runpE0sM_0s_0Bc_ |
4457 | 0 | .await |
4458 | 0 | })); Unexecuted instantiation: _RNCNCINvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background3runpE0sM_0Ba_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sM_0B1f_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runpE0sM_0Ba_ |
4459 | | } |
4460 | | |
4461 | | WakeUpReason::Event(Event::ChainHeadStorageOperationProgress { |
4462 | 0 | subscription_id, |
4463 | 0 | operation_id, |
4464 | | progress: sync_service::StorageQueryProgress::Finished, |
4465 | | }) => { |
4466 | | // A `chainHead_storage` operation has finished successfully. |
4467 | | |
4468 | 0 | let Some(subscription_info) = |
4469 | 0 | me.chain_head_follow_subscriptions.get_mut(&subscription_id) |
4470 | | else { |
4471 | 0 | unreachable!() |
4472 | | }; |
4473 | 0 | let Some(operation_info) = subscription_info |
4474 | 0 | .operations_in_progress |
4475 | 0 | .remove(&operation_id) |
4476 | | else { |
4477 | | // If the operation was cancelled, then a `ChainHeadOperationCancelled` |
4478 | | // event should have been generated instead. |
4479 | 0 | unreachable!() |
4480 | | }; |
4481 | | |
4482 | 0 | subscription_info.available_operation_slots += operation_info.occupied_slots; |
4483 | 0 |
|
4484 | 0 | let _ = me |
4485 | 0 | .responses_tx |
4486 | 0 | .send( |
4487 | 0 | methods::ServerToClient::chainHead_v1_followEvent { |
4488 | 0 | subscription: Cow::Borrowed(&subscription_id), |
4489 | 0 | result: methods::FollowEvent::OperationStorageDone { |
4490 | 0 | operation_id: Cow::Borrowed(&operation_id), |
4491 | 0 | }, |
4492 | 0 | } |
4493 | 0 | .to_json_request_object_parameters(None), |
4494 | 0 | ) |
4495 | 0 | .await; |
4496 | | } |
4497 | | |
4498 | | WakeUpReason::Event(Event::ChainHeadStorageOperationProgress { |
4499 | 0 | subscription_id, |
4500 | 0 | operation_id, |
4501 | | progress: sync_service::StorageQueryProgress::Error(_), |
4502 | | }) => { |
4503 | | // A `chainHead_storage` operation has finished failed. |
4504 | | |
4505 | 0 | let Some(subscription_info) = |
4506 | 0 | me.chain_head_follow_subscriptions.get_mut(&subscription_id) |
4507 | | else { |
4508 | 0 | unreachable!() |
4509 | | }; |
4510 | 0 | let Some(operation_info) = subscription_info |
4511 | 0 | .operations_in_progress |
4512 | 0 | .remove(&operation_id) |
4513 | | else { |
4514 | | // If the operation was cancelled, then a `ChainHeadOperationCancelled` |
4515 | | // event should have been generated instead. |
4516 | 0 | unreachable!() |
4517 | | }; |
4518 | | |
4519 | 0 | subscription_info.available_operation_slots += operation_info.occupied_slots; |
4520 | 0 |
|
4521 | 0 | let _ = me |
4522 | 0 | .responses_tx |
4523 | 0 | .send( |
4524 | 0 | methods::ServerToClient::chainHead_v1_followEvent { |
4525 | 0 | subscription: Cow::Borrowed(&subscription_id), |
4526 | 0 | result: methods::FollowEvent::OperationInaccessible { |
4527 | 0 | operation_id: Cow::Borrowed(&operation_id), |
4528 | 0 | }, |
4529 | 0 | } |
4530 | 0 | .to_json_request_object_parameters(None), |
4531 | 0 | ) |
4532 | 0 | .await; |
4533 | | } |
4534 | | |
4535 | | WakeUpReason::Event(Event::ChainHeadSubscriptionDeadSubcription { |
4536 | 0 | subscription_id, |
4537 | | }) => { |
4538 | | // The runtime service or sync service subscription of a `chainHead_follow` |
4539 | | // subscription has died. |
4540 | | |
4541 | | // It might be that the JSON-RPC client has already unsubscribed. |
4542 | 0 | let Some(subscription_info) = |
4543 | 0 | me.chain_head_follow_subscriptions.remove(&subscription_id) |
4544 | | else { |
4545 | 0 | continue; |
4546 | | }; |
4547 | | |
4548 | | // Cancel operations isn't necessary, but is also not a bad idea, to |
4549 | | // save resources. |
4550 | 0 | for (_, operation) in subscription_info.operations_in_progress { |
4551 | 0 | operation.interrupt.notify(usize::MAX); |
4552 | 0 | } |
4553 | | |
4554 | | // Send a stop event. |
4555 | 0 | let _ = me |
4556 | 0 | .responses_tx |
4557 | 0 | .send( |
4558 | 0 | methods::ServerToClient::chainHead_v1_followEvent { |
4559 | 0 | subscription: Cow::Borrowed(&subscription_id), |
4560 | 0 | result: methods::FollowEvent::Stop {}, |
4561 | 0 | } |
4562 | 0 | .to_json_request_object_parameters(None), |
4563 | 0 | ) |
4564 | 0 | .await; |
4565 | | } |
4566 | | |
4567 | 0 | WakeUpReason::Event(Event::ChainHeadOperationCancelled) => { |
4568 | 0 | // Nothing to do. |
4569 | 0 | } |
4570 | | |
4571 | 0 | WakeUpReason::RuntimeServiceSubscriptionReady(subscribe_all) => { |
4572 | 0 | // Runtime service is now ready to give us blocks. |
4573 | 0 | // This only relates to the legacy JSON-RPC API. |
4574 | 0 |
|
4575 | 0 | // Transition to `RuntimeServiceSubscription::Active`. |
4576 | 0 | let mut pinned_blocks = |
4577 | 0 | hashbrown::HashMap::with_capacity_and_hasher(32, Default::default()); |
4578 | 0 | let mut finalized_and_pruned_lru = lru::LruCache::with_hasher( |
4579 | 0 | NonZeroUsize::new(32).unwrap(), |
4580 | 0 | fnv::FnvBuildHasher::default(), |
4581 | 0 | ); |
4582 | 0 |
|
4583 | 0 | let finalized_block_hash = header::hash_from_scale_encoded_header( |
4584 | 0 | &subscribe_all.finalized_block_scale_encoded_header, |
4585 | 0 | ); |
4586 | 0 | pinned_blocks.insert( |
4587 | 0 | finalized_block_hash, |
4588 | 0 | RecentBlock { |
4589 | 0 | scale_encoded_header: subscribe_all.finalized_block_scale_encoded_header, |
4590 | 0 | runtime_version: Arc::new(subscribe_all.finalized_block_runtime), |
4591 | 0 | }, |
4592 | 0 | ); |
4593 | 0 | finalized_and_pruned_lru.put(finalized_block_hash, ()); |
4594 | 0 |
|
4595 | 0 | let mut current_best_block = finalized_block_hash; |
4596 | | |
4597 | 0 | for block in subscribe_all.non_finalized_blocks_ancestry_order { |
4598 | 0 | let hash = header::hash_from_scale_encoded_header(&block.scale_encoded_header); |
4599 | 0 | pinned_blocks.insert( |
4600 | 0 | hash, |
4601 | 0 | RecentBlock { |
4602 | 0 | scale_encoded_header: block.scale_encoded_header, |
4603 | 0 | runtime_version: match block.new_runtime { |
4604 | 0 | Some(r) => Arc::new(r), |
4605 | 0 | None => pinned_blocks |
4606 | 0 | .get(&block.parent_hash) |
4607 | 0 | .unwrap() |
4608 | 0 | .runtime_version |
4609 | 0 | .clone(), |
4610 | | }, |
4611 | | }, |
4612 | | ); |
4613 | | |
4614 | 0 | if block.is_new_best { |
4615 | 0 | current_best_block = hash; |
4616 | 0 | } |
4617 | | } |
4618 | | |
4619 | 0 | me.runtime_service_subscription = RuntimeServiceSubscription::Active { |
4620 | 0 | subscription: subscribe_all.new_blocks, |
4621 | 0 | pinned_blocks, |
4622 | 0 | finalized_and_pruned_lru, |
4623 | 0 | current_best_block, |
4624 | 0 | new_heads_and_runtime_subscriptions_stale: Some(None), |
4625 | 0 | current_finalized_block: finalized_block_hash, |
4626 | 0 | finalized_heads_subscriptions_stale: true, |
4627 | 0 | }; |
4628 | | |
4629 | | // Advance all the requests that are waiting for the best block hash to be known. |
4630 | | // We use `mem::take` as this de-allocates the memory of the `Vec`. |
4631 | 0 | for (request_id, request_ty) in mem::take(&mut me.best_block_hash_pending) { |
4632 | 0 | me.multistage_requests_to_advance.push_back(( |
4633 | 0 | request_id, |
4634 | 0 | MultiStageRequestStage::BlockHashKnown { |
4635 | 0 | block_hash: current_best_block, |
4636 | 0 | }, |
4637 | 0 | request_ty, |
4638 | 0 | )); |
4639 | 0 | } |
4640 | | |
4641 | | // Answer all the pending `chain_getFinalizedHash` requests. |
4642 | | // We use `mem::take` as this de-allocates the memory of the `Vec`. |
4643 | 0 | for request_id in mem::take(&mut me.pending_get_finalized_head) { |
4644 | 0 | let _ = me |
4645 | 0 | .responses_tx |
4646 | 0 | .send( |
4647 | 0 | methods::Response::chain_getFinalizedHead(methods::HashHexString( |
4648 | 0 | finalized_block_hash, |
4649 | 0 | )) |
4650 | 0 | .to_json_response(&request_id), |
4651 | 0 | ) |
4652 | 0 | .await; |
4653 | | } |
4654 | | } |
4655 | | |
4656 | 0 | WakeUpReason::RuntimeServiceSubscriptionDead => { |
4657 | 0 | // The subscription towards the runtime service needs to be renewed. |
4658 | 0 |
|
4659 | 0 | // The buffer size should be large enough so that, if the CPU is busy, it |
4660 | 0 | // doesn't become full before the execution of this task resumes. |
4661 | 0 | // The maximum number of pinned block is ignored, as this maximum is a way to |
4662 | 0 | // avoid malicious behaviors. This code is by definition not considered |
4663 | 0 | // malicious. |
4664 | 0 | let runtime_service = me.runtime_service.clone(); |
4665 | 0 | me.runtime_service_subscription = |
4666 | 0 | RuntimeServiceSubscription::Pending(Box::pin(async move { |
4667 | 0 | runtime_service |
4668 | 0 | .subscribe_all( |
4669 | 0 | 32, |
4670 | 0 | NonZeroUsize::new(usize::MAX).unwrap_or_else(|| unreachable!()), Unexecuted instantiation: _RNCNCNCINvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background3runpE0sN_00Bc_ Unexecuted instantiation: _RNCNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sN_00B1h_ Unexecuted instantiation: _RNCNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runpE0sN_00Bc_ |
4671 | 0 | ) |
4672 | 0 | .await |
4673 | 0 | })); Unexecuted instantiation: _RNCNCINvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background3runpE0sN_0Ba_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sN_0B1f_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runpE0sN_0Ba_ |
4674 | 0 | } |
4675 | | |
4676 | | WakeUpReason::RuntimeServiceSubscriptionNotification { |
4677 | | notification: |
4678 | | runtime_service::Notification::BestBlockChanged { |
4679 | 0 | hash: new_best_hash, |
4680 | 0 | .. |
4681 | 0 | }, |
4682 | 0 | current_best_block, |
4683 | 0 | new_heads_and_runtime_subscriptions_stale, |
4684 | 0 | .. |
4685 | 0 | } => { |
4686 | 0 | // Runtime service has notified that the best block has changed. |
4687 | 0 | // This only relates to the legacy JSON-RPC API. |
4688 | 0 | // This is handled by marking subscriptions as stale. |
4689 | 0 | *new_heads_and_runtime_subscriptions_stale = Some(Some(*current_best_block)); |
4690 | 0 | *current_best_block = new_best_hash; |
4691 | 0 | } |
4692 | | |
4693 | | WakeUpReason::RuntimeServiceSubscriptionNotification { |
4694 | 0 | notification: runtime_service::Notification::Block(block), |
4695 | 0 | pinned_blocks, |
4696 | 0 | current_best_block, |
4697 | 0 | new_heads_and_runtime_subscriptions_stale, |
4698 | | .. |
4699 | | } => { |
4700 | | // Runtime service has notified of a new best block. |
4701 | | // This only relates to the legacy JSON-RPC API. |
4702 | 0 | let json_rpc_header = match methods::Header::from_scale_encoded_header( |
4703 | 0 | &block.scale_encoded_header, |
4704 | 0 | me.runtime_service.block_number_bytes(), |
4705 | 0 | ) { |
4706 | 0 | Ok(h) => h, |
4707 | 0 | Err(error) => { |
4708 | 0 | log!( |
4709 | 0 | &me.platform, |
4710 | 0 | Warn, |
4711 | 0 | &me.log_target, |
4712 | 0 | format!( |
4713 | 0 | "`chain_subscribeAllHeads` subscription has skipped block \ |
4714 | 0 | due to undecodable header. Hash: {}. Error: {}", |
4715 | 0 | HashDisplay(&header::hash_from_scale_encoded_header( |
4716 | 0 | &block.scale_encoded_header |
4717 | 0 | )), |
4718 | 0 | error |
4719 | 0 | ) |
4720 | 0 | ); |
4721 | 0 | continue; |
4722 | | } |
4723 | | }; |
4724 | | |
4725 | 0 | let hash = header::hash_from_scale_encoded_header(&block.scale_encoded_header); |
4726 | 0 |
|
4727 | 0 | // The JSON-RPC client is likely to query things about the new block. We thus |
4728 | 0 | // put it in cache. |
4729 | 0 | me.block_headers_cache.put( |
4730 | 0 | hash, |
4731 | 0 | Ok(( |
4732 | 0 | block.scale_encoded_header.clone(), |
4733 | 0 | json_rpc_header.state_root.0, |
4734 | 0 | json_rpc_header.number, |
4735 | 0 | )), |
4736 | 0 | ); |
4737 | | |
4738 | 0 | let _was_in = pinned_blocks.insert( |
4739 | 0 | hash, |
4740 | 0 | RecentBlock { |
4741 | 0 | scale_encoded_header: block.scale_encoded_header, |
4742 | 0 | runtime_version: match block.new_runtime { |
4743 | 0 | Some(r) => Arc::new(r), |
4744 | 0 | None => pinned_blocks |
4745 | 0 | .get(&block.parent_hash) |
4746 | 0 | .unwrap() |
4747 | 0 | .runtime_version |
4748 | 0 | .clone(), |
4749 | | }, |
4750 | | }, |
4751 | | ); |
4752 | 0 | debug_assert!(_was_in.is_none()); |
4753 | | |
4754 | 0 | for subscription_id in &me.all_heads_subscriptions { |
4755 | 0 | let _ = me |
4756 | 0 | .responses_tx |
4757 | 0 | .send( |
4758 | 0 | methods::ServerToClient::chain_allHead { |
4759 | 0 | subscription: subscription_id.as_str().into(), |
4760 | 0 | result: json_rpc_header.clone(), |
4761 | 0 | } |
4762 | 0 | .to_json_request_object_parameters(None), |
4763 | 0 | ) |
4764 | 0 | .await; |
4765 | | } |
4766 | | |
4767 | 0 | if block.is_new_best { |
4768 | 0 | *new_heads_and_runtime_subscriptions_stale = Some(Some(*current_best_block)); |
4769 | 0 | *current_best_block = hash; |
4770 | 0 | } |
4771 | | } |
4772 | | |
4773 | | WakeUpReason::RuntimeServiceSubscriptionNotification { |
4774 | | notification: |
4775 | | runtime_service::Notification::Finalized { |
4776 | 0 | hash: finalized_hash, |
4777 | 0 | pruned_blocks, |
4778 | 0 | best_block_hash_if_changed, |
4779 | 0 | }, |
4780 | 0 | pinned_blocks, |
4781 | 0 | finalized_and_pruned_lru, |
4782 | 0 | subscription, |
4783 | 0 | current_best_block, |
4784 | 0 | new_heads_and_runtime_subscriptions_stale, |
4785 | 0 | current_finalized_block, |
4786 | 0 | finalized_heads_subscriptions_stale, |
4787 | 0 | } => { |
4788 | 0 | // Runtime service has notified a new finalized block. |
4789 | 0 | // This only relates to the legacy JSON-RPC API. |
4790 | 0 |
|
4791 | 0 | *current_finalized_block = finalized_hash; |
4792 | 0 | *finalized_heads_subscriptions_stale = true; |
4793 | 0 |
|
4794 | 0 | debug_assert!(pruned_blocks |
4795 | 0 | .iter() |
4796 | 0 | .all(|hash| pinned_blocks.contains_key(hash))); Unexecuted instantiation: _RNCNCINvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background3runpE0s12_0Ba_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0s12_0B1f_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runpE0s12_0Ba_ |
4797 | | |
4798 | | // Add the pruned and finalized blocks to the LRU cache. The least-recently used |
4799 | | // entries in the cache are unpinned and no longer tracked. |
4800 | | // |
4801 | | // An important detail here is that the newly-finalized block is added to the list |
4802 | | // at the end, in order to guarantee that it doesn't get removed. This is |
4803 | | // necessary in order to guarantee that the current finalized (and current best, |
4804 | | // if the best block is also the finalized block) remains pinned until at least |
4805 | | // a different block gets finalized. |
4806 | 0 | for block_hash in pruned_blocks.into_iter().chain(iter::once(finalized_hash)) { |
4807 | 0 | if finalized_and_pruned_lru.len() == finalized_and_pruned_lru.cap().get() { |
4808 | 0 | let (hash_to_unpin, _) = finalized_and_pruned_lru.pop_lru().unwrap(); |
4809 | 0 | subscription.unpin_block(hash_to_unpin).await; |
4810 | 0 | pinned_blocks.remove(&hash_to_unpin).unwrap(); |
4811 | 0 | } |
4812 | 0 | finalized_and_pruned_lru.put(block_hash, ()); |
4813 | | } |
4814 | | |
4815 | 0 | if let Some(new_best_block_hash) = best_block_hash_if_changed { |
4816 | 0 | *new_heads_and_runtime_subscriptions_stale = Some(Some(*current_best_block)); |
4817 | 0 | *current_best_block = new_best_block_hash; |
4818 | 0 | } |
4819 | | } |
4820 | | |
4821 | | WakeUpReason::Event(Event::BlockInfoRetrieved { |
4822 | 0 | block_hash, |
4823 | 0 | result: Ok(result), |
4824 | 0 | }) => { |
4825 | 0 | // A block header necessary for a "multi-stage request" has successfully been |
4826 | 0 | // retrieved. |
4827 | 0 |
|
4828 | 0 | me.block_headers_cache.put(block_hash, result); |
4829 | | |
4830 | 0 | for (request_id, request) in me |
4831 | 0 | .block_headers_pending |
4832 | 0 | .remove(&block_hash) |
4833 | 0 | .into_iter() |
4834 | 0 | .flat_map(|l| l) Unexecuted instantiation: _RNCNCINvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background3runpE0sO_0Ba_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sO_0B1f_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runpE0sO_0Ba_ |
4835 | 0 | { |
4836 | 0 | // Note that we push_front in order to guarantee that the information is |
4837 | 0 | // not removed from cache before the request is processed. |
4838 | 0 | me.multistage_requests_to_advance.push_front(( |
4839 | 0 | request_id, |
4840 | 0 | MultiStageRequestStage::BlockHashKnown { block_hash }, |
4841 | 0 | request, |
4842 | 0 | )); |
4843 | 0 | } |
4844 | | } |
4845 | | |
4846 | | WakeUpReason::Event(Event::BlockInfoRetrieved { |
4847 | 0 | block_hash, |
4848 | | result: Err(()), |
4849 | | }) => { |
4850 | | // A block header necessary for a "multi-stage request" has failed to be |
4851 | | // retrieved. |
4852 | 0 | for (request_id, _) in me |
4853 | 0 | .block_headers_pending |
4854 | 0 | .remove(&block_hash) |
4855 | 0 | .into_iter() |
4856 | 0 | .flat_map(|l| l) Unexecuted instantiation: _RNCNCINvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background3runpE0sP_0Ba_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sP_0B1f_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runpE0sP_0Ba_ |
4857 | | { |
4858 | 0 | let _ = me |
4859 | 0 | .responses_tx |
4860 | 0 | .send(parse::build_error_response( |
4861 | 0 | &request_id, |
4862 | 0 | parse::ErrorResponse::ApplicationDefined( |
4863 | 0 | -32800, |
4864 | 0 | "Failed to retrieve block information from the network", |
4865 | 0 | ), |
4866 | 0 | None, |
4867 | 0 | )) |
4868 | 0 | .await; |
4869 | | } |
4870 | | } |
4871 | | |
4872 | | WakeUpReason::Event(Event::RuntimeDownloaded { |
4873 | 0 | block_hash, |
4874 | 0 | result: Ok(result), |
4875 | 0 | }) => { |
4876 | 0 | // A block runtime necessary for a "multi-stage request" has successfully been |
4877 | 0 | // retrieved. |
4878 | 0 |
|
4879 | 0 | me.block_runtimes_cache.put(block_hash, result); |
4880 | | |
4881 | 0 | for (request_id, request) in me |
4882 | 0 | .block_runtimes_pending |
4883 | 0 | .remove(&block_hash) |
4884 | 0 | .into_iter() |
4885 | 0 | .flat_map(|l| l) Unexecuted instantiation: _RNCNCINvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background3runpE0sQ_0Ba_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sQ_0B1f_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runpE0sQ_0Ba_ |
4886 | 0 | { |
4887 | 0 | // Note that we push_front in order to guarantee that the information is |
4888 | 0 | // not removed from cache before the request is processed. |
4889 | 0 | me.multistage_requests_to_advance.push_front(( |
4890 | 0 | request_id, |
4891 | 0 | MultiStageRequestStage::BlockHashKnown { block_hash }, |
4892 | 0 | request, |
4893 | 0 | )); |
4894 | 0 | } |
4895 | | } |
4896 | | |
4897 | | WakeUpReason::Event(Event::RuntimeDownloaded { |
4898 | 0 | block_hash, |
4899 | 0 | result: Err(error_message), |
4900 | | }) => { |
4901 | | // A block runtime necessary for a "multi-stage request" has failed to be |
4902 | | // retrieved. |
4903 | 0 | for (request_id, _) in me |
4904 | 0 | .block_runtimes_pending |
4905 | 0 | .remove(&block_hash) |
4906 | 0 | .into_iter() |
4907 | 0 | .flat_map(|l| l) Unexecuted instantiation: _RNCNCINvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background3runpE0sR_0Ba_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sR_0B1f_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runpE0sR_0Ba_ |
4908 | | { |
4909 | 0 | let _ = me |
4910 | 0 | .responses_tx |
4911 | 0 | .send(parse::build_error_response( |
4912 | 0 | &request_id, |
4913 | 0 | parse::ErrorResponse::ApplicationDefined( |
4914 | 0 | -32800, |
4915 | 0 | &format!( |
4916 | 0 | "Failed to retrieve runtime from the \ |
4917 | 0 | network: {error_message}" |
4918 | 0 | ), |
4919 | 0 | ), |
4920 | 0 | None, |
4921 | 0 | )) |
4922 | 0 | .await; |
4923 | | } |
4924 | | } |
4925 | | |
4926 | | WakeUpReason::Event(Event::TransactionEvent { |
4927 | 0 | subscription_id, |
4928 | 0 | event: transactions_service::TransactionStatus::Dropped(drop_reason), |
4929 | | .. |
4930 | | }) => { |
4931 | | // Transactions service has notified that a transaction has left its pool. |
4932 | 0 | let Some(transaction_watch) = |
4933 | 0 | me.transactions_subscriptions.remove(&subscription_id) |
4934 | | else { |
4935 | | // JSON-RPC client has unsubscribed from this transaction and is no longer |
4936 | | // interested in events. |
4937 | 0 | continue; |
4938 | | }; |
4939 | | |
4940 | 0 | match (drop_reason, &transaction_watch.ty) { |
4941 | | ( |
4942 | | transactions_service::DropReason::GapInChain |
4943 | | | transactions_service::DropReason::Crashed, |
4944 | 0 | TransactionWatchTy::NewApi { transaction_bytes }, |
4945 | | ) => { |
4946 | | // In case of `transaction_v1_broadcast`, we re-submit the transaction |
4947 | | // if it was dropped for a temporary reasons. |
4948 | 0 | let mut new_watcher = Box::pin( |
4949 | 0 | me.transactions_service |
4950 | 0 | .submit_and_watch_transaction(transaction_bytes.clone(), 16, false) |
4951 | 0 | .await, |
4952 | | ); |
4953 | | |
4954 | 0 | let _prev_value = me |
4955 | 0 | .transactions_subscriptions |
4956 | 0 | .insert(subscription_id.clone(), transaction_watch); |
4957 | 0 | debug_assert!(_prev_value.is_none()); |
4958 | | |
4959 | | // Push a new background task that waits for the next notification. |
4960 | 0 | me.background_tasks.push(Box::pin(async move { |
4961 | 0 | let Some(status) = new_watcher.as_mut().next().await else { |
4962 | 0 | unreachable!() |
4963 | | }; |
4964 | 0 | Event::TransactionEvent { |
4965 | 0 | subscription_id, |
4966 | 0 | event: status, |
4967 | 0 | watcher: new_watcher, |
4968 | 0 | } |
4969 | 0 | })); Unexecuted instantiation: _RNCNCINvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background3runpE0sS_0Ba_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sS_0B1f_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runpE0sS_0Ba_ |
4970 | | } |
4971 | | |
4972 | | ( |
4973 | | transactions_service::DropReason::Finalized { .. } |
4974 | | | transactions_service::DropReason::Invalid(_) |
4975 | | | transactions_service::DropReason::MaxPendingTransactionsReached |
4976 | | | transactions_service::DropReason::ValidateError(_), |
4977 | | TransactionWatchTy::NewApi { .. }, |
4978 | | ) => { |
4979 | | // In case of `transaction_v1_broadcast`, the transaction is re-inserted |
4980 | | // in the list, but no new notification-generating task is pushed, making |
4981 | | // the transaction effectively dead and waiting for `transaction_v1_stop` |
4982 | | // to be called to remove it. |
4983 | 0 | let _prev_value = me |
4984 | 0 | .transactions_subscriptions |
4985 | 0 | .insert(subscription_id.clone(), transaction_watch); |
4986 | 0 | debug_assert!(_prev_value.is_none()); |
4987 | | } |
4988 | | |
4989 | | (transactions_service::DropReason::GapInChain, TransactionWatchTy::Legacy) |
4990 | | | ( |
4991 | | transactions_service::DropReason::MaxPendingTransactionsReached, |
4992 | | TransactionWatchTy::Legacy, |
4993 | | ) |
4994 | | | (transactions_service::DropReason::Invalid(_), TransactionWatchTy::Legacy) |
4995 | | | ( |
4996 | | transactions_service::DropReason::ValidateError(_), |
4997 | | TransactionWatchTy::Legacy, |
4998 | | ) |
4999 | | | (transactions_service::DropReason::Crashed, TransactionWatchTy::Legacy) => { |
5000 | 0 | let _ = me |
5001 | 0 | .responses_tx |
5002 | 0 | .send( |
5003 | 0 | methods::ServerToClient::author_extrinsicUpdate { |
5004 | 0 | subscription: Cow::Borrowed(&subscription_id), |
5005 | 0 | result: methods::TransactionStatus::Dropped, |
5006 | 0 | } |
5007 | 0 | .to_json_request_object_parameters(None), |
5008 | 0 | ) |
5009 | 0 | .await; |
5010 | | } |
5011 | | ( |
5012 | | transactions_service::DropReason::GapInChain, |
5013 | | TransactionWatchTy::NewApiWatch, |
5014 | | ) => { |
5015 | 0 | let _ = me |
5016 | 0 | .responses_tx |
5017 | 0 | .send( |
5018 | 0 | methods::ServerToClient::transactionWatch_v1_watchEvent { |
5019 | 0 | subscription: Cow::Borrowed(&subscription_id), |
5020 | 0 | result: methods::TransactionWatchEvent::Dropped { |
5021 | 0 | error: "gap in chain of blocks".into(), |
5022 | 0 | broadcasted: transaction_watch.num_broadcasted_peers != 0, |
5023 | 0 | }, |
5024 | 0 | } |
5025 | 0 | .to_json_request_object_parameters(None), |
5026 | 0 | ) |
5027 | 0 | .await; |
5028 | | } |
5029 | | ( |
5030 | | transactions_service::DropReason::MaxPendingTransactionsReached, |
5031 | | TransactionWatchTy::NewApiWatch, |
5032 | | ) => { |
5033 | 0 | let _ = me |
5034 | 0 | .responses_tx |
5035 | 0 | .send( |
5036 | 0 | methods::ServerToClient::transactionWatch_v1_watchEvent { |
5037 | 0 | subscription: Cow::Borrowed(&subscription_id), |
5038 | 0 | result: methods::TransactionWatchEvent::Dropped { |
5039 | 0 | error: "transactions pool full".into(), |
5040 | 0 | broadcasted: transaction_watch.num_broadcasted_peers != 0, |
5041 | 0 | }, |
5042 | 0 | } |
5043 | 0 | .to_json_request_object_parameters(None), |
5044 | 0 | ) |
5045 | 0 | .await; |
5046 | | } |
5047 | | ( |
5048 | 0 | transactions_service::DropReason::Invalid(error), |
5049 | 0 | TransactionWatchTy::NewApiWatch, |
5050 | 0 | ) => { |
5051 | 0 | let _ = me |
5052 | 0 | .responses_tx |
5053 | 0 | .send( |
5054 | 0 | methods::ServerToClient::transactionWatch_v1_watchEvent { |
5055 | 0 | subscription: Cow::Borrowed(&subscription_id), |
5056 | 0 | result: methods::TransactionWatchEvent::Invalid { |
5057 | 0 | error: error.to_string().into(), |
5058 | 0 | }, |
5059 | 0 | } |
5060 | 0 | .to_json_request_object_parameters(None), |
5061 | 0 | ) |
5062 | 0 | .await; |
5063 | | } |
5064 | | ( |
5065 | 0 | transactions_service::DropReason::ValidateError(error), |
5066 | 0 | TransactionWatchTy::NewApiWatch, |
5067 | 0 | ) => { |
5068 | 0 | let _ = me |
5069 | 0 | .responses_tx |
5070 | 0 | .send( |
5071 | 0 | methods::ServerToClient::transactionWatch_v1_watchEvent { |
5072 | 0 | subscription: Cow::Borrowed(&subscription_id), |
5073 | 0 | result: methods::TransactionWatchEvent::Error { |
5074 | 0 | error: error.to_string().into(), |
5075 | 0 | }, |
5076 | 0 | } |
5077 | 0 | .to_json_request_object_parameters(None), |
5078 | 0 | ) |
5079 | 0 | .await; |
5080 | | } |
5081 | | ( |
5082 | | transactions_service::DropReason::Crashed, |
5083 | | TransactionWatchTy::NewApiWatch, |
5084 | | ) => { |
5085 | 0 | let _ = me |
5086 | 0 | .responses_tx |
5087 | 0 | .send( |
5088 | 0 | methods::ServerToClient::transactionWatch_v1_watchEvent { |
5089 | 0 | subscription: Cow::Borrowed(&subscription_id), |
5090 | 0 | result: methods::TransactionWatchEvent::Error { |
5091 | 0 | error: "transactions service has crashed".into(), |
5092 | 0 | }, |
5093 | 0 | } |
5094 | 0 | .to_json_request_object_parameters(None), |
5095 | 0 | ) |
5096 | 0 | .await; |
5097 | | } |
5098 | | |
5099 | | ( |
5100 | 0 | transactions_service::DropReason::Finalized { block_hash, .. }, |
5101 | 0 | TransactionWatchTy::Legacy, |
5102 | 0 | ) => { |
5103 | 0 | let _ = me |
5104 | 0 | .responses_tx |
5105 | 0 | .send( |
5106 | 0 | methods::ServerToClient::author_extrinsicUpdate { |
5107 | 0 | subscription: Cow::Borrowed(&subscription_id), |
5108 | 0 | result: methods::TransactionStatus::Finalized( |
5109 | 0 | methods::HashHexString(block_hash), |
5110 | 0 | ), |
5111 | 0 | } |
5112 | 0 | .to_json_request_object_parameters(None), |
5113 | 0 | ) |
5114 | 0 | .await; |
5115 | | } |
5116 | | ( |
5117 | 0 | transactions_service::DropReason::Finalized { block_hash, index }, |
5118 | 0 | TransactionWatchTy::NewApiWatch, |
5119 | 0 | ) => { |
5120 | 0 | let _ = me |
5121 | 0 | .responses_tx |
5122 | 0 | .send( |
5123 | 0 | methods::ServerToClient::transactionWatch_v1_watchEvent { |
5124 | 0 | subscription: Cow::Borrowed(&subscription_id), |
5125 | 0 | result: methods::TransactionWatchEvent::Finalized { |
5126 | 0 | block: methods::TransactionWatchEventBlock { |
5127 | 0 | hash: methods::HashHexString(block_hash), |
5128 | 0 | index, |
5129 | 0 | }, |
5130 | 0 | }, |
5131 | 0 | } |
5132 | 0 | .to_json_request_object_parameters(None), |
5133 | 0 | ) |
5134 | 0 | .await; |
5135 | | } |
5136 | | } |
5137 | | } |
5138 | | |
5139 | | WakeUpReason::Event(Event::TransactionEvent { |
5140 | 0 | subscription_id, |
5141 | 0 | event, |
5142 | 0 | mut watcher, |
5143 | | }) => { |
5144 | | // Event (other than `Dropped`, as it's handled above) from the |
5145 | | // transactions service. |
5146 | 0 | let Some(transaction_watch) = |
5147 | 0 | me.transactions_subscriptions.get_mut(&subscription_id) |
5148 | | else { |
5149 | | // JSON-RPC client has unsubscribed from this transaction and is no longer |
5150 | | // interested in events. |
5151 | 0 | continue; |
5152 | | }; |
5153 | | |
5154 | 0 | match (event, &transaction_watch.ty) { |
5155 | 0 | (_, TransactionWatchTy::NewApi { .. }) => { |
5156 | 0 | // Events are ignored when it comes to `transaction_v1_broadcast`. |
5157 | 0 | } |
5158 | | |
5159 | | ( |
5160 | 0 | transactions_service::TransactionStatus::Broadcast(peers), |
5161 | 0 | TransactionWatchTy::Legacy, |
5162 | 0 | ) => { |
5163 | 0 | let _ = me |
5164 | 0 | .responses_tx |
5165 | 0 | .send( |
5166 | 0 | methods::ServerToClient::author_extrinsicUpdate { |
5167 | 0 | subscription: Cow::Borrowed(&subscription_id), |
5168 | 0 | result: methods::TransactionStatus::Broadcast( |
5169 | 0 | peers.into_iter().map(|peer| peer.to_base58()).collect(), Unexecuted instantiation: _RNCNCINvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background3runpE0sT_0Ba_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sT_0B1f_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runpE0sT_0Ba_ |
5170 | 0 | ), |
5171 | 0 | } |
5172 | 0 | .to_json_request_object_parameters(None), |
5173 | 0 | ) |
5174 | 0 | .await; |
5175 | | } |
5176 | | ( |
5177 | 0 | transactions_service::TransactionStatus::Broadcast(peers), |
5178 | 0 | TransactionWatchTy::NewApiWatch, |
5179 | 0 | ) => { |
5180 | 0 | transaction_watch.num_broadcasted_peers += peers.len(); |
5181 | 0 | let _ = me |
5182 | 0 | .responses_tx |
5183 | 0 | .send( |
5184 | 0 | methods::ServerToClient::transactionWatch_v1_watchEvent { |
5185 | 0 | subscription: Cow::Borrowed(&subscription_id), |
5186 | 0 | result: methods::TransactionWatchEvent::Broadcasted { |
5187 | 0 | num_peers: u32::try_from( |
5188 | 0 | transaction_watch.num_broadcasted_peers, |
5189 | 0 | ) |
5190 | 0 | .unwrap_or(u32::MAX), |
5191 | 0 | }, |
5192 | 0 | } |
5193 | 0 | .to_json_request_object_parameters(None), |
5194 | 0 | ) |
5195 | 0 | .await; |
5196 | | } |
5197 | | |
5198 | | ( |
5199 | | transactions_service::TransactionStatus::Validated, |
5200 | | TransactionWatchTy::Legacy, |
5201 | 0 | ) => { |
5202 | 0 | // Nothing to do. |
5203 | 0 | } |
5204 | | ( |
5205 | | transactions_service::TransactionStatus::Validated, |
5206 | | TransactionWatchTy::NewApiWatch, |
5207 | | ) => { |
5208 | 0 | let _ = me |
5209 | 0 | .responses_tx |
5210 | 0 | .send( |
5211 | 0 | methods::ServerToClient::transactionWatch_v1_watchEvent { |
5212 | 0 | subscription: Cow::Borrowed(&subscription_id), |
5213 | 0 | result: methods::TransactionWatchEvent::Validated {}, |
5214 | 0 | } |
5215 | 0 | .to_json_request_object_parameters(None), |
5216 | 0 | ) |
5217 | 0 | .await; |
5218 | | } |
5219 | | |
5220 | | ( |
5221 | | transactions_service::TransactionStatus::IncludedBlockUpdate { |
5222 | 0 | block_hash: Some((block_hash, _)), |
5223 | 0 | }, |
5224 | 0 | TransactionWatchTy::Legacy, |
5225 | 0 | ) => { |
5226 | 0 | transaction_watch.included_block = Some(block_hash); |
5227 | 0 | let _ = me |
5228 | 0 | .responses_tx |
5229 | 0 | .send( |
5230 | 0 | methods::ServerToClient::author_extrinsicUpdate { |
5231 | 0 | subscription: Cow::Borrowed(&subscription_id), |
5232 | 0 | result: methods::TransactionStatus::InBlock( |
5233 | 0 | methods::HashHexString(block_hash), |
5234 | 0 | ), |
5235 | 0 | } |
5236 | 0 | .to_json_request_object_parameters(None), |
5237 | 0 | ) |
5238 | 0 | .await; |
5239 | | } |
5240 | | ( |
5241 | | transactions_service::TransactionStatus::IncludedBlockUpdate { |
5242 | | block_hash: None, |
5243 | | }, |
5244 | | TransactionWatchTy::Legacy, |
5245 | | ) => { |
5246 | 0 | if let Some(block_hash) = transaction_watch.included_block.take() { |
5247 | 0 | let _ = me |
5248 | 0 | .responses_tx |
5249 | 0 | .send( |
5250 | 0 | methods::ServerToClient::author_extrinsicUpdate { |
5251 | 0 | subscription: Cow::Borrowed(&subscription_id), |
5252 | 0 | result: methods::TransactionStatus::Retracted( |
5253 | 0 | methods::HashHexString(block_hash), |
5254 | 0 | ), |
5255 | 0 | } |
5256 | 0 | .to_json_request_object_parameters(None), |
5257 | 0 | ) |
5258 | 0 | .await; |
5259 | 0 | } |
5260 | | } |
5261 | | ( |
5262 | | transactions_service::TransactionStatus::IncludedBlockUpdate { |
5263 | 0 | block_hash: Some((block_hash, index)), |
5264 | 0 | }, |
5265 | 0 | TransactionWatchTy::NewApiWatch, |
5266 | 0 | ) => { |
5267 | 0 | transaction_watch.included_block = Some(block_hash); |
5268 | 0 | let _ = me |
5269 | 0 | .responses_tx |
5270 | 0 | .send( |
5271 | 0 | methods::ServerToClient::transactionWatch_v1_watchEvent { |
5272 | 0 | subscription: Cow::Borrowed(&subscription_id), |
5273 | 0 | result: |
5274 | 0 | methods::TransactionWatchEvent::BestChainBlockIncluded { |
5275 | 0 | block: Some(methods::TransactionWatchEventBlock { |
5276 | 0 | hash: methods::HashHexString(block_hash), |
5277 | 0 | index, |
5278 | 0 | }), |
5279 | 0 | }, |
5280 | 0 | } |
5281 | 0 | .to_json_request_object_parameters(None), |
5282 | 0 | ) |
5283 | 0 | .await; |
5284 | | } |
5285 | | ( |
5286 | | transactions_service::TransactionStatus::IncludedBlockUpdate { |
5287 | | block_hash: None, |
5288 | | }, |
5289 | | TransactionWatchTy::NewApiWatch, |
5290 | | ) => { |
5291 | 0 | let _ = me |
5292 | 0 | .responses_tx |
5293 | 0 | .send( |
5294 | 0 | methods::ServerToClient::transactionWatch_v1_watchEvent { |
5295 | 0 | subscription: Cow::Borrowed(&subscription_id), |
5296 | 0 | result: |
5297 | 0 | methods::TransactionWatchEvent::BestChainBlockIncluded { |
5298 | 0 | block: None, |
5299 | 0 | }, |
5300 | 0 | } |
5301 | 0 | .to_json_request_object_parameters(None), |
5302 | 0 | ) |
5303 | 0 | .await; |
5304 | | } |
5305 | | |
5306 | | // `Dropped` was handle above separately. |
5307 | 0 | (transactions_service::TransactionStatus::Dropped(_), _) => unreachable!(), |
5308 | | } |
5309 | | |
5310 | | // Push a new background task that waits for the next notification. |
5311 | 0 | me.background_tasks.push(Box::pin(async move { |
5312 | 0 | let Some(status) = watcher.as_mut().next().await else { |
5313 | 0 | unreachable!() |
5314 | | }; |
5315 | 0 | Event::TransactionEvent { |
5316 | 0 | subscription_id, |
5317 | 0 | event: status, |
5318 | 0 | watcher, |
5319 | 0 | } |
5320 | 0 | })); Unexecuted instantiation: _RNCNCINvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background3runpE0sU_0Ba_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sU_0B1f_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runpE0sU_0Ba_ |
5321 | | } |
5322 | | |
5323 | | WakeUpReason::Event(Event::ChainGetBlockResult { |
5324 | 0 | request_id_json, |
5325 | 0 | mut result, |
5326 | 0 | expected_block_hash, |
5327 | | }) => { |
5328 | | // A network request necessary to fulfill `chain_getBlock` has finished. |
5329 | | |
5330 | | // Check whether the header and body are present and valid. |
5331 | | // TODO: try the request again with a different peerin case the response is invalid, instead of returning null |
5332 | 0 | if let Ok(block) = &result { |
5333 | 0 | if let (Some(header), Some(body)) = (&block.header, &block.body) { |
5334 | 0 | if header::hash_from_scale_encoded_header(header) == expected_block_hash { |
5335 | 0 | if let Ok(decoded) = |
5336 | 0 | header::decode(header, me.sync_service.block_number_bytes()) |
5337 | | { |
5338 | 0 | if header::extrinsics_root(body) != *decoded.extrinsics_root { |
5339 | 0 | result = Err(()); |
5340 | 0 | } |
5341 | 0 | } else { |
5342 | 0 | // Note that if the header is undecodable it doesn't necessarily mean |
5343 | 0 | // that the header and/or body is bad, but given that we have no way to |
5344 | 0 | // check this we return an error. |
5345 | 0 | result = Err(()); |
5346 | 0 | } |
5347 | 0 | } else { |
5348 | 0 | result = Err(()); |
5349 | 0 | } |
5350 | 0 | } else { |
5351 | 0 | result = Err(()); |
5352 | 0 | } |
5353 | 0 | } |
5354 | | |
5355 | | // Send the response. |
5356 | 0 | if let Ok(block) = result { |
5357 | 0 | let _ = me |
5358 | 0 | .responses_tx |
5359 | 0 | .send( |
5360 | 0 | methods::Response::chain_getBlock(methods::Block { |
5361 | 0 | extrinsics: block |
5362 | 0 | .body |
5363 | 0 | .unwrap() |
5364 | 0 | .into_iter() |
5365 | 0 | .map(methods::HexString) |
5366 | 0 | .collect(), |
5367 | 0 | header: methods::Header::from_scale_encoded_header( |
5368 | 0 | &block.header.unwrap(), |
5369 | 0 | me.sync_service.block_number_bytes(), |
5370 | 0 | ) |
5371 | 0 | .unwrap(), |
5372 | 0 | // There's no way to verify the correctness of the justifications, consequently |
5373 | 0 | // we always return an empty list. |
5374 | 0 | justifications: None, |
5375 | 0 | }) |
5376 | 0 | .to_json_response(&request_id_json), |
5377 | 0 | ) |
5378 | 0 | .await; |
5379 | | } else { |
5380 | 0 | let _ = me |
5381 | 0 | .responses_tx |
5382 | 0 | .send(parse::build_success_response(&request_id_json, "null")) |
5383 | 0 | .await; |
5384 | | } |
5385 | | } |
5386 | | |
5387 | | WakeUpReason::StartStorageSubscriptionsUpdates => { |
5388 | | // Some existing `state_storageSubscribe` haven't been notified of subscription |
5389 | | // changes of the latest best block. Start storage requests on the network. |
5390 | | |
5391 | | let RuntimeServiceSubscription::Active { |
5392 | 0 | pinned_blocks, |
5393 | 0 | current_best_block, |
5394 | | .. |
5395 | 0 | } = &mut me.runtime_service_subscription |
5396 | | else { |
5397 | 0 | unreachable!() |
5398 | | }; |
5399 | | |
5400 | | // If the header of the current best block can't be decoded, we don't do anything. |
5401 | 0 | let (block_number, state_trie_root) = match header::decode( |
5402 | 0 | &pinned_blocks |
5403 | 0 | .get(current_best_block) |
5404 | 0 | .unwrap() |
5405 | 0 | .scale_encoded_header, |
5406 | 0 | me.runtime_service.block_number_bytes(), |
5407 | 0 | ) { |
5408 | 0 | Ok(header) => (header.number, *header.state_root), |
5409 | | Err(_) => { |
5410 | | // Can't decode the header of the current best block. |
5411 | | // All the subscriptions are marked as non-stale, since they are up-to-date |
5412 | | // with the current best block. |
5413 | | // TODO: print warning? |
5414 | 0 | me.legacy_api_stale_storage_subscriptions.clear(); |
5415 | 0 | continue; |
5416 | | } |
5417 | | }; |
5418 | | |
5419 | | // Build the list of keys that must be requested by aggregating the keys requested |
5420 | | // by all stale storage subscriptions. |
5421 | 0 | let mut keys = hashbrown::HashSet::with_hasher(SipHasherBuild::new({ |
5422 | 0 | let mut seed = [0; 16]; |
5423 | 0 | me.platform.fill_random_bytes(&mut seed); |
5424 | 0 | seed |
5425 | 0 | })); |
5426 | 0 | keys.extend( |
5427 | 0 | me.legacy_api_stale_storage_subscriptions |
5428 | 0 | .iter() |
5429 | 0 | .map(|s_id| { |
5430 | 0 | me.legacy_api_storage_subscriptions |
5431 | 0 | .range((s_id.clone(), Vec::new())..) |
5432 | 0 | .take_while(move |(s, _)| s == s_id) Unexecuted instantiation: _RNCNCNCINvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background3runpE0sV_00Bc_ Unexecuted instantiation: _RNCNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sV_00B1h_ Unexecuted instantiation: _RNCNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runpE0sV_00Bc_ |
5433 | 0 | .map(|(_, key)| key) Unexecuted instantiation: _RNCNCNCINvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background3runpE0sV_0s_0Bc_ Unexecuted instantiation: _RNCNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sV_0s_0B1h_ Unexecuted instantiation: _RNCNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runpE0sV_0s_0Bc_ |
5434 | 0 | }) Unexecuted instantiation: _RNCNCINvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background3runpE0sV_0Ba_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sV_0B1f_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runpE0sV_0Ba_ |
5435 | 0 | .flat_map(|keys_list| keys_list.cloned()), Unexecuted instantiation: _RNCNCINvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background3runpE0sW_0Ba_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sW_0B1f_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runpE0sW_0Ba_ |
5436 | 0 | ); |
5437 | 0 |
|
5438 | 0 | // If the list of keys to query is empty, we mark all subscriptions as no longer |
5439 | 0 | // stale and loop again. This is necessary in order to prevent infinite loops if |
5440 | 0 | // the JSON-RPC client subscribes to an empty list of items. |
5441 | 0 | if keys.is_empty() { |
5442 | 0 | me.legacy_api_stale_storage_subscriptions.clear(); |
5443 | 0 | continue; |
5444 | 0 | } |
5445 | 0 |
|
5446 | 0 | // Start the task in the background. |
5447 | 0 | // The task will generate a |
5448 | 0 | // `Event::LegacyApiStorageSubscriptionsUpdate` once it is done. |
5449 | 0 | me.legacy_api_storage_query_in_progress = true; |
5450 | 0 | me.background_tasks.push(Box::pin({ |
5451 | 0 | let block_hash = *current_best_block; |
5452 | 0 | let sync_service = me.sync_service.clone(); |
5453 | 0 | async move { |
5454 | 0 | let mut out = Vec::with_capacity(keys.len()); |
5455 | 0 | let mut query = sync_service |
5456 | 0 | .storage_query( |
5457 | 0 | block_number, |
5458 | 0 | block_hash, |
5459 | 0 | state_trie_root, |
5460 | 0 | keys.into_iter() |
5461 | 0 | .map(|key| sync_service::StorageRequestItem { |
5462 | 0 | key, |
5463 | 0 | ty: sync_service::StorageRequestItemTy::Value, |
5464 | 0 | }), Unexecuted instantiation: _RNCNCNCINvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background3runpE0sX_00Bc_ Unexecuted instantiation: _RNCNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sX_00B1h_ Unexecuted instantiation: _RNCNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runpE0sX_00Bc_ |
5465 | 0 | 4, |
5466 | 0 | Duration::from_secs(12), |
5467 | 0 | NonZeroU32::new(2).unwrap(), |
5468 | 0 | ) |
5469 | 0 | .advance() |
5470 | 0 | .await; |
5471 | 0 | loop { |
5472 | 0 | match query { |
5473 | | sync_service::StorageQueryProgress::Progress { |
5474 | 0 | item, |
5475 | 0 | query: next, |
5476 | 0 | .. |
5477 | 0 | } => { |
5478 | 0 | out.push(item); |
5479 | 0 | query = next.advance().await; |
5480 | | } |
5481 | | sync_service::StorageQueryProgress::Finished => { |
5482 | 0 | break Event::LegacyApiStorageSubscriptionsUpdate { |
5483 | 0 | block_hash, |
5484 | 0 | result: Ok(out), |
5485 | 0 | }; |
5486 | | } |
5487 | 0 | sync_service::StorageQueryProgress::Error(error) => { |
5488 | 0 | break Event::LegacyApiStorageSubscriptionsUpdate { |
5489 | 0 | block_hash, |
5490 | 0 | result: Err(error), |
5491 | 0 | }; |
5492 | | } |
5493 | | } |
5494 | | } |
5495 | 0 | } Unexecuted instantiation: _RNCNCINvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background3runpE0sX_0Ba_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sX_0B1f_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runpE0sX_0Ba_ |
5496 | 0 | })); |
5497 | | } |
5498 | | |
5499 | | WakeUpReason::Event(Event::LegacyApiStorageSubscriptionsUpdate { |
5500 | 0 | block_hash, |
5501 | 0 | result: Ok(result), |
5502 | 0 | }) => { |
5503 | 0 | // Background task dedicated to performing a storage query for the storage |
5504 | 0 | // subscriptions has finished. |
5505 | 0 |
|
5506 | 0 | debug_assert!(me.legacy_api_storage_query_in_progress); |
5507 | 0 | me.legacy_api_storage_query_in_progress = false; |
5508 | | |
5509 | | // Determine whether another storage query targeting a more up-to-date block |
5510 | | // must be started afterwards. |
5511 | 0 | let is_up_to_date = match me.runtime_service_subscription { |
5512 | | RuntimeServiceSubscription::Active { |
5513 | 0 | current_best_block, .. |
5514 | 0 | } => current_best_block == block_hash, |
5515 | | RuntimeServiceSubscription::NotCreated |
5516 | 0 | | RuntimeServiceSubscription::Pending(_) => true, |
5517 | | }; |
5518 | | |
5519 | | // Because all the keys of all the subscriptions are merged into one network |
5520 | | // request, we must now attribute each item in the result back to its subscription. |
5521 | | // While this solution is a bit CPU-heavy, it is a more elegant solution than |
5522 | | // keeping track of subscription in the background task. |
5523 | 0 | let mut notifications_to_send = hashbrown::HashMap::< |
5524 | 0 | Arc<str>, |
5525 | 0 | Vec<(methods::HexString, Option<methods::HexString>)>, |
5526 | 0 | _, |
5527 | 0 | >::with_capacity_and_hasher( |
5528 | 0 | me.legacy_api_storage_subscriptions.len(), |
5529 | 0 | fnv::FnvBuildHasher::default(), |
5530 | 0 | ); |
5531 | 0 | for item in result { |
5532 | 0 | let sync_service::StorageResultItem::Value { key, value } = item else { |
5533 | 0 | unreachable!() |
5534 | | }; |
5535 | 0 | for subscription_id in me |
5536 | 0 | .legacy_api_storage_subscriptions_by_key |
5537 | 0 | .range((key.clone(), Arc::from(String::new()))..) |
5538 | 0 | .take_while(|(k, _)| *k == key) Unexecuted instantiation: _RNCNCINvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background3runpE0sY_0Ba_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sY_0B1f_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runpE0sY_0Ba_ |
5539 | 0 | .map(|(_, s_id)| s_id.clone()) Unexecuted instantiation: _RNCNCINvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background3runpE0sZ_0Ba_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sZ_0B1f_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runpE0sZ_0Ba_ |
5540 | 0 | { |
5541 | 0 | notifications_to_send |
5542 | 0 | .entry(subscription_id) |
5543 | 0 | .or_insert_with(Vec::new) |
5544 | 0 | .push(( |
5545 | 0 | methods::HexString(key.clone()), |
5546 | 0 | value.clone().map(methods::HexString), |
5547 | 0 | )); |
5548 | 0 | } |
5549 | | } |
5550 | | |
5551 | | // Send the notifications and mark the subscriptions as no longer stale if |
5552 | | // relevant. |
5553 | 0 | for (subscription_id, changes) in notifications_to_send { |
5554 | 0 | if is_up_to_date { |
5555 | 0 | me.legacy_api_stale_storage_subscriptions |
5556 | 0 | .remove(&subscription_id); |
5557 | 0 | } |
5558 | 0 | let _ = me |
5559 | 0 | .responses_tx |
5560 | 0 | .send( |
5561 | 0 | methods::ServerToClient::state_storage { |
5562 | 0 | subscription: Cow::Borrowed(&*subscription_id), |
5563 | 0 | result: methods::StorageChangeSet { |
5564 | 0 | block: methods::HashHexString(block_hash), |
5565 | 0 | changes, |
5566 | 0 | }, |
5567 | 0 | } |
5568 | 0 | .to_json_request_object_parameters(None), |
5569 | 0 | ) |
5570 | 0 | .await; |
5571 | | } |
5572 | | } |
5573 | | |
5574 | | // Background task dedicated to performing a storage query for the storage |
5575 | | // subscription has finished but was unsuccessful. |
5576 | | WakeUpReason::Event(Event::LegacyApiStorageSubscriptionsUpdate { |
5577 | | result: Err(_), |
5578 | | .. |
5579 | | }) => { |
5580 | | // Background task dedicated to performing a storage query for the storage |
5581 | | // subscriptions has failed. |
5582 | 0 | debug_assert!(me.legacy_api_storage_query_in_progress); |
5583 | 0 | me.legacy_api_storage_query_in_progress = false; |
5584 | | // TODO: add a delay or something? |
5585 | | } |
5586 | | |
5587 | | WakeUpReason::NotifyFinalizedHeads => { |
5588 | | // All `chain_subscribeFinalizedHeads` subscriptions must be notified of the |
5589 | | // latest finalized block. |
5590 | | |
5591 | | let RuntimeServiceSubscription::Active { |
5592 | 0 | pinned_blocks, |
5593 | 0 | current_finalized_block, |
5594 | 0 | finalized_heads_subscriptions_stale, |
5595 | | .. |
5596 | 0 | } = &mut me.runtime_service_subscription |
5597 | | else { |
5598 | 0 | unreachable!() |
5599 | | }; |
5600 | | |
5601 | 0 | let finalized_block_header = &pinned_blocks |
5602 | 0 | .get(current_finalized_block) |
5603 | 0 | .unwrap() |
5604 | 0 | .scale_encoded_header; |
5605 | 0 | let finalized_block_json_rpc_header = |
5606 | 0 | match methods::Header::from_scale_encoded_header( |
5607 | 0 | finalized_block_header, |
5608 | 0 | me.runtime_service.block_number_bytes(), |
5609 | 0 | ) { |
5610 | 0 | Ok(h) => h, |
5611 | 0 | Err(error) => { |
5612 | 0 | log!( |
5613 | 0 | &me.platform, |
5614 | 0 | Warn, |
5615 | 0 | &me.log_target, |
5616 | 0 | format!( |
5617 | 0 | "`chain_subscribeFinalizedHeads` subscription has skipped \ |
5618 | 0 | block due to undecodable header. Hash: {}. Error: {}", |
5619 | 0 | HashDisplay(current_finalized_block), |
5620 | 0 | error, |
5621 | 0 | ) |
5622 | 0 | ); |
5623 | 0 | continue; |
5624 | | } |
5625 | | }; |
5626 | | |
5627 | 0 | for subscription_id in &me.finalized_heads_subscriptions { |
5628 | 0 | let _ = me |
5629 | 0 | .responses_tx |
5630 | 0 | .send( |
5631 | 0 | methods::ServerToClient::chain_finalizedHead { |
5632 | 0 | subscription: Cow::Borrowed(subscription_id), |
5633 | 0 | result: finalized_block_json_rpc_header.clone(), |
5634 | 0 | } |
5635 | 0 | .to_json_request_object_parameters(None), |
5636 | 0 | ) |
5637 | 0 | .await; |
5638 | | } |
5639 | | |
5640 | 0 | *finalized_heads_subscriptions_stale = false; |
5641 | | } |
5642 | | |
5643 | 0 | WakeUpReason::NotifyNewHeadsRuntimeSubscriptions(previous_best_block) => { |
5644 | | // All `chain_subscribeNewHeads` subscriptions must be notified of the |
5645 | | // latest best block. |
5646 | | |
5647 | | let RuntimeServiceSubscription::Active { |
5648 | 0 | pinned_blocks, |
5649 | 0 | current_best_block, |
5650 | | .. |
5651 | 0 | } = &mut me.runtime_service_subscription |
5652 | | else { |
5653 | 0 | unreachable!() |
5654 | | }; |
5655 | | |
5656 | 0 | let best_block_header = &pinned_blocks |
5657 | 0 | .get(current_best_block) |
5658 | 0 | .unwrap() |
5659 | 0 | .scale_encoded_header; |
5660 | 0 | let best_block_json_rpc_header = match methods::Header::from_scale_encoded_header( |
5661 | 0 | best_block_header, |
5662 | 0 | me.runtime_service.block_number_bytes(), |
5663 | 0 | ) { |
5664 | 0 | Ok(h) => h, |
5665 | 0 | Err(error) => { |
5666 | 0 | log!( |
5667 | 0 | &me.platform, |
5668 | 0 | Warn, |
5669 | 0 | &me.log_target, |
5670 | 0 | format!( |
5671 | 0 | "`chain_subscribeNewHeads` subscription has skipped block due to \ |
5672 | 0 | undecodable header. Hash: {}. Error: {}", |
5673 | 0 | HashDisplay(current_best_block), |
5674 | 0 | error |
5675 | 0 | ) |
5676 | 0 | ); |
5677 | 0 | continue; |
5678 | | } |
5679 | | }; |
5680 | | |
5681 | 0 | for subscription_id in &me.new_heads_subscriptions { |
5682 | 0 | let _ = me |
5683 | 0 | .responses_tx |
5684 | 0 | .send( |
5685 | 0 | methods::ServerToClient::chain_newHead { |
5686 | 0 | subscription: Cow::Borrowed(subscription_id), |
5687 | 0 | result: best_block_json_rpc_header.clone(), |
5688 | 0 | } |
5689 | 0 | .to_json_request_object_parameters(None), |
5690 | 0 | ) |
5691 | 0 | .await; |
5692 | | } |
5693 | | |
5694 | 0 | let new_best_runtime = &pinned_blocks |
5695 | 0 | .get(current_best_block) |
5696 | 0 | .unwrap() |
5697 | 0 | .runtime_version; |
5698 | 0 | if previous_best_block.map_or(true, |prev_best_block| { |
5699 | 0 | !Arc::ptr_eq( |
5700 | 0 | new_best_runtime, |
5701 | 0 | &pinned_blocks.get(&prev_best_block).unwrap().runtime_version, |
5702 | 0 | ) |
5703 | 0 | }) { Unexecuted instantiation: _RNCNCINvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background3runpE0s10_0Ba_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0s10_0B1f_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runpE0s10_0Ba_ |
5704 | 0 | if let Ok(new_best_runtime) = &**new_best_runtime { |
5705 | 0 | for subscription_id in &me.runtime_version_subscriptions { |
5706 | 0 | let _ = me |
5707 | 0 | .responses_tx |
5708 | 0 | .send( |
5709 | 0 | methods::ServerToClient::state_runtimeVersion { |
5710 | 0 | subscription: subscription_id.as_str().into(), |
5711 | 0 | result: Some(convert_runtime_version_legacy( |
5712 | 0 | new_best_runtime, |
5713 | 0 | )), |
5714 | 0 | } |
5715 | 0 | .to_json_request_object_parameters(None), |
5716 | 0 | ) |
5717 | 0 | .await; |
5718 | | } |
5719 | 0 | } |
5720 | 0 | } |
5721 | | |
5722 | | // The `state_subscribeStorage` subscriptions are marked as stale after sending |
5723 | | // out the notifications. |
5724 | 0 | me.legacy_api_stale_storage_subscriptions.extend( |
5725 | 0 | me.legacy_api_storage_subscriptions |
5726 | 0 | .iter() |
5727 | 0 | .map(|(s_id, _)| s_id.clone()), Unexecuted instantiation: _RNCNCINvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background3runpE0s11_0Ba_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0s11_0B1f_ Unexecuted instantiation: _RNCNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runpE0s11_0Ba_ |
5728 | 0 | ); |
5729 | | } |
5730 | | } |
5731 | | } |
5732 | 0 | } Unexecuted instantiation: _RNCINvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background3runpE0B8_ Unexecuted instantiation: _RNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0B1d_ Unexecuted instantiation: _RNCINvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background3runpE0B8_ |
5733 | | |
5734 | 0 | fn convert_runtime_version_legacy( |
5735 | 0 | runtime_spec: &smoldot::executor::CoreVersion, |
5736 | 0 | ) -> methods::RuntimeVersion { |
5737 | 0 | let runtime_spec = runtime_spec.decode(); |
5738 | 0 | methods::RuntimeVersion { |
5739 | 0 | spec_name: runtime_spec.spec_name.into(), |
5740 | 0 | impl_name: runtime_spec.impl_name.into(), |
5741 | 0 | authoring_version: u64::from(runtime_spec.authoring_version), |
5742 | 0 | spec_version: u64::from(runtime_spec.spec_version), |
5743 | 0 | impl_version: u64::from(runtime_spec.impl_version), |
5744 | 0 | transaction_version: runtime_spec.transaction_version.map(u64::from), |
5745 | 0 | state_version: runtime_spec.state_version.map(u8::from).map(u64::from), |
5746 | 0 | apis: runtime_spec |
5747 | 0 | .apis |
5748 | 0 | .map(|api| (methods::HexString(api.name_hash.to_vec()), api.version)) Unexecuted instantiation: _RNCNvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background30convert_runtime_version_legacy0B7_ Unexecuted instantiation: _RNCNvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background30convert_runtime_version_legacy0B7_ |
5749 | 0 | .collect(), |
5750 | 0 | } |
5751 | 0 | } Unexecuted instantiation: _RNvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background30convert_runtime_version_legacy Unexecuted instantiation: _RNvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background30convert_runtime_version_legacy |
5752 | | |
5753 | 0 | fn convert_runtime_version(runtime_spec: &smoldot::executor::CoreVersion) -> methods::RuntimeSpec { |
5754 | 0 | let runtime_spec = runtime_spec.decode(); |
5755 | 0 | methods::RuntimeSpec { |
5756 | 0 | spec_name: runtime_spec.spec_name.into(), |
5757 | 0 | impl_name: runtime_spec.impl_name.into(), |
5758 | 0 | spec_version: runtime_spec.spec_version, |
5759 | 0 | impl_version: runtime_spec.impl_version, |
5760 | 0 | transaction_version: runtime_spec.transaction_version, |
5761 | 0 | apis: runtime_spec |
5762 | 0 | .apis |
5763 | 0 | .map(|api| (methods::HexString(api.name_hash.to_vec()), api.version)) Unexecuted instantiation: _RNCNvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background23convert_runtime_version0B7_ Unexecuted instantiation: _RNCNvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background23convert_runtime_version0B7_ |
5764 | 0 | .collect(), |
5765 | 0 | } |
5766 | 0 | } Unexecuted instantiation: _RNvNtNtCsiGub1lfKphe_13smoldot_light16json_rpc_service10background23convert_runtime_version Unexecuted instantiation: _RNvNtNtCsih6EgvAwZF2_13smoldot_light16json_rpc_service10background23convert_runtime_version |