tycho_network/proto/
dht.rs

1use std::sync::Arc;
2
3use bytes::Bytes;
4use tl_proto::{TlRead, TlWrite};
5use tycho_util::tl;
6
7use crate::types::{PeerId, PeerInfo};
8use crate::util::check_peer_signature;
9
10#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, TlRead, TlWrite)]
11#[tl(boxed, scheme = "proto.tl")]
12pub enum PeerValueKeyName {
13    #[tl(id = "dht.peerValueKeyName.nodeInfo")]
14    NodeInfo,
15}
16
17#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, TlRead, TlWrite)]
18#[tl(boxed, scheme = "proto.tl")]
19pub enum MergedValueKeyName {
20    #[tl(id = "dht.mergedValueKeyName.publicOverlayEntries")]
21    PublicOverlayEntries,
22}
23
24/// Key for values that can only be updated by the owner.
25///
26/// See [`SignedValueKeyRef`] for the non-owned version of the struct.
27#[derive(Debug, Clone, PartialEq, Eq, TlRead, TlWrite)]
28#[tl(boxed, id = "dht.peerValueKey", scheme = "proto.tl")]
29pub struct PeerValueKey {
30    /// Key name.
31    pub name: PeerValueKeyName,
32    /// Public key of the owner.
33    pub peer_id: PeerId,
34}
35
36/// Key for values that can only be updated by the owner.
37///
38/// See [`SignedValueKey`] for the owned version of the struct.
39#[derive(Debug, Clone, PartialEq, Eq, TlRead, TlWrite)]
40#[tl(boxed, id = "dht.peerValueKey", scheme = "proto.tl")]
41pub struct PeerValueKeyRef<'tl> {
42    /// Key name.
43    pub name: PeerValueKeyName,
44    /// Public key of the owner.
45    pub peer_id: &'tl PeerId,
46}
47
48impl PeerValueKeyRef<'_> {
49    pub fn as_owned(&self) -> PeerValueKey {
50        PeerValueKey {
51            name: self.name,
52            peer_id: *self.peer_id,
53        }
54    }
55}
56
57/// Key for group-managed values.
58///
59/// See [`MergedValueKeyRef`] for the non-owned version of the struct.
60#[derive(Debug, Clone, PartialEq, Eq, TlRead, TlWrite)]
61#[tl(boxed, id = "dht.mergedValueKey", scheme = "proto.tl")]
62pub struct MergedValueKey {
63    /// Key name.
64    pub name: MergedValueKeyName,
65    /// Group id.
66    pub group_id: [u8; 32],
67}
68
69/// Key for group-managed values.
70///
71/// See [`MergedValueKey`] for the owned version of the struct.
72#[derive(Debug, Clone, PartialEq, Eq, TlRead, TlWrite)]
73#[tl(boxed, id = "dht.mergedValueKey", scheme = "proto.tl")]
74pub struct MergedValueKeyRef<'tl> {
75    /// Key name.
76    pub name: MergedValueKeyName,
77    /// Group id.
78    pub group_id: &'tl [u8; 32],
79}
80
81impl MergedValueKeyRef<'_> {
82    pub fn as_owned(&self) -> MergedValueKey {
83        MergedValueKey {
84            name: self.name,
85            group_id: *self.group_id,
86        }
87    }
88}
89
90/// Value with a known owner.
91///
92/// See [`PeerValueRef`] for the non-owned version of the struct.
93#[derive(Debug, Clone, PartialEq, Eq, TlRead, TlWrite)]
94#[tl(boxed, id = "dht.peerValue", scheme = "proto.tl")]
95pub struct PeerValue {
96    /// Peer value key.
97    pub key: PeerValueKey,
98    /// Any data.
99    pub data: Box<[u8]>,
100    /// Unix timestamp up to which this value is valid.
101    pub expires_at: u32,
102    /// A `ed25519` signature of this entry.
103    #[tl(signature, with = "tl::signature_owned")]
104    pub signature: Box<[u8; 64]>,
105}
106
107/// Value with a known owner.
108///
109/// See [`PeerValue`] for the owned version of the struct.
110#[derive(Debug, Clone, PartialEq, Eq, TlRead, TlWrite)]
111#[tl(boxed, id = "dht.peerValue", scheme = "proto.tl")]
112pub struct PeerValueRef<'tl> {
113    /// Peer value key.
114    pub key: PeerValueKeyRef<'tl>,
115    /// Any data.
116    pub data: &'tl [u8],
117    /// Unix timestamp up to which this value is valid.
118    pub expires_at: u32,
119    /// A `ed25519` signature of this entry.
120    #[tl(signature, with = "tl::signature_ref")]
121    pub signature: &'tl [u8; 64],
122}
123
124impl PeerValueRef<'_> {
125    pub fn as_owned(&self) -> PeerValue {
126        PeerValue {
127            key: self.key.as_owned(),
128            data: Box::from(self.data),
129            expires_at: self.expires_at,
130            signature: Box::new(*self.signature),
131        }
132    }
133}
134
135/// Group-managed value.
136///
137/// See [`MergedValueRef`] for the non-owned version of the struct.
138#[derive(Debug, Clone, PartialEq, Eq, TlRead, TlWrite)]
139#[tl(boxed, id = "dht.mergedValue", scheme = "proto.tl")]
140pub struct MergedValue {
141    /// Key info.
142    pub key: MergedValueKey,
143    /// Any data.
144    pub data: Box<[u8]>,
145    /// Unix timestamp up to which this value is valid.
146    pub expires_at: u32,
147}
148
149/// Group-managed value.
150///
151/// See [`MergedValue`] for the owned version of the struct.
152#[derive(Debug, Clone, PartialEq, Eq, TlRead, TlWrite)]
153#[tl(boxed, id = "dht.mergedValue", scheme = "proto.tl")]
154pub struct MergedValueRef<'tl> {
155    /// Key info.
156    pub key: MergedValueKeyRef<'tl>,
157    /// Any data.
158    pub data: &'tl [u8],
159    /// Unix timestamp up to which this value is valid.
160    pub expires_at: u32,
161}
162
163impl MergedValueRef<'_> {
164    pub fn as_owned(&self) -> MergedValue {
165        MergedValue {
166            key: self.key.as_owned(),
167            data: Box::from(self.data),
168            expires_at: self.expires_at,
169        }
170    }
171}
172
173/// Stored value.
174///
175/// See [`ValueRef`] for the non-owned version of the struct.
176#[derive(Debug, Clone, PartialEq, Eq)]
177pub enum Value {
178    /// Value with a known owner.
179    Peer(PeerValue),
180    /// Group-managed value.
181    Merged(MergedValue),
182}
183
184impl Value {
185    /// Fully verifies the value.
186    ///
187    /// NOTE: Might be expensive since it requires signature verification.
188    pub fn verify(&self, at: u32, key_hash: &[u8; 32]) -> bool {
189        self.verify_ext(at, key_hash, &mut false)
190    }
191
192    /// Fully verifies the value.
193    ///
194    /// NOTE: Might be expensive since it requires signature verification.
195    pub fn verify_ext(&self, at: u32, key_hash: &[u8; 32], signature_checked: &mut bool) -> bool {
196        match self {
197            Self::Peer(value) => {
198                let timings_ok = value.expires_at >= at && key_hash == &tl_proto::hash(&value.key);
199                if !timings_ok {
200                    return false;
201                }
202
203                *signature_checked = true;
204                check_peer_signature(&value.key.peer_id, &value.signature, value)
205            }
206            Self::Merged(value) => {
207                value.expires_at >= at && key_hash == &tl_proto::hash(&value.key)
208            }
209        }
210    }
211
212    pub const fn expires_at(&self) -> u32 {
213        match self {
214            Self::Peer(value) => value.expires_at,
215            Self::Merged(value) => value.expires_at,
216        }
217    }
218}
219
220impl TlWrite for Value {
221    type Repr = tl_proto::Boxed;
222
223    fn max_size_hint(&self) -> usize {
224        match self {
225            Self::Peer(value) => value.max_size_hint(),
226            Self::Merged(value) => value.max_size_hint(),
227        }
228    }
229
230    fn write_to<P>(&self, packet: &mut P)
231    where
232        P: tl_proto::TlPacket,
233    {
234        match self {
235            Self::Peer(value) => value.write_to(packet),
236            Self::Merged(value) => value.write_to(packet),
237        }
238    }
239}
240
241impl<'a> TlRead<'a> for Value {
242    type Repr = tl_proto::Boxed;
243
244    fn read_from(packet: &mut &'a [u8]) -> tl_proto::TlResult<Self> {
245        let id = u32::read_from(&mut std::convert::identity(packet))?;
246        match id {
247            PeerValue::TL_ID => PeerValue::read_from(packet).map(Self::Peer),
248            MergedValue::TL_ID => MergedValue::read_from(packet).map(Self::Merged),
249            _ => Err(tl_proto::TlError::UnknownConstructor),
250        }
251    }
252}
253
254/// Stored value.
255///
256/// See [`Value`] for the owned version of the struct.
257#[derive(Debug, Clone, PartialEq, Eq)]
258pub enum ValueRef<'tl> {
259    /// Value with a known owner.
260    Peer(PeerValueRef<'tl>),
261    /// Group-managed value.
262    Merged(MergedValueRef<'tl>),
263}
264
265impl ValueRef<'_> {
266    pub const fn expires_at(&self) -> u32 {
267        match self {
268            Self::Peer(value) => value.expires_at,
269            Self::Merged(value) => value.expires_at,
270        }
271    }
272}
273
274impl TlWrite for ValueRef<'_> {
275    type Repr = tl_proto::Boxed;
276
277    fn max_size_hint(&self) -> usize {
278        match self {
279            Self::Peer(value) => value.max_size_hint(),
280            Self::Merged(value) => value.max_size_hint(),
281        }
282    }
283
284    fn write_to<P>(&self, packet: &mut P)
285    where
286        P: tl_proto::TlPacket,
287    {
288        match self {
289            Self::Peer(value) => value.write_to(packet),
290            Self::Merged(value) => value.write_to(packet),
291        }
292    }
293}
294
295impl<'a> TlRead<'a> for ValueRef<'a> {
296    type Repr = tl_proto::Boxed;
297
298    fn read_from(packet: &mut &'a [u8]) -> tl_proto::TlResult<Self> {
299        let id = u32::read_from(&mut std::convert::identity(packet))?;
300        match id {
301            PeerValue::TL_ID => PeerValueRef::read_from(packet).map(Self::Peer),
302            MergedValue::TL_ID => MergedValueRef::read_from(packet).map(Self::Merged),
303            _ => Err(tl_proto::TlError::UnknownConstructor),
304        }
305    }
306}
307
308/// A response for the [`rpc::FindNode`] query.
309#[derive(Debug, Clone, TlRead, TlWrite)]
310#[tl(boxed, id = "dht.nodesFound", scheme = "proto.tl")]
311pub struct NodeResponse {
312    /// List of nodes closest to the key.
313    #[tl(with = "tl::VecWithMaxLen::<20>")]
314    pub nodes: Vec<Arc<PeerInfo>>,
315}
316
317/// A response for the [`rpc::FindValue`] query.
318#[derive(Debug, Clone, TlRead, TlWrite)]
319#[tl(boxed, scheme = "proto.tl")]
320pub enum ValueResponse {
321    /// An existing value for the specified key.
322    #[tl(id = "dht.valueFound")]
323    Found(Box<Value>),
324    /// List of nodes closest to the key.
325    #[tl(id = "dht.valueNotFound")]
326    NotFound(#[tl(with = "tl::VecWithMaxLen::<20>")] Vec<Arc<PeerInfo>>),
327}
328
329/// A response for the [`rpc::FindValue`] query.
330#[derive(Debug, Clone)]
331pub enum ValueResponseRaw {
332    Found(Bytes),
333    NotFound(Vec<Arc<PeerInfo>>),
334}
335
336impl TlWrite for ValueResponseRaw {
337    type Repr = tl_proto::Boxed;
338
339    fn max_size_hint(&self) -> usize {
340        4 + match self {
341            Self::Found(value) => value.max_size_hint(),
342            Self::NotFound(nodes) => nodes.max_size_hint(),
343        }
344    }
345
346    fn write_to<P>(&self, packet: &mut P)
347    where
348        P: tl_proto::TlPacket,
349    {
350        const FOUND_TL_ID: u32 = tl_proto::id!("dht.valueFound", scheme = "proto.tl");
351        const NOT_FOUND_TL_ID: u32 = tl_proto::id!("dht.valueNotFound", scheme = "proto.tl");
352
353        match self {
354            Self::Found(value) => {
355                packet.write_u32(FOUND_TL_ID);
356                packet.write_raw_slice(value);
357            }
358            Self::NotFound(nodes) => {
359                packet.write_u32(NOT_FOUND_TL_ID);
360                nodes.write_to(packet);
361            }
362        }
363    }
364}
365
366/// A response for the [`rpc::GetNodeInfo`] query.
367#[derive(Debug, Clone, TlRead, TlWrite)]
368#[tl(boxed, id = "dht.nodeInfoFound", scheme = "proto.tl")]
369pub struct NodeInfoResponse {
370    /// Signed node info.
371    pub info: PeerInfo,
372}
373
374/// DHT RPC models.
375pub mod rpc {
376    use super::*;
377
378    /// Query wrapper with an announced peer info.
379    #[derive(Debug, Clone, TlRead, TlWrite)]
380    #[tl(boxed, id = "dht.withPeerInfo", scheme = "proto.tl")]
381    #[repr(transparent)]
382    pub struct WithPeerInfo {
383        /// A signed info of the sender.
384        pub peer_info: PeerInfo,
385    }
386
387    impl WithPeerInfo {
388        pub fn wrap(value: &'_ PeerInfo) -> &'_ Self {
389            // SAFETY: `rpc::WithPeerInfo` has the same memory layout as `PeerInfo`.
390            unsafe { &*(value as *const PeerInfo).cast() }
391        }
392    }
393
394    /// Suggest a node to store that value.
395    #[derive(Debug, Clone, TlRead, TlWrite)]
396    #[tl(boxed, id = "dht.store", scheme = "proto.tl")]
397    #[repr(transparent)]
398    pub struct Store {
399        /// A value to store.
400        pub value: Value,
401    }
402
403    /// Suggest a node to store that value.
404    #[derive(Debug, Clone, TlRead, TlWrite)]
405    #[tl(boxed, id = "dht.store", scheme = "proto.tl")]
406    #[repr(transparent)]
407    pub struct StoreRef<'tl> {
408        /// A value to store.
409        pub value: ValueRef<'tl>,
410    }
411
412    impl<'tl> StoreRef<'tl> {
413        pub fn wrap<'a>(value: &'a ValueRef<'tl>) -> &'a Self {
414            // SAFETY: `rpc::StoreRef` has the same memory layout as `ValueRef`.
415            unsafe { &*(value as *const ValueRef<'tl>).cast() }
416        }
417    }
418
419    /// Search for `k` the closest nodes.
420    ///
421    /// See [`NodeResponse`].
422    #[derive(Debug, Clone, TlRead, TlWrite)]
423    #[tl(boxed, id = "dht.findNode", scheme = "proto.tl")]
424    pub struct FindNode {
425        /// Key hash.
426        pub key: [u8; 32],
427        /// Maximum number of nodes to return.
428        pub k: u32,
429    }
430
431    /// Search for a value if stored or `k` the closest nodes.
432    ///
433    /// See [`ValueResponse`].
434    #[derive(Debug, Clone, TlRead, TlWrite)]
435    #[tl(boxed, id = "dht.findValue", scheme = "proto.tl")]
436    pub struct FindValue {
437        /// Key hash.
438        pub key: [u8; 32],
439        /// Maximum number of nodes to return.
440        pub k: u32,
441    }
442
443    /// Requests a signed address list from the node.
444    ///
445    /// See [`NodeInfoResponse`].
446    #[derive(Debug, Clone, TlRead, TlWrite)]
447    #[tl(boxed, id = "dht.getNodeInfo", scheme = "proto.tl")]
448    pub struct GetNodeInfo;
449}