Coverage Report

Created: 2024-05-16 12:16

/__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
}