Skip to main content

iroh_dht_experiment/
lib.rs

1//! # Minimal DHT for iroh
2//!
3//! This follows a lot of the design decisions of the bittorrent mainline DHT,
4//! with two major differences:
5//! - we use BLAKE3 instead of SHA1, and therefore extend the keyspace to 32
6//!   bytes. 32 bytes is also a more natural fit for other purposes such as
7//!   storage of data for ED25519 keys like in [bep_0044]
8//! - connections are not raw UDP but iroh connections, with use of [0rtt] to make
9//!   the DHT typical tiny interactions faster.
10//!
11//! Other than that this is a pretty straightforward [kademlia] implementation,
12//! using the XOR metric and standard routing tables.
13//!
14//! A DHT is two things:
15//!
16//! ## Data storage
17//!
18//! A multimap of keys to values, where values are self contained pieces of data
19//! that have some way to be verified and have some relationship to the key.
20//!
21//! Values should have some kind of expiry mechanism, but they don't need
22//! provenance since they are self-contained.
23//!
24//! The storage part of the DHT is basically a standalone tracker, except for
25//! the fact that it will reject set requests that are obviously far away
26//! from the node id in terms of the DHT metric.
27//!
28//! Disabling the latter mechanism would allow a single DHT node to act as a
29//! tracker.
30//!
31//! Examples of values:
32//!
33//! - Provider node ids for a key, where the key is interpreted as a BLAKE3 hash
34//!   of some data. Expiry is a timestamp, validation is checking that the node
35//!   has the data by means of a BLAKE3 probe. This is equivalent to the main
36//!   purpose of mainline outlined in [bep_0005].
37//!
38//! - A signed message, e.g. a pkarr record, where the key is interpreted as
39//!   the public key of the signer. Expiry is a timestamp, validation is
40//!   checking the signature against the public key. Almost identical to [bep_0044],
41//!   except that we don't have to hash the key because it fits our keyspace.
42//!
43//! - Self-contained immutable data, where the key is interpreted as a BLAKE3
44//!   hash of the data. Expiry is a timestamp, validation is checking that the
45//!   data matches the hash. This is also part of [bep_0044].
46//!
47//! We use [postcard] on the wire and most likely also on disk.
48//!
49//! ## Routing
50//!
51//! A way to find the n most natural locations for a given key. Routing is only
52//! concerned with the key, not the value.
53//!
54//! DHT nodes talk to each other using a simple [rpc] protocol. RPC requests can
55//! always be answered using purely local information.
56//!
57//! A DHT node is controlled using the [api] protocol, which contains higher
58//! level operations that trigger interactions with multiple DHT nodes.
59//!
60//! Both protocols are implemented using the [irpc] crate. As an intro to irpc,
61//! see the [irpc blog post].
62//!
63//! [bep_0044]: https://www.bittorrent.org/beps/bep_0044.html
64//! [bep_0005]: https://www.bittorrent.org/beps/bep_0005.html
65//! [kademlia]: https://pdos.csail.mit.edu/~petar/papers/maymounkov-kademlia-lncs.pdf
66//! [postcard]: https://postcard.jamesmunns.com/
67//! [0rtt]: https://www.iroh.computer/blog/0rtt-api
68//! [irpc]: https://docs.rs/irpc/latest/irpc/
69//! [irpc blog post]: https://www.iroh.computer/blog/irpc/
70use std::{
71    collections::{BTreeMap, BTreeSet, HashSet, VecDeque},
72    sync::Arc,
73    time::{Duration, UNIX_EPOCH},
74};
75
76use futures_buffered::FuturesUnordered;
77use indexmap::IndexSet;
78use iroh::NodeId;
79use irpc::channel::{mpsc, oneshot};
80use n0_future::{BufferedStreamExt, MaybeFuture, StreamExt, stream};
81use rand::{Rng, SeedableRng, rngs::StdRng, seq::index::sample};
82use serde::{Deserialize, Serialize};
83use tokio::task::JoinSet;
84#[cfg(test)]
85mod tests;
86pub mod rpc {
87    //! RPC protocol between DHT nodes.
88    //!
89    //! These are low level operations that only affect the node being called.
90    //! E.g. finding closest nodes for a node based on the current content of
91    //! the routing table, as well as storing and retrieving values from the
92    //! node itself.
93    //!
94    //! The protocol is defined in [`RpcProto`], which has a corresponding full
95    //! message type [`RpcMessage`].
96    //!
97    //! The entry point is [`RpcClient`].
98    use std::{
99        fmt,
100        num::NonZeroU64,
101        ops::Deref,
102        sync::{Arc, Weak},
103    };
104
105    use iroh::{Endpoint, NodeAddr, NodeId, PublicKey};
106    use iroh_base::SignatureError;
107    use irpc::{
108        channel::{mpsc, oneshot},
109        rpc_requests,
110    };
111    use serde::{Deserialize, Serialize};
112    use serde_big_array::BigArray;
113
114    pub const ALPN: &[u8] = b"iroh/dht/0";
115
116    /// Entry type for BLAKE3 content discovery.
117    ///
118    /// Provides a similar functionality to BEP-5 in mainline, but for BLAKE3
119    /// hashes instead of SHA-1 hashes.
120    #[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
121    pub struct Blake3Provider {
122        timestamp: u64, // Unix timestamp for expiry
123        node_id: [u8; 32],
124    }
125
126    /// Small immutable value.
127    #[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
128    pub struct Blake3Immutable {
129        pub timestamp: u64, // Unix timestamp for expiry
130        pub data: Vec<u8>,
131    }
132
133    /// Entry type for signed messages, e.g. pkarr records, for node discovery.
134    ///
135    /// Provides a similar functionality BEP-44 in mainline.
136    #[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
137    pub struct ED25519SignedMessage {
138        /// Unix timestamp for expiry
139        pub timestamp: u64,
140        /// A 64-byte signature using Ed25519
141        #[serde(with = "BigArray")]
142        pub signature: [u8; 64],
143        /// The signed message data. This must be <= 1024 bytes so an entire
144        /// set request fits a single non-fragmented UDP packet even with QUIC
145        /// overhead.
146        pub data: Vec<u8>,
147    }
148
149    /// DHT value type.
150    ///
151    /// The order of the enum is important for serialization/deserialization
152    #[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
153    pub enum Value {
154        Blake3Provider(Blake3Provider),
155        ED25519SignedMessage(ED25519SignedMessage),
156        Blake3Immutable(Blake3Immutable),
157    }
158
159    impl Value {
160        /// Returns the kind of this value.
161        pub fn kind(&self) -> Kind {
162            match self {
163                Value::Blake3Provider(_) => Kind::Blake3Provider,
164                Value::ED25519SignedMessage(_) => Kind::ED25519SignedMessage,
165                Value::Blake3Immutable(_) => Kind::Blake3Immutable,
166            }
167        }
168    }
169
170    /// DHT value kind type.
171    ///
172    /// Must have the same order as [`Value`] for serialization/deserialization
173    #[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
174    pub enum Kind {
175        Blake3Provider,
176        ED25519SignedMessage,
177        Blake3Immutable,
178    }
179
180    /// We use a 32 byte keyspace so we can represent things like modern hashes
181    /// and public keys without having to map them to a smaller keyspace.
182    #[derive(Clone, Copy, Ord, PartialOrd, PartialEq, Eq, Hash, Serialize, Deserialize)]
183    pub struct Id([u8; 32]); // 256-bit identifier
184
185    impl From<[u8; 32]> for Id {
186        fn from(bytes: [u8; 32]) -> Self {
187            Id(bytes)
188        }
189    }
190
191    impl Deref for Id {
192        type Target = [u8; 32];
193
194        fn deref(&self) -> &Self::Target {
195            &self.0
196        }
197    }
198
199    impl fmt::Debug for Id {
200        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
201            write!(f, "Id({})", hex::encode(self.0))
202        }
203    }
204
205    impl fmt::Display for Id {
206        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
207            write!(f, "{}", hex::encode(self.0))
208        }
209    }
210
211    impl From<PublicKey> for Id {
212        fn from(pk: PublicKey) -> Self {
213            Id(*pk.as_bytes())
214        }
215    }
216
217    impl From<blake3::Hash> for Id {
218        fn from(pk: blake3::Hash) -> Self {
219            Id(*pk.as_bytes())
220        }
221    }
222
223    impl Id {
224        pub fn blake3_hash(data: &[u8]) -> Self {
225            let hash = blake3::hash(data);
226            Id(hash.into())
227        }
228
229        pub fn node_id(id: iroh::NodeId) -> Self {
230            Id::from(*id.as_bytes())
231        }
232    }
233
234    /// Set a key to a value.
235    ///
236    /// The storage is allowed to reject set requests if the key is far away from
237    /// the node id in terms of the DHT metric, or if the value is invalid.
238    ///
239    /// The storage is also allowed to drop values at any time. This is not a
240    /// command but more a request to store the value.
241    #[derive(Debug, Serialize, Deserialize)]
242    pub struct Set {
243        /// The key to set the value for.
244        pub key: Id,
245        /// The value being set.
246        pub value: Value,
247    }
248
249    #[derive(Debug, Serialize, Deserialize)]
250    pub enum SetResponse {
251        /// The set request was successful.
252        Ok,
253        /// The key was too far away from the node id in terms of the DHT metric.
254        ErrDistance,
255        /// The value is too old.
256        ErrExpired,
257        /// The node does not have capacity to store the value.
258        ErrFull,
259        /// The value is invalid, e.g. the signature does not match the public key.
260        ErrInvalid,
261    }
262
263    /// Get all values of a certain kind for a key, as a stream.
264    #[derive(Debug, Serialize, Deserialize)]
265    pub struct GetAll {
266        /// The key to get the values for.
267        pub key: Id,
268        /// The kind of value to get.
269        pub kind: Kind,
270        /// Optional seed for randomization of the returned stream of values.
271        /// If this is not provided, items will be returned in an unspecified order.
272        pub seed: Option<NonZeroU64>,
273        /// Number of values to return, if specified. If not specified, all values
274        /// of the specified kind for the key will be returned until the receiver
275        /// stops or the stream ends.
276        pub n: Option<NonZeroU64>,
277    }
278
279    /// A request to query the routing table for the most natural locations
280    #[derive(Debug, Serialize, Deserialize)]
281    pub struct FindNode {
282        /// The key to find the most natural locations (nodes) for.
283        pub id: Id,
284        /// The requester wants to be included in the routing table.
285        ///
286        /// For the irpc memory or quinn transport, you have to just believe this value.
287        /// For the irpc iroh transport, this must be the node ID of the requester.
288        pub requester: Option<NodeId>,
289    }
290
291    /// Protocol for rpc communication.
292    ///
293    /// Note: you might wonder why there is no ping message. To do a basic
294    /// liveness check, just do a FindNode with a random ID. It should be just
295    /// as cheap in terms of roundtrips, and already gives you some useful
296    /// information.
297    #[rpc_requests(message = RpcMessage)]
298    #[derive(Debug, Serialize, Deserialize)]
299    pub enum RpcProto {
300        /// Set a key to a value.
301        #[rpc(tx = oneshot::Sender<SetResponse>)]
302        Set(Set),
303        /// Get all values of a certain kind for a key, as a stream of values.
304        #[rpc(tx = mpsc::Sender<Value>)]
305        GetAll(GetAll),
306        /// A request to query the routing table for the most natural locations
307        #[rpc(tx = oneshot::Sender<Vec<NodeAddr>>)]
308        FindNode(FindNode),
309    }
310
311    #[derive(Debug, Clone)]
312    pub struct RpcClient(pub(crate) Arc<irpc::Client<RpcProto>>);
313
314    #[derive(Debug, Clone)]
315    pub struct WeakRpcClient(pub(crate) Weak<irpc::Client<RpcProto>>);
316
317    impl WeakRpcClient {
318        pub fn upgrade(&self) -> Option<RpcClient> {
319            self.0.upgrade().map(RpcClient)
320        }
321    }
322
323    impl RpcClient {
324        pub fn remote(endpoint: Endpoint, id: Id) -> std::result::Result<Self, SignatureError> {
325            let id = iroh::NodeId::from_bytes(&id)?;
326            let client = irpc_iroh::client(endpoint, id, ALPN);
327            Ok(Self::new(client))
328        }
329
330        pub fn new(client: irpc::Client<RpcProto>) -> Self {
331            Self(Arc::new(client))
332        }
333
334        pub async fn set(&self, key: Id, value: Value) -> irpc::Result<SetResponse> {
335            self.0.rpc(Set { key, value }).await
336        }
337
338        pub async fn get_all(
339            &self,
340            key: Id,
341            kind: Kind,
342            seed: Option<NonZeroU64>,
343            n: Option<NonZeroU64>,
344        ) -> irpc::Result<irpc::channel::mpsc::Receiver<Value>> {
345            self.0
346                .server_streaming(GetAll { key, kind, seed, n }, 32)
347                .await
348        }
349
350        pub async fn find_node(
351            &self,
352            id: Id,
353            requester: Option<NodeId>,
354        ) -> irpc::Result<Vec<NodeAddr>> {
355            self.0.rpc(FindNode { id, requester }).await
356        }
357
358        pub fn downgrade(&self) -> WeakRpcClient {
359            WeakRpcClient(Arc::downgrade(&self.0))
360        }
361    }
362}
363
364pub mod api {
365    //! RPC protocol for an user to talk to a DHT node.
366    //!
367    //! These are operations that affect the entire network, such as storing or retrieving a value.
368    //!
369    //! The protocol is defined in [`ApiProto`], which has a corresponding full
370    //! message type [`ApiMessage`].
371    //!
372    //! The entry point is [`ApiClient`].
373    use std::{
374        collections::BTreeMap,
375        num::NonZeroU64,
376        sync::{Arc, Weak},
377        time::Duration,
378    };
379
380    use iroh::NodeId;
381    use irpc::{
382        channel::{mpsc, none::NoSender, oneshot},
383        rpc_requests,
384    };
385    use serde::{Deserialize, Serialize};
386
387    use crate::{
388        now,
389        routing::NodeInfo,
390        rpc::{Blake3Immutable, Id, Kind, Value},
391    };
392
393    #[rpc_requests(message = ApiMessage)]
394    #[derive(Debug, Serialize, Deserialize)]
395    pub enum ApiProto {
396        /// NodesSeen can only be called for node ids, but we don't bother
397        /// to provide AddrInfo here since the routing table does not store it.
398        #[rpc(tx = NoSender)]
399        #[wrap(NodesSeen)]
400        NodesSeen { ids: Vec<NodeId> },
401        /// NodesDead can only be called for node ids, but we don't bother
402        /// to provide AddrInfo here since the routing table does not store it.
403        #[rpc(tx = NoSender)]
404        #[wrap(NodesDead)]
405        NodesDead { ids: Vec<NodeId> },
406        #[rpc(tx = oneshot::Sender<Vec<NodeId>>)]
407        #[wrap(Lookup)]
408        Lookup {
409            initial: Option<Vec<NodeId>>,
410            id: Id,
411        },
412        #[rpc(tx = mpsc::Sender<NodeId>)]
413        #[wrap(NetworkPut)]
414        NetworkPut { id: Id, value: Value },
415        #[rpc(tx = mpsc::Sender<(NodeId, Value)>)]
416        #[wrap(NetworkGet)]
417        NetworkGet {
418            id: Id,
419            kind: Kind,
420            seed: Option<NonZeroU64>,
421            n: Option<NonZeroU64>,
422        },
423        /// Get the routing table for testing
424        #[rpc(tx = oneshot::Sender<Vec<Vec<NodeInfo>>>)]
425        #[wrap(GetRoutingTable)]
426        GetRoutingTable,
427        /// Get storage stats for testing
428        #[rpc(tx = oneshot::Sender<BTreeMap<Id, BTreeMap<Kind, usize>>>)]
429        #[wrap(GetStorageStats)]
430        GetStorageStats,
431        /// Perform a self lookup
432        #[rpc(tx = oneshot::Sender<()>)]
433        #[wrap(SelfLookup)]
434        SelfLookup,
435        /// Perform a random lookup
436        #[rpc(tx = oneshot::Sender<()>)]
437        #[wrap(RandomLookup)]
438        RandomLookup,
439        /// Perform a candidate lookup
440        #[rpc(tx = oneshot::Sender<()>)]
441        #[wrap(CandidateLookup)]
442        CandidateLookup,
443    }
444
445    #[derive(Debug, Clone)]
446    pub struct ApiClient(pub(crate) Arc<irpc::Client<ApiProto>>);
447
448    impl ApiClient {
449        /// notify the node that we have just seen these nodes.
450        ///
451        /// The impl should add these nodes to the routing table.
452        pub async fn nodes_seen(&self, ids: &[NodeId]) -> irpc::Result<()> {
453            self.0.notify(NodesSeen { ids: ids.to_vec() }).await
454        }
455
456        /// notify the node that we have tried to contact these nodes and have not gotten a response.
457        ///
458        /// The impl can either clean these nodes from its routing table immediately or after a repeat offense.
459        pub async fn nodes_dead(&self, ids: &[NodeId]) -> irpc::Result<()> {
460            self.0.notify(NodesDead { ids: ids.to_vec() }).await
461        }
462
463        pub async fn get_storage_stats(&self) -> irpc::Result<BTreeMap<Id, BTreeMap<Kind, usize>>> {
464            self.0.rpc(GetStorageStats).await
465        }
466
467        pub async fn get_routing_table(&self) -> irpc::Result<Vec<Vec<NodeInfo>>> {
468            self.0.rpc(GetRoutingTable).await
469        }
470
471        pub async fn lookup(
472            &self,
473            id: Id,
474            initial: Option<Vec<NodeId>>,
475        ) -> irpc::Result<Vec<NodeId>> {
476            self.0.rpc(Lookup { id, initial }).await
477        }
478
479        pub async fn get_immutable(&self, hash: blake3::Hash) -> irpc::Result<Option<Vec<u8>>> {
480            let id = Id::from(*hash.as_bytes());
481            let mut rx = self
482                .0
483                .server_streaming(
484                    NetworkGet {
485                        id,
486                        kind: Kind::Blake3Immutable,
487                        seed: None,
488                        n: Some(NonZeroU64::new(1).unwrap()),
489                    },
490                    32,
491                )
492                .await?;
493            loop {
494                match rx.recv().await {
495                    Ok(Some((_, value))) => {
496                        let Value::Blake3Immutable(Blake3Immutable { data, .. }) = value else {
497                            continue; // Skip non-Blake3Immutable values
498                        };
499                        if blake3::hash(&data) == hash {
500                            return Ok(Some(data));
501                        } else {
502                            continue; // Hash mismatch, skip this value
503                        }
504                    }
505                    Ok(None) => {
506                        break Ok(None);
507                    }
508                    Err(e) => {
509                        break Err(e.into());
510                    }
511                }
512            }
513        }
514
515        pub async fn put_immutable(
516            &self,
517            value: &[u8],
518        ) -> irpc::Result<(blake3::Hash, Vec<NodeId>)> {
519            let hash = blake3::hash(value);
520            let id = Id::from(*hash.as_bytes());
521            let mut rx = self
522                .0
523                .server_streaming(
524                    NetworkPut {
525                        id,
526                        value: Value::Blake3Immutable(Blake3Immutable {
527                            timestamp: now(),
528                            data: value.to_vec(),
529                        }),
530                    },
531                    32,
532                )
533                .await?;
534            let mut res = Vec::new();
535            loop {
536                match rx.recv().await {
537                    Ok(Some(id)) => res.push(id),
538                    Ok(None) => break,
539                    Err(_) => {}
540                }
541            }
542            Ok((hash, res))
543        }
544
545        pub async fn self_lookup(&self) {
546            self.0.rpc(SelfLookup).await.ok();
547        }
548
549        pub async fn random_lookup(&self) {
550            self.0.rpc(RandomLookup).await.ok();
551        }
552
553        pub async fn candidate_lookup(&self) {
554            self.0.rpc(CandidateLookup).await.ok();
555        }
556
557        pub fn downgrade(&self) -> WeakApiClient {
558            WeakApiClient(Arc::downgrade(&self.0))
559        }
560    }
561
562    #[derive(Debug, Clone)]
563    pub struct WeakApiClient(pub(crate) Weak<irpc::Client<ApiProto>>);
564
565    impl WeakApiClient {
566        pub fn upgrade(&self) -> irpc::Result<ApiClient> {
567            self.0
568                .upgrade()
569                .map(ApiClient)
570                .ok_or(irpc::Error::Send(irpc::channel::SendError::ReceiverClosed))
571        }
572
573        pub async fn nodes_dead(&self, ids: &[NodeId]) -> irpc::Result<()> {
574            self.upgrade()?.nodes_dead(ids).await
575        }
576
577        pub async fn nodes_seen(&self, ids: &[NodeId]) -> irpc::Result<()> {
578            self.upgrade()?.nodes_seen(ids).await
579        }
580
581        pub(crate) async fn self_lookup_periodic(self, interval: Duration) {
582            loop {
583                tokio::time::sleep(interval).await;
584                let Ok(api) = self.upgrade() else {
585                    return;
586                };
587                api.self_lookup().await;
588            }
589        }
590
591        pub(crate) async fn random_lookup_periodic(self, interval: Duration) {
592            loop {
593                tokio::time::sleep(interval).await;
594                let Ok(api) = self.upgrade() else {
595                    return;
596                };
597                api.random_lookup().await;
598            }
599        }
600
601        pub(crate) async fn candidate_lookup_periodic(self, interval: Duration) {
602            loop {
603                tokio::time::sleep(interval).await;
604                let Ok(api) = self.upgrade() else {
605                    return;
606                };
607                api.candidate_lookup().await;
608            }
609        }
610    }
611}
612pub use api::ApiClient;
613use tracing::{error, info, warn};
614
615mod routing {
616    use std::{
617        fmt,
618        ops::{Index, IndexMut},
619    };
620
621    use arrayvec::ArrayVec;
622    use iroh::NodeId;
623    use serde::{Deserialize, Serialize};
624
625    use super::rpc::Id;
626
627    pub const K: usize = 20; // Bucket size
628    pub const ALPHA: usize = 3; // Concurrency parameter
629    pub const BUCKET_COUNT: usize = 256; // For 256-bit keys
630
631    /// Calculate XOR distance between two 32-byte values
632    fn xor(a: &[u8; 32], b: &[u8; 32]) -> [u8; 32] {
633        let mut result = [0u8; 32];
634        for i in 0..32 {
635            result[i] = a[i] ^ b[i];
636        }
637        result
638    }
639
640    /// Count leading zero bits in a 32-byte array
641    fn leading_zeros(data: &[u8; 32]) -> usize {
642        for (byte_idx, &byte) in data.iter().enumerate() {
643            if byte != 0 {
644                return byte_idx * 8 + byte.leading_zeros() as usize;
645            }
646        }
647        256 // All zeros
648    }
649
650    #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
651    pub struct Distance([u8; 32]);
652
653    impl Distance {
654        pub fn between(a: &[u8; 32], b: &[u8; 32]) -> Self {
655            Self(xor(a, b))
656        }
657
658        /// This is the inverse of between.
659        ///
660        /// Distance::between(&x, &y).to_node(&y) == x
661        pub fn inverse(&self, b: &[u8; 32]) -> [u8; 32] {
662            xor(&self.0, b)
663        }
664
665        pub const MAX: Self = Self([u8::MAX; 32]);
666    }
667
668    impl fmt::Debug for Distance {
669        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
670            write!(f, "Distance({self})")
671        }
672    }
673
674    impl fmt::Display for Distance {
675        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
676            write!(f, "{}", hex::encode(self.0))
677        }
678    }
679
680    #[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
681    pub struct NodeInfo {
682        pub id: NodeId,
683        pub last_seen: u64,
684    }
685
686    impl NodeInfo {
687        pub fn new(id: NodeId, last_seen: u64) -> Self {
688            Self { id, last_seen }
689        }
690    }
691
692    #[derive(Debug, Clone, Default)]
693    pub struct KBucket {
694        nodes: ArrayVec<NodeInfo, K>,
695    }
696
697    impl KBucket {
698        fn new() -> Self {
699            Self {
700                nodes: ArrayVec::new(),
701            }
702        }
703
704        pub fn add_node(&mut self, node: NodeInfo) -> bool {
705            // Check if node already exists and update it
706            for existing in &mut self.nodes {
707                if existing.id == node.id {
708                    existing.last_seen = node.last_seen;
709                    return true; // Updated existing node
710                }
711            }
712
713            // Add new node if space available
714            if self.nodes.len() < K {
715                self.nodes.push(node);
716                return true;
717            }
718
719            false // Bucket full
720        }
721
722        fn remove_node(&mut self, id: &NodeId) {
723            self.nodes.retain(|n| n.id != *id);
724        }
725
726        pub fn nodes(&self) -> &[NodeInfo] {
727            &self.nodes
728        }
729    }
730
731    #[derive(Debug)]
732    pub struct RoutingTable {
733        pub buckets: Box<Buckets>,
734        pub local_id: NodeId,
735    }
736
737    #[derive(Debug, Clone)]
738    pub struct Buckets(pub [KBucket; BUCKET_COUNT]);
739
740    impl Buckets {
741        pub fn iter(&self) -> std::slice::Iter<'_, KBucket> {
742            self.0.iter()
743        }
744    }
745
746    impl Index<usize> for Buckets {
747        type Output = KBucket;
748        fn index(&self, index: usize) -> &Self::Output {
749            &self.0[index]
750        }
751    }
752
753    impl IndexMut<usize> for Buckets {
754        fn index_mut(&mut self, index: usize) -> &mut Self::Output {
755            &mut self.0[index]
756        }
757    }
758
759    impl Default for Buckets {
760        fn default() -> Self {
761            Self(std::array::from_fn(|_| KBucket::new()))
762        }
763    }
764
765    impl RoutingTable {
766        pub fn new(local_id: NodeId, buckets: Option<Box<Buckets>>) -> Self {
767            let buckets = buckets
768                .map(|mut buckets| {
769                    for bucket in buckets.0.iter_mut() {
770                        bucket.nodes.retain(|n| n.id != local_id);
771                    }
772                    buckets
773                })
774                .unwrap_or_default();
775            Self { buckets, local_id }
776        }
777
778        fn bucket_index(&self, target: &[u8; 32]) -> usize {
779            let distance = xor(self.local_id.as_bytes(), target);
780            let zeros = leading_zeros(&distance);
781            if zeros >= BUCKET_COUNT {
782                0 // Same node case
783            } else {
784                BUCKET_COUNT - 1 - zeros
785            }
786        }
787
788        pub(crate) fn contains(&self, id: &NodeId) -> bool {
789            let bucket_idx = self.bucket_index(id.as_bytes());
790            self.buckets[bucket_idx]
791                .nodes()
792                .iter()
793                .any(|node| node.id == *id)
794        }
795
796        pub fn add_node(&mut self, node: NodeInfo) -> bool {
797            if node.id == self.local_id {
798                return false;
799            }
800
801            let bucket_idx = self.bucket_index(node.id.as_bytes());
802            self.buckets[bucket_idx].add_node(node)
803        }
804
805        pub(crate) fn remove_node(&mut self, id: &NodeId) {
806            let bucket_idx = self.bucket_index(id.as_bytes());
807            self.buckets[bucket_idx].remove_node(id);
808        }
809
810        pub fn nodes(&self) -> impl Iterator<Item = &NodeInfo> {
811            self.buckets.iter().flat_map(|bucket| bucket.nodes())
812        }
813
814        pub fn find_closest_nodes(&self, target: &Id, k: usize) -> Vec<NodeId> {
815            // this does a brute force scan, but even so it should be very fast.
816            // xor is basically free, and comparing distances as well.
817            // so the most expensive thing is probably the memory allocation.
818            //
819            // for a full routing table, this would be 256*20*32 = 163840 bytes.
820            let mut candidates = Vec::with_capacity(self.nodes().count());
821            candidates.extend(
822                self.nodes()
823                    .map(|node| Distance::between(target, node.id.as_bytes())),
824            );
825            if k < candidates.len() {
826                candidates.select_nth_unstable(k - 1);
827                candidates.truncate(k);
828            }
829            candidates.sort_unstable();
830
831            candidates
832                .into_iter()
833                .map(|dist| {
834                    NodeId::from_bytes(&dist.inverse(target))
835                        .expect("inverse called with different target than between")
836                })
837                .collect()
838        }
839    }
840}
841
842#[doc(hidden)]
843pub mod bench_exports {
844    pub use crate::{
845        routing::{Buckets, KBucket, NodeInfo, RoutingTable},
846        rpc::Id,
847    };
848}
849
850use crate::{
851    api::{ApiMessage, Lookup, NetworkGet, NetworkPut, WeakApiClient},
852    pool::ClientPool,
853    routing::{ALPHA, BUCKET_COUNT, Buckets, Distance, K, NodeInfo, RoutingTable},
854    rpc::{Id, Kind, RpcClient, RpcMessage, SetResponse, Value},
855    u256::U256,
856};
857
858struct Node {
859    routing_table: RoutingTable,
860    storage: MemStorage,
861}
862
863impl Node {
864    fn id(&self) -> &NodeId {
865        &self.routing_table.local_id
866    }
867}
868
869struct MemStorage {
870    /// The DHT data storage, mapping keys to values.
871    /// Separated by kind to allow for efficient retrieval.
872    data: BTreeMap<Id, BTreeMap<Kind, IndexSet<Value>>>,
873}
874
875impl MemStorage {
876    fn new() -> Self {
877        Self {
878            data: BTreeMap::new(),
879        }
880    }
881
882    /// Set a value for a key.
883    fn set(&mut self, key: Id, value: Value) {
884        let kind = value.kind();
885        self.data
886            .entry(key)
887            .or_default()
888            .entry(kind)
889            .or_default()
890            .insert(value);
891    }
892
893    /// Get all values of a certain kind for a key.
894    fn get_all(&self, key: &Id, kind: &Kind) -> Option<&IndexSet<Value>> {
895        self.data.get(key).and_then(|kinds| kinds.get(kind))
896    }
897}
898
899mod u256 {
900    #![allow(clippy::needless_range_loop)]
901    use std::ops::{BitAnd, BitOr, BitXor, Deref, Not, Shl, Shr};
902
903    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
904    pub struct U256([u8; 32]);
905
906    impl Deref for U256 {
907        type Target = [u8; 32];
908
909        fn deref(&self) -> &Self::Target {
910            &self.0
911        }
912    }
913
914    impl U256 {
915        /// Minimum value (all zeros)
916        pub const MIN: U256 = U256([0u8; 32]);
917
918        /// Maximum value (all ones)
919        pub const MAX: U256 = U256([0xffu8; 32]);
920
921        /// Create a new U256 from a byte array (little-endian)
922        pub fn from_le_bytes(bytes: [u8; 32]) -> Self {
923            U256(bytes)
924        }
925
926        /// Get the underlying byte array (little-endian)
927        #[allow(clippy::wrong_self_convention)]
928        pub fn to_le_bytes(&self) -> [u8; 32] {
929            self.0
930        }
931
932        /// Get the number of leading zeros
933        #[allow(dead_code)]
934        pub fn leading_zeros(&self) -> u32 {
935            let mut count = 0;
936            for &byte in self.0.iter().rev() {
937                if byte == 0 {
938                    count += 8;
939                } else {
940                    count += byte.leading_zeros();
941                    break;
942                }
943            }
944            count
945        }
946    }
947
948    // Bitwise XOR
949    impl BitXor for U256 {
950        type Output = Self;
951
952        fn bitxor(self, rhs: Self) -> Self::Output {
953            let mut result = [0u8; 32];
954            for i in 0..32 {
955                result[i] = self.0[i] ^ rhs.0[i];
956            }
957            U256(result)
958        }
959    }
960
961    // Bitwise AND
962    impl BitAnd for U256 {
963        type Output = Self;
964
965        fn bitand(self, rhs: Self) -> Self::Output {
966            let mut result = [0u8; 32];
967            for i in 0..32 {
968                result[i] = self.0[i] & rhs.0[i];
969            }
970            U256(result)
971        }
972    }
973
974    // Bitwise OR
975    impl BitOr for U256 {
976        type Output = Self;
977
978        fn bitor(self, rhs: Self) -> Self::Output {
979            let mut result = [0u8; 32];
980            for i in 0..32 {
981                result[i] = self.0[i] | rhs.0[i];
982            }
983            U256(result)
984        }
985    }
986
987    // Bitwise NOT
988    impl Not for U256 {
989        type Output = Self;
990
991        fn not(self) -> Self::Output {
992            let mut result = [0u8; 32];
993            for i in 0..32 {
994                result[i] = !self.0[i];
995            }
996            U256(result)
997        }
998    }
999
1000    // Left shift without wraparound
1001    impl Shl<u32> for U256 {
1002        type Output = Self;
1003
1004        fn shl(self, rhs: u32) -> Self::Output {
1005            if rhs >= 256 {
1006                return U256::MIN;
1007            }
1008
1009            // Split into two u128 values (little-endian)
1010            let low = u128::from_le_bytes(self.0[0..16].try_into().unwrap());
1011            let high = u128::from_le_bytes(self.0[16..32].try_into().unwrap());
1012
1013            let (new_low, new_high) = if rhs >= 128 {
1014                // Shift more than 128 bits: low becomes 0, high gets low shifted
1015                let shift_amount = rhs - 128;
1016                (0, low << shift_amount)
1017            } else {
1018                // Normal shift: both parts get shifted, with overflow from low to high
1019                let overflow_bits = 128 - rhs;
1020                let new_low = low << rhs;
1021                let new_high = (high << rhs) | (low >> overflow_bits);
1022                (new_low, new_high)
1023            };
1024
1025            let mut result = [0u8; 32];
1026            result[0..16].copy_from_slice(&new_low.to_le_bytes());
1027            result[16..32].copy_from_slice(&new_high.to_le_bytes());
1028
1029            U256(result)
1030        }
1031    }
1032
1033    // Right shift without wraparound
1034    impl Shr<u32> for U256 {
1035        type Output = Self;
1036
1037        fn shr(self, rhs: u32) -> Self::Output {
1038            if rhs >= 256 {
1039                return U256::MIN;
1040            }
1041
1042            // Split into two u128 values (little-endian)
1043            let low = u128::from_le_bytes(self.0[0..16].try_into().unwrap());
1044            let high = u128::from_le_bytes(self.0[16..32].try_into().unwrap());
1045
1046            let (new_low, new_high) = if rhs >= 128 {
1047                // Shift more than 128 bits: high becomes 0, low gets high shifted
1048                let shift_amount = rhs - 128;
1049                (high >> shift_amount, 0)
1050            } else {
1051                // Normal shift: both parts get shifted, with overflow from high to low
1052                let overflow_bits = 128 - rhs;
1053                let new_low = (low >> rhs) | (high << overflow_bits);
1054                let new_high = high >> rhs;
1055                (new_low, new_high)
1056            };
1057
1058            let mut result = [0u8; 32];
1059            result[0..16].copy_from_slice(&new_low.to_le_bytes());
1060            result[16..32].copy_from_slice(&new_high.to_le_bytes());
1061
1062            U256(result)
1063        }
1064    }
1065}
1066
1067pub mod pool {
1068    //! An abstract pool of [`RpcClient`]s.
1069    //!
1070    //! This can be implemented with an in-memory implementation for tests,
1071    //! and with a proper iroh connection pool for real use.
1072
1073    use std::sync::{Arc, RwLock};
1074
1075    use iroh::{
1076        Endpoint, NodeAddr, NodeId,
1077        endpoint::{RecvStream, SendStream},
1078    };
1079    use iroh_blobs::util::connection_pool::{ConnectionPool, ConnectionRef};
1080    use snafu::Snafu;
1081    use tracing::error;
1082
1083    use crate::rpc::{RpcClient, WeakRpcClient};
1084
1085    /// A pool that can efficiently provide clients given a node id, and knows its
1086    /// own identity.
1087    ///
1088    /// For tests, this is just a map from id to client. For production, this will
1089    /// wrap an iroh Endpoint and have some sort of connection cache.
1090    pub trait ClientPool: Send + Sync + Clone + Sized + 'static {
1091        /// Our own node id
1092        fn id(&self) -> NodeId;
1093
1094        /// Adds dialing info to a node id.
1095        ///
1096        /// The default impl doesn't add anything.
1097        fn node_addr(&self, node_id: NodeId) -> NodeAddr {
1098            node_id.into()
1099        }
1100
1101        /// Adds dialing info for a node id.
1102        ///
1103        /// The default impl doesn't add anything.
1104        fn add_node_addr(&self, _addr: NodeAddr) {}
1105
1106        /// Use the client to perform an operation.
1107        ///
1108        /// You must not clone the client out of the closure. If you do, this client
1109        /// can become unusable at any time!
1110        fn client(&self, id: NodeId) -> impl Future<Output = Result<RpcClient, String>> + Send;
1111    }
1112
1113    /// Error when a pool can not obtain a client.
1114    #[derive(Debug, Snafu)]
1115    pub struct PoolError {
1116        pub message: String,
1117    }
1118
1119    /// A client pool backed by real iroh connections.
1120    #[derive(Debug, Clone)]
1121    pub struct IrohPool {
1122        endpoint: Endpoint,
1123        inner: ConnectionPool,
1124        self_client: Arc<RwLock<Option<WeakRpcClient>>>,
1125    }
1126
1127    impl IrohPool {
1128        pub fn new(endpoint: Endpoint, inner: ConnectionPool) -> Self {
1129            Self {
1130                endpoint,
1131                inner,
1132                self_client: Arc::new(RwLock::new(None)),
1133            }
1134        }
1135
1136        /// Iroh connections to self are not allowed. But when storing or getting values,
1137        /// it is perfectly reasonable to have the own id as the best location.
1138        ///
1139        /// To support this seamlessly, this allows creating a client to self.
1140        ///
1141        /// This has to be a weak client since we don't want the pool, which is owned by the
1142        /// node actor, to keep the node actor alive.
1143        pub fn set_self_client(&self, client: Option<WeakRpcClient>) {
1144            let mut self_client = self.self_client.write().unwrap();
1145            *self_client = client;
1146        }
1147    }
1148
1149    #[derive(Debug, Clone)]
1150    struct IrohConnection(Arc<ConnectionRef>);
1151
1152    impl irpc::rpc::RemoteConnection for IrohConnection {
1153        fn clone_boxed(&self) -> Box<dyn irpc::rpc::RemoteConnection> {
1154            Box::new(self.clone())
1155        }
1156
1157        fn open_bi(
1158            &self,
1159        ) -> n0_future::future::Boxed<
1160            std::result::Result<(SendStream, RecvStream), irpc::RequestError>,
1161        > {
1162            let conn = self.0.clone();
1163            Box::pin(async move {
1164                let (send, recv) = conn.open_bi().await?;
1165                Ok((send, recv))
1166            })
1167        }
1168    }
1169
1170    impl ClientPool for IrohPool {
1171        fn id(&self) -> NodeId {
1172            self.endpoint.node_id()
1173        }
1174
1175        fn node_addr(&self, node_id: NodeId) -> NodeAddr {
1176            // enrich the node id with available dialing info if available.
1177            //
1178            // for ids in our routing table, we should have dialed them at some
1179            // point, so we should have some info for them. If not, the
1180            // receiver will have to rely on node discovery.
1181            match self.endpoint.remote_info(node_id) {
1182                Some(info) => info.into(),
1183                None => node_id.into(),
1184            }
1185        }
1186
1187        fn add_node_addr(&self, addr: NodeAddr) {
1188            // don't add self info.
1189            // this should not happen, but just in case
1190            if addr.node_id == self.id() {
1191                return;
1192            }
1193            // don't add useless info.
1194            if addr.relay_url.is_none() && addr.direct_addresses.is_empty() {
1195                return;
1196            }
1197            // this can still fail, for the reason AddNodeAddrError::EmptyPruned ¯\_(ツ)_/¯
1198            self.endpoint.add_node_addr_with_source(addr, "").ok();
1199        }
1200
1201        async fn client(&self, node_id: NodeId) -> Result<RpcClient, String> {
1202            if node_id == self.id() {
1203                // If we are trying to connect to ourselves, return the self client if available.
1204                if let Some(client) = self.self_client.read().unwrap().clone() {
1205                    return client
1206                        .upgrade()
1207                        .ok_or_else(|| "Self client is no longer available".to_string());
1208                } else {
1209                    error!("Self client not set");
1210                    return Err("Self client not set".to_string());
1211                }
1212            }
1213            let connection = self
1214                .inner
1215                .get_or_connect(node_id)
1216                .await
1217                .map_err(|e| format!("Failed to connect: {e}"));
1218            let connection = connection?;
1219            let client = RpcClient::new(irpc::Client::boxed(IrohConnection(Arc::new(connection))));
1220            Ok(client)
1221        }
1222    }
1223}
1224
1225/// State of the actor that is required in the async handlers
1226#[derive(Debug, Clone)]
1227struct State<P> {
1228    /// ability to send messages to ourselves, e.g. to update the routing table
1229    api: WeakApiClient,
1230    /// client pool
1231    pool: P,
1232    /// configuration
1233    config: Config,
1234}
1235
1236struct Candidates {
1237    ids: VecDeque<NodeId>,
1238    max_size: usize,
1239}
1240
1241impl Candidates {
1242    fn new(max_size: usize) -> Self {
1243        Self {
1244            ids: VecDeque::new(),
1245            max_size,
1246        }
1247    }
1248
1249    /// Adds a candidate, dedups, and maintains the max size.
1250    fn add(&mut self, id: NodeId) {
1251        self.ids.retain(|x| x != &id);
1252        self.ids.push_front(id);
1253        while self.ids.len() > self.max_size {
1254            self.ids.pop_back();
1255        }
1256    }
1257
1258    /// Returns the candidates, most recent first, and clears the set
1259    fn clear_and_take(&mut self) -> Vec<NodeId> {
1260        let res = self.ids.iter().cloned().collect();
1261        self.ids.clear();
1262        res
1263    }
1264}
1265
1266struct Actor<P> {
1267    node: Node,
1268    /// receiver for rpc messages from the network
1269    rpc_rx: tokio::sync::mpsc::Receiver<RpcMessage>,
1270    /// receiver for api messages from in process or local network
1271    api_rx: tokio::sync::mpsc::Receiver<ApiMessage>,
1272    /// ongoing tasks
1273    tasks: JoinSet<()>,
1274    /// state
1275    state: State<P>,
1276    /// candidates for inclusion in the routing table
1277    candidates: Option<Candidates>,
1278    /// Rng for random lookups
1279    rng: rand::rngs::StdRng,
1280}
1281
1282/// Dht lookup config
1283///
1284/// Note that while transient, parallelism and alpha are parameters that you can
1285/// modify to suit your needs, you very rarely want to modify the k parameter.
1286#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1287pub struct Config {
1288    /// DHT parameter K
1289    k: usize,
1290    /// DHT parameter ALPHA
1291    alpha: usize,
1292    /// Parallelism for the set or getall requests once we have found the k
1293    /// closest nodes.
1294    parallelism: usize,
1295    /// Whether the requester is a transient node.
1296    transient: bool,
1297    /// Random number generator seed.
1298    rng_seed: Option<[u8; 32]>,
1299    /// Lookup strategies.
1300    lookup_strategies: LookupStrategies,
1301}
1302
1303pub mod config {
1304    //! Detailed configuration for the DHT.
1305    use super::*;
1306
1307    #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1308    pub struct LookupStrategies {
1309        /// Random lookup strategy.
1310        pub random: Option<RandomLookupStrategy>,
1311        /// Self lookup strategy.
1312        pub self_id: Option<SelfLookupStrategy>,
1313        /// Candidate lookup strategy.
1314        pub candidate: Option<CandidateLookupStrategy>,
1315    }
1316
1317    impl LookupStrategies {
1318        /// No lookup strategies.
1319        ///
1320        /// This is good for testing, since it allows you to trigger lookups
1321        /// manually. But don't use this in prod!
1322        pub fn none() -> Self {
1323            Self {
1324                random: None,
1325                self_id: None,
1326                candidate: None,
1327            }
1328        }
1329    }
1330
1331    /// This is the mechanism for adding new nodes to a DHT.
1332    ///
1333    /// When we find a node that is not in our routing table, we don't immediately
1334    /// update our routing table. It could be a node that claims to be permanent
1335    /// but is transient. Instead, we add it to a list of candidates for inclusion
1336    /// that gets used periodically.
1337    ///
1338    /// We perform random lookups with the candidate nodes as initial peers. This
1339    /// will cause them to be validated by sending them a FindNode query, and as
1340    /// a side effect of validating we update our routing table.
1341    #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
1342    pub struct CandidateLookupStrategy {
1343        pub max_lookups: usize,
1344        pub interval: Duration,
1345    }
1346
1347    /// Perform a periodic self-lookup.
1348    ///
1349    /// This is useful to update the buckets close to self. Random lookups will
1350    /// rarely hit these.
1351
1352    #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
1353    pub struct SelfLookupStrategy {
1354        pub interval: Duration,
1355    }
1356
1357    /// Perform a periodic random lookup.
1358    ///
1359    /// This is useful to update the buckets that are far away from self.
1360    #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
1361    pub struct RandomLookupStrategy {
1362        pub interval: Duration,
1363        pub blended: bool,
1364    }
1365}
1366use config::*;
1367
1368impl Config {
1369    /// Configuration for a transient node that does not want to be included
1370    /// in the routing tables of its peers.
1371    pub fn transient() -> Self {
1372        Self {
1373            transient: true,
1374            ..Default::default()
1375        }
1376    }
1377
1378    /// Configuration for a persistent node that wants to be included
1379    /// in the routing tables of its peers.
1380    pub fn persistent() -> Self {
1381        Self {
1382            transient: false,
1383            ..Default::default()
1384        }
1385    }
1386
1387    pub fn candidate_lookup_strategy(mut self, value: CandidateLookupStrategy) -> Self {
1388        self.lookup_strategies.candidate = Some(value);
1389        self
1390    }
1391
1392    pub fn random_lookup_strategy(mut self, value: RandomLookupStrategy) -> Self {
1393        self.lookup_strategies.random = Some(value);
1394        self
1395    }
1396
1397    pub fn self_lookup_strategy(mut self, value: SelfLookupStrategy) -> Self {
1398        self.lookup_strategies.self_id = Some(value);
1399        self
1400    }
1401}
1402
1403impl Default for Config {
1404    fn default() -> Self {
1405        Self {
1406            k: K,
1407            alpha: ALPHA,
1408            parallelism: 4,
1409            transient: true,
1410            lookup_strategies: LookupStrategies {
1411                random: None,
1412                self_id: None,
1413                candidate: None,
1414            },
1415            rng_seed: None,
1416        }
1417    }
1418}
1419
1420impl<P> Actor<P>
1421where
1422    P: ClientPool,
1423{
1424    fn new(
1425        node: Node,
1426        rx: tokio::sync::mpsc::Receiver<RpcMessage>,
1427        pool: P,
1428        config: Config,
1429    ) -> (Self, ApiClient) {
1430        let (api_tx, internal_rx) = tokio::sync::mpsc::channel(32);
1431        let api = ApiClient(Arc::new(api_tx.into()));
1432        let mut tasks = JoinSet::new();
1433        let state = State {
1434            api: api.downgrade(),
1435            pool,
1436            config: config.clone(),
1437        };
1438        tasks.spawn(state.clone().notify_self());
1439        (
1440            Self {
1441                node,
1442                rpc_rx: rx,
1443                api_rx: internal_rx,
1444                tasks,
1445                state,
1446                candidates: config
1447                    .lookup_strategies
1448                    .candidate
1449                    .map(|s| Candidates::new(s.max_lookups * config.k)),
1450                rng: config
1451                    .rng_seed
1452                    .map(StdRng::from_seed)
1453                    .unwrap_or(StdRng::from_entropy()),
1454            },
1455            api,
1456        )
1457    }
1458
1459    async fn run(mut self) {
1460        loop {
1461            tokio::select! {
1462                msg = self.rpc_rx.recv() => {
1463                    if let Some(msg) = msg {
1464                        self.handle_rpc(msg).await;
1465                    } else {
1466                        break;
1467                    }
1468                }
1469                msg = self.api_rx.recv() => {
1470                    if let Some(msg) = msg {
1471                        self.handle_api(msg).await;
1472                    } else {
1473                        break;
1474                    }
1475                }
1476                Some(res) = self.tasks.join_next(), if !self.tasks.is_empty() => {
1477                    if let Err(e) = res {
1478                        error!("Task failed: {:?}", e);
1479                    }
1480                }
1481            }
1482        }
1483    }
1484
1485    /// Handle a single API message
1486    async fn handle_api(&mut self, message: ApiMessage) {
1487        match message {
1488            ApiMessage::NodesSeen(msg) => {
1489                let now = now();
1490                for id in msg.ids.iter().copied() {
1491                    self.node
1492                        .routing_table
1493                        .add_node(NodeInfo { id, last_seen: now });
1494                }
1495            }
1496            ApiMessage::NodesDead(msg) => {
1497                for id in msg.ids.iter() {
1498                    self.node.routing_table.remove_node(id);
1499                }
1500            }
1501            ApiMessage::Lookup(msg) => {
1502                let initial = msg
1503                    .initial
1504                    .clone()
1505                    .unwrap_or_else(|| self.node.routing_table.find_closest_nodes(&msg.id, K));
1506                self.tasks
1507                    .spawn(self.state.clone().lookup(initial, msg.inner, msg.tx));
1508            }
1509            ApiMessage::NetworkGet(msg) => {
1510                // perform a network get by calling the iterative search using the closest
1511                // nodes from the local routing table, then performing individual requests
1512                // for the resulting k closest live nodes.
1513                let initial = self.node.routing_table.find_closest_nodes(&msg.id, K);
1514                self.tasks
1515                    .spawn(self.state.clone().network_get(initial, msg.inner, msg.tx));
1516            }
1517            ApiMessage::NetworkPut(msg) => {
1518                // perform a network put by calling the iterative search using the closest
1519                // nodes from the local routing table, then performing individual requests
1520                // for the resulting k closest live nodes.
1521                let initial = self.node.routing_table.find_closest_nodes(&msg.id, K);
1522                self.tasks
1523                    .spawn(self.state.clone().network_put(initial, msg.inner, msg.tx));
1524            }
1525            ApiMessage::GetRoutingTable(msg) => {
1526                let table = self
1527                    .node
1528                    .routing_table
1529                    .buckets
1530                    .iter()
1531                    .map(|bucket| bucket.nodes().to_vec())
1532                    .collect();
1533                msg.tx.send(table).await.ok();
1534            }
1535            ApiMessage::GetStorageStats(msg) => {
1536                // Collect storage stats, mapping Id to Kind to count of values
1537                let mut stats = BTreeMap::new();
1538                for (key, kinds) in &self.node.storage.data {
1539                    let kind_stats = kinds
1540                        .iter()
1541                        .map(|(kind, values)| (*kind, values.len()))
1542                        .collect();
1543                    stats.insert(*key, kind_stats);
1544                }
1545                msg.tx.send(stats).await.ok();
1546            }
1547            ApiMessage::SelfLookup(msg) => {
1548                let id = self.state.pool.id().into();
1549                // todo: choose initial to be farthest away from self, otherwise
1550                // the self lookup won't be very useful.
1551                let api = self.state.api.clone();
1552                self.tasks.spawn(async move {
1553                    let Ok(api) = api.upgrade() else {
1554                        return;
1555                    };
1556                    api.lookup(id, None).await.ok();
1557                    msg.tx.send(()).await.ok();
1558                });
1559            }
1560            ApiMessage::RandomLookup(msg) => {
1561                // bucket up to which the node should overlap with self.
1562                // 0 is fully random, 256 is just self.
1563                let blended = false;
1564                let id = if blended {
1565                    let bucket = self.rng.gen_range::<u32, _>(0..BUCKET_COUNT as u32 + 2);
1566                    let this = U256::from_le_bytes(*self.node.id().as_bytes());
1567                    let random = U256::from_le_bytes(self.rng.r#gen());
1568                    let res = blend(this, random, bucket);
1569                    Id::from(res.to_le_bytes())
1570                } else {
1571                    Id::from(self.rng.r#gen::<[u8; 32]>())
1572                };
1573                let api = self.state.api.clone();
1574                self.tasks.spawn(async move {
1575                    let Ok(api) = api.upgrade() else {
1576                        return;
1577                    };
1578                    api.lookup(id, None).await.ok();
1579                    msg.tx.send(()).await.ok();
1580                });
1581            }
1582            ApiMessage::CandidateLookup(msg) => {
1583                if self.state.config.lookup_strategies.candidate.is_none() {
1584                    warn!(
1585                        "Received CandidateLookup request, but no candidate lookup strategy is configured"
1586                    );
1587                    return;
1588                };
1589                let Some(candidates) = self.candidates.as_mut() else {
1590                    warn!("Received CandidateLookup request, but no candidates are being tracked");
1591                    return;
1592                };
1593                // use the most recent `max_lookups * k` candidates
1594                let chosen = candidates.clear_and_take();
1595                // perform a random lookup using the candidates as initial.
1596                //
1597                let api = self.state.api.clone();
1598                let groups = chosen
1599                    .chunks(self.state.config.k)
1600                    .map(|chunk| {
1601                        let id = Id::from(self.rng.r#gen::<[u8; 32]>());
1602                        (id, chunk.to_vec())
1603                    })
1604                    .collect::<Vec<_>>();
1605                self.tasks.spawn(async move {
1606                    let Ok(api) = api.upgrade() else {
1607                        return;
1608                    };
1609                    // this will check if they exist. If yes, they will be added to the routing table.
1610                    for (id, ids) in groups {
1611                        api.lookup(id, Some(ids)).await.ok();
1612                    }
1613                    msg.tx.send(()).await.ok();
1614                });
1615            }
1616        }
1617    }
1618
1619    async fn handle_rpc(&mut self, message: RpcMessage) {
1620        match message {
1621            RpcMessage::Set(msg) => {
1622                // just set the value in the local storage
1623                //
1624                // TODO: check if the data is expired or invalid and return the
1625                // appropriate error response.
1626                //
1627                // Sanity check that this node is a good node to store
1628                // the data at, using the local routing table, and if not return
1629                // a SetResponse::ErrDistance.
1630                let ids = self
1631                    .node
1632                    .routing_table
1633                    .find_closest_nodes(&msg.key, self.state.config.k);
1634                let self_dist = Distance::between(self.node.id().as_bytes(), &msg.key);
1635                // if we know k nodes that are closer to the key than we are, we don't want to store
1636                // the data!
1637                if ids.len() >= self.state.config.k
1638                    && ids.iter().all(|id| {
1639                        Distance::between(self.node.id().as_bytes(), id.as_bytes()) < self_dist
1640                    })
1641                {
1642                    msg.tx.send(SetResponse::ErrDistance).await.ok();
1643                    return;
1644                }
1645                self.node.storage.set(msg.key, msg.value.clone());
1646                msg.tx.send(SetResponse::Ok).await.ok();
1647            }
1648            RpcMessage::GetAll(msg) => {
1649                // Get all values, applying the provided filters and limits.
1650                let Some(values) = self.node.storage.get_all(&msg.key, &msg.kind) else {
1651                    return;
1652                };
1653                // Randomize the order of the results given the provided seed
1654                if let Some(seed) = msg.seed {
1655                    let mut rng = rand::rngs::StdRng::seed_from_u64(seed.get());
1656                    let n = msg.n.map(|x| x.get()).unwrap_or(values.len() as u64) as usize;
1657                    let indices = sample(&mut rng, values.len(), n);
1658                    for i in indices {
1659                        if let Some(value) = values.get_index(i)
1660                            && msg.tx.send(value.clone()).await.is_err()
1661                        {
1662                            break;
1663                        }
1664                    }
1665                } else {
1666                    // just send them in whatever order they return from the store.
1667                    for value in values {
1668                        if msg.tx.send(value.clone()).await.is_err() {
1669                            break;
1670                        }
1671                    }
1672                }
1673            }
1674            RpcMessage::FindNode(msg) => {
1675                // call local find_node and just return the results
1676                let ids = self
1677                    .node
1678                    .routing_table
1679                    .find_closest_nodes(&msg.id, self.state.config.k)
1680                    .into_iter()
1681                    .take(self.state.config.k) // should not be needed, but just in case
1682                    .map(|id| self.state.pool.node_addr(id))
1683                    .collect();
1684                if let Some(requester) = msg.requester {
1685                    self.add_candidate(requester);
1686                }
1687                msg.tx.send(ids).await.ok();
1688            }
1689        }
1690    }
1691
1692    fn add_candidate(&mut self, id: NodeId) {
1693        if self.state.config.transient {
1694            warn!("Received FindNode request for transient node");
1695            return;
1696        }
1697        if self.node.routing_table.contains(&id) {
1698            // we trust this node already, update the time
1699            self.node.routing_table.add_node(NodeInfo {
1700                id,
1701                last_seen: now(),
1702            });
1703        }
1704        let Some(candidates) = &mut self.candidates else {
1705            // candidate tracking is not enabled
1706            return;
1707        };
1708        // add it to the candidates for routing table inclusion
1709        candidates.add(id);
1710    }
1711}
1712
1713impl<P: ClientPool> State<P> {
1714    async fn lookup(self, initial: Vec<NodeId>, msg: Lookup, tx: oneshot::Sender<Vec<NodeId>>) {
1715        let ids = self.clone().iterative_find_node(msg.id, initial).await;
1716        tx.send(ids).await.ok();
1717    }
1718
1719    async fn network_put(self, initial: Vec<NodeId>, msg: NetworkPut, tx: mpsc::Sender<NodeId>) {
1720        let ids = self.clone().iterative_find_node(msg.id, initial).await;
1721        stream::iter(ids)
1722            .for_each_concurrent(self.config.parallelism, |id| {
1723                let pool = self.pool.clone();
1724                let value = msg.value.clone();
1725                let tx = tx.clone();
1726                async move {
1727                    let Ok(client) = pool.client(id).await else {
1728                        return;
1729                    };
1730                    if client.set(msg.id, value).await.is_ok() {
1731                        tx.send(id).await.ok();
1732                    }
1733                    drop(client);
1734                }
1735            })
1736            .await;
1737    }
1738
1739    async fn network_get(
1740        self,
1741        initial: Vec<NodeId>,
1742        msg: NetworkGet,
1743        tx: mpsc::Sender<(NodeId, Value)>,
1744    ) {
1745        let ids = self.clone().iterative_find_node(msg.id, initial).await;
1746        stream::iter(ids)
1747            .for_each_concurrent(self.config.parallelism, |id| {
1748                let pool = self.pool.clone();
1749                let tx = tx.clone();
1750                let msg = NetworkGet {
1751                    id: msg.id,
1752                    kind: msg.kind,
1753                    seed: msg.seed,
1754                    n: msg.n,
1755                };
1756                async move {
1757                    let Ok(client) = pool.client(id).await else {
1758                        return;
1759                    };
1760                    // Get all values of the specified kind for the key
1761                    let Ok(mut rx) = client.get_all(msg.id, msg.kind, msg.seed, msg.n).await else {
1762                        return;
1763                    };
1764                    while let Ok(Some(value)) = rx.recv().await {
1765                        if tx.send((id, value)).await.is_err() {
1766                            break;
1767                        }
1768                    }
1769                    drop(client);
1770                }
1771            })
1772            .await;
1773    }
1774
1775    async fn query_one(&self, id: NodeId, target: Id) -> Result<Vec<NodeId>, &'static str> {
1776        let requester = if self.config.transient {
1777            None
1778        } else {
1779            Some(self.pool.id())
1780        };
1781
1782        let client = self
1783            .pool
1784            .client(id)
1785            .await
1786            .map_err(|_| "Error getting client")?;
1787        let infos = client
1788            .find_node(target, requester)
1789            .await
1790            .map_err(|_| "Failed to query node");
1791        if let Err(e) = &infos {
1792            info!(%id, "Failed to query node: {e}");
1793            return Err("Failed to query node");
1794        }
1795        let infos = infos?;
1796        drop(client);
1797        let ids = infos.iter().map(|info| info.node_id).collect();
1798        for info in infos {
1799            self.pool.add_node_addr(info);
1800        }
1801        Ok(ids)
1802    }
1803
1804    async fn iterative_find_node(self, target: Id, initial: Vec<NodeId>) -> Vec<NodeId> {
1805        let mut candidates = initial
1806            .into_iter()
1807            .filter(|addr| *addr != self.pool.id())
1808            .map(|id| (Distance::between(&target, id.as_bytes()), id))
1809            .collect::<BTreeSet<_>>();
1810        let mut queried = HashSet::new();
1811        let mut tasks = FuturesUnordered::new();
1812        let mut result = BTreeSet::new();
1813        queried.insert(self.pool.id());
1814        result.insert((
1815            Distance::between(self.pool.id().as_bytes(), &target),
1816            self.pool.id(),
1817        ));
1818
1819        loop {
1820            for _ in 0..self.config.alpha {
1821                let Some(pair @ (_, id)) = candidates.pop_first() else {
1822                    break;
1823                };
1824                queried.insert(id);
1825                let fut = self.query_one(id, target);
1826                tasks.push(async move { (pair, fut.await) });
1827            }
1828
1829            while let Some((pair @ (_, id), cands)) = tasks.next().await {
1830                let Ok(cands) = cands else {
1831                    self.api.nodes_dead(&[id]).await.ok();
1832                    continue;
1833                };
1834                for cand in cands {
1835                    let dist = Distance::between(&target, cand.as_bytes());
1836                    if !queried.contains(&cand) {
1837                        candidates.insert((dist, cand));
1838                    }
1839                }
1840                self.api.nodes_seen(&[id]).await.ok();
1841                result.insert(pair);
1842            }
1843
1844            // truncate the result to k.
1845            while result.len() > self.config.k {
1846                result.pop_last();
1847            }
1848
1849            // find the k-th best distance
1850            let kth_best_distance = result
1851                .iter()
1852                .nth(self.config.k - 1)
1853                .map(|(dist, _)| *dist)
1854                .unwrap_or(Distance::MAX);
1855
1856            // true if we candidates that are better than distance for result[k-1].
1857            let has_closer_candidates = candidates
1858                .first()
1859                .map(|(dist, _)| *dist < kth_best_distance)
1860                .unwrap_or_default();
1861
1862            if !has_closer_candidates {
1863                break;
1864            }
1865        }
1866
1867        // result already has size <= k
1868        result.into_iter().map(|(_, id)| id).collect()
1869    }
1870
1871    /// Task that sends messages to self in periodic intervals for routing
1872    /// table maintenance, if configured.
1873    async fn notify_self(self) {
1874        let mut self_lookup = MaybeFuture::None;
1875        let mut random_lookup = MaybeFuture::None;
1876        let mut candidate_lookup = MaybeFuture::None;
1877        if let Some(strategy) = &self.config.lookup_strategies.self_id {
1878            let api = self.api.clone();
1879            self_lookup = MaybeFuture::Some(api.self_lookup_periodic(strategy.interval));
1880        }
1881        if let Some(strategy) = &self.config.lookup_strategies.random {
1882            let api = self.api.clone();
1883            random_lookup = MaybeFuture::Some(api.random_lookup_periodic(strategy.interval));
1884        }
1885        if let Some(strategy) = &self.config.lookup_strategies.candidate {
1886            let api = self.api.clone();
1887            candidate_lookup = MaybeFuture::Some(api.candidate_lookup_periodic(strategy.interval));
1888        }
1889        tokio::pin!(self_lookup, random_lookup, candidate_lookup);
1890        loop {
1891            tokio::select! {
1892                _ = &mut self_lookup => {
1893                }
1894                _ = &mut random_lookup => {
1895                }
1896                _ = &mut candidate_lookup => {
1897                }
1898            }
1899        }
1900    }
1901}
1902
1903/// Blend two values.
1904///
1905/// n is the number of bits different from a
1906/// n=0, just a
1907/// n=1, smallest bit is different from a
1908/// n=2, second smallest bit is different from a, smallest bit is from b
1909/// n=256, highest bit is different from a, all others from b
1910/// n>256, just b
1911fn blend(a: U256, b: U256, n: u32) -> U256 {
1912    if n >= 256 {
1913        return b;
1914    }
1915    let a_mask = U256::MAX << n;
1916    let b_mask = (!a_mask) >> 1;
1917    let xor_mask = !(a_mask | b_mask);
1918    a & a_mask | a ^ xor_mask | b & b_mask
1919}
1920
1921fn now() -> u64 {
1922    UNIX_EPOCH.elapsed().unwrap().as_secs()
1923}
1924
1925/// Creates a DHT node
1926pub fn create_node<P: ClientPool>(
1927    id: NodeId,
1928    pool: P,
1929    bootstrap: Vec<NodeId>,
1930    config: Config,
1931) -> (RpcClient, ApiClient) {
1932    create_node_impl(id, pool, bootstrap, None, config)
1933}
1934
1935/// Create a node, with the option to set the initial routing table buckets
1936fn create_node_impl<P: ClientPool>(
1937    id: NodeId,
1938    pool: P,
1939    bootstrap: Vec<NodeId>,
1940    buckets: Option<Box<Buckets>>,
1941    config: Config,
1942) -> (RpcClient, ApiClient) {
1943    let mut node = Node {
1944        routing_table: RoutingTable::new(id, buckets),
1945        storage: MemStorage::new(),
1946    };
1947    for bootstrap_id in bootstrap {
1948        if bootstrap_id != id {
1949            node.routing_table.add_node(NodeInfo {
1950                id: bootstrap_id,
1951                last_seen: now(),
1952            });
1953        }
1954    }
1955    let (tx, rx) = tokio::sync::mpsc::channel(32);
1956    let (actor, api) = Actor::<P>::new(node, rx, pool, config);
1957    tokio::spawn(actor.run());
1958    (RpcClient::new(irpc::Client::local(tx)), api)
1959}