Coverage Report

Created: 2024-05-16 12:16

/__w/smoldot/smoldot/repo/lib/src/network/codec/grandpa_warp_sync.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
//! 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
0
#[derive(Debug, derive_more::Display)]
Unexecuted instantiation: _RNvXs1_NtNtNtCsN16ciHI6Qf_7smoldot7network5codec17grandpa_warp_syncNtB5_34DecodeGrandpaWarpSyncResponseErrorNtNtCsaYZPK01V26L_4core3fmt7Display3fmt
Unexecuted instantiation: _RNvXs1_NtNtNtCseuYC0Zibziv_7smoldot7network5codec17grandpa_warp_syncNtB5_34DecodeGrandpaWarpSyncResponseErrorNtNtCsaYZPK01V26L_4core3fmt7Display3fmt
74
#[display(fmt = "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::combinator::all_consuming(nom::combinator::map(
83
0
        nom::sequence::tuple((
84
0
            decode_fragments(block_number_bytes),
85
0
            nom::number::streaming::le_u8,
86
0
        )),
87
0
        |(fragments, is_finished)| GrandpaWarpSyncResponse {
88
0
            fragments,
89
0
            is_finished: is_finished != 0,
90
0
        },
Unexecuted instantiation: _RNCNvNtNtNtCsN16ciHI6Qf_7smoldot7network5codec17grandpa_warp_sync33decode_grandpa_warp_sync_response0B9_
Unexecuted instantiation: _RNCNvNtNtNtCseuYC0Zibziv_7smoldot7network5codec17grandpa_warp_sync33decode_grandpa_warp_sync_response0B9_
91
0
    ))(encoded)
92
0
    .map(|(_, parse_result)| parse_result)
Unexecuted instantiation: _RNCNvNtNtNtCsN16ciHI6Qf_7smoldot7network5codec17grandpa_warp_sync33decode_grandpa_warp_sync_responses_0B9_
Unexecuted instantiation: _RNCNvNtNtNtCseuYC0Zibziv_7smoldot7network5codec17grandpa_warp_sync33decode_grandpa_warp_sync_responses_0B9_
93
0
    .map_err(|_| DecodeGrandpaWarpSyncResponseError)
Unexecuted instantiation: _RNCNvNtNtNtCsN16ciHI6Qf_7smoldot7network5codec17grandpa_warp_sync33decode_grandpa_warp_sync_responses0_0B9_
Unexecuted instantiation: _RNCNvNtNtNtCseuYC0Zibziv_7smoldot7network5codec17grandpa_warp_sync33decode_grandpa_warp_sync_responses0_0B9_
94
0
}
Unexecuted instantiation: _RNvNtNtNtCsN16ciHI6Qf_7smoldot7network5codec17grandpa_warp_sync33decode_grandpa_warp_sync_response
Unexecuted instantiation: _RNvNtNtNtCseuYC0Zibziv_7smoldot7network5codec17grandpa_warp_sync33decode_grandpa_warp_sync_response
95
96
0
fn decode_fragments<'a>(
97
0
    block_number_bytes: usize,
98
0
) -> impl FnMut(&'a [u8]) -> nom::IResult<&[u8], Vec<GrandpaWarpSyncResponseFragment>> {
99
0
    nom::combinator::flat_map(crate::util::nom_scale_compact_usize, move |num_elems| {
100
0
        nom::multi::many_m_n(num_elems, num_elems, decode_fragment(block_number_bytes))
101
0
    })
Unexecuted instantiation: _RNCNvNtNtNtCsN16ciHI6Qf_7smoldot7network5codec17grandpa_warp_sync16decode_fragments0B9_
Unexecuted instantiation: _RNCNvNtNtNtCseuYC0Zibziv_7smoldot7network5codec17grandpa_warp_sync16decode_fragments0B9_
102
0
}
Unexecuted instantiation: _RNvNtNtNtCsN16ciHI6Qf_7smoldot7network5codec17grandpa_warp_sync16decode_fragments
Unexecuted instantiation: _RNvNtNtNtCseuYC0Zibziv_7smoldot7network5codec17grandpa_warp_sync16decode_fragments
103
104
0
fn decode_fragment<'a>(
105
0
    block_number_bytes: usize,
106
0
) -> impl FnMut(&'a [u8]) -> nom::IResult<&[u8], GrandpaWarpSyncResponseFragment> {
107
0
    nom::combinator::map(
108
0
        nom::sequence::tuple((
109
0
            nom::combinator::recognize(move |s| {
110
0
                header::decode_partial(s, block_number_bytes)
111
0
                    .map(|(a, b)| (b, a))
Unexecuted instantiation: _RNCNCNvNtNtNtCsN16ciHI6Qf_7smoldot7network5codec17grandpa_warp_sync15decode_fragment00Bb_
Unexecuted instantiation: _RNCNCNvNtNtNtCseuYC0Zibziv_7smoldot7network5codec17grandpa_warp_sync15decode_fragment00Bb_
112
0
                    .map_err(|_| {
113
0
                        nom::Err::Failure(nom::error::make_error(s, nom::error::ErrorKind::Verify))
114
0
                    })
Unexecuted instantiation: _RNCNCNvNtNtNtCsN16ciHI6Qf_7smoldot7network5codec17grandpa_warp_sync15decode_fragment0s_0Bb_
Unexecuted instantiation: _RNCNCNvNtNtNtCseuYC0Zibziv_7smoldot7network5codec17grandpa_warp_sync15decode_fragment0s_0Bb_
115
0
            }),
Unexecuted instantiation: _RNCNvNtNtNtCsN16ciHI6Qf_7smoldot7network5codec17grandpa_warp_sync15decode_fragment0B9_
Unexecuted instantiation: _RNCNvNtNtNtCseuYC0Zibziv_7smoldot7network5codec17grandpa_warp_sync15decode_fragment0B9_
116
0
            nom::combinator::recognize(move |s| {
117
0
                finality::decode::decode_partial_grandpa_justification(s, block_number_bytes)
118
0
                    .map(|(a, b)| (b, a))
Unexecuted instantiation: _RNCNCNvNtNtNtCsN16ciHI6Qf_7smoldot7network5codec17grandpa_warp_sync15decode_fragments_00Bb_
Unexecuted instantiation: _RNCNCNvNtNtNtCseuYC0Zibziv_7smoldot7network5codec17grandpa_warp_sync15decode_fragments_00Bb_
119
0
                    .map_err(|_| {
120
0
                        nom::Err::Failure(nom::error::make_error(s, nom::error::ErrorKind::Verify))
121
0
                    })
Unexecuted instantiation: _RNCNCNvNtNtNtCsN16ciHI6Qf_7smoldot7network5codec17grandpa_warp_sync15decode_fragments_0s_0Bb_
Unexecuted instantiation: _RNCNCNvNtNtNtCseuYC0Zibziv_7smoldot7network5codec17grandpa_warp_sync15decode_fragments_0s_0Bb_
122
0
            }),
Unexecuted instantiation: _RNCNvNtNtNtCsN16ciHI6Qf_7smoldot7network5codec17grandpa_warp_sync15decode_fragments_0B9_
Unexecuted instantiation: _RNCNvNtNtNtCseuYC0Zibziv_7smoldot7network5codec17grandpa_warp_sync15decode_fragments_0B9_
123
0
        )),
124
0
        move |(scale_encoded_header, scale_encoded_justification)| {
125
0
            GrandpaWarpSyncResponseFragment {
126
0
                scale_encoded_header,
127
0
                scale_encoded_justification,
128
0
            }
129
0
        },
Unexecuted instantiation: _RNCNvNtNtNtCsN16ciHI6Qf_7smoldot7network5codec17grandpa_warp_sync15decode_fragments0_0B9_
Unexecuted instantiation: _RNCNvNtNtNtCseuYC0Zibziv_7smoldot7network5codec17grandpa_warp_sync15decode_fragments0_0B9_
130
0
    )
131
0
}
Unexecuted instantiation: _RNvNtNtNtCsN16ciHI6Qf_7smoldot7network5codec17grandpa_warp_sync15decode_fragment
Unexecuted instantiation: _RNvNtNtNtCseuYC0Zibziv_7smoldot7network5codec17grandpa_warp_sync15decode_fragment