Skip to main content

karyon_p2p/discovery/kademlia/
messages.rs

1//! Wire messages for the Kademlia DHT plane.
2//!
3//! Two transports:
4//! - `KadNetMsg` over a length-prefixed framed TCP/TLS/QUIC connection
5//!   (lookup-plane), with `KadNetCmd` as the command tag.
6//! - `RefreshMsg` directly over UDP (refresh-plane).
7
8use bincode::{Decode, Encode};
9
10use karyon_net::{
11    codec::{Codec, LengthCodec},
12    ByteBuffer,
13};
14
15use crate::{
16    bloom::Bloom,
17    message::PeerAddr,
18    util::{decode, encode},
19    version::VersionInt,
20    PeerID, Result,
21};
22
23// ---------- Lookup-plane wire envelope ----------
24
25/// Wire envelope for the kademlia lookup-plane (LookupService's own
26/// short-lived TCP/TLS/QUIC connections).
27#[derive(Decode, Encode, Debug, Clone)]
28pub struct KadNetMsg {
29    pub header: KadNetMsgHeader,
30    pub payload: Vec<u8>,
31}
32
33impl KadNetMsg {
34    pub fn new<T: Encode>(command: KadNetCmd, t: T) -> Result<Self> {
35        Ok(Self {
36            header: KadNetMsgHeader { command },
37            payload: encode(&t)?,
38        })
39    }
40}
41
42#[derive(Decode, Encode, Debug, Clone)]
43pub struct KadNetMsgHeader {
44    pub command: KadNetCmd,
45}
46
47/// Commands valid on the kademlia lookup-plane wire.
48#[derive(Decode, Encode, Debug, Clone)]
49#[repr(u8)]
50pub enum KadNetCmd {
51    /// Lookup-level liveness check with version + nonce.
52    Ping,
53    /// Reply to Ping carrying the nonce.
54    Pong,
55    /// Request closest peers to a given peer id.
56    FindPeer,
57    /// Sender's own PeerMsg (advertise self).
58    Peer,
59    /// List of PeerMsg returned in response to FindPeer.
60    Peers,
61    /// Graceful close marker.
62    Shutdown,
63}
64
65/// Length-prefixed bincode codec for the kademlia lookup-plane wire.
66#[derive(Clone)]
67pub struct KadNetMsgCodec {
68    inner_codec: LengthCodec,
69}
70
71impl KadNetMsgCodec {
72    pub fn new() -> Self {
73        Self {
74            inner_codec: LengthCodec::default(),
75        }
76    }
77}
78
79impl Default for KadNetMsgCodec {
80    fn default() -> Self {
81        Self::new()
82    }
83}
84
85impl Codec<ByteBuffer> for KadNetMsgCodec {
86    type Message = KadNetMsg;
87    type Error = karyon_net::Error;
88
89    fn encode(
90        &self,
91        src: &KadNetMsg,
92        dst: &mut ByteBuffer,
93    ) -> std::result::Result<usize, karyon_net::Error> {
94        let src = encode(src).map_err(|e| karyon_net::Error::IO(std::io::Error::other(e)))?;
95        self.inner_codec.encode(&src, dst)
96    }
97
98    fn decode(
99        &self,
100        src: &mut ByteBuffer,
101    ) -> std::result::Result<Option<(usize, KadNetMsg)>, karyon_net::Error> {
102        match self.inner_codec.decode(src)? {
103            Some((n, s)) => {
104                let (m, _) = decode::<KadNetMsg>(&s)
105                    .map_err(|e| karyon_net::Error::IO(std::io::Error::other(e)))?;
106                Ok(Some((n, m)))
107            }
108            None => Ok(None),
109        }
110    }
111}
112
113// ---------- Lookup-plane payloads ----------
114
115/// Ping payload: liveness + version probe.
116#[derive(Decode, Encode, Debug, Clone)]
117pub struct PingMsg {
118    pub nonce: [u8; 32],
119    pub version: VersionInt,
120}
121
122/// Pong payload echoing back the nonce.
123#[derive(Decode, Encode, Debug)]
124pub struct PongMsg(pub [u8; 32]);
125
126/// FindPeer payload: target peer id we want closest peers for.
127#[derive(Decode, Encode, Debug)]
128pub struct FindPeerMsg(pub PeerID);
129
130/// Peer payload: a single peer's advertised addresses + protocol bloom.
131#[derive(Decode, Encode, Debug, Clone, PartialEq, Eq)]
132pub struct PeerMsg {
133    pub peer_id: PeerID,
134    pub addrs: Vec<PeerAddr>,
135    pub discovery_addrs: Vec<PeerAddr>,
136    pub protocols: Bloom,
137}
138
139/// Peers payload: list of peer entries returned by FindPeer.
140#[derive(Decode, Encode, Debug)]
141pub struct PeersMsg {
142    pub peers: Vec<PeerMsg>,
143}
144
145// ---------- Refresh-plane wire (UDP) ----------
146
147/// Refresh-plane message sent directly over UDP. No envelope/codec
148/// because each datagram carries one self-describing message.
149#[derive(Decode, Encode, Debug, Clone)]
150pub enum RefreshMsg {
151    Ping([u8; 32]),
152    Pong([u8; 32]),
153}