Coverage Report

Created: 2026-02-09 03:50

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/__w/smoldot/smoldot/repo/lib/src/network/codec/storage_call_proof.rs
Line
Count
Source
1
// Smoldot
2
// Copyright (C) 2019-2022  Parity Technologies (UK) Ltd.
3
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
4
5
// This program is free software: you can redistribute it and/or modify
6
// it under the terms of the GNU General Public License as published by
7
// the Free Software Foundation, either version 3 of the License, or
8
// (at your option) any later version.
9
10
// This program is distributed in the hope that it will be useful,
11
// but WITHOUT ANY WARRANTY; without even the implied warranty of
12
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
// GNU General Public License for more details.
14
15
// You should have received a copy of the GNU General Public License
16
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
18
use crate::util::protobuf;
19
20
use alloc::{borrow::Cow, vec::Vec};
21
22
/// Maximum size in bytes for light protocol requests (1 MiB).
23
pub const LIGHT_PROTOCOL_REQUEST_MAX_SIZE: usize = 1024 * 1024;
24
25
/// Description of a storage proof request that can be sent to a peer.
26
#[derive(Debug, Clone, PartialEq, Eq)]
27
pub struct StorageProofRequestConfig<TKeysIter> {
28
    /// Hash of the block to request the storage of.
29
    pub block_hash: [u8; 32],
30
    /// List of storage keys to query.
31
    pub keys: TKeysIter,
32
}
33
34
// See https://github.com/paritytech/substrate/blob/c8653447fc8ef8d95a92fe164c96dffb37919e85/client/network/sync/src/schema/api.v1.proto
35
// for protocol definition.
36
37
/// Builds the bytes corresponding to a storage proof request.
38
0
pub fn build_storage_proof_request<'a>(
39
0
    config: StorageProofRequestConfig<impl Iterator<Item = impl AsRef<[u8]> + Clone + 'a> + 'a>,
40
0
) -> impl Iterator<Item = impl AsRef<[u8]>> {
41
0
    protobuf::message_tag_encode(
42
        2,
43
0
        protobuf::bytes_tag_encode(2, config.block_hash)
44
0
            .map(either::Left)
45
0
            .chain(
46
0
                config
47
0
                    .keys
48
0
                    .flat_map(|key| protobuf::bytes_tag_encode(3, key))
Unexecuted instantiation: _RNCINvNtNtNtCs58uIhW5Y0bx_7smoldot7network5codec18storage_call_proof27build_storage_proof_requestppE0Ba_
Unexecuted instantiation: _RNCINvNtNtNtCs3z09vimSLiZ_7smoldot7network5codec18storage_call_proof27build_storage_proof_requestppE0Ba_
Unexecuted instantiation: _RNCINvNtNtNtCs3z09vimSLiZ_7smoldot7network5codec18storage_call_proof27build_storage_proof_requestINtNtCsgag4BwXrjBQ_5alloc3vec3VechEINtNtB1A_9into_iter8IntoIterB1x_EE0Cs3ofuaB6yaZz_18smoldot_light_wasm
Unexecuted instantiation: _RNCINvNtNtNtCs3z09vimSLiZ_7smoldot7network5codec18storage_call_proof27build_storage_proof_requestINtNtCsgag4BwXrjBQ_5alloc3vec3VechEINtNtB1A_9into_iter8IntoIterB1x_EE0Csj0Ln1dRYnP8_17smoldot_full_node
Unexecuted instantiation: _RNCINvNtNtNtCs3z09vimSLiZ_7smoldot7network5codec18storage_call_proof27build_storage_proof_requestINtNtCsgag4BwXrjBQ_5alloc3vec3VechEINtNtB1A_9into_iter8IntoIterB1x_EE0Cs9Z0u6foPxGK_17smoldot_full_node
49
0
                    .map(either::Right),
50
            ),
51
    )
52
0
}
Unexecuted instantiation: _RINvNtNtNtCs58uIhW5Y0bx_7smoldot7network5codec18storage_call_proof27build_storage_proof_requestppEB8_
Unexecuted instantiation: _RINvNtNtNtCs3z09vimSLiZ_7smoldot7network5codec18storage_call_proof27build_storage_proof_requestppEB8_
Unexecuted instantiation: _RINvNtNtNtCs3z09vimSLiZ_7smoldot7network5codec18storage_call_proof27build_storage_proof_requestINtNtCsgag4BwXrjBQ_5alloc3vec3VechEINtNtB1y_9into_iter8IntoIterB1v_EECs3ofuaB6yaZz_18smoldot_light_wasm
Unexecuted instantiation: _RINvNtNtNtCs3z09vimSLiZ_7smoldot7network5codec18storage_call_proof27build_storage_proof_requestINtNtCsgag4BwXrjBQ_5alloc3vec3VechEINtNtB1y_9into_iter8IntoIterB1v_EECsj0Ln1dRYnP8_17smoldot_full_node
Unexecuted instantiation: _RINvNtNtNtCs3z09vimSLiZ_7smoldot7network5codec18storage_call_proof27build_storage_proof_requestINtNtCsgag4BwXrjBQ_5alloc3vec3VechEINtNtB1y_9into_iter8IntoIterB1v_EECs9Z0u6foPxGK_17smoldot_full_node
53
54
/// Description of a call proof request that can be sent to a peer.
55
#[derive(Debug, Clone, PartialEq, Eq)]
56
pub struct CallProofRequestConfig<'a, I> {
57
    /// Hash of the block to request the storage of.
58
    pub block_hash: [u8; 32],
59
    /// Name of the runtime function to call.
60
    pub method: Cow<'a, str>,
61
    /// Iterator to buffers of bytes to be concatenated then passed as input to the call. The
62
    /// semantics of these bytes depend on which method is being called.
63
    pub parameter_vectored: I,
64
}
65
66
// See https://github.com/paritytech/substrate/blob/c8653447fc8ef8d95a92fe164c96dffb37919e85/client/network/light/src/schema/light.v1.proto
67
// for protocol definition.
68
69
/// Builds the bytes corresponding to a call proof request.
70
0
pub fn build_call_proof_request<'a>(
71
0
    config: CallProofRequestConfig<'a, impl Iterator<Item = impl AsRef<[u8]> + 'a> + 'a>,
72
0
) -> impl Iterator<Item = impl AsRef<[u8]>> {
73
    // TODO: don't allocate here
74
0
    let parameter = config
75
0
        .parameter_vectored
76
0
        .fold(Vec::with_capacity(512), |mut a, b| {
77
0
            a.extend_from_slice(b.as_ref());
78
0
            a
79
0
        });
Unexecuted instantiation: _RNCINvNtNtNtCs58uIhW5Y0bx_7smoldot7network5codec18storage_call_proof24build_call_proof_requestppE0Ba_
Unexecuted instantiation: _RNCINvNtNtNtCs3z09vimSLiZ_7smoldot7network5codec18storage_call_proof24build_call_proof_requestppE0Ba_
Unexecuted instantiation: _RNCINvNtNtNtCs3z09vimSLiZ_7smoldot7network5codec18storage_call_proof24build_call_proof_requestINtNtCsgag4BwXrjBQ_5alloc3vec3VechEINtNtB1x_9into_iter8IntoIterB1u_EE0Cs3ofuaB6yaZz_18smoldot_light_wasm
Unexecuted instantiation: _RNCINvNtNtNtCs3z09vimSLiZ_7smoldot7network5codec18storage_call_proof24build_call_proof_requestINtNtCsgag4BwXrjBQ_5alloc3vec3VechEINtNtB1x_9into_iter8IntoIterB1u_EE0Csj0Ln1dRYnP8_17smoldot_full_node
Unexecuted instantiation: _RNCINvNtNtNtCs3z09vimSLiZ_7smoldot7network5codec18storage_call_proof24build_call_proof_requestINtNtCsgag4BwXrjBQ_5alloc3vec3VechEINtNtB1x_9into_iter8IntoIterB1u_EE0Cs9Z0u6foPxGK_17smoldot_full_node
80
81
0
    protobuf::message_tag_encode(
82
        1,
83
0
        protobuf::bytes_tag_encode(2, config.block_hash)
84
0
            .map(either::Left)
85
0
            .chain(
86
0
                protobuf::string_tag_encode(3, config.method)
87
0
                    .map(either::Left)
88
0
                    .map(either::Right),
89
            )
90
0
            .chain(
91
0
                protobuf::bytes_tag_encode(4, parameter)
92
0
                    .map(either::Right)
93
0
                    .map(either::Right),
94
            ),
95
    )
96
0
}
Unexecuted instantiation: _RINvNtNtNtCs58uIhW5Y0bx_7smoldot7network5codec18storage_call_proof24build_call_proof_requestppEB8_
Unexecuted instantiation: _RINvNtNtNtCs3z09vimSLiZ_7smoldot7network5codec18storage_call_proof24build_call_proof_requestppEB8_
Unexecuted instantiation: _RINvNtNtNtCs3z09vimSLiZ_7smoldot7network5codec18storage_call_proof24build_call_proof_requestINtNtCsgag4BwXrjBQ_5alloc3vec3VechEINtNtB1v_9into_iter8IntoIterB1s_EECs3ofuaB6yaZz_18smoldot_light_wasm
Unexecuted instantiation: _RINvNtNtNtCs3z09vimSLiZ_7smoldot7network5codec18storage_call_proof24build_call_proof_requestINtNtCsgag4BwXrjBQ_5alloc3vec3VechEINtNtB1v_9into_iter8IntoIterB1s_EECsj0Ln1dRYnP8_17smoldot_full_node
Unexecuted instantiation: _RINvNtNtNtCs3z09vimSLiZ_7smoldot7network5codec18storage_call_proof24build_call_proof_requestINtNtCsgag4BwXrjBQ_5alloc3vec3VechEINtNtB1v_9into_iter8IntoIterB1s_EECs9Z0u6foPxGK_17smoldot_full_node
97
98
/// Decodes a response to a storage proof request or a call proof request.
99
///
100
/// On success, returns a SCALE-encoded Merkle proof, or `None` if the remote couldn't answer
101
/// the request.
102
0
pub fn decode_storage_or_call_proof_response(
103
0
    ty: StorageOrCallProof,
104
0
    response_bytes: &[u8],
105
0
) -> Result<Option<&[u8]>, DecodeStorageCallProofResponseError> {
106
0
    let field_num = match ty {
107
0
        StorageOrCallProof::CallProof => 1,
108
        // Both storage and child storage use remote_read_response (field 2)
109
0
        StorageOrCallProof::StorageProof | StorageOrCallProof::ChildStorageProof => 2,
110
    };
111
112
    // TODO: while the `proof` field is correctly optional, the `response` field isn't supposed to be optional; make it `#[required]` again once https://github.com/paritytech/substrate/pull/12732 has been merged and released
113
114
0
    let mut parser = nom::combinator::all_consuming::<_, nom::error::Error<&[u8]>, _>(
115
0
        nom::combinator::complete(protobuf::message_decode! {
116
            #[optional] response = field_num => protobuf::message_tag_decode(protobuf::message_decode!{
117
                #[optional] proof = 2 => protobuf::bytes_tag_decode
118
            }),
119
        }),
120
    );
121
122
0
    let proof = match nom::Finish::finish(nom::Parser::parse(&mut parser, response_bytes)) {
123
0
        Ok((_, out)) => out.response.and_then(|r| r.proof),
124
0
        Err(_) => return Err(DecodeStorageCallProofResponseError::ProtobufDecode),
125
    };
126
127
0
    Ok(proof)
128
0
}
Unexecuted instantiation: _RNvNtNtNtCs58uIhW5Y0bx_7smoldot7network5codec18storage_call_proof37decode_storage_or_call_proof_response
Unexecuted instantiation: _RNvNtNtNtCs3z09vimSLiZ_7smoldot7network5codec18storage_call_proof37decode_storage_or_call_proof_response
129
130
/// Error potentially returned by [`decode_storage_or_call_proof_response`].
131
#[derive(Debug, derive_more::Display, derive_more::Error, Clone)]
132
pub enum DecodeStorageCallProofResponseError {
133
    /// Error while decoding the Protobuf encoding.
134
    ProtobufDecode,
135
    /// Response isn't a response to a storage proof request.
136
    BadResponseTy,
137
    /// Failed to decode response as a storage proof.
138
    ProofDecodeError,
139
}
140
141
/// Passed as parameter to [`decode_storage_or_call_proof_response`] to indicate what kind of
142
/// request the response corresponds to.
143
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
144
pub enum StorageOrCallProof {
145
    StorageProof,
146
    CallProof,
147
    ChildStorageProof,
148
}
149
150
/// Description of a child storage proof request that can be sent to a peer.
151
#[derive(Debug, Clone, PartialEq, Eq)]
152
pub struct ChildStorageProofRequestConfig<TChildTrie, TKeysIter> {
153
    /// Hash of the block to request the storage of.
154
    pub block_hash: [u8; 32],
155
    /// Child storage key (the child trie name, without the `:child_storage:default:` prefix).
156
    pub child_trie: TChildTrie,
157
    /// List of storage keys to query within the child trie.
158
    pub keys: TKeysIter,
159
}
160
161
// See https://github.com/paritytech/substrate/blob/c8653447fc8ef8d95a92fe164c96dffb37919e85/client/network/light/src/schema/light.v1.proto
162
// for protocol definition (RemoteReadChildRequest message).
163
164
/// Builds the bytes corresponding to a child storage proof request.
165
0
pub fn build_child_storage_proof_request<'a>(
166
0
    config: ChildStorageProofRequestConfig<
167
0
        impl AsRef<[u8]> + Clone + 'a,
168
0
        impl Iterator<Item = impl AsRef<[u8]> + Clone + 'a> + 'a,
169
0
    >,
170
0
) -> impl Iterator<Item = impl AsRef<[u8]>> {
171
    // Message format for RemoteReadChildRequest (tag 4 in Request oneof):
172
    // - Field 2: block hash
173
    // - Field 3: child storage key (child trie name)
174
    // - Field 6: keys to fetch
175
0
    protobuf::message_tag_encode(
176
        4,
177
0
        protobuf::bytes_tag_encode(2, config.block_hash)
178
0
            .map(either::Left)
179
0
            .chain(
180
0
                protobuf::bytes_tag_encode(3, config.child_trie)
181
0
                    .map(either::Left)
182
0
                    .map(either::Right),
183
            )
184
0
            .chain(
185
0
                config
186
0
                    .keys
187
0
                    .flat_map(|key| protobuf::bytes_tag_encode(6, key))
Unexecuted instantiation: _RNCINvNtNtNtCs58uIhW5Y0bx_7smoldot7network5codec18storage_call_proof33build_child_storage_proof_requestpppE0Ba_
Unexecuted instantiation: _RNCINvNtNtNtCs3z09vimSLiZ_7smoldot7network5codec18storage_call_proof33build_child_storage_proof_requestpppE0Ba_
Unexecuted instantiation: _RNCINvNtNtNtCs3z09vimSLiZ_7smoldot7network5codec18storage_call_proof33build_child_storage_proof_requestRINtNtCsgag4BwXrjBQ_5alloc3vec3VechERShINtNtNtNtCs3XFJfFEDSOQ_4core4iter8adapters3map3MapINtNtNtB2p_5slice4iter4IterB1E_ENCNCINvNtCs6WQPKU7qeQD_13smoldot_light15network_service15background_taskNtNtCs3ofuaB6yaZz_18smoldot_light_wasm8platform11PlatformRefE0s8_0EE0B4O_
188
0
                    .map(either::Right)
189
0
                    .map(either::Right),
190
            ),
191
    )
192
0
}
Unexecuted instantiation: _RINvNtNtNtCs58uIhW5Y0bx_7smoldot7network5codec18storage_call_proof33build_child_storage_proof_requestpppEB8_
Unexecuted instantiation: _RINvNtNtNtCs3z09vimSLiZ_7smoldot7network5codec18storage_call_proof33build_child_storage_proof_requestpppEB8_
Unexecuted instantiation: _RINvNtNtNtCs3z09vimSLiZ_7smoldot7network5codec18storage_call_proof33build_child_storage_proof_requestRINtNtCsgag4BwXrjBQ_5alloc3vec3VechERShINtNtNtNtCs3XFJfFEDSOQ_4core4iter8adapters3map3MapINtNtNtB2n_5slice4iter4IterB1C_ENCNCINvNtCs6WQPKU7qeQD_13smoldot_light15network_service15background_taskNtNtCs3ofuaB6yaZz_18smoldot_light_wasm8platform11PlatformRefE0s8_0EEB4M_