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