/__w/smoldot/smoldot/repo/lib/src/network/codec/block_announces.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::header; |
19 | | |
20 | | use alloc::vec; |
21 | | use nom::Finish as _; |
22 | | |
23 | | /// Decoded handshake sent or received when opening a block announces notifications substream. |
24 | | #[derive(Debug, Copy, Clone, PartialEq, Eq)] |
25 | | pub struct BlockAnnouncesHandshakeRef<'a> { |
26 | | /// Role a node reports playing on the network. |
27 | | pub role: Role, |
28 | | |
29 | | /// Height of the best block according to this node. |
30 | | pub best_number: u64, |
31 | | |
32 | | /// Hash of the best block according to this node. |
33 | | pub best_hash: &'a [u8; 32], |
34 | | |
35 | | /// Hash of the genesis block according to this node. |
36 | | /// |
37 | | /// > **Note**: This should be compared to the locally known genesis block hash, to make sure |
38 | | /// > that both nodes are on the same chain. |
39 | | pub genesis_hash: &'a [u8; 32], |
40 | | } |
41 | | |
42 | | /// Role a node reports playing on the network. |
43 | | /// |
44 | | /// This role can be seen more or less as a priority level. The role a node reports cannot be |
45 | | /// trusted but is used as a hint. For example, Grandpa votes can be broadcasted with a higher |
46 | | /// priority to the nodes that report themselves as authorities. |
47 | | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] |
48 | | pub enum Role { |
49 | | /// Authorities author blocks and participate in the consensus. |
50 | | /// |
51 | | /// This role is non-binding, and is used only as a hint to prioritize some nodes over others. |
52 | | Authority, |
53 | | |
54 | | /// Full nodes store the state of the chain. They are part of the infrastructure of the chain |
55 | | /// in the sense that light nodes benefit from having a lot of full nodes to connect to. |
56 | | /// |
57 | | /// This role is non-binding, and is used only as a hint to prioritize some nodes over others. |
58 | | Full, |
59 | | |
60 | | /// Light nodes are the lowest priority nodes. |
61 | | Light, |
62 | | } |
63 | | |
64 | | impl Role { |
65 | | /// Returns the SCALE encoding of this enum. Always guaranteed to be one byte. |
66 | 0 | pub fn scale_encoding(&self) -> [u8; 1] { |
67 | 0 | match *self { |
68 | 0 | Role::Full => [0b1], |
69 | 0 | Role::Light => [0b10], |
70 | 0 | Role::Authority => [0b100], |
71 | | } |
72 | 0 | } Unexecuted instantiation: _RNvMNtNtNtCsN16ciHI6Qf_7smoldot7network5codec15block_announcesNtB2_4Role14scale_encoding Unexecuted instantiation: _RNvMNtNtNtCseuYC0Zibziv_7smoldot7network5codec15block_announcesNtB2_4Role14scale_encoding |
73 | | } |
74 | | |
75 | | /// Decoded block announcement notification. |
76 | | #[derive(Debug)] |
77 | | pub struct BlockAnnounceRef<'a> { |
78 | | /// SCALE-encoded header in the announce. |
79 | | /// |
80 | | /// > **Note**: Due to the way block announces are encoded, the header is always guaranteed to |
81 | | /// > decode correctly. However this shouldn't be relied upon. |
82 | | pub scale_encoded_header: &'a [u8], |
83 | | |
84 | | /// True if the block is the new best block of the announcer. |
85 | | pub is_best: bool, |
86 | | // TODO: missing a `Vec<u8>` field that SCALE-decodes into this type: https://github.com/paritytech/polkadot/blob/fff4635925c12c80717a524367687fcc304bcb13/node%2Fprimitives%2Fsrc%2Flib.rs#L87 |
87 | | } |
88 | | |
89 | | /// Turns a block announcement into its SCALE-encoding ready to be sent over the wire. |
90 | | /// |
91 | | /// This function returns an iterator of buffers. The encoded message consists in the |
92 | | /// concatenation of the buffers. |
93 | 0 | pub fn encode_block_announce( |
94 | 0 | announce: BlockAnnounceRef<'_>, |
95 | 0 | ) -> impl Iterator<Item = impl AsRef<[u8]> + '_> + '_ { |
96 | 0 | let is_best = if announce.is_best { [1u8] } else { [0u8] }; |
97 | | |
98 | 0 | [ |
99 | 0 | either::Left(announce.scale_encoded_header), |
100 | 0 | either::Right(is_best), |
101 | 0 | either::Right([0u8]), |
102 | 0 | ] |
103 | 0 | .into_iter() |
104 | 0 | } Unexecuted instantiation: _RNvNtNtNtCsN16ciHI6Qf_7smoldot7network5codec15block_announces21encode_block_announce Unexecuted instantiation: _RNvNtNtNtCseuYC0Zibziv_7smoldot7network5codec15block_announces21encode_block_announce |
105 | | |
106 | | /// Decodes a block announcement. |
107 | 0 | pub fn decode_block_announce( |
108 | 0 | bytes: &[u8], |
109 | 0 | block_number_bytes: usize, |
110 | 0 | ) -> Result<BlockAnnounceRef, DecodeBlockAnnounceError> { |
111 | 0 | let result: Result<_, nom::error::Error<_>> = |
112 | 0 | nom::combinator::all_consuming(nom::combinator::complete(nom::combinator::map( |
113 | 0 | nom::sequence::tuple(( |
114 | 0 | nom::combinator::recognize(|enc_hdr| { |
115 | 0 | match header::decode_partial(enc_hdr, block_number_bytes) { |
116 | 0 | Ok((hdr, rest)) => Ok((rest, hdr)), |
117 | 0 | Err(_) => Err(nom::Err::Failure(nom::error::make_error( |
118 | 0 | enc_hdr, |
119 | 0 | nom::error::ErrorKind::Verify, |
120 | 0 | ))), |
121 | | } |
122 | 0 | }), Unexecuted instantiation: _RNCNvNtNtNtCsN16ciHI6Qf_7smoldot7network5codec15block_announces21decode_block_announce0B9_ Unexecuted instantiation: _RNCNvNtNtNtCseuYC0Zibziv_7smoldot7network5codec15block_announces21decode_block_announce0B9_ |
123 | 0 | nom::branch::alt(( |
124 | 0 | nom::combinator::map(nom::bytes::streaming::tag(&[0]), |_| false), Unexecuted instantiation: _RNCNvNtNtNtCsN16ciHI6Qf_7smoldot7network5codec15block_announces21decode_block_announces_0B9_ Unexecuted instantiation: _RNCNvNtNtNtCseuYC0Zibziv_7smoldot7network5codec15block_announces21decode_block_announces_0B9_ |
125 | 0 | nom::combinator::map(nom::bytes::streaming::tag(&[1]), |_| true), Unexecuted instantiation: _RNCNvNtNtNtCsN16ciHI6Qf_7smoldot7network5codec15block_announces21decode_block_announces0_0B9_ Unexecuted instantiation: _RNCNvNtNtNtCseuYC0Zibziv_7smoldot7network5codec15block_announces21decode_block_announces0_0B9_ |
126 | 0 | )), |
127 | 0 | crate::util::nom_bytes_decode, |
128 | 0 | )), |
129 | 0 | |(scale_encoded_header, is_best, _)| BlockAnnounceRef { |
130 | 0 | scale_encoded_header, |
131 | 0 | is_best, |
132 | 0 | }, Unexecuted instantiation: _RNCNvNtNtNtCsN16ciHI6Qf_7smoldot7network5codec15block_announces21decode_block_announces1_0B9_ Unexecuted instantiation: _RNCNvNtNtNtCseuYC0Zibziv_7smoldot7network5codec15block_announces21decode_block_announces1_0B9_ |
133 | 0 | )))(bytes) |
134 | 0 | .finish(); |
135 | 0 |
|
136 | 0 | match result { |
137 | 0 | Ok((_, ann)) => Ok(ann), |
138 | 0 | Err(err) => Err(DecodeBlockAnnounceError(err.code)), |
139 | | } |
140 | 0 | } Unexecuted instantiation: _RNvNtNtNtCsN16ciHI6Qf_7smoldot7network5codec15block_announces21decode_block_announce Unexecuted instantiation: _RNvNtNtNtCseuYC0Zibziv_7smoldot7network5codec15block_announces21decode_block_announce |
141 | | |
142 | | /// Error potentially returned by [`decode_block_announces_handshake`]. |
143 | 0 | #[derive(Debug, derive_more::Display)] Unexecuted instantiation: _RNvXse_NtNtNtCsN16ciHI6Qf_7smoldot7network5codec15block_announcesNtB5_24DecodeBlockAnnounceErrorNtNtCsaYZPK01V26L_4core3fmt7Display3fmt Unexecuted instantiation: _RNvXse_NtNtNtCseuYC0Zibziv_7smoldot7network5codec15block_announcesNtB5_24DecodeBlockAnnounceErrorNtNtCsaYZPK01V26L_4core3fmt7Display3fmt |
144 | | #[display(fmt = "Failed to decode a block announcement")] |
145 | | pub struct DecodeBlockAnnounceError(nom::error::ErrorKind); |
146 | | |
147 | | /// Turns a block announces handshake into its SCALE-encoding ready to be sent over the wire. |
148 | | /// |
149 | | /// This function returns an iterator of buffers. The encoded message consists in the |
150 | | /// concatenation of the buffers. |
151 | 0 | pub fn encode_block_announces_handshake( |
152 | 0 | handshake: BlockAnnouncesHandshakeRef<'_>, |
153 | 0 | block_number_bytes: usize, |
154 | 0 | ) -> impl Iterator<Item = impl AsRef<[u8]> + '_> + '_ { |
155 | 0 | let mut header = vec![0; 1 + block_number_bytes]; |
156 | 0 | header[0] = handshake.role.scale_encoding()[0]; |
157 | 0 | // TODO: what to do if the best number doesn't fit in the given size? right now we just wrap around |
158 | 0 | header[1..].copy_from_slice(&handshake.best_number.to_le_bytes()[..block_number_bytes]); |
159 | 0 |
|
160 | 0 | [ |
161 | 0 | either::Left(header), |
162 | 0 | either::Right(handshake.best_hash), |
163 | 0 | either::Right(handshake.genesis_hash), |
164 | 0 | ] |
165 | 0 | .into_iter() |
166 | 0 | } Unexecuted instantiation: _RNvNtNtNtCsN16ciHI6Qf_7smoldot7network5codec15block_announces32encode_block_announces_handshake Unexecuted instantiation: _RNvNtNtNtCseuYC0Zibziv_7smoldot7network5codec15block_announces32encode_block_announces_handshake |
167 | | |
168 | | /// Decodes a SCALE-encoded block announces handshake. |
169 | 0 | pub fn decode_block_announces_handshake( |
170 | 0 | expected_block_number_bytes: usize, |
171 | 0 | handshake: &[u8], |
172 | 0 | ) -> Result<BlockAnnouncesHandshakeRef, BlockAnnouncesHandshakeDecodeError> { |
173 | 0 | let result: Result<_, nom::error::Error<_>> = |
174 | 0 | nom::combinator::all_consuming(nom::combinator::complete(nom::combinator::map( |
175 | 0 | nom::sequence::tuple(( |
176 | 0 | nom::branch::alt(( |
177 | 0 | nom::combinator::map(nom::bytes::streaming::tag(&[0b1]), |_| Role::Full), Unexecuted instantiation: _RNCNvNtNtNtCsN16ciHI6Qf_7smoldot7network5codec15block_announces32decode_block_announces_handshake0B9_ Unexecuted instantiation: _RNCNvNtNtNtCseuYC0Zibziv_7smoldot7network5codec15block_announces32decode_block_announces_handshake0B9_ |
178 | 0 | nom::combinator::map(nom::bytes::streaming::tag(&[0b10]), |_| Role::Light), Unexecuted instantiation: _RNCNvNtNtNtCsN16ciHI6Qf_7smoldot7network5codec15block_announces32decode_block_announces_handshakes_0B9_ Unexecuted instantiation: _RNCNvNtNtNtCseuYC0Zibziv_7smoldot7network5codec15block_announces32decode_block_announces_handshakes_0B9_ |
179 | 0 | nom::combinator::map(nom::bytes::streaming::tag(&[0b100]), |_| Role::Authority), Unexecuted instantiation: _RNCNvNtNtNtCsN16ciHI6Qf_7smoldot7network5codec15block_announces32decode_block_announces_handshakes0_0B9_ Unexecuted instantiation: _RNCNvNtNtNtCseuYC0Zibziv_7smoldot7network5codec15block_announces32decode_block_announces_handshakes0_0B9_ |
180 | 0 | )), |
181 | 0 | crate::util::nom_varsize_number_decode_u64(expected_block_number_bytes), |
182 | 0 | nom::bytes::streaming::take(32u32), |
183 | 0 | nom::bytes::streaming::take(32u32), |
184 | 0 | )), |
185 | 0 | |(role, best_number, best_hash, genesis_hash)| BlockAnnouncesHandshakeRef { |
186 | 0 | role, |
187 | 0 | best_number, |
188 | 0 | best_hash: TryFrom::try_from(best_hash).unwrap(), |
189 | 0 | genesis_hash: TryFrom::try_from(genesis_hash).unwrap(), |
190 | 0 | }, Unexecuted instantiation: _RNCNvNtNtNtCsN16ciHI6Qf_7smoldot7network5codec15block_announces32decode_block_announces_handshakes1_0B9_ Unexecuted instantiation: _RNCNvNtNtNtCseuYC0Zibziv_7smoldot7network5codec15block_announces32decode_block_announces_handshakes1_0B9_ |
191 | 0 | )))(handshake) |
192 | 0 | .finish(); |
193 | 0 |
|
194 | 0 | match result { |
195 | 0 | Ok((_, hs)) => Ok(hs), |
196 | 0 | Err(err) => Err(BlockAnnouncesHandshakeDecodeError(err.code)), |
197 | | } |
198 | 0 | } Unexecuted instantiation: _RNvNtNtNtCsN16ciHI6Qf_7smoldot7network5codec15block_announces32decode_block_announces_handshake Unexecuted instantiation: _RNvNtNtNtCseuYC0Zibziv_7smoldot7network5codec15block_announces32decode_block_announces_handshake |
199 | | |
200 | | /// Error potentially returned by [`decode_block_announces_handshake`]. |
201 | 0 | #[derive(Debug, Clone, derive_more::Display)] Unexecuted instantiation: _RNvXsh_NtNtNtCsN16ciHI6Qf_7smoldot7network5codec15block_announcesNtB5_34BlockAnnouncesHandshakeDecodeErrorNtNtCsaYZPK01V26L_4core3fmt7Display3fmt Unexecuted instantiation: _RNvXsh_NtNtNtCseuYC0Zibziv_7smoldot7network5codec15block_announcesNtB5_34BlockAnnouncesHandshakeDecodeErrorNtNtCsaYZPK01V26L_4core3fmt7Display3fmt |
202 | | #[display(fmt = "Failed to decode a block announces handshake")] |
203 | | pub struct BlockAnnouncesHandshakeDecodeError(nom::error::ErrorKind); |