Coverage Report

Created: 2024-05-16 12:16

/__w/smoldot/smoldot/repo/lib/src/network/codec/state_request.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
//! State requests protocol.
19
//!
20
//! # Overview
21
//!
22
//! A state request consists in asking for the remote to send back all the storage entries
23
//! starting at a specific key. As many storage entries are included in the response as possible,
24
//! until the response fits the size of 2MiB.
25
//!
26
//! After a response has been received, the sender is expected to send back another request (with
27
//! a start key right after the last key of the response) in order to continue downloading the
28
//! storage entries.
29
//!
30
//! The format of the response is a compact Merkle proof.
31
//!
32
//! > **Note**: The implementation in this module always requests a proof from the server.
33
//! >           Substrate nodes also support a "no proof" mode where, instead of a proof, the list
34
//! >           of entries are simply returned without any way to verify them. This alternative
35
//! >           mode is supposed to be used only in situations where the peer the request is sent
36
//! >           to is trusted. Because this "no proof" mode is very niche, the implementation in
37
//! >           this module doesn't support it.
38
//!
39
//! # AboutĀ child tries
40
//!
41
//! For the purpose of this protocol, the content of child tries is as if they existed in the main
42
//! trie under the key `:child_storage:default:`. For example, the child trie `0xabcd` is
43
//! considered to be at key `concat(b":child_storage:default:", 0xabcd)`.
44
//!
45
//! In the response, the child trie Merkle proof is associated with the main trie entry
46
//! corresponding to the child trie. In other words, it is as if the child trie entry in the main
47
//! trie was a branch node, except that it has a value corresponding to the hash of the root of
48
//! the child trie.
49
//!
50
51
use crate::util::protobuf;
52
53
/// Description of a state request that can be sent to a peer.
54
#[derive(Debug, Clone, PartialEq, Eq)]
55
pub struct StateRequest<'a> {
56
    /// Hash of the block to make the request against.
57
    pub block_hash: &'a [u8; 32],
58
59
    /// Response shouldn't contain any key lexicographically inferior to this key.
60
    ///
61
    /// > **Note**: Because a response has a limited size, this field lets you send additional
62
    /// >           requests that start where the previous response has ended.
63
    pub start_key: StateRequestStart<'a>,
64
}
65
66
/// See [`StateRequest::start_key`].
67
#[derive(Debug, Clone, PartialEq, Eq)]
68
pub enum StateRequestStart<'a> {
69
    /// Start iterating at a key in the main trie.
70
    MainTrie(&'a [u8]),
71
    /// Start iterating at a key in a child trie.
72
    ChildTrieDefault {
73
        /// Key of the child trie.
74
        child_trie: &'a [u8],
75
        /// Key within the child trie.
76
        key: &'a [u8],
77
    },
78
}
79
80
// See <https://github.com/paritytech/substrate/blob/088a7fc5e66cc08b1a5ac2fbe53e19baf7349489/client/network/sync/src/schema/api.v1.proto#L72-L105>
81
// for protocol definition.
82
83
/// Builds the bytes corresponding to a state request.
84
0
pub fn build_state_request(
85
0
    config: StateRequest<'_>,
86
0
) -> impl Iterator<Item = impl AsRef<[u8]> + '_> + '_ {
87
0
    let start = match config.start_key {
88
0
        StateRequestStart::MainTrie(key) => {
89
0
            either::Left(protobuf::bytes_tag_encode(2, key).map(either::Left))
90
        }
91
0
        StateRequestStart::ChildTrieDefault { child_trie, key } => either::Right(
92
0
            protobuf::bytes_tag_encode(2, {
93
0
                let mut vec = b":child_storage:default:".to_vec();
94
0
                vec.extend(child_trie);
95
0
                vec
96
0
            })
97
0
            .map(either::Left)
98
0
            .chain(protobuf::bytes_tag_encode(2, key).map(either::Right))
99
0
            .map(either::Right),
100
0
        ),
101
    };
102
103
0
    protobuf::bytes_tag_encode(1, config.block_hash)
104
0
        .map(either::Right)
105
0
        .map(either::Right)
106
0
        .chain(start.map(either::Left).map(either::Right))
107
0
        .chain(protobuf::bool_tag_encode(3, false).map(either::Left))
108
0
}
Unexecuted instantiation: _RNvNtNtNtCsN16ciHI6Qf_7smoldot7network5codec13state_request19build_state_request
Unexecuted instantiation: _RNvNtNtNtCseuYC0Zibziv_7smoldot7network5codec13state_request19build_state_request
109
110
/// Decodes a response to a state request.
111
///
112
/// On success, contains a Merkle proof.
113
0
pub fn decode_state_response(response_bytes: &[u8]) -> Result<&[u8], DecodeStateResponseError> {
114
0
    let mut parser = nom::combinator::all_consuming::<_, _, nom::error::Error<&[u8]>, _>(
115
0
        nom::combinator::complete(protobuf::message_decode! {
116
0
            #[required] proof = 2 => protobuf::bytes_tag_decode,
117
0
        }),
Unexecuted instantiation: _RNCNCNvNtNtNtCsN16ciHI6Qf_7smoldot7network5codec13state_request21decode_state_response00Bb_
Unexecuted instantiation: _RNCNCNvNtNtNtCseuYC0Zibziv_7smoldot7network5codec13state_request21decode_state_response00Bb_
118
0
    );
119
120
0
    let proof = match nom::Finish::finish(parser(response_bytes)) {
121
0
        Ok((_, proof)) => proof.proof,
122
0
        Err(_) => return Err(DecodeStateResponseError::ProtobufDecode),
123
    };
124
125
0
    Ok(proof)
126
0
}
Unexecuted instantiation: _RNvNtNtNtCsN16ciHI6Qf_7smoldot7network5codec13state_request21decode_state_response
Unexecuted instantiation: _RNvNtNtNtCseuYC0Zibziv_7smoldot7network5codec13state_request21decode_state_response
127
128
/// Error potentially returned by [`decode_state_response`].
129
0
#[derive(Debug, derive_more::Display, Clone)]
Unexecuted instantiation: _RNvXs9_NtNtNtCsN16ciHI6Qf_7smoldot7network5codec13state_requestNtB5_24DecodeStateResponseErrorNtNtCsaYZPK01V26L_4core3fmt7Display3fmt
Unexecuted instantiation: _RNvXs9_NtNtNtCseuYC0Zibziv_7smoldot7network5codec13state_requestNtB5_24DecodeStateResponseErrorNtNtCsaYZPK01V26L_4core3fmt7Display3fmt
130
#[display(fmt = "Failed to decode response")]
131
pub enum DecodeStateResponseError {
132
    /// Error while decoding the Protobuf encoding.
133
    ProtobufDecode,
134
}