/__w/smoldot/smoldot/repo/light-base/src/runtime_service.rs
Line | Count | Source (jump to first uncovered line) |
1 | | // Smoldot |
2 | | // Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. |
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 | | //! Background runtime download service. |
19 | | //! |
20 | | //! This service plugs on top of a [`sync_service`], listens for new best blocks and checks |
21 | | //! whether the runtime has changed in any way. Its objective is to always provide an up-to-date |
22 | | //! [`executor::host::HostVmPrototype`] ready to be called by other services. |
23 | | //! |
24 | | //! # Usage |
25 | | //! |
26 | | //! The runtime service lets user subscribe to block updates, similar to the [`sync_service`]. |
27 | | //! These subscriptions are implemented by subscribing to the underlying [`sync_service`] and, |
28 | | //! for each notification, checking whether the runtime has changed (thanks to the presence or |
29 | | //! absence of a header digest item), and downloading the runtime code if necessary. Therefore, |
30 | | //! these notifications might come with a delay compared to directly using the [`sync_service`]. |
31 | | //! |
32 | | //! If it isn't possible to download the runtime code of a block (for example because peers refuse |
33 | | //! to answer or have already pruned the block) or if the runtime service already has too many |
34 | | //! pending downloads, this block is simply not reported on the subscriptions. The download will |
35 | | //! be repeatedly tried until it succeeds. |
36 | | //! |
37 | | //! Consequently, you are strongly encouraged to not use both the [`sync_service`] *and* the |
38 | | //! [`RuntimeService`] of the same chain. They each provide a consistent view of the chain, but |
39 | | //! this view isn't necessarily the same on both services. |
40 | | //! |
41 | | //! The main service offered by the runtime service is [`RuntimeService::subscribe_all`], that |
42 | | //! notifies about new blocks once their runtime is known. |
43 | | //! |
44 | | //! # Blocks pinning |
45 | | //! |
46 | | //! Blocks that are reported through [`RuntimeService::subscribe_all`] are automatically *pinned*. |
47 | | //! If multiple subscriptions exist, each block is pinned once per subscription. |
48 | | //! |
49 | | //! As long as a block is pinned, the [`RuntimeService`] is guaranteed to keep in its internal |
50 | | //! state the runtime of this block and its properties. |
51 | | //! |
52 | | //! Blocks must be manually unpinned by calling [`Subscription::unpin_block`]. |
53 | | //! Failing to do so is effectively a memory leak. If the number of pinned blocks becomes too |
54 | | //! large, the subscription is force-killed by the [`RuntimeService`]. |
55 | | //! |
56 | | |
57 | | use crate::{log, network_service, platform::PlatformRef, sync_service}; |
58 | | |
59 | | use alloc::{ |
60 | | borrow::{Cow, ToOwned as _}, |
61 | | boxed::Box, |
62 | | collections::{BTreeMap, VecDeque}, |
63 | | format, |
64 | | string::{String, ToString as _}, |
65 | | sync::{Arc, Weak}, |
66 | | vec::Vec, |
67 | | }; |
68 | | use async_lock::Mutex; |
69 | | use core::{ |
70 | | cmp, iter, mem, |
71 | | num::{NonZeroU32, NonZeroUsize}, |
72 | | ops, |
73 | | pin::Pin, |
74 | | time::Duration, |
75 | | }; |
76 | | use futures_channel::oneshot; |
77 | | use futures_lite::FutureExt as _; |
78 | | use futures_util::{future, stream, Stream, StreamExt as _}; |
79 | | use itertools::Itertools as _; |
80 | | use rand::seq::IteratorRandom as _; |
81 | | use rand_chacha::rand_core::SeedableRng as _; |
82 | | use smoldot::{ |
83 | | chain::async_tree, |
84 | | executor, header, |
85 | | informant::{BytesDisplay, HashDisplay}, |
86 | | trie::{self, proof_decode, Nibble}, |
87 | | }; |
88 | | |
89 | | /// Configuration for a runtime service. |
90 | | pub struct Config<TPlat: PlatformRef> { |
91 | | /// Name of the chain, for logging purposes. |
92 | | /// |
93 | | /// > **Note**: This name will be directly printed out. Any special character should already |
94 | | /// > have been filtered out from this name. |
95 | | pub log_name: String, |
96 | | |
97 | | /// Access to the platform's capabilities. |
98 | | pub platform: TPlat, |
99 | | |
100 | | /// Service responsible for synchronizing the chain. |
101 | | pub sync_service: Arc<sync_service::SyncService<TPlat>>, |
102 | | |
103 | | /// Service responsible for accessing the networking of the chain. |
104 | | pub network_service: Arc<network_service::NetworkServiceChain<TPlat>>, |
105 | | |
106 | | /// Header of the genesis block of the chain, in SCALE encoding. |
107 | | pub genesis_block_scale_encoded_header: Vec<u8>, |
108 | | } |
109 | | |
110 | | /// Runtime currently pinned within a [`RuntimeService`]. |
111 | | /// |
112 | | /// Destroying this object automatically unpins the runtime. |
113 | | #[derive(Clone)] |
114 | | pub struct PinnedRuntime(Arc<Runtime>); |
115 | | |
116 | | /// See [the module-level documentation](..). |
117 | | pub struct RuntimeService<TPlat: PlatformRef> { |
118 | | /// Configuration of the background task. Used to restart the background task if necessary. |
119 | | background_task_config: BackgroundTaskConfig<TPlat>, |
120 | | |
121 | | /// Sender to send messages to the background task. |
122 | | to_background: Mutex<async_channel::Sender<ToBackground<TPlat>>>, |
123 | | } |
124 | | |
125 | | impl<TPlat: PlatformRef> RuntimeService<TPlat> { |
126 | | /// Initializes a new runtime service. |
127 | 0 | pub fn new(config: Config<TPlat>) -> Self { |
128 | 0 | // Target to use for all the logs of this service. |
129 | 0 | let log_target = format!("runtime-{}", config.log_name); |
130 | 0 |
|
131 | 0 | let background_task_config = BackgroundTaskConfig { |
132 | 0 | log_target: log_target.clone(), |
133 | 0 | platform: config.platform.clone(), |
134 | 0 | sync_service: config.sync_service, |
135 | 0 | network_service: config.network_service, |
136 | 0 | genesis_block_scale_encoded_header: config.genesis_block_scale_encoded_header, |
137 | 0 | }; |
138 | 0 |
|
139 | 0 | // Spawns a task that runs in the background and updates the content of the mutex. |
140 | 0 | let to_background; |
141 | 0 | config.platform.spawn_task(log_target.clone().into(), { |
142 | 0 | let (tx, rx) = async_channel::bounded(16); |
143 | 0 | let tx_weak = tx.downgrade(); |
144 | 0 | to_background = tx; |
145 | 0 | let background_task_config = background_task_config.clone(); |
146 | 0 | run_background(background_task_config, rx, tx_weak) |
147 | 0 | }); |
148 | 0 |
|
149 | 0 | RuntimeService { |
150 | 0 | background_task_config, |
151 | 0 | to_background: Mutex::new(to_background), |
152 | 0 | } |
153 | 0 | } Unexecuted instantiation: _RNvMNtCsiGub1lfKphe_13smoldot_light15runtime_serviceINtB2_14RuntimeServicepE3newB4_ Unexecuted instantiation: _RNvMNtCsih6EgvAwZF2_13smoldot_light15runtime_serviceINtB2_14RuntimeServiceNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE3newB1e_ Unexecuted instantiation: _RNvMNtCsih6EgvAwZF2_13smoldot_light15runtime_serviceINtB2_14RuntimeServicepE3newB4_ |
154 | | |
155 | | /// Calls [`sync_service::SyncService::block_number_bytes`] on the sync service associated to |
156 | | /// this runtime service. |
157 | 0 | pub fn block_number_bytes(&self) -> usize { |
158 | 0 | self.background_task_config |
159 | 0 | .sync_service |
160 | 0 | .block_number_bytes() |
161 | 0 | } Unexecuted instantiation: _RNvMNtCsiGub1lfKphe_13smoldot_light15runtime_serviceINtB2_14RuntimeServicepE18block_number_bytesB4_ Unexecuted instantiation: _RNvMNtCsih6EgvAwZF2_13smoldot_light15runtime_serviceINtB2_14RuntimeServiceNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE18block_number_bytesB1e_ Unexecuted instantiation: _RNvMNtCsih6EgvAwZF2_13smoldot_light15runtime_serviceINtB2_14RuntimeServicepE18block_number_bytesB4_ |
162 | | |
163 | | /// Subscribes to the state of the chain: the current state and the new blocks. |
164 | | /// |
165 | | /// This function only returns once the runtime of the current finalized block is known. This |
166 | | /// might take a long time. |
167 | | /// |
168 | | /// Only up to `buffer_size` block notifications are buffered in the channel. If the channel |
169 | | /// is full when a new notification is attempted to be pushed, the channel gets closed. |
170 | | /// |
171 | | /// A maximum number of finalized or non-canonical (i.e. not part of the finalized chain) |
172 | | /// pinned blocks must be passed, indicating the maximum number of blocks that are finalized |
173 | | /// or non-canonical that the runtime service will pin at the same time for this subscription. |
174 | | /// If this maximum is reached, the channel will get closed. In situations where the subscriber |
175 | | /// is guaranteed to always properly unpin blocks, a value of `usize::MAX` can be |
176 | | /// passed in order to ignore this maximum. |
177 | | /// |
178 | | /// The channel also gets closed if a gap in the finality happens, such as after a Grandpa |
179 | | /// warp syncing. |
180 | | /// |
181 | | /// See [`SubscribeAll`] for information about the return value. |
182 | 0 | pub async fn subscribe_all( |
183 | 0 | &self, |
184 | 0 | buffer_size: usize, |
185 | 0 | max_pinned_blocks: NonZeroUsize, |
186 | 0 | ) -> SubscribeAll<TPlat> { Unexecuted instantiation: _RNvMNtCsiGub1lfKphe_13smoldot_light15runtime_serviceINtB2_14RuntimeServicepE13subscribe_allB4_ Unexecuted instantiation: _RNvMNtCsih6EgvAwZF2_13smoldot_light15runtime_serviceINtB2_14RuntimeServiceNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE13subscribe_allB1e_ Unexecuted instantiation: _RNvMNtCsih6EgvAwZF2_13smoldot_light15runtime_serviceINtB2_14RuntimeServicepE13subscribe_allB4_ |
187 | 0 | loop { |
188 | 0 | let (result_tx, result_rx) = oneshot::channel(); |
189 | 0 | let _ = self |
190 | 0 | .send_message_or_restart_service(ToBackground::SubscribeAll( |
191 | 0 | ToBackgroundSubscribeAll { |
192 | 0 | result_tx, |
193 | 0 | buffer_size, |
194 | 0 | max_pinned_blocks, |
195 | 0 | }, |
196 | 0 | )) |
197 | 0 | .await; |
198 | | |
199 | 0 | if let Ok(subscribe_all) = result_rx.await { |
200 | 0 | break subscribe_all; |
201 | 0 | } |
202 | | } |
203 | 0 | } Unexecuted instantiation: _RNCNvMNtCsiGub1lfKphe_13smoldot_light15runtime_serviceINtB4_14RuntimeServicepE13subscribe_all0B6_ Unexecuted instantiation: _RNCNvMNtCsih6EgvAwZF2_13smoldot_light15runtime_serviceINtB4_14RuntimeServiceNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE13subscribe_all0B1g_ Unexecuted instantiation: _RNCNvMNtCsih6EgvAwZF2_13smoldot_light15runtime_serviceINtB4_14RuntimeServicepE13subscribe_all0B6_ |
204 | | |
205 | | /// Unpins a block after it has been reported by a subscription. |
206 | | /// |
207 | | /// Has no effect if the [`SubscriptionId`] is not or no longer valid (as the runtime service |
208 | | /// can kill any subscription at any moment). |
209 | | /// |
210 | | /// # Panic |
211 | | /// |
212 | | /// Panics if the block hash has not been reported or has already been unpinned. |
213 | | /// |
214 | | // TODO: add #[track_caller] once possible, see https://github.com/rust-lang/rust/issues/87417 |
215 | 0 | pub async fn unpin_block(&self, subscription_id: SubscriptionId, block_hash: [u8; 32]) { Unexecuted instantiation: _RNvMNtCsiGub1lfKphe_13smoldot_light15runtime_serviceINtB2_14RuntimeServicepE11unpin_blockB4_ Unexecuted instantiation: _RNvMNtCsih6EgvAwZF2_13smoldot_light15runtime_serviceINtB2_14RuntimeServiceNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE11unpin_blockB1e_ Unexecuted instantiation: _RNvMNtCsih6EgvAwZF2_13smoldot_light15runtime_serviceINtB2_14RuntimeServicepE11unpin_blockB4_ |
216 | 0 | let (result_tx, result_rx) = oneshot::channel(); |
217 | 0 | let _ = self |
218 | 0 | .to_background |
219 | 0 | .lock() |
220 | 0 | .await |
221 | 0 | .send(ToBackground::UnpinBlock { |
222 | 0 | result_tx, |
223 | 0 | subscription_id, |
224 | 0 | block_hash, |
225 | 0 | }) |
226 | 0 | .await; |
227 | 0 | match result_rx.await { |
228 | 0 | Ok(Ok(())) => { |
229 | 0 | // Background task has indicated success. |
230 | 0 | } |
231 | 0 | Err(_) => { |
232 | 0 | // Background task has crashed. Subscription is stale. Function has no effect. |
233 | 0 | } |
234 | | Ok(Err(_)) => { |
235 | | // Background task has indicated that the block has already been unpinned. |
236 | 0 | panic!() |
237 | | } |
238 | | } |
239 | 0 | } Unexecuted instantiation: _RNCNvMNtCsiGub1lfKphe_13smoldot_light15runtime_serviceINtB4_14RuntimeServicepE11unpin_block0B6_ Unexecuted instantiation: _RNCNvMNtCsih6EgvAwZF2_13smoldot_light15runtime_serviceINtB4_14RuntimeServiceNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE11unpin_block0B1g_ Unexecuted instantiation: _RNCNvMNtCsih6EgvAwZF2_13smoldot_light15runtime_serviceINtB4_14RuntimeServicepE11unpin_block0B6_ |
240 | | |
241 | | /// Returns the storage value and Merkle value of the `:code` key of the finalized block. |
242 | | /// |
243 | | /// Returns `None` if the runtime of the current finalized block is not known yet. |
244 | | // TODO: this function has a bad API but is hopefully temporary |
245 | 0 | pub async fn finalized_runtime_storage_merkle_values( |
246 | 0 | &self, |
247 | 0 | ) -> Option<(Option<Vec<u8>>, Option<Vec<u8>>, Option<Vec<Nibble>>)> { Unexecuted instantiation: _RNvMNtCsiGub1lfKphe_13smoldot_light15runtime_serviceINtB2_14RuntimeServicepE39finalized_runtime_storage_merkle_valuesB4_ Unexecuted instantiation: _RNvMNtCsih6EgvAwZF2_13smoldot_light15runtime_serviceINtB2_14RuntimeServiceNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE39finalized_runtime_storage_merkle_valuesB1e_ Unexecuted instantiation: _RNvMNtCsih6EgvAwZF2_13smoldot_light15runtime_serviceINtB2_14RuntimeServicepE39finalized_runtime_storage_merkle_valuesB4_ |
248 | 0 | let (result_tx, result_rx) = oneshot::channel(); |
249 | 0 |
|
250 | 0 | let _ = self |
251 | 0 | .to_background |
252 | 0 | .lock() |
253 | 0 | .await |
254 | 0 | .send(ToBackground::FinalizedRuntimeStorageMerkleValues { result_tx }) |
255 | 0 | .await; |
256 | | |
257 | 0 | result_rx.await.unwrap_or(None) |
258 | 0 | } Unexecuted instantiation: _RNCNvMNtCsiGub1lfKphe_13smoldot_light15runtime_serviceINtB4_14RuntimeServicepE39finalized_runtime_storage_merkle_values0B6_ Unexecuted instantiation: _RNCNvMNtCsih6EgvAwZF2_13smoldot_light15runtime_serviceINtB4_14RuntimeServiceNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE39finalized_runtime_storage_merkle_values0B1g_ Unexecuted instantiation: _RNCNvMNtCsih6EgvAwZF2_13smoldot_light15runtime_serviceINtB4_14RuntimeServicepE39finalized_runtime_storage_merkle_values0B6_ |
259 | | |
260 | | /// Pins the runtime of a pinned block. |
261 | | /// |
262 | | /// The hash of the block passed as parameter corresponds to the block whose runtime is to |
263 | | /// be pinned. The block must be currently pinned in the context of the provided |
264 | | /// [`SubscriptionId`]. |
265 | | /// |
266 | | /// Returns the pinned runtime, plus the state trie root hash and height of the block. |
267 | | /// |
268 | | /// Returns an error if the subscription is stale, meaning that it has been reset by the |
269 | | /// runtime service. |
270 | 0 | pub async fn pin_pinned_block_runtime( |
271 | 0 | &self, |
272 | 0 | subscription_id: SubscriptionId, |
273 | 0 | block_hash: [u8; 32], |
274 | 0 | ) -> Result<(PinnedRuntime, [u8; 32], u64), PinPinnedBlockRuntimeError> { Unexecuted instantiation: _RNvMNtCsiGub1lfKphe_13smoldot_light15runtime_serviceINtB2_14RuntimeServicepE24pin_pinned_block_runtimeB4_ Unexecuted instantiation: _RNvMNtCsih6EgvAwZF2_13smoldot_light15runtime_serviceINtB2_14RuntimeServiceNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE24pin_pinned_block_runtimeB1e_ Unexecuted instantiation: _RNvMNtCsih6EgvAwZF2_13smoldot_light15runtime_serviceINtB2_14RuntimeServicepE24pin_pinned_block_runtimeB4_ |
275 | 0 | let (result_tx, result_rx) = oneshot::channel(); |
276 | 0 |
|
277 | 0 | let _ = self |
278 | 0 | .to_background |
279 | 0 | .lock() |
280 | 0 | .await |
281 | 0 | .send(ToBackground::PinPinnedBlockRuntime { |
282 | 0 | result_tx, |
283 | 0 | subscription_id, |
284 | 0 | block_hash, |
285 | 0 | }) |
286 | 0 | .await; |
287 | | |
288 | 0 | match result_rx.await { |
289 | 0 | Ok(result) => result.map(|(r, v, n)| (PinnedRuntime(r), v, n)), Unexecuted instantiation: _RNCNCNvMNtCsiGub1lfKphe_13smoldot_light15runtime_serviceINtB6_14RuntimeServicepE24pin_pinned_block_runtime00B8_ Unexecuted instantiation: _RNCNCNvMNtCsih6EgvAwZF2_13smoldot_light15runtime_serviceINtB6_14RuntimeServiceNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE24pin_pinned_block_runtime00B1i_ Unexecuted instantiation: _RNCNCNvMNtCsih6EgvAwZF2_13smoldot_light15runtime_serviceINtB6_14RuntimeServicepE24pin_pinned_block_runtime00B8_ |
290 | | Err(_) => { |
291 | | // Background service has crashed. This means that the subscription is obsolete. |
292 | 0 | Err(PinPinnedBlockRuntimeError::ObsoleteSubscription) |
293 | | } |
294 | | } |
295 | 0 | } Unexecuted instantiation: _RNCNvMNtCsiGub1lfKphe_13smoldot_light15runtime_serviceINtB4_14RuntimeServicepE24pin_pinned_block_runtime0B6_ Unexecuted instantiation: _RNCNvMNtCsih6EgvAwZF2_13smoldot_light15runtime_serviceINtB4_14RuntimeServiceNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE24pin_pinned_block_runtime0B1g_ Unexecuted instantiation: _RNCNvMNtCsih6EgvAwZF2_13smoldot_light15runtime_serviceINtB4_14RuntimeServicepE24pin_pinned_block_runtime0B6_ |
296 | | |
297 | | /// Performs a runtime call. |
298 | | /// |
299 | | /// The hash of the block passed as parameter corresponds to the block whose runtime to use |
300 | | /// to make the call. The block must be currently pinned in the context of the provided |
301 | | /// [`SubscriptionId`]. |
302 | | /// |
303 | | /// Returns an error if the subscription is stale, meaning that it has been reset by the |
304 | | /// runtime service. |
305 | 0 | pub async fn runtime_call( |
306 | 0 | &self, |
307 | 0 | pinned_runtime: PinnedRuntime, |
308 | 0 | block_hash: [u8; 32], |
309 | 0 | block_number: u64, |
310 | 0 | block_state_trie_root_hash: [u8; 32], |
311 | 0 | function_name: String, |
312 | 0 | required_api_version: Option<(String, ops::RangeInclusive<u32>)>, |
313 | 0 | parameters_vectored: Vec<u8>, |
314 | 0 | total_attempts: u32, |
315 | 0 | timeout_per_request: Duration, |
316 | 0 | max_parallel: NonZeroU32, |
317 | 0 | ) -> Result<RuntimeCallSuccess, RuntimeCallError> { Unexecuted instantiation: _RNvMNtCsiGub1lfKphe_13smoldot_light15runtime_serviceINtB2_14RuntimeServicepE12runtime_callB4_ Unexecuted instantiation: _RNvMNtCsih6EgvAwZF2_13smoldot_light15runtime_serviceINtB2_14RuntimeServiceNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE12runtime_callB1e_ Unexecuted instantiation: _RNvMNtCsih6EgvAwZF2_13smoldot_light15runtime_serviceINtB2_14RuntimeServicepE12runtime_callB4_ |
318 | 0 | let (result_tx, result_rx) = oneshot::channel(); |
319 | 0 |
|
320 | 0 | self.send_message_or_restart_service(ToBackground::RuntimeCall { |
321 | 0 | result_tx, |
322 | 0 | pinned_runtime: pinned_runtime.0, |
323 | 0 | block_hash, |
324 | 0 | block_number, |
325 | 0 | block_state_trie_root_hash, |
326 | 0 | function_name, |
327 | 0 | required_api_version, |
328 | 0 | parameters_vectored, |
329 | 0 | total_attempts, |
330 | 0 | timeout_per_request, |
331 | 0 | _max_parallel: max_parallel, |
332 | 0 | }) |
333 | 0 | .await; |
334 | | |
335 | 0 | match result_rx.await { |
336 | 0 | Ok(result) => result, |
337 | | Err(_) => { |
338 | | // Background service has crashed. |
339 | 0 | Err(RuntimeCallError::Crash) |
340 | | } |
341 | | } |
342 | 0 | } Unexecuted instantiation: _RNCNvMNtCsiGub1lfKphe_13smoldot_light15runtime_serviceINtB4_14RuntimeServicepE12runtime_call0B6_ Unexecuted instantiation: _RNCNvMNtCsih6EgvAwZF2_13smoldot_light15runtime_serviceINtB4_14RuntimeServiceNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE12runtime_call0B1g_ Unexecuted instantiation: _RNCNvMNtCsih6EgvAwZF2_13smoldot_light15runtime_serviceINtB4_14RuntimeServicepE12runtime_call0B6_ |
343 | | |
344 | | /// Tries to find a runtime within the [`RuntimeService`] that has the given storage code and |
345 | | /// heap pages. If none is found, compiles the runtime and stores it within the |
346 | | /// [`RuntimeService`]. |
347 | 0 | pub async fn compile_and_pin_runtime( |
348 | 0 | &self, |
349 | 0 | storage_code: Option<Vec<u8>>, |
350 | 0 | storage_heap_pages: Option<Vec<u8>>, |
351 | 0 | code_merkle_value: Option<Vec<u8>>, |
352 | 0 | closest_ancestor_excluding: Option<Vec<Nibble>>, |
353 | 0 | ) -> Result<PinnedRuntime, CompileAndPinRuntimeError> { Unexecuted instantiation: _RNvMNtCsiGub1lfKphe_13smoldot_light15runtime_serviceINtB2_14RuntimeServicepE23compile_and_pin_runtimeB4_ Unexecuted instantiation: _RNvMNtCsih6EgvAwZF2_13smoldot_light15runtime_serviceINtB2_14RuntimeServiceNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE23compile_and_pin_runtimeB1e_ Unexecuted instantiation: _RNvMNtCsih6EgvAwZF2_13smoldot_light15runtime_serviceINtB2_14RuntimeServicepE23compile_and_pin_runtimeB4_ |
354 | 0 | let (result_tx, result_rx) = oneshot::channel(); |
355 | 0 |
|
356 | 0 | let _ = self |
357 | 0 | .send_message_or_restart_service(ToBackground::CompileAndPinRuntime { |
358 | 0 | result_tx, |
359 | 0 | storage_code, |
360 | 0 | storage_heap_pages, |
361 | 0 | code_merkle_value, |
362 | 0 | closest_ancestor_excluding, |
363 | 0 | }) |
364 | 0 | .await; |
365 | | |
366 | | Ok(PinnedRuntime( |
367 | 0 | result_rx |
368 | 0 | .await |
369 | 0 | .map_err(|_| CompileAndPinRuntimeError::Crash)?, Unexecuted instantiation: _RNCNCNvMNtCsiGub1lfKphe_13smoldot_light15runtime_serviceINtB6_14RuntimeServicepE23compile_and_pin_runtime00B8_ Unexecuted instantiation: _RNCNCNvMNtCsih6EgvAwZF2_13smoldot_light15runtime_serviceINtB6_14RuntimeServiceNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE23compile_and_pin_runtime00B1i_ Unexecuted instantiation: _RNCNCNvMNtCsih6EgvAwZF2_13smoldot_light15runtime_serviceINtB6_14RuntimeServicepE23compile_and_pin_runtime00B8_ |
370 | | )) |
371 | 0 | } Unexecuted instantiation: _RNCNvMNtCsiGub1lfKphe_13smoldot_light15runtime_serviceINtB4_14RuntimeServicepE23compile_and_pin_runtime0B6_ Unexecuted instantiation: _RNCNvMNtCsih6EgvAwZF2_13smoldot_light15runtime_serviceINtB4_14RuntimeServiceNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE23compile_and_pin_runtime0B1g_ Unexecuted instantiation: _RNCNvMNtCsih6EgvAwZF2_13smoldot_light15runtime_serviceINtB4_14RuntimeServicepE23compile_and_pin_runtime0B6_ |
372 | | |
373 | | /// Returns the runtime specification of the given runtime. |
374 | 0 | pub async fn pinned_runtime_specification( |
375 | 0 | &self, |
376 | 0 | pinned_runtime: PinnedRuntime, |
377 | 0 | ) -> Result<executor::CoreVersion, PinnedRuntimeSpecificationError> { Unexecuted instantiation: _RNvMNtCsiGub1lfKphe_13smoldot_light15runtime_serviceINtB2_14RuntimeServicepE28pinned_runtime_specificationB4_ Unexecuted instantiation: _RNvMNtCsih6EgvAwZF2_13smoldot_light15runtime_serviceINtB2_14RuntimeServiceNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE28pinned_runtime_specificationB1e_ Unexecuted instantiation: _RNvMNtCsih6EgvAwZF2_13smoldot_light15runtime_serviceINtB2_14RuntimeServicepE28pinned_runtime_specificationB4_ |
378 | 0 | match &pinned_runtime.0.runtime { |
379 | 0 | Ok(rt) => Ok(rt.runtime_version().clone()), |
380 | 0 | Err(error) => Err(PinnedRuntimeSpecificationError::InvalidRuntime( |
381 | 0 | error.clone(), |
382 | 0 | )), |
383 | | } |
384 | 0 | } Unexecuted instantiation: _RNCNvMNtCsiGub1lfKphe_13smoldot_light15runtime_serviceINtB4_14RuntimeServicepE28pinned_runtime_specification0B6_ Unexecuted instantiation: _RNCNvMNtCsih6EgvAwZF2_13smoldot_light15runtime_serviceINtB4_14RuntimeServiceNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE28pinned_runtime_specification0B1g_ Unexecuted instantiation: _RNCNvMNtCsih6EgvAwZF2_13smoldot_light15runtime_serviceINtB4_14RuntimeServicepE28pinned_runtime_specification0B6_ |
385 | | |
386 | | /// Returns true if it is believed that we are near the head of the chain. |
387 | | /// |
388 | | /// The way this method is implemented is opaque and cannot be relied on. The return value |
389 | | /// should only ever be shown to the user and not used for any meaningful logic. |
390 | 0 | pub async fn is_near_head_of_chain_heuristic(&self) -> bool { Unexecuted instantiation: _RNvMNtCsiGub1lfKphe_13smoldot_light15runtime_serviceINtB2_14RuntimeServicepE31is_near_head_of_chain_heuristicB4_ Unexecuted instantiation: _RNvMNtCsih6EgvAwZF2_13smoldot_light15runtime_serviceINtB2_14RuntimeServiceNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE31is_near_head_of_chain_heuristicB1e_ Unexecuted instantiation: _RNvMNtCsih6EgvAwZF2_13smoldot_light15runtime_serviceINtB2_14RuntimeServicepE31is_near_head_of_chain_heuristicB4_ |
391 | 0 | let (result_tx, result_rx) = oneshot::channel(); |
392 | 0 | let _ = self |
393 | 0 | .to_background |
394 | 0 | .lock() |
395 | 0 | .await |
396 | 0 | .send(ToBackground::IsNearHeadOfChainHeuristic { result_tx }) |
397 | 0 | .await; |
398 | 0 | result_rx.await.unwrap_or(false) |
399 | 0 | } Unexecuted instantiation: _RNCNvMNtCsiGub1lfKphe_13smoldot_light15runtime_serviceINtB4_14RuntimeServicepE31is_near_head_of_chain_heuristic0B6_ Unexecuted instantiation: _RNCNvMNtCsih6EgvAwZF2_13smoldot_light15runtime_serviceINtB4_14RuntimeServiceNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE31is_near_head_of_chain_heuristic0B1g_ Unexecuted instantiation: _RNCNvMNtCsih6EgvAwZF2_13smoldot_light15runtime_serviceINtB4_14RuntimeServicepE31is_near_head_of_chain_heuristic0B6_ |
400 | | |
401 | | /// Sends a message to the background task. Restarts the background task if it has crashed. |
402 | 0 | async fn send_message_or_restart_service(&self, message: ToBackground<TPlat>) { Unexecuted instantiation: _RNvMNtCsiGub1lfKphe_13smoldot_light15runtime_serviceINtB2_14RuntimeServicepE31send_message_or_restart_serviceB4_ Unexecuted instantiation: _RNvMNtCsih6EgvAwZF2_13smoldot_light15runtime_serviceINtB2_14RuntimeServiceNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE31send_message_or_restart_serviceB1e_ Unexecuted instantiation: _RNvMNtCsih6EgvAwZF2_13smoldot_light15runtime_serviceINtB2_14RuntimeServicepE31send_message_or_restart_serviceB4_ |
403 | 0 | let mut lock = self.to_background.lock().await; |
404 | | |
405 | 0 | if lock.is_closed() { |
406 | 0 | let (tx, rx) = async_channel::bounded(16); |
407 | 0 | let tx_weak = tx.downgrade(); |
408 | 0 | *lock = tx; |
409 | 0 |
|
410 | 0 | self.background_task_config.platform.spawn_task( |
411 | 0 | self.background_task_config.log_target.clone().into(), |
412 | 0 | { |
413 | 0 | let background_task_config = self.background_task_config.clone(); |
414 | 0 | let platform = background_task_config.platform.clone(); |
415 | 0 | async move { |
416 | 0 | // Sleep for a bit in order to avoid infinite loops of repeated crashes. |
417 | 0 | background_task_config |
418 | 0 | .platform |
419 | 0 | .sleep(Duration::from_secs(2)) |
420 | 0 | .await; |
421 | 0 | let log_target = background_task_config.log_target.clone(); |
422 | 0 | log!(&platform, Debug, &log_target, "restart"); |
423 | 0 | run_background(background_task_config, rx, tx_weak).await; |
424 | 0 | log!(&platform, Debug, &log_target, "shutdown"); |
425 | 0 | } Unexecuted instantiation: _RNCNCNvMNtCsiGub1lfKphe_13smoldot_light15runtime_serviceINtB6_14RuntimeServicepE31send_message_or_restart_service00B8_ Unexecuted instantiation: _RNCNCNvMNtCsih6EgvAwZF2_13smoldot_light15runtime_serviceINtB6_14RuntimeServiceNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE31send_message_or_restart_service00B1i_ Unexecuted instantiation: _RNCNCNvMNtCsih6EgvAwZF2_13smoldot_light15runtime_serviceINtB6_14RuntimeServicepE31send_message_or_restart_service00B8_ |
426 | 0 | }, |
427 | 0 | ); |
428 | 0 | } |
429 | | |
430 | | // Note that the background task might have crashed again at this point already, and thus |
431 | | // errors are not impossible. |
432 | 0 | let _ = lock.send(message).await; |
433 | 0 | } Unexecuted instantiation: _RNCNvMNtCsiGub1lfKphe_13smoldot_light15runtime_serviceINtB4_14RuntimeServicepE31send_message_or_restart_service0B6_ Unexecuted instantiation: _RNCNvMNtCsih6EgvAwZF2_13smoldot_light15runtime_serviceINtB4_14RuntimeServiceNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE31send_message_or_restart_service0B1g_ Unexecuted instantiation: _RNCNvMNtCsih6EgvAwZF2_13smoldot_light15runtime_serviceINtB4_14RuntimeServicepE31send_message_or_restart_service0B6_ |
434 | | } |
435 | | |
436 | | /// Return value of [`RuntimeService::subscribe_all`]. |
437 | | pub struct SubscribeAll<TPlat: PlatformRef> { |
438 | | /// SCALE-encoded header of the finalized block at the time of the subscription. |
439 | | pub finalized_block_scale_encoded_header: Vec<u8>, |
440 | | |
441 | | /// If the runtime of the finalized block is known, contains the information about it. |
442 | | pub finalized_block_runtime: Result<executor::CoreVersion, RuntimeError>, |
443 | | |
444 | | /// List of all known non-finalized blocks at the time of subscription. |
445 | | /// |
446 | | /// Only one element in this list has [`BlockNotification::is_new_best`] equal to true. |
447 | | /// |
448 | | /// The blocks are guaranteed to be ordered so that parents are always found before their |
449 | | /// children. |
450 | | pub non_finalized_blocks_ancestry_order: Vec<BlockNotification>, |
451 | | |
452 | | /// Channel onto which new blocks are sent. The channel gets closed if it is full when a new |
453 | | /// block needs to be reported. |
454 | | pub new_blocks: Subscription<TPlat>, |
455 | | } |
456 | | |
457 | | #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] |
458 | | pub struct SubscriptionId(u64); |
459 | | |
460 | | pub struct Subscription<TPlat: PlatformRef> { |
461 | | subscription_id: u64, |
462 | | channel: Pin<Box<async_channel::Receiver<Notification>>>, |
463 | | to_background: async_channel::Sender<ToBackground<TPlat>>, |
464 | | } |
465 | | |
466 | | impl<TPlat: PlatformRef> Subscription<TPlat> { |
467 | 0 | pub async fn next(&mut self) -> Option<Notification> { Unexecuted instantiation: _RNvMs_NtCsiGub1lfKphe_13smoldot_light15runtime_serviceINtB4_12SubscriptionpE4nextB6_ Unexecuted instantiation: _RNvMs_NtCsih6EgvAwZF2_13smoldot_light15runtime_serviceINtB4_12SubscriptionNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE4nextB1e_ Unexecuted instantiation: _RNvMs_NtCsih6EgvAwZF2_13smoldot_light15runtime_serviceINtB4_12SubscriptionpE4nextB6_ |
468 | 0 | self.channel.next().await |
469 | 0 | } Unexecuted instantiation: _RNCNvMs_NtCsiGub1lfKphe_13smoldot_light15runtime_serviceINtB6_12SubscriptionpE4next0B8_ Unexecuted instantiation: _RNCNvMs_NtCsih6EgvAwZF2_13smoldot_light15runtime_serviceINtB6_12SubscriptionNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE4next0B1g_ Unexecuted instantiation: _RNCNvMs_NtCsih6EgvAwZF2_13smoldot_light15runtime_serviceINtB6_12SubscriptionpE4next0B8_ |
470 | | |
471 | | /// Returns an opaque identifier that can be used to call [`RuntimeService::unpin_block`]. |
472 | 0 | pub fn id(&self) -> SubscriptionId { |
473 | 0 | SubscriptionId(self.subscription_id) |
474 | 0 | } Unexecuted instantiation: _RNvMs_NtCsiGub1lfKphe_13smoldot_light15runtime_serviceINtB4_12SubscriptionpE2idB6_ Unexecuted instantiation: _RNvMs_NtCsih6EgvAwZF2_13smoldot_light15runtime_serviceINtB4_12SubscriptionNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE2idB1e_ Unexecuted instantiation: _RNvMs_NtCsih6EgvAwZF2_13smoldot_light15runtime_serviceINtB4_12SubscriptionpE2idB6_ |
475 | | |
476 | | /// Unpins a block after it has been reported. |
477 | | /// |
478 | | /// # Panic |
479 | | /// |
480 | | /// Panics if the block hash has not been reported or has already been unpinned. |
481 | | /// |
482 | 0 | pub async fn unpin_block(&self, block_hash: [u8; 32]) { Unexecuted instantiation: _RNvMs_NtCsiGub1lfKphe_13smoldot_light15runtime_serviceINtB4_12SubscriptionpE11unpin_blockB6_ Unexecuted instantiation: _RNvMs_NtCsih6EgvAwZF2_13smoldot_light15runtime_serviceINtB4_12SubscriptionNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE11unpin_blockB1e_ Unexecuted instantiation: _RNvMs_NtCsih6EgvAwZF2_13smoldot_light15runtime_serviceINtB4_12SubscriptionpE11unpin_blockB6_ |
483 | 0 | let (result_tx, result_rx) = oneshot::channel(); |
484 | 0 | let _ = self |
485 | 0 | .to_background |
486 | 0 | .send(ToBackground::UnpinBlock { |
487 | 0 | result_tx, |
488 | 0 | subscription_id: SubscriptionId(self.subscription_id), |
489 | 0 | block_hash, |
490 | 0 | }) |
491 | 0 | .await; |
492 | 0 | result_rx.await.unwrap().unwrap() |
493 | 0 | } Unexecuted instantiation: _RNCNvMs_NtCsiGub1lfKphe_13smoldot_light15runtime_serviceINtB6_12SubscriptionpE11unpin_block0B8_ Unexecuted instantiation: _RNCNvMs_NtCsih6EgvAwZF2_13smoldot_light15runtime_serviceINtB6_12SubscriptionNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE11unpin_block0B1g_ Unexecuted instantiation: _RNCNvMs_NtCsih6EgvAwZF2_13smoldot_light15runtime_serviceINtB6_12SubscriptionpE11unpin_block0B8_ |
494 | | } |
495 | | |
496 | | /// Notification about a new block or a new finalized block. |
497 | | /// |
498 | | /// See [`RuntimeService::subscribe_all`]. |
499 | | #[derive(Debug, Clone)] |
500 | | pub enum Notification { |
501 | | /// A non-finalized block has been finalized. |
502 | | Finalized { |
503 | | /// BLAKE2 hash of the header of the block that has been finalized. |
504 | | /// |
505 | | /// A block with this hash is guaranteed to have earlier been reported in a |
506 | | /// [`BlockNotification`], either in [`SubscribeAll::non_finalized_blocks_ancestry_order`] |
507 | | /// or in a [`Notification::Block`]. |
508 | | /// |
509 | | /// It is also guaranteed that this block is a child of the previously-finalized block. In |
510 | | /// other words, if multiple blocks are finalized at the same time, only one |
511 | | /// [`Notification::Finalized`] is generated and contains the highest finalized block. |
512 | | /// |
513 | | /// If it is not possible for the [`RuntimeService`] to avoid a gap in the list of |
514 | | /// finalized blocks, then the [`SubscribeAll::new_blocks`] channel is force-closed. |
515 | | hash: [u8; 32], |
516 | | |
517 | | /// If the current best block is pruned by the finalization, contains the updated hash |
518 | | /// of the best block after the finalization. |
519 | | /// |
520 | | /// If the newly-finalized block is an ancestor of the current best block, then this field |
521 | | /// contains the hash of this current best block. Otherwise, the best block is now |
522 | | /// the non-finalized block with the given hash. |
523 | | /// |
524 | | /// A block with this hash is guaranteed to have earlier been reported in a |
525 | | /// [`BlockNotification`], either in [`SubscribeAll::non_finalized_blocks_ancestry_order`] |
526 | | /// or in a [`Notification::Block`]. |
527 | | best_block_hash_if_changed: Option<[u8; 32]>, |
528 | | |
529 | | /// List of BLAKE2 hashes of the headers of the blocks that have been discarded because |
530 | | /// they're not descendants of the newly-finalized block. |
531 | | /// |
532 | | /// This list contains all the siblings of the newly-finalized block and all their |
533 | | /// descendants. |
534 | | pruned_blocks: Vec<[u8; 32]>, |
535 | | }, |
536 | | |
537 | | /// A new block has been added to the list of unfinalized blocks. |
538 | | Block(BlockNotification), |
539 | | |
540 | | /// The best block has changed to a different one. |
541 | | BestBlockChanged { |
542 | | /// Hash of the new best block. |
543 | | /// |
544 | | /// This can be either the hash of the latest finalized block or the hash of a |
545 | | /// non-finalized block. |
546 | | hash: [u8; 32], |
547 | | }, |
548 | | } |
549 | | |
550 | | /// Notification about a new block. |
551 | | /// |
552 | | /// See [`RuntimeService::subscribe_all`]. |
553 | | #[derive(Debug, Clone)] |
554 | | pub struct BlockNotification { |
555 | | /// True if this block is considered as the best block of the chain. |
556 | | pub is_new_best: bool, |
557 | | |
558 | | /// SCALE-encoded header of the block. |
559 | | pub scale_encoded_header: Vec<u8>, |
560 | | |
561 | | /// BLAKE2 hash of the header of the parent of this block. |
562 | | /// |
563 | | /// |
564 | | /// A block with this hash is guaranteed to have earlier been reported in a |
565 | | /// [`BlockNotification`], either in [`SubscribeAll::non_finalized_blocks_ancestry_order`] or |
566 | | /// in a [`Notification::Block`]. |
567 | | /// |
568 | | /// > **Note**: The header of a block contains the hash of its parent. When it comes to |
569 | | /// > consensus algorithms such as Babe or Aura, the syncing code verifies that this |
570 | | /// > hash, stored in the header, actually corresponds to a valid block. However, |
571 | | /// > when it comes to parachain consensus, no such verification is performed. |
572 | | /// > Contrary to the hash stored in the header, the value of this field is |
573 | | /// > guaranteed to refer to a block that is known by the syncing service. This |
574 | | /// > allows a subscriber of the state of the chain to precisely track the hierarchy |
575 | | /// > of blocks, without risking to run into a problem in case of a block with an |
576 | | /// > invalid header. |
577 | | pub parent_hash: [u8; 32], |
578 | | |
579 | | /// If the runtime of the block is different from its parent, contains the information about |
580 | | /// the new runtime. |
581 | | pub new_runtime: Option<Result<executor::CoreVersion, RuntimeError>>, |
582 | | } |
583 | | |
584 | | /// Successful runtime call. |
585 | | #[derive(Debug)] |
586 | | pub struct RuntimeCallSuccess { |
587 | | /// Output of the runtime call. |
588 | | pub output: Vec<u8>, |
589 | | |
590 | | /// Version of the API that was found. `Some` if and only if an API requirement was passed. |
591 | | pub api_version: Option<u32>, |
592 | | } |
593 | | |
594 | | /// See [`RuntimeService::pin_pinned_block_runtime`]. |
595 | 0 | #[derive(Debug, derive_more::Display, Clone)] Unexecuted instantiation: _RNvXsh_NtCsiGub1lfKphe_13smoldot_light15runtime_serviceNtB5_26PinPinnedBlockRuntimeErrorNtNtCsaYZPK01V26L_4core3fmt7Display3fmt Unexecuted instantiation: _RNvXsh_NtCsih6EgvAwZF2_13smoldot_light15runtime_serviceNtB5_26PinPinnedBlockRuntimeErrorNtNtCsaYZPK01V26L_4core3fmt7Display3fmt |
596 | | pub enum PinPinnedBlockRuntimeError { |
597 | | /// Subscription is dead. |
598 | | ObsoleteSubscription, |
599 | | |
600 | | /// Requested block isn't pinned by the subscription. |
601 | | BlockNotPinned, |
602 | | } |
603 | | |
604 | | /// See [`RuntimeService::pinned_runtime_specification`]. |
605 | 0 | #[derive(Debug, derive_more::Display, Clone)] Unexecuted instantiation: _RNvXsk_NtCsiGub1lfKphe_13smoldot_light15runtime_serviceNtB5_31PinnedRuntimeSpecificationErrorNtNtCsaYZPK01V26L_4core3fmt7Display3fmt Unexecuted instantiation: _RNvXsk_NtCsih6EgvAwZF2_13smoldot_light15runtime_serviceNtB5_31PinnedRuntimeSpecificationErrorNtNtCsaYZPK01V26L_4core3fmt7Display3fmt |
606 | | pub enum PinnedRuntimeSpecificationError { |
607 | | /// The runtime is invalid. |
608 | | InvalidRuntime(RuntimeError), |
609 | | } |
610 | | |
611 | | /// See [`RuntimeService::runtime_call`]. |
612 | 0 | #[derive(Debug, derive_more::Display, Clone)] Unexecuted instantiation: _RNvXsn_NtCsiGub1lfKphe_13smoldot_light15runtime_serviceNtB5_16RuntimeCallErrorNtNtCsaYZPK01V26L_4core3fmt7Display3fmt Unexecuted instantiation: _RNvXsn_NtCsih6EgvAwZF2_13smoldot_light15runtime_serviceNtB5_16RuntimeCallErrorNtNtCsaYZPK01V26L_4core3fmt7Display3fmt |
613 | | pub enum RuntimeCallError { |
614 | | /// The runtime of the requested block is invalid. |
615 | | InvalidRuntime(RuntimeError), |
616 | | |
617 | | /// API version required for the call isn't fulfilled. |
618 | | ApiVersionRequirementUnfulfilled, |
619 | | |
620 | | /// Runtime service has crashed while the call was in progress. |
621 | | /// |
622 | | /// This doesn't necessarily indicate that the call was responsible for this crash. |
623 | | Crash, |
624 | | |
625 | | /// Error during the execution of the runtime. |
626 | | /// |
627 | | /// There is no point in trying the same call again, as it would result in the same error. |
628 | | #[display(fmt = "Error during the execution of the runtime: {_0}")] |
629 | | Execution(RuntimeCallExecutionError), |
630 | | |
631 | | /// Error trying to access the storage required for the runtime call. |
632 | | /// |
633 | | /// Because these errors are non-fatal, the operation is attempted multiple times, and as such |
634 | | /// there can be multiple errors. |
635 | | /// |
636 | | /// Trying the same call again might succeed. |
637 | | #[display(fmt = "Error trying to access the storage required for the runtime call")] |
638 | | // TODO: better display? |
639 | | Inaccessible(Vec<RuntimeCallInaccessibleError>), |
640 | | } |
641 | | |
642 | | /// See [`RuntimeCallError::Execution`]. |
643 | 0 | #[derive(Debug, derive_more::Display, Clone)] Unexecuted instantiation: _RNvXsq_NtCsiGub1lfKphe_13smoldot_light15runtime_serviceNtB5_25RuntimeCallExecutionErrorNtNtCsaYZPK01V26L_4core3fmt7Display3fmt Unexecuted instantiation: _RNvXsq_NtCsih6EgvAwZF2_13smoldot_light15runtime_serviceNtB5_25RuntimeCallExecutionErrorNtNtCsaYZPK01V26L_4core3fmt7Display3fmt |
644 | | pub enum RuntimeCallExecutionError { |
645 | | /// Failed to initialize the virtual machine. |
646 | | Start(executor::host::StartErr), |
647 | | /// Error during the execution of the virtual machine. |
648 | | Execution(executor::runtime_call::ErrorDetail), |
649 | | /// Virtual machine has called a host function that it is not allowed to call. |
650 | | ForbiddenHostFunction, |
651 | | } |
652 | | |
653 | | /// See [`RuntimeCallError::Inaccessible`]. |
654 | 0 | #[derive(Debug, derive_more::Display, Clone)] Unexecuted instantiation: _RNvXst_NtCsiGub1lfKphe_13smoldot_light15runtime_serviceNtB5_28RuntimeCallInaccessibleErrorNtNtCsaYZPK01V26L_4core3fmt7Display3fmt Unexecuted instantiation: _RNvXst_NtCsih6EgvAwZF2_13smoldot_light15runtime_serviceNtB5_28RuntimeCallInaccessibleErrorNtNtCsaYZPK01V26L_4core3fmt7Display3fmt |
655 | | pub enum RuntimeCallInaccessibleError { |
656 | | /// Failed to download the call proof from the network. |
657 | | Network(network_service::CallProofRequestError), |
658 | | /// Call proof downloaded from the network has an invalid format. |
659 | | InvalidCallProof(proof_decode::Error), |
660 | | /// One or more entries are missing from the downloaded call proof. |
661 | | MissingProofEntry, |
662 | | } |
663 | | |
664 | | /// Error when analyzing the runtime. |
665 | 0 | #[derive(Debug, derive_more::Display, Clone)] Unexecuted instantiation: _RNvXsw_NtCsiGub1lfKphe_13smoldot_light15runtime_serviceNtB5_12RuntimeErrorNtNtCsaYZPK01V26L_4core3fmt7Display3fmt Unexecuted instantiation: _RNvXsw_NtCsih6EgvAwZF2_13smoldot_light15runtime_serviceNtB5_12RuntimeErrorNtNtCsaYZPK01V26L_4core3fmt7Display3fmt |
666 | | pub enum RuntimeError { |
667 | | /// The `:code` key of the storage is empty. |
668 | | CodeNotFound, |
669 | | /// Error while parsing the `:heappages` storage value. |
670 | | #[display(fmt = "Failed to parse `:heappages` storage value: {_0}")] |
671 | | InvalidHeapPages(executor::InvalidHeapPagesError), |
672 | | /// Error while compiling the runtime. |
673 | | #[display(fmt = "{_0}")] |
674 | | Build(executor::host::NewErr), |
675 | | } |
676 | | |
677 | | /// Error potentially returned by [`RuntimeService::compile_and_pin_runtime`]. |
678 | 0 | #[derive(Debug, derive_more::Display, Clone)] Unexecuted instantiation: _RNvXsz_NtCsiGub1lfKphe_13smoldot_light15runtime_serviceNtB5_25CompileAndPinRuntimeErrorNtNtCsaYZPK01V26L_4core3fmt7Display3fmt Unexecuted instantiation: _RNvXsz_NtCsih6EgvAwZF2_13smoldot_light15runtime_serviceNtB5_25CompileAndPinRuntimeErrorNtNtCsaYZPK01V26L_4core3fmt7Display3fmt |
679 | | pub enum CompileAndPinRuntimeError { |
680 | | /// Background service has crashed while compiling this runtime. The crash might however not |
681 | | /// necessarily be caused by the runtime compilation. |
682 | | Crash, |
683 | | } |
684 | | |
685 | | /// Message towards the background task. |
686 | | enum ToBackground<TPlat: PlatformRef> { |
687 | | SubscribeAll(ToBackgroundSubscribeAll<TPlat>), |
688 | | CompileAndPinRuntime { |
689 | | result_tx: oneshot::Sender<Arc<Runtime>>, |
690 | | storage_code: Option<Vec<u8>>, |
691 | | storage_heap_pages: Option<Vec<u8>>, |
692 | | code_merkle_value: Option<Vec<u8>>, |
693 | | closest_ancestor_excluding: Option<Vec<Nibble>>, |
694 | | }, |
695 | | FinalizedRuntimeStorageMerkleValues { |
696 | | // TODO: overcomplicated |
697 | | result_tx: oneshot::Sender<Option<(Option<Vec<u8>>, Option<Vec<u8>>, Option<Vec<Nibble>>)>>, |
698 | | }, |
699 | | IsNearHeadOfChainHeuristic { |
700 | | result_tx: oneshot::Sender<bool>, |
701 | | }, |
702 | | UnpinBlock { |
703 | | result_tx: oneshot::Sender<Result<(), ()>>, |
704 | | subscription_id: SubscriptionId, |
705 | | block_hash: [u8; 32], |
706 | | }, |
707 | | PinPinnedBlockRuntime { |
708 | | result_tx: |
709 | | oneshot::Sender<Result<(Arc<Runtime>, [u8; 32], u64), PinPinnedBlockRuntimeError>>, |
710 | | subscription_id: SubscriptionId, |
711 | | block_hash: [u8; 32], |
712 | | }, |
713 | | RuntimeCall { |
714 | | result_tx: oneshot::Sender<Result<RuntimeCallSuccess, RuntimeCallError>>, |
715 | | pinned_runtime: Arc<Runtime>, |
716 | | block_hash: [u8; 32], |
717 | | block_number: u64, |
718 | | block_state_trie_root_hash: [u8; 32], |
719 | | function_name: String, |
720 | | required_api_version: Option<(String, ops::RangeInclusive<u32>)>, |
721 | | parameters_vectored: Vec<u8>, |
722 | | total_attempts: u32, |
723 | | timeout_per_request: Duration, |
724 | | _max_parallel: NonZeroU32, |
725 | | }, |
726 | | } |
727 | | |
728 | | struct ToBackgroundSubscribeAll<TPlat: PlatformRef> { |
729 | | result_tx: oneshot::Sender<SubscribeAll<TPlat>>, |
730 | | buffer_size: usize, |
731 | | max_pinned_blocks: NonZeroUsize, |
732 | | } |
733 | | |
734 | | #[derive(Clone)] |
735 | | struct PinnedBlock { |
736 | | /// Reference-counted runtime of the pinned block. |
737 | | runtime: Arc<Runtime>, |
738 | | |
739 | | /// Hash of the trie root of the pinned block. |
740 | | state_trie_root_hash: [u8; 32], |
741 | | |
742 | | /// Height of the pinned block. |
743 | | block_number: u64, |
744 | | |
745 | | /// `true` if the block is non-finalized and part of the canonical chain. |
746 | | /// If `true`, then the block doesn't count towards the maximum number of pinned blocks of |
747 | | /// the subscription. |
748 | | block_ignores_limit: bool, |
749 | | } |
750 | | |
751 | | #[derive(Clone)] |
752 | | struct Block { |
753 | | /// Hash of the block in question. Redundant with `header`, but the hash is so often needed |
754 | | /// that it makes sense to cache it. |
755 | | hash: [u8; 32], |
756 | | |
757 | | /// Height of the block. |
758 | | height: u64, |
759 | | |
760 | | /// Header of the block in question. |
761 | | /// Guaranteed to always be valid for the output best and finalized blocks. Otherwise, |
762 | | /// not guaranteed to be valid. |
763 | | scale_encoded_header: Vec<u8>, |
764 | | } |
765 | | |
766 | | #[derive(Clone)] |
767 | | struct BackgroundTaskConfig<TPlat: PlatformRef> { |
768 | | log_target: String, |
769 | | platform: TPlat, |
770 | | sync_service: Arc<sync_service::SyncService<TPlat>>, |
771 | | network_service: Arc<network_service::NetworkServiceChain<TPlat>>, |
772 | | genesis_block_scale_encoded_header: Vec<u8>, |
773 | | } |
774 | | |
775 | 0 | async fn run_background<TPlat: PlatformRef>( |
776 | 0 | config: BackgroundTaskConfig<TPlat>, |
777 | 0 | to_background: async_channel::Receiver<ToBackground<TPlat>>, |
778 | 0 | to_background_tx: async_channel::WeakSender<ToBackground<TPlat>>, |
779 | 0 | ) { Unexecuted instantiation: _RINvNtCsiGub1lfKphe_13smoldot_light15runtime_service14run_backgroundpEB4_ Unexecuted instantiation: _RINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefEB18_ Unexecuted instantiation: _RINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundpEB4_ |
780 | 0 | log!( |
781 | 0 | &config.platform, |
782 | 0 | Trace, |
783 | 0 | &config.log_target, |
784 | 0 | "start", |
785 | 0 | genesis_block_hash = HashDisplay(&header::hash_from_scale_encoded_header( |
786 | 0 | &config.genesis_block_scale_encoded_header |
787 | 0 | )) |
788 | 0 | ); |
789 | 0 |
|
790 | 0 | // State machine containing all the state that will be manipulated below. |
791 | 0 | let mut background = { |
792 | 0 | let tree = { |
793 | 0 | let mut tree = async_tree::AsyncTree::new(async_tree::Config { |
794 | 0 | finalized_async_user_data: None, |
795 | 0 | retry_after_failed: Duration::from_secs(10), |
796 | 0 | blocks_capacity: 32, |
797 | 0 | }); |
798 | 0 | let node_index = tree.input_insert_block( |
799 | 0 | Block { |
800 | 0 | hash: header::hash_from_scale_encoded_header( |
801 | 0 | &config.genesis_block_scale_encoded_header, |
802 | 0 | ), |
803 | 0 | height: 0, |
804 | 0 | scale_encoded_header: config.genesis_block_scale_encoded_header, |
805 | 0 | }, |
806 | 0 | None, |
807 | 0 | false, |
808 | 0 | true, |
809 | 0 | ); |
810 | 0 | tree.input_finalize(node_index); |
811 | 0 |
|
812 | 0 | Tree::FinalizedBlockRuntimeUnknown { tree } |
813 | 0 | }; |
814 | 0 |
|
815 | 0 | Background { |
816 | 0 | log_target: config.log_target.clone(), |
817 | 0 | platform: config.platform.clone(), |
818 | 0 | sync_service: config.sync_service.clone(), |
819 | 0 | network_service: config.network_service.clone(), |
820 | 0 | to_background: Box::pin(to_background.clone()), |
821 | 0 | to_background_tx: to_background_tx.clone(), |
822 | 0 | next_subscription_id: 0, |
823 | 0 | tree, |
824 | 0 | runtimes: slab::Slab::with_capacity(2), |
825 | 0 | pending_subscriptions: VecDeque::with_capacity(8), |
826 | 0 | blocks_stream: None, |
827 | 0 | runtime_downloads: stream::FuturesUnordered::new(), |
828 | 0 | progress_runtime_call_requests: stream::FuturesUnordered::new(), |
829 | 0 | } |
830 | | }; |
831 | | |
832 | | // Inner loop. Process incoming events. |
833 | | loop { |
834 | | // Yield at every loop in order to provide better tasks granularity. |
835 | 0 | futures_lite::future::yield_now().await; |
836 | | |
837 | | enum WakeUpReason<TPlat: PlatformRef> { |
838 | | MustSubscribe, |
839 | | StartDownload(async_tree::AsyncOpId, async_tree::NodeIndex), |
840 | | TreeAdvanceFinalizedKnown(async_tree::OutputUpdate<Block, Arc<Runtime>>), |
841 | | TreeAdvanceFinalizedUnknown(async_tree::OutputUpdate<Block, Option<Arc<Runtime>>>), |
842 | | StartPendingSubscribeAll(ToBackgroundSubscribeAll<TPlat>), |
843 | | Notification(sync_service::Notification), |
844 | | SyncServiceSubscriptionReset, |
845 | | ToBackground(ToBackground<TPlat>), |
846 | | ForegroundClosed, |
847 | | RuntimeDownloadFinished( |
848 | | async_tree::AsyncOpId, |
849 | | Result< |
850 | | ( |
851 | | Option<Vec<u8>>, |
852 | | Option<Vec<u8>>, |
853 | | Option<Vec<u8>>, |
854 | | Option<Vec<Nibble>>, |
855 | | ), |
856 | | RuntimeDownloadError, |
857 | | >, |
858 | | ), |
859 | | ProgressRuntimeCallRequest(ProgressRuntimeCallRequest), |
860 | | } |
861 | | |
862 | | // Wait for something to happen or for some processing to be necessary. |
863 | 0 | let wake_up_reason: WakeUpReason<_> = { |
864 | 0 | let finalized_block_known = |
865 | 0 | matches!(background.tree, Tree::FinalizedBlockRuntimeKnown { .. }); |
866 | 0 | let num_runtime_downloads = background.runtime_downloads.len(); |
867 | 0 | let any_subscription = match &background.tree { |
868 | | Tree::FinalizedBlockRuntimeKnown { |
869 | 0 | all_blocks_subscriptions, |
870 | 0 | .. |
871 | 0 | } => !all_blocks_subscriptions.is_empty(), |
872 | 0 | Tree::FinalizedBlockRuntimeUnknown { .. } => false, |
873 | | }; |
874 | 0 | let any_pending_subscription = !background.pending_subscriptions.is_empty(); |
875 | 0 | async { |
876 | 0 | if finalized_block_known { |
877 | 0 | if let Some(pending_subscription) = background.pending_subscriptions.pop_front() |
878 | | { |
879 | 0 | WakeUpReason::StartPendingSubscribeAll(pending_subscription) |
880 | | } else { |
881 | 0 | future::pending().await |
882 | | } |
883 | | } else { |
884 | 0 | future::pending().await |
885 | | } |
886 | 0 | } Unexecuted instantiation: _RNCNCINvNtCsiGub1lfKphe_13smoldot_light15runtime_service14run_backgroundpE00B8_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE00B1c_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundpE00B8_ |
887 | 0 | .or(async { |
888 | 0 | if let Some(blocks_stream) = background.blocks_stream.as_mut() { |
889 | 0 | if !any_subscription && !any_pending_subscription { |
890 | 0 | WakeUpReason::SyncServiceSubscriptionReset |
891 | | } else { |
892 | 0 | blocks_stream.next().await.map_or( |
893 | 0 | WakeUpReason::SyncServiceSubscriptionReset, |
894 | 0 | WakeUpReason::Notification, |
895 | 0 | ) |
896 | | } |
897 | 0 | } else if any_pending_subscription { |
898 | | // Only start subscribing to the sync service if there is any pending |
899 | | // runtime service subscription. |
900 | 0 | WakeUpReason::MustSubscribe |
901 | | } else { |
902 | 0 | future::pending().await |
903 | | } |
904 | 0 | }) Unexecuted instantiation: _RNCNCINvNtCsiGub1lfKphe_13smoldot_light15runtime_service14run_backgroundpE0s_0B8_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0s_0B1c_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundpE0s_0B8_ |
905 | 0 | .or(async { |
906 | 0 | background |
907 | 0 | .to_background |
908 | 0 | .next() |
909 | 0 | .await |
910 | 0 | .map_or(WakeUpReason::ForegroundClosed, WakeUpReason::ToBackground) |
911 | 0 | }) Unexecuted instantiation: _RNCNCINvNtCsiGub1lfKphe_13smoldot_light15runtime_service14run_backgroundpE0s0_0B8_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0s0_0B1c_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundpE0s0_0B8_ |
912 | 0 | .or(async { |
913 | 0 | if !background.runtime_downloads.is_empty() { |
914 | 0 | let (async_op_id, download_result) = |
915 | 0 | background.runtime_downloads.select_next_some().await; |
916 | 0 | WakeUpReason::RuntimeDownloadFinished(async_op_id, download_result) |
917 | | } else { |
918 | 0 | future::pending().await |
919 | | } |
920 | 0 | }) Unexecuted instantiation: _RNCNCINvNtCsiGub1lfKphe_13smoldot_light15runtime_service14run_backgroundpE0s1_0B8_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0s1_0B1c_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundpE0s1_0B8_ |
921 | 0 | .or(async { |
922 | 0 | if !background.progress_runtime_call_requests.is_empty() { |
923 | 0 | let result = background |
924 | 0 | .progress_runtime_call_requests |
925 | 0 | .select_next_some() |
926 | 0 | .await; |
927 | 0 | WakeUpReason::ProgressRuntimeCallRequest(result) |
928 | | } else { |
929 | 0 | future::pending().await |
930 | | } |
931 | 0 | }) Unexecuted instantiation: _RNCNCINvNtCsiGub1lfKphe_13smoldot_light15runtime_service14run_backgroundpE0s2_0B8_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0s2_0B1c_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundpE0s2_0B8_ |
932 | 0 | .or(async { |
933 | | loop { |
934 | | // There might be a new runtime download to start. |
935 | | // Don't download more than 2 runtimes at a time. |
936 | 0 | let wait = if num_runtime_downloads < 2 { |
937 | | // Grab what to download. If there's nothing more to download, do nothing. |
938 | 0 | let async_op = match &mut background.tree { |
939 | 0 | Tree::FinalizedBlockRuntimeKnown { tree, .. } => { |
940 | 0 | tree.next_necessary_async_op(&background.platform.now()) |
941 | | } |
942 | 0 | Tree::FinalizedBlockRuntimeUnknown { tree, .. } => { |
943 | 0 | tree.next_necessary_async_op(&background.platform.now()) |
944 | | } |
945 | | }; |
946 | | |
947 | 0 | match async_op { |
948 | 0 | async_tree::NextNecessaryAsyncOp::Ready(dl) => { |
949 | 0 | break WakeUpReason::StartDownload(dl.id, dl.block_index) |
950 | | } |
951 | 0 | async_tree::NextNecessaryAsyncOp::NotReady { when } => { |
952 | 0 | if let Some(when) = when { |
953 | 0 | either::Left(background.platform.sleep_until(when)) |
954 | | } else { |
955 | 0 | either::Right(future::pending()) |
956 | | } |
957 | | } |
958 | | } |
959 | | } else { |
960 | 0 | either::Right(future::pending()) |
961 | | }; |
962 | | |
963 | 0 | match &mut background.tree { |
964 | 0 | Tree::FinalizedBlockRuntimeKnown { tree, .. } => { |
965 | 0 | match tree.try_advance_output() { |
966 | 0 | Some(update) => { |
967 | 0 | break WakeUpReason::TreeAdvanceFinalizedKnown(update) |
968 | | } |
969 | 0 | None => wait.await, |
970 | | } |
971 | | } |
972 | 0 | Tree::FinalizedBlockRuntimeUnknown { tree, .. } => { |
973 | 0 | match tree.try_advance_output() { |
974 | 0 | Some(update) => { |
975 | 0 | break WakeUpReason::TreeAdvanceFinalizedUnknown(update) |
976 | | } |
977 | 0 | None => wait.await, |
978 | | } |
979 | | } |
980 | | } |
981 | | } |
982 | 0 | }) Unexecuted instantiation: _RNCNCINvNtCsiGub1lfKphe_13smoldot_light15runtime_service14run_backgroundpE0s3_0B8_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0s3_0B1c_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundpE0s3_0B8_ |
983 | 0 | .await |
984 | | }; |
985 | | |
986 | 0 | match wake_up_reason { |
987 | 0 | WakeUpReason::StartDownload(download_id, block_index) => { |
988 | 0 | let block = match &mut background.tree { |
989 | 0 | Tree::FinalizedBlockRuntimeKnown { tree, .. } => &tree[block_index], |
990 | 0 | Tree::FinalizedBlockRuntimeUnknown { tree, .. } => &tree[block_index], |
991 | | }; |
992 | | |
993 | 0 | log!( |
994 | 0 | &background.platform, |
995 | 0 | Debug, |
996 | 0 | &background.log_target, |
997 | 0 | "block-runtime-download-start", |
998 | 0 | block_hash = HashDisplay(&block.hash) |
999 | 0 | ); |
1000 | 0 |
|
1001 | 0 | // Dispatches a runtime download task to `runtime_downloads`. |
1002 | 0 | background.runtime_downloads.push(Box::pin({ |
1003 | 0 | let future = download_runtime( |
1004 | 0 | background.sync_service.clone(), |
1005 | 0 | block.hash, |
1006 | 0 | &block.scale_encoded_header, |
1007 | 0 | ); |
1008 | 0 |
|
1009 | 0 | async move { (download_id, future.await) } Unexecuted instantiation: _RNCNCINvNtCsiGub1lfKphe_13smoldot_light15runtime_service14run_backgroundpE0s4_0B8_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0s4_0B1c_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundpE0s4_0B8_ |
1010 | 0 | })); |
1011 | | } |
1012 | | |
1013 | | WakeUpReason::TreeAdvanceFinalizedKnown(async_tree::OutputUpdate::Finalized { |
1014 | 0 | user_data: new_finalized, |
1015 | 0 | best_output_block_updated, |
1016 | 0 | pruned_blocks, |
1017 | 0 | former_finalized_async_op_user_data: former_finalized_runtime, |
1018 | | .. |
1019 | | }) => { |
1020 | | let Tree::FinalizedBlockRuntimeKnown { |
1021 | 0 | tree, |
1022 | 0 | finalized_block, |
1023 | 0 | all_blocks_subscriptions, |
1024 | 0 | pinned_blocks, |
1025 | 0 | } = &mut background.tree |
1026 | | else { |
1027 | 0 | unreachable!() |
1028 | | }; |
1029 | | |
1030 | 0 | *finalized_block = new_finalized; |
1031 | 0 | let best_block_hash_if_changed = if best_output_block_updated { |
1032 | 0 | Some( |
1033 | 0 | tree.output_best_block_index() |
1034 | 0 | .map_or(finalized_block.hash, |(idx, _)| tree[idx].hash), Unexecuted instantiation: _RNCNCINvNtCsiGub1lfKphe_13smoldot_light15runtime_service14run_backgroundpE0s5_0B8_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0s5_0B1c_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundpE0s5_0B8_ |
1035 | 0 | ) |
1036 | | } else { |
1037 | 0 | None |
1038 | | }; |
1039 | | |
1040 | 0 | log!( |
1041 | 0 | &background.platform, |
1042 | 0 | Trace, |
1043 | 0 | &background.log_target, |
1044 | 0 | "output-chain-finalized", |
1045 | 0 | block_hash = HashDisplay(&finalized_block.hash), |
1046 | 0 | best_block_hash = if let Some(best_block_hash) = best_block_hash_if_changed { |
1047 | 0 | Cow::Owned(HashDisplay(&best_block_hash).to_string()) |
1048 | | } else { |
1049 | 0 | Cow::Borrowed("<unchanged>") |
1050 | | }, |
1051 | 0 | num_subscribers = all_blocks_subscriptions.len() |
1052 | 0 | ); |
1053 | 0 |
|
1054 | 0 | // The finalization might cause some runtimes in the list of runtimes |
1055 | 0 | // to have become unused. Clean them up. |
1056 | 0 | drop(former_finalized_runtime); |
1057 | 0 | background |
1058 | 0 | .runtimes |
1059 | 0 | .retain(|_, runtime| runtime.strong_count() > 0); Unexecuted instantiation: _RNCNCINvNtCsiGub1lfKphe_13smoldot_light15runtime_service14run_backgroundpE0s6_0B8_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0s6_0B1c_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundpE0s6_0B8_ |
1060 | 0 |
|
1061 | 0 | let all_blocks_notif = Notification::Finalized { |
1062 | 0 | best_block_hash_if_changed, |
1063 | 0 | hash: finalized_block.hash, |
1064 | 0 | pruned_blocks: pruned_blocks.iter().map(|(_, b, _)| b.hash).collect(), Unexecuted instantiation: _RNCNCINvNtCsiGub1lfKphe_13smoldot_light15runtime_service14run_backgroundpE0s7_0B8_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0s7_0B1c_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundpE0s7_0B8_ |
1065 | 0 | }; |
1066 | 0 |
|
1067 | 0 | let mut to_remove = Vec::new(); |
1068 | 0 | for (subscription_id, (sender, finalized_pinned_remaining)) in |
1069 | 0 | all_blocks_subscriptions.iter_mut() |
1070 | | { |
1071 | 0 | let count_limit = pruned_blocks.len() + 1; |
1072 | 0 |
|
1073 | 0 | if *finalized_pinned_remaining < count_limit { |
1074 | 0 | to_remove.push(*subscription_id); |
1075 | 0 | continue; |
1076 | 0 | } |
1077 | 0 |
|
1078 | 0 | if sender.try_send(all_blocks_notif.clone()).is_err() { |
1079 | 0 | to_remove.push(*subscription_id); |
1080 | 0 | continue; |
1081 | 0 | } |
1082 | 0 |
|
1083 | 0 | *finalized_pinned_remaining -= count_limit; |
1084 | | |
1085 | | // Mark the finalized and pruned blocks as finalized or non-canonical. |
1086 | 0 | for block in iter::once(&finalized_block.hash) |
1087 | 0 | .chain(pruned_blocks.iter().map(|(_, b, _)| &b.hash)) Unexecuted instantiation: _RNCNCINvNtCsiGub1lfKphe_13smoldot_light15runtime_service14run_backgroundpE0s8_0B8_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0s8_0B1c_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundpE0s8_0B8_ |
1088 | | { |
1089 | 0 | if let Some(pin) = pinned_blocks.get_mut(&(*subscription_id, *block)) { |
1090 | 0 | debug_assert!(pin.block_ignores_limit); |
1091 | 0 | pin.block_ignores_limit = false; |
1092 | 0 | } |
1093 | | } |
1094 | | } |
1095 | 0 | for to_remove in to_remove { |
1096 | 0 | all_blocks_subscriptions.remove(&to_remove); |
1097 | 0 | let pinned_blocks_to_remove = pinned_blocks |
1098 | 0 | .range((to_remove, [0; 32])..=(to_remove, [0xff; 32])) |
1099 | 0 | .map(|((_, h), _)| *h) Unexecuted instantiation: _RNCNCINvNtCsiGub1lfKphe_13smoldot_light15runtime_service14run_backgroundpE0s9_0B8_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0s9_0B1c_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundpE0s9_0B8_ |
1100 | 0 | .collect::<Vec<_>>(); |
1101 | 0 | for block in pinned_blocks_to_remove { |
1102 | 0 | pinned_blocks.remove(&(to_remove, block)); |
1103 | 0 | } |
1104 | | } |
1105 | | } |
1106 | | |
1107 | 0 | WakeUpReason::TreeAdvanceFinalizedKnown(async_tree::OutputUpdate::Block(block)) => { |
1108 | | let Tree::FinalizedBlockRuntimeKnown { |
1109 | 0 | tree, |
1110 | 0 | finalized_block, |
1111 | 0 | all_blocks_subscriptions, |
1112 | 0 | pinned_blocks, |
1113 | 0 | } = &mut background.tree |
1114 | | else { |
1115 | 0 | unreachable!() |
1116 | | }; |
1117 | | |
1118 | 0 | let block_index = block.index; |
1119 | 0 | let block_runtime = tree.block_async_user_data(block_index).unwrap().clone(); |
1120 | 0 | let block_hash = tree[block_index].hash; |
1121 | 0 | let scale_encoded_header = tree[block_index].scale_encoded_header.clone(); |
1122 | 0 | let is_new_best = block.is_new_best; |
1123 | 0 |
|
1124 | 0 | let (block_number, state_trie_root_hash) = { |
1125 | 0 | let decoded = header::decode( |
1126 | 0 | &scale_encoded_header, |
1127 | 0 | background.sync_service.block_number_bytes(), |
1128 | 0 | ) |
1129 | 0 | .unwrap(); |
1130 | 0 | (decoded.number, *decoded.state_root) |
1131 | 0 | }; |
1132 | 0 |
|
1133 | 0 | let parent_runtime = tree |
1134 | 0 | .parent(block_index) |
1135 | 0 | .map_or(tree.output_finalized_async_user_data().clone(), |idx| { |
1136 | 0 | tree.block_async_user_data(idx).unwrap().clone() |
1137 | 0 | }); Unexecuted instantiation: _RNCNCINvNtCsiGub1lfKphe_13smoldot_light15runtime_service14run_backgroundpE0sa_0B8_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sa_0B1c_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundpE0sa_0B8_ |
1138 | 0 |
|
1139 | 0 | log!( |
1140 | 0 | &background.platform, |
1141 | 0 | Trace, |
1142 | 0 | &background.log_target, |
1143 | 0 | "output-chain-new-block", |
1144 | 0 | block_hash = HashDisplay(&tree[block_index].hash), |
1145 | 0 | is_new_best, |
1146 | 0 | num_subscribers = all_blocks_subscriptions.len() |
1147 | 0 | ); |
1148 | | |
1149 | 0 | let notif = Notification::Block(BlockNotification { |
1150 | 0 | parent_hash: tree |
1151 | 0 | .parent(block_index) |
1152 | 0 | .map_or(finalized_block.hash, |idx| tree[idx].hash), Unexecuted instantiation: _RNCNCINvNtCsiGub1lfKphe_13smoldot_light15runtime_service14run_backgroundpE0sb_0B8_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sb_0B1c_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundpE0sb_0B8_ |
1153 | 0 | is_new_best, |
1154 | 0 | scale_encoded_header, |
1155 | 0 | new_runtime: if !Arc::ptr_eq(&parent_runtime, &block_runtime) { |
1156 | 0 | Some( |
1157 | 0 | block_runtime |
1158 | 0 | .runtime |
1159 | 0 | .as_ref() |
1160 | 0 | .map(|rt| rt.runtime_version().clone()) Unexecuted instantiation: _RNCNCINvNtCsiGub1lfKphe_13smoldot_light15runtime_service14run_backgroundpE0sc_0B8_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sc_0B1c_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundpE0sc_0B8_ |
1161 | 0 | .map_err(|err| err.clone()), Unexecuted instantiation: _RNCNCINvNtCsiGub1lfKphe_13smoldot_light15runtime_service14run_backgroundpE0sd_0B8_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sd_0B1c_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundpE0sd_0B8_ |
1162 | 0 | ) |
1163 | | } else { |
1164 | 0 | None |
1165 | | }, |
1166 | | }); |
1167 | | |
1168 | 0 | let mut to_remove = Vec::new(); |
1169 | 0 | for (subscription_id, (sender, _)) in all_blocks_subscriptions.iter_mut() { |
1170 | 0 | if sender.try_send(notif.clone()).is_ok() { |
1171 | 0 | let _prev_value = pinned_blocks.insert( |
1172 | 0 | (*subscription_id, block_hash), |
1173 | 0 | PinnedBlock { |
1174 | 0 | runtime: block_runtime.clone(), |
1175 | 0 | state_trie_root_hash, |
1176 | 0 | block_number, |
1177 | 0 | block_ignores_limit: true, |
1178 | 0 | }, |
1179 | 0 | ); |
1180 | 0 | debug_assert!(_prev_value.is_none()); |
1181 | 0 | } else { |
1182 | 0 | to_remove.push(*subscription_id); |
1183 | 0 | } |
1184 | | } |
1185 | 0 | for to_remove in to_remove { |
1186 | 0 | all_blocks_subscriptions.remove(&to_remove); |
1187 | 0 | let pinned_blocks_to_remove = pinned_blocks |
1188 | 0 | .range((to_remove, [0; 32])..=(to_remove, [0xff; 32])) |
1189 | 0 | .map(|((_, h), _)| *h) Unexecuted instantiation: _RNCNCINvNtCsiGub1lfKphe_13smoldot_light15runtime_service14run_backgroundpE0se_0B8_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0se_0B1c_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundpE0se_0B8_ |
1190 | 0 | .collect::<Vec<_>>(); |
1191 | 0 | for block in pinned_blocks_to_remove { |
1192 | 0 | pinned_blocks.remove(&(to_remove, block)); |
1193 | 0 | } |
1194 | | } |
1195 | | } |
1196 | | |
1197 | | WakeUpReason::TreeAdvanceFinalizedKnown( |
1198 | 0 | async_tree::OutputUpdate::BestBlockChanged { best_block_index }, |
1199 | | ) => { |
1200 | | let Tree::FinalizedBlockRuntimeKnown { |
1201 | 0 | tree, |
1202 | 0 | finalized_block, |
1203 | 0 | all_blocks_subscriptions, |
1204 | 0 | pinned_blocks, |
1205 | 0 | } = &mut background.tree |
1206 | | else { |
1207 | 0 | unreachable!() |
1208 | | }; |
1209 | | |
1210 | 0 | let hash = best_block_index |
1211 | 0 | .map_or(&*finalized_block, |idx| &tree[idx]) Unexecuted instantiation: _RNCNCINvNtCsiGub1lfKphe_13smoldot_light15runtime_service14run_backgroundpE0sf_0B8_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sf_0B1c_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundpE0sf_0B8_ |
1212 | 0 | .hash; |
1213 | 0 |
|
1214 | 0 | log!( |
1215 | 0 | &background.platform, |
1216 | 0 | Trace, |
1217 | 0 | &background.log_target, |
1218 | 0 | "output-chain-best-block-update", |
1219 | 0 | block_hash = HashDisplay(&hash), |
1220 | 0 | num_subscribers = all_blocks_subscriptions.len() |
1221 | 0 | ); |
1222 | 0 |
|
1223 | 0 | let notif = Notification::BestBlockChanged { hash }; |
1224 | 0 |
|
1225 | 0 | let mut to_remove = Vec::new(); |
1226 | 0 | for (subscription_id, (sender, _)) in all_blocks_subscriptions.iter_mut() { |
1227 | 0 | if sender.try_send(notif.clone()).is_err() { |
1228 | 0 | to_remove.push(*subscription_id); |
1229 | 0 | } |
1230 | | } |
1231 | 0 | for to_remove in to_remove { |
1232 | 0 | all_blocks_subscriptions.remove(&to_remove); |
1233 | 0 | let pinned_blocks_to_remove = pinned_blocks |
1234 | 0 | .range((to_remove, [0; 32])..=(to_remove, [0xff; 32])) |
1235 | 0 | .map(|((_, h), _)| *h) Unexecuted instantiation: _RNCNCINvNtCsiGub1lfKphe_13smoldot_light15runtime_service14run_backgroundpE0sg_0B8_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sg_0B1c_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundpE0sg_0B8_ |
1236 | 0 | .collect::<Vec<_>>(); |
1237 | 0 | for block in pinned_blocks_to_remove { |
1238 | 0 | pinned_blocks.remove(&(to_remove, block)); |
1239 | 0 | } |
1240 | | } |
1241 | | } |
1242 | | |
1243 | | WakeUpReason::TreeAdvanceFinalizedUnknown(async_tree::OutputUpdate::Block(_)) |
1244 | | | WakeUpReason::TreeAdvanceFinalizedUnknown( |
1245 | | async_tree::OutputUpdate::BestBlockChanged { .. }, |
1246 | | ) => { |
1247 | | // Nothing to do. |
1248 | 0 | continue; |
1249 | | } |
1250 | | |
1251 | | WakeUpReason::TreeAdvanceFinalizedUnknown(async_tree::OutputUpdate::Finalized { |
1252 | 0 | user_data: new_finalized, |
1253 | 0 | former_finalized_async_op_user_data, |
1254 | 0 | best_output_block_updated, |
1255 | | .. |
1256 | | }) => { |
1257 | 0 | let Tree::FinalizedBlockRuntimeUnknown { tree, .. } = &mut background.tree else { |
1258 | 0 | unreachable!() |
1259 | | }; |
1260 | | |
1261 | | // Make sure that this is the first finalized block whose runtime is |
1262 | | // known, otherwise there's a pretty big bug somewhere. |
1263 | 0 | debug_assert!(former_finalized_async_op_user_data.is_none()); |
1264 | | |
1265 | 0 | let best_block_hash_if_changed = if best_output_block_updated { |
1266 | 0 | Some( |
1267 | 0 | tree.output_best_block_index() |
1268 | 0 | .map_or(new_finalized.hash, |(idx, _)| tree[idx].hash), Unexecuted instantiation: _RNCNCINvNtCsiGub1lfKphe_13smoldot_light15runtime_service14run_backgroundpE0sh_0B8_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sh_0B1c_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundpE0sh_0B8_ |
1269 | 0 | ) |
1270 | | } else { |
1271 | 0 | None |
1272 | | }; |
1273 | 0 | log!( |
1274 | 0 | &background.platform, |
1275 | 0 | Trace, |
1276 | 0 | &background.log_target, |
1277 | 0 | "output-chain-initialized", |
1278 | 0 | finalized_block_hash = HashDisplay(&new_finalized.hash), |
1279 | 0 | best_block_hash = if let Some(best_block_hash) = best_block_hash_if_changed { |
1280 | 0 | Cow::Owned(HashDisplay(&best_block_hash).to_string()) |
1281 | | } else { |
1282 | 0 | Cow::Borrowed("<unchanged>") |
1283 | | }, |
1284 | | ); |
1285 | | |
1286 | | // Substitute `tree` with a dummy empty tree just in order to extract |
1287 | | // the value. The `tree` only contains "async op user datas" equal |
1288 | | // to `Some` (they're inserted manually when a download finishes) |
1289 | | // except for the finalized block which has now just been extracted. |
1290 | | // We can safely unwrap() all these user datas. |
1291 | 0 | let new_tree = mem::replace( |
1292 | 0 | tree, |
1293 | 0 | async_tree::AsyncTree::new(async_tree::Config { |
1294 | 0 | finalized_async_user_data: None, |
1295 | 0 | retry_after_failed: Duration::new(0, 0), |
1296 | 0 | blocks_capacity: 0, |
1297 | 0 | }), |
1298 | 0 | ) |
1299 | 0 | .map_async_op_user_data(|runtime_index| runtime_index.unwrap()); Unexecuted instantiation: _RNCNCINvNtCsiGub1lfKphe_13smoldot_light15runtime_service14run_backgroundpE0si_0B8_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0si_0B1c_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundpE0si_0B8_ |
1300 | 0 |
|
1301 | 0 | // Change the state of `Background` to the "finalized runtime known" state. |
1302 | 0 | background.tree = Tree::FinalizedBlockRuntimeKnown { |
1303 | 0 | all_blocks_subscriptions: hashbrown::HashMap::with_capacity_and_hasher( |
1304 | 0 | 32, |
1305 | 0 | Default::default(), |
1306 | 0 | ), // TODO: capacity? |
1307 | 0 | pinned_blocks: BTreeMap::new(), |
1308 | 0 | tree: new_tree, |
1309 | 0 | finalized_block: new_finalized, |
1310 | 0 | }; |
1311 | | } |
1312 | | |
1313 | | WakeUpReason::MustSubscribe => { |
1314 | | // Subscription to the sync service must be recreated. |
1315 | | |
1316 | | // The buffer size should be large enough so that, if the CPU is busy, it |
1317 | | // doesn't become full before the execution of the runtime service resumes. |
1318 | | // Note that this `await` freezes the entire runtime service background task, |
1319 | | // but the sync service guarantees that `subscribe_all` returns very quickly. |
1320 | 0 | let subscription = background.sync_service.subscribe_all(32, true).await; |
1321 | | |
1322 | 0 | log!( |
1323 | 0 | &background.platform, |
1324 | 0 | Trace, |
1325 | 0 | &background.log_target, |
1326 | 0 | "sync-service-subscribed", |
1327 | 0 | finalized_block_hash = HashDisplay(&header::hash_from_scale_encoded_header( |
1328 | 0 | &subscription.finalized_block_scale_encoded_header |
1329 | 0 | )), |
1330 | 0 | finalized_block_runtime_known = ?subscription.finalized_block_runtime.is_some() |
1331 | 0 | ); |
1332 | 0 |
|
1333 | 0 | // Update the state of `Background` with what we just grabbed. |
1334 | 0 | // |
1335 | 0 | // Note that the content of `Background` is reset unconditionally. |
1336 | 0 | // It might seem like a good idea to only reset the content of `Background` if the new |
1337 | 0 | // subscription has a different finalized block than currently. However, there is |
1338 | 0 | // absolutely no guarantee for the non-finalized blocks currently in the tree to be a |
1339 | 0 | // subset or superset of the non-finalized blocks in the new subscription. |
1340 | 0 | // Using the new subscription but keeping the existing tree could therefore result in |
1341 | 0 | // state inconsistencies. |
1342 | 0 | // |
1343 | 0 | // Additionally, the situation where a subscription is killed but the finalized block |
1344 | 0 | // didn't change should be extremely rare anyway. |
1345 | 0 | { |
1346 | 0 | background.runtimes = slab::Slab::with_capacity(2); // TODO: hardcoded capacity |
1347 | | |
1348 | | // TODO: DRY below |
1349 | 0 | if let Some(finalized_block_runtime) = subscription.finalized_block_runtime { |
1350 | 0 | let finalized_block_hash = header::hash_from_scale_encoded_header( |
1351 | 0 | &subscription.finalized_block_scale_encoded_header, |
1352 | 0 | ); |
1353 | 0 | let finalized_block_height = header::decode( |
1354 | 0 | &subscription.finalized_block_scale_encoded_header, |
1355 | 0 | background.sync_service.block_number_bytes(), |
1356 | 0 | ) |
1357 | 0 | .unwrap() |
1358 | 0 | .number; // TODO: consider feeding the information from the sync service? |
1359 | 0 |
|
1360 | 0 | let storage_code_len = u64::try_from( |
1361 | 0 | finalized_block_runtime |
1362 | 0 | .storage_code |
1363 | 0 | .as_ref() |
1364 | 0 | .map_or(0, |v| v.len()), Unexecuted instantiation: _RNCNCINvNtCsiGub1lfKphe_13smoldot_light15runtime_service14run_backgroundpE0sj_0B8_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sj_0B1c_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundpE0sj_0B8_ |
1365 | 0 | ) |
1366 | 0 | .unwrap(); |
1367 | 0 |
|
1368 | 0 | let runtime = Arc::new(Runtime { |
1369 | 0 | runtime_code: finalized_block_runtime.storage_code, |
1370 | 0 | heap_pages: finalized_block_runtime.storage_heap_pages, |
1371 | 0 | code_merkle_value: finalized_block_runtime.code_merkle_value, |
1372 | 0 | closest_ancestor_excluding: finalized_block_runtime |
1373 | 0 | .closest_ancestor_excluding, |
1374 | 0 | runtime: Ok(finalized_block_runtime.virtual_machine), |
1375 | 0 | }); |
1376 | 0 |
|
1377 | 0 | match &runtime.runtime { |
1378 | 0 | Ok(runtime) => { |
1379 | 0 | log!( |
1380 | 0 | &background.platform, |
1381 | 0 | Info, |
1382 | 0 | &background.log_target, |
1383 | 0 | format!( |
1384 | 0 | "Finalized block runtime ready. Spec version: {}. \ |
1385 | 0 | Size of `:code`: {}.", |
1386 | 0 | runtime.runtime_version().decode().spec_version, |
1387 | 0 | BytesDisplay(storage_code_len) |
1388 | 0 | ) |
1389 | 0 | ); |
1390 | 0 | } |
1391 | 0 | Err(error) => { |
1392 | 0 | log!( |
1393 | 0 | &background.platform, |
1394 | 0 | Warn, |
1395 | 0 | &background.log_target, |
1396 | 0 | format!( |
1397 | 0 | "Erronenous finalized block runtime. Size of \ |
1398 | 0 | `:code`: {}.\nError: {}\nThis indicates an incompatibility \ |
1399 | 0 | between smoldot and the chain.", |
1400 | 0 | BytesDisplay(storage_code_len), |
1401 | 0 | error |
1402 | 0 | ) |
1403 | 0 | ); |
1404 | 0 | } |
1405 | | } |
1406 | | |
1407 | 0 | background.tree = Tree::FinalizedBlockRuntimeKnown { |
1408 | 0 | all_blocks_subscriptions: hashbrown::HashMap::with_capacity_and_hasher( |
1409 | 0 | 32, |
1410 | 0 | Default::default(), |
1411 | 0 | ), // TODO: capacity? |
1412 | 0 | pinned_blocks: BTreeMap::new(), |
1413 | 0 | finalized_block: Block { |
1414 | 0 | hash: finalized_block_hash, |
1415 | 0 | height: finalized_block_height, |
1416 | 0 | scale_encoded_header: subscription |
1417 | 0 | .finalized_block_scale_encoded_header, |
1418 | 0 | }, |
1419 | 0 | tree: { |
1420 | 0 | let mut tree = |
1421 | 0 | async_tree::AsyncTree::<_, Block, _>::new(async_tree::Config { |
1422 | 0 | finalized_async_user_data: runtime, |
1423 | 0 | retry_after_failed: Duration::from_secs(10), // TODO: hardcoded |
1424 | 0 | blocks_capacity: 32, |
1425 | 0 | }); |
1426 | | |
1427 | 0 | for block in subscription.non_finalized_blocks_ancestry_order { |
1428 | 0 | let parent_index = if block.parent_hash == finalized_block_hash |
1429 | | { |
1430 | 0 | None |
1431 | | } else { |
1432 | 0 | Some( |
1433 | 0 | tree.input_output_iter_unordered() |
1434 | 0 | .find(|b| b.user_data.hash == block.parent_hash) Unexecuted instantiation: _RNCNCINvNtCsiGub1lfKphe_13smoldot_light15runtime_service14run_backgroundpE0sk_0B8_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sk_0B1c_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundpE0sk_0B8_ |
1435 | 0 | .unwrap() |
1436 | 0 | .id, |
1437 | 0 | ) |
1438 | | }; |
1439 | | |
1440 | 0 | let same_runtime_as_parent = same_runtime_as_parent( |
1441 | 0 | &block.scale_encoded_header, |
1442 | 0 | background.sync_service.block_number_bytes(), |
1443 | 0 | ); |
1444 | 0 | let _ = tree.input_insert_block( |
1445 | 0 | Block { |
1446 | 0 | hash: header::hash_from_scale_encoded_header( |
1447 | 0 | &block.scale_encoded_header, |
1448 | 0 | ), |
1449 | 0 | height: header::decode( |
1450 | 0 | &block.scale_encoded_header, |
1451 | 0 | background.sync_service.block_number_bytes(), |
1452 | 0 | ) |
1453 | 0 | .unwrap() |
1454 | 0 | .number, // TODO: consider feeding the information from the sync service? |
1455 | 0 | scale_encoded_header: block.scale_encoded_header, |
1456 | 0 | }, |
1457 | 0 | parent_index, |
1458 | 0 | same_runtime_as_parent, |
1459 | 0 | block.is_new_best, |
1460 | 0 | ); |
1461 | | } |
1462 | | |
1463 | 0 | tree |
1464 | | }, |
1465 | | }; |
1466 | 0 | } else { |
1467 | 0 | background.tree = Tree::FinalizedBlockRuntimeUnknown { |
1468 | | tree: { |
1469 | 0 | let mut tree = async_tree::AsyncTree::new(async_tree::Config { |
1470 | 0 | finalized_async_user_data: None, |
1471 | 0 | retry_after_failed: Duration::from_secs(10), // TODO: hardcoded |
1472 | 0 | blocks_capacity: 32, |
1473 | 0 | }); |
1474 | 0 | let node_index = tree.input_insert_block( |
1475 | 0 | Block { |
1476 | 0 | hash: header::hash_from_scale_encoded_header( |
1477 | 0 | &subscription.finalized_block_scale_encoded_header, |
1478 | 0 | ), |
1479 | 0 | height: header::decode( |
1480 | 0 | &subscription.finalized_block_scale_encoded_header, |
1481 | 0 | background.sync_service.block_number_bytes(), |
1482 | 0 | ) |
1483 | 0 | .unwrap() |
1484 | 0 | .number, // TODO: consider feeding the information from the sync service? |
1485 | 0 | scale_encoded_header: subscription |
1486 | 0 | .finalized_block_scale_encoded_header, |
1487 | 0 | }, |
1488 | 0 | None, |
1489 | 0 | false, |
1490 | 0 | true, |
1491 | 0 | ); |
1492 | 0 | tree.input_finalize(node_index); |
1493 | | |
1494 | 0 | for block in subscription.non_finalized_blocks_ancestry_order { |
1495 | 0 | // TODO: O(n) |
1496 | 0 | let parent_index = tree |
1497 | 0 | .input_output_iter_unordered() |
1498 | 0 | .find(|b| b.user_data.hash == block.parent_hash) Unexecuted instantiation: _RNCNCINvNtCsiGub1lfKphe_13smoldot_light15runtime_service14run_backgroundpE0sl_0B8_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sl_0B1c_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundpE0sl_0B8_ |
1499 | 0 | .unwrap() |
1500 | 0 | .id; |
1501 | 0 |
|
1502 | 0 | let same_runtime_as_parent = same_runtime_as_parent( |
1503 | 0 | &block.scale_encoded_header, |
1504 | 0 | background.sync_service.block_number_bytes(), |
1505 | 0 | ); |
1506 | 0 | let _ = tree.input_insert_block( |
1507 | 0 | Block { |
1508 | 0 | hash: header::hash_from_scale_encoded_header( |
1509 | 0 | &block.scale_encoded_header, |
1510 | 0 | ), |
1511 | 0 | height: header::decode( |
1512 | 0 | &block.scale_encoded_header, |
1513 | 0 | background.sync_service.block_number_bytes(), |
1514 | 0 | ) |
1515 | 0 | .unwrap() |
1516 | 0 | .number, // TODO: consider feeding the information from the sync service? |
1517 | 0 | scale_encoded_header: block.scale_encoded_header, |
1518 | 0 | }, |
1519 | 0 | Some(parent_index), |
1520 | 0 | same_runtime_as_parent, |
1521 | 0 | block.is_new_best, |
1522 | 0 | ); |
1523 | 0 | } |
1524 | | |
1525 | 0 | tree |
1526 | | }, |
1527 | | }; |
1528 | | } |
1529 | | } |
1530 | | |
1531 | 0 | background.blocks_stream = Some(Box::pin(subscription.new_blocks)); |
1532 | 0 | background.runtime_downloads = stream::FuturesUnordered::new(); |
1533 | | } |
1534 | | |
1535 | 0 | WakeUpReason::StartPendingSubscribeAll(pending_subscription) => { |
1536 | | // A subscription is waiting to be started. |
1537 | | |
1538 | | // Extract the components of the `FinalizedBlockRuntimeKnown`. |
1539 | 0 | let (tree, finalized_block, pinned_blocks, all_blocks_subscriptions) = |
1540 | 0 | match &mut background.tree { |
1541 | | Tree::FinalizedBlockRuntimeKnown { |
1542 | 0 | tree, |
1543 | 0 | finalized_block, |
1544 | 0 | pinned_blocks, |
1545 | 0 | all_blocks_subscriptions, |
1546 | 0 | } => ( |
1547 | 0 | tree, |
1548 | 0 | finalized_block, |
1549 | 0 | pinned_blocks, |
1550 | 0 | all_blocks_subscriptions, |
1551 | 0 | ), |
1552 | 0 | _ => unreachable!(), |
1553 | | }; |
1554 | | |
1555 | 0 | let (tx, new_blocks_channel) = |
1556 | 0 | async_channel::bounded(pending_subscription.buffer_size); |
1557 | 0 | let subscription_id = background.next_subscription_id; |
1558 | 0 | debug_assert_eq!( |
1559 | 0 | pinned_blocks |
1560 | 0 | .range((subscription_id, [0; 32])..=(subscription_id, [0xff; 32])) |
1561 | 0 | .count(), |
1562 | | 0 |
1563 | | ); |
1564 | 0 | background.next_subscription_id += 1; |
1565 | 0 |
|
1566 | 0 | log!( |
1567 | 0 | &background.platform, |
1568 | 0 | Trace, |
1569 | 0 | &background.log_target, |
1570 | 0 | "pending-runtime-service-subscriptions-process", |
1571 | 0 | subscription_id |
1572 | 0 | ); |
1573 | 0 |
|
1574 | 0 | let decoded_finalized_block = header::decode( |
1575 | 0 | &finalized_block.scale_encoded_header, |
1576 | 0 | background.sync_service.block_number_bytes(), |
1577 | 0 | ) |
1578 | 0 | .unwrap(); |
1579 | 0 |
|
1580 | 0 | let _prev_value = pinned_blocks.insert( |
1581 | 0 | (subscription_id, finalized_block.hash), |
1582 | 0 | PinnedBlock { |
1583 | 0 | runtime: tree.output_finalized_async_user_data().clone(), |
1584 | 0 | state_trie_root_hash: *decoded_finalized_block.state_root, |
1585 | 0 | block_number: decoded_finalized_block.number, |
1586 | 0 | block_ignores_limit: false, |
1587 | 0 | }, |
1588 | 0 | ); |
1589 | 0 | debug_assert!(_prev_value.is_none()); |
1590 | | |
1591 | 0 | let mut non_finalized_blocks_ancestry_order = |
1592 | 0 | Vec::with_capacity(tree.num_input_non_finalized_blocks()); |
1593 | 0 | for block in tree.input_output_iter_ancestry_order() { |
1594 | 0 | let runtime = match block.async_op_user_data { |
1595 | 0 | Some(rt) => rt.clone(), |
1596 | 0 | None => continue, // Runtime of that block not known yet, so it shouldn't be reported. |
1597 | | }; |
1598 | | |
1599 | 0 | let block_hash = block.user_data.hash; |
1600 | 0 | let parent_runtime = tree.parent(block.id).map_or( |
1601 | 0 | tree.output_finalized_async_user_data().clone(), |
1602 | 0 | |parent_idx| tree.block_async_user_data(parent_idx).unwrap().clone(), Unexecuted instantiation: _RNCNCINvNtCsiGub1lfKphe_13smoldot_light15runtime_service14run_backgroundpE0sm_0B8_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sm_0B1c_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundpE0sm_0B8_ |
1603 | 0 | ); |
1604 | 0 |
|
1605 | 0 | let parent_hash = *header::decode( |
1606 | 0 | &block.user_data.scale_encoded_header, |
1607 | 0 | background.sync_service.block_number_bytes(), |
1608 | 0 | ) |
1609 | 0 | .unwrap() |
1610 | 0 | .parent_hash; // TODO: correct? if yes, document |
1611 | 0 | debug_assert!( |
1612 | 0 | parent_hash == finalized_block.hash |
1613 | 0 | || tree |
1614 | 0 | .input_output_iter_ancestry_order() |
1615 | 0 | .any(|b| parent_hash == b.user_data.hash |
1616 | 0 | && b.async_op_user_data.is_some()) Unexecuted instantiation: _RNCNCINvNtCsiGub1lfKphe_13smoldot_light15runtime_service14run_backgroundpE0sL_0B8_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sL_0B1c_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundpE0sL_0B8_ |
1617 | | ); |
1618 | | |
1619 | 0 | let decoded_header = header::decode( |
1620 | 0 | &block.user_data.scale_encoded_header, |
1621 | 0 | background.sync_service.block_number_bytes(), |
1622 | 0 | ) |
1623 | 0 | .unwrap(); |
1624 | 0 |
|
1625 | 0 | let _prev_value = pinned_blocks.insert( |
1626 | 0 | (subscription_id, block_hash), |
1627 | 0 | PinnedBlock { |
1628 | 0 | runtime: runtime.clone(), |
1629 | 0 | state_trie_root_hash: *decoded_header.state_root, |
1630 | 0 | block_number: decoded_header.number, |
1631 | 0 | block_ignores_limit: true, |
1632 | 0 | }, |
1633 | 0 | ); |
1634 | 0 | debug_assert!(_prev_value.is_none()); |
1635 | | |
1636 | 0 | non_finalized_blocks_ancestry_order.push(BlockNotification { |
1637 | 0 | is_new_best: block.is_output_best, |
1638 | 0 | parent_hash, |
1639 | 0 | scale_encoded_header: block.user_data.scale_encoded_header.clone(), |
1640 | 0 | new_runtime: if !Arc::ptr_eq(&runtime, &parent_runtime) { |
1641 | 0 | Some( |
1642 | 0 | runtime |
1643 | 0 | .runtime |
1644 | 0 | .as_ref() |
1645 | 0 | .map(|rt| rt.runtime_version().clone()) Unexecuted instantiation: _RNCNCINvNtCsiGub1lfKphe_13smoldot_light15runtime_service14run_backgroundpE0sn_0B8_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sn_0B1c_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundpE0sn_0B8_ |
1646 | 0 | .map_err(|err| err.clone()), Unexecuted instantiation: _RNCNCINvNtCsiGub1lfKphe_13smoldot_light15runtime_service14run_backgroundpE0so_0B8_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0so_0B1c_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundpE0so_0B8_ |
1647 | 0 | ) |
1648 | | } else { |
1649 | 0 | None |
1650 | | }, |
1651 | | }); |
1652 | | } |
1653 | | |
1654 | 0 | debug_assert!(matches!( |
1655 | 0 | non_finalized_blocks_ancestry_order |
1656 | 0 | .iter() |
1657 | 0 | .filter(|b| b.is_new_best) Unexecuted instantiation: _RNCNCINvNtCsiGub1lfKphe_13smoldot_light15runtime_service14run_backgroundpE0sM_0B8_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sM_0B1c_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundpE0sM_0B8_ |
1658 | 0 | .count(), |
1659 | | 0 | 1 |
1660 | | )); |
1661 | | |
1662 | 0 | all_blocks_subscriptions.insert( |
1663 | 0 | subscription_id, |
1664 | 0 | (tx, pending_subscription.max_pinned_blocks.get() - 1), |
1665 | 0 | ); |
1666 | 0 |
|
1667 | 0 | let _ = pending_subscription.result_tx.send(SubscribeAll { |
1668 | 0 | finalized_block_scale_encoded_header: finalized_block |
1669 | 0 | .scale_encoded_header |
1670 | 0 | .clone(), |
1671 | 0 | finalized_block_runtime: tree |
1672 | 0 | .output_finalized_async_user_data() |
1673 | 0 | .runtime |
1674 | 0 | .as_ref() |
1675 | 0 | .map(|rt| rt.runtime_version().clone()) Unexecuted instantiation: _RNCNCINvNtCsiGub1lfKphe_13smoldot_light15runtime_service14run_backgroundpE0sp_0B8_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sp_0B1c_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundpE0sp_0B8_ |
1676 | 0 | .map_err(|err| err.clone()), Unexecuted instantiation: _RNCNCINvNtCsiGub1lfKphe_13smoldot_light15runtime_service14run_backgroundpE0sq_0B8_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sq_0B1c_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundpE0sq_0B8_ |
1677 | 0 | non_finalized_blocks_ancestry_order, |
1678 | 0 | new_blocks: Subscription { |
1679 | 0 | subscription_id, |
1680 | 0 | channel: Box::pin(new_blocks_channel), |
1681 | 0 | to_background: background.to_background_tx.upgrade().unwrap(), |
1682 | 0 | }, |
1683 | 0 | }); |
1684 | | } |
1685 | | |
1686 | 0 | WakeUpReason::SyncServiceSubscriptionReset => { |
1687 | 0 | // The sync service subscription has been or must be reset. |
1688 | 0 | log!( |
1689 | 0 | &background.platform, |
1690 | 0 | Trace, |
1691 | 0 | &background.log_target, |
1692 | 0 | "sync-subscription-reset" |
1693 | 0 | ); |
1694 | 0 | background.blocks_stream = None; |
1695 | 0 | } |
1696 | | |
1697 | | WakeUpReason::ForegroundClosed => { |
1698 | | // Frontend and all subscriptions have shut down. |
1699 | 0 | log!( |
1700 | 0 | &background.platform, |
1701 | 0 | Debug, |
1702 | 0 | &background.log_target, |
1703 | 0 | "graceful-shutdown" |
1704 | 0 | ); |
1705 | 0 | return; |
1706 | | } |
1707 | | |
1708 | 0 | WakeUpReason::ToBackground(ToBackground::SubscribeAll(msg)) => { |
1709 | 0 | // Foreground wants to subscribe. |
1710 | 0 |
|
1711 | 0 | log!( |
1712 | 0 | &background.platform, |
1713 | 0 | Trace, |
1714 | 0 | &background.log_target, |
1715 | 0 | "runtime-service-subscription-requested" |
1716 | 0 | ); |
1717 | 0 |
|
1718 | 0 | // In order to avoid potentially growing `pending_subscriptions` forever, we |
1719 | 0 | // remove senders that are closed. This is `O(n)`, but we expect this list to |
1720 | 0 | // be rather small. |
1721 | 0 | background |
1722 | 0 | .pending_subscriptions |
1723 | 0 | .retain(|s| !s.result_tx.is_canceled()); Unexecuted instantiation: _RNCNCINvNtCsiGub1lfKphe_13smoldot_light15runtime_service14run_backgroundpE0sr_0B8_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sr_0B1c_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundpE0sr_0B8_ |
1724 | 0 | background.pending_subscriptions.push_back(msg); |
1725 | 0 | } |
1726 | | |
1727 | | WakeUpReason::ToBackground(ToBackground::CompileAndPinRuntime { |
1728 | 0 | result_tx, |
1729 | 0 | storage_code, |
1730 | 0 | storage_heap_pages, |
1731 | 0 | code_merkle_value, |
1732 | 0 | closest_ancestor_excluding, |
1733 | 0 | }) => { |
1734 | 0 | // Foreground wants to compile the given runtime. |
1735 | 0 |
|
1736 | 0 | // Try to find an existing identical runtime. |
1737 | 0 | let existing_runtime = background |
1738 | 0 | .runtimes |
1739 | 0 | .iter() |
1740 | 0 | .filter_map(|(_, rt)| rt.upgrade()) Unexecuted instantiation: _RNCNCINvNtCsiGub1lfKphe_13smoldot_light15runtime_service14run_backgroundpE0ss_0B8_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0ss_0B1c_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundpE0ss_0B8_ |
1741 | 0 | .find(|rt| { |
1742 | 0 | rt.runtime_code == storage_code && rt.heap_pages == storage_heap_pages |
1743 | 0 | }); Unexecuted instantiation: _RNCNCINvNtCsiGub1lfKphe_13smoldot_light15runtime_service14run_backgroundpE0st_0B8_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0st_0B1c_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundpE0st_0B8_ |
1744 | | |
1745 | 0 | let runtime = if let Some(existing_runtime) = existing_runtime { |
1746 | 0 | log!( |
1747 | 0 | &background.platform, |
1748 | 0 | Trace, |
1749 | 0 | &background.log_target, |
1750 | 0 | "foreground-compile-and-pin-runtime-cache-hit" |
1751 | 0 | ); |
1752 | 0 | existing_runtime |
1753 | | } else { |
1754 | | // No identical runtime was found. Try compiling the new runtime. |
1755 | 0 | let before_compilation = background.platform.now(); |
1756 | 0 | let runtime = compile_runtime( |
1757 | 0 | &background.platform, |
1758 | 0 | &background.log_target, |
1759 | 0 | &storage_code, |
1760 | 0 | &storage_heap_pages, |
1761 | 0 | ); |
1762 | 0 | let compilation_duration = background.platform.now() - before_compilation; |
1763 | 0 | log!( |
1764 | 0 | &background.platform, |
1765 | 0 | Debug, |
1766 | 0 | &background.log_target, |
1767 | 0 | "foreground-compile-and-pin-runtime-cache-miss", |
1768 | 0 | ?compilation_duration, |
1769 | 0 | compilation_success = runtime.is_ok() |
1770 | 0 | ); |
1771 | 0 | let runtime = Arc::new(Runtime { |
1772 | 0 | heap_pages: storage_heap_pages, |
1773 | 0 | runtime_code: storage_code, |
1774 | 0 | code_merkle_value, |
1775 | 0 | closest_ancestor_excluding, |
1776 | 0 | runtime, |
1777 | 0 | }); |
1778 | 0 | background.runtimes.insert(Arc::downgrade(&runtime)); |
1779 | 0 | runtime |
1780 | | }; |
1781 | | |
1782 | 0 | let _ = result_tx.send(runtime); |
1783 | | } |
1784 | | |
1785 | | WakeUpReason::ToBackground(ToBackground::FinalizedRuntimeStorageMerkleValues { |
1786 | 0 | result_tx, |
1787 | 0 | }) => { |
1788 | 0 | // Foreground wants the finalized runtime storage Merkle values. |
1789 | 0 |
|
1790 | 0 | log!( |
1791 | 0 | &background.platform, |
1792 | 0 | Trace, |
1793 | 0 | &background.log_target, |
1794 | 0 | "foreground-finalized-runtime-storage-merkle-values" |
1795 | 0 | ); |
1796 | 0 |
|
1797 | 0 | let _ = result_tx.send( |
1798 | 0 | if let Tree::FinalizedBlockRuntimeKnown { tree, .. } = &background.tree { |
1799 | 0 | let runtime = &tree.output_finalized_async_user_data(); |
1800 | 0 | Some(( |
1801 | 0 | runtime.runtime_code.clone(), |
1802 | 0 | runtime.code_merkle_value.clone(), |
1803 | 0 | runtime.closest_ancestor_excluding.clone(), |
1804 | 0 | )) |
1805 | | } else { |
1806 | 0 | None |
1807 | | }, |
1808 | | ); |
1809 | | } |
1810 | | |
1811 | 0 | WakeUpReason::ToBackground(ToBackground::IsNearHeadOfChainHeuristic { result_tx }) => { |
1812 | 0 | // Foreground wants to query whether we are at the head of the chain. |
1813 | 0 |
|
1814 | 0 | log!( |
1815 | 0 | &background.platform, |
1816 | 0 | Trace, |
1817 | 0 | &background.log_target, |
1818 | 0 | "foreground-is-near-head-of-chain-heuristic" |
1819 | 0 | ); |
1820 | 0 |
|
1821 | 0 | // If we aren't subscribed to the sync service yet, we notify that we are not |
1822 | 0 | // near the head of the chain. |
1823 | 0 | if background.blocks_stream.is_none() { |
1824 | 0 | let _ = result_tx.send(false); |
1825 | 0 | continue; |
1826 | 0 | } |
1827 | | |
1828 | | // Check whether any runtime has been downloaded yet. If not, we notify that |
1829 | | // we're not near the head of the chain. |
1830 | | let Tree::FinalizedBlockRuntimeKnown { |
1831 | 0 | tree, |
1832 | 0 | finalized_block, |
1833 | | .. |
1834 | 0 | } = &background.tree |
1835 | | else { |
1836 | 0 | let _ = result_tx.send(false); |
1837 | 0 | continue; |
1838 | | }; |
1839 | | |
1840 | | // The runtime service head might be close to the sync service head, but if the |
1841 | | // sync service is far away from the head of the chain, then the runtime service |
1842 | | // is necessarily also far away. |
1843 | 0 | if !background |
1844 | 0 | .sync_service |
1845 | 0 | .is_near_head_of_chain_heuristic() |
1846 | 0 | .await |
1847 | | { |
1848 | 0 | let _ = result_tx.send(false); |
1849 | 0 | continue; |
1850 | 0 | } |
1851 | | |
1852 | | // If the input best block (i.e. what the sync service feeds us) is equal to |
1853 | | // output finalized block (i.e. what the runtime service has downloaded), we are |
1854 | | // at the very head of the chain. |
1855 | 0 | let Some(input_best) = tree.input_best_block_index() else { |
1856 | 0 | let _ = result_tx.send(true); |
1857 | 0 | continue; |
1858 | | }; |
1859 | | |
1860 | | // We consider ourselves as being at the head of the chain if the |
1861 | | // distance between the output tree best (i.e. whose runtime has |
1862 | | // been downloaded) and the input tree best (i.e. what the sync service |
1863 | | // feeds us) is smaller than a certain number of blocks. |
1864 | | // Note that the input best can have a smaller block height than the |
1865 | | // output, for example in case of reorg. |
1866 | 0 | let is_near = tree[input_best].height.saturating_sub( |
1867 | 0 | tree.output_best_block_index() |
1868 | 0 | .map_or(finalized_block.height, |(idx, _)| tree[idx].height), Unexecuted instantiation: _RNCNCINvNtCsiGub1lfKphe_13smoldot_light15runtime_service14run_backgroundpE0su_0B8_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0su_0B1c_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundpE0su_0B8_ |
1869 | 0 | ) <= 12; |
1870 | 0 | let _ = result_tx.send(is_near); |
1871 | | } |
1872 | | |
1873 | | WakeUpReason::ToBackground(ToBackground::UnpinBlock { |
1874 | 0 | result_tx, |
1875 | 0 | subscription_id, |
1876 | 0 | block_hash, |
1877 | 0 | }) => { |
1878 | 0 | // Foreground wants a block unpinned. |
1879 | 0 |
|
1880 | 0 | log!( |
1881 | 0 | &background.platform, |
1882 | 0 | Trace, |
1883 | 0 | &background.log_target, |
1884 | 0 | "foreground-unpin-block", |
1885 | 0 | subscription_id = subscription_id.0, |
1886 | 0 | block_hash = HashDisplay(&block_hash) |
1887 | 0 | ); |
1888 | | |
1889 | | if let Tree::FinalizedBlockRuntimeKnown { |
1890 | 0 | all_blocks_subscriptions, |
1891 | 0 | pinned_blocks, |
1892 | | .. |
1893 | 0 | } = &mut background.tree |
1894 | | { |
1895 | 0 | let block_ignores_limit = match pinned_blocks |
1896 | 0 | .remove(&(subscription_id.0, block_hash)) |
1897 | | { |
1898 | 0 | Some(b) => b.block_ignores_limit, |
1899 | | None => { |
1900 | | // Cold path.Ã’ |
1901 | 0 | if let Some((_, _)) = all_blocks_subscriptions.get(&subscription_id.0) { |
1902 | 0 | let _ = result_tx.send(Err(())); |
1903 | 0 | } else { |
1904 | 0 | let _ = result_tx.send(Ok(())); |
1905 | 0 | } |
1906 | 0 | continue; |
1907 | | } |
1908 | | }; |
1909 | | |
1910 | 0 | background.runtimes.retain(|_, rt| rt.strong_count() > 0); Unexecuted instantiation: _RNCNCINvNtCsiGub1lfKphe_13smoldot_light15runtime_service14run_backgroundpE0sv_0B8_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sv_0B1c_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundpE0sv_0B8_ |
1911 | 0 |
|
1912 | 0 | if !block_ignores_limit { |
1913 | 0 | let (_, finalized_pinned_remaining) = all_blocks_subscriptions |
1914 | 0 | .get_mut(&subscription_id.0) |
1915 | 0 | .unwrap(); |
1916 | 0 | *finalized_pinned_remaining += 1; |
1917 | 0 | } |
1918 | 0 | } |
1919 | | |
1920 | 0 | let _ = result_tx.send(Ok(())); |
1921 | | } |
1922 | | |
1923 | | WakeUpReason::ToBackground(ToBackground::PinPinnedBlockRuntime { |
1924 | 0 | result_tx, |
1925 | 0 | subscription_id, |
1926 | 0 | block_hash, |
1927 | 0 | }) => { |
1928 | 0 | // Foreground wants to pin the runtime of a pinned block. |
1929 | 0 |
|
1930 | 0 | log!( |
1931 | 0 | &background.platform, |
1932 | 0 | Trace, |
1933 | 0 | &background.log_target, |
1934 | 0 | "foreground-pin-pinned-block-runtime", |
1935 | 0 | subscription_id = subscription_id.0, |
1936 | 0 | block_hash = HashDisplay(&block_hash) |
1937 | 0 | ); |
1938 | | |
1939 | 0 | let pinned_block = { |
1940 | | if let Tree::FinalizedBlockRuntimeKnown { |
1941 | 0 | all_blocks_subscriptions, |
1942 | 0 | pinned_blocks, |
1943 | | .. |
1944 | 0 | } = &mut background.tree |
1945 | | { |
1946 | 0 | match pinned_blocks.get(&(subscription_id.0, block_hash)) { |
1947 | 0 | Some(v) => v.clone(), |
1948 | | None => { |
1949 | | // Cold path. |
1950 | 0 | if let Some((_, _)) = |
1951 | 0 | all_blocks_subscriptions.get(&subscription_id.0) |
1952 | 0 | { |
1953 | 0 | let _ = result_tx |
1954 | 0 | .send(Err(PinPinnedBlockRuntimeError::BlockNotPinned)); |
1955 | 0 | } else { |
1956 | 0 | let _ = result_tx.send(Err( |
1957 | 0 | PinPinnedBlockRuntimeError::ObsoleteSubscription, |
1958 | 0 | )); |
1959 | 0 | } |
1960 | 0 | continue; |
1961 | | } |
1962 | | } |
1963 | | } else { |
1964 | | let _ = |
1965 | 0 | result_tx.send(Err(PinPinnedBlockRuntimeError::ObsoleteSubscription)); |
1966 | 0 | continue; |
1967 | | } |
1968 | | }; |
1969 | | |
1970 | 0 | let _ = result_tx.send(Ok(( |
1971 | 0 | pinned_block.runtime.clone(), |
1972 | 0 | pinned_block.state_trie_root_hash, |
1973 | 0 | pinned_block.block_number, |
1974 | 0 | ))); |
1975 | | } |
1976 | | |
1977 | | WakeUpReason::ToBackground(ToBackground::RuntimeCall { |
1978 | 0 | result_tx, |
1979 | 0 | pinned_runtime, |
1980 | 0 | block_hash, |
1981 | 0 | block_number, |
1982 | 0 | block_state_trie_root_hash, |
1983 | 0 | function_name, |
1984 | 0 | required_api_version, |
1985 | 0 | parameters_vectored, |
1986 | 0 | total_attempts, |
1987 | 0 | timeout_per_request, |
1988 | 0 | _max_parallel: _, // TODO: unused /!\ |
1989 | 0 | }) => { |
1990 | 0 | // Foreground wants to perform a runtime call. |
1991 | 0 |
|
1992 | 0 | log!( |
1993 | 0 | &background.platform, |
1994 | 0 | Debug, |
1995 | 0 | &background.log_target, |
1996 | 0 | "foreground-runtime-call-start", |
1997 | 0 | block_hash = HashDisplay(&block_hash), |
1998 | 0 | block_number, |
1999 | 0 | block_state_trie_root_hash = HashDisplay(&block_state_trie_root_hash), |
2000 | 0 | function_name, |
2001 | 0 | ?required_api_version, |
2002 | 0 | parameters_vectored = HashDisplay(¶meters_vectored), |
2003 | 0 | total_attempts, |
2004 | 0 | ?timeout_per_request |
2005 | 0 | ); |
2006 | | |
2007 | 0 | let runtime = match &pinned_runtime.runtime { |
2008 | 0 | Ok(rt) => rt.clone(), |
2009 | 0 | Err(error) => { |
2010 | 0 | // The runtime call can't succeed because smoldot was incapable of |
2011 | 0 | // compiling the runtime. |
2012 | 0 | log!( |
2013 | 0 | &background.platform, |
2014 | 0 | Trace, |
2015 | 0 | &background.log_target, |
2016 | 0 | "foreground-runtime-call-abort", |
2017 | 0 | block_hash = HashDisplay(&block_hash), |
2018 | 0 | error = "invalid-runtime" |
2019 | 0 | ); |
2020 | 0 | let _ = |
2021 | 0 | result_tx.send(Err(RuntimeCallError::InvalidRuntime(error.clone()))); |
2022 | 0 | continue; |
2023 | | } |
2024 | | }; |
2025 | | |
2026 | 0 | let api_version = |
2027 | 0 | if let Some((api_name, api_version_required)) = required_api_version { |
2028 | 0 | let api_version_if_fulfilled = runtime |
2029 | 0 | .runtime_version() |
2030 | 0 | .decode() |
2031 | 0 | .apis |
2032 | 0 | .find_version(&api_name) |
2033 | 0 | .filter(|api_version| api_version_required.contains(&api_version)); Unexecuted instantiation: _RNCNCINvNtCsiGub1lfKphe_13smoldot_light15runtime_service14run_backgroundpE0sw_0B8_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sw_0B1c_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundpE0sw_0B8_ |
2034 | | |
2035 | 0 | let Some(api_version) = api_version_if_fulfilled else { |
2036 | | // API version required by caller isn't fulfilled. |
2037 | 0 | log!( |
2038 | 0 | &background.platform, |
2039 | 0 | Trace, |
2040 | 0 | &background.log_target, |
2041 | 0 | "foreground-runtime-call-abort", |
2042 | 0 | block_hash = HashDisplay(&block_hash), |
2043 | 0 | error = "api-version-requirement-unfulfilled" |
2044 | 0 | ); |
2045 | 0 | let _ = result_tx |
2046 | 0 | .send(Err(RuntimeCallError::ApiVersionRequirementUnfulfilled)); |
2047 | 0 | continue; |
2048 | | }; |
2049 | | |
2050 | 0 | Some(api_version) |
2051 | | } else { |
2052 | 0 | None |
2053 | | }; |
2054 | | |
2055 | 0 | background |
2056 | 0 | .progress_runtime_call_requests |
2057 | 0 | .push(Box::pin(async move { |
2058 | 0 | ProgressRuntimeCallRequest::Initialize(RuntimeCallRequest { |
2059 | 0 | block_hash, |
2060 | 0 | block_number, |
2061 | 0 | block_state_trie_root_hash, |
2062 | 0 | function_name, |
2063 | 0 | api_version, |
2064 | 0 | parameters_vectored, |
2065 | 0 | runtime, |
2066 | 0 | total_attempts, |
2067 | 0 | timeout_per_request, |
2068 | 0 | inaccessible_errors: Vec::with_capacity(cmp::min( |
2069 | 0 | 16, |
2070 | 0 | usize::try_from(total_attempts).unwrap_or(usize::MAX), |
2071 | 0 | )), |
2072 | 0 | result_tx, |
2073 | 0 | }) |
2074 | 0 | })); Unexecuted instantiation: _RNCNCINvNtCsiGub1lfKphe_13smoldot_light15runtime_service14run_backgroundpE0sx_0B8_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sx_0B1c_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundpE0sx_0B8_ |
2075 | | } |
2076 | | |
2077 | 0 | WakeUpReason::ProgressRuntimeCallRequest(progress) => { |
2078 | 0 | let (mut operation, call_proof_and_sender) = match progress { |
2079 | 0 | ProgressRuntimeCallRequest::Initialize(operation) => (operation, None), |
2080 | | ProgressRuntimeCallRequest::CallProofRequestDone { |
2081 | 0 | result: Ok(proof), |
2082 | 0 | call_proof_sender, |
2083 | 0 | operation, |
2084 | 0 | } => (operation, Some((proof, call_proof_sender))), |
2085 | | ProgressRuntimeCallRequest::CallProofRequestDone { |
2086 | 0 | result: Err(error), |
2087 | 0 | mut operation, |
2088 | 0 | call_proof_sender, |
2089 | 0 | } => { |
2090 | 0 | log!( |
2091 | 0 | &background.platform, |
2092 | 0 | Trace, |
2093 | 0 | &background.log_target, |
2094 | 0 | "foreground-runtime-call-progress-fail", |
2095 | 0 | block_hash = HashDisplay(&operation.block_hash), |
2096 | 0 | function_name = operation.function_name, |
2097 | 0 | parameters_vectored = HashDisplay(&operation.parameters_vectored), |
2098 | 0 | remaining_attempts = usize::try_from(operation.total_attempts).unwrap() |
2099 | 0 | - operation.inaccessible_errors.len() |
2100 | 0 | - 1, |
2101 | 0 | ?error |
2102 | 0 | ); |
2103 | 0 | operation |
2104 | 0 | .inaccessible_errors |
2105 | 0 | .push(RuntimeCallInaccessibleError::Network(error)); |
2106 | 0 | background |
2107 | 0 | .network_service |
2108 | 0 | .ban_and_disconnect( |
2109 | 0 | call_proof_sender, |
2110 | 0 | network_service::BanSeverity::Low, |
2111 | 0 | "call-proof-request-failed", |
2112 | 0 | ) |
2113 | 0 | .await; |
2114 | 0 | (operation, None) |
2115 | | } |
2116 | | }; |
2117 | | |
2118 | | // If the foreground is no longer interested in the result, abort now in order to |
2119 | | // save resources. |
2120 | 0 | if operation.result_tx.is_canceled() { |
2121 | 0 | continue; |
2122 | 0 | } |
2123 | | |
2124 | | // Process the call proof. |
2125 | 0 | if let Some((call_proof, call_proof_sender)) = call_proof_and_sender { |
2126 | 0 | match runtime_call_single_attempt( |
2127 | 0 | &background.platform, |
2128 | 0 | operation.runtime.clone(), |
2129 | 0 | &operation.function_name, |
2130 | 0 | &operation.parameters_vectored, |
2131 | 0 | &operation.block_state_trie_root_hash, |
2132 | 0 | call_proof.decode(), |
2133 | 0 | ) |
2134 | 0 | .await |
2135 | | { |
2136 | 0 | (timing, Ok(output)) => { |
2137 | 0 | // Execution finished successfully. |
2138 | 0 | // This is the happy path. |
2139 | 0 | log!( |
2140 | 0 | &background.platform, |
2141 | 0 | Debug, |
2142 | 0 | &background.log_target, |
2143 | 0 | "foreground-runtime-call-success", |
2144 | 0 | block_hash = HashDisplay(&operation.block_hash), |
2145 | 0 | function_name = operation.function_name, |
2146 | 0 | parameters_vectored = HashDisplay(&operation.parameters_vectored), |
2147 | 0 | output = HashDisplay(&output), |
2148 | 0 | virtual_machine_call_duration = ?timing.virtual_machine_call_duration, |
2149 | 0 | proof_access_duration = ?timing.proof_access_duration, |
2150 | 0 | ); |
2151 | 0 | let _ = operation.result_tx.send(Ok(RuntimeCallSuccess { |
2152 | 0 | output, |
2153 | 0 | api_version: operation.api_version, |
2154 | 0 | })); |
2155 | 0 | continue; |
2156 | | } |
2157 | 0 | (timing, Err(SingleRuntimeCallAttemptError::Execution(error))) => { |
2158 | 0 | log!( |
2159 | 0 | &background.platform, |
2160 | 0 | Debug, |
2161 | 0 | &background.log_target, |
2162 | 0 | "foreground-runtime-call-fail", |
2163 | 0 | block_hash = HashDisplay(&operation.block_hash), |
2164 | 0 | function_name = operation.function_name, |
2165 | 0 | parameters_vectored = HashDisplay(&operation.parameters_vectored), |
2166 | 0 | ?error, |
2167 | 0 | virtual_machine_call_duration = ?timing.virtual_machine_call_duration, |
2168 | 0 | proof_access_duration = ?timing.proof_access_duration, |
2169 | 0 | ); |
2170 | 0 | let _ = operation |
2171 | 0 | .result_tx |
2172 | 0 | .send(Err(RuntimeCallError::Execution(error))); |
2173 | 0 | continue; |
2174 | | } |
2175 | 0 | (timing, Err(SingleRuntimeCallAttemptError::Inaccessible(error))) => { |
2176 | 0 | // This path is reached only if the call proof was invalid. |
2177 | 0 | log!( |
2178 | 0 | &background.platform, |
2179 | 0 | Debug, |
2180 | 0 | &background.log_target, |
2181 | 0 | "foreground-runtime-call-progress-invalid-call-proof", |
2182 | 0 | block_hash = HashDisplay(&operation.block_hash), |
2183 | 0 | function_name = operation.function_name, |
2184 | 0 | parameters_vectored = HashDisplay(&operation.parameters_vectored), |
2185 | 0 | remaining_attempts = usize::try_from(operation.total_attempts) |
2186 | 0 | .unwrap() |
2187 | 0 | - operation.inaccessible_errors.len() |
2188 | 0 | - 1, |
2189 | 0 | ?error, |
2190 | 0 | virtual_machine_call_duration = ?timing.virtual_machine_call_duration, |
2191 | 0 | proof_access_duration = ?timing.proof_access_duration, |
2192 | 0 | ); |
2193 | 0 | operation.inaccessible_errors.push(error); |
2194 | 0 | background |
2195 | 0 | .network_service |
2196 | 0 | .ban_and_disconnect( |
2197 | 0 | call_proof_sender, |
2198 | 0 | network_service::BanSeverity::High, |
2199 | 0 | "invalid-call-proof", |
2200 | 0 | ) |
2201 | 0 | .await; |
2202 | | } |
2203 | | } |
2204 | 0 | } |
2205 | | |
2206 | | // If we have failed to obtain a valid proof several times, abort the runtime |
2207 | | // call attempt altogether. |
2208 | 0 | if u32::try_from(operation.inaccessible_errors.len()).unwrap_or(u32::MAX) |
2209 | 0 | >= operation.total_attempts |
2210 | | { |
2211 | | // No log line is printed here because one is already printed earlier. |
2212 | 0 | let _ = operation.result_tx.send(Err(RuntimeCallError::Inaccessible( |
2213 | 0 | operation.inaccessible_errors, |
2214 | 0 | ))); |
2215 | 0 | continue; |
2216 | 0 | } |
2217 | | |
2218 | | // This can be reached if the call proof was invalid or absent. We must start a |
2219 | | // new call proof request. |
2220 | | |
2221 | | // Choose peer to query. |
2222 | | // TODO: better peer selection |
2223 | | // TODO: can there be a race condition where the sync service forgets that a peer has knowledge of a block? shouldn't we somehow cache the peers that know this block ahead of time or something? |
2224 | 0 | let Some(call_proof_target) = background |
2225 | 0 | .sync_service |
2226 | 0 | .peers_assumed_know_blocks(operation.block_number, &operation.block_hash) |
2227 | 0 | .await |
2228 | 0 | .choose(&mut rand_chacha::ChaCha20Rng::from_seed({ |
2229 | 0 | // TODO: hacky |
2230 | 0 | let mut seed = [0; 32]; |
2231 | 0 | background.platform.fill_random_bytes(&mut seed); |
2232 | 0 | seed |
2233 | 0 | })) |
2234 | | else { |
2235 | | // No peer knows this block. Returning with a failure. |
2236 | 0 | log!( |
2237 | 0 | &background.platform, |
2238 | 0 | Debug, |
2239 | 0 | &background.log_target, |
2240 | 0 | "foreground-runtime-call-request-fail", |
2241 | 0 | block_hash = HashDisplay(&operation.block_hash), |
2242 | 0 | function_name = operation.function_name, |
2243 | 0 | parameters_vectored = HashDisplay(&operation.parameters_vectored), |
2244 | 0 | error = "no-peer-for-call-request" |
2245 | 0 | ); |
2246 | 0 | let _ = operation.result_tx.send(Err(RuntimeCallError::Inaccessible( |
2247 | 0 | operation.inaccessible_errors, |
2248 | 0 | ))); |
2249 | 0 | continue; |
2250 | | }; |
2251 | | |
2252 | 0 | log!( |
2253 | 0 | &background.platform, |
2254 | 0 | Trace, |
2255 | 0 | &background.log_target, |
2256 | 0 | "foreground-runtime-call-request-start", |
2257 | 0 | block_hash = HashDisplay(&operation.block_hash), |
2258 | 0 | function_name = operation.function_name, |
2259 | 0 | parameters_vectored = HashDisplay(&operation.parameters_vectored), |
2260 | 0 | call_proof_target, |
2261 | 0 | ); |
2262 | 0 |
|
2263 | 0 | // Start the request. |
2264 | 0 | background.progress_runtime_call_requests.push(Box::pin({ |
2265 | 0 | let call_proof_request_future = |
2266 | 0 | background.network_service.clone().call_proof_request( |
2267 | 0 | call_proof_target.clone(), |
2268 | 0 | network_service::CallProofRequestConfig { |
2269 | 0 | block_hash: operation.block_hash, |
2270 | 0 | method: Cow::Owned(operation.function_name.clone()), // TODO: overhead |
2271 | 0 | parameter_vectored: iter::once( |
2272 | 0 | operation.parameters_vectored.clone(), |
2273 | 0 | ), // TODO: overhead |
2274 | 0 | }, |
2275 | 0 | operation.timeout_per_request, |
2276 | 0 | ); |
2277 | 0 |
|
2278 | 0 | async move { |
2279 | 0 | let result = call_proof_request_future.await; |
2280 | 0 | ProgressRuntimeCallRequest::CallProofRequestDone { |
2281 | 0 | result, |
2282 | 0 | operation, |
2283 | 0 | call_proof_sender: call_proof_target, |
2284 | 0 | } |
2285 | 0 | } Unexecuted instantiation: _RNCNCINvNtCsiGub1lfKphe_13smoldot_light15runtime_service14run_backgroundpE0sy_0B8_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sy_0B1c_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundpE0sy_0B8_ |
2286 | 0 | })); |
2287 | | } |
2288 | | |
2289 | 0 | WakeUpReason::Notification(sync_service::Notification::Block(new_block)) => { |
2290 | 0 | // Sync service has reported a new block. |
2291 | 0 |
|
2292 | 0 | let same_runtime_as_parent = same_runtime_as_parent( |
2293 | 0 | &new_block.scale_encoded_header, |
2294 | 0 | background.sync_service.block_number_bytes(), |
2295 | 0 | ); |
2296 | 0 |
|
2297 | 0 | if same_runtime_as_parent { |
2298 | 0 | log!( |
2299 | 0 | &background.platform, |
2300 | 0 | Trace, |
2301 | 0 | &background.log_target, |
2302 | 0 | "input-chain-new-block", |
2303 | 0 | block_hash = HashDisplay(&header::hash_from_scale_encoded_header( |
2304 | 0 | &new_block.scale_encoded_header |
2305 | 0 | )), |
2306 | 0 | parent_block_hash = HashDisplay(&new_block.parent_hash), |
2307 | 0 | is_new_best = new_block.is_new_best, |
2308 | 0 | same_runtime_as_parent = true |
2309 | 0 | ); |
2310 | 0 | } else { |
2311 | 0 | log!( |
2312 | 0 | &background.platform, |
2313 | 0 | Debug, |
2314 | 0 | &background.log_target, |
2315 | 0 | "input-chain-new-block-runtime-upgrade", |
2316 | 0 | block_hash = HashDisplay(&header::hash_from_scale_encoded_header( |
2317 | 0 | &new_block.scale_encoded_header |
2318 | 0 | )), |
2319 | 0 | parent_block_hash = HashDisplay(&new_block.parent_hash), |
2320 | 0 | is_new_best = new_block.is_new_best |
2321 | 0 | ); |
2322 | 0 | } |
2323 | | |
2324 | 0 | match &mut background.tree { |
2325 | | Tree::FinalizedBlockRuntimeKnown { |
2326 | 0 | tree, |
2327 | 0 | finalized_block, |
2328 | | .. |
2329 | 0 | } => { |
2330 | 0 | let parent_index = if new_block.parent_hash == finalized_block.hash { |
2331 | 0 | None |
2332 | | } else { |
2333 | 0 | Some( |
2334 | 0 | // TODO: O(n) |
2335 | 0 | tree.input_output_iter_unordered() |
2336 | 0 | .find(|block| block.user_data.hash == new_block.parent_hash) Unexecuted instantiation: _RNCNCINvNtCsiGub1lfKphe_13smoldot_light15runtime_service14run_backgroundpE0sz_0B8_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sz_0B1c_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundpE0sz_0B8_ |
2337 | 0 | .unwrap() |
2338 | 0 | .id, |
2339 | 0 | ) |
2340 | | }; |
2341 | | |
2342 | 0 | tree.input_insert_block( |
2343 | 0 | Block { |
2344 | 0 | hash: header::hash_from_scale_encoded_header( |
2345 | 0 | &new_block.scale_encoded_header, |
2346 | 0 | ), |
2347 | 0 | height: header::decode( |
2348 | 0 | &new_block.scale_encoded_header, |
2349 | 0 | background.sync_service.block_number_bytes(), |
2350 | 0 | ) |
2351 | 0 | .unwrap() |
2352 | 0 | .number, // TODO: consider feeding the information from the sync service? |
2353 | 0 | scale_encoded_header: new_block.scale_encoded_header, |
2354 | 0 | }, |
2355 | 0 | parent_index, |
2356 | 0 | same_runtime_as_parent, |
2357 | 0 | new_block.is_new_best, |
2358 | 0 | ); |
2359 | | } |
2360 | 0 | Tree::FinalizedBlockRuntimeUnknown { tree, .. } => { |
2361 | 0 | // TODO: O(n) |
2362 | 0 | let parent_index = tree |
2363 | 0 | .input_output_iter_unordered() |
2364 | 0 | .find(|block| block.user_data.hash == new_block.parent_hash) Unexecuted instantiation: _RNCNCINvNtCsiGub1lfKphe_13smoldot_light15runtime_service14run_backgroundpE0sA_0B8_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sA_0B1c_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundpE0sA_0B8_ |
2365 | 0 | .unwrap() |
2366 | 0 | .id; |
2367 | 0 | tree.input_insert_block( |
2368 | 0 | Block { |
2369 | 0 | hash: header::hash_from_scale_encoded_header( |
2370 | 0 | &new_block.scale_encoded_header, |
2371 | 0 | ), |
2372 | 0 | height: header::decode( |
2373 | 0 | &new_block.scale_encoded_header, |
2374 | 0 | background.sync_service.block_number_bytes(), |
2375 | 0 | ) |
2376 | 0 | .unwrap() |
2377 | 0 | .number, // TODO: consider feeding the information from the sync service? |
2378 | 0 | scale_encoded_header: new_block.scale_encoded_header, |
2379 | 0 | }, |
2380 | 0 | Some(parent_index), |
2381 | 0 | same_runtime_as_parent, |
2382 | 0 | new_block.is_new_best, |
2383 | 0 | ); |
2384 | 0 | } |
2385 | | } |
2386 | | } |
2387 | | |
2388 | | WakeUpReason::Notification(sync_service::Notification::Finalized { |
2389 | 0 | hash, |
2390 | 0 | best_block_hash_if_changed, |
2391 | 0 | .. |
2392 | 0 | }) => { |
2393 | 0 | // Sync service has reported a finalized block. |
2394 | 0 |
|
2395 | 0 | log!( |
2396 | 0 | &background.platform, |
2397 | 0 | Trace, |
2398 | 0 | &background.log_target, |
2399 | 0 | "input-chain-finalized", |
2400 | 0 | block_hash = HashDisplay(&hash), |
2401 | 0 | best_block_hash = if let Some(best_block_hash) = best_block_hash_if_changed { |
2402 | 0 | Cow::Owned(HashDisplay(&best_block_hash).to_string()) |
2403 | | } else { |
2404 | 0 | Cow::Borrowed("<unchanged>") |
2405 | | } |
2406 | | ); |
2407 | | |
2408 | 0 | if let Some(best_block_hash) = best_block_hash_if_changed { |
2409 | 0 | match &mut background.tree { |
2410 | 0 | Tree::FinalizedBlockRuntimeKnown { tree, .. } => { |
2411 | 0 | let new_best_block = tree |
2412 | 0 | .input_output_iter_unordered() |
2413 | 0 | .find(|block| block.user_data.hash == best_block_hash) Unexecuted instantiation: _RNCNCINvNtCsiGub1lfKphe_13smoldot_light15runtime_service14run_backgroundpE0sB_0B8_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sB_0B1c_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundpE0sB_0B8_ |
2414 | 0 | .unwrap() |
2415 | 0 | .id; |
2416 | 0 | tree.input_set_best_block(Some(new_best_block)); |
2417 | 0 | } |
2418 | 0 | Tree::FinalizedBlockRuntimeUnknown { tree, .. } => { |
2419 | 0 | let new_best_block = tree |
2420 | 0 | .input_output_iter_unordered() |
2421 | 0 | .find(|block| block.user_data.hash == best_block_hash) Unexecuted instantiation: _RNCNCINvNtCsiGub1lfKphe_13smoldot_light15runtime_service14run_backgroundpE0sC_0B8_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sC_0B1c_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundpE0sC_0B8_ |
2422 | 0 | .unwrap() |
2423 | 0 | .id; |
2424 | 0 | tree.input_set_best_block(Some(new_best_block)); |
2425 | 0 | } |
2426 | | } |
2427 | 0 | } |
2428 | | |
2429 | 0 | match &mut background.tree { |
2430 | | Tree::FinalizedBlockRuntimeKnown { |
2431 | 0 | tree, |
2432 | 0 | finalized_block, |
2433 | 0 | .. |
2434 | 0 | } => { |
2435 | 0 | debug_assert_ne!(finalized_block.hash, hash); |
2436 | 0 | let node_to_finalize = tree |
2437 | 0 | .input_output_iter_unordered() |
2438 | 0 | .find(|block| block.user_data.hash == hash) Unexecuted instantiation: _RNCNCINvNtCsiGub1lfKphe_13smoldot_light15runtime_service14run_backgroundpE0sD_0B8_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sD_0B1c_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundpE0sD_0B8_ |
2439 | 0 | .unwrap() |
2440 | 0 | .id; |
2441 | 0 | tree.input_finalize(node_to_finalize); |
2442 | | } |
2443 | 0 | Tree::FinalizedBlockRuntimeUnknown { tree, .. } => { |
2444 | 0 | let node_to_finalize = tree |
2445 | 0 | .input_output_iter_unordered() |
2446 | 0 | .find(|block| block.user_data.hash == hash) Unexecuted instantiation: _RNCNCINvNtCsiGub1lfKphe_13smoldot_light15runtime_service14run_backgroundpE0sE_0B8_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sE_0B1c_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundpE0sE_0B8_ |
2447 | 0 | .unwrap() |
2448 | 0 | .id; |
2449 | 0 | tree.input_finalize(node_to_finalize); |
2450 | 0 | } |
2451 | | } |
2452 | | } |
2453 | | |
2454 | 0 | WakeUpReason::Notification(sync_service::Notification::BestBlockChanged { hash }) => { |
2455 | 0 | // Sync service has reported a change in the best block. |
2456 | 0 |
|
2457 | 0 | log!( |
2458 | 0 | &background.platform, |
2459 | 0 | Trace, |
2460 | 0 | &background.log_target, |
2461 | 0 | "input-chain-best-block-update", |
2462 | 0 | block_hash = HashDisplay(&hash) |
2463 | 0 | ); |
2464 | 0 |
|
2465 | 0 | match &mut background.tree { |
2466 | | Tree::FinalizedBlockRuntimeKnown { |
2467 | 0 | finalized_block, |
2468 | 0 | tree, |
2469 | | .. |
2470 | 0 | } => { |
2471 | 0 | let idx = if hash == finalized_block.hash { |
2472 | 0 | None |
2473 | | } else { |
2474 | 0 | Some( |
2475 | 0 | tree.input_output_iter_unordered() |
2476 | 0 | .find(|block| block.user_data.hash == hash) Unexecuted instantiation: _RNCNCINvNtCsiGub1lfKphe_13smoldot_light15runtime_service14run_backgroundpE0sF_0B8_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sF_0B1c_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundpE0sF_0B8_ |
2477 | 0 | .unwrap() |
2478 | 0 | .id, |
2479 | 0 | ) |
2480 | | }; |
2481 | 0 | tree.input_set_best_block(idx); |
2482 | | } |
2483 | 0 | Tree::FinalizedBlockRuntimeUnknown { tree, .. } => { |
2484 | 0 | let idx = tree |
2485 | 0 | .input_output_iter_unordered() |
2486 | 0 | .find(|block| block.user_data.hash == hash) Unexecuted instantiation: _RNCNCINvNtCsiGub1lfKphe_13smoldot_light15runtime_service14run_backgroundpE0sG_0B8_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sG_0B1c_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundpE0sG_0B8_ |
2487 | 0 | .unwrap() |
2488 | 0 | .id; |
2489 | 0 | tree.input_set_best_block(Some(idx)); |
2490 | 0 | } |
2491 | | } |
2492 | | } |
2493 | | |
2494 | | WakeUpReason::RuntimeDownloadFinished( |
2495 | 0 | async_op_id, |
2496 | 0 | Ok(( |
2497 | 0 | storage_code, |
2498 | 0 | storage_heap_pages, |
2499 | 0 | code_merkle_value, |
2500 | 0 | closest_ancestor_excluding, |
2501 | | )), |
2502 | | ) => { |
2503 | | // A runtime has successfully finished downloading. |
2504 | | |
2505 | 0 | let concerned_blocks = match &background.tree { |
2506 | 0 | Tree::FinalizedBlockRuntimeKnown { tree, .. } => { |
2507 | 0 | either::Left(tree.async_op_blocks(async_op_id)) |
2508 | | } |
2509 | 0 | Tree::FinalizedBlockRuntimeUnknown { tree, .. } => { |
2510 | 0 | either::Right(tree.async_op_blocks(async_op_id)) |
2511 | | } |
2512 | | } |
2513 | 0 | .format_with(", ", |block, fmt| fmt(&HashDisplay(&block.hash))) Unexecuted instantiation: _RNCNCINvNtCsiGub1lfKphe_13smoldot_light15runtime_service14run_backgroundpE0sH_0B8_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sH_0B1c_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundpE0sH_0B8_ |
2514 | 0 | .to_string(); |
2515 | 0 |
|
2516 | 0 | // Try to find an existing runtime identical to the one that has just been |
2517 | 0 | // downloaded. This loop is `O(n)`, but given that we expect this list to very |
2518 | 0 | // small (at most 1 or 2 elements), this is not a problem. |
2519 | 0 | let existing_runtime = background |
2520 | 0 | .runtimes |
2521 | 0 | .iter() |
2522 | 0 | .filter_map(|(_, rt)| rt.upgrade()) Unexecuted instantiation: _RNCNCINvNtCsiGub1lfKphe_13smoldot_light15runtime_service14run_backgroundpE0sI_0B8_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sI_0B1c_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundpE0sI_0B8_ |
2523 | 0 | .find(|rt| { |
2524 | 0 | rt.runtime_code == storage_code && rt.heap_pages == storage_heap_pages |
2525 | 0 | }); Unexecuted instantiation: _RNCNCINvNtCsiGub1lfKphe_13smoldot_light15runtime_service14run_backgroundpE0sJ_0B8_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sJ_0B1c_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundpE0sJ_0B8_ |
2526 | | |
2527 | | // If no identical runtime was found, try compiling the runtime. |
2528 | 0 | let runtime = if let Some(existing_runtime) = existing_runtime { |
2529 | 0 | log!( |
2530 | 0 | &background.platform, |
2531 | 0 | Debug, |
2532 | 0 | &background.log_target, |
2533 | 0 | "runtime-download-finish-compilation-cache-hit", |
2534 | 0 | block_hashes = concerned_blocks, |
2535 | 0 | ); |
2536 | 0 | existing_runtime |
2537 | | } else { |
2538 | 0 | let before_compilation = background.platform.now(); |
2539 | 0 | let runtime = compile_runtime( |
2540 | 0 | &background.platform, |
2541 | 0 | &background.log_target, |
2542 | 0 | &storage_code, |
2543 | 0 | &storage_heap_pages, |
2544 | 0 | ); |
2545 | 0 | let compilation_duration = background.platform.now() - before_compilation; |
2546 | 0 | log!( |
2547 | 0 | &background.platform, |
2548 | 0 | Debug, |
2549 | 0 | &background.log_target, |
2550 | 0 | "runtime-download-finish-compilation-cache-miss", |
2551 | 0 | ?compilation_duration, |
2552 | 0 | compilation_success = runtime.is_ok(), |
2553 | 0 | block_hashes = concerned_blocks, |
2554 | 0 | ); |
2555 | 0 | match &runtime { |
2556 | 0 | Ok(runtime) => { |
2557 | 0 | log!( |
2558 | 0 | &background.platform, |
2559 | 0 | Info, |
2560 | 0 | &background.log_target, |
2561 | 0 | format!( |
2562 | 0 | "Successfully compiled runtime. Spec version: {}. \ |
2563 | 0 | Size of `:code`: {}.", |
2564 | 0 | runtime.runtime_version().decode().spec_version, |
2565 | 0 | BytesDisplay( |
2566 | 0 | u64::try_from(storage_code.as_ref().map_or(0, |v| v.len())) Unexecuted instantiation: _RNCNCINvNtCsiGub1lfKphe_13smoldot_light15runtime_service14run_backgroundpE0sN_0B8_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sN_0B1c_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundpE0sN_0B8_ |
2567 | 0 | .unwrap() |
2568 | 0 | ) |
2569 | 0 | ) |
2570 | 0 | ); |
2571 | 0 | } |
2572 | 0 | Err(error) => { |
2573 | 0 | log!( |
2574 | 0 | &background.platform, |
2575 | 0 | Warn, |
2576 | 0 | &background.log_target, |
2577 | 0 | format!( |
2578 | 0 | "Failed to compile runtime. Size of `:code`: {}.\nError: {}\n\ |
2579 | 0 | This indicates an incompatibility between smoldot and \ |
2580 | 0 | the chain.", |
2581 | 0 | BytesDisplay( |
2582 | 0 | u64::try_from(storage_code.as_ref().map_or(0, |v| v.len())) Unexecuted instantiation: _RNCNCINvNtCsiGub1lfKphe_13smoldot_light15runtime_service14run_backgroundpE0sO_0B8_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sO_0B1c_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundpE0sO_0B8_ |
2583 | 0 | .unwrap() |
2584 | 0 | ), |
2585 | 0 | error |
2586 | 0 | ) |
2587 | 0 | ); |
2588 | 0 | } |
2589 | | } |
2590 | | |
2591 | 0 | let runtime = Arc::new(Runtime { |
2592 | 0 | heap_pages: storage_heap_pages, |
2593 | 0 | runtime_code: storage_code, |
2594 | 0 | runtime, |
2595 | 0 | code_merkle_value, |
2596 | 0 | closest_ancestor_excluding, |
2597 | 0 | }); |
2598 | 0 |
|
2599 | 0 | background.runtimes.insert(Arc::downgrade(&runtime)); |
2600 | 0 | runtime |
2601 | | }; |
2602 | | |
2603 | | // Insert the runtime into the tree. |
2604 | 0 | match &mut background.tree { |
2605 | 0 | Tree::FinalizedBlockRuntimeKnown { tree, .. } => { |
2606 | 0 | tree.async_op_finished(async_op_id, runtime); |
2607 | 0 | } |
2608 | 0 | Tree::FinalizedBlockRuntimeUnknown { tree, .. } => { |
2609 | 0 | tree.async_op_finished(async_op_id, Some(runtime)); |
2610 | 0 | } |
2611 | | } |
2612 | | } |
2613 | | |
2614 | 0 | WakeUpReason::RuntimeDownloadFinished(async_op_id, Err(error)) => { |
2615 | | // A runtime download has failed. |
2616 | | |
2617 | 0 | let concerned_blocks = match &background.tree { |
2618 | 0 | Tree::FinalizedBlockRuntimeKnown { tree, .. } => { |
2619 | 0 | either::Left(tree.async_op_blocks(async_op_id)) |
2620 | | } |
2621 | 0 | Tree::FinalizedBlockRuntimeUnknown { tree, .. } => { |
2622 | 0 | either::Right(tree.async_op_blocks(async_op_id)) |
2623 | | } |
2624 | | } |
2625 | 0 | .format_with(", ", |block, fmt| fmt(&HashDisplay(&block.hash))) Unexecuted instantiation: _RNCNCINvNtCsiGub1lfKphe_13smoldot_light15runtime_service14run_backgroundpE0sK_0B8_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0sK_0B1c_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundpE0sK_0B8_ |
2626 | 0 | .to_string(); |
2627 | 0 |
|
2628 | 0 | log!( |
2629 | 0 | &background.platform, |
2630 | 0 | Debug, |
2631 | 0 | &background.log_target, |
2632 | 0 | "runtime-download-error", |
2633 | 0 | block_hashes = concerned_blocks, |
2634 | 0 | ?error |
2635 | 0 | ); |
2636 | 0 | if !error.is_network_problem() { |
2637 | 0 | log!( |
2638 | 0 | &background.platform, |
2639 | 0 | Warn, |
2640 | 0 | &background.log_target, |
2641 | 0 | format!( |
2642 | 0 | "Failed to download :code and :heappages of blocks {}: {}", |
2643 | 0 | concerned_blocks, error |
2644 | 0 | ) |
2645 | 0 | ); |
2646 | 0 | } |
2647 | | |
2648 | 0 | match &mut background.tree { |
2649 | 0 | Tree::FinalizedBlockRuntimeKnown { tree, .. } => { |
2650 | 0 | tree.async_op_failure(async_op_id, &background.platform.now()); |
2651 | 0 | } |
2652 | 0 | Tree::FinalizedBlockRuntimeUnknown { tree, .. } => { |
2653 | 0 | tree.async_op_failure(async_op_id, &background.platform.now()); |
2654 | 0 | } |
2655 | | } |
2656 | | } |
2657 | | } |
2658 | | } |
2659 | 0 | } Unexecuted instantiation: _RNCINvNtCsiGub1lfKphe_13smoldot_light15runtime_service14run_backgroundpE0B6_ Unexecuted instantiation: _RNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0B1a_ Unexecuted instantiation: _RNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service14run_backgroundpE0B6_ |
2660 | | |
2661 | 0 | #[derive(Debug, Clone, derive_more::Display)] Unexecuted instantiation: _RNvXsG_NtCsiGub1lfKphe_13smoldot_light15runtime_serviceNtB5_20RuntimeDownloadErrorNtNtCsaYZPK01V26L_4core3fmt7Display3fmt Unexecuted instantiation: _RNvXsG_NtCsih6EgvAwZF2_13smoldot_light15runtime_serviceNtB5_20RuntimeDownloadErrorNtNtCsaYZPK01V26L_4core3fmt7Display3fmt |
2662 | | enum RuntimeDownloadError { |
2663 | | #[display(fmt = "{_0}")] |
2664 | | StorageQuery(sync_service::StorageQueryError), |
2665 | | #[display(fmt = "Couldn't decode header: {_0}")] |
2666 | | InvalidHeader(header::Error), |
2667 | | } |
2668 | | |
2669 | | impl RuntimeDownloadError { |
2670 | | /// Returns `true` if this is caused by networking issues, as opposed to a consensus-related |
2671 | | /// issue. |
2672 | 0 | fn is_network_problem(&self) -> bool { |
2673 | 0 | match self { |
2674 | 0 | RuntimeDownloadError::StorageQuery(err) => err.is_network_problem(), |
2675 | 0 | RuntimeDownloadError::InvalidHeader(_) => false, |
2676 | | } |
2677 | 0 | } Unexecuted instantiation: _RNvMs0_NtCsiGub1lfKphe_13smoldot_light15runtime_serviceNtB5_20RuntimeDownloadError18is_network_problem Unexecuted instantiation: _RNvMs0_NtCsih6EgvAwZF2_13smoldot_light15runtime_serviceNtB5_20RuntimeDownloadError18is_network_problem |
2678 | | } |
2679 | | |
2680 | | struct Background<TPlat: PlatformRef> { |
2681 | | /// Target to use for all the logs of this service. |
2682 | | log_target: String, |
2683 | | |
2684 | | /// See [`Config::platform`]. |
2685 | | platform: TPlat, |
2686 | | |
2687 | | /// See [`Config::sync_service`]. |
2688 | | sync_service: Arc<sync_service::SyncService<TPlat>>, |
2689 | | |
2690 | | /// See [`Config::network_service`]. |
2691 | | network_service: Arc<network_service::NetworkServiceChain<TPlat>>, |
2692 | | |
2693 | | /// Receiver for messages to the background task. |
2694 | | to_background: Pin<Box<async_channel::Receiver<ToBackground<TPlat>>>>, |
2695 | | |
2696 | | /// Sending side of [`Background::to_background`]. |
2697 | | to_background_tx: async_channel::WeakSender<ToBackground<TPlat>>, |
2698 | | |
2699 | | /// Identifier of the next subscription for |
2700 | | /// [`Tree::FinalizedBlockRuntimeKnown::all_blocks_subscriptions`]. |
2701 | | /// |
2702 | | /// To avoid race conditions, subscription IDs are never used, even if we switch back to |
2703 | | /// [`Tree::FinalizedBlockRuntimeUnknown`]. |
2704 | | next_subscription_id: u64, |
2705 | | |
2706 | | /// List of runtimes referenced by the tree in [`Tree`] and by |
2707 | | /// [`Tree::FinalizedBlockRuntimeKnown::pinned_blocks`]. |
2708 | | /// |
2709 | | /// Might contains obsolete values (i.e. stale `Weak`s) and thus must be cleaned from time to |
2710 | | /// time. |
2711 | | /// |
2712 | | /// Because this list shouldn't contain many entries, it is acceptable to iterate over all |
2713 | | /// the elements. |
2714 | | runtimes: slab::Slab<Weak<Runtime>>, |
2715 | | |
2716 | | /// Tree of blocks received from the sync service. Keeps track of which block has been |
2717 | | /// reported to the outer API. |
2718 | | tree: Tree<TPlat>, |
2719 | | |
2720 | | /// List of subscription attempts started with |
2721 | | /// [`Tree::FinalizedBlockRuntimeKnown::all_blocks_subscriptions`]. |
2722 | | /// |
2723 | | /// When in the [`Tree::FinalizedBlockRuntimeKnown`] state, a [`SubscribeAll`] is constructed |
2724 | | /// and sent back for each of these senders. |
2725 | | /// When in the [`Tree::FinalizedBlockRuntimeUnknown`] state, the senders patiently wait here. |
2726 | | pending_subscriptions: VecDeque<ToBackgroundSubscribeAll<TPlat>>, |
2727 | | |
2728 | | /// Stream of notifications coming from the sync service. `None` if not subscribed yet. |
2729 | | blocks_stream: Option<Pin<Box<dyn Stream<Item = sync_service::Notification> + Send>>>, |
2730 | | |
2731 | | /// List of runtimes currently being downloaded from the network. |
2732 | | /// For each item, the download id, storage value of `:code`, storage value of `:heappages`, |
2733 | | /// and Merkle value and closest ancestor of `:code`. |
2734 | | // TODO: use struct |
2735 | | runtime_downloads: stream::FuturesUnordered< |
2736 | | future::BoxFuture< |
2737 | | 'static, |
2738 | | ( |
2739 | | async_tree::AsyncOpId, |
2740 | | Result< |
2741 | | ( |
2742 | | Option<Vec<u8>>, |
2743 | | Option<Vec<u8>>, |
2744 | | Option<Vec<u8>>, |
2745 | | Option<Vec<Nibble>>, |
2746 | | ), |
2747 | | RuntimeDownloadError, |
2748 | | >, |
2749 | | ), |
2750 | | >, |
2751 | | >, |
2752 | | |
2753 | | /// List of actions to perform to progress runtime calls requested by the frontend. |
2754 | | progress_runtime_call_requests: |
2755 | | stream::FuturesUnordered<future::BoxFuture<'static, ProgressRuntimeCallRequest>>, |
2756 | | } |
2757 | | |
2758 | | enum Tree<TPlat: PlatformRef> { |
2759 | | FinalizedBlockRuntimeKnown { |
2760 | | /// Tree of blocks. Holds the state of the download of everything. Always `Some` when the |
2761 | | /// `Mutex` is being locked. Temporarily switched to `None` during some operations. |
2762 | | /// |
2763 | | /// The asynchronous operation user data is a `usize` corresponding to the index within |
2764 | | /// [`Background::runtimes`]. |
2765 | | tree: async_tree::AsyncTree<TPlat::Instant, Block, Arc<Runtime>>, |
2766 | | |
2767 | | /// Finalized block. Outside of the tree. |
2768 | | finalized_block: Block, |
2769 | | |
2770 | | /// List of senders that get notified when new blocks arrive. |
2771 | | /// See [`RuntimeService::subscribe_all`]. Alongside with each sender, the number of pinned |
2772 | | /// finalized or non-canonical blocks remaining for this subscription. |
2773 | | /// |
2774 | | /// Keys are assigned from [`Background::next_subscription_id`]. |
2775 | | all_blocks_subscriptions: hashbrown::HashMap< |
2776 | | u64, |
2777 | | (async_channel::Sender<Notification>, usize), |
2778 | | fnv::FnvBuildHasher, |
2779 | | >, |
2780 | | |
2781 | | /// List of pinned blocks. |
2782 | | /// |
2783 | | /// Every time a block is reported to the API user, it is inserted in this map. The block |
2784 | | /// is inserted after it has been pushed in the channel, but before it is pulled. |
2785 | | /// Therefore, if the channel is closed it is the background that needs to purge all |
2786 | | /// blocks from this container that are no longer relevant. |
2787 | | /// |
2788 | | /// Keys are `(subscription_id, block_hash)`. Values are indices within |
2789 | | /// [`Background::runtimes`], state trie root hashes, block numbers, and whether the block |
2790 | | /// is non-finalized and part of the canonical chain. |
2791 | | pinned_blocks: BTreeMap<(u64, [u8; 32]), PinnedBlock>, |
2792 | | }, |
2793 | | FinalizedBlockRuntimeUnknown { |
2794 | | /// Tree of blocks. Holds the state of the download of everything. Always `Some` when the |
2795 | | /// `Mutex` is being locked. Temporarily switched to `None` during some operations. |
2796 | | /// |
2797 | | /// The finalized block according to the [`async_tree::AsyncTree`] is actually a dummy. |
2798 | | /// The "real" finalized block is a non-finalized block within this tree. |
2799 | | /// |
2800 | | /// The asynchronous operation user data is a `usize` corresponding to the index within |
2801 | | /// [`Background::runtimes`]. The asynchronous operation user data is `None` for the dummy |
2802 | | /// finalized block. |
2803 | | // TODO: explain better |
2804 | | tree: async_tree::AsyncTree<TPlat::Instant, Block, Option<Arc<Runtime>>>, |
2805 | | }, |
2806 | | } |
2807 | | |
2808 | | /// See [`Background::progress_runtime_call_requests`]. |
2809 | | enum ProgressRuntimeCallRequest { |
2810 | | /// Must start the first call proof request. |
2811 | | Initialize(RuntimeCallRequest), |
2812 | | /// A call proof request has finished and the runtime call can be advanced. |
2813 | | CallProofRequestDone { |
2814 | | /// Outcome of the latest call proof request. |
2815 | | result: Result<network_service::EncodedMerkleProof, network_service::CallProofRequestError>, |
2816 | | /// Identity of the peer the call proof request was made against. |
2817 | | call_proof_sender: network_service::PeerId, |
2818 | | operation: RuntimeCallRequest, |
2819 | | }, |
2820 | | } |
2821 | | |
2822 | | /// See [`ProgressRuntimeCallRequest`]. |
2823 | | struct RuntimeCallRequest { |
2824 | | block_hash: [u8; 32], |
2825 | | block_number: u64, |
2826 | | block_state_trie_root_hash: [u8; 32], |
2827 | | function_name: String, |
2828 | | /// Version of the API that was found. `Some` if and only if an API requirement was passed. |
2829 | | api_version: Option<u32>, |
2830 | | parameters_vectored: Vec<u8>, |
2831 | | runtime: executor::host::HostVmPrototype, |
2832 | | total_attempts: u32, |
2833 | | timeout_per_request: Duration, |
2834 | | inaccessible_errors: Vec<RuntimeCallInaccessibleError>, |
2835 | | result_tx: oneshot::Sender<Result<RuntimeCallSuccess, RuntimeCallError>>, |
2836 | | } |
2837 | | |
2838 | | struct Runtime { |
2839 | | /// Successfully-compiled runtime and all its information. Can contain an error if an error |
2840 | | /// happened, including a problem when obtaining the runtime specs. |
2841 | | runtime: Result<executor::host::HostVmPrototype, RuntimeError>, |
2842 | | |
2843 | | /// Merkle value of the `:code` trie node. |
2844 | | /// |
2845 | | /// Can be `None` if the storage is empty, in which case the runtime will have failed to |
2846 | | /// build. |
2847 | | code_merkle_value: Option<Vec<u8>>, |
2848 | | |
2849 | | /// Closest ancestor of the `:code` key except for `:code` itself. |
2850 | | closest_ancestor_excluding: Option<Vec<Nibble>>, |
2851 | | |
2852 | | /// Undecoded storage value of `:code` corresponding to the [`Runtime::runtime`] |
2853 | | /// field. |
2854 | | /// |
2855 | | /// Can be `None` if the storage is empty, in which case the runtime will have failed to |
2856 | | /// build. |
2857 | | // TODO: consider storing hash instead |
2858 | | runtime_code: Option<Vec<u8>>, |
2859 | | |
2860 | | /// Undecoded storage value of `:heappages` corresponding to the |
2861 | | /// [`Runtime::runtime`] field. |
2862 | | /// |
2863 | | /// Can be `None` if the storage is empty, in which case the runtime will have failed to |
2864 | | /// build. |
2865 | | // TODO: consider storing hash instead |
2866 | | heap_pages: Option<Vec<u8>>, |
2867 | | } |
2868 | | |
2869 | 0 | fn compile_runtime<TPlat: PlatformRef>( |
2870 | 0 | platform: &TPlat, |
2871 | 0 | log_target: &str, |
2872 | 0 | code: &Option<Vec<u8>>, |
2873 | 0 | heap_pages: &Option<Vec<u8>>, |
2874 | 0 | ) -> Result<executor::host::HostVmPrototype, RuntimeError> { |
2875 | | // Parameters for `HostVmPrototype::new`. |
2876 | 0 | let module = code.as_ref().ok_or(RuntimeError::CodeNotFound)?; |
2877 | 0 | let heap_pages = executor::storage_heap_pages_to_value(heap_pages.as_deref()) |
2878 | 0 | .map_err(RuntimeError::InvalidHeapPages)?; |
2879 | 0 | let exec_hint = executor::vm::ExecHint::CompileWithNonDeterministicValidation; |
2880 | 0 |
|
2881 | 0 | // We try once with `allow_unresolved_imports: false`. If this fails due to unresolved |
2882 | 0 | // import, we try again but with `allowed_unresolved_imports: true`. |
2883 | 0 | // Having unresolved imports might cause errors later on, for example when validating |
2884 | 0 | // transactions or getting the parachain heads, but for now we continue the execution |
2885 | 0 | // and print a warning. |
2886 | 0 | match executor::host::HostVmPrototype::new(executor::host::Config { |
2887 | 0 | module, |
2888 | 0 | heap_pages, |
2889 | 0 | exec_hint, |
2890 | 0 | allow_unresolved_imports: false, |
2891 | 0 | }) { |
2892 | 0 | Ok(vm) => return Ok(vm), |
2893 | | Err(executor::host::NewErr::VirtualMachine( |
2894 | | executor::vm::NewErr::UnresolvedFunctionImport { |
2895 | 0 | function, |
2896 | 0 | module_name, |
2897 | 0 | }, |
2898 | 0 | )) => { |
2899 | 0 | match executor::host::HostVmPrototype::new(executor::host::Config { |
2900 | 0 | module, |
2901 | 0 | heap_pages, |
2902 | 0 | exec_hint, |
2903 | 0 | allow_unresolved_imports: true, |
2904 | 0 | }) { |
2905 | 0 | Ok(vm) => { |
2906 | 0 | log!( |
2907 | 0 | platform, |
2908 | 0 | Warn, |
2909 | 0 | log_target, |
2910 | 0 | format!( |
2911 | 0 | "Unresolved host function in runtime: `{}`:`{}`. Smoldot might \ |
2912 | 0 | encounter errors later on. Please report this issue in \ |
2913 | 0 | https://github.com/smol-dot/smoldot", |
2914 | 0 | module_name, function |
2915 | 0 | ) |
2916 | 0 | ); |
2917 | 0 |
|
2918 | 0 | Ok(vm) |
2919 | | } |
2920 | | Err(executor::host::NewErr::VirtualMachine( |
2921 | | executor::vm::NewErr::UnresolvedFunctionImport { .. }, |
2922 | 0 | )) => unreachable!(), |
2923 | 0 | Err(error) => { |
2924 | 0 | // It's still possible that errors other than an unresolved host |
2925 | 0 | // function happen. |
2926 | 0 | Err(RuntimeError::Build(error)) |
2927 | | } |
2928 | | } |
2929 | | } |
2930 | 0 | Err(error) => Err(RuntimeError::Build(error)), |
2931 | | } |
2932 | 0 | } Unexecuted instantiation: _RINvNtCsiGub1lfKphe_13smoldot_light15runtime_service15compile_runtimepEB4_ Unexecuted instantiation: _RINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service15compile_runtimeNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefEB19_ Unexecuted instantiation: _RINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service15compile_runtimepEB4_ |
2933 | | |
2934 | | /// Returns `true` if the block can be assumed to have the same runtime as its parent. |
2935 | 0 | fn same_runtime_as_parent(header: &[u8], block_number_bytes: usize) -> bool { |
2936 | 0 | match header::decode(header, block_number_bytes) { |
2937 | 0 | Ok(h) => !h.digest.has_runtime_environment_updated(), |
2938 | 0 | Err(_) => false, |
2939 | | } |
2940 | 0 | } Unexecuted instantiation: _RNvNtCsiGub1lfKphe_13smoldot_light15runtime_service22same_runtime_as_parent Unexecuted instantiation: _RNvNtCsih6EgvAwZF2_13smoldot_light15runtime_service22same_runtime_as_parent |
2941 | | |
2942 | 0 | fn download_runtime<TPlat: PlatformRef>( |
2943 | 0 | sync_service: Arc<sync_service::SyncService<TPlat>>, |
2944 | 0 | block_hash: [u8; 32], |
2945 | 0 | scale_encoded_header: &[u8], |
2946 | 0 | ) -> impl future::Future< |
2947 | 0 | Output = Result< |
2948 | 0 | ( |
2949 | 0 | Option<Vec<u8>>, |
2950 | 0 | Option<Vec<u8>>, |
2951 | 0 | Option<Vec<u8>>, |
2952 | 0 | Option<Vec<Nibble>>, |
2953 | 0 | ), |
2954 | 0 | RuntimeDownloadError, |
2955 | 0 | >, |
2956 | 0 | > { |
2957 | | // In order to perform the download, we need to known the state root hash of the |
2958 | | // block in question, which requires decoding the block. If the decoding fails, |
2959 | | // we report that the asynchronous operation has failed with the hope that this |
2960 | | // block gets pruned in the future. |
2961 | 0 | let block_info = match header::decode(scale_encoded_header, sync_service.block_number_bytes()) { |
2962 | 0 | Ok(decoded_header) => Ok((*decoded_header.state_root, decoded_header.number)), |
2963 | 0 | Err(error) => Err(RuntimeDownloadError::InvalidHeader(error)), |
2964 | | }; |
2965 | | |
2966 | 0 | async move { |
2967 | 0 | let (state_root, block_number) = block_info?; |
2968 | | |
2969 | 0 | let mut storage_code = None; |
2970 | 0 | let mut storage_heap_pages = None; |
2971 | 0 | let mut code_merkle_value = None; |
2972 | 0 | let mut code_closest_ancestor_excluding = None; |
2973 | | |
2974 | 0 | let mut query = sync_service |
2975 | 0 | .clone() |
2976 | 0 | .storage_query( |
2977 | 0 | block_number, |
2978 | 0 | block_hash, |
2979 | 0 | state_root, |
2980 | 0 | [ |
2981 | 0 | sync_service::StorageRequestItem { |
2982 | 0 | key: b":code".to_vec(), |
2983 | 0 | ty: sync_service::StorageRequestItemTy::ClosestDescendantMerkleValue, |
2984 | 0 | }, |
2985 | 0 | sync_service::StorageRequestItem { |
2986 | 0 | key: b":code".to_vec(), |
2987 | 0 | ty: sync_service::StorageRequestItemTy::Value, |
2988 | 0 | }, |
2989 | 0 | sync_service::StorageRequestItem { |
2990 | 0 | key: b":heappages".to_vec(), |
2991 | 0 | ty: sync_service::StorageRequestItemTy::Value, |
2992 | 0 | }, |
2993 | 0 | ] |
2994 | 0 | .into_iter(), |
2995 | 0 | 3, |
2996 | 0 | Duration::from_secs(20), |
2997 | 0 | NonZeroU32::new(3).unwrap(), |
2998 | 0 | ) |
2999 | 0 | .advance() |
3000 | 0 | .await; |
3001 | | |
3002 | | loop { |
3003 | 0 | match query { |
3004 | | sync_service::StorageQueryProgress::Finished => { |
3005 | 0 | break Ok(( |
3006 | 0 | storage_code, |
3007 | 0 | storage_heap_pages, |
3008 | 0 | code_merkle_value, |
3009 | 0 | code_closest_ancestor_excluding, |
3010 | 0 | )) |
3011 | | } |
3012 | | sync_service::StorageQueryProgress::Progress { |
3013 | | request_index: 0, |
3014 | | item: |
3015 | | sync_service::StorageResultItem::ClosestDescendantMerkleValue { |
3016 | 0 | closest_descendant_merkle_value, |
3017 | 0 | found_closest_ancestor_excluding, |
3018 | 0 | .. |
3019 | 0 | }, |
3020 | 0 | query: next, |
3021 | 0 | } => { |
3022 | 0 | code_merkle_value = closest_descendant_merkle_value; |
3023 | 0 | code_closest_ancestor_excluding = found_closest_ancestor_excluding; |
3024 | 0 | query = next.advance().await; |
3025 | | } |
3026 | | sync_service::StorageQueryProgress::Progress { |
3027 | | request_index: 1, |
3028 | 0 | item: sync_service::StorageResultItem::Value { value, .. }, |
3029 | 0 | query: next, |
3030 | 0 | } => { |
3031 | 0 | storage_code = value; |
3032 | 0 | query = next.advance().await; |
3033 | | } |
3034 | | sync_service::StorageQueryProgress::Progress { |
3035 | | request_index: 2, |
3036 | 0 | item: sync_service::StorageResultItem::Value { value, .. }, |
3037 | 0 | query: next, |
3038 | 0 | } => { |
3039 | 0 | storage_heap_pages = value; |
3040 | 0 | query = next.advance().await; |
3041 | | } |
3042 | 0 | sync_service::StorageQueryProgress::Progress { .. } => unreachable!(), |
3043 | 0 | sync_service::StorageQueryProgress::Error(error) => { |
3044 | 0 | break Err(RuntimeDownloadError::StorageQuery(error)) |
3045 | | } |
3046 | | } |
3047 | | } |
3048 | 0 | } Unexecuted instantiation: _RNCINvNtCsiGub1lfKphe_13smoldot_light15runtime_service16download_runtimepE0B6_ Unexecuted instantiation: _RNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service16download_runtimeNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0B1c_ Unexecuted instantiation: _RNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service16download_runtimepE0B6_ |
3049 | 0 | } Unexecuted instantiation: _RINvNtCsiGub1lfKphe_13smoldot_light15runtime_service16download_runtimepEB4_ Unexecuted instantiation: _RINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service16download_runtimeNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefEB1a_ Unexecuted instantiation: _RINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service16download_runtimepEB4_ |
3050 | | |
3051 | | /// Tries to perform a runtime call using the given call proof. |
3052 | | /// |
3053 | | /// This function can have three possible outcomes: success, failure because the call proof is |
3054 | | /// invalid/incomplete, or failure because the execution fails. |
3055 | | /// |
3056 | | /// This function is async in order to periodically yield during the execution. |
3057 | 0 | async fn runtime_call_single_attempt<TPlat: PlatformRef>( |
3058 | 0 | platform: &TPlat, |
3059 | 0 | runtime: executor::host::HostVmPrototype, |
3060 | 0 | function_name: &str, |
3061 | 0 | parameters_vectored: &[u8], |
3062 | 0 | block_state_trie_root_hash: &[u8; 32], |
3063 | 0 | call_proof: &[u8], |
3064 | 0 | ) -> ( |
3065 | 0 | SingleRuntimeCallTiming, |
3066 | 0 | Result<Vec<u8>, SingleRuntimeCallAttemptError>, |
3067 | 0 | ) { Unexecuted instantiation: _RINvNtCsiGub1lfKphe_13smoldot_light15runtime_service27runtime_call_single_attemptpEB4_ Unexecuted instantiation: _RINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service27runtime_call_single_attemptNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefEB1l_ Unexecuted instantiation: _RINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service27runtime_call_single_attemptpEB4_ |
3068 | 0 | // Try to decode the proof. Succeed just means that the proof has the correct |
3069 | 0 | // encoding, and doesn't guarantee that the proof has all the necessary |
3070 | 0 | // entries. |
3071 | 0 | let call_proof = trie::proof_decode::decode_and_verify_proof(trie::proof_decode::Config { |
3072 | 0 | proof: call_proof, |
3073 | 0 | }); |
3074 | 0 |
|
3075 | 0 | // Keep track of the total time taken by the runtime call attempt. |
3076 | 0 | let mut timing = SingleRuntimeCallTiming { |
3077 | 0 | virtual_machine_call_duration: Duration::new(0, 0), |
3078 | 0 | proof_access_duration: Duration::new(0, 0), |
3079 | 0 | }; |
3080 | 0 |
|
3081 | 0 | // Attempt the runtime call. |
3082 | 0 | // If the call succeed, we interrupt the flow and `continue`. |
3083 | 0 | let runtime_call_duration_before = platform.now(); |
3084 | 0 | let mut call = match executor::runtime_call::run(executor::runtime_call::Config { |
3085 | 0 | virtual_machine: runtime, |
3086 | 0 | function_to_call: function_name, |
3087 | 0 | parameter: iter::once(parameters_vectored), |
3088 | 0 | storage_proof_size_behavior: |
3089 | 0 | executor::runtime_call::StorageProofSizeBehavior::proof_recording_disabled(), |
3090 | 0 | storage_main_trie_changes: Default::default(), |
3091 | 0 | max_log_level: 0, |
3092 | 0 | calculate_trie_changes: false, |
3093 | 0 | }) { |
3094 | 0 | Ok(call) => call, |
3095 | 0 | Err((error, _)) => { |
3096 | 0 | // If starting the execution triggers an error, then the runtime call cannot |
3097 | 0 | // possibly succeed. |
3098 | 0 | // This can happen for example because the requested function doesn't exist. |
3099 | 0 | return ( |
3100 | 0 | timing, |
3101 | 0 | Err(SingleRuntimeCallAttemptError::Execution( |
3102 | 0 | RuntimeCallExecutionError::Start(error), |
3103 | 0 | )), |
3104 | 0 | ); |
3105 | | } |
3106 | | }; |
3107 | 0 | timing.virtual_machine_call_duration += platform.now() - runtime_call_duration_before; |
3108 | | |
3109 | | loop { |
3110 | 0 | let call_proof = match &call_proof { |
3111 | 0 | Ok(p) => p, |
3112 | 0 | Err(error) => { |
3113 | 0 | return ( |
3114 | 0 | timing, |
3115 | 0 | Err(SingleRuntimeCallAttemptError::Inaccessible( |
3116 | 0 | RuntimeCallInaccessibleError::InvalidCallProof(error.clone()), |
3117 | 0 | )), |
3118 | 0 | ); |
3119 | | } |
3120 | | }; |
3121 | | |
3122 | | // Yield once at every iteration. This avoids monopolizing the CPU for |
3123 | | // too long. |
3124 | 0 | futures_lite::future::yield_now().await; |
3125 | | |
3126 | 0 | let child_trie = match call { |
3127 | 0 | executor::runtime_call::RuntimeCall::Finished(Ok(finished)) => { |
3128 | 0 | // Execution finished successfully. |
3129 | 0 | // This is the happy path. |
3130 | 0 | let output = finished.virtual_machine.value().as_ref().to_owned(); |
3131 | 0 | return (timing, Ok(output)); |
3132 | | } |
3133 | 0 | executor::runtime_call::RuntimeCall::Finished(Err(error)) => { |
3134 | 0 | // Execution finished with an error. |
3135 | 0 | return ( |
3136 | 0 | timing, |
3137 | 0 | Err(SingleRuntimeCallAttemptError::Execution( |
3138 | 0 | RuntimeCallExecutionError::Execution(error.detail), |
3139 | 0 | )), |
3140 | 0 | ); |
3141 | | } |
3142 | 0 | executor::runtime_call::RuntimeCall::StorageGet(ref get) => { |
3143 | 0 | get.child_trie().map(|c| c.as_ref().to_owned()) // TODO: overhead Unexecuted instantiation: _RNCNCINvNtCsiGub1lfKphe_13smoldot_light15runtime_service27runtime_call_single_attemptpE00B8_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service27runtime_call_single_attemptNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE00B1p_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service27runtime_call_single_attemptpE00B8_ |
3144 | | } |
3145 | 0 | executor::runtime_call::RuntimeCall::ClosestDescendantMerkleValue(ref mv) => { |
3146 | 0 | mv.child_trie().map(|c| c.as_ref().to_owned()) Unexecuted instantiation: _RNCNCINvNtCsiGub1lfKphe_13smoldot_light15runtime_service27runtime_call_single_attemptpE0s_0B8_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service27runtime_call_single_attemptNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0s_0B1p_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service27runtime_call_single_attemptpE0s_0B8_ |
3147 | | } // TODO: overhead |
3148 | 0 | executor::runtime_call::RuntimeCall::NextKey(ref nk) => { |
3149 | 0 | nk.child_trie().map(|c| c.as_ref().to_owned()) // TODO: overhead Unexecuted instantiation: _RNCNCINvNtCsiGub1lfKphe_13smoldot_light15runtime_service27runtime_call_single_attemptpE0s0_0B8_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service27runtime_call_single_attemptNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0s0_0B1p_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service27runtime_call_single_attemptpE0s0_0B8_ |
3150 | | } |
3151 | 0 | executor::runtime_call::RuntimeCall::SignatureVerification(r) => { |
3152 | 0 | let runtime_call_duration_before = platform.now(); |
3153 | 0 | call = r.verify_and_resume(); |
3154 | 0 | timing.virtual_machine_call_duration += |
3155 | 0 | platform.now() - runtime_call_duration_before; |
3156 | 0 | continue; |
3157 | | } |
3158 | 0 | executor::runtime_call::RuntimeCall::LogEmit(r) => { |
3159 | 0 | // Logs are ignored. |
3160 | 0 | let runtime_call_duration_before = platform.now(); |
3161 | 0 | call = r.resume(); |
3162 | 0 | timing.virtual_machine_call_duration += |
3163 | 0 | platform.now() - runtime_call_duration_before; |
3164 | 0 | continue; |
3165 | | } |
3166 | | executor::runtime_call::RuntimeCall::Offchain(_) => { |
3167 | | // Forbidden host function called. |
3168 | 0 | return ( |
3169 | 0 | timing, |
3170 | 0 | Err(SingleRuntimeCallAttemptError::Execution( |
3171 | 0 | RuntimeCallExecutionError::ForbiddenHostFunction, |
3172 | 0 | )), |
3173 | 0 | ); |
3174 | | } |
3175 | 0 | executor::runtime_call::RuntimeCall::OffchainStorageSet(r) => { |
3176 | 0 | // Ignore offchain storage writes. |
3177 | 0 | let runtime_call_duration_before = platform.now(); |
3178 | 0 | call = r.resume(); |
3179 | 0 | timing.virtual_machine_call_duration += |
3180 | 0 | platform.now() - runtime_call_duration_before; |
3181 | 0 | continue; |
3182 | | } |
3183 | | }; |
3184 | | |
3185 | 0 | let proof_access_duration_before = platform.now(); |
3186 | 0 | let trie_root = if let Some(child_trie) = child_trie { |
3187 | | // TODO: allocation here, but probably not problematic |
3188 | | const PREFIX: &[u8] = b":child_storage:default:"; |
3189 | 0 | let mut key = Vec::with_capacity(PREFIX.len() + child_trie.len()); |
3190 | 0 | key.extend_from_slice(PREFIX); |
3191 | 0 | key.extend_from_slice(child_trie.as_ref()); |
3192 | 0 | match call_proof.storage_value(&block_state_trie_root_hash, &key) { |
3193 | | Err(_) => { |
3194 | 0 | return ( |
3195 | 0 | timing, |
3196 | 0 | Err(SingleRuntimeCallAttemptError::Inaccessible( |
3197 | 0 | RuntimeCallInaccessibleError::MissingProofEntry, |
3198 | 0 | )), |
3199 | 0 | ) |
3200 | | } |
3201 | 0 | Ok(None) => None, |
3202 | 0 | Ok(Some((value, _))) => match <&[u8; 32]>::try_from(value) { |
3203 | 0 | Ok(hash) => Some(hash), |
3204 | | Err(_) => { |
3205 | 0 | return ( |
3206 | 0 | timing, |
3207 | 0 | Err(SingleRuntimeCallAttemptError::Inaccessible( |
3208 | 0 | RuntimeCallInaccessibleError::MissingProofEntry, |
3209 | 0 | )), |
3210 | 0 | ) |
3211 | | } |
3212 | | }, |
3213 | | } |
3214 | | } else { |
3215 | 0 | Some(block_state_trie_root_hash) |
3216 | | }; |
3217 | | |
3218 | 0 | match call { |
3219 | 0 | executor::runtime_call::RuntimeCall::StorageGet(get) => { |
3220 | 0 | let storage_value = if let Some(trie_root) = trie_root { |
3221 | 0 | call_proof.storage_value(&trie_root, get.key().as_ref()) |
3222 | | } else { |
3223 | 0 | Ok(None) |
3224 | | }; |
3225 | 0 | let Ok(storage_value) = storage_value else { |
3226 | 0 | return ( |
3227 | 0 | timing, |
3228 | 0 | Err(SingleRuntimeCallAttemptError::Inaccessible( |
3229 | 0 | RuntimeCallInaccessibleError::MissingProofEntry, |
3230 | 0 | )), |
3231 | 0 | ); |
3232 | | }; |
3233 | 0 | timing.proof_access_duration += platform.now() - proof_access_duration_before; |
3234 | 0 |
|
3235 | 0 | let runtime_call_duration_before = platform.now(); |
3236 | 0 | call = get.inject_value(storage_value.map(|(val, vers)| (iter::once(val), vers))); Unexecuted instantiation: _RNCNCINvNtCsiGub1lfKphe_13smoldot_light15runtime_service27runtime_call_single_attemptpE0s1_0B8_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service27runtime_call_single_attemptNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0s1_0B1p_ Unexecuted instantiation: _RNCNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service27runtime_call_single_attemptpE0s1_0B8_ |
3237 | 0 | timing.virtual_machine_call_duration += |
3238 | 0 | platform.now() - runtime_call_duration_before; |
3239 | | } |
3240 | 0 | executor::runtime_call::RuntimeCall::ClosestDescendantMerkleValue(mv) => { |
3241 | 0 | let merkle_value = if let Some(trie_root) = trie_root { |
3242 | 0 | call_proof.closest_descendant_merkle_value(&trie_root, mv.key()) |
3243 | | } else { |
3244 | 0 | Ok(None) |
3245 | | }; |
3246 | 0 | let Ok(merkle_value) = merkle_value else { |
3247 | 0 | return ( |
3248 | 0 | timing, |
3249 | 0 | Err(SingleRuntimeCallAttemptError::Inaccessible( |
3250 | 0 | RuntimeCallInaccessibleError::MissingProofEntry, |
3251 | 0 | )), |
3252 | 0 | ); |
3253 | | }; |
3254 | 0 | timing.proof_access_duration += platform.now() - proof_access_duration_before; |
3255 | 0 |
|
3256 | 0 | let runtime_call_duration_before = platform.now(); |
3257 | 0 | call = mv.inject_merkle_value(merkle_value); |
3258 | 0 | timing.virtual_machine_call_duration += |
3259 | 0 | platform.now() - runtime_call_duration_before; |
3260 | | } |
3261 | 0 | executor::runtime_call::RuntimeCall::NextKey(nk) => { |
3262 | 0 | let next_key = if let Some(trie_root) = trie_root { |
3263 | 0 | call_proof.next_key( |
3264 | 0 | &trie_root, |
3265 | 0 | nk.key(), |
3266 | 0 | nk.or_equal(), |
3267 | 0 | nk.prefix(), |
3268 | 0 | nk.branch_nodes(), |
3269 | 0 | ) |
3270 | | } else { |
3271 | 0 | Ok(None) |
3272 | | }; |
3273 | 0 | let Ok(next_key) = next_key else { |
3274 | 0 | return ( |
3275 | 0 | timing, |
3276 | 0 | Err(SingleRuntimeCallAttemptError::Inaccessible( |
3277 | 0 | RuntimeCallInaccessibleError::MissingProofEntry, |
3278 | 0 | )), |
3279 | 0 | ); |
3280 | | }; |
3281 | 0 | timing.proof_access_duration += platform.now() - proof_access_duration_before; |
3282 | 0 |
|
3283 | 0 | let runtime_call_duration_before = platform.now(); |
3284 | 0 | call = nk.inject_key(next_key); |
3285 | 0 | timing.virtual_machine_call_duration += |
3286 | 0 | platform.now() - runtime_call_duration_before; |
3287 | | } |
3288 | 0 | _ => unreachable!(), |
3289 | | } |
3290 | | } |
3291 | 0 | } Unexecuted instantiation: _RNCINvNtCsiGub1lfKphe_13smoldot_light15runtime_service27runtime_call_single_attemptpE0B6_ Unexecuted instantiation: _RNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service27runtime_call_single_attemptNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE0B1n_ Unexecuted instantiation: _RNCINvNtCsih6EgvAwZF2_13smoldot_light15runtime_service27runtime_call_single_attemptpE0B6_ |
3292 | | |
3293 | | /// See [`runtime_call_single_attempt`]. |
3294 | | #[derive(Debug, Clone)] |
3295 | | struct SingleRuntimeCallTiming { |
3296 | | /// Time spent execution the virtual machine. |
3297 | | virtual_machine_call_duration: Duration, |
3298 | | /// Time spent accessing the call proof. |
3299 | | proof_access_duration: Duration, |
3300 | | } |
3301 | | |
3302 | | /// See [`runtime_call_single_attempt`]. |
3303 | 0 | #[derive(Debug, derive_more::Display, Clone)] Unexecuted instantiation: _RNvXsK_NtCsiGub1lfKphe_13smoldot_light15runtime_serviceNtB5_29SingleRuntimeCallAttemptErrorNtNtCsaYZPK01V26L_4core3fmt7Display3fmt Unexecuted instantiation: _RNvXsK_NtCsih6EgvAwZF2_13smoldot_light15runtime_serviceNtB5_29SingleRuntimeCallAttemptErrorNtNtCsaYZPK01V26L_4core3fmt7Display3fmt |
3304 | | enum SingleRuntimeCallAttemptError { |
3305 | | /// Error during the execution of the runtime. |
3306 | | /// |
3307 | | /// There is no point in trying the same call again, as it would result in the same error. |
3308 | | #[display(fmt = "Error during the execution of the runtime: {_0}")] |
3309 | | Execution(RuntimeCallExecutionError), |
3310 | | |
3311 | | /// Error trying to access the storage required for the runtime call. |
3312 | | /// |
3313 | | /// Trying the same call again might succeed. |
3314 | | #[display(fmt = "Error trying to access the storage required for the runtime call: {_0}")] |
3315 | | Inaccessible(RuntimeCallInaccessibleError), |
3316 | | } |