/__w/smoldot/smoldot/repo/lib/src/network/codec/grandpa_warp_sync.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 | | //! The GrandPa warp sync protocol is a request-response protocol. |
19 | | //! |
20 | | //! The request's body consists in a block hash. |
21 | | //! |
22 | | //! The response's body consists in a sequence of so-called *fragments*. Each fragment consists in |
23 | | //! a block header and a GrandPa justification corresponding to this header. The justification |
24 | | //! must be verified. |
25 | | //! |
26 | | //! The fragments only contain blocks higher than the hash of the block passed in the request. |
27 | | //! |
28 | | //! By doing a GrandPa warp sync request, a node is capable of quickly obtaining a proof that a |
29 | | //! certain recent block has been finalized by authorities. |
30 | | //! |
31 | | //! A proof has to be minimum. All the headers in all fragments, except for the last, have to |
32 | | //! contain a change in the set of GrandPa authorities. |
33 | | //! |
34 | | //! The responding node has the possibility to cut proofs that are above a certain threshold. When |
35 | | //! it does so, [`GrandpaWarpSyncResponse::is_finished`] should be set to `false`, so that the |
36 | | //! requester can start additional warp sync requests afterwards. |
37 | | |
38 | | use crate::{finality, header}; |
39 | | |
40 | | use alloc::vec::Vec; |
41 | | |
42 | | // TODO: all the constraints explained here should be checked when decoding the message |
43 | | |
44 | | /// Response to a GrandPa warp sync request. |
45 | | #[derive(Debug)] |
46 | | pub struct GrandpaWarpSyncResponse<'a> { |
47 | | /// List of fragments that consist in the proof. |
48 | | /// |
49 | | /// The fragments must be ordered by ascending block height. |
50 | | pub fragments: Vec<GrandpaWarpSyncResponseFragment<'a>>, |
51 | | |
52 | | /// If `true`, the last fragment corresponds to the highest finalized block known to the |
53 | | /// responder. If `false`, the requested is encouraged to start a follow-up GrandPa warp sync |
54 | | /// request starting at the last block in the fragments. |
55 | | pub is_finished: bool, |
56 | | } |
57 | | |
58 | | /// Response to a GrandPa warp sync request. |
59 | | #[derive(Debug)] |
60 | | pub struct GrandpaWarpSyncResponseFragment<'a> { |
61 | | /// Header of a block in the chain. |
62 | | /// |
63 | | /// Must always contain a change in the list of authorities, except for the last fragment |
64 | | /// if [`GrandpaWarpSyncResponse::is_finished`] is `true`. |
65 | | pub scale_encoded_header: &'a [u8], |
66 | | |
67 | | /// Justification that proves the finality of |
68 | | /// [`GrandpaWarpSyncResponseFragment::scale_encoded_header`]. |
69 | | pub scale_encoded_justification: &'a [u8], |
70 | | } |
71 | | |
72 | | /// Error potentially returned by [`decode_grandpa_warp_sync_response`]. |
73 | | #[derive(Debug, derive_more::Display, derive_more::Error)] |
74 | | #[display("Failed to decode response")] |
75 | | pub struct DecodeGrandpaWarpSyncResponseError; |
76 | | |
77 | | /// Decodes a SCALE-encoded GrandPa warp sync response. |
78 | 0 | pub fn decode_grandpa_warp_sync_response( |
79 | 0 | encoded: &[u8], |
80 | 0 | block_number_bytes: usize, |
81 | 0 | ) -> Result<GrandpaWarpSyncResponse, DecodeGrandpaWarpSyncResponseError> { |
82 | 0 | nom::Parser::parse( |
83 | 0 | &mut nom::combinator::all_consuming::<_, (&[u8], nom::error::ErrorKind), _>( |
84 | 0 | nom::combinator::map( |
85 | 0 | ( |
86 | 0 | decode_fragments(block_number_bytes), |
87 | 0 | nom::number::streaming::le_u8, |
88 | 0 | ), |
89 | | |(fragments, is_finished)| GrandpaWarpSyncResponse { |
90 | 0 | fragments, |
91 | 0 | is_finished: is_finished != 0, |
92 | 0 | }, Unexecuted instantiation: _RNCNvNtNtNtCsjlkOsLH0Zfj_7smoldot7network5codec17grandpa_warp_sync33decode_grandpa_warp_sync_response0B9_ Unexecuted instantiation: _RNCNvNtNtNtCsc1ywvx6YAnK_7smoldot7network5codec17grandpa_warp_sync33decode_grandpa_warp_sync_response0B9_ |
93 | | ), |
94 | | ), |
95 | 0 | encoded, |
96 | | ) |
97 | 0 | .map(|(_, parse_result)| parse_result) |
98 | 0 | .map_err(|_| DecodeGrandpaWarpSyncResponseError) |
99 | 0 | } Unexecuted instantiation: _RNvNtNtNtCsjlkOsLH0Zfj_7smoldot7network5codec17grandpa_warp_sync33decode_grandpa_warp_sync_response Unexecuted instantiation: _RNvNtNtNtCsc1ywvx6YAnK_7smoldot7network5codec17grandpa_warp_sync33decode_grandpa_warp_sync_response |
100 | | |
101 | 0 | fn decode_fragments<'a, E: nom::error::ParseError<&'a [u8]>>( |
102 | 0 | block_number_bytes: usize, |
103 | 0 | ) -> impl nom::Parser<&'a [u8], Output = Vec<GrandpaWarpSyncResponseFragment<'a>>, Error = E> { |
104 | 0 | nom::combinator::flat_map(crate::util::nom_scale_compact_usize, move |num_elems| { |
105 | 0 | nom::multi::many_m_n(num_elems, num_elems, decode_fragment(block_number_bytes)) |
106 | 0 | }) Unexecuted instantiation: _RNCINvNtNtNtCsjlkOsLH0Zfj_7smoldot7network5codec17grandpa_warp_sync16decode_fragmentspE0Ba_ Unexecuted instantiation: _RNCINvNtNtNtCsc1ywvx6YAnK_7smoldot7network5codec17grandpa_warp_sync16decode_fragmentsTRShNtNtCsfjEU5fc64iO_3nom5error9ErrorKindEE0Ba_ |
107 | 0 | } Unexecuted instantiation: _RINvNtNtNtCsjlkOsLH0Zfj_7smoldot7network5codec17grandpa_warp_sync16decode_fragmentspEB8_ Unexecuted instantiation: _RINvNtNtNtCsc1ywvx6YAnK_7smoldot7network5codec17grandpa_warp_sync16decode_fragmentsTRShNtNtCsfjEU5fc64iO_3nom5error9ErrorKindEEB8_ |
108 | | |
109 | 0 | fn decode_fragment<'a, E: nom::error::ParseError<&'a [u8]>>( |
110 | 0 | block_number_bytes: usize, |
111 | 0 | ) -> impl nom::Parser<&'a [u8], Output = GrandpaWarpSyncResponseFragment<'a>, Error = E> { |
112 | 0 | nom::combinator::map( |
113 | | ( |
114 | 0 | nom::combinator::recognize(move |s| { |
115 | 0 | header::decode_partial(s, block_number_bytes) |
116 | 0 | .map(|(a, b)| (b, a)) Unexecuted instantiation: _RNCNCINvNtNtNtCsjlkOsLH0Zfj_7smoldot7network5codec17grandpa_warp_sync15decode_fragmentpE00Bc_ Unexecuted instantiation: _RNCNCINvNtNtNtCsc1ywvx6YAnK_7smoldot7network5codec17grandpa_warp_sync15decode_fragmentTRShNtNtCsfjEU5fc64iO_3nom5error9ErrorKindEE00Bc_ |
117 | 0 | .map_err(|_| { |
118 | 0 | nom::Err::Failure(nom::error::make_error(s, nom::error::ErrorKind::Verify)) |
119 | 0 | }) Unexecuted instantiation: _RNCNCINvNtNtNtCsjlkOsLH0Zfj_7smoldot7network5codec17grandpa_warp_sync15decode_fragmentpE0s_0Bc_ Unexecuted instantiation: _RNCNCINvNtNtNtCsc1ywvx6YAnK_7smoldot7network5codec17grandpa_warp_sync15decode_fragmentTRShNtNtCsfjEU5fc64iO_3nom5error9ErrorKindEE0s_0Bc_ |
120 | 0 | }), Unexecuted instantiation: _RNCINvNtNtNtCsjlkOsLH0Zfj_7smoldot7network5codec17grandpa_warp_sync15decode_fragmentpE0Ba_ Unexecuted instantiation: _RNCINvNtNtNtCsc1ywvx6YAnK_7smoldot7network5codec17grandpa_warp_sync15decode_fragmentTRShNtNtCsfjEU5fc64iO_3nom5error9ErrorKindEE0Ba_ |
121 | 0 | nom::combinator::recognize(move |s| { |
122 | 0 | finality::decode::decode_partial_grandpa_justification(s, block_number_bytes) |
123 | 0 | .map(|(a, b)| (b, a)) Unexecuted instantiation: _RNCNCINvNtNtNtCsjlkOsLH0Zfj_7smoldot7network5codec17grandpa_warp_sync15decode_fragmentpEs_00Bc_ Unexecuted instantiation: _RNCNCINvNtNtNtCsc1ywvx6YAnK_7smoldot7network5codec17grandpa_warp_sync15decode_fragmentTRShNtNtCsfjEU5fc64iO_3nom5error9ErrorKindEEs_00Bc_ |
124 | 0 | .map_err(|_| { |
125 | 0 | nom::Err::Failure(nom::error::make_error(s, nom::error::ErrorKind::Verify)) |
126 | 0 | }) Unexecuted instantiation: _RNCNCINvNtNtNtCsjlkOsLH0Zfj_7smoldot7network5codec17grandpa_warp_sync15decode_fragmentpEs_0s_0Bc_ Unexecuted instantiation: _RNCNCINvNtNtNtCsc1ywvx6YAnK_7smoldot7network5codec17grandpa_warp_sync15decode_fragmentTRShNtNtCsfjEU5fc64iO_3nom5error9ErrorKindEEs_0s_0Bc_ |
127 | 0 | }), Unexecuted instantiation: _RNCINvNtNtNtCsjlkOsLH0Zfj_7smoldot7network5codec17grandpa_warp_sync15decode_fragmentpEs_0Ba_ Unexecuted instantiation: _RNCINvNtNtNtCsc1ywvx6YAnK_7smoldot7network5codec17grandpa_warp_sync15decode_fragmentTRShNtNtCsfjEU5fc64iO_3nom5error9ErrorKindEEs_0Ba_ |
128 | | ), |
129 | 0 | move |(scale_encoded_header, scale_encoded_justification)| { |
130 | 0 | GrandpaWarpSyncResponseFragment { |
131 | 0 | scale_encoded_header, |
132 | 0 | scale_encoded_justification, |
133 | 0 | } |
134 | 0 | }, Unexecuted instantiation: _RNCINvNtNtNtCsjlkOsLH0Zfj_7smoldot7network5codec17grandpa_warp_sync15decode_fragmentpEs0_0Ba_ Unexecuted instantiation: _RNCINvNtNtNtCsc1ywvx6YAnK_7smoldot7network5codec17grandpa_warp_sync15decode_fragmentTRShNtNtCsfjEU5fc64iO_3nom5error9ErrorKindEEs0_0Ba_ |
135 | | ) |
136 | 0 | } Unexecuted instantiation: _RINvNtNtNtCsjlkOsLH0Zfj_7smoldot7network5codec17grandpa_warp_sync15decode_fragmentpEB8_ Unexecuted instantiation: _RINvNtNtNtCsc1ywvx6YAnK_7smoldot7network5codec17grandpa_warp_sync15decode_fragmentTRShNtNtCsfjEU5fc64iO_3nom5error9ErrorKindEEB8_ |