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: &'a [u8], offset: &mut usize) -> tl_proto::TlResult<Self> {
245        let id = u32::read_from(packet, offset)?;
246        *offset -= 4;
247        match id {
248            PeerValue::TL_ID => PeerValue::read_from(packet, offset).map(Self::Peer),
249            MergedValue::TL_ID => MergedValue::read_from(packet, offset).map(Self::Merged),
250            _ => Err(tl_proto::TlError::UnknownConstructor),
251        }
252    }
253}
254
255/// Stored value.
256///
257/// See [`Value`] for the owned version of the struct.
258#[derive(Debug, Clone, PartialEq, Eq)]
259pub enum ValueRef<'tl> {
260    /// Value with a known owner.
261    Peer(PeerValueRef<'tl>),
262    /// Group-managed value.
263    Merged(MergedValueRef<'tl>),
264}
265
266impl ValueRef<'_> {
267    pub const fn expires_at(&self) -> u32 {
268        match self {
269            Self::Peer(value) => value.expires_at,
270            Self::Merged(value) => value.expires_at,
271        }
272    }
273}
274
275impl TlWrite for ValueRef<'_> {
276    type Repr = tl_proto::Boxed;
277
278    fn max_size_hint(&self) -> usize {
279        match self {
280            Self::Peer(value) => value.max_size_hint(),
281            Self::Merged(value) => value.max_size_hint(),
282        }
283    }
284
285    fn write_to<P>(&self, packet: &mut P)
286    where
287        P: tl_proto::TlPacket,
288    {
289        match self {
290            Self::Peer(value) => value.write_to(packet),
291            Self::Merged(value) => value.write_to(packet),
292        }
293    }
294}
295
296impl<'a> TlRead<'a> for ValueRef<'a> {
297    type Repr = tl_proto::Boxed;
298
299    fn read_from(packet: &'a [u8], offset: &mut usize) -> tl_proto::TlResult<Self> {
300        let id = u32::read_from(packet, offset)?;
301        *offset -= 4;
302        match id {
303            PeerValue::TL_ID => PeerValueRef::read_from(packet, offset).map(Self::Peer),
304            MergedValue::TL_ID => MergedValueRef::read_from(packet, offset).map(Self::Merged),
305            _ => Err(tl_proto::TlError::UnknownConstructor),
306        }
307    }
308}
309
310/// A response for the [`rpc::FindNode`] query.
311#[derive(Debug, Clone, TlRead, TlWrite)]
312#[tl(boxed, id = "dht.nodesFound", scheme = "proto.tl")]
313pub struct NodeResponse {
314    /// List of nodes closest to the key.
315    #[tl(with = "tl::VecWithMaxLen::<20>")]
316    pub nodes: Vec<Arc<PeerInfo>>,
317}
318
319/// A response for the [`rpc::FindValue`] query.
320#[derive(Debug, Clone, TlRead, TlWrite)]
321#[tl(boxed, scheme = "proto.tl")]
322pub enum ValueResponse {
323    /// An existing value for the specified key.
324    #[tl(id = "dht.valueFound")]
325    Found(Box<Value>),
326    /// List of nodes closest to the key.
327    #[tl(id = "dht.valueNotFound")]
328    NotFound(#[tl(with = "tl::VecWithMaxLen::<20>")] Vec<Arc<PeerInfo>>),
329}
330
331/// A response for the [`rpc::FindValue`] query.
332#[derive(Debug, Clone)]
333pub enum ValueResponseRaw {
334    Found(Bytes),
335    NotFound(Vec<Arc<PeerInfo>>),
336}
337
338impl TlWrite for ValueResponseRaw {
339    type Repr = tl_proto::Boxed;
340
341    fn max_size_hint(&self) -> usize {
342        4 + match self {
343            Self::Found(value) => value.max_size_hint(),
344            Self::NotFound(nodes) => nodes.max_size_hint(),
345        }
346    }
347
348    fn write_to<P>(&self, packet: &mut P)
349    where
350        P: tl_proto::TlPacket,
351    {
352        const FOUND_TL_ID: u32 = tl_proto::id!("dht.valueFound", scheme = "proto.tl");
353        const NOT_FOUND_TL_ID: u32 = tl_proto::id!("dht.valueNotFound", scheme = "proto.tl");
354
355        match self {
356            Self::Found(value) => {
357                packet.write_u32(FOUND_TL_ID);
358                packet.write_raw_slice(value);
359            }
360            Self::NotFound(nodes) => {
361                packet.write_u32(NOT_FOUND_TL_ID);
362                nodes.write_to(packet);
363            }
364        }
365    }
366}
367
368/// A response for the [`rpc::GetNodeInfo`] query.
369#[derive(Debug, Clone, TlRead, TlWrite)]
370#[tl(boxed, id = "dht.nodeInfoFound", scheme = "proto.tl")]
371pub struct NodeInfoResponse {
372    /// Signed node info.
373    pub info: PeerInfo,
374}
375
376/// DHT RPC models.
377pub mod rpc {
378    use super::*;
379
380    /// Query wrapper with an announced peer info.
381    #[derive(Debug, Clone, TlRead, TlWrite)]
382    #[tl(boxed, id = "dht.withPeerInfo", scheme = "proto.tl")]
383    #[repr(transparent)]
384    pub struct WithPeerInfo {
385        /// A signed info of the sender.
386        pub peer_info: PeerInfo,
387    }
388
389    impl WithPeerInfo {
390        pub fn wrap(value: &'_ PeerInfo) -> &'_ Self {
391            // SAFETY: `rpc::WithPeerInfo` has the same memory layout as `PeerInfo`.
392            unsafe { &*(value as *const PeerInfo).cast() }
393        }
394    }
395
396    /// Suggest a node to store that value.
397    #[derive(Debug, Clone, TlRead, TlWrite)]
398    #[tl(boxed, id = "dht.store", scheme = "proto.tl")]
399    #[repr(transparent)]
400    pub struct Store {
401        /// A value to store.
402        pub value: Value,
403    }
404
405    /// Suggest a node to store that value.
406    #[derive(Debug, Clone, TlRead, TlWrite)]
407    #[tl(boxed, id = "dht.store", scheme = "proto.tl")]
408    #[repr(transparent)]
409    pub struct StoreRef<'tl> {
410        /// A value to store.
411        pub value: ValueRef<'tl>,
412    }
413
414    impl<'tl> StoreRef<'tl> {
415        pub fn wrap<'a>(value: &'a ValueRef<'tl>) -> &'a Self {
416            // SAFETY: `rpc::StoreRef` has the same memory layout as `ValueRef`.
417            unsafe { &*(value as *const ValueRef<'tl>).cast() }
418        }
419    }
420
421    /// Search for `k` the closest nodes.
422    ///
423    /// See [`NodeResponse`].
424    #[derive(Debug, Clone, TlRead, TlWrite)]
425    #[tl(boxed, id = "dht.findNode", scheme = "proto.tl")]
426    pub struct FindNode {
427        /// Key hash.
428        pub key: [u8; 32],
429        /// Maximum number of nodes to return.
430        pub k: u32,
431    }
432
433    /// Search for a value if stored or `k` the closest nodes.
434    ///
435    /// See [`ValueResponse`].
436    #[derive(Debug, Clone, TlRead, TlWrite)]
437    #[tl(boxed, id = "dht.findValue", scheme = "proto.tl")]
438    pub struct FindValue {
439        /// Key hash.
440        pub key: [u8; 32],
441        /// Maximum number of nodes to return.
442        pub k: u32,
443    }
444
445    /// Requests a signed address list from the node.
446    ///
447    /// See [`NodeInfoResponse`].
448    #[derive(Debug, Clone, TlRead, TlWrite)]
449    #[tl(boxed, id = "dht.getNodeInfo", scheme = "proto.tl")]
450    pub struct GetNodeInfo;
451}