/__w/smoldot/smoldot/repo/lib/src/network/codec/identify.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 identify protocol is a request-response protocol. |
19 | | //! |
20 | | //! The request's body is empty. Contrary to other request-response protocols, it doesn't even |
21 | | //! contain a message length. As soon as the protocol has been negotiated, the other side should |
22 | | //! send back the response. |
23 | | //! |
24 | | //! The response's body consists in various useful general-purpose information about the node. |
25 | | //! See [`IdentifyResponse`] for details. |
26 | | //! |
27 | | //! The two most important fields are [`IdentifyResponse::listen_addrs`] and |
28 | | //! [`IdentifyResponse::observed_addr`]. They are necessary in order for nodes to discover their |
29 | | //! public address, and in order to insert peers in the Kademlia k-buckets. |
30 | | //! |
31 | | //! See also [the official specification](https://github.com/libp2p/specs/tree/69e57d59dc5d59d3979d79842b577ec2c483f7fa/identify). |
32 | | |
33 | | use crate::{ |
34 | | libp2p::peer_id::{FromProtobufEncodingError, PublicKey}, |
35 | | util::protobuf, |
36 | | }; |
37 | | |
38 | | use alloc::vec::{self, Vec}; |
39 | | |
40 | | /// Description of a response to an identify request. |
41 | | #[derive(Debug, Clone, PartialEq, Eq)] |
42 | | pub struct IdentifyResponse<'a, TLaIter, TProtoIter> { |
43 | | /// Name of the set of protocols supposed by the node. |
44 | | pub protocol_version: &'a str, |
45 | | |
46 | | /// Name and version of the software that responds. Similar to `User-Agent` in the HTTP |
47 | | /// protocol. Used for debugging purposes. |
48 | | pub agent_version: &'a str, |
49 | | |
50 | | /// Ed25519 public key of the local node. |
51 | | pub ed25519_public_key: [u8; 32], |
52 | | |
53 | | /// List of multiaddresses the local node is listening on. This should include first and |
54 | | /// foremost addresses that are publicly-reachable. |
55 | | /// |
56 | | /// > **Note**: Each item should be decoded into a multiaddr, but keep in mind that it might |
57 | | /// > not be valid. |
58 | | pub listen_addrs: TLaIter, |
59 | | |
60 | | /// Multiaddress of the sender of the identify request, as seen from the receiver. |
61 | | /// |
62 | | /// > **Note**: This should be decoded into a multiaddr, but keep in mind that it might not |
63 | | /// > be valid. |
64 | | pub observed_addr: &'a [u8], |
65 | | |
66 | | /// Names of the protocols supported by the local node. |
67 | | pub protocols: TProtoIter, |
68 | | } |
69 | | |
70 | | // See https://github.com/libp2p/specs/tree/master/identify#the-identify-message for the protobuf |
71 | | // message format. |
72 | | |
73 | | /// Builds the bytes corresponding to a block request. |
74 | 0 | pub fn build_identify_response<'a>( |
75 | 0 | config: IdentifyResponse< |
76 | 0 | 'a, |
77 | 0 | impl Iterator<Item = &'a [u8]> + 'a, |
78 | 0 | impl Iterator<Item = &'a str> + 'a, |
79 | 0 | >, |
80 | 0 | ) -> impl Iterator<Item = impl AsRef<[u8]> + 'a> + 'a { |
81 | 0 | protobuf::string_tag_encode(5, config.protocol_version) |
82 | 0 | .map(either::Left) |
83 | 0 | .map(either::Left) |
84 | 0 | .map(either::Left) |
85 | 0 | .chain( |
86 | 0 | protobuf::string_tag_encode(6, config.agent_version) |
87 | 0 | .map(either::Right) |
88 | 0 | .map(either::Left) |
89 | 0 | .map(either::Left), |
90 | 0 | ) |
91 | 0 | .chain( |
92 | 0 | protobuf::bytes_tag_encode( |
93 | 0 | 1, |
94 | 0 | PublicKey::Ed25519(config.ed25519_public_key).to_protobuf_encoding(), |
95 | 0 | ) |
96 | 0 | .map(either::Left) |
97 | 0 | .map(either::Right) |
98 | 0 | .map(either::Left), |
99 | 0 | ) |
100 | 0 | .chain( |
101 | 0 | config |
102 | 0 | .listen_addrs |
103 | 0 | .flat_map(|addr| protobuf::bytes_tag_encode(2, addr)) Unexecuted instantiation: _RNCINvNtNtNtCsN16ciHI6Qf_7smoldot7network5codec8identify23build_identify_responseppE0Ba_ Unexecuted instantiation: _RNCINvNtNtNtCseuYC0Zibziv_7smoldot7network5codec8identify23build_identify_responseINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyRShEINtNtNtB1p_8adapters3map3MapINtNtNtB1r_5slice4iter4IterNtNtCsdZExvAaxgia_5alloc6string6StringENCNvMs0_NtB8_7serviceINtB3R_12ChainNetworkINtNtCsih6EgvAwZF2_13smoldot_light15network_service5ChainNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefEINtCsldkcb8n7qNv_13async_channel6SenderNtNtNtBa_6libp2p10collection23CoordinatorToConnectionENtNtB1r_4time8DurationE16respond_identifys_0EE0B5o_ Unexecuted instantiation: _RNCINvNtNtNtCseuYC0Zibziv_7smoldot7network5codec8identify23build_identify_responseppE0Ba_ Unexecuted instantiation: _RNCINvNtNtNtCseuYC0Zibziv_7smoldot7network5codec8identify23build_identify_responseINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyRShEINtNtNtB1p_8adapters3map3MapINtNtNtB1r_5slice4iter4IterNtNtCsdZExvAaxgia_5alloc6string6StringENCNvMs0_NtB8_7serviceINtB3R_12ChainNetworkNtNtCsiUjFBJteJ7x_17smoldot_full_node15network_service5ChainINtCsldkcb8n7qNv_13async_channel6SenderNtNtNtBa_6libp2p10collection23CoordinatorToConnectionENtNtCsbpXXxgr6u8g_3std4time7InstantE16respond_identifys_0EE0B4t_ |
104 | 0 | .map(either::Right) |
105 | 0 | .map(either::Right) |
106 | 0 | .map(either::Left), |
107 | 0 | ) |
108 | 0 | .chain( |
109 | 0 | protobuf::bytes_tag_encode(4, config.observed_addr) |
110 | 0 | .map(either::Left) |
111 | 0 | .map(either::Right), |
112 | 0 | ) |
113 | 0 | .chain( |
114 | 0 | config |
115 | 0 | .protocols |
116 | 0 | .flat_map(|p| protobuf::string_tag_encode(3, p)) Unexecuted instantiation: _RNCINvNtNtNtCsN16ciHI6Qf_7smoldot7network5codec8identify23build_identify_responseppEs_0Ba_ Unexecuted instantiation: _RNCINvNtNtNtCseuYC0Zibziv_7smoldot7network5codec8identify23build_identify_responseINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyRShEINtNtNtB1p_8adapters3map3MapINtNtNtB1r_5slice4iter4IterNtNtCsdZExvAaxgia_5alloc6string6StringENCNvMs0_NtB8_7serviceINtB3R_12ChainNetworkINtNtCsih6EgvAwZF2_13smoldot_light15network_service5ChainNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefEINtCsldkcb8n7qNv_13async_channel6SenderNtNtNtBa_6libp2p10collection23CoordinatorToConnectionENtNtB1r_4time8DurationE16respond_identifys_0EEs_0B5o_ Unexecuted instantiation: _RNCINvNtNtNtCseuYC0Zibziv_7smoldot7network5codec8identify23build_identify_responseppEs_0Ba_ Unexecuted instantiation: _RNCINvNtNtNtCseuYC0Zibziv_7smoldot7network5codec8identify23build_identify_responseINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyRShEINtNtNtB1p_8adapters3map3MapINtNtNtB1r_5slice4iter4IterNtNtCsdZExvAaxgia_5alloc6string6StringENCNvMs0_NtB8_7serviceINtB3R_12ChainNetworkNtNtCsiUjFBJteJ7x_17smoldot_full_node15network_service5ChainINtCsldkcb8n7qNv_13async_channel6SenderNtNtNtBa_6libp2p10collection23CoordinatorToConnectionENtNtCsbpXXxgr6u8g_3std4time7InstantE16respond_identifys_0EEs_0B4t_ |
117 | 0 | .map(either::Right) |
118 | 0 | .map(either::Right), |
119 | 0 | ) |
120 | 0 | } Unexecuted instantiation: _RINvNtNtNtCsN16ciHI6Qf_7smoldot7network5codec8identify23build_identify_responseppEB8_ Unexecuted instantiation: _RINvNtNtNtCseuYC0Zibziv_7smoldot7network5codec8identify23build_identify_responseINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyRShEINtNtNtB1n_8adapters3map3MapINtNtNtB1p_5slice4iter4IterNtNtCsdZExvAaxgia_5alloc6string6StringENCNvMs0_NtB6_7serviceINtB3P_12ChainNetworkINtNtCsih6EgvAwZF2_13smoldot_light15network_service5ChainNtNtCsDDUKWWCHAU_18smoldot_light_wasm8platform11PlatformRefEINtCsldkcb8n7qNv_13async_channel6SenderNtNtNtB8_6libp2p10collection23CoordinatorToConnectionENtNtB1p_4time8DurationE16respond_identifys_0EEB5m_ Unexecuted instantiation: _RINvNtNtNtCseuYC0Zibziv_7smoldot7network5codec8identify23build_identify_responseppEB8_ Unexecuted instantiation: _RINvNtNtNtCseuYC0Zibziv_7smoldot7network5codec8identify23build_identify_responseINtNtNtNtCsaYZPK01V26L_4core4iter7sources5empty5EmptyRShEINtNtNtB1n_8adapters3map3MapINtNtNtB1p_5slice4iter4IterNtNtCsdZExvAaxgia_5alloc6string6StringENCNvMs0_NtB6_7serviceINtB3P_12ChainNetworkNtNtCsiUjFBJteJ7x_17smoldot_full_node15network_service5ChainINtCsldkcb8n7qNv_13async_channel6SenderNtNtNtB8_6libp2p10collection23CoordinatorToConnectionENtNtCsbpXXxgr6u8g_3std4time7InstantE16respond_identifys_0EEB4r_ |
121 | | |
122 | | /// Decodes a response to an identify request. |
123 | 0 | pub fn decode_identify_response( |
124 | 0 | response_bytes: &'_ [u8], |
125 | 0 | ) -> Result< |
126 | 0 | IdentifyResponse<'_, vec::IntoIter<&'_ [u8]>, vec::IntoIter<&'_ str>>, |
127 | 0 | DecodeIdentifyResponseError, |
128 | 0 | > { |
129 | 0 | let mut parser = nom::combinator::all_consuming::<_, _, nom::error::Error<&[u8]>, _>( |
130 | 0 | nom::combinator::complete(protobuf::message_decode! { |
131 | 0 | #[optional] protocol_version = 5 => protobuf::string_tag_decode, |
132 | 0 | #[optional] agent_version = 6 => protobuf::string_tag_decode, |
133 | 0 | #[optional] ed25519_public_key = 1 => protobuf::bytes_tag_decode, |
134 | 0 | #[repeated(max = 1024)] listen_addrs = 2 => protobuf::bytes_tag_decode, |
135 | 0 | #[optional] observed_addr = 4 => protobuf::bytes_tag_decode, |
136 | 0 | #[repeated(max = 1024)] protocols = 3 => protobuf::string_tag_decode, Unexecuted instantiation: _RNCNvNtNtNtCsN16ciHI6Qf_7smoldot7network5codec8identify24decode_identify_response0B9_ Unexecuted instantiation: _RNCNvNtNtNtCseuYC0Zibziv_7smoldot7network5codec8identify24decode_identify_response0B9_ |
137 | 0 | }), |
138 | 0 | ); |
139 | | |
140 | 0 | let decoded = match nom::Finish::finish(parser(response_bytes)) { |
141 | 0 | Ok((_, out)) => out, |
142 | 0 | Err(_) => return Err(DecodeIdentifyResponseError::ProtobufDecode), |
143 | | }; |
144 | | |
145 | | Ok(IdentifyResponse { |
146 | 0 | agent_version: decoded.agent_version.unwrap_or_default(), |
147 | 0 | protocol_version: decoded.protocol_version.unwrap_or_default(), |
148 | 0 | ed25519_public_key: match PublicKey::from_protobuf_encoding( |
149 | 0 | decoded.ed25519_public_key.unwrap_or_default(), |
150 | 0 | ) |
151 | 0 | .map_err(DecodeIdentifyResponseError::InvalidPublicKey)? |
152 | | { |
153 | 0 | PublicKey::Ed25519(key) => key, |
154 | 0 | }, |
155 | 0 | listen_addrs: decoded.listen_addrs.into_iter(), |
156 | 0 | observed_addr: decoded.observed_addr.unwrap_or_default(), |
157 | 0 | protocols: decoded.protocols.into_iter(), |
158 | | }) |
159 | 0 | } Unexecuted instantiation: _RNvNtNtNtCsN16ciHI6Qf_7smoldot7network5codec8identify24decode_identify_response Unexecuted instantiation: _RNvNtNtNtCseuYC0Zibziv_7smoldot7network5codec8identify24decode_identify_response |
160 | | |
161 | | /// Error potentially returned by [`decode_identify_response`]. |
162 | 0 | #[derive(Debug, derive_more::Display)] Unexecuted instantiation: _RNvXs4_NtNtNtCsN16ciHI6Qf_7smoldot7network5codec8identifyNtB5_27DecodeIdentifyResponseErrorNtNtCsaYZPK01V26L_4core3fmt7Display3fmt Unexecuted instantiation: _RNvXs4_NtNtNtCseuYC0Zibziv_7smoldot7network5codec8identifyNtB5_27DecodeIdentifyResponseErrorNtNtCsaYZPK01V26L_4core3fmt7Display3fmt |
163 | | pub enum DecodeIdentifyResponseError { |
164 | | /// Error while decoding the Protobuf encoding. |
165 | | ProtobufDecode, |
166 | | /// Couldn't decode the public key of the remote. |
167 | | #[display(fmt = "Failed to decode remote public key: {_0}")] |
168 | | InvalidPublicKey(FromProtobufEncodingError), |
169 | | } |