Coverage Report

Created: 2024-05-16 12:16

/__w/smoldot/smoldot/repo/lib/src/network/codec/block_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
use crate::util::protobuf;
19
20
use alloc::{borrow::ToOwned as _, vec::Vec};
21
use core::num::NonZeroU32;
22
23
/// Description of a block request that can be sent to a peer.
24
#[derive(Debug, Clone, PartialEq, Eq)]
25
pub struct BlocksRequestConfig {
26
    /// First block that the remote must return.
27
    pub start: BlocksRequestConfigStart,
28
    /// Number of blocks to request. The remote is free to return fewer blocks than requested.
29
    pub desired_count: NonZeroU32,
30
    /// Whether the first block should be the one with the highest number, of the one with the
31
    /// lowest number.
32
    pub direction: BlocksRequestDirection,
33
    /// Which fields should be present in the response.
34
    pub fields: BlocksRequestFields,
35
}
36
37
/// Whether the first block should be the one with the highest number, of the one with the lowest
38
/// number.
39
#[derive(Debug, Clone, PartialEq, Eq)]
40
pub enum BlocksRequestDirection {
41
    /// Blocks should be returned in ascending number, starting from the requested one. In other
42
    /// words, amongst all the blocks in the response, the requested block must be the one with
43
    /// the lowest block number, and all the other blocks are its descendants.
44
    Ascending,
45
    /// Blocks should be returned in descending number, starting from the requested one. In other
46
    /// words, amongst all the blocks in the response, the requested block must be the one with
47
    /// the highest block number, and all the other blocks are its ancestors.
48
    Descending,
49
}
50
51
/// Which fields should be present in the response.
52
#[derive(Debug, Clone, PartialEq, Eq)]
53
pub struct BlocksRequestFields {
54
    pub header: bool,
55
    pub body: bool,
56
    pub justifications: bool,
57
}
58
59
/// Which block the remote must return first.
60
#[derive(Debug, Clone, PartialEq, Eq)]
61
pub enum BlocksRequestConfigStart {
62
    /// Hash of the block.
63
    Hash([u8; 32]),
64
    /// Number of the block, where 0 would be the genesis block.
65
    Number(u64),
66
}
67
68
// See https://github.com/paritytech/substrate/blob/c8653447fc8ef8d95a92fe164c96dffb37919e85/client/network/sync/src/schema/api.v1.proto
69
// for protocol definition.
70
71
/// Builds the bytes corresponding to a block request.
72
0
pub fn build_block_request(
73
0
    block_number_bytes: usize,
74
0
    config: &BlocksRequestConfig,
75
0
) -> impl Iterator<Item = impl AsRef<[u8]>> {
76
0
    let mut fields = 0u32;
77
0
    if config.fields.header {
78
0
        fields |= 1 << 24;
79
0
    }
80
0
    if config.fields.body {
81
0
        fields |= 1 << 25;
82
0
    }
83
0
    if config.fields.justifications {
84
0
        fields |= 1 << 28;
85
0
    }
86
87
0
    let from_block = match config.start {
88
0
        BlocksRequestConfigStart::Hash(h) => {
89
0
            either::Left(protobuf::bytes_tag_encode(2, h).map(either::Left))
90
        }
91
0
        BlocksRequestConfigStart::Number(n) => {
92
0
            let mut data = Vec::with_capacity(block_number_bytes);
93
0
            data.extend_from_slice(&n.to_le_bytes());
94
0
            // TODO: unclear what to do if the block number doesn't fit within `expected_block_number_bytes`
95
0
            data.resize(block_number_bytes, 0);
96
0
            either::Right(protobuf::bytes_tag_encode(3, data).map(either::Right))
97
        }
98
    };
99
100
0
    protobuf::uint32_tag_encode(1, fields)
101
0
        .map(either::Left)
102
0
        .map(either::Left)
103
0
        .map(either::Left)
104
0
        .chain(
105
0
            from_block
106
0
                .map(either::Right)
107
0
                .map(either::Left)
108
0
                .map(either::Left),
109
0
        )
110
0
        .chain(
111
0
            protobuf::enum_tag_encode(
112
0
                5,
113
0
                match config.direction {
114
0
                    BlocksRequestDirection::Ascending => 0,
115
0
                    BlocksRequestDirection::Descending => 1,
116
                },
117
            )
118
0
            .map(either::Left)
119
0
            .map(either::Right)
120
0
            .map(either::Left),
121
0
        )
122
0
        .chain(
123
0
            protobuf::uint32_tag_encode(6, config.desired_count.get())
124
0
                .map(either::Right)
125
0
                .map(either::Right)
126
0
                .map(either::Left),
127
0
        )
128
0
        // The `support_multiple_justifications` flag indicates that we support responses
129
0
        // containing multiple justifications. This flag is simply a way to maintain backwards
130
0
        // compatibility in the protocol.
131
0
        .chain(protobuf::bool_tag_encode(7, true).map(either::Right))
132
0
}
Unexecuted instantiation: _RNvNtNtNtCsN16ciHI6Qf_7smoldot7network5codec13block_request19build_block_request
Unexecuted instantiation: _RNvNtNtNtCseuYC0Zibziv_7smoldot7network5codec13block_request19build_block_request
133
134
/// Decodes a blocks request.
135
// TODO: should have a more zero-cost API, but we're limited by the protobuf library for that
136
2
pub fn decode_block_request(
137
2
    expected_block_number_bytes: usize,
138
2
    request_bytes: &[u8],
139
2
) -> Result<BlocksRequestConfig, DecodeBlockRequestError> {
140
2
    let mut parser = nom::combinator::all_consuming::<_, _, nom::error::Error<&[u8]>, _>(
141
2
        nom::combinator::complete(protobuf::message_decode! {
142
0
            #[required] fields = 1 => protobuf::uint32_tag_decode,
143
0
            #[optional] hash = 2 => protobuf::bytes_tag_decode,
144
0
            #[optional] number = 3 => protobuf::bytes_tag_decode,
145
0
            #[optional] direction = 5 => protobuf::enum_tag_decode,
146
0
            #[optional] max_blocks = 6 => protobuf::uint32_tag_decode,
147
2
        }),
Unexecuted instantiation: _RNCNCNvNtNtNtCsN16ciHI6Qf_7smoldot7network5codec13block_request20decode_block_requests_00Bb_
Unexecuted instantiation: _RNCNCNvNtNtNtCseuYC0Zibziv_7smoldot7network5codec13block_request20decode_block_requests_00Bb_
148
2
    );
149
150
2
    let 
decoded1
= match nom::Finish::finish(parser(request_bytes)) {
151
1
        Ok((_, rq)) => rq,
152
1
        Err(_) => return Err(DecodeBlockRequestError::ProtobufDecode),
153
    };
154
155
    Ok(BlocksRequestConfig {
156
1
        start: match (decoded.hash, decoded.number) {
157
0
            (Some(h), None) => BlocksRequestConfigStart::Hash(
158
0
                <[u8; 32]>::try_from(h)
159
0
                    .map_err(|_| DecodeBlockRequestError::InvalidBlockHashLength)?,
Unexecuted instantiation: _RNCNvNtNtNtCsN16ciHI6Qf_7smoldot7network5codec13block_request20decode_block_request0B9_
Unexecuted instantiation: _RNCNvNtNtNtCseuYC0Zibziv_7smoldot7network5codec13block_request20decode_block_request0B9_
160
            ),
161
1
            (None, Some(n)) => {
162
1
                if n.len() != expected_block_number_bytes {
163
0
                    return Err(DecodeBlockRequestError::InvalidBlockNumber);
164
1
                }
165
1
166
1
                // The exact format is the SCALE encoding of a block number.
167
1
                // The block number can have a varying number of bytes, and it is therefore
168
1
                // not really possible to know how many bytes to expect here.
169
1
                // Because the SCALE encoding of a number is the number in little endian format,
170
1
                // we decode the bytes in little endian format in a way that works no matter the
171
1
                // number of bytes.
172
1
                let mut num = 0u64;
173
1
                let mut shift = 0u32;
174
5
                for 
byte4
in n {
175
4
                    let shifted = u64::from(*byte)
176
4
                        .checked_mul(1 << shift)
177
4
                        .ok_or(DecodeBlockRequestError::InvalidBlockNumber)
?0
;
178
4
                    num = num
179
4
                        .checked_add(shifted)
180
4
                        .ok_or(DecodeBlockRequestError::InvalidBlockNumber)
?0
;
181
4
                    shift = shift
182
4
                        .checked_add(8)
183
4
                        .ok_or(DecodeBlockRequestError::InvalidBlockNumber)
?0
;
184
                }
185
186
1
                BlocksRequestConfigStart::Number(num)
187
            }
188
0
            (Some(_), Some(_)) => return Err(DecodeBlockRequestError::ProtobufDecode),
189
0
            (None, None) => return Err(DecodeBlockRequestError::MissingStartBlock),
190
        },
191
        desired_count: {
192
            // A missing field or a `0` field are both interpreted as "no limit".
193
1
            NonZeroU32::new(decoded.max_blocks.unwrap_or(u32::MAX))
194
1
                .unwrap_or(NonZeroU32::new(u32::MAX).unwrap())
195
1
        },
196
1
        direction: match decoded.direction {
197
1
            None | Some(0) => BlocksRequestDirection::Ascending,
198
0
            Some(1) => BlocksRequestDirection::Descending,
199
0
            Some(_) => return Err(DecodeBlockRequestError::InvalidDirection),
200
        },
201
        fields: {
202
1
            if (decoded.fields & !(1 << 24 | 1 << 25 | 1 << 28)) != 0 {
203
0
                return Err(DecodeBlockRequestError::UnknownFieldBits);
204
1
            }
205
1
            BlocksRequestFields {
206
1
                header: (decoded.fields & (1 << 24)) != 0,
207
1
                body: (decoded.fields & (1 << 25)) != 0,
208
1
                justifications: (decoded.fields & (1 << 28)) != 0,
209
1
            }
210
        },
211
    })
212
2
}
_RNvNtNtNtCsN16ciHI6Qf_7smoldot7network5codec13block_request20decode_block_request
Line
Count
Source
136
2
pub fn decode_block_request(
137
2
    expected_block_number_bytes: usize,
138
2
    request_bytes: &[u8],
139
2
) -> Result<BlocksRequestConfig, DecodeBlockRequestError> {
140
2
    let mut parser = nom::combinator::all_consuming::<_, _, nom::error::Error<&[u8]>, _>(
141
2
        nom::combinator::complete(protobuf::message_decode! {
142
            #[required] fields = 1 => protobuf::uint32_tag_decode,
143
            #[optional] hash = 2 => protobuf::bytes_tag_decode,
144
            #[optional] number = 3 => protobuf::bytes_tag_decode,
145
            #[optional] direction = 5 => protobuf::enum_tag_decode,
146
            #[optional] max_blocks = 6 => protobuf::uint32_tag_decode,
147
2
        }),
148
2
    );
149
150
2
    let 
decoded1
= match nom::Finish::finish(parser(request_bytes)) {
151
1
        Ok((_, rq)) => rq,
152
1
        Err(_) => return Err(DecodeBlockRequestError::ProtobufDecode),
153
    };
154
155
    Ok(BlocksRequestConfig {
156
1
        start: match (decoded.hash, decoded.number) {
157
0
            (Some(h), None) => BlocksRequestConfigStart::Hash(
158
0
                <[u8; 32]>::try_from(h)
159
0
                    .map_err(|_| DecodeBlockRequestError::InvalidBlockHashLength)?,
160
            ),
161
1
            (None, Some(n)) => {
162
1
                if n.len() != expected_block_number_bytes {
163
0
                    return Err(DecodeBlockRequestError::InvalidBlockNumber);
164
1
                }
165
1
166
1
                // The exact format is the SCALE encoding of a block number.
167
1
                // The block number can have a varying number of bytes, and it is therefore
168
1
                // not really possible to know how many bytes to expect here.
169
1
                // Because the SCALE encoding of a number is the number in little endian format,
170
1
                // we decode the bytes in little endian format in a way that works no matter the
171
1
                // number of bytes.
172
1
                let mut num = 0u64;
173
1
                let mut shift = 0u32;
174
5
                for 
byte4
in n {
175
4
                    let shifted = u64::from(*byte)
176
4
                        .checked_mul(1 << shift)
177
4
                        .ok_or(DecodeBlockRequestError::InvalidBlockNumber)
?0
;
178
4
                    num = num
179
4
                        .checked_add(shifted)
180
4
                        .ok_or(DecodeBlockRequestError::InvalidBlockNumber)
?0
;
181
4
                    shift = shift
182
4
                        .checked_add(8)
183
4
                        .ok_or(DecodeBlockRequestError::InvalidBlockNumber)
?0
;
184
                }
185
186
1
                BlocksRequestConfigStart::Number(num)
187
            }
188
0
            (Some(_), Some(_)) => return Err(DecodeBlockRequestError::ProtobufDecode),
189
0
            (None, None) => return Err(DecodeBlockRequestError::MissingStartBlock),
190
        },
191
        desired_count: {
192
            // A missing field or a `0` field are both interpreted as "no limit".
193
1
            NonZeroU32::new(decoded.max_blocks.unwrap_or(u32::MAX))
194
1
                .unwrap_or(NonZeroU32::new(u32::MAX).unwrap())
195
1
        },
196
1
        direction: match decoded.direction {
197
1
            None | Some(0) => BlocksRequestDirection::Ascending,
198
0
            Some(1) => BlocksRequestDirection::Descending,
199
0
            Some(_) => return Err(DecodeBlockRequestError::InvalidDirection),
200
        },
201
        fields: {
202
1
            if (decoded.fields & !(1 << 24 | 1 << 25 | 1 << 28)) != 0 {
203
0
                return Err(DecodeBlockRequestError::UnknownFieldBits);
204
1
            }
205
1
            BlocksRequestFields {
206
1
                header: (decoded.fields & (1 << 24)) != 0,
207
1
                body: (decoded.fields & (1 << 25)) != 0,
208
1
                justifications: (decoded.fields & (1 << 28)) != 0,
209
1
            }
210
        },
211
    })
212
2
}
Unexecuted instantiation: _RNvNtNtNtCseuYC0Zibziv_7smoldot7network5codec13block_request20decode_block_request
213
214
/// Builds the bytes corresponding to a block response.
215
// TODO: more zero-cost API
216
0
pub fn build_block_response(response: Vec<BlockData>) -> impl Iterator<Item = impl AsRef<[u8]>> {
217
0
    // Note that this function assumes that `support_multiple_justifications` was true in the
218
0
    // request. We intentionally don't support old versions where it was false.
219
0
220
0
    response.into_iter().flat_map(|block| {
221
0
        protobuf::message_tag_encode(1, {
222
0
            let justifications = if let Some(justifications) = block.justifications {
223
0
                let mut j = Vec::with_capacity(
224
0
                    4 + justifications
225
0
                        .iter()
226
0
                        .fold(0, |sz, j| sz + 4 + 6 + j.justification.len()),
Unexecuted instantiation: _RNCNCNvNtNtNtCsN16ciHI6Qf_7smoldot7network5codec13block_request20build_block_response00Bb_
Unexecuted instantiation: _RNCNCNvNtNtNtCseuYC0Zibziv_7smoldot7network5codec13block_request20build_block_response00Bb_
Unexecuted instantiation: _RNCNCNvNtNtNtCseuYC0Zibziv_7smoldot7network5codec13block_request20build_block_response00CsiUjFBJteJ7x_17smoldot_full_node
227
0
                );
228
0
                j.extend_from_slice(
229
0
                    crate::util::encode_scale_compact_usize(justifications.len()).as_ref(),
230
0
                );
231
0
                for justification in &justifications {
232
0
                    j.extend_from_slice(&justification.engine_id);
233
0
                    j.extend_from_slice(
234
0
                        crate::util::encode_scale_compact_usize(justification.justification.len())
235
0
                            .as_ref(),
236
0
                    );
237
0
                    j.extend_from_slice(&justification.justification);
238
0
                }
239
0
                Some(j)
240
            } else {
241
0
                None
242
            };
243
244
0
            protobuf::bytes_tag_encode(1, block.hash)
245
0
                .map(either::Left)
246
0
                .chain(
247
0
                    block
248
0
                        .header
249
0
                        .into_iter()
250
0
                        .flat_map(|h| protobuf::bytes_tag_encode(2, h))
Unexecuted instantiation: _RNCNCNvNtNtNtCsN16ciHI6Qf_7smoldot7network5codec13block_request20build_block_response0s_0Bb_
Unexecuted instantiation: _RNCNCNvNtNtNtCseuYC0Zibziv_7smoldot7network5codec13block_request20build_block_response0s_0Bb_
Unexecuted instantiation: _RNCNCNvNtNtNtCseuYC0Zibziv_7smoldot7network5codec13block_request20build_block_response0s_0CsiUjFBJteJ7x_17smoldot_full_node
251
0
                        .map(either::Right),
252
0
                )
253
0
                .map(either::Left)
254
0
                .chain(
255
0
                    block
256
0
                        .body
257
0
                        .into_iter()
258
0
                        .flat_map(|b| b.into_iter())
Unexecuted instantiation: _RNCNCNvNtNtNtCsN16ciHI6Qf_7smoldot7network5codec13block_request20build_block_response0s0_0Bb_
Unexecuted instantiation: _RNCNCNvNtNtNtCseuYC0Zibziv_7smoldot7network5codec13block_request20build_block_response0s0_0Bb_
Unexecuted instantiation: _RNCNCNvNtNtNtCseuYC0Zibziv_7smoldot7network5codec13block_request20build_block_response0s0_0CsiUjFBJteJ7x_17smoldot_full_node
259
0
                        .flat_map(|tx| protobuf::bytes_tag_encode(3, tx))
Unexecuted instantiation: _RNCNCNvNtNtNtCsN16ciHI6Qf_7smoldot7network5codec13block_request20build_block_response0s1_0Bb_
Unexecuted instantiation: _RNCNCNvNtNtNtCseuYC0Zibziv_7smoldot7network5codec13block_request20build_block_response0s1_0Bb_
Unexecuted instantiation: _RNCNCNvNtNtNtCseuYC0Zibziv_7smoldot7network5codec13block_request20build_block_response0s1_0CsiUjFBJteJ7x_17smoldot_full_node
260
0
                        .map(either::Left)
261
0
                        .chain(
262
0
                            justifications
263
0
                                .into_iter()
264
0
                                .flat_map(|j| protobuf::bytes_tag_encode(8, j))
Unexecuted instantiation: _RNCNCNvNtNtNtCsN16ciHI6Qf_7smoldot7network5codec13block_request20build_block_response0s2_0Bb_
Unexecuted instantiation: _RNCNCNvNtNtNtCseuYC0Zibziv_7smoldot7network5codec13block_request20build_block_response0s2_0Bb_
Unexecuted instantiation: _RNCNCNvNtNtNtCseuYC0Zibziv_7smoldot7network5codec13block_request20build_block_response0s2_0CsiUjFBJteJ7x_17smoldot_full_node
265
0
                                .map(either::Right),
266
0
                        )
267
0
                        .map(either::Right),
268
0
                )
269
0
        })
270
0
    })
Unexecuted instantiation: _RNCNvNtNtNtCsN16ciHI6Qf_7smoldot7network5codec13block_request20build_block_response0B9_
Unexecuted instantiation: _RNCNvNtNtNtCseuYC0Zibziv_7smoldot7network5codec13block_request20build_block_response0B9_
Unexecuted instantiation: _RNCNvNtNtNtCseuYC0Zibziv_7smoldot7network5codec13block_request20build_block_response0CsiUjFBJteJ7x_17smoldot_full_node
271
0
}
Unexecuted instantiation: _RNvNtNtNtCsN16ciHI6Qf_7smoldot7network5codec13block_request20build_block_response
Unexecuted instantiation: _RNvNtNtNtCseuYC0Zibziv_7smoldot7network5codec13block_request20build_block_response
272
273
/// Decodes a response to a block request.
274
// TODO: should have a more zero-cost API
275
1
pub fn decode_block_response(
276
1
    response_bytes: &[u8],
277
1
) -> Result<Vec<BlockData>, DecodeBlockResponseError> {
278
1
    let mut parser = nom::combinator::all_consuming::<_, _, nom::error::Error<&[u8]>, _>(
279
1
        nom::combinator::complete(protobuf::message_decode! {
280
1
            #[repeated(max = 32768)] blocks = 1 => protobuf::message_tag_decode(protobuf::message_decode!{
281
0
                #[required] hash = 1 => protobuf::bytes_tag_decode,
282
0
                #[optional] header = 2 => protobuf::bytes_tag_decode,
283
0
                #[repeated(max = usize::MAX)] body = 3 => protobuf::bytes_tag_decode,
284
0
                #[optional] justifications = 8 => protobuf::bytes_tag_decode,
285
1
            }),
_RNCNvNtNtNtCsN16ciHI6Qf_7smoldot7network5codec13block_request21decode_block_responses0_0B9_
Line
Count
Source
279
1
        nom::combinator::complete(protobuf::message_decode! {
280
1
            #[repeated(max = 32768)] blocks = 1 => protobuf::message_tag_decode(protobuf::message_decode!{
281
                #[required] hash = 1 => protobuf::bytes_tag_decode,
282
                #[optional] header = 2 => protobuf::bytes_tag_decode,
283
                #[repeated(max = usize::MAX)] body = 3 => protobuf::bytes_tag_decode,
284
                #[optional] justifications = 8 => protobuf::bytes_tag_decode,
285
1
            }),
Unexecuted instantiation: _RNCNvNtNtNtCseuYC0Zibziv_7smoldot7network5codec13block_request21decode_block_responses0_0B9_
Unexecuted instantiation: _RNCNCNCNvNtNtNtCsN16ciHI6Qf_7smoldot7network5codec13block_request21decode_block_responses0_000Bd_
Unexecuted instantiation: _RNCNCNCNvNtNtNtCseuYC0Zibziv_7smoldot7network5codec13block_request21decode_block_responses0_000Bd_
286
1
        }),
287
1
    );
288
289
1
    let 
blocks0
= match nom::Finish::finish(parser(response_bytes)) {
290
0
        Ok((_, out)) => out.blocks,
291
1
        Err(_) => return Err(DecodeBlockResponseError::ProtobufDecode),
292
    };
293
294
0
    let mut blocks_out = Vec::with_capacity(blocks.len());
295
0
    for block in blocks {
296
0
        if block.hash.len() != 32 {
297
0
            return Err(DecodeBlockResponseError::InvalidHashLength);
298
0
        }
299
0
300
0
        blocks_out.push(BlockData {
301
0
            hash: <[u8; 32]>::try_from(block.hash).unwrap(),
302
0
            header: block.header.as_ref().map(|h| h.to_vec()),
Unexecuted instantiation: _RNCNvNtNtNtCsN16ciHI6Qf_7smoldot7network5codec13block_request21decode_block_response0B9_
Unexecuted instantiation: _RNCNvNtNtNtCseuYC0Zibziv_7smoldot7network5codec13block_request21decode_block_response0B9_
303
0
            // TODO: no; we might not have asked for the body
304
0
            body: Some(block.body.into_iter().map(|tx| tx.to_vec()).collect()),
Unexecuted instantiation: _RNCNvNtNtNtCsN16ciHI6Qf_7smoldot7network5codec13block_request21decode_block_responses_0B9_
Unexecuted instantiation: _RNCNvNtNtNtCseuYC0Zibziv_7smoldot7network5codec13block_request21decode_block_responses_0B9_
305
0
            justifications: if let Some(justifications) = block.justifications {
306
0
                let result: nom::IResult<_, _> = nom::combinator::all_consuming(
307
0
                    nom::combinator::complete(decode_justifications),
308
0
                )(justifications);
309
0
                match result {
310
0
                    Ok((_, out)) => Some(out),
311
                    Err(nom::Err::Error(_) | nom::Err::Failure(_)) => {
312
0
                        return Err(DecodeBlockResponseError::InvalidJustifications)
313
                    }
314
0
                    Err(_) => unreachable!(),
315
                }
316
            } else {
317
0
                None
318
            },
319
        });
320
    }
321
322
0
    Ok(blocks_out)
323
1
}
_RNvNtNtNtCsN16ciHI6Qf_7smoldot7network5codec13block_request21decode_block_response
Line
Count
Source
275
1
pub fn decode_block_response(
276
1
    response_bytes: &[u8],
277
1
) -> Result<Vec<BlockData>, DecodeBlockResponseError> {
278
1
    let mut parser = nom::combinator::all_consuming::<_, _, nom::error::Error<&[u8]>, _>(
279
1
        nom::combinator::complete(protobuf::message_decode! {
280
            #[repeated(max = 32768)] blocks = 1 => protobuf::message_tag_decode(protobuf::message_decode!{
281
                #[required] hash = 1 => protobuf::bytes_tag_decode,
282
                #[optional] header = 2 => protobuf::bytes_tag_decode,
283
                #[repeated(max = usize::MAX)] body = 3 => protobuf::bytes_tag_decode,
284
                #[optional] justifications = 8 => protobuf::bytes_tag_decode,
285
            }),
286
1
        }),
287
1
    );
288
289
1
    let 
blocks0
= match nom::Finish::finish(parser(response_bytes)) {
290
0
        Ok((_, out)) => out.blocks,
291
1
        Err(_) => return Err(DecodeBlockResponseError::ProtobufDecode),
292
    };
293
294
0
    let mut blocks_out = Vec::with_capacity(blocks.len());
295
0
    for block in blocks {
296
0
        if block.hash.len() != 32 {
297
0
            return Err(DecodeBlockResponseError::InvalidHashLength);
298
0
        }
299
0
300
0
        blocks_out.push(BlockData {
301
0
            hash: <[u8; 32]>::try_from(block.hash).unwrap(),
302
0
            header: block.header.as_ref().map(|h| h.to_vec()),
303
0
            // TODO: no; we might not have asked for the body
304
0
            body: Some(block.body.into_iter().map(|tx| tx.to_vec()).collect()),
305
0
            justifications: if let Some(justifications) = block.justifications {
306
0
                let result: nom::IResult<_, _> = nom::combinator::all_consuming(
307
0
                    nom::combinator::complete(decode_justifications),
308
0
                )(justifications);
309
0
                match result {
310
0
                    Ok((_, out)) => Some(out),
311
                    Err(nom::Err::Error(_) | nom::Err::Failure(_)) => {
312
0
                        return Err(DecodeBlockResponseError::InvalidJustifications)
313
                    }
314
0
                    Err(_) => unreachable!(),
315
                }
316
            } else {
317
0
                None
318
            },
319
        });
320
    }
321
322
0
    Ok(blocks_out)
323
1
}
Unexecuted instantiation: _RNvNtNtNtCseuYC0Zibziv_7smoldot7network5codec13block_request21decode_block_response
324
325
/// Block sent in a block response.
326
///
327
/// > **Note**: Assuming that this response comes from the network, the information in this struct
328
/// >           can be erroneous and shouldn't be trusted.
329
#[derive(Debug, Clone, PartialEq, Eq)]
330
pub struct BlockData {
331
    /// Block hash.
332
    ///
333
    /// > **Note**: This should contain the hash of the header, but, like the rest of this
334
    /// >           structure, this cannot be trusted.
335
    pub hash: [u8; 32],
336
337
    /// SCALE-encoded block header, if requested.
338
    pub header: Option<Vec<u8>>,
339
340
    /// Block body, if requested. Each item (each `Vec<u8>`) is a SCALE-encoded extrinsic.
341
    /// These extrinsics aren't decodable, as their meaning depends on the chain.
342
    ///
343
    /// > **Note**: Be aware that in many chains an extrinsic is actually a `Vec<u8>`, which
344
    /// >           means that you will find, at the beginning of each SCALE-encoded extrinsic,
345
    /// >           a length prefix. Don't get fooled into thinking that this length prefix must
346
    /// >           be removed. It is part of the opaque format extrinsic format.
347
    pub body: Option<Vec<Vec<u8>>>,
348
349
    /// List of justifications, if requested and available.
350
    ///
351
    /// Each justification is a tuple of a "consensus engine id" and a SCALE-encoded
352
    /// justifications.
353
    ///
354
    /// Will be `None` if and only if not requested.
355
    pub justifications: Option<Vec<Justification>>,
356
}
357
358
/// See [`BlockData::justifications`].
359
#[derive(Debug, Clone, PartialEq, Eq)]
360
pub struct Justification {
361
    /// Short identifier of the consensus engine associated with that justification.
362
    pub engine_id: [u8; 4],
363
    /// Body of the justification.
364
    pub justification: Vec<u8>,
365
}
366
367
/// Error potentially returned by [`decode_block_request`].
368
0
#[derive(Debug, derive_more::Display)]
Unexecuted instantiation: _RNvXst_NtNtNtCsN16ciHI6Qf_7smoldot7network5codec13block_requestNtB5_23DecodeBlockRequestErrorNtNtCsaYZPK01V26L_4core3fmt7Display3fmt
Unexecuted instantiation: _RNvXst_NtNtNtCseuYC0Zibziv_7smoldot7network5codec13block_requestNtB5_23DecodeBlockRequestErrorNtNtCsaYZPK01V26L_4core3fmt7Display3fmt
369
pub enum DecodeBlockRequestError {
370
    /// Error while decoding the Protobuf encoding.
371
    ProtobufDecode,
372
    /// Zero blocks requested.
373
    ZeroBlocksRequested,
374
    /// Value in the direction field is invalid.
375
    InvalidDirection,
376
    /// Start block field is missing.
377
    MissingStartBlock,
378
    /// Invalid block number passed.
379
    InvalidBlockNumber,
380
    /// Block hash length isn't correct.
381
    InvalidBlockHashLength,
382
    /// Requested fields contains bits that are unknown.
383
    UnknownFieldBits,
384
}
385
386
/// Error potentially returned by [`decode_block_response`].
387
0
#[derive(Debug, derive_more::Display)]
Unexecuted instantiation: _RNvXsv_NtNtNtCsN16ciHI6Qf_7smoldot7network5codec13block_requestNtB5_24DecodeBlockResponseErrorNtNtCsaYZPK01V26L_4core3fmt7Display3fmt
Unexecuted instantiation: _RNvXsv_NtNtNtCseuYC0Zibziv_7smoldot7network5codec13block_requestNtB5_24DecodeBlockResponseErrorNtNtCsaYZPK01V26L_4core3fmt7Display3fmt
388
pub enum DecodeBlockResponseError {
389
    /// Error while decoding the Protobuf encoding.
390
    ProtobufDecode,
391
    /// Hash length isn't of the correct length.
392
    InvalidHashLength,
393
    BodyDecodeError,
394
    /// List of justifications isn't in a correct format.
395
    InvalidJustifications,
396
}
397
398
0
fn decode_justifications<'a, E: nom::error::ParseError<&'a [u8]>>(
399
0
    bytes: &'a [u8],
400
0
) -> nom::IResult<&'a [u8], Vec<Justification>, E> {
401
0
    nom::combinator::flat_map(crate::util::nom_scale_compact_usize, |num_elems| {
402
0
        nom::multi::many_m_n(
403
0
            num_elems,
404
0
            num_elems,
405
0
            nom::combinator::map(
406
0
                nom::sequence::tuple((
407
0
                    nom::bytes::streaming::take(4u32),
408
0
                    crate::util::nom_bytes_decode,
409
0
                )),
410
0
                move |(consensus_engine, justification)| Justification {
411
0
                    engine_id: <[u8; 4]>::try_from(consensus_engine).unwrap(),
412
0
                    justification: justification.to_owned(),
413
0
                },
Unexecuted instantiation: _RNCNCINvNtNtNtCsN16ciHI6Qf_7smoldot7network5codec13block_request21decode_justificationsINtNtCs6ga8gEqbpRc_3nom5error5ErrorRShEE00Bc_
Unexecuted instantiation: _RNCNCINvNtNtNtCseuYC0Zibziv_7smoldot7network5codec13block_request21decode_justificationsINtNtCs6ga8gEqbpRc_3nom5error5ErrorRShEE00Bc_
414
0
            ),
415
0
        )
416
0
    })(bytes)
Unexecuted instantiation: _RNCINvNtNtNtCsN16ciHI6Qf_7smoldot7network5codec13block_request21decode_justificationsINtNtCs6ga8gEqbpRc_3nom5error5ErrorRShEE0Ba_
Unexecuted instantiation: _RNCINvNtNtNtCseuYC0Zibziv_7smoldot7network5codec13block_request21decode_justificationsINtNtCs6ga8gEqbpRc_3nom5error5ErrorRShEE0Ba_
417
0
}
Unexecuted instantiation: _RINvNtNtNtCsN16ciHI6Qf_7smoldot7network5codec13block_request21decode_justificationsINtNtCs6ga8gEqbpRc_3nom5error5ErrorRShEEB8_
Unexecuted instantiation: _RINvNtNtNtCseuYC0Zibziv_7smoldot7network5codec13block_request21decode_justificationsINtNtCs6ga8gEqbpRc_3nom5error5ErrorRShEEB8_
418
419
#[cfg(test)]
420
mod tests {
421
    #[test]
422
1
    fn regression_2339() {
423
1
        // Regression test for https://github.com/paritytech/smoldot/issues/2339.
424
1
        let _ = super::decode_block_request(4, &[26, 10]);
425
1
    }
426
427
    #[test]
428
1
    fn regression_incomplete_justification() {
429
1
        let _ = super::decode_block_response(&[
430
1
            200, 200, 255, 255, 10, 8, 0, 47, 0, 1, 26, 0, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88,
431
1
            88, 88, 88, 88, 88, 88, 1, 10, 1, 255, 2, 0, 0, 1, 255, 2, 10, 0, 36, 1, 8, 105, 105,
432
1
            105, 105, 105, 105, 97, 105, 105, 88, 1, 0, 0, 88, 88, 88, 88, 88, 88, 10, 175, 10, 0,
433
1
            105, 1, 10, 1, 255, 2, 0, 0, 10, 4, 66, 0, 66, 38, 88, 88, 18, 0, 88, 26, 0, 8, 5, 0,
434
1
            0, 0, 0, 0, 0, 0, 105, 1, 8, 105, 105, 105, 105, 105, 105, 88, 88, 88, 88, 88, 0, 0,
435
1
            88, 88, 0, 0, 0, 18, 0, 26, 1, 88, 88, 88, 88, 36, 10, 255, 0, 2, 10, 0, 36, 1, 8, 105,
436
1
            105, 105, 105, 105, 105, 97, 105, 105, 105, 88, 88, 88, 88, 88, 88, 88, 88, 88, 10, 48,
437
1
            10, 0, 105, 1, 10, 2, 0, 12, 0, 0, 0, 0, 0, 0, 0, 18, 0, 26, 1, 88, 88, 88, 88, 36, 10,
438
1
            1, 255, 2, 10, 0, 105, 1, 8, 105, 105, 105, 105, 105, 105, 97, 105, 105, 105, 88, 88,
439
1
            88, 0, 88, 88, 36, 10, 1, 255, 2, 10, 0, 36, 1, 8, 0, 1, 26, 0, 88, 88, 88, 88, 88, 88,
440
1
            10, 48, 10, 0, 105, 1, 10, 1, 255, 2, 0, 0, 0, 18, 0, 26, 1, 88, 88, 88, 88, 36, 244,
441
1
            1, 88, 88, 88, 88, 10, 48, 10, 0, 105, 1, 10, 1, 255, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18,
442
1
            0, 26, 1, 88, 88, 0, 0, 0, 0, 0, 0, 26, 0, 26, 1, 88, 88, 88, 88, 36, 10, 1, 255, 2,
443
1
            10, 0, 105, 1, 8, 105, 105, 105, 105, 105, 105, 97, 105, 105, 105, 18, 0, 0, 0, 0, 0,
444
1
            0, 0, 88, 0, 18, 0, 26, 1, 88, 88, 0, 0, 0, 0, 0, 0, 18, 0, 26, 1, 88, 88, 88, 88, 36,
445
1
            10, 1, 255, 2, 10, 0, 105, 1, 86, 0, 0, 0, 0, 0, 0, 0, 8, 105, 105, 105, 105, 105, 105,
446
1
            97, 105, 88, 88, 88, 88, 88, 10, 48, 10, 0, 105, 1, 10, 1, 255, 2, 128, 0, 0, 0, 32, 0,
447
1
            0, 0, 0, 18, 0, 26, 1, 88, 88, 88, 88, 36, 10, 1, 88, 88, 36, 10, 1, 255, 2, 10, 0,
448
1
            105, 1, 8, 105, 105, 105, 105, 105, 105, 97, 105, 105, 105, 88, 88, 88, 88, 88, 88, 88,
449
1
            88, 88, 10, 48, 10, 0, 105, 1, 10, 1, 255, 2, 0, 0, 0, 0, 32, 0, 0, 0, 0, 18, 0, 26, 1,
450
1
            88, 88, 88, 88, 36, 10, 1, 255, 2, 0, 0, 0, 0, 0, 0, 0, 8, 0, 47, 0, 1, 0, 0, 88, 88,
451
1
            88, 88, 88, 88, 10, 48, 10, 0, 105, 1, 10, 1, 255, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 0,
452
1
            26, 1, 88, 88, 88, 88, 36, 10, 1, 255, 2, 10, 0, 105, 1, 8, 105, 105, 105, 105, 105,
453
1
            105, 97, 105, 105, 105, 88, 88, 88, 88, 88, 88, 88, 88, 88, 10, 32, 10, 0, 105, 139,
454
1
            10, 1, 255, 2, 0, 0, 0, 0, 0, 0, 18, 0, 26, 1, 88, 88, 0, 0, 0, 0, 0, 0, 18, 0, 26, 1,
455
1
            0, 1, 26, 0, 88, 88, 88, 88, 88, 88, 10, 48, 10, 0, 105, 1, 10, 1, 255, 2, 0, 0, 0, 18,
456
1
            0, 26, 1, 88, 88, 88, 88, 36, 10, 1, 88, 88, 88, 88, 10, 48, 10, 0, 105, 1, 10, 1, 255,
457
1
            2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 18, 0, 26, 1, 88, 88, 88, 88, 36, 10,
458
1
            1, 88, 88, 36, 10, 1, 255, 2, 10, 0, 105, 1, 8, 105, 105, 105, 105, 105, 105, 97, 105,
459
1
            105, 105, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 0, 18, 0, 26, 1, 88, 88, 0, 0, 0, 0,
460
1
            0, 0, 18, 0, 26, 1, 88, 88, 88, 88, 36, 10, 1, 255, 2, 10, 0, 105, 1, 86, 0, 0, 0, 0,
461
1
            0, 0, 0, 8, 105, 105, 105, 105, 105, 105, 97, 105, 88, 88, 88, 88, 88, 10, 48, 10, 0,
462
1
            105, 1, 10, 1, 255, 2, 0, 0, 0, 0, 32, 0, 0, 0, 0, 18, 0, 26, 1, 88, 88, 88, 88, 36,
463
1
            10, 1, 88, 88, 36, 10, 1, 255, 2, 10, 0, 105, 1, 8, 105, 93, 105, 105, 105, 105, 105,
464
1
            97, 105, 105, 0, 47, 0, 1, 0, 0, 88, 88, 88, 88, 88, 88, 10, 48, 10, 0, 105, 1, 10, 1,
465
1
            255, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 0, 26, 1, 88, 88, 88, 88, 36, 10, 1, 255, 2, 10,
466
1
            0, 105, 1, 8, 105, 105, 105, 105, 97, 105, 88, 88, 88, 88, 88, 10, 48, 10, 0, 105, 1,
467
1
            10, 1, 255, 2, 0, 0, 0, 0, 32, 0, 0, 0, 0, 18, 0, 26, 1, 88, 88, 88, 88, 36, 10, 1, 88,
468
1
            88, 36, 10, 1, 255, 2, 10, 0, 105, 1, 8, 105, 93, 105, 105, 105, 105, 105, 97, 105,
469
1
            105, 0, 47, 0, 1, 0, 0, 88, 88, 88, 88, 88, 88, 10, 48, 10, 0, 105, 1, 10, 1, 255, 2,
470
1
            0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 0, 26, 1, 88, 88, 88, 88, 36, 10, 1, 255, 2, 10, 0, 105,
471
1
            1, 8, 105, 105, 105, 105, 105, 105, 97, 105, 105, 105, 88, 88, 88, 88, 88, 88, 88, 88,
472
1
            88, 10, 32, 10, 0, 105, 139, 10, 0, 0, 0, 0, 0, 18, 0, 26, 1, 88, 88, 88, 88, 36, 10,
473
1
            1, 255, 2, 10, 0, 105, 1, 86, 0, 0, 0, 0, 0, 0, 0, 8, 105, 105, 105, 105, 105, 105, 97,
474
1
            105, 88, 88, 88, 88, 88, 10, 48, 10, 0, 105, 1, 10, 1, 255, 2, 0, 0, 0, 0, 32, 0, 0, 0,
475
1
            0, 18, 0, 26, 1, 88, 88, 88, 88, 36, 10, 1, 88, 88, 36, 10, 1, 255, 2, 10, 0, 105, 1,
476
1
            8, 105, 105, 105, 105, 105, 105, 97, 105, 105, 105, 88, 88, 88, 88, 88, 88, 88, 88, 88,
477
1
            10, 48, 10, 0, 105, 0, 10, 1, 255, 2, 0, 0, 0, 0, 32, 0, 0, 0, 0, 18, 0, 26, 1, 88, 88,
478
1
            88, 88, 36, 10, 1, 255, 2, 0, 0, 0, 0, 0, 0, 0, 8, 0, 47, 0, 1, 0, 0, 88, 88, 88, 88,
479
1
            88, 88, 10, 48, 10, 0, 105, 1, 10, 1, 255, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 0, 26, 1,
480
1
            88, 88, 88, 88, 36, 142, 1, 255, 2, 10, 0, 105, 1, 8, 105, 105, 105, 105, 105, 105, 97,
481
1
            105, 105, 105, 88, 88, 88, 88, 88, 88, 88, 88, 88, 10, 32, 10, 0, 105, 139, 10, 1, 255,
482
1
            2, 0, 0, 0, 0, 0, 0, 18, 0, 26, 1, 88, 88, 0, 0, 0, 0, 0, 0, 18, 0, 26, 1, 88, 88, 88,
483
1
            88, 0, 26, 1, 88, 88, 88, 88, 36, 10, 1, 255, 2, 10, 0, 36, 1, 8, 105, 105, 105, 105,
484
1
            105, 105, 97, 105, 105, 105, 88, 88, 88, 88, 88, 88, 2, 0, 0, 0, 0, 32, 88, 36, 10, 1,
485
1
            255, 255, 255, 251, 2, 10, 0, 105, 1, 86, 0, 0, 0, 0, 0, 0, 0, 8, 105, 105, 105, 105,
486
1
            105, 105, 97, 105, 88, 88, 88, 88, 0, 0, 0, 0, 32, 0, 0, 0, 0, 18, 5, 26, 1, 88, 88,
487
1
            88, 88, 36, 10, 1, 255, 2, 0, 0, 0, 0, 0, 0, 0, 8, 0, 47, 0, 1, 0, 0, 88, 88, 88, 88,
488
1
            88, 88, 10, 48, 10, 0, 105, 1, 10, 1, 255, 2, 0, 0, 0, 0, 0, 0, 128, 0, 0, 18, 0, 26,
489
1
            1, 88, 88, 88, 88, 36, 142, 1, 255, 2, 255, 10, 0, 105, 1, 8, 105, 255, 2, 10, 0, 36,
490
1
            1, 8, 105, 105, 105, 105, 105, 105, 97, 105, 105, 105, 88, 88, 88, 88, 88, 88, 2, 0, 0,
491
1
            0, 0, 1, 255, 2, 105, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88,
492
1
        ]);
493
1
    }
494
495
    #[test]
496
1
    fn regression_2833() {
497
1
        // Regression test for https://github.com/paritytech/smoldot/issues/2833.
498
1
        let decoded = super::decode_block_request(
499
1
            4,
500
1
            &[
501
1
                8, 128, 128, 128, 136, 1, 26, 4, 237, 91, 33, 0, 48, 1, 56, 1,
502
1
            ],
503
1
        )
504
1
        .unwrap();
505
1
        assert!(
matches!0
(
506
1
            decoded.direction,
507
            super::BlocksRequestDirection::Ascending
508
        ));
509
1
    }
510
}