/__w/smoldot/smoldot/repo/light-base/src/sync_service/parachain.rs
Line | Count | Source |
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 | | use super::ToBackground; |
19 | | use crate::{log, network_service, platform::PlatformRef, runtime_service, util}; |
20 | | |
21 | | use alloc::{borrow::ToOwned as _, boxed::Box, format, string::String, sync::Arc, vec::Vec}; |
22 | | use core::{mem, num::NonZero, pin::Pin, time::Duration}; |
23 | | use futures_lite::FutureExt as _; |
24 | | use futures_util::{StreamExt as _, future, stream}; |
25 | | use hashbrown::HashMap; |
26 | | use itertools::Itertools as _; |
27 | | use smoldot::{ |
28 | | chain::async_tree, header, informant::HashDisplay, libp2p::PeerId, network::codec, sync::para, |
29 | | }; |
30 | | |
31 | | /// Starts a sync service background task to synchronize a parachain. |
32 | 0 | pub(super) async fn start_parachain<TPlat: PlatformRef>( |
33 | 0 | log_target: String, |
34 | 0 | platform: TPlat, |
35 | 0 | finalized_block_header: Vec<u8>, |
36 | 0 | block_number_bytes: usize, |
37 | 0 | relay_chain_sync: Arc<runtime_service::RuntimeService<TPlat>>, |
38 | 0 | parachain_id: u32, |
39 | 0 | from_foreground: Pin<Box<async_channel::Receiver<ToBackground>>>, |
40 | 0 | network_service: Arc<network_service::NetworkServiceChain<TPlat>>, |
41 | 0 | ) { Unexecuted instantiation: _RINvNtNtCsgnEOxJmACC4_13smoldot_light12sync_service9parachain15start_parachainpEB6_ Unexecuted instantiation: _RINvNtNtCs508CmrSPkZh_13smoldot_light12sync_service9parachain15start_parachainNtNtCs7snhGEhbuap_18smoldot_light_wasm8platform11PlatformRefEB1i_ Unexecuted instantiation: _RINvNtNtCs508CmrSPkZh_13smoldot_light12sync_service9parachain15start_parachainpEB6_ |
42 | | ParachainBackgroundTask { |
43 | 0 | log_target, |
44 | 0 | from_foreground, |
45 | 0 | block_number_bytes, |
46 | 0 | parachain_id, |
47 | 0 | from_network_service: None, |
48 | 0 | network_service, |
49 | 0 | obsolete_finalized_parahead: finalized_block_header, |
50 | 0 | sync_sources: HashMap::with_capacity_and_hasher( |
51 | | 0, |
52 | 0 | util::SipHasherBuild::new({ |
53 | 0 | let mut seed = [0; 16]; |
54 | 0 | platform.fill_random_bytes(&mut seed); |
55 | 0 | seed |
56 | | }), |
57 | | ), |
58 | | subscription_state: ParachainBackgroundState::NotSubscribed { |
59 | 0 | all_subscriptions: Vec::new(), |
60 | | subscribe_future: { |
61 | 0 | let relay_chain_sync = relay_chain_sync.clone(); |
62 | 0 | Box::pin(async move { |
63 | 0 | relay_chain_sync |
64 | 0 | .subscribe_all(32, NonZero::<usize>::new(usize::MAX).unwrap()) |
65 | 0 | .await |
66 | 0 | }) Unexecuted instantiation: _RNCNCINvNtNtCsgnEOxJmACC4_13smoldot_light12sync_service9parachain15start_parachainpE00Ba_ Unexecuted instantiation: _RNCNCINvNtNtCs508CmrSPkZh_13smoldot_light12sync_service9parachain15start_parachainNtNtCs7snhGEhbuap_18smoldot_light_wasm8platform11PlatformRefE00B1m_ Unexecuted instantiation: _RNCNCINvNtNtCs508CmrSPkZh_13smoldot_light12sync_service9parachain15start_parachainpE00Ba_ |
67 | | }, |
68 | | }, |
69 | 0 | relay_chain_sync, |
70 | 0 | platform, |
71 | | } |
72 | 0 | .run() |
73 | 0 | .await; |
74 | 0 | } Unexecuted instantiation: _RNCINvNtNtCsgnEOxJmACC4_13smoldot_light12sync_service9parachain15start_parachainpE0B8_ Unexecuted instantiation: _RNCINvNtNtCs508CmrSPkZh_13smoldot_light12sync_service9parachain15start_parachainNtNtCs7snhGEhbuap_18smoldot_light_wasm8platform11PlatformRefE0B1k_ Unexecuted instantiation: _RNCINvNtNtCs508CmrSPkZh_13smoldot_light12sync_service9parachain15start_parachainpE0B8_ |
75 | | |
76 | | /// Task that is running in the background. |
77 | | struct ParachainBackgroundTask<TPlat: PlatformRef> { |
78 | | /// Target to use for all logs. |
79 | | log_target: String, |
80 | | |
81 | | /// Access to the platform's capabilities. |
82 | | platform: TPlat, |
83 | | |
84 | | /// Channel receiving message from the sync service frontend. |
85 | | from_foreground: Pin<Box<async_channel::Receiver<ToBackground>>>, |
86 | | |
87 | | /// Number of bytes to use to encode the parachain block numbers in headers. |
88 | | block_number_bytes: usize, |
89 | | |
90 | | /// Id of the parachain registered within the relay chain. Chosen by the user. |
91 | | parachain_id: u32, |
92 | | |
93 | | /// Networking service connected to the peer-to-peer network of the parachain. |
94 | | network_service: Arc<network_service::NetworkServiceChain<TPlat>>, |
95 | | |
96 | | /// Events coming from the networking service. `None` if not subscribed yet. |
97 | | from_network_service: Option<Pin<Box<async_channel::Receiver<network_service::Event>>>>, |
98 | | |
99 | | /// Runtime service of the relay chain. |
100 | | relay_chain_sync: Arc<runtime_service::RuntimeService<TPlat>>, |
101 | | |
102 | | /// Last-known finalized parachain header. Can be very old and obsolete. |
103 | | /// Updated after we successfully fetch the parachain head of a relay chain finalized block, |
104 | | /// and left untouched if the fetch fails. |
105 | | /// Initialized to the parachain genesis block header. |
106 | | obsolete_finalized_parahead: Vec<u8>, |
107 | | |
108 | | /// List of parachain network sources. |
109 | | /// |
110 | | /// Values are their role, and self-reported best block when we connected to them. This best |
111 | | /// block is never updated. |
112 | | /// |
113 | | /// > **Note**: In the past, smoldot used to track exactly which peer knows which block |
114 | | /// > based on block announces. This, however, caused issues due to the fact that |
115 | | /// > there's a disconnect between the parachain best block on the relay chain |
116 | | /// > and the parachain best block on the network. We currently simply assume that |
117 | | /// > all parachain nodes know about all parachain blocks from the relay chain. |
118 | | sync_sources: HashMap<PeerId, (codec::Role, u64, [u8; 32]), util::SipHasherBuild>, |
119 | | |
120 | | /// Extra fields that are set after the subscription to the runtime service events has |
121 | | /// succeeded. |
122 | | subscription_state: ParachainBackgroundState<TPlat>, |
123 | | } |
124 | | |
125 | | enum ParachainBackgroundState<TPlat: PlatformRef> { |
126 | | /// Currently subscribing to the relay chain runtime service. |
127 | | NotSubscribed { |
128 | | /// List of senders that will get notified when the tree of blocks is modified. |
129 | | /// |
130 | | /// These subscriptions are pending and no notification should be sent to them until the |
131 | | /// subscription to the relay chain runtime service is finished. |
132 | | all_subscriptions: Vec<async_channel::Sender<super::Notification>>, |
133 | | |
134 | | /// Future when the subscription has finished. |
135 | | subscribe_future: future::BoxFuture<'static, runtime_service::SubscribeAll<TPlat>>, |
136 | | }, |
137 | | |
138 | | /// Subscribed to the relay chain runtime service. |
139 | | Subscribed(ParachainBackgroundTaskAfterSubscription<TPlat>), |
140 | | } |
141 | | |
142 | | struct ParachainBackgroundTaskAfterSubscription<TPlat: PlatformRef> { |
143 | | /// List of senders that get notified when the tree of blocks is modified. |
144 | | all_subscriptions: Vec<async_channel::Sender<super::Notification>>, |
145 | | |
146 | | /// Stream of blocks of the relay chain this parachain is registered on. |
147 | | /// The buffer size should be large enough so that, if the CPU is busy, it doesn't become full |
148 | | /// before the execution of the sync service resumes. |
149 | | /// The maximum number of pinned block is ignored, as this maximum is a way to avoid malicious |
150 | | /// behaviors. This code is by definition not considered malicious. |
151 | | relay_chain_subscribe_all: runtime_service::Subscription<TPlat>, |
152 | | |
153 | | /// Hash of the best parachain that has been reported to the subscriptions. |
154 | | /// `None` if and only if no finalized parachain head is known yet. |
155 | | reported_best_parahead_hash: Option<[u8; 32]>, |
156 | | |
157 | | /// Tree of relay chain blocks. Blocks are inserted when received from the relay chain |
158 | | /// sync service. Once inside, their corresponding parachain head is fetched. Once the |
159 | | /// parachain head is fetched, this parachain head is reported to our subscriptions. |
160 | | /// |
161 | | /// The root of the tree is a "virtual" block. It can be thought as the parent of the relay |
162 | | /// chain finalized block, but is there even if the relay chain finalized block is block 0. |
163 | | /// |
164 | | /// All block in the tree has an associated parachain head behind an `Option`. This `Option` |
165 | | /// always contains `Some`, except for the "virtual" root block for which it is `None`. |
166 | | /// |
167 | | /// If the output finalized block has a parachain head equal to `None`, it therefore means |
168 | | /// that no finalized parachain head is known yet. |
169 | | /// Note that, when it is the case, `SubscribeAll` messages from the frontend are still |
170 | | /// answered with a single finalized block set to `obsolete_finalized_parahead`. Once a |
171 | | /// finalized parachain head is known, it is important to reset all subscriptions. |
172 | | /// |
173 | | /// The set of blocks in this tree whose parachain block hasn't been fetched yet is the same |
174 | | /// as the set of blocks that is maintained pinned on the runtime service. Blocks are unpinned |
175 | | /// when their parachain head fetching succeeds or when they are removed from the tree. |
176 | | async_tree: async_tree::AsyncTree<TPlat::Instant, [u8; 32], Option<Vec<u8>>>, |
177 | | |
178 | | /// If `true`, [`ParachainBackgroundTaskAfterSubscription::async_tree`] might need to |
179 | | /// be advanced. |
180 | | must_process_sync_tree: bool, |
181 | | |
182 | | /// List of in-progress parachain head fetching operations. |
183 | | /// |
184 | | /// The operations require some blocks to be pinned within the relay chain runtime service, |
185 | | /// which is guaranteed by the fact that `relay_chain_subscribe_all.new_blocks` stays |
186 | | /// alive for longer than this container, and by the fact that we unpin block after a |
187 | | /// fetching operation has finished and that we never fetch twice for the same block. |
188 | | in_progress_paraheads: stream::FuturesUnordered< |
189 | | future::BoxFuture<'static, (async_tree::AsyncOpId, Result<Vec<u8>, ParaheadError>)>, |
190 | | >, |
191 | | |
192 | | /// Future that is ready when we need to start a new parachain head fetch operation. |
193 | | next_start_parahead_fetch: Pin<Box<dyn Future<Output = ()> + Send>>, |
194 | | } |
195 | | |
196 | | impl<TPlat: PlatformRef> ParachainBackgroundTask<TPlat> { |
197 | 0 | async fn run(mut self) { Unexecuted instantiation: _RNvMNtNtCsgnEOxJmACC4_13smoldot_light12sync_service9parachainINtB2_23ParachainBackgroundTaskpE3runB6_ Unexecuted instantiation: _RNvMNtNtCs508CmrSPkZh_13smoldot_light12sync_service9parachainINtB2_23ParachainBackgroundTaskNtNtCs7snhGEhbuap_18smoldot_light_wasm8platform11PlatformRefE3runB1w_ Unexecuted instantiation: _RNvMNtNtCs508CmrSPkZh_13smoldot_light12sync_service9parachainINtB2_23ParachainBackgroundTaskpE3runB6_ |
198 | | loop { |
199 | | // Yield at every loop in order to provide better tasks granularity. |
200 | 0 | futures_lite::future::yield_now().await; |
201 | | |
202 | | // Wait until something interesting happens. |
203 | | enum WakeUpReason<TPlat: PlatformRef> { |
204 | | ForegroundClosed, |
205 | | ForegroundMessage(ToBackground), |
206 | | NewSubscription(runtime_service::SubscribeAll<TPlat>), |
207 | | StartParaheadFetch, |
208 | | ParaheadFetchFinished { |
209 | | async_op_id: async_tree::AsyncOpId, |
210 | | parahead_result: Result<Vec<u8>, ParaheadError>, |
211 | | }, |
212 | | Notification(runtime_service::Notification), |
213 | | SubscriptionDead, |
214 | | MustSubscribeNetworkEvents, |
215 | | NetworkEvent(network_service::Event), |
216 | | AdvanceSyncTree, |
217 | | } |
218 | | |
219 | 0 | let wake_up_reason: WakeUpReason<_> = { |
220 | | let ( |
221 | 0 | subscribe_future, |
222 | 0 | next_start_parahead_fetch, |
223 | 0 | relay_chain_subscribe_all, |
224 | 0 | in_progress_paraheads, |
225 | 0 | must_process_sync_tree, |
226 | 0 | is_relaychain_subscribed, |
227 | 0 | ) = match &mut self.subscription_state { |
228 | | ParachainBackgroundState::NotSubscribed { |
229 | 0 | subscribe_future, .. |
230 | 0 | } => (Some(subscribe_future), None, None, None, None, false), |
231 | 0 | ParachainBackgroundState::Subscribed(runtime_subscription) => ( |
232 | 0 | None, |
233 | 0 | Some(&mut runtime_subscription.next_start_parahead_fetch), |
234 | 0 | Some(&mut runtime_subscription.relay_chain_subscribe_all), |
235 | 0 | Some(&mut runtime_subscription.in_progress_paraheads), |
236 | 0 | Some(&mut runtime_subscription.must_process_sync_tree), |
237 | 0 | true, |
238 | 0 | ), |
239 | | }; |
240 | | |
241 | 0 | async { |
242 | 0 | if let Some(subscribe_future) = subscribe_future { |
243 | 0 | WakeUpReason::NewSubscription(subscribe_future.await) |
244 | | } else { |
245 | 0 | future::pending().await |
246 | | } |
247 | 0 | } Unexecuted instantiation: _RNCNCNvMNtNtCsgnEOxJmACC4_13smoldot_light12sync_service9parachainINtB6_23ParachainBackgroundTaskpE3run00Ba_ Unexecuted instantiation: _RNCNCNvMNtNtCs508CmrSPkZh_13smoldot_light12sync_service9parachainINtB6_23ParachainBackgroundTaskNtNtCs7snhGEhbuap_18smoldot_light_wasm8platform11PlatformRefE3run00B1A_ Unexecuted instantiation: _RNCNCNvMNtNtCs508CmrSPkZh_13smoldot_light12sync_service9parachainINtB6_23ParachainBackgroundTaskpE3run00Ba_ |
248 | 0 | .or(async { |
249 | 0 | match self.from_foreground.next().await { |
250 | 0 | Some(msg) => WakeUpReason::ForegroundMessage(msg), |
251 | 0 | None => WakeUpReason::ForegroundClosed, |
252 | | } |
253 | 0 | }) Unexecuted instantiation: _RNCNCNvMNtNtCsgnEOxJmACC4_13smoldot_light12sync_service9parachainINtB6_23ParachainBackgroundTaskpE3run0s_0Ba_ Unexecuted instantiation: _RNCNCNvMNtNtCs508CmrSPkZh_13smoldot_light12sync_service9parachainINtB6_23ParachainBackgroundTaskNtNtCs7snhGEhbuap_18smoldot_light_wasm8platform11PlatformRefE3run0s_0B1A_ Unexecuted instantiation: _RNCNCNvMNtNtCs508CmrSPkZh_13smoldot_light12sync_service9parachainINtB6_23ParachainBackgroundTaskpE3run0s_0Ba_ |
254 | 0 | .or(async { |
255 | 0 | if let Some(relay_chain_subscribe_all) = relay_chain_subscribe_all { |
256 | 0 | match relay_chain_subscribe_all.next().await { |
257 | 0 | Some(notif) => WakeUpReason::Notification(notif), |
258 | 0 | None => WakeUpReason::SubscriptionDead, |
259 | | } |
260 | | } else { |
261 | 0 | future::pending().await |
262 | | } |
263 | 0 | }) Unexecuted instantiation: _RNCNCNvMNtNtCsgnEOxJmACC4_13smoldot_light12sync_service9parachainINtB6_23ParachainBackgroundTaskpE3run0s0_0Ba_ Unexecuted instantiation: _RNCNCNvMNtNtCs508CmrSPkZh_13smoldot_light12sync_service9parachainINtB6_23ParachainBackgroundTaskNtNtCs7snhGEhbuap_18smoldot_light_wasm8platform11PlatformRefE3run0s0_0B1A_ Unexecuted instantiation: _RNCNCNvMNtNtCs508CmrSPkZh_13smoldot_light12sync_service9parachainINtB6_23ParachainBackgroundTaskpE3run0s0_0Ba_ |
264 | 0 | .or(async { |
265 | 0 | if is_relaychain_subscribed { |
266 | 0 | if let Some(from_network_service) = self.from_network_service.as_mut() { |
267 | 0 | match from_network_service.next().await { |
268 | 0 | Some(ev) => WakeUpReason::NetworkEvent(ev), |
269 | | None => { |
270 | 0 | self.from_network_service = None; |
271 | 0 | WakeUpReason::MustSubscribeNetworkEvents |
272 | | } |
273 | | } |
274 | | } else { |
275 | 0 | WakeUpReason::MustSubscribeNetworkEvents |
276 | | } |
277 | | } else { |
278 | 0 | future::pending().await |
279 | | } |
280 | 0 | }) Unexecuted instantiation: _RNCNCNvMNtNtCsgnEOxJmACC4_13smoldot_light12sync_service9parachainINtB6_23ParachainBackgroundTaskpE3run0s1_0Ba_ Unexecuted instantiation: _RNCNCNvMNtNtCs508CmrSPkZh_13smoldot_light12sync_service9parachainINtB6_23ParachainBackgroundTaskNtNtCs7snhGEhbuap_18smoldot_light_wasm8platform11PlatformRefE3run0s1_0B1A_ Unexecuted instantiation: _RNCNCNvMNtNtCs508CmrSPkZh_13smoldot_light12sync_service9parachainINtB6_23ParachainBackgroundTaskpE3run0s1_0Ba_ |
281 | 0 | .or(async { |
282 | 0 | if let Some(next_start_parahead_fetch) = next_start_parahead_fetch { |
283 | 0 | next_start_parahead_fetch.as_mut().await; |
284 | 0 | *next_start_parahead_fetch = Box::pin(future::pending()); |
285 | 0 | WakeUpReason::StartParaheadFetch |
286 | | } else { |
287 | 0 | future::pending().await |
288 | | } |
289 | 0 | }) Unexecuted instantiation: _RNCNCNvMNtNtCsgnEOxJmACC4_13smoldot_light12sync_service9parachainINtB6_23ParachainBackgroundTaskpE3run0s2_0Ba_ Unexecuted instantiation: _RNCNCNvMNtNtCs508CmrSPkZh_13smoldot_light12sync_service9parachainINtB6_23ParachainBackgroundTaskNtNtCs7snhGEhbuap_18smoldot_light_wasm8platform11PlatformRefE3run0s2_0B1A_ Unexecuted instantiation: _RNCNCNvMNtNtCs508CmrSPkZh_13smoldot_light12sync_service9parachainINtB6_23ParachainBackgroundTaskpE3run0s2_0Ba_ |
290 | 0 | .or(async { |
291 | 0 | if let Some(in_progress_paraheads) = in_progress_paraheads { |
292 | 0 | if !in_progress_paraheads.is_empty() { |
293 | 0 | let (async_op_id, parahead_result) = |
294 | 0 | in_progress_paraheads.next().await.unwrap(); |
295 | 0 | WakeUpReason::ParaheadFetchFinished { |
296 | 0 | async_op_id, |
297 | 0 | parahead_result, |
298 | 0 | } |
299 | | } else { |
300 | 0 | future::pending().await |
301 | | } |
302 | | } else { |
303 | 0 | future::pending().await |
304 | | } |
305 | 0 | }) Unexecuted instantiation: _RNCNCNvMNtNtCsgnEOxJmACC4_13smoldot_light12sync_service9parachainINtB6_23ParachainBackgroundTaskpE3run0s3_0Ba_ Unexecuted instantiation: _RNCNCNvMNtNtCs508CmrSPkZh_13smoldot_light12sync_service9parachainINtB6_23ParachainBackgroundTaskNtNtCs7snhGEhbuap_18smoldot_light_wasm8platform11PlatformRefE3run0s3_0B1A_ Unexecuted instantiation: _RNCNCNvMNtNtCs508CmrSPkZh_13smoldot_light12sync_service9parachainINtB6_23ParachainBackgroundTaskpE3run0s3_0Ba_ |
306 | 0 | .or(async { |
307 | 0 | if let Some(must_process_sync_tree) = must_process_sync_tree { |
308 | 0 | if *must_process_sync_tree { |
309 | 0 | *must_process_sync_tree = false; |
310 | 0 | WakeUpReason::AdvanceSyncTree |
311 | | } else { |
312 | 0 | future::pending().await |
313 | | } |
314 | | } else { |
315 | 0 | future::pending().await |
316 | | } |
317 | 0 | }) Unexecuted instantiation: _RNCNCNvMNtNtCsgnEOxJmACC4_13smoldot_light12sync_service9parachainINtB6_23ParachainBackgroundTaskpE3run0s4_0Ba_ Unexecuted instantiation: _RNCNCNvMNtNtCs508CmrSPkZh_13smoldot_light12sync_service9parachainINtB6_23ParachainBackgroundTaskNtNtCs7snhGEhbuap_18smoldot_light_wasm8platform11PlatformRefE3run0s4_0B1A_ Unexecuted instantiation: _RNCNCNvMNtNtCs508CmrSPkZh_13smoldot_light12sync_service9parachainINtB6_23ParachainBackgroundTaskpE3run0s4_0Ba_ |
318 | 0 | .await |
319 | | }; |
320 | | |
321 | 0 | match (wake_up_reason, &mut self.subscription_state) { |
322 | | (WakeUpReason::ForegroundClosed, _) => { |
323 | | // Terminate the background task. |
324 | 0 | return; |
325 | | } |
326 | | |
327 | 0 | (WakeUpReason::NewSubscription(relay_chain_subscribe_all), _) => { |
328 | | // Subscription to the relay chain has finished. |
329 | 0 | log!( |
330 | 0 | &self.platform, |
331 | | Debug, |
332 | 0 | &self.log_target, |
333 | | "relay-chain-new-subscription", |
334 | 0 | finalized_hash = HashDisplay(&header::hash_from_scale_encoded_header( |
335 | 0 | &relay_chain_subscribe_all.finalized_block_scale_encoded_header |
336 | 0 | )), |
337 | 0 | subscription_id = ?relay_chain_subscribe_all.new_blocks.id(), |
338 | | ); |
339 | 0 | log!( |
340 | 0 | &self.platform, |
341 | | Debug, |
342 | 0 | &self.log_target, |
343 | | "parahead-fetch-operations-cleared" |
344 | | ); |
345 | | |
346 | 0 | let async_tree = { |
347 | 0 | let mut async_tree = |
348 | 0 | async_tree::AsyncTree::<TPlat::Instant, [u8; 32], _>::new( |
349 | 0 | async_tree::Config { |
350 | 0 | finalized_async_user_data: None, |
351 | 0 | retry_after_failed: Duration::from_secs(5), |
352 | 0 | blocks_capacity: 32, |
353 | 0 | }, |
354 | | ); |
355 | 0 | let finalized_hash = header::hash_from_scale_encoded_header( |
356 | 0 | &relay_chain_subscribe_all.finalized_block_scale_encoded_header, |
357 | | ); |
358 | 0 | let finalized_index = |
359 | 0 | async_tree.input_insert_block(finalized_hash, None, false, true); |
360 | 0 | async_tree.input_finalize(finalized_index); |
361 | 0 | for block in relay_chain_subscribe_all.non_finalized_blocks_ancestry_order { |
362 | 0 | let hash = |
363 | 0 | header::hash_from_scale_encoded_header(&block.scale_encoded_header); |
364 | 0 | let parent = async_tree |
365 | 0 | .input_output_iter_unordered() |
366 | 0 | .find(|b| *b.user_data == block.parent_hash) Unexecuted instantiation: _RNCNCNvMNtNtCsgnEOxJmACC4_13smoldot_light12sync_service9parachainINtB6_23ParachainBackgroundTaskpE3run0s5_0Ba_ Unexecuted instantiation: _RNCNCNvMNtNtCs508CmrSPkZh_13smoldot_light12sync_service9parachainINtB6_23ParachainBackgroundTaskNtNtCs7snhGEhbuap_18smoldot_light_wasm8platform11PlatformRefE3run0s5_0B1A_ Unexecuted instantiation: _RNCNCNvMNtNtCs508CmrSPkZh_13smoldot_light12sync_service9parachainINtB6_23ParachainBackgroundTaskpE3run0s5_0Ba_ |
367 | 0 | .map(|b| b.id) |
368 | 0 | .unwrap_or(finalized_index); |
369 | 0 | async_tree.input_insert_block( |
370 | 0 | hash, |
371 | 0 | Some(parent), |
372 | | false, |
373 | 0 | block.is_new_best, |
374 | | ); |
375 | | } |
376 | 0 | async_tree |
377 | | }; |
378 | | |
379 | 0 | self.subscription_state = ParachainBackgroundState::Subscribed( |
380 | | ParachainBackgroundTaskAfterSubscription { |
381 | 0 | all_subscriptions: match &mut self.subscription_state { |
382 | | ParachainBackgroundState::NotSubscribed { |
383 | 0 | all_subscriptions, |
384 | | .. |
385 | 0 | } => mem::take(all_subscriptions), |
386 | 0 | _ => unreachable!(), |
387 | | }, |
388 | 0 | relay_chain_subscribe_all: relay_chain_subscribe_all.new_blocks, |
389 | 0 | reported_best_parahead_hash: None, |
390 | 0 | async_tree, |
391 | | must_process_sync_tree: false, |
392 | 0 | in_progress_paraheads: stream::FuturesUnordered::new(), |
393 | 0 | next_start_parahead_fetch: Box::pin(future::ready(())), |
394 | | }, |
395 | | ); |
396 | | } |
397 | | |
398 | | ( |
399 | | WakeUpReason::AdvanceSyncTree, |
400 | 0 | ParachainBackgroundState::Subscribed(runtime_subscription), |
401 | | ) => { |
402 | 0 | if let Some(update) = runtime_subscription.async_tree.try_advance_output() { |
403 | | // Make sure to process any notification that comes after. |
404 | 0 | runtime_subscription.must_process_sync_tree = true; |
405 | | |
406 | 0 | match update { |
407 | | async_tree::OutputUpdate::Finalized { |
408 | 0 | former_finalized_async_op_user_data: former_finalized_parahead, |
409 | 0 | pruned_blocks, |
410 | 0 | best_output_block_updated, |
411 | | .. |
412 | 0 | } if *runtime_subscription |
413 | 0 | .async_tree |
414 | 0 | .output_finalized_async_user_data() |
415 | 0 | != former_finalized_parahead => |
416 | | { |
417 | 0 | let new_finalized_parahead = runtime_subscription |
418 | 0 | .async_tree |
419 | 0 | .output_finalized_async_user_data(); |
420 | 0 | debug_assert!(new_finalized_parahead.is_some()); |
421 | | |
422 | | // If this is the first time a finalized parahead is known, any |
423 | | // `SubscribeAll` message that has been answered beforehand was |
424 | | // answered in a dummy way with a potentially obsolete finalized |
425 | | // header. |
426 | | // For this reason, we reset all subscriptions to force all |
427 | | // subscribers to re-subscribe. |
428 | 0 | if former_finalized_parahead.is_none() { |
429 | 0 | runtime_subscription.all_subscriptions.clear(); |
430 | 0 | } |
431 | | |
432 | 0 | let hash = header::hash_from_scale_encoded_header( |
433 | 0 | new_finalized_parahead.as_ref().unwrap(), |
434 | | ); |
435 | | |
436 | 0 | self.obsolete_finalized_parahead = |
437 | 0 | new_finalized_parahead.clone().unwrap(); |
438 | | |
439 | | // Must unpin the pruned blocks if they haven't already been unpinned. |
440 | 0 | let mut pruned_blocks_hashes = |
441 | 0 | Vec::with_capacity(pruned_blocks.len()); |
442 | 0 | for (_, hash, pruned_block_parahead) in pruned_blocks { |
443 | 0 | if pruned_block_parahead.is_none() { |
444 | 0 | runtime_subscription |
445 | 0 | .relay_chain_subscribe_all |
446 | 0 | .unpin_block(hash) |
447 | 0 | .await; |
448 | 0 | } |
449 | 0 | pruned_blocks_hashes.push(hash); |
450 | | } |
451 | | |
452 | 0 | log!( |
453 | 0 | &self.platform, |
454 | | Debug, |
455 | 0 | &self.log_target, |
456 | | "subscriptions-notify-parablock-finalized", |
457 | 0 | hash = HashDisplay(&hash) |
458 | | ); |
459 | | |
460 | 0 | let best_block_hash = runtime_subscription |
461 | 0 | .async_tree |
462 | 0 | .output_best_block_index() |
463 | 0 | .map(|(_, parahead)| { |
464 | 0 | header::hash_from_scale_encoded_header( |
465 | 0 | parahead.as_ref().unwrap(), |
466 | | ) |
467 | 0 | }) Unexecuted instantiation: _RNCNCNvMNtNtCsgnEOxJmACC4_13smoldot_light12sync_service9parachainINtB6_23ParachainBackgroundTaskpE3run0s7_0Ba_ Unexecuted instantiation: _RNCNCNvMNtNtCs508CmrSPkZh_13smoldot_light12sync_service9parachainINtB6_23ParachainBackgroundTaskNtNtCs7snhGEhbuap_18smoldot_light_wasm8platform11PlatformRefE3run0s7_0B1A_ Unexecuted instantiation: _RNCNCNvMNtNtCs508CmrSPkZh_13smoldot_light12sync_service9parachainINtB6_23ParachainBackgroundTaskpE3run0s7_0Ba_ |
468 | 0 | .unwrap_or(hash); |
469 | 0 | runtime_subscription.reported_best_parahead_hash = |
470 | 0 | Some(best_block_hash); |
471 | | |
472 | | // Elements in `all_subscriptions` are removed one by one and |
473 | | // inserted back if the channel is still open. |
474 | 0 | for index in (0..runtime_subscription.all_subscriptions.len()).rev() |
475 | | { |
476 | 0 | let sender = |
477 | 0 | runtime_subscription.all_subscriptions.swap_remove(index); |
478 | 0 | let notif = super::Notification::Finalized { |
479 | 0 | hash, |
480 | 0 | best_block_hash_if_changed: if best_output_block_updated { |
481 | 0 | Some(best_block_hash) |
482 | | } else { |
483 | 0 | None |
484 | | }, |
485 | 0 | pruned_blocks: pruned_blocks_hashes.clone(), |
486 | | }; |
487 | 0 | if sender.try_send(notif).is_ok() { |
488 | 0 | runtime_subscription.all_subscriptions.push(sender); |
489 | 0 | } |
490 | | } |
491 | | } |
492 | | |
493 | | async_tree::OutputUpdate::Finalized { .. } |
494 | | | async_tree::OutputUpdate::BestBlockChanged { .. } => { |
495 | | // Do not report anything to subscriptions if no finalized parahead is |
496 | | // known yet. |
497 | 0 | let finalized_parahead = match runtime_subscription |
498 | 0 | .async_tree |
499 | 0 | .output_finalized_async_user_data() |
500 | | { |
501 | 0 | Some(p) => p, |
502 | 0 | None => continue, |
503 | | }; |
504 | | |
505 | | // Calculate hash of the parablock corresponding to the new best relay |
506 | | // chain block. |
507 | 0 | let parahash = header::hash_from_scale_encoded_header( |
508 | 0 | runtime_subscription |
509 | 0 | .async_tree |
510 | 0 | .output_best_block_index() |
511 | 0 | .map(|(_, b)| b.as_ref().unwrap()) Unexecuted instantiation: _RNCNCNvMNtNtCsgnEOxJmACC4_13smoldot_light12sync_service9parachainINtB6_23ParachainBackgroundTaskpE3run0s8_0Ba_ Unexecuted instantiation: _RNCNCNvMNtNtCs508CmrSPkZh_13smoldot_light12sync_service9parachainINtB6_23ParachainBackgroundTaskNtNtCs7snhGEhbuap_18smoldot_light_wasm8platform11PlatformRefE3run0s8_0B1A_ Unexecuted instantiation: _RNCNCNvMNtNtCs508CmrSPkZh_13smoldot_light12sync_service9parachainINtB6_23ParachainBackgroundTaskpE3run0s8_0Ba_ |
512 | 0 | .unwrap_or(finalized_parahead), |
513 | | ); |
514 | | |
515 | 0 | if runtime_subscription.reported_best_parahead_hash.as_ref() |
516 | 0 | != Some(¶hash) |
517 | | { |
518 | 0 | runtime_subscription.reported_best_parahead_hash = |
519 | 0 | Some(parahash); |
520 | | |
521 | | // The networking service needs to be kept up to date with what the local |
522 | | // node considers as the best block. |
523 | 0 | if let Ok(header) = |
524 | 0 | header::decode(finalized_parahead, self.block_number_bytes) |
525 | | { |
526 | 0 | self.network_service |
527 | 0 | .set_local_best_block(parahash, header.number) |
528 | 0 | .await; |
529 | 0 | } |
530 | | |
531 | 0 | log!( |
532 | 0 | &self.platform, |
533 | | Debug, |
534 | 0 | &self.log_target, |
535 | | "subscriptions-notify-best-block-changed", |
536 | 0 | hash = HashDisplay(¶hash) |
537 | | ); |
538 | | |
539 | | // Elements in `all_subscriptions` are removed one by one and |
540 | | // inserted back if the channel is still open. |
541 | 0 | for index in |
542 | 0 | (0..runtime_subscription.all_subscriptions.len()).rev() |
543 | | { |
544 | 0 | let sender = runtime_subscription |
545 | 0 | .all_subscriptions |
546 | 0 | .swap_remove(index); |
547 | 0 | let notif = super::Notification::BestBlockChanged { |
548 | 0 | hash: parahash, |
549 | 0 | }; |
550 | 0 | if sender.try_send(notif).is_ok() { |
551 | 0 | runtime_subscription.all_subscriptions.push(sender); |
552 | 0 | } |
553 | | } |
554 | 0 | } |
555 | | } |
556 | | |
557 | 0 | async_tree::OutputUpdate::Block(block) => { |
558 | | // `block` borrows `async_tree`. We need to mutably access `async_tree` |
559 | | // below, so deconstruct `block` beforehand. |
560 | 0 | let is_new_best = block.is_new_best; |
561 | 0 | let block_index = block.index; |
562 | 0 | let scale_encoded_header: Vec<u8> = runtime_subscription |
563 | 0 | .async_tree |
564 | 0 | .block_async_user_data(block.index) |
565 | 0 | .unwrap() |
566 | 0 | .clone() |
567 | 0 | .unwrap(); |
568 | 0 | let parahash = |
569 | 0 | header::hash_from_scale_encoded_header(&scale_encoded_header); |
570 | | |
571 | | // Do not report anything to subscriptions if no finalized parahead is |
572 | | // known yet. |
573 | 0 | let finalized_parahead = match runtime_subscription |
574 | 0 | .async_tree |
575 | 0 | .output_finalized_async_user_data() |
576 | | { |
577 | 0 | Some(p) => p, |
578 | 0 | None => continue, |
579 | | }; |
580 | | |
581 | | // Do not report the new block if it has already been reported in the |
582 | | // past. This covers situations where the parahead is identical to the |
583 | | // relay chain's parent's parahead, but also situations where multiple |
584 | | // sibling relay chain blocks have the same parahead. |
585 | 0 | if *finalized_parahead == scale_encoded_header |
586 | 0 | || runtime_subscription |
587 | 0 | .async_tree |
588 | 0 | .input_output_iter_unordered() |
589 | 0 | .filter(|item| item.id != block_index) Unexecuted instantiation: _RNCNCNvMNtNtCsgnEOxJmACC4_13smoldot_light12sync_service9parachainINtB6_23ParachainBackgroundTaskpE3run0s9_0Ba_ Unexecuted instantiation: _RNCNCNvMNtNtCs508CmrSPkZh_13smoldot_light12sync_service9parachainINtB6_23ParachainBackgroundTaskNtNtCs7snhGEhbuap_18smoldot_light_wasm8platform11PlatformRefE3run0s9_0B1A_ Unexecuted instantiation: _RNCNCNvMNtNtCs508CmrSPkZh_13smoldot_light12sync_service9parachainINtB6_23ParachainBackgroundTaskpE3run0s9_0Ba_ |
590 | 0 | .filter_map(|item| item.async_op_user_data) |
591 | 0 | .any(|item| item.as_ref() == Some(&scale_encoded_header)) Unexecuted instantiation: _RNCNCNvMNtNtCsgnEOxJmACC4_13smoldot_light12sync_service9parachainINtB6_23ParachainBackgroundTaskpE3run0sb_0Ba_ Unexecuted instantiation: _RNCNCNvMNtNtCs508CmrSPkZh_13smoldot_light12sync_service9parachainINtB6_23ParachainBackgroundTaskNtNtCs7snhGEhbuap_18smoldot_light_wasm8platform11PlatformRefE3run0sb_0B1A_ Unexecuted instantiation: _RNCNCNvMNtNtCs508CmrSPkZh_13smoldot_light12sync_service9parachainINtB6_23ParachainBackgroundTaskpE3run0sb_0Ba_ |
592 | | { |
593 | | // While the parablock has already been reported, it is possible that |
594 | | // it becomes the new best block while it wasn't before, in which |
595 | | // case we should send a notification. |
596 | 0 | if is_new_best |
597 | 0 | && runtime_subscription.reported_best_parahead_hash.as_ref() |
598 | 0 | != Some(¶hash) |
599 | | { |
600 | 0 | runtime_subscription.reported_best_parahead_hash = |
601 | 0 | Some(parahash); |
602 | | |
603 | | // The networking service needs to be kept up to date with what the |
604 | | // local node considers as the best block. |
605 | 0 | if let Ok(header) = header::decode( |
606 | 0 | finalized_parahead, |
607 | 0 | self.block_number_bytes, |
608 | 0 | ) { |
609 | 0 | self.network_service |
610 | 0 | .set_local_best_block(parahash, header.number) |
611 | 0 | .await; |
612 | 0 | } |
613 | | |
614 | 0 | log!( |
615 | 0 | &self.platform, |
616 | | Debug, |
617 | 0 | &self.log_target, |
618 | | "subscriptions-notify-best-block-changed", |
619 | 0 | hash = HashDisplay(¶hash) |
620 | | ); |
621 | | |
622 | | // Elements in `all_subscriptions` are removed one by one and |
623 | | // inserted back if the channel is still open. |
624 | 0 | for index in |
625 | 0 | (0..runtime_subscription.all_subscriptions.len()).rev() |
626 | | { |
627 | 0 | let sender = runtime_subscription |
628 | 0 | .all_subscriptions |
629 | 0 | .swap_remove(index); |
630 | 0 | let notif = super::Notification::BestBlockChanged { |
631 | 0 | hash: parahash, |
632 | 0 | }; |
633 | 0 | if sender.try_send(notif).is_ok() { |
634 | 0 | runtime_subscription.all_subscriptions.push(sender); |
635 | 0 | } |
636 | | } |
637 | 0 | } |
638 | | |
639 | 0 | continue; |
640 | 0 | } |
641 | | |
642 | 0 | if is_new_best { |
643 | 0 | runtime_subscription.reported_best_parahead_hash = |
644 | 0 | Some(parahash); |
645 | 0 | } |
646 | | |
647 | 0 | let parent_hash = header::hash_from_scale_encoded_header( |
648 | 0 | runtime_subscription |
649 | 0 | .async_tree |
650 | 0 | .parent(block_index) |
651 | 0 | .map(|idx| { |
652 | 0 | runtime_subscription |
653 | 0 | .async_tree |
654 | 0 | .block_async_user_data(idx) |
655 | 0 | .unwrap() |
656 | 0 | .as_ref() |
657 | 0 | .unwrap() |
658 | 0 | }) Unexecuted instantiation: _RNCNCNvMNtNtCsgnEOxJmACC4_13smoldot_light12sync_service9parachainINtB6_23ParachainBackgroundTaskpE3run0sc_0Ba_ Unexecuted instantiation: _RNCNCNvMNtNtCs508CmrSPkZh_13smoldot_light12sync_service9parachainINtB6_23ParachainBackgroundTaskNtNtCs7snhGEhbuap_18smoldot_light_wasm8platform11PlatformRefE3run0sc_0B1A_ Unexecuted instantiation: _RNCNCNvMNtNtCs508CmrSPkZh_13smoldot_light12sync_service9parachainINtB6_23ParachainBackgroundTaskpE3run0sc_0Ba_ |
659 | 0 | .unwrap_or(finalized_parahead), |
660 | | ); |
661 | | |
662 | 0 | log!( |
663 | 0 | &self.platform, |
664 | | Debug, |
665 | 0 | &self.log_target, |
666 | | "subscriptions-notify-new-parablock", |
667 | 0 | hash = HashDisplay(¶hash), |
668 | 0 | parent_hash = HashDisplay(&parent_hash), |
669 | | ?is_new_best |
670 | | ); |
671 | | |
672 | | // Elements in `all_subscriptions` are removed one by one and |
673 | | // inserted back if the channel is still open. |
674 | 0 | for index in (0..runtime_subscription.all_subscriptions.len()).rev() |
675 | | { |
676 | 0 | let sender = |
677 | 0 | runtime_subscription.all_subscriptions.swap_remove(index); |
678 | 0 | let notif = |
679 | 0 | super::Notification::Block(super::BlockNotification { |
680 | 0 | is_new_best, |
681 | 0 | parent_hash, |
682 | 0 | scale_encoded_header: scale_encoded_header.clone(), |
683 | 0 | }); |
684 | 0 | if sender.try_send(notif).is_ok() { |
685 | 0 | runtime_subscription.all_subscriptions.push(sender); |
686 | 0 | } |
687 | | } |
688 | | } |
689 | | } |
690 | 0 | } |
691 | | } |
692 | | |
693 | | ( |
694 | | WakeUpReason::StartParaheadFetch, |
695 | 0 | ParachainBackgroundState::Subscribed(runtime_subscription), |
696 | | ) => { |
697 | | // Must start downloading a parahead. |
698 | | |
699 | | // Internal state check. |
700 | 0 | debug_assert_eq!( |
701 | 0 | runtime_subscription.reported_best_parahead_hash.is_some(), |
702 | 0 | runtime_subscription |
703 | 0 | .async_tree |
704 | 0 | .output_finalized_async_user_data() |
705 | 0 | .is_some() |
706 | | ); |
707 | | |
708 | | // Limit the maximum number of simultaneous downloads. |
709 | 0 | if runtime_subscription.in_progress_paraheads.len() >= 4 { |
710 | 0 | continue; |
711 | 0 | } |
712 | | |
713 | 0 | match runtime_subscription |
714 | 0 | .async_tree |
715 | 0 | .next_necessary_async_op(&self.platform.now()) |
716 | | { |
717 | 0 | async_tree::NextNecessaryAsyncOp::NotReady { when: Some(when) } => { |
718 | 0 | runtime_subscription.next_start_parahead_fetch = |
719 | 0 | Box::pin(self.platform.sleep_until(when)); |
720 | 0 | } |
721 | 0 | async_tree::NextNecessaryAsyncOp::NotReady { when: None } => { |
722 | 0 | runtime_subscription.next_start_parahead_fetch = |
723 | 0 | Box::pin(future::pending()); |
724 | 0 | } |
725 | 0 | async_tree::NextNecessaryAsyncOp::Ready(op) => { |
726 | 0 | log!( |
727 | 0 | &self.platform, |
728 | | Debug, |
729 | 0 | &self.log_target, |
730 | | "parahead-fetch-operation-started", |
731 | | relay_block_hash = |
732 | 0 | HashDisplay(&runtime_subscription.async_tree[op.block_index]), |
733 | | ); |
734 | | |
735 | 0 | runtime_subscription.in_progress_paraheads.push({ |
736 | 0 | let relay_chain_sync = self.relay_chain_sync.clone(); |
737 | 0 | let subscription_id = |
738 | 0 | runtime_subscription.relay_chain_subscribe_all.id(); |
739 | 0 | let block_hash = runtime_subscription.async_tree[op.block_index]; |
740 | 0 | let async_op_id = op.id; |
741 | 0 | let parachain_id = self.parachain_id; |
742 | 0 | Box::pin(async move { |
743 | | ( |
744 | 0 | async_op_id, |
745 | 0 | fetch_parahead( |
746 | 0 | &relay_chain_sync, |
747 | 0 | subscription_id, |
748 | 0 | parachain_id, |
749 | 0 | &block_hash, |
750 | 0 | ) |
751 | 0 | .await, |
752 | | ) |
753 | 0 | }) Unexecuted instantiation: _RNCNCNvMNtNtCsgnEOxJmACC4_13smoldot_light12sync_service9parachainINtB6_23ParachainBackgroundTaskpE3run0sd_0Ba_ Unexecuted instantiation: _RNCNCNvMNtNtCs508CmrSPkZh_13smoldot_light12sync_service9parachainINtB6_23ParachainBackgroundTaskNtNtCs7snhGEhbuap_18smoldot_light_wasm8platform11PlatformRefE3run0sd_0B1A_ Unexecuted instantiation: _RNCNCNvMNtNtCs508CmrSPkZh_13smoldot_light12sync_service9parachainINtB6_23ParachainBackgroundTaskpE3run0sd_0Ba_ |
754 | | }); |
755 | | |
756 | | // There might be more downloads to start. |
757 | 0 | runtime_subscription.next_start_parahead_fetch = |
758 | 0 | Box::pin(future::ready(())); |
759 | | } |
760 | | } |
761 | | } |
762 | | |
763 | | ( |
764 | | WakeUpReason::Notification(runtime_service::Notification::Finalized { |
765 | 0 | hash, |
766 | 0 | best_block_hash_if_changed, |
767 | | .. |
768 | | }), |
769 | 0 | ParachainBackgroundState::Subscribed(runtime_subscription), |
770 | | ) => { |
771 | | // Relay chain has a new finalized block. |
772 | 0 | log!( |
773 | 0 | &self.platform, |
774 | | Debug, |
775 | 0 | &self.log_target, |
776 | | "relay-chain-block-finalized", |
777 | 0 | hash = HashDisplay(&hash) |
778 | | ); |
779 | | |
780 | 0 | if let Some(best_block_hash_if_changed) = best_block_hash_if_changed { |
781 | 0 | let best = runtime_subscription |
782 | 0 | .async_tree |
783 | 0 | .input_output_iter_unordered() |
784 | 0 | .find(|b| *b.user_data == best_block_hash_if_changed) Unexecuted instantiation: _RNCNCNvMNtNtCsgnEOxJmACC4_13smoldot_light12sync_service9parachainINtB6_23ParachainBackgroundTaskpE3run0se_0Ba_ Unexecuted instantiation: _RNCNCNvMNtNtCs508CmrSPkZh_13smoldot_light12sync_service9parachainINtB6_23ParachainBackgroundTaskNtNtCs7snhGEhbuap_18smoldot_light_wasm8platform11PlatformRefE3run0se_0B1A_ Unexecuted instantiation: _RNCNCNvMNtNtCs508CmrSPkZh_13smoldot_light12sync_service9parachainINtB6_23ParachainBackgroundTaskpE3run0se_0Ba_ |
785 | 0 | .unwrap() |
786 | | .id; |
787 | 0 | runtime_subscription |
788 | 0 | .async_tree |
789 | 0 | .input_set_best_block(Some(best)); |
790 | 0 | } |
791 | | |
792 | 0 | let finalized = runtime_subscription |
793 | 0 | .async_tree |
794 | 0 | .input_output_iter_unordered() |
795 | 0 | .find(|b| *b.user_data == hash) Unexecuted instantiation: _RNCNCNvMNtNtCsgnEOxJmACC4_13smoldot_light12sync_service9parachainINtB6_23ParachainBackgroundTaskpE3run0sf_0Ba_ Unexecuted instantiation: _RNCNCNvMNtNtCs508CmrSPkZh_13smoldot_light12sync_service9parachainINtB6_23ParachainBackgroundTaskNtNtCs7snhGEhbuap_18smoldot_light_wasm8platform11PlatformRefE3run0sf_0B1A_ Unexecuted instantiation: _RNCNCNvMNtNtCs508CmrSPkZh_13smoldot_light12sync_service9parachainINtB6_23ParachainBackgroundTaskpE3run0sf_0Ba_ |
796 | 0 | .unwrap() |
797 | | .id; |
798 | 0 | runtime_subscription.async_tree.input_finalize(finalized); |
799 | 0 | runtime_subscription.must_process_sync_tree = true; |
800 | | } |
801 | | |
802 | | ( |
803 | 0 | WakeUpReason::Notification(runtime_service::Notification::Block(block)), |
804 | 0 | ParachainBackgroundState::Subscribed(runtime_subscription), |
805 | | ) => { |
806 | | // Relay chain has a new block. |
807 | 0 | let hash = header::hash_from_scale_encoded_header(&block.scale_encoded_header); |
808 | | |
809 | 0 | log!( |
810 | 0 | &self.platform, |
811 | | Debug, |
812 | 0 | &self.log_target, |
813 | | "relay-chain-new-block", |
814 | 0 | hash = HashDisplay(&hash), |
815 | 0 | parent_hash = HashDisplay(&block.parent_hash) |
816 | | ); |
817 | | |
818 | 0 | let parent = runtime_subscription |
819 | 0 | .async_tree |
820 | 0 | .input_output_iter_unordered() |
821 | 0 | .find(|b| *b.user_data == block.parent_hash) Unexecuted instantiation: _RNCNCNvMNtNtCsgnEOxJmACC4_13smoldot_light12sync_service9parachainINtB6_23ParachainBackgroundTaskpE3run0sg_0Ba_ Unexecuted instantiation: _RNCNCNvMNtNtCs508CmrSPkZh_13smoldot_light12sync_service9parachainINtB6_23ParachainBackgroundTaskNtNtCs7snhGEhbuap_18smoldot_light_wasm8platform11PlatformRefE3run0sg_0B1A_ Unexecuted instantiation: _RNCNCNvMNtNtCs508CmrSPkZh_13smoldot_light12sync_service9parachainINtB6_23ParachainBackgroundTaskpE3run0sg_0Ba_ |
822 | 0 | .map(|b| b.id); // TODO: check if finalized |
823 | 0 | runtime_subscription.async_tree.input_insert_block( |
824 | 0 | hash, |
825 | 0 | parent, |
826 | | false, |
827 | 0 | block.is_new_best, |
828 | | ); |
829 | 0 | runtime_subscription.must_process_sync_tree = true; |
830 | | |
831 | 0 | runtime_subscription.next_start_parahead_fetch = Box::pin(future::ready(())); |
832 | | } |
833 | | |
834 | | ( |
835 | | WakeUpReason::Notification(runtime_service::Notification::BestBlockChanged { |
836 | 0 | hash, |
837 | | }), |
838 | 0 | ParachainBackgroundState::Subscribed(runtime_subscription), |
839 | | ) => { |
840 | | // Relay chain has a new best block. |
841 | 0 | log!( |
842 | 0 | &self.platform, |
843 | | Debug, |
844 | 0 | &self.log_target, |
845 | | "relay-chain-best-block-changed", |
846 | 0 | hash = HashDisplay(&hash) |
847 | | ); |
848 | | |
849 | | // If the block isn't found in `async_tree`, assume that it is equal to the |
850 | | // finalized block (that has left the tree already). |
851 | 0 | let node_idx = runtime_subscription |
852 | 0 | .async_tree |
853 | 0 | .input_output_iter_unordered() |
854 | 0 | .find(|b| *b.user_data == hash) Unexecuted instantiation: _RNCNCNvMNtNtCsgnEOxJmACC4_13smoldot_light12sync_service9parachainINtB6_23ParachainBackgroundTaskpE3run0si_0Ba_ Unexecuted instantiation: _RNCNCNvMNtNtCs508CmrSPkZh_13smoldot_light12sync_service9parachainINtB6_23ParachainBackgroundTaskNtNtCs7snhGEhbuap_18smoldot_light_wasm8platform11PlatformRefE3run0si_0B1A_ Unexecuted instantiation: _RNCNCNvMNtNtCs508CmrSPkZh_13smoldot_light12sync_service9parachainINtB6_23ParachainBackgroundTaskpE3run0si_0Ba_ |
855 | 0 | .map(|b| b.id); |
856 | 0 | runtime_subscription |
857 | 0 | .async_tree |
858 | 0 | .input_set_best_block(node_idx); |
859 | | |
860 | 0 | runtime_subscription.must_process_sync_tree = true; |
861 | | } |
862 | | |
863 | | (WakeUpReason::SubscriptionDead, _) => { |
864 | | // Recreate the channel. |
865 | 0 | log!( |
866 | 0 | &self.platform, |
867 | | Debug, |
868 | 0 | &self.log_target, |
869 | | "relay-chain-subscription-reset" |
870 | | ); |
871 | 0 | self.subscription_state = ParachainBackgroundState::NotSubscribed { |
872 | 0 | all_subscriptions: Vec::new(), |
873 | | subscribe_future: { |
874 | 0 | let relay_chain_sync = self.relay_chain_sync.clone(); |
875 | 0 | Box::pin(async move { |
876 | 0 | relay_chain_sync |
877 | 0 | .subscribe_all(32, NonZero::<usize>::new(usize::MAX).unwrap()) |
878 | 0 | .await |
879 | 0 | }) Unexecuted instantiation: _RNCNCNvMNtNtCsgnEOxJmACC4_13smoldot_light12sync_service9parachainINtB6_23ParachainBackgroundTaskpE3run0sk_0Ba_ Unexecuted instantiation: _RNCNCNvMNtNtCs508CmrSPkZh_13smoldot_light12sync_service9parachainINtB6_23ParachainBackgroundTaskNtNtCs7snhGEhbuap_18smoldot_light_wasm8platform11PlatformRefE3run0sk_0B1A_ Unexecuted instantiation: _RNCNCNvMNtNtCs508CmrSPkZh_13smoldot_light12sync_service9parachainINtB6_23ParachainBackgroundTaskpE3run0sk_0Ba_ |
880 | | }, |
881 | | }; |
882 | | } |
883 | | |
884 | | ( |
885 | | WakeUpReason::ParaheadFetchFinished { |
886 | 0 | async_op_id, |
887 | 0 | parahead_result: Ok(parahead), |
888 | | }, |
889 | 0 | ParachainBackgroundState::Subscribed(runtime_subscription), |
890 | | ) => { |
891 | | // A parahead fetching operation is successful. |
892 | 0 | log!( |
893 | 0 | &self.platform, |
894 | | Debug, |
895 | 0 | &self.log_target, |
896 | | "parahead-fetch-operation-success", |
897 | 0 | parahead_hash = HashDisplay( |
898 | 0 | blake2_rfc::blake2b::blake2b(32, b"", ¶head).as_bytes() |
899 | 0 | ), |
900 | 0 | relay_blocks = runtime_subscription |
901 | 0 | .async_tree |
902 | 0 | .async_op_blocks(async_op_id) |
903 | 0 | .map(|b| HashDisplay(b)) Unexecuted instantiation: _RNCNCNvMNtNtCsgnEOxJmACC4_13smoldot_light12sync_service9parachainINtB6_23ParachainBackgroundTaskpE3run0sr_0Ba_ Unexecuted instantiation: _RNCNCNvMNtNtCs508CmrSPkZh_13smoldot_light12sync_service9parachainINtB6_23ParachainBackgroundTaskNtNtCs7snhGEhbuap_18smoldot_light_wasm8platform11PlatformRefE3run0sr_0B1A_ Unexecuted instantiation: _RNCNCNvMNtNtCs508CmrSPkZh_13smoldot_light12sync_service9parachainINtB6_23ParachainBackgroundTaskpE3run0sr_0Ba_ |
904 | 0 | .join(",") |
905 | | ); |
906 | | |
907 | | // Unpin the relay blocks whose parahead is now known. |
908 | 0 | for block in runtime_subscription |
909 | 0 | .async_tree |
910 | 0 | .async_op_finished(async_op_id, Some(parahead)) |
911 | | { |
912 | 0 | let hash = &runtime_subscription.async_tree[block]; |
913 | 0 | runtime_subscription |
914 | 0 | .relay_chain_subscribe_all |
915 | 0 | .unpin_block(*hash) |
916 | 0 | .await; |
917 | | } |
918 | | |
919 | 0 | runtime_subscription.must_process_sync_tree = true; |
920 | | |
921 | 0 | runtime_subscription.next_start_parahead_fetch = Box::pin(future::ready(())); |
922 | | } |
923 | | |
924 | | ( |
925 | | WakeUpReason::ParaheadFetchFinished { |
926 | | parahead_result: |
927 | | Err(ParaheadError::PinRuntimeError( |
928 | | runtime_service::PinPinnedBlockRuntimeError::ObsoleteSubscription, |
929 | | )), |
930 | | .. |
931 | | }, |
932 | | _, |
933 | | ) => { |
934 | | // The relay chain runtime service has some kind of gap or issue and has |
935 | | // discarded the runtime. |
936 | | // Destroy the subscription and recreate the channels. |
937 | 0 | log!( |
938 | 0 | &self.platform, |
939 | | Debug, |
940 | 0 | &self.log_target, |
941 | | "relay-chain-subscription-reset" |
942 | | ); |
943 | 0 | self.subscription_state = ParachainBackgroundState::NotSubscribed { |
944 | 0 | all_subscriptions: Vec::new(), |
945 | | subscribe_future: { |
946 | 0 | let relay_chain_sync = self.relay_chain_sync.clone(); |
947 | 0 | Box::pin(async move { |
948 | 0 | relay_chain_sync |
949 | 0 | .subscribe_all(32, NonZero::<usize>::new(usize::MAX).unwrap()) |
950 | 0 | .await |
951 | 0 | }) Unexecuted instantiation: _RNCNCNvMNtNtCsgnEOxJmACC4_13smoldot_light12sync_service9parachainINtB6_23ParachainBackgroundTaskpE3run0sl_0Ba_ Unexecuted instantiation: _RNCNCNvMNtNtCs508CmrSPkZh_13smoldot_light12sync_service9parachainINtB6_23ParachainBackgroundTaskNtNtCs7snhGEhbuap_18smoldot_light_wasm8platform11PlatformRefE3run0sl_0B1A_ Unexecuted instantiation: _RNCNCNvMNtNtCs508CmrSPkZh_13smoldot_light12sync_service9parachainINtB6_23ParachainBackgroundTaskpE3run0sl_0Ba_ |
952 | | }, |
953 | | }; |
954 | | } |
955 | | |
956 | | ( |
957 | | WakeUpReason::ParaheadFetchFinished { |
958 | 0 | async_op_id, |
959 | 0 | parahead_result: Err(error), |
960 | | }, |
961 | 0 | ParachainBackgroundState::Subscribed(runtime_subscription), |
962 | | ) => { |
963 | | // Failed fetching a parahead. |
964 | | |
965 | | // Several relay chains initially didn't support parachains, and have later |
966 | | // been upgraded to support them. Similarly, the parachain might not have had a |
967 | | // core on the relay chain until recently. For these reasons, errors when the |
968 | | // relay chain is not near head of the chain are most likely normal and do |
969 | | // not warrant logging an error. |
970 | | // Note that `is_near_head_of_chain_heuristic` is normally not acceptable to |
971 | | // use due to being too vague, but since this is just about whether to print a |
972 | | // log message, it's completely fine. |
973 | 0 | if self |
974 | 0 | .relay_chain_sync |
975 | 0 | .is_near_head_of_chain_heuristic() |
976 | 0 | .await |
977 | 0 | && !error.is_network_problem() |
978 | | { |
979 | 0 | log!( |
980 | 0 | &self.platform, |
981 | | Error, |
982 | 0 | &self.log_target, |
983 | 0 | format!( |
984 | 0 | "Failed to fetch the parachain head from relay chain blocks {}: {}", |
985 | 0 | runtime_subscription |
986 | 0 | .async_tree |
987 | 0 | .async_op_blocks(async_op_id) |
988 | 0 | .map(|b| HashDisplay(b)) Unexecuted instantiation: _RNCNCNvMNtNtCsgnEOxJmACC4_13smoldot_light12sync_service9parachainINtB6_23ParachainBackgroundTaskpE3run0ss_0Ba_ Unexecuted instantiation: _RNCNCNvMNtNtCs508CmrSPkZh_13smoldot_light12sync_service9parachainINtB6_23ParachainBackgroundTaskNtNtCs7snhGEhbuap_18smoldot_light_wasm8platform11PlatformRefE3run0ss_0B1A_ Unexecuted instantiation: _RNCNCNvMNtNtCs508CmrSPkZh_13smoldot_light12sync_service9parachainINtB6_23ParachainBackgroundTaskpE3run0ss_0Ba_ |
989 | 0 | .join(", "), |
990 | | error |
991 | | ) |
992 | | ); |
993 | 0 | } |
994 | | |
995 | 0 | log!( |
996 | 0 | &self.platform, |
997 | | Debug, |
998 | 0 | &self.log_target, |
999 | | "parahead-fetch-operation-error", |
1000 | 0 | relay_blocks = runtime_subscription |
1001 | 0 | .async_tree |
1002 | 0 | .async_op_blocks(async_op_id) |
1003 | 0 | .map(|b| HashDisplay(b)) Unexecuted instantiation: _RNCNCNvMNtNtCsgnEOxJmACC4_13smoldot_light12sync_service9parachainINtB6_23ParachainBackgroundTaskpE3run0st_0Ba_ Unexecuted instantiation: _RNCNCNvMNtNtCs508CmrSPkZh_13smoldot_light12sync_service9parachainINtB6_23ParachainBackgroundTaskNtNtCs7snhGEhbuap_18smoldot_light_wasm8platform11PlatformRefE3run0st_0B1A_ Unexecuted instantiation: _RNCNCNvMNtNtCs508CmrSPkZh_13smoldot_light12sync_service9parachainINtB6_23ParachainBackgroundTaskpE3run0st_0Ba_ |
1004 | 0 | .join(","), |
1005 | | ?error |
1006 | | ); |
1007 | | |
1008 | 0 | runtime_subscription |
1009 | 0 | .async_tree |
1010 | 0 | .async_op_failure(async_op_id, &self.platform.now()); |
1011 | | |
1012 | 0 | runtime_subscription.next_start_parahead_fetch = Box::pin(future::ready(())); |
1013 | | } |
1014 | | |
1015 | | ( |
1016 | | WakeUpReason::ForegroundMessage(ToBackground::IsNearHeadOfChainHeuristic { |
1017 | 0 | send_back, |
1018 | | }), |
1019 | 0 | ParachainBackgroundState::Subscribed(sub), |
1020 | 0 | ) if sub.async_tree.output_finalized_async_user_data().is_some() => { |
1021 | | // Since there is a mapping between relay chain blocks and parachain blocks, |
1022 | | // whether a parachain is at the head of the chain is the same thing as whether |
1023 | | // its relay chain is at the head of the chain. |
1024 | | // Note that there is no ordering guarantee of any kind w.r.t. block |
1025 | | // subscriptions notifications. |
1026 | 0 | let val = self |
1027 | 0 | .relay_chain_sync |
1028 | 0 | .is_near_head_of_chain_heuristic() |
1029 | 0 | .await; |
1030 | 0 | let _ = send_back.send(val); |
1031 | | } |
1032 | | |
1033 | | ( |
1034 | | WakeUpReason::ForegroundMessage(ToBackground::IsNearHeadOfChainHeuristic { |
1035 | 0 | send_back, |
1036 | | }), |
1037 | | _, |
1038 | 0 | ) => { |
1039 | 0 | // If no finalized parahead is known yet, we might be very close to the head |
1040 | 0 | // but also maybe very very far away. We lean on the cautious side and always |
1041 | 0 | // return `false`. |
1042 | 0 | let _ = send_back.send(false); |
1043 | 0 | } |
1044 | | |
1045 | | ( |
1046 | | WakeUpReason::ForegroundMessage(ToBackground::SubscribeAll { |
1047 | 0 | send_back, |
1048 | 0 | buffer_size, |
1049 | | .. |
1050 | | }), |
1051 | | ParachainBackgroundState::NotSubscribed { |
1052 | 0 | all_subscriptions, .. |
1053 | | }, |
1054 | 0 | ) => { |
1055 | 0 | let (tx, new_blocks) = async_channel::bounded(buffer_size.saturating_sub(1)); |
1056 | 0 |
|
1057 | 0 | // No known finalized parahead. |
1058 | 0 | let _ = send_back.send(super::SubscribeAll { |
1059 | 0 | finalized_block_scale_encoded_header: self |
1060 | 0 | .obsolete_finalized_parahead |
1061 | 0 | .clone(), |
1062 | 0 | finalized_block_runtime: None, |
1063 | 0 | non_finalized_blocks_ancestry_order: Vec::new(), |
1064 | 0 | new_blocks, |
1065 | 0 | }); |
1066 | 0 |
|
1067 | 0 | all_subscriptions.push(tx); |
1068 | 0 | } |
1069 | | |
1070 | | ( |
1071 | | WakeUpReason::ForegroundMessage(ToBackground::SubscribeAll { |
1072 | 0 | send_back, |
1073 | 0 | buffer_size, |
1074 | | .. |
1075 | | }), |
1076 | 0 | ParachainBackgroundState::Subscribed(runtime_subscription), |
1077 | | ) => { |
1078 | 0 | let (tx, new_blocks) = async_channel::bounded(buffer_size.saturating_sub(1)); |
1079 | | |
1080 | | // There are two possibilities here: either we know of any recent finalized |
1081 | | // parahead, or we don't. In case where we don't know of any finalized parahead |
1082 | | // yet, we report a single obsolete finalized parahead, which is |
1083 | | // `obsolete_finalized_parahead`. The rest of this module makes sure that no |
1084 | | // other block is reported to subscriptions as long as this is the case, and |
1085 | | // that subscriptions are reset once the first known finalized parahead |
1086 | | // is known. |
1087 | 0 | if let Some(finalized_parahead) = runtime_subscription |
1088 | 0 | .async_tree |
1089 | 0 | .output_finalized_async_user_data() |
1090 | | { |
1091 | | // Finalized parahead is known. |
1092 | 0 | let finalized_parahash = |
1093 | 0 | header::hash_from_scale_encoded_header(finalized_parahead); |
1094 | 0 | let _ = send_back.send(super::SubscribeAll { |
1095 | 0 | finalized_block_scale_encoded_header: finalized_parahead.clone(), |
1096 | 0 | finalized_block_runtime: None, |
1097 | | non_finalized_blocks_ancestry_order: { |
1098 | 0 | let mut list = |
1099 | 0 | Vec::<([u8; 32], super::BlockNotification)>::with_capacity( |
1100 | 0 | runtime_subscription |
1101 | 0 | .async_tree |
1102 | 0 | .num_input_non_finalized_blocks(), |
1103 | | ); |
1104 | | |
1105 | 0 | for relay_block in runtime_subscription |
1106 | 0 | .async_tree |
1107 | 0 | .input_output_iter_ancestry_order() |
1108 | | { |
1109 | 0 | let parablock = match relay_block.async_op_user_data { |
1110 | 0 | Some(b) => b.as_ref().unwrap(), |
1111 | 0 | None => continue, |
1112 | | }; |
1113 | | |
1114 | 0 | let parablock_hash = |
1115 | 0 | header::hash_from_scale_encoded_header(parablock); |
1116 | | |
1117 | | // TODO: O(n) |
1118 | 0 | if let Some((_, entry)) = |
1119 | 0 | list.iter_mut().find(|(h, _)| *h == parablock_hash) Unexecuted instantiation: _RNCNCNvMNtNtCsgnEOxJmACC4_13smoldot_light12sync_service9parachainINtB6_23ParachainBackgroundTaskpE3run0sm_0Ba_ Unexecuted instantiation: _RNCNCNvMNtNtCs508CmrSPkZh_13smoldot_light12sync_service9parachainINtB6_23ParachainBackgroundTaskNtNtCs7snhGEhbuap_18smoldot_light_wasm8platform11PlatformRefE3run0sm_0B1A_ Unexecuted instantiation: _RNCNCNvMNtNtCs508CmrSPkZh_13smoldot_light12sync_service9parachainINtB6_23ParachainBackgroundTaskpE3run0sm_0Ba_ |
1120 | | { |
1121 | | // Block is already in the list. Don't add it a second time. |
1122 | 0 | if relay_block.is_output_best { |
1123 | 0 | entry.is_new_best = true; |
1124 | 0 | } |
1125 | 0 | continue; |
1126 | 0 | } |
1127 | | |
1128 | | // Find the parent of the parablock. This is done by going through |
1129 | | // the ancestors of the corresponding relay chain block (until and |
1130 | | // including the finalized relay chain block) until we find one |
1131 | | // whose parablock is different from the parablock in question. |
1132 | | // If none is found, the parablock is the same as the finalized |
1133 | | // parablock. |
1134 | 0 | let parent_hash = runtime_subscription |
1135 | 0 | .async_tree |
1136 | 0 | .ancestors(relay_block.id) |
1137 | 0 | .find_map(|idx| { |
1138 | 0 | let hash = header::hash_from_scale_encoded_header( |
1139 | 0 | runtime_subscription |
1140 | 0 | .async_tree |
1141 | 0 | .block_async_user_data(idx) |
1142 | 0 | .unwrap() |
1143 | 0 | .as_ref() |
1144 | 0 | .unwrap(), |
1145 | | ); |
1146 | 0 | if hash != parablock_hash { |
1147 | 0 | Some(hash) |
1148 | | } else { |
1149 | 0 | None |
1150 | | } |
1151 | 0 | }) Unexecuted instantiation: _RNCNCNvMNtNtCsgnEOxJmACC4_13smoldot_light12sync_service9parachainINtB6_23ParachainBackgroundTaskpE3run0sn_0Ba_ Unexecuted instantiation: _RNCNCNvMNtNtCs508CmrSPkZh_13smoldot_light12sync_service9parachainINtB6_23ParachainBackgroundTaskNtNtCs7snhGEhbuap_18smoldot_light_wasm8platform11PlatformRefE3run0sn_0B1A_ Unexecuted instantiation: _RNCNCNvMNtNtCs508CmrSPkZh_13smoldot_light12sync_service9parachainINtB6_23ParachainBackgroundTaskpE3run0sn_0Ba_ |
1152 | 0 | .or_else(|| { |
1153 | 0 | if finalized_parahash != parablock_hash { |
1154 | 0 | Some(finalized_parahash) |
1155 | | } else { |
1156 | 0 | None |
1157 | | } |
1158 | 0 | }); Unexecuted instantiation: _RNCNCNvMNtNtCsgnEOxJmACC4_13smoldot_light12sync_service9parachainINtB6_23ParachainBackgroundTaskpE3run0so_0Ba_ Unexecuted instantiation: _RNCNCNvMNtNtCs508CmrSPkZh_13smoldot_light12sync_service9parachainINtB6_23ParachainBackgroundTaskNtNtCs7snhGEhbuap_18smoldot_light_wasm8platform11PlatformRefE3run0so_0B1A_ Unexecuted instantiation: _RNCNCNvMNtNtCs508CmrSPkZh_13smoldot_light12sync_service9parachainINtB6_23ParachainBackgroundTaskpE3run0so_0Ba_ |
1159 | | |
1160 | | // `parent_hash` is `None` if the parablock is |
1161 | | // the same as the finalized parablock, in which case we |
1162 | | // don't add it to the list. |
1163 | 0 | if let Some(parent_hash) = parent_hash { |
1164 | 0 | debug_assert!( |
1165 | 0 | list.iter().filter(|(h, _)| *h == parent_hash).count() Unexecuted instantiation: _RNCNCNvMNtNtCsgnEOxJmACC4_13smoldot_light12sync_service9parachainINtB6_23ParachainBackgroundTaskpE3run0su_0Ba_ Unexecuted instantiation: _RNCNCNvMNtNtCs508CmrSPkZh_13smoldot_light12sync_service9parachainINtB6_23ParachainBackgroundTaskNtNtCs7snhGEhbuap_18smoldot_light_wasm8platform11PlatformRefE3run0su_0B1A_ Unexecuted instantiation: _RNCNCNvMNtNtCs508CmrSPkZh_13smoldot_light12sync_service9parachainINtB6_23ParachainBackgroundTaskpE3run0su_0Ba_ |
1166 | | == 1 |
1167 | 0 | || parent_hash == finalized_parahash |
1168 | | ); |
1169 | 0 | list.push(( |
1170 | 0 | parablock_hash, |
1171 | 0 | super::BlockNotification { |
1172 | 0 | is_new_best: relay_block.is_output_best, |
1173 | 0 | scale_encoded_header: parablock.clone(), |
1174 | 0 | parent_hash, |
1175 | 0 | }, |
1176 | 0 | )); |
1177 | 0 | } |
1178 | | } |
1179 | | |
1180 | 0 | list.into_iter().map(|(_, v)| v).collect() |
1181 | | }, |
1182 | 0 | new_blocks, |
1183 | | }); |
1184 | 0 | } else { |
1185 | 0 | // No known finalized parahead. |
1186 | 0 | let _ = send_back.send(super::SubscribeAll { |
1187 | 0 | finalized_block_scale_encoded_header: self |
1188 | 0 | .obsolete_finalized_parahead |
1189 | 0 | .clone(), |
1190 | 0 | finalized_block_runtime: None, |
1191 | 0 | non_finalized_blocks_ancestry_order: Vec::new(), |
1192 | 0 | new_blocks, |
1193 | 0 | }); |
1194 | 0 | } |
1195 | | |
1196 | 0 | runtime_subscription.all_subscriptions.push(tx); |
1197 | | } |
1198 | | |
1199 | | ( |
1200 | | WakeUpReason::ForegroundMessage(ToBackground::PeersAssumedKnowBlock { |
1201 | 0 | send_back, |
1202 | | .. |
1203 | | }), |
1204 | | _, |
1205 | 0 | ) => { |
1206 | 0 | // Simply assume that all peers know about all blocks. |
1207 | 0 | // |
1208 | 0 | // We could in principle check whether the block is higher than the current |
1209 | 0 | // finalized block, and if not if it is in the list of paraheads found in the |
1210 | 0 | // relay chain. But because parachain blocks might not be decodable, we can't |
1211 | 0 | // know their number, and thus we can't know if the requested block is a |
1212 | 0 | // descendant of the finalized block. |
1213 | 0 | // Assuming that all peers know all blocks is the only sane way of |
1214 | 0 | // implementing this. |
1215 | 0 | let _ = send_back.send(self.sync_sources.keys().cloned().collect()); |
1216 | 0 | } |
1217 | | |
1218 | 0 | (WakeUpReason::ForegroundMessage(ToBackground::SyncingPeers { send_back }), _) => { |
1219 | 0 | let _ = send_back.send( |
1220 | 0 | self.sync_sources |
1221 | 0 | .iter() |
1222 | 0 | .map(|(peer_id, (role, best_height, best_hash))| { |
1223 | | //let (height, hash) = self.sync_sources.best_block(local_id); |
1224 | 0 | (peer_id.clone(), *role, *best_height, *best_hash) |
1225 | 0 | }) Unexecuted instantiation: _RNCNCNvMNtNtCsgnEOxJmACC4_13smoldot_light12sync_service9parachainINtB6_23ParachainBackgroundTaskpE3run0sq_0Ba_ Unexecuted instantiation: _RNCNCNvMNtNtCs508CmrSPkZh_13smoldot_light12sync_service9parachainINtB6_23ParachainBackgroundTaskNtNtCs7snhGEhbuap_18smoldot_light_wasm8platform11PlatformRefE3run0sq_0B1A_ Unexecuted instantiation: _RNCNCNvMNtNtCs508CmrSPkZh_13smoldot_light12sync_service9parachainINtB6_23ParachainBackgroundTaskpE3run0sq_0Ba_ |
1226 | 0 | .collect(), |
1227 | | ); |
1228 | | } |
1229 | | |
1230 | | ( |
1231 | | WakeUpReason::ForegroundMessage(ToBackground::SerializeChainInformation { |
1232 | 0 | send_back, |
1233 | | }), |
1234 | | _, |
1235 | 0 | ) => { |
1236 | 0 | let _ = send_back.send(None); |
1237 | 0 | } |
1238 | | |
1239 | | (WakeUpReason::MustSubscribeNetworkEvents, _) => { |
1240 | 0 | debug_assert!(self.from_network_service.is_none()); |
1241 | 0 | self.sync_sources.clear(); |
1242 | 0 | self.from_network_service = Some(Box::pin( |
1243 | | // As documented, `subscribe().await` is expected to return quickly. |
1244 | 0 | self.network_service.subscribe().await, |
1245 | | )); |
1246 | | } |
1247 | | |
1248 | | ( |
1249 | | WakeUpReason::NetworkEvent(network_service::Event::Connected { |
1250 | 0 | peer_id, |
1251 | 0 | role, |
1252 | 0 | best_block_number, |
1253 | 0 | best_block_hash, |
1254 | | }), |
1255 | | _, |
1256 | | ) => { |
1257 | 0 | let _former_value = self |
1258 | 0 | .sync_sources |
1259 | 0 | .insert(peer_id, (role, best_block_number, best_block_hash)); |
1260 | 0 | debug_assert!(_former_value.is_none()); |
1261 | | } |
1262 | | |
1263 | | ( |
1264 | 0 | WakeUpReason::NetworkEvent(network_service::Event::Disconnected { peer_id }), |
1265 | | _, |
1266 | | ) => { |
1267 | 0 | let _role = self.sync_sources.remove(&peer_id); |
1268 | 0 | debug_assert!(_role.is_some()); |
1269 | | } |
1270 | | |
1271 | | ( |
1272 | | WakeUpReason::NetworkEvent(network_service::Event::BlockAnnounce { |
1273 | 0 | peer_id: _peer_id, |
1274 | | .. |
1275 | | }), |
1276 | | _, |
1277 | | ) => { |
1278 | 0 | debug_assert!(self.sync_sources.contains_key(&_peer_id)); |
1279 | | } |
1280 | | |
1281 | 0 | (WakeUpReason::NetworkEvent(_), _) => { |
1282 | 0 | // Uninteresting message. |
1283 | 0 | } |
1284 | | |
1285 | | ( |
1286 | | WakeUpReason::ParaheadFetchFinished { .. } |
1287 | | | WakeUpReason::AdvanceSyncTree |
1288 | | | WakeUpReason::Notification(_) |
1289 | | | WakeUpReason::StartParaheadFetch, |
1290 | | ParachainBackgroundState::NotSubscribed { .. }, |
1291 | | ) => { |
1292 | | // These paths are unreachable. |
1293 | 0 | debug_assert!(false); |
1294 | | } |
1295 | | } |
1296 | | } |
1297 | 0 | } Unexecuted instantiation: _RNCNvMNtNtCsgnEOxJmACC4_13smoldot_light12sync_service9parachainINtB4_23ParachainBackgroundTaskpE3run0B8_ Unexecuted instantiation: _RNCNvMNtNtCs508CmrSPkZh_13smoldot_light12sync_service9parachainINtB4_23ParachainBackgroundTaskNtNtCs7snhGEhbuap_18smoldot_light_wasm8platform11PlatformRefE3run0B1y_ Unexecuted instantiation: _RNCNvMNtNtCs508CmrSPkZh_13smoldot_light12sync_service9parachainINtB4_23ParachainBackgroundTaskpE3run0B8_ |
1298 | | } |
1299 | | |
1300 | 0 | async fn fetch_parahead<TPlat: PlatformRef>( |
1301 | 0 | relay_chain_sync: &Arc<runtime_service::RuntimeService<TPlat>>, |
1302 | 0 | subscription_id: runtime_service::SubscriptionId, |
1303 | 0 | parachain_id: u32, |
1304 | 0 | block_hash: &[u8; 32], |
1305 | 0 | ) -> Result<Vec<u8>, ParaheadError> { Unexecuted instantiation: _RINvNtNtCsgnEOxJmACC4_13smoldot_light12sync_service9parachain14fetch_paraheadpEB6_ Unexecuted instantiation: _RINvNtNtCs508CmrSPkZh_13smoldot_light12sync_service9parachain14fetch_paraheadNtNtCs7snhGEhbuap_18smoldot_light_wasm8platform11PlatformRefEB1h_ Unexecuted instantiation: _RINvNtNtCs508CmrSPkZh_13smoldot_light12sync_service9parachain14fetch_paraheadpEB6_ |
1306 | | // Call `ParachainHost_persisted_validation_data` in order to know where the parachain is. |
1307 | 0 | let (pinned_runtime, block_state_trie_root, block_number) = relay_chain_sync |
1308 | 0 | .pin_pinned_block_runtime(subscription_id, *block_hash) |
1309 | 0 | .await |
1310 | 0 | .map_err(ParaheadError::PinRuntimeError)?; |
1311 | 0 | let success = relay_chain_sync |
1312 | 0 | .runtime_call( |
1313 | 0 | pinned_runtime, |
1314 | 0 | *block_hash, |
1315 | 0 | block_number, |
1316 | 0 | block_state_trie_root, |
1317 | 0 | para::PERSISTED_VALIDATION_FUNCTION_NAME.to_owned(), |
1318 | 0 | None, // TODO: /!\ |
1319 | 0 | para::persisted_validation_data_parameters( |
1320 | 0 | parachain_id, |
1321 | 0 | para::OccupiedCoreAssumption::TimedOut, |
1322 | | ) |
1323 | 0 | .fold(Vec::new(), |mut a, b| { |
1324 | 0 | a.extend_from_slice(b.as_ref()); |
1325 | 0 | a |
1326 | 0 | }), Unexecuted instantiation: _RNCNCINvNtNtCsgnEOxJmACC4_13smoldot_light12sync_service9parachain14fetch_paraheadpE00Ba_ Unexecuted instantiation: _RNCNCINvNtNtCs508CmrSPkZh_13smoldot_light12sync_service9parachain14fetch_paraheadNtNtCs7snhGEhbuap_18smoldot_light_wasm8platform11PlatformRefE00B1l_ Unexecuted instantiation: _RNCNCINvNtNtCs508CmrSPkZh_13smoldot_light12sync_service9parachain14fetch_paraheadpE00Ba_ |
1327 | | 6, |
1328 | 0 | Duration::from_secs(10), |
1329 | 0 | NonZero::<u32>::new(2).unwrap(), |
1330 | | ) |
1331 | 0 | .await |
1332 | 0 | .map_err(ParaheadError::RuntimeCall)?; |
1333 | | |
1334 | | // Try decode the result of the runtime call. |
1335 | | // If this fails, it indicates an incompatibility between smoldot and the relay chain. |
1336 | 0 | match para::decode_persisted_validation_data_return_value( |
1337 | 0 | &success.output, |
1338 | 0 | relay_chain_sync.block_number_bytes(), |
1339 | | ) { |
1340 | 0 | Ok(Some(pvd)) => Ok(pvd.parent_head.to_vec()), |
1341 | 0 | Ok(None) => Err(ParaheadError::NoCore), |
1342 | 0 | Err(error) => Err(ParaheadError::InvalidRuntimeOutput(error)), |
1343 | | } |
1344 | 0 | } Unexecuted instantiation: _RNCINvNtNtCsgnEOxJmACC4_13smoldot_light12sync_service9parachain14fetch_paraheadpE0B8_ Unexecuted instantiation: _RNCINvNtNtCs508CmrSPkZh_13smoldot_light12sync_service9parachain14fetch_paraheadNtNtCs7snhGEhbuap_18smoldot_light_wasm8platform11PlatformRefE0B1j_ Unexecuted instantiation: _RNCINvNtNtCs508CmrSPkZh_13smoldot_light12sync_service9parachain14fetch_paraheadpE0B8_ |
1345 | | |
1346 | | /// Error that can happen when fetching the parachain head corresponding to a relay chain block. |
1347 | | #[derive(Debug, derive_more::Display, derive_more::Error)] |
1348 | | enum ParaheadError { |
1349 | | /// Error while performing call request over the network. |
1350 | | #[display("Error while performing call request over the network: {_0}")] |
1351 | | RuntimeCall(runtime_service::RuntimeCallError), |
1352 | | /// Error pinning the runtime of the block. |
1353 | | PinRuntimeError(runtime_service::PinPinnedBlockRuntimeError), |
1354 | | /// Parachain doesn't have a core in the relay chain. |
1355 | | NoCore, |
1356 | | /// Error while decoding the output of the call. |
1357 | | /// |
1358 | | /// This indicates some kind of incompatibility between smoldot and the relay chain. |
1359 | | #[display("Error while decoding the output of the call: {_0}")] |
1360 | | InvalidRuntimeOutput(para::Error), |
1361 | | } |
1362 | | |
1363 | | impl ParaheadError { |
1364 | | /// Returns `true` if this is caused by networking issues, as opposed to a consensus-related |
1365 | | /// issue. |
1366 | 0 | fn is_network_problem(&self) -> bool { |
1367 | 0 | match self { |
1368 | 0 | ParaheadError::RuntimeCall(runtime_service::RuntimeCallError::Inaccessible(_)) => true, |
1369 | | ParaheadError::RuntimeCall( |
1370 | | runtime_service::RuntimeCallError::Execution(_) |
1371 | | | runtime_service::RuntimeCallError::Crash |
1372 | | | runtime_service::RuntimeCallError::InvalidRuntime(_) |
1373 | | | runtime_service::RuntimeCallError::ApiVersionRequirementUnfulfilled, |
1374 | 0 | ) => false, |
1375 | 0 | ParaheadError::PinRuntimeError(_) => false, |
1376 | 0 | ParaheadError::NoCore => false, |
1377 | 0 | ParaheadError::InvalidRuntimeOutput(_) => false, |
1378 | | } |
1379 | 0 | } Unexecuted instantiation: _RNvMs_NtNtCsgnEOxJmACC4_13smoldot_light12sync_service9parachainNtB4_13ParaheadError18is_network_problem Unexecuted instantiation: _RNvMs_NtNtCs508CmrSPkZh_13smoldot_light12sync_service9parachainNtB4_13ParaheadError18is_network_problem |
1380 | | } |