188 lines
5.3 KiB
Nim
Raw Normal View History

2025-02-10 15:34:41 +01:00
import std/os
2025-02-06 10:16:47 +01:00
import std/net
2025-02-12 13:25:37 +01:00
import std/sequtils
2025-02-06 10:16:47 +01:00
import pkg/chronicles
import pkg/chronos
import pkg/libp2p
import pkg/questionable
import pkg/questionable/results
import pkg/codexdht/discv5/[routing_table, protocol as discv5]
from pkg/nimcrypto import keccak256
2025-02-10 15:34:41 +01:00
import ../utils/keyutils
import ../utils/datastoreutils
2025-02-10 14:49:30 +01:00
import ../utils/rng
import ../component
2025-02-10 15:34:41 +01:00
import ../state
2025-02-12 13:25:37 +01:00
import ../types
2025-02-06 10:16:47 +01:00
export discv5
logScope:
topics = "dht"
2025-02-12 13:50:12 +01:00
type
2025-02-12 13:25:37 +01:00
GetNeighborsResponse* = ref object
isResponsive*: bool
nodeIds*: seq[Nid]
2025-02-06 10:16:47 +01:00
2025-02-12 13:25:37 +01:00
Dht* = ref object of Component
state: State
protocol*: discv5.Protocol
key: PrivateKey
peerId: PeerId
announceAddrs*: seq[MultiAddress]
providerRecord*: ?SignedPeerRecord
dhtRecord*: ?SignedPeerRecord
2025-02-06 10:16:47 +01:00
2025-02-12 13:50:12 +01:00
proc responsive(nodeIds: seq[Nid]): GetNeighborsResponse =
GetNeighborsResponse(isResponsive: true, nodeIds: nodeIds)
proc unresponsive(): GetNeighborsResponse =
GetNeighborsResponse(isResponsive: false, nodeIds: newSeq[Nid]())
2025-02-06 15:32:39 +01:00
proc getNode*(d: Dht, nodeId: NodeId): ?!Node =
let node = d.protocol.getNode(nodeId)
if node.isSome():
return success(node.get())
2025-02-12 13:25:37 +01:00
return failure("Node not found for id: " & nodeId.toHex())
2025-02-06 15:32:39 +01:00
method getRoutingTableNodeIds*(d: Dht): seq[Nid] {.base, gcsafe, raises: [].} =
2025-02-12 13:25:37 +01:00
var ids = newSeq[Nid]()
2025-02-06 15:32:39 +01:00
for bucket in d.protocol.routingTable.buckets:
for node in bucket.nodes:
ids.add(node.id)
return ids
2025-02-12 13:50:12 +01:00
method getNeighbors*(
d: Dht, target: Nid
): Future[?!GetNeighborsResponse] {.async: (raises: []), base.} =
2025-02-06 15:32:39 +01:00
without node =? d.getNode(target), err:
2025-02-12 13:50:12 +01:00
return success(unresponsive())
2025-02-06 15:32:39 +01:00
2025-02-07 13:44:35 +01:00
let distances = @[256.uint16]
2025-02-12 13:25:37 +01:00
try:
let response = await d.protocol.findNode(node, distances)
if response.isOk():
let nodes = response.get()
2025-02-12 13:50:12 +01:00
return success(responsive(nodes.mapIt(it.id)))
else:
let errmsg = $(response.error())
if errmsg == "Nodes message not received in time":
return success(unresponsive())
return failure(errmsg)
2025-02-12 13:25:37 +01:00
except CatchableError as exc:
return failure(exc.msg)
2025-02-06 15:32:39 +01:00
2025-02-06 10:16:47 +01:00
proc findPeer*(d: Dht, peerId: PeerId): Future[?PeerRecord] {.async.} =
trace "protocol.resolve..."
let node = await d.protocol.resolve(toNodeId(peerId))
return
if node.isSome():
node.get().record.data.some
else:
PeerRecord.none
method removeProvider*(d: Dht, peerId: PeerId): Future[void] {.base, gcsafe.} =
trace "Removing provider", peerId
d.protocol.removeProvidersLocal(peerId)
2025-02-10 16:02:47 +01:00
proc updateAnnounceRecord(d: Dht, addrs: openArray[MultiAddress]) =
2025-02-06 10:16:47 +01:00
d.announceAddrs = @addrs
trace "Updating announce record", addrs = d.announceAddrs
d.providerRecord = SignedPeerRecord
.init(d.key, PeerRecord.init(d.peerId, d.announceAddrs))
.expect("Should construct signed record").some
if not d.protocol.isNil:
d.protocol.updateRecord(d.providerRecord).expect("Should update SPR")
2025-02-10 16:02:47 +01:00
proc updateDhtRecord(d: Dht, addrs: openArray[MultiAddress]) =
2025-02-06 10:16:47 +01:00
trace "Updating Dht record", addrs = addrs
d.dhtRecord = SignedPeerRecord
.init(d.key, PeerRecord.init(d.peerId, @addrs))
.expect("Should construct signed record").some
if not d.protocol.isNil:
d.protocol.updateRecord(d.dhtRecord).expect("Should update SPR")
2025-02-10 16:24:54 +01:00
method start*(d: Dht): Future[?!void] {.async.} =
2025-02-06 10:16:47 +01:00
d.protocol.open()
await d.protocol.start()
2025-02-10 14:49:30 +01:00
return success()
2025-02-06 10:16:47 +01:00
2025-02-10 15:34:41 +01:00
method stop*(d: Dht): Future[?!void] {.async.} =
2025-02-06 10:16:47 +01:00
await d.protocol.closeWait()
2025-02-10 14:49:30 +01:00
return success()
2025-02-06 10:16:47 +01:00
2025-02-10 15:34:41 +01:00
proc new(
2025-02-06 10:16:47 +01:00
T: type Dht,
2025-02-10 16:24:54 +01:00
state: State,
2025-02-06 10:16:47 +01:00
key: PrivateKey,
bindIp = IPv4_any(),
bindPort = 0.Port,
announceAddrs: openArray[MultiAddress],
bootstrapNodes: openArray[SignedPeerRecord] = [],
store: Datastore = SQLiteDatastore.new(Memory).expect("Should not fail!"),
): Dht =
2025-02-10 16:24:54 +01:00
var self = Dht(
state: state, key: key, peerId: PeerId.init(key).expect("Should construct PeerId")
)
2025-02-06 10:16:47 +01:00
self.updateAnnounceRecord(announceAddrs)
2025-02-06 15:32:39 +01:00
# This disables IP limits:
2025-02-06 10:16:47 +01:00
let discoveryConfig = DiscoveryConfig(
tableIpLimits: TableIpLimits(tableIpLimit: high(uint), bucketIpLimit: high(uint)),
bitsPerHop: DefaultBitsPerHop,
)
2025-02-06 15:32:39 +01:00
trace "Creating DHT protocol", ip = $bindIp, port = $bindPort
2025-02-06 10:16:47 +01:00
self.protocol = newProtocol(
key,
bindIp = bindIp,
bindPort = bindPort,
record = self.providerRecord.get,
bootstrapRecords = bootstrapNodes,
rng = Rng.instance(),
providers = ProvidersManager.new(store),
config = discoveryConfig,
)
self
2025-02-10 15:34:41 +01:00
2025-02-10 16:24:54 +01:00
proc createDht*(state: State): Future[?!Dht] {.async.} =
without dhtStore =? createDatastore(state.config.dataDir / "dht"), err:
2025-02-10 15:34:41 +01:00
return failure(err)
2025-02-10 16:24:54 +01:00
let keyPath = state.config.dataDir / "privatekey"
2025-02-10 15:34:41 +01:00
without privateKey =? setupKey(keyPath), err:
return failure(err)
var listenAddresses = newSeq[MultiAddress]()
# TODO: when p2p connections are supported:
2025-02-10 16:24:54 +01:00
# let aaa = MultiAddress.init("/ip4/" & state.config.publicIp & "/tcp/53678").expect("Should init multiaddress")
2025-02-10 15:34:41 +01:00
# listenAddresses.add(aaa)
var discAddresses = newSeq[MultiAddress]()
let bbb = MultiAddress
2025-02-10 16:24:54 +01:00
.init("/ip4/" & state.config.publicIp & "/udp/" & $state.config.discPort)
2025-02-10 15:34:41 +01:00
.expect("Should init multiaddress")
discAddresses.add(bbb)
let dht = Dht.new(
2025-02-10 16:24:54 +01:00
state,
2025-02-10 15:34:41 +01:00
privateKey,
2025-02-10 16:24:54 +01:00
bindPort = state.config.discPort,
2025-02-10 15:34:41 +01:00
announceAddrs = listenAddresses,
2025-02-10 16:24:54 +01:00
bootstrapNodes = state.config.bootNodes,
2025-02-10 15:34:41 +01:00
store = dhtStore,
)
dht.updateAnnounceRecord(listenAddresses)
dht.updateDhtRecord(discAddresses)
return success(dht)