Coverage Report

Created: 2024-05-16 12:16

/__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
}