Skip to content

swift-libp2p/swift-libp2p-kad-dht

Repository files navigation

LibP2PKadDHT

Swift Package Manager compatible Build & Test (macos and linux)

A Kademlia Distributed Hash Table for LibP2P

Table of Contents

Overview

The Kademlia Distributed Hash Table (DHT) subsystem in libp2p is a DHT implementation largely based on the Kademlia [0] whitepaper, augmented with notions from S/Kademlia [1], Coral [2] and the BitTorrent DHT.

DHT operations

The libp2p Kademlia DHT offers the following types of operations:

  • Peer routing

    • Finding the closest nodes to a given key via FIND_NODE.
  • Value storage and retrieval

    • Storing a value on the nodes closest to the value's key by looking up the closest nodes via FIND_NODE and then putting the value to those nodes via PUT_VALUE.

    • Getting a value by its key from the nodes closest to that key via GET_VALUE.

  • Content provider advertisement and discovery

    • Adding oneself to the list of providers for a given key at the nodes closest to that key by finding the closest nodes via FIND_NODE and then adding oneself via ADD_PROVIDER.

    • Getting providers for a given key from the nodes closest to that key via GET_PROVIDERS.

In addition the libp2p Kademlia DHT offers the auxiliary bootstrap operation.

Note:

Install

Include the following dependency in your Package.swift file

let package = Package(
    ...
    dependencies: [
        ...
        .package(name: "LibP2PKadDHT", url: "https://github.com/swift-libp2p/swift-libp2p-kad-dht.git", .upToNextMajor(from: "0.0.1"))
    ],
    
    ...
)

Usage

Example

check out the tests for more examples

import LibP2PKadDHT

let app = Application(.detect())

/// If you'd like to use the DHT as a DHT...
app.dht.use( .kadDHT )

/// Or if you're just interested in its peer discovery functionality
app.discovery.use(.kadDHT)

API

extension StorageKeys {
    static let MyDHT = "MyDHT"
}

let dht = BasicKadDHT(protocolPrefix: "mydht", version: "1.0.0", k: 20, alpha: 4, peerstore: .shared(app.peerstore) ?? .basicInMemory, kvstore: .basicInMemory...)
app.use(dht, id: .MyDHT)

dht.handle(namespace: "pk", valueAs: PublicKey.self, withValidator: { msg in msg.record.value.multihash == msg.key && msg.key }).onPutSuccess({ msg in ... }).onPutFailed({ msg, error in ...})

dht.handle(namespace: "ipfs", valueAs: IPNSRecord.self, withValidator: { msg in ... }).onPutSuccess({ msg in ... }).onPutFailed({ msg, error in ...})

// ... or ...
app.dht.group("myDHT") { myDHT in
    myDHT.on("pk", validator: ...) { pk in ... }
    myDHT.on("ipld", validator: ...) { ipld in ... }
} 

/// Somewhere else without reference to `dht`
app.dht.getValue(forKey: "Qm12...") { value in .... } //if you only have one DHT running
/// or
app.dhts.for(id: .MyDHT).put(value: Record, forKey: "Qm12...") { success in ... }
/// or
let val = await app.dhts.for(id: "MyDHT").findNode("Qm13...")
let routingTableInfo = await app.dhts.for(id: "MyDHT").routingTableInfo()

}

Contributing

Contributions are welcomed! This code is very much a proof of concept. I can guarantee you there's a better / safer way to accomplish the same results. Any suggestions, improvements, or even just critques, are welcome!

Let's make this code better together! 🤝

Credits

License

MIT © 2022 Breth Inc.