Coverage Report

Created: 2024-05-16 12:16

/__w/smoldot/smoldot/repo/lib/src/network/codec/kademlia.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::{libp2p::peer_id, util::protobuf};
19
20
use alloc::vec::Vec;
21
22
// See https://github.com/libp2p/specs/tree/master/kad-dht#rpc-messages for the protobuf format.
23
24
/// Builds a wire message to send on the Kademlia request-response protocol to ask the target to
25
/// return the nodes closest to the parameter.
26
// TODO: parameter type?
27
0
pub fn build_find_node_request(peer_id: &[u8]) -> Vec<u8> {
28
0
    // The capacity is arbitrary but large enough to avoid Vec reallocations.
29
0
    let mut out = Vec::with_capacity(64 + peer_id.len());
30
0
    for slice in protobuf::enum_tag_encode(1, 4) {
31
0
        out.extend_from_slice(slice.as_ref());
32
0
    }
33
0
    for slice in protobuf::bytes_tag_encode(2, peer_id) {
34
0
        out.extend_from_slice(slice.as_ref());
35
0
    }
36
0
    out
37
0
}
Unexecuted instantiation: _RNvNtNtNtCsN16ciHI6Qf_7smoldot7network5codec8kademlia23build_find_node_request
Unexecuted instantiation: _RNvNtNtNtCseuYC0Zibziv_7smoldot7network5codec8kademlia23build_find_node_request
38
39
/// Decodes a response to a request built using [`build_find_node_request`].
40
// TODO: return a borrow of the response bytes ; we're limited by protobuf library
41
0
pub fn decode_find_node_response(
42
0
    response_bytes: &[u8],
43
0
) -> Result<Vec<(peer_id::PeerId, Vec<Vec<u8>>)>, DecodeFindNodeResponseError> {
44
0
    let mut parser = nom::combinator::all_consuming::<_, _, nom::error::Error<&[u8]>, _>(
45
0
        nom::combinator::complete(protobuf::message_decode! {
46
0
            #[optional] response_ty = 1 => protobuf::enum_tag_decode,
47
0
            #[repeated(max = 1024)] peers = 8 => protobuf::message_tag_decode(protobuf::message_decode!{
48
0
                #[required] peer_id = 1 => protobuf::bytes_tag_decode,
49
0
                #[repeated(max = 1024)] addrs = 2 => protobuf::bytes_tag_decode,
50
0
            }),
Unexecuted instantiation: _RNCNvNtNtNtCsN16ciHI6Qf_7smoldot7network5codec8kademlia25decode_find_node_responses_0B9_
Unexecuted instantiation: _RNCNvNtNtNtCseuYC0Zibziv_7smoldot7network5codec8kademlia25decode_find_node_responses_0B9_
Unexecuted instantiation: _RNCNCNCNvNtNtNtCsN16ciHI6Qf_7smoldot7network5codec8kademlia25decode_find_node_responses_000Bd_
Unexecuted instantiation: _RNCNCNCNvNtNtNtCseuYC0Zibziv_7smoldot7network5codec8kademlia25decode_find_node_responses_000Bd_
51
0
        }),
52
0
    );
53
54
0
    let closer_peers = match nom::Finish::finish(parser(response_bytes)) {
55
0
        Ok((_, out)) if out.response_ty.unwrap_or(0) == 4 => out.peers,
56
0
        Ok((_, _)) => return Err(DecodeFindNodeResponseError::BadResponseTy),
57
        Err(_) => {
58
0
            return Err(DecodeFindNodeResponseError::ProtobufDecode(
59
0
                ProtobufDecodeError,
60
0
            ))
61
        }
62
    };
63
64
0
    let mut result = Vec::with_capacity(closer_peers.len());
65
0
    for peer in closer_peers {
66
0
        let peer_id = peer_id::PeerId::from_bytes(peer.peer_id.to_vec())
67
0
            .map_err(|(err, _)| DecodeFindNodeResponseError::BadPeerId(err))?;
Unexecuted instantiation: _RNCNvNtNtNtCsN16ciHI6Qf_7smoldot7network5codec8kademlia25decode_find_node_response0B9_
Unexecuted instantiation: _RNCNvNtNtNtCseuYC0Zibziv_7smoldot7network5codec8kademlia25decode_find_node_response0B9_
68
69
0
        let mut multiaddrs = Vec::with_capacity(peer.addrs.len());
70
0
        for addr in peer.addrs {
71
0
            multiaddrs.push(addr.to_vec());
72
0
        }
73
74
0
        result.push((peer_id, multiaddrs));
75
    }
76
77
0
    Ok(result)
78
0
}
Unexecuted instantiation: _RNvNtNtNtCsN16ciHI6Qf_7smoldot7network5codec8kademlia25decode_find_node_response
Unexecuted instantiation: _RNvNtNtNtCseuYC0Zibziv_7smoldot7network5codec8kademlia25decode_find_node_response
79
80
/// Error potentially returned by [`decode_find_node_response`].
81
0
#[derive(Debug, derive_more::Display)]
Unexecuted instantiation: _RNvXs_NtNtNtCsN16ciHI6Qf_7smoldot7network5codec8kademliaNtB4_27DecodeFindNodeResponseErrorNtNtCsaYZPK01V26L_4core3fmt7Display3fmt
Unexecuted instantiation: _RNvXs_NtNtNtCseuYC0Zibziv_7smoldot7network5codec8kademliaNtB4_27DecodeFindNodeResponseErrorNtNtCsaYZPK01V26L_4core3fmt7Display3fmt
82
pub enum DecodeFindNodeResponseError {
83
    /// Error while decoding the Protobuf encoding.
84
    #[display(fmt = "Error decoding the response: {_0}")]
85
    ProtobufDecode(ProtobufDecodeError),
86
    /// Response isn't a response to a find node request.
87
    BadResponseTy,
88
    /// Error while parsing a [`peer_id::PeerId`] in the response.
89
    #[display(fmt = "Invalid PeerId: {_0}")]
90
    BadPeerId(peer_id::FromBytesError),
91
}
92
93
/// Error while decoding the Protobuf encoding.
94
0
#[derive(Debug, derive_more::Display)]
Unexecuted instantiation: _RNvXs1_NtNtNtCsN16ciHI6Qf_7smoldot7network5codec8kademliaNtB5_19ProtobufDecodeErrorNtNtCsaYZPK01V26L_4core3fmt7Display3fmt
Unexecuted instantiation: _RNvXs1_NtNtNtCseuYC0Zibziv_7smoldot7network5codec8kademliaNtB5_19ProtobufDecodeErrorNtNtCsaYZPK01V26L_4core3fmt7Display3fmt
95
pub struct ProtobufDecodeError;