RoutingTable

class RoutingTable(id: Sha1Hash, val bucketSize: Int, val settings: RoutingTableSettings = RoutingTableSettings())

The Kademlia k-bucket routing table. This is the pure-Kotlin port of libtorrent's routing_table (include/libtorrent/kademlia/routing_table.hpp, src/kademlia/routing_table.cpp).

The table is a list of buckets, each holding up to k (bucketSize) live nodes plus a small replacement cache. Bucket 0 is the one furthest from our own id; every time the bucket closest to us fills up it is split in two, doubling the resolution of the part of the keyspace nearest to us. This is the standard Kademlia structure that lets a lookup converge on any target in O(log n) hops.

What this port covers (the faithful core):

Deliberate omissions / simplifications (each noted at its call site):

  • Endpoints are host: String + port: Int (no socket types in commonMain), matching NodeEntry. IP identity for de-duplication and the BEP 42 verified flag come from NodeEntry.addr (PeerAddress).

  • restrict_routing_ips is honoured via compareIpCidr and IpSet, but a node whose host is not a parseable IP literal (so addr == null) is treated as having no IP identity. It never collides in IpSet and is never rejected by the CIDR-closeness check. Real libtorrent always has a numeric address here.

  • RTT-based refresh scheduling (next_refresh) and the logging/dht_logger plumbing are not ported; they don't affect the routing structure that findNode returns.

  • Time-based fields (last_queried, first_seen) are not modelled. The one place upstream uses last_queried to influence structure is the malicious-node "schedule a re-ping" loop, which we collapse to a fail-count reset (see addNodeImpl).

Settings come from RoutingTableSettings, which mirrors the five DHT settings_pack booleans/int the routing table reads, with libtorrent's exact defaults.

Parameters

id

our own node id (m_id).

bucketSize

the constant k from the Kademlia paper (m_bucket_size); must be a power of two.

settings

the DHT routing knobs (see RoutingTableSettings).

Constructors

Link copied to clipboard
constructor(id: Sha1Hash, bucketSize: Int, settings: RoutingTableSettings = RoutingTableSettings())

Types

Link copied to clipboard

Result of addNodeImpl, port of routing_table::add_node_status_t.

Link copied to clipboard
object Companion

Properties

Link copied to clipboard
Link copied to clipboard

our own node id (m_id). Read-only from outside; mutated by updateNodeId.

Link copied to clipboard

Functions

Link copied to clipboard

Add a node, performing bucket splits as needed. Port of bool routing_table::add_node(node_entry const& e). Returns true if the node ended up in the table (live or replacement bucket).

Link copied to clipboard

The core insert state machine. Port of routing_table::add_node_status_t routing_table::add_node_impl(node_entry e). Returns one of AddNodeStatus; addNode drives the splitting loop on AddNodeStatus.NEED_BUCKET_SPLIT.

Link copied to clipboard
fun addRouterNode(host: String, port: Int)

Register a router node by endpoint (add_router_node).

Link copied to clipboard
fun bucketLimit(bucket: Int): Int

The per-bucket size limit. Port of int routing_table::bucket_limit(int). With the extended routing table (the default), the first four buckets are larger (k*{16,8,4,2}) so the table gets closer to us in fewer hops.

Link copied to clipboard
fun bucketSize(bucket: Int): Int

Number of live nodes in bucket bucket (bucket_size(int)).

Link copied to clipboard
fun depth(): Int

The number of bits down we have full buckets. Port of int depth(). This uses (and updates) depthCache exactly like the upstream mutable m_depth.

Link copied to clipboard
fun findNode(target: Sha1Hash, count: Int = 0, includeFailed: Boolean = false): List<NodeEntry>

Gather up to count nodes from our buckets closest (by XOR distance) to target. Port of std::vector<node_entry> routing_table::find_node(node_id const& target, find_nodes_flags_t options, int count).

Link copied to clipboard
fun forEachNode(includeReplacements: Boolean = false, f: (NodeEntry) -> Unit)

Invoke f for every live node, then (optionally) every replacement node.

Link copied to clipboard
fun heardAbout(id: Sha1Hash, host: String, port: Int)

We heard about a node (e.g. in another node's find_node response) but have not confirmed it ourselves. Port of void heard_about(...): the entry is created un-pinged, and if its bucket is full it is simply ignored.

Link copied to clipboard
fun isFull(bucket: Int): Boolean

true if bucket bucket is full in both its live list and its replacement cache. Port of bool routing_table::is_full(int).

Link copied to clipboard

true if the table has no live nodes at all. This is a KiteTorrent convenience (libtorrent has no single need_bootstrap() function; the DHT node decides to bootstrap based on routing_table::size() and the return value of nodeSeen). A freshly-constructed table that has never seen a node is empty and therefore needs bootstrapping.

Link copied to clipboard
fun nodeFailed(nid: Sha1Hash, host: String, port: Int)

Record that a request to a node timed out. Port of void routing_table::node_failed(node_id const&, udp::endpoint const&). After enough failures (or for a never-pinged node) the entry is removed and the bucket back-filled from its replacement cache.

Link copied to clipboard
fun nodeSeen(id: Sha1Hash, host: String, port: Int, rtt: Int): Boolean

We saw a verified sign of life from a node (a response with a matching transaction id). Port of bool routing_table::node_seen(...): the node is created as pinged and added; the return value is true if the table wants a refresh (i.e. a findNode on our own id should be issued). The node is only considered if it passes verifyNodeAddress.

Link copied to clipboard

The number of active buckets (num_active_buckets).

Link copied to clipboard

Our own node id (id()); same as the id property.

Link copied to clipboard

The set of router endpoints, each as "host:port" (begin()/end()).

Link copied to clipboard
fun size(): Triple<Int, Int, Int>

(nodes, replacements, confirmed): total live nodes, total replacement nodes, and the number of live nodes that are confirmed up. Port of std::tuple<int,int,int> routing_table::size().

Link copied to clipboard

Change our node id and rebuild the whole table around it. Port of void routing_table::update_node_id(node_id const& id).