/__w/smoldot/smoldot/repo/light-base/src/sync_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 syncing service. |
19 | | //! |
20 | | //! The role of the [`SyncService`] is to do whatever necessary to obtain and stay up-to-date |
21 | | //! with the best and the finalized blocks of a chain. |
22 | | //! |
23 | | //! The configuration of the chain to synchronize must be passed when creating a [`SyncService`], |
24 | | //! after which it will spawn background tasks and use the networking service to stay |
25 | | //! synchronized. |
26 | | //! |
27 | | //! Use [`SyncService::subscribe_all`] to get notified about updates to the state of the chain. |
28 | | |
29 | | use crate::{log, network_service, platform::PlatformRef, runtime_service}; |
30 | | |
31 | | use alloc::{ |
32 | | borrow::ToOwned as _, boxed::Box, collections::VecDeque, format, string::String, sync::Arc, |
33 | | vec::Vec, |
34 | | }; |
35 | | use core::{cmp, fmt, future::Future, mem, num::NonZeroU32, pin::Pin, time::Duration}; |
36 | | use futures_channel::oneshot; |
37 | | use rand::seq::IteratorRandom as _; |
38 | | use rand_chacha::rand_core::SeedableRng as _; |
39 | | use smoldot::{ |
40 | | chain, |
41 | | executor::host, |
42 | | libp2p::PeerId, |
43 | | network::{codec, service}, |
44 | | trie::{self, prefix_proof, proof_decode, Nibble}, |
45 | | }; |
46 | | |
47 | | mod parachain; |
48 | | mod standalone; |
49 | | |
50 | | pub use network_service::Role; |
51 | | |
52 | | /// Configuration for a [`SyncService`]. |
53 | | pub struct Config<TPlat: PlatformRef> { |
54 | | /// Name of the chain, for logging purposes. |
55 | | /// |
56 | | /// > **Note**: This name will be directly printed out. Any special character should already |
57 | | /// > have been filtered out from this name. |
58 | | pub log_name: String, |
59 | | |
60 | | /// Number of bytes of the block number in the networking protocol. |
61 | | pub block_number_bytes: usize, |
62 | | |
63 | | /// Access to the platform's capabilities. |
64 | | pub platform: TPlat, |
65 | | |
66 | | /// Access to the network, and index of the chain to sync from the point of view of the |
67 | | /// network service. |
68 | | pub network_service: Arc<network_service::NetworkServiceChain<TPlat>>, |
69 | | /// Extra fields depending on whether the chain is a relay chain or a parachain. |
70 | | pub chain_type: ConfigChainType<TPlat>, |
71 | | } |
72 | | |
73 | | /// See [`Config::chain_type`]. |
74 | | pub enum ConfigChainType<TPlat: PlatformRef> { |
75 | | /// Chain is a relay chain. |
76 | | RelayChain(ConfigRelayChain), |
77 | | /// Chain is a parachain. |
78 | | Parachain(ConfigParachain<TPlat>), |
79 | | } |
80 | | |
81 | | /// See [`ConfigChainType::RelayChain`]. |
82 | | pub struct ConfigRelayChain { |
83 | | /// State of the finalized chain. |
84 | | pub chain_information: chain::chain_information::ValidChainInformation, |
85 | | |
86 | | /// Known valid Merkle value and storage value combination for the `:code` key. |
87 | | /// |
88 | | /// If provided, the warp syncing algorithm will first fetch the Merkle value of `:code`, and |
89 | | /// if it matches the Merkle value provided in the hint, use the storage value in the hint |
90 | | /// instead of downloading it. If the hint doesn't match, an extra round-trip will be needed, |
91 | | /// but if the hint matches it saves a big download. |
92 | | pub runtime_code_hint: Option<ConfigRelayChainRuntimeCodeHint>, |
93 | | } |
94 | | |
95 | | /// See [`ConfigRelayChain::runtime_code_hint`]. |
96 | | pub struct ConfigRelayChainRuntimeCodeHint { |
97 | | /// Storage value of the `:code` trie node corresponding to |
98 | | /// [`ConfigRelayChainRuntimeCodeHint::merkle_value`]. |
99 | | pub storage_value: Vec<u8>, |
100 | | /// Merkle value of the `:code` trie node in the storage main trie. |
101 | | pub merkle_value: Vec<u8>, |
102 | | /// Closest ancestor of the `:code` key except for `:code` itself. |
103 | | pub closest_ancestor_excluding: Vec<Nibble>, |
104 | | } |
105 | | |
106 | | /// See [`ConfigChainType::Parachain`]. |
107 | | pub struct ConfigParachain<TPlat: PlatformRef> { |
108 | | /// Runtime service that synchronizes the relay chain of this parachain. |
109 | | pub relay_chain_sync: Arc<runtime_service::RuntimeService<TPlat>>, |
110 | | |
111 | | /// SCALE-encoded header of a known finalized block of the parachain. Used in the situation |
112 | | /// where the API user subscribes using [`SyncService::subscribe_all`] before any parachain |
113 | | /// block can be gathered. |
114 | | pub finalized_block_header: Vec<u8>, |
115 | | |
116 | | /// Id of the parachain within the relay chain. |
117 | | /// |
118 | | /// This is an arbitrary number used to identify the parachain within the storage of the |
119 | | /// relay chain. |
120 | | /// |
121 | | /// > **Note**: This information is normally found in the chain specification of the |
122 | | /// > parachain. |
123 | | pub para_id: u32, |
124 | | } |
125 | | |
126 | | /// Identifier for a blocks request to be performed. |
127 | | #[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] |
128 | | pub struct BlocksRequestId(usize); |
129 | | |
130 | | pub struct SyncService<TPlat: PlatformRef> { |
131 | | /// Sender of messages towards the background task. |
132 | | to_background: async_channel::Sender<ToBackground>, |
133 | | |
134 | | /// See [`Config::platform`]. |
135 | | platform: TPlat, |
136 | | |
137 | | /// See [`Config::network_service`]. |
138 | | network_service: Arc<network_service::NetworkServiceChain<TPlat>>, |
139 | | /// See [`Config::block_number_bytes`]. |
140 | | block_number_bytes: usize, |
141 | | } |
142 | | |
143 | | impl<TPlat: PlatformRef> SyncService<TPlat> { |
144 | 0 | pub fn new(config: Config<TPlat>) -> Self { |
145 | 0 | let (to_background, from_foreground) = async_channel::bounded(16); |
146 | 0 | let from_foreground = Box::pin(from_foreground); |
147 | 0 |
|
148 | 0 | let log_target = format!("sync-service-{}", config.log_name); |
149 | | |
150 | 0 | let task: Pin<Box<dyn Future<Output = ()> + Send>> = match config.chain_type { |
151 | 0 | ConfigChainType::Parachain(config_parachain) => Box::pin(parachain::start_parachain( |
152 | 0 | log_target.clone(), |
153 | 0 | config.platform.clone(), |
154 | 0 | config_parachain.finalized_block_header, |
155 | 0 | config.block_number_bytes, |
156 | 0 | config_parachain.relay_chain_sync.clone(), |
157 | 0 | config_parachain.para_id, |
158 | 0 | from_foreground, |
159 | 0 | config.network_service.clone(), |
160 | 0 | )), |
161 | 0 | ConfigChainType::RelayChain(config_relay_chain) => { |
162 | 0 | Box::pin(standalone::start_standalone_chain( |
163 | 0 | log_target.clone(), |
164 | 0 | config.platform.clone(), |
165 | 0 | config_relay_chain.chain_information, |
166 | 0 | config.block_number_bytes, |
167 | 0 | config_relay_chain.runtime_code_hint, |
168 | 0 | from_foreground, |
169 | 0 | config.network_service.clone(), |
170 | 0 | )) |
171 | | } |
172 | | }; |
173 | | |
174 | 0 | config.platform.spawn_task(log_target.clone().into(), { |
175 | 0 | let platform = config.platform.clone(); |
176 | 0 | async move { |
177 | 0 | task.await; |
178 | 0 | log!(&platform, Debug, &log_target, "shutdown"); |
179 | 0 | } Unexecuted instantiation: _RNCNvMNtCsiGub1lfKphe_13smoldot_light12sync_serviceINtB4_11SyncServicepE3new0B6_ Unexecuted instantiation: _RNCNvMNtCsih6EgvAwZF2_13smoldot_light12sync_serviceINtB4_11SyncServiceNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE3new0B1a_ Unexecuted instantiation: _RNCNvMNtCsih6EgvAwZF2_13smoldot_light12sync_serviceINtB4_11SyncServicepE3new0B6_ |
180 | 0 | }); |
181 | 0 |
|
182 | 0 | SyncService { |
183 | 0 | to_background, |
184 | 0 | platform: config.platform, |
185 | 0 | network_service: config.network_service, |
186 | 0 | block_number_bytes: config.block_number_bytes, |
187 | 0 | } |
188 | 0 | } Unexecuted instantiation: _RNvMNtCsiGub1lfKphe_13smoldot_light12sync_serviceINtB2_11SyncServicepE3newB4_ Unexecuted instantiation: _RNvMNtCsih6EgvAwZF2_13smoldot_light12sync_serviceINtB2_11SyncServiceNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE3newB18_ Unexecuted instantiation: _RNvMNtCsih6EgvAwZF2_13smoldot_light12sync_serviceINtB2_11SyncServicepE3newB4_ |
189 | | |
190 | | /// Returns the value initially passed as [`Config::block_number_bytes`̀]. |
191 | 0 | pub fn block_number_bytes(&self) -> usize { |
192 | 0 | self.block_number_bytes |
193 | 0 | } Unexecuted instantiation: _RNvMNtCsiGub1lfKphe_13smoldot_light12sync_serviceINtB2_11SyncServicepE18block_number_bytesB4_ Unexecuted instantiation: _RNvMNtCsih6EgvAwZF2_13smoldot_light12sync_serviceINtB2_11SyncServiceNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE18block_number_bytesB18_ Unexecuted instantiation: _RNvMNtCsih6EgvAwZF2_13smoldot_light12sync_serviceINtB2_11SyncServicepE18block_number_bytesB4_ |
194 | | |
195 | | /// Returns the state of the finalized block of the chain, after passing it through |
196 | | /// [`smoldot::database::finalized_serialize::encode_chain`]. |
197 | | /// |
198 | | /// Returns `None` if this information couldn't be obtained because not enough is known about |
199 | | /// the chain. |
200 | 0 | pub async fn serialize_chain_information( |
201 | 0 | &self, |
202 | 0 | ) -> Option<chain::chain_information::ValidChainInformation> { Unexecuted instantiation: _RNvMNtCsiGub1lfKphe_13smoldot_light12sync_serviceINtB2_11SyncServicepE27serialize_chain_informationB4_ Unexecuted instantiation: _RNvMNtCsih6EgvAwZF2_13smoldot_light12sync_serviceINtB2_11SyncServiceNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE27serialize_chain_informationB18_ Unexecuted instantiation: _RNvMNtCsih6EgvAwZF2_13smoldot_light12sync_serviceINtB2_11SyncServicepE27serialize_chain_informationB4_ |
203 | 0 | let (send_back, rx) = oneshot::channel(); |
204 | 0 |
|
205 | 0 | self.to_background |
206 | 0 | .send(ToBackground::SerializeChainInformation { send_back }) |
207 | 0 | .await |
208 | 0 | .unwrap(); |
209 | 0 |
|
210 | 0 | rx.await.unwrap() |
211 | 0 | } Unexecuted instantiation: _RNCNvMNtCsiGub1lfKphe_13smoldot_light12sync_serviceINtB4_11SyncServicepE27serialize_chain_information0B6_ Unexecuted instantiation: _RNCNvMNtCsih6EgvAwZF2_13smoldot_light12sync_serviceINtB4_11SyncServiceNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE27serialize_chain_information0B1a_ Unexecuted instantiation: _RNCNvMNtCsih6EgvAwZF2_13smoldot_light12sync_serviceINtB4_11SyncServicepE27serialize_chain_information0B6_ |
212 | | |
213 | | /// Subscribes to the state of the chain: the current state and the new blocks. |
214 | | /// |
215 | | /// All new blocks are reported. Only up to `buffer_size` block notifications are buffered |
216 | | /// in the channel. If the channel is full when a new notification is attempted to be pushed, |
217 | | /// the channel gets closed. |
218 | | /// |
219 | | /// The channel also gets closed if a gap in the finality happens, such as after a Grandpa |
220 | | /// warp syncing. |
221 | | /// |
222 | | /// See [`SubscribeAll`] for information about the return value. |
223 | | /// |
224 | | /// If `runtime_interest` is `false`, then [`SubscribeAll::finalized_block_runtime`] will |
225 | | /// always be `None`. Since the runtime can only be provided to one call to this function, |
226 | | /// only one subscriber should use `runtime_interest` equal to `true`. |
227 | | /// |
228 | | /// While this function is asynchronous, it is guaranteed to finish relatively quickly. Only |
229 | | /// CPU operations are performed. |
230 | 0 | pub async fn subscribe_all(&self, buffer_size: usize, runtime_interest: bool) -> SubscribeAll { Unexecuted instantiation: _RNvMNtCsiGub1lfKphe_13smoldot_light12sync_serviceINtB2_11SyncServicepE13subscribe_allB4_ Unexecuted instantiation: _RNvMNtCsih6EgvAwZF2_13smoldot_light12sync_serviceINtB2_11SyncServiceNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE13subscribe_allB18_ Unexecuted instantiation: _RNvMNtCsih6EgvAwZF2_13smoldot_light12sync_serviceINtB2_11SyncServicepE13subscribe_allB4_ |
231 | 0 | let (send_back, rx) = oneshot::channel(); |
232 | 0 |
|
233 | 0 | self.to_background |
234 | 0 | .send(ToBackground::SubscribeAll { |
235 | 0 | send_back, |
236 | 0 | buffer_size, |
237 | 0 | runtime_interest, |
238 | 0 | }) |
239 | 0 | .await |
240 | 0 | .unwrap(); |
241 | 0 |
|
242 | 0 | rx.await.unwrap() |
243 | 0 | } Unexecuted instantiation: _RNCNvMNtCsiGub1lfKphe_13smoldot_light12sync_serviceINtB4_11SyncServicepE13subscribe_all0B6_ Unexecuted instantiation: _RNCNvMNtCsih6EgvAwZF2_13smoldot_light12sync_serviceINtB4_11SyncServiceNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE13subscribe_all0B1a_ Unexecuted instantiation: _RNCNvMNtCsih6EgvAwZF2_13smoldot_light12sync_serviceINtB4_11SyncServicepE13subscribe_all0B6_ |
244 | | |
245 | | /// Returns true if it is believed that we are near the head of the chain. |
246 | | /// |
247 | | /// The way this method is implemented is opaque and cannot be relied on. The return value |
248 | | /// should only ever be shown to the user and not used for any meaningful logic. |
249 | 0 | pub async fn is_near_head_of_chain_heuristic(&self) -> bool { Unexecuted instantiation: _RNvMNtCsiGub1lfKphe_13smoldot_light12sync_serviceINtB2_11SyncServicepE31is_near_head_of_chain_heuristicB4_ Unexecuted instantiation: _RNvMNtCsih6EgvAwZF2_13smoldot_light12sync_serviceINtB2_11SyncServiceNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE31is_near_head_of_chain_heuristicB18_ Unexecuted instantiation: _RNvMNtCsih6EgvAwZF2_13smoldot_light12sync_serviceINtB2_11SyncServicepE31is_near_head_of_chain_heuristicB4_ |
250 | 0 | let (send_back, rx) = oneshot::channel(); |
251 | 0 |
|
252 | 0 | self.to_background |
253 | 0 | .send(ToBackground::IsNearHeadOfChainHeuristic { send_back }) |
254 | 0 | .await |
255 | 0 | .unwrap(); |
256 | 0 |
|
257 | 0 | rx.await.unwrap() |
258 | 0 | } Unexecuted instantiation: _RNCNvMNtCsiGub1lfKphe_13smoldot_light12sync_serviceINtB4_11SyncServicepE31is_near_head_of_chain_heuristic0B6_ Unexecuted instantiation: _RNCNvMNtCsih6EgvAwZF2_13smoldot_light12sync_serviceINtB4_11SyncServiceNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE31is_near_head_of_chain_heuristic0B1a_ Unexecuted instantiation: _RNCNvMNtCsih6EgvAwZF2_13smoldot_light12sync_serviceINtB4_11SyncServicepE31is_near_head_of_chain_heuristic0B6_ |
259 | | |
260 | | /// Returns the list of peers from the [`network_service::NetworkService`] that are used to |
261 | | /// synchronize blocks. |
262 | | /// |
263 | | /// Returns, for each peer, their identity and best block number and hash. |
264 | | /// |
265 | | /// This function is subject to race condition. The list returned by this function can change |
266 | | /// at any moment. The return value should only ever be shown to the user and not used for any |
267 | | /// meaningful logic |
268 | 0 | pub async fn syncing_peers( |
269 | 0 | &self, |
270 | 0 | ) -> impl ExactSizeIterator<Item = (PeerId, codec::Role, u64, [u8; 32])> { Unexecuted instantiation: _RNvMNtCsiGub1lfKphe_13smoldot_light12sync_serviceINtB2_11SyncServicepE13syncing_peersB4_ Unexecuted instantiation: _RNvMNtCsih6EgvAwZF2_13smoldot_light12sync_serviceINtB2_11SyncServiceNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE13syncing_peersB18_ Unexecuted instantiation: _RNvMNtCsih6EgvAwZF2_13smoldot_light12sync_serviceINtB2_11SyncServicepE13syncing_peersB4_ |
271 | 0 | let (send_back, rx) = oneshot::channel(); |
272 | 0 |
|
273 | 0 | self.to_background |
274 | 0 | .send(ToBackground::SyncingPeers { send_back }) |
275 | 0 | .await |
276 | 0 | .unwrap(); |
277 | 0 |
|
278 | 0 | rx.await.unwrap().into_iter() |
279 | 0 | } Unexecuted instantiation: _RNCNvMNtCsiGub1lfKphe_13smoldot_light12sync_serviceINtB4_11SyncServicepE13syncing_peers0B6_ Unexecuted instantiation: _RNCNvMNtCsih6EgvAwZF2_13smoldot_light12sync_serviceINtB4_11SyncServiceNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE13syncing_peers0B1a_ Unexecuted instantiation: _RNCNvMNtCsih6EgvAwZF2_13smoldot_light12sync_serviceINtB4_11SyncServicepE13syncing_peers0B6_ |
280 | | |
281 | | /// Returns the list of peers from the [`network_service::NetworkService`] that are expected to |
282 | | /// be aware of the given block. |
283 | | /// |
284 | | /// A peer is returned by this method either if it has directly sent a block announce in the |
285 | | /// past, or if the requested block height is below the finalized block height and the best |
286 | | /// block of the peer is above the requested block. In other words, it is assumed that all |
287 | | /// peers are always on the same finalized chain as the local node. |
288 | | /// |
289 | | /// This function is subject to race condition. The list returned by this function is not |
290 | | /// necessarily exact, as a peer might have known about a block in the past but no longer |
291 | | /// does. |
292 | 0 | pub async fn peers_assumed_know_blocks( |
293 | 0 | &self, |
294 | 0 | block_number: u64, |
295 | 0 | block_hash: &[u8; 32], |
296 | 0 | ) -> impl Iterator<Item = PeerId> { Unexecuted instantiation: _RNvMNtCsiGub1lfKphe_13smoldot_light12sync_serviceINtB2_11SyncServicepE25peers_assumed_know_blocksB4_ Unexecuted instantiation: _RNvMNtCsih6EgvAwZF2_13smoldot_light12sync_serviceINtB2_11SyncServiceNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE25peers_assumed_know_blocksB18_ Unexecuted instantiation: _RNvMNtCsih6EgvAwZF2_13smoldot_light12sync_serviceINtB2_11SyncServicepE25peers_assumed_know_blocksB4_ |
297 | 0 | let (send_back, rx) = oneshot::channel(); |
298 | 0 |
|
299 | 0 | self.to_background |
300 | 0 | .send(ToBackground::PeersAssumedKnowBlock { |
301 | 0 | send_back, |
302 | 0 | block_number, |
303 | 0 | block_hash: *block_hash, |
304 | 0 | }) |
305 | 0 | .await |
306 | 0 | .unwrap(); |
307 | 0 |
|
308 | 0 | rx.await.unwrap().into_iter() |
309 | 0 | } Unexecuted instantiation: _RNCNvMNtCsiGub1lfKphe_13smoldot_light12sync_serviceINtB4_11SyncServicepE25peers_assumed_know_blocks0B6_ Unexecuted instantiation: _RNCNvMNtCsih6EgvAwZF2_13smoldot_light12sync_serviceINtB4_11SyncServiceNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE25peers_assumed_know_blocks0B1a_ Unexecuted instantiation: _RNCNvMNtCsih6EgvAwZF2_13smoldot_light12sync_serviceINtB4_11SyncServicepE25peers_assumed_know_blocks0B6_ |
310 | | |
311 | | // TODO: doc; explain the guarantees |
312 | 0 | pub async fn block_query( |
313 | 0 | self: Arc<Self>, |
314 | 0 | block_number: u64, |
315 | 0 | hash: [u8; 32], |
316 | 0 | fields: codec::BlocksRequestFields, |
317 | 0 | total_attempts: u32, |
318 | 0 | timeout_per_request: Duration, |
319 | 0 | _max_parallel: NonZeroU32, |
320 | 0 | ) -> Result<codec::BlockData, ()> { Unexecuted instantiation: _RNvMNtCsiGub1lfKphe_13smoldot_light12sync_serviceINtB2_11SyncServicepE11block_queryB4_ Unexecuted instantiation: _RNvMNtCsih6EgvAwZF2_13smoldot_light12sync_serviceINtB2_11SyncServiceNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE11block_queryB18_ Unexecuted instantiation: _RNvMNtCsih6EgvAwZF2_13smoldot_light12sync_serviceINtB2_11SyncServicepE11block_queryB4_ |
321 | 0 | // TODO: better error? |
322 | 0 | let request_config = codec::BlocksRequestConfig { |
323 | 0 | start: codec::BlocksRequestConfigStart::Hash(hash), |
324 | 0 | desired_count: NonZeroU32::new(1).unwrap(), |
325 | 0 | direction: codec::BlocksRequestDirection::Ascending, |
326 | 0 | fields: fields.clone(), |
327 | 0 | }; |
328 | | |
329 | | // TODO: handle max_parallel |
330 | | // TODO: better peers selection ; don't just take the first 3 |
331 | 0 | for target in self |
332 | 0 | .peers_assumed_know_blocks(block_number, &hash) |
333 | 0 | .await |
334 | 0 | .take(usize::try_from(total_attempts).unwrap_or(usize::MAX)) |
335 | | { |
336 | 0 | let mut result = match self |
337 | 0 | .network_service |
338 | 0 | .clone() |
339 | 0 | .blocks_request(target.clone(), request_config.clone(), timeout_per_request) |
340 | 0 | .await |
341 | | { |
342 | 0 | Ok(b) if !b.is_empty() => b, |
343 | | Ok(_) | Err(_) => { |
344 | 0 | self.network_service |
345 | 0 | .ban_and_disconnect( |
346 | 0 | target, |
347 | 0 | network_service::BanSeverity::Low, |
348 | 0 | "blocks-request-failed", |
349 | 0 | ) |
350 | 0 | .await; |
351 | 0 | continue; |
352 | | } |
353 | | }; |
354 | | |
355 | 0 | return Ok(result.remove(0)); |
356 | | } |
357 | | |
358 | 0 | Err(()) |
359 | 0 | } Unexecuted instantiation: _RNCNvMNtCsiGub1lfKphe_13smoldot_light12sync_serviceINtB4_11SyncServicepE11block_query0B6_ Unexecuted instantiation: _RNCNvMNtCsih6EgvAwZF2_13smoldot_light12sync_serviceINtB4_11SyncServiceNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE11block_query0B1a_ Unexecuted instantiation: _RNCNvMNtCsih6EgvAwZF2_13smoldot_light12sync_serviceINtB4_11SyncServicepE11block_query0B6_ |
360 | | |
361 | | // TODO: doc; explain the guarantees |
362 | 0 | pub async fn block_query_unknown_number( |
363 | 0 | self: Arc<Self>, |
364 | 0 | hash: [u8; 32], |
365 | 0 | fields: codec::BlocksRequestFields, |
366 | 0 | total_attempts: u32, |
367 | 0 | timeout_per_request: Duration, |
368 | 0 | _max_parallel: NonZeroU32, |
369 | 0 | ) -> Result<codec::BlockData, ()> { Unexecuted instantiation: _RNvMNtCsiGub1lfKphe_13smoldot_light12sync_serviceINtB2_11SyncServicepE26block_query_unknown_numberB4_ Unexecuted instantiation: _RNvMNtCsih6EgvAwZF2_13smoldot_light12sync_serviceINtB2_11SyncServiceNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE26block_query_unknown_numberB18_ Unexecuted instantiation: _RNvMNtCsih6EgvAwZF2_13smoldot_light12sync_serviceINtB2_11SyncServicepE26block_query_unknown_numberB4_ |
370 | 0 | // TODO: better error? |
371 | 0 | let request_config = codec::BlocksRequestConfig { |
372 | 0 | start: codec::BlocksRequestConfigStart::Hash(hash), |
373 | 0 | desired_count: NonZeroU32::new(1).unwrap(), |
374 | 0 | direction: codec::BlocksRequestDirection::Ascending, |
375 | 0 | fields: fields.clone(), |
376 | 0 | }; |
377 | | |
378 | | // TODO: handle max_parallel |
379 | | // TODO: better peers selection ; don't just take the first |
380 | 0 | for target in self |
381 | 0 | .network_service |
382 | 0 | .peers_list() |
383 | 0 | .await |
384 | 0 | .take(usize::try_from(total_attempts).unwrap_or(usize::MAX)) |
385 | | { |
386 | 0 | let mut result = match self |
387 | 0 | .network_service |
388 | 0 | .clone() |
389 | 0 | .blocks_request(target, request_config.clone(), timeout_per_request) |
390 | 0 | .await |
391 | | { |
392 | 0 | Ok(b) if !b.is_empty() => b, |
393 | | Ok(_) | Err(_) => { |
394 | | // Because we have no idea whether the block is canonical, it might be |
395 | | // totally legitimate for the peer to refuse the request. For this reason, |
396 | | // we don't ban it. |
397 | 0 | continue; |
398 | | } |
399 | | }; |
400 | | |
401 | 0 | return Ok(result.remove(0)); |
402 | | } |
403 | | |
404 | 0 | Err(()) |
405 | 0 | } Unexecuted instantiation: _RNCNvMNtCsiGub1lfKphe_13smoldot_light12sync_serviceINtB4_11SyncServicepE26block_query_unknown_number0B6_ Unexecuted instantiation: _RNCNvMNtCsih6EgvAwZF2_13smoldot_light12sync_serviceINtB4_11SyncServiceNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE26block_query_unknown_number0B1a_ Unexecuted instantiation: _RNCNvMNtCsih6EgvAwZF2_13smoldot_light12sync_serviceINtB4_11SyncServicepE26block_query_unknown_number0B6_ |
406 | | |
407 | | /// Performs one or more storage proof requests in order to fulfill the `requests` passed as |
408 | | /// parameter. |
409 | | /// |
410 | | /// Must be passed a block hash, a block number, and the Merkle value of the root node of the |
411 | | /// storage trie of this same block. The value of `block_number` corresponds to the value |
412 | | /// in the [`smoldot::header::HeaderRef::number`] field, and the value of `main_trie_root_hash` |
413 | | /// corresponds to the value in the [`smoldot::header::HeaderRef::state_root`] field. |
414 | | /// |
415 | | /// The result will contain items corresponding to the requests, but in no particular order. |
416 | | /// |
417 | | /// See the documentation of [`StorageRequestItem`] and [`StorageResultItem`] for more |
418 | | /// information. |
419 | 0 | pub fn storage_query( |
420 | 0 | self: Arc<Self>, |
421 | 0 | block_number: u64, |
422 | 0 | block_hash: [u8; 32], |
423 | 0 | main_trie_root_hash: [u8; 32], |
424 | 0 | requests: impl Iterator<Item = StorageRequestItem>, |
425 | 0 | total_attempts: u32, |
426 | 0 | timeout_per_request: Duration, |
427 | 0 | max_parallel: NonZeroU32, |
428 | 0 | ) -> StorageQuery<TPlat> { |
429 | 0 | let total_attempts = usize::try_from(total_attempts).unwrap_or(usize::MAX); |
430 | 0 |
|
431 | 0 | let requests = requests |
432 | 0 | .map(|request| match request.ty { |
433 | | StorageRequestItemTy::DescendantsHashes |
434 | | | StorageRequestItemTy::DescendantsValues => RequestImpl::PrefixScan { |
435 | | scan: prefix_proof::prefix_scan(prefix_proof::Config { |
436 | 0 | prefix: &request.key, |
437 | 0 | trie_root_hash: main_trie_root_hash, |
438 | 0 | full_storage_values_required: matches!( |
439 | 0 | request.ty, |
440 | | StorageRequestItemTy::DescendantsValues |
441 | | ), |
442 | | }), |
443 | 0 | requested_key: request.key, |
444 | | }, |
445 | 0 | StorageRequestItemTy::Value => RequestImpl::ValueOrHash { |
446 | 0 | key: request.key, |
447 | 0 | hash: false, |
448 | 0 | }, |
449 | 0 | StorageRequestItemTy::Hash => RequestImpl::ValueOrHash { |
450 | 0 | key: request.key, |
451 | 0 | hash: true, |
452 | 0 | }, |
453 | | StorageRequestItemTy::ClosestDescendantMerkleValue => { |
454 | 0 | RequestImpl::ClosestDescendantMerkleValue { key: request.key } |
455 | | } |
456 | 0 | }) Unexecuted instantiation: _RNCINvMNtCsiGub1lfKphe_13smoldot_light12sync_serviceINtB5_11SyncServicepE13storage_querypE0B7_ Unexecuted instantiation: _RNCINvMNtCsih6EgvAwZF2_13smoldot_light12sync_serviceINtB5_11SyncServiceNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE13storage_queryINtNtNtCsaYZPK01V26L_4core5array4iter8IntoIterNtB5_18StorageRequestItemKj3_EE0B1b_ Unexecuted instantiation: _RNCINvMNtCsih6EgvAwZF2_13smoldot_light12sync_serviceINtB5_11SyncServiceNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE13storage_queryINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterNtB5_18StorageRequestItemEE0B1b_ Unexecuted instantiation: _RNCINvMNtCsih6EgvAwZF2_13smoldot_light12sync_serviceINtB5_11SyncServiceNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE13storage_queryINtCs1qmLyiTSqYF_6either6EitherINtNtNtNtCsaYZPK01V26L_4core4iter7sources4once4OnceNtB5_18StorageRequestItemEINtNtNtB2W_8adapters3map3MapINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterNtNtNtCseuYC0Zibziv_7smoldot8json_rpc7methods9HexStringENCNCINvNtNtB7_16json_rpc_service10background3runB17_E0sx_0EEE0B1b_ Unexecuted instantiation: _RNCINvMNtCsih6EgvAwZF2_13smoldot_light12sync_serviceINtB5_11SyncServiceNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE13storage_queryINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtCsiw7FdGorGc8_9hashbrown3set8IntoIterINtNtCsdZExvAaxgia_5alloc3vec3VechEENCNCNCINvNtNtB7_16json_rpc_service10background3runB17_E0sX_00EE0B1b_ Unexecuted instantiation: _RNCINvMNtCsih6EgvAwZF2_13smoldot_light12sync_serviceINtB5_11SyncServicepE13storage_querypE0B7_ |
457 | 0 | .enumerate() |
458 | 0 | .collect::<Vec<_>>(); |
459 | 0 |
|
460 | 0 | StorageQuery { |
461 | 0 | block_number, |
462 | 0 | block_hash, |
463 | 0 | main_trie_root_hash, |
464 | 0 | total_attempts, |
465 | 0 | timeout_per_request, |
466 | 0 | _max_parallel: max_parallel, |
467 | 0 | outcome_errors: Vec::with_capacity(total_attempts), |
468 | 0 | available_results: VecDeque::with_capacity(requests.len() * 4), |
469 | 0 | requests_remaining: requests, |
470 | 0 | response_nodes_cap: (16 * 1024 * 1024) / 164, |
471 | 0 | randomness: rand_chacha::ChaCha20Rng::from_seed({ |
472 | 0 | let mut seed = [0; 32]; |
473 | 0 | self.platform.fill_random_bytes(&mut seed); |
474 | 0 | seed |
475 | 0 | }), |
476 | 0 | sync_service: self, |
477 | 0 | } |
478 | 0 | } Unexecuted instantiation: _RINvMNtCsiGub1lfKphe_13smoldot_light12sync_serviceINtB3_11SyncServicepE13storage_querypEB5_ Unexecuted instantiation: _RINvMNtCsih6EgvAwZF2_13smoldot_light12sync_serviceINtB3_11SyncServiceNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE13storage_queryINtCs1qmLyiTSqYF_6either6EitherINtNtNtNtCsaYZPK01V26L_4core4iter7sources4once4OnceNtB3_18StorageRequestItemEINtNtNtB2U_8adapters3map3MapINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterNtNtNtCseuYC0Zibziv_7smoldot8json_rpc7methods9HexStringENCNCINvNtNtB5_16json_rpc_service10background3runB15_E0sx_0EEEB19_ Unexecuted instantiation: _RINvMNtCsih6EgvAwZF2_13smoldot_light12sync_serviceINtB3_11SyncServiceNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE13storage_queryINtNtNtCsaYZPK01V26L_4core5array4iter8IntoIterNtB3_18StorageRequestItemKj3_EEB19_ Unexecuted instantiation: _RINvMNtCsih6EgvAwZF2_13smoldot_light12sync_serviceINtB3_11SyncServiceNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE13storage_queryINtNtNtCsdZExvAaxgia_5alloc3vec9into_iter8IntoIterNtB3_18StorageRequestItemEEB19_ Unexecuted instantiation: _RINvMNtCsih6EgvAwZF2_13smoldot_light12sync_serviceINtB3_11SyncServiceNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE13storage_queryINtNtNtNtCsaYZPK01V26L_4core4iter8adapters3map3MapINtNtCsiw7FdGorGc8_9hashbrown3set8IntoIterINtNtCsdZExvAaxgia_5alloc3vec3VechEENCNCNCINvNtNtB5_16json_rpc_service10background3runB15_E0sX_00EEB19_ Unexecuted instantiation: _RINvMNtCsih6EgvAwZF2_13smoldot_light12sync_serviceINtB3_11SyncServicepE13storage_querypEB5_ |
479 | | } |
480 | | |
481 | | /// An item requested with [`SyncService::storage_query`]. |
482 | | #[derive(Debug, Clone)] |
483 | | pub struct StorageRequestItem { |
484 | | /// Key to request. Exactly what is requested depends on [`StorageRequestItem::ty`]. |
485 | | pub key: Vec<u8>, |
486 | | /// Detail about what is being requested. |
487 | | pub ty: StorageRequestItemTy, |
488 | | } |
489 | | |
490 | | /// See [`StorageRequestItem::ty`]. |
491 | | #[derive(Debug, Clone)] |
492 | | pub enum StorageRequestItemTy { |
493 | | /// The storage value associated to the [`StorageRequestItem::key`] is requested. |
494 | | /// A [`StorageResultItem::Value`] will be returned containing the potential value. |
495 | | Value, |
496 | | |
497 | | /// The hash of the storage value associated to the [`StorageRequestItem::key`] is requested. |
498 | | /// A [`StorageResultItem::Hash`] will be returned containing the potential hash. |
499 | | Hash, |
500 | | |
501 | | /// The list of the descendants of the [`StorageRequestItem::key`] (including the `key` |
502 | | /// itself) that have a storage value is requested. |
503 | | /// |
504 | | /// Zero or more [`StorageResultItem::DescendantValue`] will be returned where the |
505 | | /// [`StorageResultItem::DescendantValue::requested_key`] is equal to |
506 | | /// [`StorageRequestItem::key`]. |
507 | | DescendantsValues, |
508 | | |
509 | | /// The list of the descendants of the [`StorageRequestItem::key`] (including the `key` |
510 | | /// itself) that have a storage value is requested. |
511 | | /// |
512 | | /// Zero or more [`StorageResultItem::DescendantHash`] will be returned where the |
513 | | /// [`StorageResultItem::DescendantHash::requested_key`] is equal to |
514 | | /// [`StorageRequestItem::key`]. |
515 | | DescendantsHashes, |
516 | | |
517 | | /// The Merkle value of the trie node that is the closest ancestor to |
518 | | /// [`StorageRequestItem::key`] is requested. |
519 | | /// A [`StorageResultItem::ClosestDescendantMerkleValue`] will be returned where |
520 | | /// [`StorageResultItem::ClosestDescendantMerkleValue::requested_key`] is equal to |
521 | | /// [`StorageRequestItem::key`]. |
522 | | ClosestDescendantMerkleValue, |
523 | | } |
524 | | |
525 | | /// An item returned by [`SyncService::storage_query`]. |
526 | | #[derive(Debug, Clone)] |
527 | | pub enum StorageResultItem { |
528 | | /// Corresponds to a [`StorageRequestItemTy::Value`]. |
529 | | Value { |
530 | | /// Key that was requested. Equal to the value of [`StorageRequestItem::key`]. |
531 | | key: Vec<u8>, |
532 | | /// Storage value of the key, or `None` if there is no storage value associated with that |
533 | | /// key. |
534 | | value: Option<Vec<u8>>, |
535 | | }, |
536 | | /// Corresponds to a [`StorageRequestItemTy::Hash`]. |
537 | | Hash { |
538 | | /// Key that was requested. Equal to the value of [`StorageRequestItem::key`]. |
539 | | key: Vec<u8>, |
540 | | /// Hash of the storage value of the key, or `None` if there is no storage value |
541 | | /// associated with that key. |
542 | | hash: Option<[u8; 32]>, |
543 | | }, |
544 | | /// Corresponds to a [`StorageRequestItemTy::DescendantsValues`]. |
545 | | DescendantValue { |
546 | | /// Key that was requested. Equal to the value of [`StorageRequestItem::key`]. |
547 | | requested_key: Vec<u8>, |
548 | | /// Equal or a descendant of [`StorageResultItem::DescendantValue::requested_key`]. |
549 | | key: Vec<u8>, |
550 | | /// Storage value associated with [`StorageResultItem::DescendantValue::key`]. |
551 | | value: Vec<u8>, |
552 | | }, |
553 | | /// Corresponds to a [`StorageRequestItemTy::DescendantsHashes`]. |
554 | | DescendantHash { |
555 | | /// Key that was requested. Equal to the value of [`StorageRequestItem::key`]. |
556 | | requested_key: Vec<u8>, |
557 | | /// Equal or a descendant of [`StorageResultItem::DescendantHash::requested_key`]. |
558 | | key: Vec<u8>, |
559 | | /// Hash of the storage value associated with [`StorageResultItem::DescendantHash::key`]. |
560 | | hash: [u8; 32], |
561 | | }, |
562 | | /// Corresponds to a [`StorageRequestItemTy::ClosestDescendantMerkleValue`]. |
563 | | ClosestDescendantMerkleValue { |
564 | | /// Key that was requested. Equal to the value of [`StorageRequestItem::key`]. |
565 | | requested_key: Vec<u8>, |
566 | | /// Closest ancestor to the requested key that was found in the proof. If |
567 | | /// [`StorageResultItem::ClosestDescendantMerkleValue::closest_descendant_merkle_value`] |
568 | | /// is `Some`, then this is always the parent of the requested key. |
569 | | found_closest_ancestor_excluding: Option<Vec<Nibble>>, |
570 | | /// Merkle value of the closest descendant of |
571 | | /// [`StorageResultItem::DescendantValue::requested_key`]. The key that corresponds |
572 | | /// to this Merkle value is not included. `None` if the key has no descendant. |
573 | | closest_descendant_merkle_value: Option<Vec<u8>>, |
574 | | }, |
575 | | } |
576 | | |
577 | | /// Returned by [`SyncService::storage_query`]. Represents a storage query in progress. |
578 | | pub struct StorageQuery<TPlat: PlatformRef> { |
579 | | sync_service: Arc<SyncService<TPlat>>, |
580 | | block_number: u64, |
581 | | block_hash: [u8; 32], |
582 | | main_trie_root_hash: [u8; 32], |
583 | | /// Requests that haven't been fulfilled yet. |
584 | | /// The `usize` is the index of the request in the original list of requests that the API user |
585 | | /// provided. |
586 | | requests_remaining: Vec<(usize, RequestImpl)>, |
587 | | /// Total number of network requests to try before giving up. |
588 | | total_attempts: usize, |
589 | | /// How long to wait for a response to the request. |
590 | | timeout_per_request: Duration, |
591 | | // TODO: value presently ignored |
592 | | _max_parallel: NonZeroU32, |
593 | | /// Non-fatal errors that have happened in the network requests. |
594 | | outcome_errors: Vec<StorageQueryErrorDetail>, |
595 | | /// List of responses that are available to yield. |
596 | | /// The `usize` is the index of the request in the original list of requests that the API user |
597 | | /// provided. |
598 | | available_results: VecDeque<(usize, StorageResultItem)>, |
599 | | /// Number of nodes that are possible in a response before exceeding the response size |
600 | | /// limit. Because the size of a trie node is unknown, this can only ever be a gross |
601 | | /// estimate. |
602 | | /// If a request fails due to the limit being exceeded, this cap is dynamically reduced. |
603 | | response_nodes_cap: usize, |
604 | | /// Source of randomness. |
605 | | randomness: rand_chacha::ChaCha20Rng, |
606 | | } |
607 | | |
608 | | enum RequestImpl { |
609 | | PrefixScan { |
610 | | requested_key: Vec<u8>, |
611 | | scan: prefix_proof::PrefixScan, |
612 | | }, |
613 | | ValueOrHash { |
614 | | key: Vec<u8>, |
615 | | hash: bool, |
616 | | }, |
617 | | ClosestDescendantMerkleValue { |
618 | | key: Vec<u8>, |
619 | | }, |
620 | | } |
621 | | |
622 | | impl<TPlat: PlatformRef> StorageQuery<TPlat> { |
623 | | /// Drain any other item that might be immediately available. |
624 | | /// |
625 | | /// The `usize` corresponds to [`StorageQueryProgress::Progress::request_index`]. |
626 | 0 | pub fn try_advance(&mut self) -> Option<(usize, StorageResultItem)> { |
627 | 0 | self.available_results.pop_front() |
628 | 0 | } Unexecuted instantiation: _RNvMs_NtCsiGub1lfKphe_13smoldot_light12sync_serviceINtB4_12StorageQuerypE11try_advanceB6_ Unexecuted instantiation: _RNvMs_NtCsih6EgvAwZF2_13smoldot_light12sync_serviceINtB4_12StorageQueryNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE11try_advanceB1b_ Unexecuted instantiation: _RNvMs_NtCsih6EgvAwZF2_13smoldot_light12sync_serviceINtB4_12StorageQuerypE11try_advanceB6_ |
629 | | |
630 | | /// Wait until some progress is made. |
631 | 0 | pub async fn advance(mut self) -> StorageQueryProgress<TPlat> { Unexecuted instantiation: _RNvMs_NtCsiGub1lfKphe_13smoldot_light12sync_serviceINtB4_12StorageQuerypE7advanceB6_ Unexecuted instantiation: _RNvMs_NtCsih6EgvAwZF2_13smoldot_light12sync_serviceINtB4_12StorageQueryNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE7advanceB1b_ Unexecuted instantiation: _RNvMs_NtCsih6EgvAwZF2_13smoldot_light12sync_serviceINtB4_12StorageQuerypE7advanceB6_ |
632 | | loop { |
633 | | // TODO: instead of buffering everything here, progressively decode the proof |
634 | 0 | if let Some((request_index, item)) = self.available_results.pop_front() { |
635 | 0 | return StorageQueryProgress::Progress { |
636 | 0 | request_index, |
637 | 0 | item, |
638 | 0 | query: self, |
639 | 0 | }; |
640 | 0 | } |
641 | 0 |
|
642 | 0 | // Check if we're done. |
643 | 0 | if self.requests_remaining.is_empty() { |
644 | 0 | return StorageQueryProgress::Finished; |
645 | 0 | } |
646 | 0 |
|
647 | 0 | if self.outcome_errors.len() >= self.total_attempts { |
648 | 0 | return StorageQueryProgress::Error(StorageQueryError { |
649 | 0 | errors: self.outcome_errors, |
650 | 0 | }); |
651 | 0 | } |
652 | | |
653 | | // Choose peer to query. |
654 | | // TODO: better peers selection |
655 | 0 | let Some(target) = self |
656 | 0 | .sync_service |
657 | 0 | .peers_assumed_know_blocks(self.block_number, &self.block_hash) |
658 | 0 | .await |
659 | 0 | .choose(&mut self.randomness) |
660 | | else { |
661 | | // No peer knows this block. Returning with a failure. |
662 | 0 | return StorageQueryProgress::Error(StorageQueryError { |
663 | 0 | errors: self.outcome_errors, |
664 | 0 | }); |
665 | | }; |
666 | | |
667 | | // Build the list of keys to request. |
668 | 0 | let keys_to_request = { |
669 | | // Keep track of the number of nodes that might be found in the response. |
670 | | // This is a generous overestimation of the actual number. |
671 | 0 | let mut max_reponse_nodes = 0; |
672 | 0 |
|
673 | 0 | let mut keys = hashbrown::HashSet::with_capacity_and_hasher( |
674 | 0 | self.requests_remaining.len() * 4, |
675 | 0 | fnv::FnvBuildHasher::default(), |
676 | 0 | ); |
677 | | |
678 | 0 | for (_, request) in &self.requests_remaining { |
679 | 0 | if max_reponse_nodes >= self.response_nodes_cap { |
680 | 0 | break; |
681 | 0 | } |
682 | 0 |
|
683 | 0 | match request { |
684 | 0 | RequestImpl::PrefixScan { scan, .. } => { |
685 | 0 | for scan_key in scan.requested_keys() { |
686 | 0 | if max_reponse_nodes >= self.response_nodes_cap { |
687 | 0 | break; |
688 | 0 | } |
689 | 0 |
|
690 | 0 | let scan_key = trie::nibbles_to_bytes_suffix_extend(scan_key) |
691 | 0 | .collect::<Vec<_>>(); |
692 | 0 | let scan_key_len = scan_key.len(); |
693 | 0 | if keys.insert(scan_key) { |
694 | 0 | max_reponse_nodes += scan_key_len * 2; |
695 | 0 | } |
696 | | } |
697 | | } |
698 | 0 | RequestImpl::ValueOrHash { key, .. } => { |
699 | 0 | if keys.insert(key.clone()) { |
700 | 0 | max_reponse_nodes += key.len() * 2; |
701 | 0 | } |
702 | | } |
703 | 0 | RequestImpl::ClosestDescendantMerkleValue { key } => { |
704 | 0 | // We query the parent of `key`. |
705 | 0 | if key.is_empty() { |
706 | 0 | if keys.insert(Vec::new()) { |
707 | 0 | max_reponse_nodes += 1; |
708 | 0 | } |
709 | 0 | } else if keys.insert(key[..key.len() - 1].to_owned()) { |
710 | 0 | max_reponse_nodes += key.len() * 2 - 1; |
711 | 0 | } |
712 | | } |
713 | | } |
714 | | } |
715 | | |
716 | 0 | keys |
717 | | }; |
718 | | |
719 | 0 | let result = self |
720 | 0 | .sync_service |
721 | 0 | .network_service |
722 | 0 | .clone() |
723 | 0 | .storage_proof_request( |
724 | 0 | target.clone(), |
725 | 0 | codec::StorageProofRequestConfig { |
726 | 0 | block_hash: self.block_hash, |
727 | 0 | keys: keys_to_request.into_iter(), |
728 | 0 | }, |
729 | 0 | self.timeout_per_request, |
730 | 0 | ) |
731 | 0 | .await; |
732 | | |
733 | 0 | let proof = match result { |
734 | 0 | Ok(r) => r, |
735 | 0 | Err(err) => { |
736 | | // In case of error that isn't a protocol error, we reduce the number of |
737 | | // trie node items to request. |
738 | 0 | let reduce_max = match &err { |
739 | 0 | network_service::StorageProofRequestError::RequestTooLarge => true, |
740 | | network_service::StorageProofRequestError::Request( |
741 | 0 | service::StorageProofRequestError::Request(err), |
742 | 0 | ) => !err.is_protocol_error(), |
743 | 0 | _ => false, |
744 | | }; |
745 | | |
746 | 0 | if !matches!( |
747 | 0 | err, |
748 | | network_service::StorageProofRequestError::RequestTooLarge |
749 | 0 | ) || self.response_nodes_cap == 1 |
750 | | { |
751 | 0 | self.sync_service |
752 | 0 | .network_service |
753 | 0 | .ban_and_disconnect( |
754 | 0 | target, |
755 | 0 | network_service::BanSeverity::Low, |
756 | 0 | "storage-request-failed", |
757 | 0 | ) |
758 | 0 | .await; |
759 | 0 | self.outcome_errors |
760 | 0 | .push(StorageQueryErrorDetail::Network(err)); |
761 | 0 | } |
762 | | |
763 | 0 | if reduce_max { |
764 | 0 | self.response_nodes_cap = cmp::max(1, self.response_nodes_cap / 2); |
765 | 0 | } |
766 | | |
767 | 0 | continue; |
768 | | } |
769 | | }; |
770 | | |
771 | 0 | let decoded_proof = match proof_decode::decode_and_verify_proof(proof_decode::Config { |
772 | 0 | proof: proof.decode(), |
773 | 0 | }) { |
774 | 0 | Ok(d) => d, |
775 | 0 | Err(err) => { |
776 | 0 | self.sync_service |
777 | 0 | .network_service |
778 | 0 | .ban_and_disconnect( |
779 | 0 | target, |
780 | 0 | network_service::BanSeverity::High, |
781 | 0 | "bad-merkle-proof", |
782 | 0 | ) |
783 | 0 | .await; |
784 | 0 | self.outcome_errors |
785 | 0 | .push(StorageQueryErrorDetail::ProofVerification(err)); |
786 | 0 | continue; |
787 | | } |
788 | | }; |
789 | | |
790 | 0 | let mut proof_has_advanced_verification = false; |
791 | | |
792 | 0 | for (request_index, request) in mem::take(&mut self.requests_remaining) { |
793 | 0 | match request { |
794 | | RequestImpl::PrefixScan { |
795 | 0 | scan, |
796 | 0 | requested_key, |
797 | 0 | } => { |
798 | 0 | // TODO: how "partial" do we accept that the proof is? it should be considered malicious if the full node might return the minimum amount of information |
799 | 0 | match scan.resume_partial(proof.decode()) { |
800 | 0 | Ok(prefix_proof::ResumeOutcome::InProgress(scan)) => { |
801 | 0 | proof_has_advanced_verification = true; |
802 | 0 | self.requests_remaining.push(( |
803 | 0 | request_index, |
804 | 0 | RequestImpl::PrefixScan { |
805 | 0 | scan, |
806 | 0 | requested_key, |
807 | 0 | }, |
808 | 0 | )); |
809 | 0 | } |
810 | | Ok(prefix_proof::ResumeOutcome::Success { |
811 | 0 | entries, |
812 | 0 | full_storage_values_required, |
813 | 0 | }) => { |
814 | 0 | proof_has_advanced_verification = true; |
815 | | // The value of `full_storage_values_required` determines whether |
816 | | // we wanted full values (`true`) or hashes (`false`). |
817 | 0 | for (key, value) in entries { |
818 | 0 | match value { |
819 | 0 | prefix_proof::StorageValue::Hash(hash) => { |
820 | 0 | debug_assert!(!full_storage_values_required); |
821 | 0 | self.available_results.push_back(( |
822 | 0 | request_index, |
823 | 0 | StorageResultItem::DescendantHash { |
824 | 0 | key, |
825 | 0 | hash, |
826 | 0 | requested_key: requested_key.clone(), |
827 | 0 | }, |
828 | 0 | )); |
829 | | } |
830 | 0 | prefix_proof::StorageValue::Value(value) |
831 | 0 | if full_storage_values_required => |
832 | 0 | { |
833 | 0 | self.available_results.push_back(( |
834 | 0 | request_index, |
835 | 0 | StorageResultItem::DescendantValue { |
836 | 0 | requested_key: requested_key.clone(), |
837 | 0 | key, |
838 | 0 | value, |
839 | 0 | }, |
840 | 0 | )); |
841 | 0 | } |
842 | 0 | prefix_proof::StorageValue::Value(value) => { |
843 | 0 | let hashed_value = |
844 | 0 | blake2_rfc::blake2b::blake2b(32, &[], &value); |
845 | 0 | self.available_results.push_back(( |
846 | 0 | request_index, |
847 | 0 | StorageResultItem::DescendantHash { |
848 | 0 | key, |
849 | 0 | hash: *<&[u8; 32]>::try_from( |
850 | 0 | hashed_value.as_bytes(), |
851 | 0 | ) |
852 | 0 | .unwrap(), |
853 | 0 | requested_key: requested_key.clone(), |
854 | 0 | }, |
855 | 0 | )); |
856 | 0 | } |
857 | | } |
858 | | } |
859 | | } |
860 | | Err((_, prefix_proof::Error::InvalidProof(_))) => { |
861 | | // Since we decode the proof above, this is never supposed to |
862 | | // be reachable. |
863 | 0 | unreachable!() |
864 | | } |
865 | 0 | Err((scan, prefix_proof::Error::MissingProofEntry)) => { |
866 | 0 | self.requests_remaining.push(( |
867 | 0 | request_index, |
868 | 0 | RequestImpl::PrefixScan { |
869 | 0 | requested_key, |
870 | 0 | scan, |
871 | 0 | }, |
872 | 0 | )); |
873 | 0 | } |
874 | | } |
875 | | } |
876 | 0 | RequestImpl::ValueOrHash { key, hash } => { |
877 | 0 | match decoded_proof.trie_node_info( |
878 | 0 | &self.main_trie_root_hash, |
879 | 0 | trie::bytes_to_nibbles(key.iter().copied()), |
880 | 0 | ) { |
881 | 0 | Ok(node_info) => match node_info.storage_value { |
882 | 0 | proof_decode::StorageValue::HashKnownValueMissing(h) if hash => { |
883 | 0 | proof_has_advanced_verification = true; |
884 | 0 | self.available_results.push_back(( |
885 | 0 | request_index, |
886 | 0 | StorageResultItem::Hash { |
887 | 0 | key, |
888 | 0 | hash: Some(*h), |
889 | 0 | }, |
890 | 0 | )); |
891 | 0 | } |
892 | 0 | proof_decode::StorageValue::HashKnownValueMissing(_) => { |
893 | 0 | self.requests_remaining.push(( |
894 | 0 | request_index, |
895 | 0 | RequestImpl::ValueOrHash { key, hash }, |
896 | 0 | )); |
897 | 0 | } |
898 | 0 | proof_decode::StorageValue::Known { value, .. } => { |
899 | 0 | proof_has_advanced_verification = true; |
900 | 0 | if hash { |
901 | 0 | let hashed_value = |
902 | 0 | blake2_rfc::blake2b::blake2b(32, &[], value); |
903 | 0 | self.available_results.push_back(( |
904 | 0 | request_index, |
905 | 0 | StorageResultItem::Hash { |
906 | 0 | key, |
907 | 0 | hash: Some( |
908 | 0 | *<&[u8; 32]>::try_from(hashed_value.as_bytes()) |
909 | 0 | .unwrap(), |
910 | 0 | ), |
911 | 0 | }, |
912 | 0 | )); |
913 | 0 | } else { |
914 | 0 | self.available_results.push_back(( |
915 | 0 | request_index, |
916 | 0 | StorageResultItem::Value { |
917 | 0 | key, |
918 | 0 | value: Some(value.to_vec()), |
919 | 0 | }, |
920 | 0 | )); |
921 | 0 | } |
922 | | } |
923 | | proof_decode::StorageValue::None => { |
924 | 0 | proof_has_advanced_verification = true; |
925 | 0 | if hash { |
926 | 0 | self.available_results.push_back(( |
927 | 0 | request_index, |
928 | 0 | StorageResultItem::Hash { key, hash: None }, |
929 | 0 | )); |
930 | 0 | } else { |
931 | 0 | self.available_results.push_back(( |
932 | 0 | request_index, |
933 | 0 | StorageResultItem::Value { key, value: None }, |
934 | 0 | )); |
935 | 0 | } |
936 | | } |
937 | | }, |
938 | 0 | Err(proof_decode::IncompleteProofError { .. }) => { |
939 | 0 | self.requests_remaining |
940 | 0 | .push((request_index, RequestImpl::ValueOrHash { key, hash })); |
941 | 0 | } |
942 | | } |
943 | | } |
944 | 0 | RequestImpl::ClosestDescendantMerkleValue { key } => { |
945 | 0 | let key_nibbles = trie::bytes_to_nibbles(key.iter().copied()); |
946 | | |
947 | 0 | let closest_descendant_merkle_value = match decoded_proof |
948 | 0 | .closest_descendant_merkle_value( |
949 | 0 | &self.main_trie_root_hash, |
950 | 0 | key_nibbles.clone(), |
951 | 0 | ) { |
952 | 0 | Ok(Some(merkle_value)) => Some(merkle_value.to_vec()), |
953 | 0 | Ok(None) => None, |
954 | | Err(proof_decode::IncompleteProofError { .. }) => { |
955 | 0 | self.requests_remaining.push(( |
956 | 0 | request_index, |
957 | 0 | RequestImpl::ClosestDescendantMerkleValue { key }, |
958 | 0 | )); |
959 | 0 | continue; |
960 | | } |
961 | | }; |
962 | | |
963 | 0 | let found_closest_ancestor_excluding = match decoded_proof |
964 | 0 | .closest_ancestor_in_proof(&self.main_trie_root_hash, key_nibbles) |
965 | | { |
966 | 0 | Ok(Some(ancestor)) => Some(ancestor.collect::<Vec<_>>()), |
967 | 0 | Ok(None) => None, |
968 | | Err(proof_decode::IncompleteProofError { .. }) => { |
969 | 0 | self.requests_remaining.push(( |
970 | 0 | request_index, |
971 | 0 | RequestImpl::ClosestDescendantMerkleValue { key }, |
972 | 0 | )); |
973 | 0 | continue; |
974 | | } |
975 | | }; |
976 | | |
977 | 0 | proof_has_advanced_verification = true; |
978 | 0 |
|
979 | 0 | self.available_results.push_back(( |
980 | 0 | request_index, |
981 | 0 | StorageResultItem::ClosestDescendantMerkleValue { |
982 | 0 | requested_key: key, |
983 | 0 | closest_descendant_merkle_value, |
984 | 0 | found_closest_ancestor_excluding, |
985 | 0 | }, |
986 | 0 | )) |
987 | | } |
988 | | } |
989 | | } |
990 | | |
991 | | // If the proof doesn't contain any item that reduces the number of things to request, |
992 | | // then we push an error. |
993 | 0 | if !proof_has_advanced_verification { |
994 | 0 | self.outcome_errors |
995 | 0 | .push(StorageQueryErrorDetail::MissingProofEntry); |
996 | 0 | } |
997 | | } |
998 | 0 | } Unexecuted instantiation: _RNCNvMs_NtCsiGub1lfKphe_13smoldot_light12sync_serviceINtB6_12StorageQuerypE7advance0B8_ Unexecuted instantiation: _RNCNvMs_NtCsih6EgvAwZF2_13smoldot_light12sync_serviceINtB6_12StorageQueryNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefE7advance0B1d_ Unexecuted instantiation: _RNCNvMs_NtCsih6EgvAwZF2_13smoldot_light12sync_serviceINtB6_12StorageQuerypE7advance0B8_ |
999 | | } |
1000 | | |
1001 | | /// Progress in a storage query. Returned by [`StorageQuery::advance`]. |
1002 | | pub enum StorageQueryProgress<TPlat: PlatformRef> { |
1003 | | /// The query has successfully finished. All the items have been yielded through |
1004 | | /// [`StorageQueryProgress::Progress`]. |
1005 | | Finished, |
1006 | | /// The query has yielded an item. |
1007 | | Progress { |
1008 | | /// Index within the original list of requests passed to [`SyncService::storage_query`] |
1009 | | /// the item corresponds to. |
1010 | | request_index: usize, |
1011 | | /// The item in question. |
1012 | | item: StorageResultItem, |
1013 | | /// Query to use to continue advancing. |
1014 | | query: StorageQuery<TPlat>, |
1015 | | }, |
1016 | | /// The query has failed due to having reached the maximum number of errors. |
1017 | | Error(StorageQueryError), |
1018 | | } |
1019 | | |
1020 | | /// Error that can happen when calling [`SyncService::storage_query`]. |
1021 | | // TODO: remove? |
1022 | | #[derive(Debug, Clone)] |
1023 | | pub struct StorageQueryError { |
1024 | | /// Contains one error per peer that has been contacted. If this list is empty, then we |
1025 | | /// aren't connected to any node. |
1026 | | pub errors: Vec<StorageQueryErrorDetail>, |
1027 | | } |
1028 | | |
1029 | | impl StorageQueryError { |
1030 | | /// Returns `true` if this is caused by networking issues, as opposed to a consensus-related |
1031 | | /// issue. |
1032 | 0 | pub fn is_network_problem(&self) -> bool { |
1033 | 0 | self.errors.iter().all(|err| match err { |
1034 | | StorageQueryErrorDetail::Network( |
1035 | | network_service::StorageProofRequestError::Request( |
1036 | | service::StorageProofRequestError::Request(_) |
1037 | | | service::StorageProofRequestError::RemoteCouldntAnswer, |
1038 | | ), |
1039 | | ) |
1040 | | | StorageQueryErrorDetail::Network( |
1041 | | network_service::StorageProofRequestError::NoConnection, |
1042 | 0 | ) => true, |
1043 | | StorageQueryErrorDetail::Network( |
1044 | | network_service::StorageProofRequestError::Request( |
1045 | | service::StorageProofRequestError::Decode(_), |
1046 | | ) |
1047 | | | network_service::StorageProofRequestError::RequestTooLarge, |
1048 | 0 | ) => false, |
1049 | | StorageQueryErrorDetail::ProofVerification(_) |
1050 | 0 | | StorageQueryErrorDetail::MissingProofEntry => false, |
1051 | 0 | }) Unexecuted instantiation: _RNCNvMs0_NtCsiGub1lfKphe_13smoldot_light12sync_serviceNtB7_17StorageQueryError18is_network_problem0B9_ Unexecuted instantiation: _RNCNvMs0_NtCsih6EgvAwZF2_13smoldot_light12sync_serviceNtB7_17StorageQueryError18is_network_problem0B9_ |
1052 | 0 | } Unexecuted instantiation: _RNvMs0_NtCsiGub1lfKphe_13smoldot_light12sync_serviceNtB5_17StorageQueryError18is_network_problem Unexecuted instantiation: _RNvMs0_NtCsih6EgvAwZF2_13smoldot_light12sync_serviceNtB5_17StorageQueryError18is_network_problem |
1053 | | } |
1054 | | |
1055 | | impl fmt::Display for StorageQueryError { |
1056 | 0 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
1057 | 0 | if self.errors.is_empty() { |
1058 | 0 | write!(f, "No node available for storage query") |
1059 | | } else { |
1060 | 0 | write!(f, "Storage query errors:")?; |
1061 | 0 | for err in &self.errors { |
1062 | 0 | write!(f, "\n- {err}")?; |
1063 | | } |
1064 | 0 | Ok(()) |
1065 | | } |
1066 | 0 | } Unexecuted instantiation: _RNvXs1_NtCsiGub1lfKphe_13smoldot_light12sync_serviceNtB5_17StorageQueryErrorNtNtCsaYZPK01V26L_4core3fmt7Display3fmt Unexecuted instantiation: _RNvXs1_NtCsih6EgvAwZF2_13smoldot_light12sync_serviceNtB5_17StorageQueryErrorNtNtCsaYZPK01V26L_4core3fmt7Display3fmt |
1067 | | } |
1068 | | |
1069 | | /// See [`StorageQueryError`]. |
1070 | 0 | #[derive(Debug, derive_more::Display, Clone)] Unexecuted instantiation: _RNvXsk_NtCsiGub1lfKphe_13smoldot_light12sync_serviceNtB5_23StorageQueryErrorDetailNtNtCsaYZPK01V26L_4core3fmt7Display3fmt Unexecuted instantiation: _RNvXsk_NtCsih6EgvAwZF2_13smoldot_light12sync_serviceNtB5_23StorageQueryErrorDetailNtNtCsaYZPK01V26L_4core3fmt7Display3fmt |
1071 | | pub enum StorageQueryErrorDetail { |
1072 | | /// Error during the network request. |
1073 | | #[display(fmt = "{_0}")] |
1074 | | Network(network_service::StorageProofRequestError), |
1075 | | /// Error verifying the proof. |
1076 | | #[display(fmt = "{_0}")] |
1077 | | ProofVerification(proof_decode::Error), |
1078 | | /// Proof is missing one or more desired storage items. |
1079 | | MissingProofEntry, |
1080 | | } |
1081 | | |
1082 | | /// Return value of [`SyncService::subscribe_all`]. |
1083 | | pub struct SubscribeAll { |
1084 | | /// SCALE-encoded header of the finalized block at the time of the subscription. |
1085 | | pub finalized_block_scale_encoded_header: Vec<u8>, |
1086 | | |
1087 | | /// Runtime of the finalized block, if known. |
1088 | | /// |
1089 | | /// > **Note**: In order to do the initial synchronization, the sync service might have to |
1090 | | /// > download and use the runtime near the head of the chain. Throwing away this |
1091 | | /// > runtime at the end of the synchronization is possible, but would be wasteful. |
1092 | | /// > Instead, this runtime is provided here if possible, but no guarantee is |
1093 | | /// > offered that it can be found. |
1094 | | pub finalized_block_runtime: Option<FinalizedBlockRuntime>, |
1095 | | |
1096 | | /// List of all known non-finalized blocks at the time of subscription. |
1097 | | /// |
1098 | | /// Only one element in this list has [`BlockNotification::is_new_best`] equal to true. |
1099 | | /// |
1100 | | /// The blocks are guaranteed to be ordered so that parents are always found before their |
1101 | | /// children. |
1102 | | pub non_finalized_blocks_ancestry_order: Vec<BlockNotification>, |
1103 | | |
1104 | | /// Channel onto which new blocks are sent. The channel gets closed if it is full when a new |
1105 | | /// block needs to be reported. |
1106 | | pub new_blocks: async_channel::Receiver<Notification>, |
1107 | | } |
1108 | | |
1109 | | /// See [`SubscribeAll::finalized_block_runtime`]. |
1110 | | pub struct FinalizedBlockRuntime { |
1111 | | /// Compiled virtual machine. |
1112 | | pub virtual_machine: host::HostVmPrototype, |
1113 | | |
1114 | | /// Storage value at the `:code` key. |
1115 | | pub storage_code: Option<Vec<u8>>, |
1116 | | |
1117 | | /// Storage value at the `:heappages` key. |
1118 | | pub storage_heap_pages: Option<Vec<u8>>, |
1119 | | |
1120 | | /// Merkle value of the `:code` key. |
1121 | | pub code_merkle_value: Option<Vec<u8>>, |
1122 | | |
1123 | | /// Closest ancestor of the `:code` key except for `:code` itself. |
1124 | | pub closest_ancestor_excluding: Option<Vec<Nibble>>, |
1125 | | } |
1126 | | |
1127 | | /// Notification about a new block or a new finalized block. |
1128 | | /// |
1129 | | /// See [`SyncService::subscribe_all`]. |
1130 | | #[derive(Debug, Clone)] |
1131 | | pub enum Notification { |
1132 | | /// A non-finalized block has been finalized. |
1133 | | Finalized { |
1134 | | /// BLAKE2 hash of the block that has been finalized. |
1135 | | /// |
1136 | | /// A block with this hash is guaranteed to have earlier been reported in a |
1137 | | /// [`BlockNotification`], either in [`SubscribeAll::non_finalized_blocks_ancestry_order`] |
1138 | | /// or in a [`Notification::Block`]. |
1139 | | /// |
1140 | | /// It is, however, not guaranteed that this block is a child of the previously-finalized |
1141 | | /// block. In other words, if multiple blocks are finalized at the same time, only one |
1142 | | /// [`Notification::Finalized`] is generated and contains the highest finalized block. |
1143 | | hash: [u8; 32], |
1144 | | |
1145 | | /// If the current best block is pruned by the finalization, contains the updated hash |
1146 | | /// of the best block after the finalization. |
1147 | | /// |
1148 | | /// If the newly-finalized block is an ancestor of the current best block, then this field |
1149 | | /// contains the hash of this current best block. Otherwise, the best block is now |
1150 | | /// the non-finalized block with the given hash. |
1151 | | /// |
1152 | | /// A block with this hash is guaranteed to have earlier been reported in a |
1153 | | /// [`BlockNotification`], either in [`SubscribeAll::non_finalized_blocks_ancestry_order`] |
1154 | | /// or in a [`Notification::Block`]. |
1155 | | best_block_hash_if_changed: Option<[u8; 32]>, |
1156 | | |
1157 | | /// List of BLAKE2 hashes of the headers of the blocks that have been discarded because |
1158 | | /// they're not descendants of the newly-finalized block. |
1159 | | /// |
1160 | | /// This list contains all the siblings of the newly-finalized block and all their |
1161 | | /// descendants. |
1162 | | pruned_blocks: Vec<[u8; 32]>, |
1163 | | }, |
1164 | | |
1165 | | /// A new block has been added to the list of unfinalized blocks. |
1166 | | Block(BlockNotification), |
1167 | | |
1168 | | /// The best block has changed to a different one. |
1169 | | BestBlockChanged { |
1170 | | /// Hash of the new best block. |
1171 | | /// |
1172 | | /// This can be either the hash of the latest finalized block or the hash of a |
1173 | | /// non-finalized block. |
1174 | | hash: [u8; 32], |
1175 | | }, |
1176 | | } |
1177 | | |
1178 | | /// Notification about a new block. |
1179 | | /// |
1180 | | /// See [`SyncService::subscribe_all`]. |
1181 | | #[derive(Debug, Clone)] |
1182 | | pub struct BlockNotification { |
1183 | | /// True if this block is considered as the best block of the chain. |
1184 | | pub is_new_best: bool, |
1185 | | |
1186 | | /// SCALE-encoded header of the block. |
1187 | | pub scale_encoded_header: Vec<u8>, |
1188 | | |
1189 | | /// BLAKE2 hash of the header of the parent of this block. |
1190 | | /// |
1191 | | /// |
1192 | | /// A block with this hash is guaranteed to have earlier been reported in a |
1193 | | /// [`BlockNotification`], either in [`SubscribeAll::non_finalized_blocks_ancestry_order`] or |
1194 | | /// in a [`Notification::Block`]. |
1195 | | /// |
1196 | | /// > **Note**: The header of a block contains the hash of its parent. When it comes to |
1197 | | /// > consensus algorithms such as Babe or Aura, the syncing code verifies that this |
1198 | | /// > hash, stored in the header, actually corresponds to a valid block. However, |
1199 | | /// > when it comes to parachain consensus, no such verification is performed. |
1200 | | /// > Contrary to the hash stored in the header, the value of this field is |
1201 | | /// > guaranteed to refer to a block that is known by the syncing service. This |
1202 | | /// > allows a subscriber of the state of the chain to precisely track the hierarchy |
1203 | | /// > of blocks, without risking to run into a problem in case of a block with an |
1204 | | /// > invalid header. |
1205 | | pub parent_hash: [u8; 32], |
1206 | | } |
1207 | | |
1208 | | enum ToBackground { |
1209 | | /// See [`SyncService::is_near_head_of_chain_heuristic`]. |
1210 | | IsNearHeadOfChainHeuristic { send_back: oneshot::Sender<bool> }, |
1211 | | /// See [`SyncService::subscribe_all`]. |
1212 | | SubscribeAll { |
1213 | | send_back: oneshot::Sender<SubscribeAll>, |
1214 | | buffer_size: usize, |
1215 | | runtime_interest: bool, |
1216 | | }, |
1217 | | /// See [`SyncService::peers_assumed_know_blocks`]. |
1218 | | PeersAssumedKnowBlock { |
1219 | | send_back: oneshot::Sender<Vec<PeerId>>, |
1220 | | block_number: u64, |
1221 | | block_hash: [u8; 32], |
1222 | | }, |
1223 | | /// See [`SyncService::syncing_peers`]. |
1224 | | SyncingPeers { |
1225 | | send_back: oneshot::Sender<Vec<(PeerId, codec::Role, u64, [u8; 32])>>, |
1226 | | }, |
1227 | | /// See [`SyncService::serialize_chain_information`]. |
1228 | | SerializeChainInformation { |
1229 | | send_back: oneshot::Sender<Option<chain::chain_information::ValidChainInformation>>, |
1230 | | }, |
1231 | | } |