/__w/smoldot/smoldot/repo/lib/src/network/codec.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 | | //! Encoding and decoding of messages of the protocols used by Polkadot/Substrate. |
19 | | |
20 | | // TODO: expand docs |
21 | | |
22 | | use alloc::{borrow::Cow, string::String}; |
23 | | use core::{fmt, iter}; |
24 | | |
25 | | // Implementation note: each protocol goes into a different sub-module whose content is |
26 | | // re-exported here. |
27 | | |
28 | | mod block_announces; |
29 | | mod block_request; |
30 | | mod grandpa; |
31 | | mod grandpa_warp_sync; |
32 | | mod identify; |
33 | | mod kademlia; |
34 | | mod state_request; |
35 | | mod storage_call_proof; |
36 | | |
37 | | pub use self::block_announces::*; |
38 | | pub use self::block_request::*; |
39 | | pub use self::grandpa::*; |
40 | | pub use self::grandpa_warp_sync::*; |
41 | | pub use self::identify::*; |
42 | | pub use self::kademlia::*; |
43 | | pub use self::state_request::*; |
44 | | pub use self::storage_call_proof::*; |
45 | | |
46 | | /// Name of a protocol that is part of the Substrate/Polkadot networking. |
47 | | #[derive(Copy, Clone, PartialEq, Eq, Hash)] |
48 | | pub enum ProtocolName<'a> { |
49 | | Identify, |
50 | | Ping, |
51 | | BlockAnnounces { |
52 | | genesis_hash: [u8; 32], |
53 | | fork_id: Option<&'a str>, |
54 | | }, |
55 | | Transactions { |
56 | | genesis_hash: [u8; 32], |
57 | | fork_id: Option<&'a str>, |
58 | | }, |
59 | | Grandpa { |
60 | | genesis_hash: [u8; 32], |
61 | | fork_id: Option<&'a str>, |
62 | | }, |
63 | | Sync { |
64 | | genesis_hash: [u8; 32], |
65 | | fork_id: Option<&'a str>, |
66 | | }, |
67 | | Light { |
68 | | genesis_hash: [u8; 32], |
69 | | fork_id: Option<&'a str>, |
70 | | }, |
71 | | Kad { |
72 | | genesis_hash: [u8; 32], |
73 | | fork_id: Option<&'a str>, |
74 | | }, |
75 | | SyncWarp { |
76 | | genesis_hash: [u8; 32], |
77 | | fork_id: Option<&'a str>, |
78 | | }, |
79 | | State { |
80 | | genesis_hash: [u8; 32], |
81 | | fork_id: Option<&'a str>, |
82 | | }, |
83 | | } |
84 | | |
85 | | impl<'a> fmt::Debug for ProtocolName<'a> { |
86 | 0 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
87 | 0 | fmt::Display::fmt(self, f) |
88 | 0 | } Unexecuted instantiation: _RNvXNtNtCsN16ciHI6Qf_7smoldot7network5codecNtB2_12ProtocolNameNtNtCsaYZPK01V26L_4core3fmt5Debug3fmt Unexecuted instantiation: _RNvXNtNtCseuYC0Zibziv_7smoldot7network5codecNtB2_12ProtocolNameNtNtCsaYZPK01V26L_4core3fmt5Debug3fmt |
89 | | } |
90 | | |
91 | | impl<'a> fmt::Display for ProtocolName<'a> { |
92 | 0 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
93 | 0 | for chunk in encode_protocol_name(*self) { |
94 | 0 | f.write_str(chunk.as_ref())?; |
95 | | } |
96 | 0 | Ok(()) |
97 | 0 | } Unexecuted instantiation: _RNvXs_NtNtCsN16ciHI6Qf_7smoldot7network5codecNtB4_12ProtocolNameNtNtCsaYZPK01V26L_4core3fmt7Display3fmt Unexecuted instantiation: _RNvXs_NtNtCseuYC0Zibziv_7smoldot7network5codecNtB4_12ProtocolNameNtNtCsaYZPK01V26L_4core3fmt7Display3fmt |
98 | | } |
99 | | |
100 | | /// Turns a [`ProtocolName`] into its string version. Returns a list of objects that, when |
101 | | /// concatenated together, forms the string version of the [`ProtocolName`]. |
102 | 0 | pub fn encode_protocol_name( |
103 | 0 | protocol: ProtocolName<'_>, |
104 | 0 | ) -> impl Iterator<Item = impl AsRef<str> + '_> + '_ { |
105 | 0 | let (genesis_hash, fork_id, base_protocol_name) = match protocol { |
106 | 0 | ProtocolName::Identify => return either::Left(iter::once(Cow::Borrowed("/ipfs/id/1.0.0"))), |
107 | 0 | ProtocolName::Ping => return either::Left(iter::once(Cow::Borrowed("/ipfs/ping/1.0.0"))), |
108 | | ProtocolName::BlockAnnounces { |
109 | 0 | genesis_hash, |
110 | 0 | fork_id, |
111 | 0 | } => (genesis_hash, fork_id, "block-announces/1"), |
112 | | ProtocolName::Transactions { |
113 | 0 | genesis_hash, |
114 | 0 | fork_id, |
115 | 0 | } => (genesis_hash, fork_id, "transactions/1"), |
116 | | ProtocolName::Grandpa { |
117 | 0 | genesis_hash, |
118 | 0 | fork_id, |
119 | 0 | } => (genesis_hash, fork_id, "grandpa/1"), |
120 | | ProtocolName::Sync { |
121 | 0 | genesis_hash, |
122 | 0 | fork_id, |
123 | 0 | } => (genesis_hash, fork_id, "sync/2"), |
124 | | ProtocolName::Light { |
125 | 0 | genesis_hash, |
126 | 0 | fork_id, |
127 | 0 | } => (genesis_hash, fork_id, "light/2"), |
128 | | ProtocolName::Kad { |
129 | 0 | genesis_hash, |
130 | 0 | fork_id, |
131 | 0 | } => (genesis_hash, fork_id, "kad"), |
132 | | ProtocolName::SyncWarp { |
133 | 0 | genesis_hash, |
134 | 0 | fork_id, |
135 | 0 | } => (genesis_hash, fork_id, "sync/warp"), |
136 | | ProtocolName::State { |
137 | 0 | genesis_hash, |
138 | 0 | fork_id, |
139 | 0 | } => (genesis_hash, fork_id, "state/2"), |
140 | | }; |
141 | | |
142 | 0 | let genesis_hash = hex::encode(genesis_hash); |
143 | | |
144 | 0 | if let Some(fork_id) = fork_id { |
145 | 0 | either::Right(either::Right( |
146 | 0 | [ |
147 | 0 | Cow::Borrowed("/"), |
148 | 0 | Cow::Owned(genesis_hash), |
149 | 0 | Cow::Borrowed("/"), |
150 | 0 | Cow::Borrowed(fork_id), |
151 | 0 | Cow::Borrowed("/"), |
152 | 0 | Cow::Borrowed(base_protocol_name), |
153 | 0 | ] |
154 | 0 | .into_iter(), |
155 | 0 | )) |
156 | | } else { |
157 | 0 | either::Right(either::Left( |
158 | 0 | [ |
159 | 0 | Cow::Borrowed("/"), |
160 | 0 | Cow::Owned(genesis_hash), |
161 | 0 | Cow::Borrowed("/"), |
162 | 0 | Cow::Borrowed(base_protocol_name), |
163 | 0 | ] |
164 | 0 | .into_iter(), |
165 | 0 | )) |
166 | | } |
167 | 0 | } Unexecuted instantiation: _RNvNtNtCsN16ciHI6Qf_7smoldot7network5codec20encode_protocol_name Unexecuted instantiation: _RNvNtNtCseuYC0Zibziv_7smoldot7network5codec20encode_protocol_name |
168 | | |
169 | | /// Turns a [`ProtocolName`] into a string. |
170 | 0 | pub fn encode_protocol_name_string(protocol: ProtocolName<'_>) -> String { |
171 | 0 | encode_protocol_name(protocol).fold(String::with_capacity(128), |mut a, b| { |
172 | 0 | a.push_str(b.as_ref()); |
173 | 0 | a |
174 | 0 | }) Unexecuted instantiation: _RNCNvNtNtCsN16ciHI6Qf_7smoldot7network5codec27encode_protocol_name_string0B7_ Unexecuted instantiation: _RNCNvNtNtCseuYC0Zibziv_7smoldot7network5codec27encode_protocol_name_string0B7_ |
175 | 0 | } Unexecuted instantiation: _RNvNtNtCsN16ciHI6Qf_7smoldot7network5codec27encode_protocol_name_string Unexecuted instantiation: _RNvNtNtCseuYC0Zibziv_7smoldot7network5codec27encode_protocol_name_string |
176 | | |
177 | | /// Decodes a protocol name into its components. |
178 | | /// |
179 | | /// Returns an error if the protocol name isn't recognized. |
180 | 0 | pub fn decode_protocol_name(name: &str) -> Result<ProtocolName, ()> { |
181 | 0 | nom::combinator::all_consuming(nom::branch::alt(( |
182 | 0 | nom::combinator::map(nom::bytes::complete::tag("/ipfs/id/1.0.0"), |_| { |
183 | 0 | ProtocolName::Identify |
184 | 0 | }), Unexecuted instantiation: _RNCNvNtNtCsN16ciHI6Qf_7smoldot7network5codec20decode_protocol_name0B7_ Unexecuted instantiation: _RNCNvNtNtCseuYC0Zibziv_7smoldot7network5codec20decode_protocol_name0B7_ |
185 | 0 | nom::combinator::map(nom::bytes::complete::tag("/ipfs/ping/1.0.0"), |_| { |
186 | 0 | ProtocolName::Ping |
187 | 0 | }), Unexecuted instantiation: _RNCNvNtNtCsN16ciHI6Qf_7smoldot7network5codec20decode_protocol_names_0B7_ Unexecuted instantiation: _RNCNvNtNtCseuYC0Zibziv_7smoldot7network5codec20decode_protocol_names_0B7_ |
188 | 0 | nom::combinator::map( |
189 | 0 | nom::sequence::tuple(( |
190 | 0 | nom::bytes::complete::tag("/"), |
191 | 0 | genesis_hash, |
192 | 0 | nom::bytes::complete::tag("/"), |
193 | 0 | protocol_ty, |
194 | 0 | )), |
195 | 0 | |(_, genesis_hash, _, protocol_ty)| { |
196 | 0 | protocol_ty_to_real_protocol(protocol_ty, genesis_hash, None) |
197 | 0 | }, Unexecuted instantiation: _RNCNvNtNtCsN16ciHI6Qf_7smoldot7network5codec20decode_protocol_names0_0B7_ Unexecuted instantiation: _RNCNvNtNtCseuYC0Zibziv_7smoldot7network5codec20decode_protocol_names0_0B7_ |
198 | 0 | ), |
199 | 0 | nom::combinator::map( |
200 | 0 | nom::sequence::tuple(( |
201 | 0 | nom::bytes::complete::tag("/"), |
202 | 0 | genesis_hash, |
203 | 0 | nom::bytes::complete::tag("/"), |
204 | 0 | nom::bytes::complete::take_until("/"), |
205 | 0 | nom::bytes::complete::tag("/"), |
206 | 0 | protocol_ty, |
207 | 0 | )), |
208 | 0 | |(_, genesis_hash, _, fork_id, _, protocol_ty)| { |
209 | 0 | protocol_ty_to_real_protocol(protocol_ty, genesis_hash, Some(fork_id)) |
210 | 0 | }, Unexecuted instantiation: _RNCNvNtNtCsN16ciHI6Qf_7smoldot7network5codec20decode_protocol_names1_0B7_ Unexecuted instantiation: _RNCNvNtNtCseuYC0Zibziv_7smoldot7network5codec20decode_protocol_names1_0B7_ |
211 | 0 | ), |
212 | 0 | )))(name) |
213 | 0 | .map(|(_, parse_result)| parse_result) Unexecuted instantiation: _RNCNvNtNtCsN16ciHI6Qf_7smoldot7network5codec20decode_protocol_names2_0B7_ Unexecuted instantiation: _RNCNvNtNtCseuYC0Zibziv_7smoldot7network5codec20decode_protocol_names2_0B7_ |
214 | 0 | .map_err(|_| ()) Unexecuted instantiation: _RNCNvNtNtCsN16ciHI6Qf_7smoldot7network5codec20decode_protocol_names3_0B7_ Unexecuted instantiation: _RNCNvNtNtCseuYC0Zibziv_7smoldot7network5codec20decode_protocol_names3_0B7_ |
215 | 0 | } Unexecuted instantiation: _RNvNtNtCsN16ciHI6Qf_7smoldot7network5codec20decode_protocol_name Unexecuted instantiation: _RNvNtNtCseuYC0Zibziv_7smoldot7network5codec20decode_protocol_name |
216 | | |
217 | 0 | fn genesis_hash(name: &str) -> nom::IResult<&str, [u8; 32]> { |
218 | 0 | nom::combinator::map_opt(nom::bytes::complete::take(64u32), |hash| { |
219 | 0 | hex::decode(hash) |
220 | 0 | .ok() |
221 | 0 | .map(|hash| <[u8; 32]>::try_from(hash).unwrap_or_else(|_| unreachable!())) Unexecuted instantiation: _RNCNCNvNtNtCsN16ciHI6Qf_7smoldot7network5codec12genesis_hash00B9_ Unexecuted instantiation: _RNCNCNvNtNtCseuYC0Zibziv_7smoldot7network5codec12genesis_hash00B9_ Unexecuted instantiation: _RNCNCNCNvNtNtCsN16ciHI6Qf_7smoldot7network5codec12genesis_hash000Bb_ Unexecuted instantiation: _RNCNCNCNvNtNtCseuYC0Zibziv_7smoldot7network5codec12genesis_hash000Bb_ |
222 | 0 | })(name) Unexecuted instantiation: _RNCNvNtNtCsN16ciHI6Qf_7smoldot7network5codec12genesis_hash0B7_ Unexecuted instantiation: _RNCNvNtNtCseuYC0Zibziv_7smoldot7network5codec12genesis_hash0B7_ |
223 | 0 | } Unexecuted instantiation: _RNvNtNtCsN16ciHI6Qf_7smoldot7network5codec12genesis_hash Unexecuted instantiation: _RNvNtNtCseuYC0Zibziv_7smoldot7network5codec12genesis_hash |
224 | | |
225 | | enum ProtocolTy { |
226 | | BlockAnnounces, |
227 | | Transactions, |
228 | | Grandpa, |
229 | | Sync, |
230 | | Light, |
231 | | Kad, |
232 | | SyncWarp, |
233 | | State, |
234 | | } |
235 | | |
236 | 0 | fn protocol_ty(name: &str) -> nom::IResult<&str, ProtocolTy> { |
237 | 0 | nom::branch::alt(( |
238 | 0 | nom::combinator::map(nom::bytes::complete::tag("block-announces/1"), |_| { |
239 | 0 | ProtocolTy::BlockAnnounces |
240 | 0 | }), Unexecuted instantiation: _RNCNvNtNtCsN16ciHI6Qf_7smoldot7network5codec11protocol_ty0B7_ Unexecuted instantiation: _RNCNvNtNtCseuYC0Zibziv_7smoldot7network5codec11protocol_ty0B7_ |
241 | 0 | nom::combinator::map(nom::bytes::complete::tag("transactions/1"), |_| { |
242 | 0 | ProtocolTy::Transactions |
243 | 0 | }), Unexecuted instantiation: _RNCNvNtNtCsN16ciHI6Qf_7smoldot7network5codec11protocol_tys_0B7_ Unexecuted instantiation: _RNCNvNtNtCseuYC0Zibziv_7smoldot7network5codec11protocol_tys_0B7_ |
244 | 0 | nom::combinator::map(nom::bytes::complete::tag("grandpa/1"), |_| { |
245 | 0 | ProtocolTy::Grandpa |
246 | 0 | }), Unexecuted instantiation: _RNCNvNtNtCsN16ciHI6Qf_7smoldot7network5codec11protocol_tys0_0B7_ Unexecuted instantiation: _RNCNvNtNtCseuYC0Zibziv_7smoldot7network5codec11protocol_tys0_0B7_ |
247 | 0 | nom::combinator::map(nom::bytes::complete::tag("sync/2"), |_| ProtocolTy::Sync), Unexecuted instantiation: _RNCNvNtNtCsN16ciHI6Qf_7smoldot7network5codec11protocol_tys1_0B7_ Unexecuted instantiation: _RNCNvNtNtCseuYC0Zibziv_7smoldot7network5codec11protocol_tys1_0B7_ |
248 | 0 | nom::combinator::map(nom::bytes::complete::tag("light/2"), |_| ProtocolTy::Light), Unexecuted instantiation: _RNCNvNtNtCsN16ciHI6Qf_7smoldot7network5codec11protocol_tys2_0B7_ Unexecuted instantiation: _RNCNvNtNtCseuYC0Zibziv_7smoldot7network5codec11protocol_tys2_0B7_ |
249 | 0 | nom::combinator::map(nom::bytes::complete::tag("kad"), |_| ProtocolTy::Kad), Unexecuted instantiation: _RNCNvNtNtCsN16ciHI6Qf_7smoldot7network5codec11protocol_tys3_0B7_ Unexecuted instantiation: _RNCNvNtNtCseuYC0Zibziv_7smoldot7network5codec11protocol_tys3_0B7_ |
250 | 0 | nom::combinator::map(nom::bytes::complete::tag("sync/warp"), |_| { |
251 | 0 | ProtocolTy::SyncWarp |
252 | 0 | }), Unexecuted instantiation: _RNCNvNtNtCsN16ciHI6Qf_7smoldot7network5codec11protocol_tys4_0B7_ Unexecuted instantiation: _RNCNvNtNtCseuYC0Zibziv_7smoldot7network5codec11protocol_tys4_0B7_ |
253 | 0 | nom::combinator::map(nom::bytes::complete::tag("state/2"), |_| ProtocolTy::State), Unexecuted instantiation: _RNCNvNtNtCsN16ciHI6Qf_7smoldot7network5codec11protocol_tys5_0B7_ Unexecuted instantiation: _RNCNvNtNtCseuYC0Zibziv_7smoldot7network5codec11protocol_tys5_0B7_ |
254 | 0 | ))(name) |
255 | 0 | } Unexecuted instantiation: _RNvNtNtCsN16ciHI6Qf_7smoldot7network5codec11protocol_ty Unexecuted instantiation: _RNvNtNtCseuYC0Zibziv_7smoldot7network5codec11protocol_ty |
256 | | |
257 | 0 | fn protocol_ty_to_real_protocol( |
258 | 0 | ty: ProtocolTy, |
259 | 0 | genesis_hash: [u8; 32], |
260 | 0 | fork_id: Option<&str>, |
261 | 0 | ) -> ProtocolName { |
262 | 0 | match ty { |
263 | 0 | ProtocolTy::BlockAnnounces => ProtocolName::BlockAnnounces { |
264 | 0 | genesis_hash, |
265 | 0 | fork_id, |
266 | 0 | }, |
267 | 0 | ProtocolTy::Transactions => ProtocolName::Transactions { |
268 | 0 | genesis_hash, |
269 | 0 | fork_id, |
270 | 0 | }, |
271 | 0 | ProtocolTy::Grandpa => ProtocolName::Grandpa { |
272 | 0 | genesis_hash, |
273 | 0 | fork_id, |
274 | 0 | }, |
275 | 0 | ProtocolTy::Sync => ProtocolName::Sync { |
276 | 0 | genesis_hash, |
277 | 0 | fork_id, |
278 | 0 | }, |
279 | 0 | ProtocolTy::Light => ProtocolName::Light { |
280 | 0 | genesis_hash, |
281 | 0 | fork_id, |
282 | 0 | }, |
283 | 0 | ProtocolTy::Kad => ProtocolName::Kad { |
284 | 0 | genesis_hash, |
285 | 0 | fork_id, |
286 | 0 | }, |
287 | 0 | ProtocolTy::SyncWarp => ProtocolName::SyncWarp { |
288 | 0 | genesis_hash, |
289 | 0 | fork_id, |
290 | 0 | }, |
291 | 0 | ProtocolTy::State => ProtocolName::State { |
292 | 0 | genesis_hash, |
293 | 0 | fork_id, |
294 | 0 | }, |
295 | | } |
296 | 0 | } Unexecuted instantiation: _RNvNtNtCsN16ciHI6Qf_7smoldot7network5codec28protocol_ty_to_real_protocol Unexecuted instantiation: _RNvNtNtCseuYC0Zibziv_7smoldot7network5codec28protocol_ty_to_real_protocol |
297 | | |
298 | | // TODO: tests for the protocol names |