feat: making dns discovery async (#3175)

This commit is contained in:
gabrielmer 2024-12-03 14:39:37 +01:00 committed by GitHub
parent b8ad6c1f09
commit d7d00bfd79
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 101 additions and 42 deletions

View File

@ -439,7 +439,7 @@ proc processInput(rfd: AsyncFD, rng: ref HmacDrbgContext) {.async.} =
var wakuDnsDiscovery = WakuDnsDiscovery.init(dnsDiscoveryUrl.get(), resolver)
if wakuDnsDiscovery.isOk:
let discoveredPeers = wakuDnsDiscovery.get().findPeers()
let discoveredPeers = await wakuDnsDiscovery.get().findPeers()
if discoveredPeers.isOk:
info "Connecting to discovered peers"
discoveredNodes = discoveredPeers.get()

View File

@ -355,7 +355,9 @@ proc crawlNetwork(
proc retrieveDynamicBootstrapNodes(
dnsDiscovery: bool, dnsDiscoveryUrl: string, dnsDiscoveryNameServers: seq[IpAddress]
): Result[seq[RemotePeerInfo], string] =
): Future[Result[seq[RemotePeerInfo], string]] {.async.} =
## Retrieve dynamic bootstrap nodes (DNS discovery)
if dnsDiscovery and dnsDiscoveryUrl != "":
# DNS discovery
debug "Discovering nodes using Waku DNS discovery", url = dnsDiscoveryUrl
@ -369,14 +371,15 @@ proc retrieveDynamicBootstrapNodes(
proc resolver(domain: string): Future[string] {.async, gcsafe.} =
trace "resolving", domain = domain
let resolved = await dnsResolver.resolveTxt(domain)
return resolved[0] # Use only first answer
if resolved.len > 0:
return resolved[0] # Use only first answer
var wakuDnsDiscovery = WakuDnsDiscovery.init(dnsDiscoveryUrl, resolver)
if wakuDnsDiscovery.isOk():
return wakuDnsDiscovery.get().findPeers().mapErr(
proc(e: cstring): string =
$e
)
return (await wakuDnsDiscovery.get().findPeers()).mapErr(
proc(e: cstring): string =
$e
)
else:
warn "Failed to init Waku DNS discovery"
@ -385,11 +388,11 @@ proc retrieveDynamicBootstrapNodes(
proc getBootstrapFromDiscDns(
conf: NetworkMonitorConf
): Result[seq[enr.Record], string] =
): Future[Result[seq[enr.Record], string]] {.async.} =
try:
let dnsNameServers = @[parseIpAddress("1.1.1.1"), parseIpAddress("1.0.0.1")]
let dynamicBootstrapNodesRes =
retrieveDynamicBootstrapNodes(true, conf.dnsDiscoveryUrl, dnsNameServers)
await retrieveDynamicBootstrapNodes(true, conf.dnsDiscoveryUrl, dnsNameServers)
if not dynamicBootstrapNodesRes.isOk():
error("failed discovering peers from DNS")
let dynamicBootstrapNodes = dynamicBootstrapNodesRes.get()
@ -412,7 +415,7 @@ proc getBootstrapFromDiscDns(
proc initAndStartApp(
conf: NetworkMonitorConf
): Result[(WakuNode, WakuDiscoveryV5), string] =
): Future[Result[(WakuNode, WakuDiscoveryV5), string]] {.async.} =
let bindIp =
try:
parseIpAddress("0.0.0.0")
@ -472,7 +475,7 @@ proc initAndStartApp(
else:
nodeRes.get()
var discv5BootstrapEnrsRes = getBootstrapFromDiscDns(conf)
var discv5BootstrapEnrsRes = await getBootstrapFromDiscDns(conf)
if discv5BootstrapEnrsRes.isErr():
error("failed discovering peers from DNS")
var discv5BootstrapEnrs = discv5BootstrapEnrsRes.get()
@ -604,7 +607,7 @@ when isMainModule:
let restClient = clientRest.get()
# start waku node
let nodeRes = initAndStartApp(conf)
let nodeRes = waitFor initAndStartApp(conf)
if nodeRes.isErr():
error "could not start node"
quit 1

View File

@ -80,10 +80,10 @@ proc destroyShared(self: ptr DiscoveryRequest) =
proc retrieveBootstrapNodes(
enrTreeUrl: string, ipDnsServer: string
): Result[seq[string], string] =
): Future[Result[seq[string], string]] {.async.} =
let dnsNameServers = @[parseIpAddress(ipDnsServer)]
let discoveredPeers: seq[RemotePeerInfo] = retrieveDynamicBootstrapNodes(
true, enrTreeUrl, dnsNameServers
let discoveredPeers: seq[RemotePeerInfo] = (
await retrieveDynamicBootstrapNodes(true, enrTreeUrl, dnsNameServers)
).valueOr:
return err("failed discovering peers from DNS: " & $error)
@ -126,7 +126,9 @@ proc process*(
return ok("discv5 stopped correctly")
of GET_BOOTSTRAP_NODES:
let nodes = retrieveBootstrapNodes($self[].enrTreeUrl, $self[].nameDnsServer).valueOr:
let nodes = (
await retrieveBootstrapNodes($self[].enrTreeUrl, $self[].nameDnsServer)
).valueOr:
error "GET_BOOTSTRAP_NODES failed", error = error
return err($error)

View File

@ -79,7 +79,7 @@ suite "Waku DNS Discovery":
var wakuDnsDisc = WakuDnsDiscovery.init(location, resolver).get()
let res = wakuDnsDisc.findPeers()
let res = await wakuDnsDisc.findPeers()
check:
# We have discovered all three nodes

2
vendor/nim-dnsdisc vendored

@ -1 +1 @@
Subproject commit 38f853df30bcfdb73055b7fd7de284a47eebecc2
Subproject commit c3d37c2860bcef9e3e2616ee4c53100fe7f0e845

View File

@ -448,5 +448,15 @@ proc updateBootstrapRecords*(
return err("wrong enr given: " & enrWithoutQuotes)
self.protocol.bootstrapRecords = newRecords
self.protocol.seedTable()
return ok()
proc updateBootstrapRecords*(
self: var WakuDiscoveryV5, updatedRecords: seq[enr.Record]
): void =
self.protocol.bootstrapRecords = updatedRecords
# If we're updating the table with nodes that already existed, it will log an error when trying
# to add a bootstrap node that was already there. That's ok.
self.protocol.seedTable()

View File

@ -6,7 +6,7 @@
## EIP-1459 is defined in https://eips.ethereum.org/EIPS/eip-1459
import
std/[options, net],
std/[options, net, sequtils],
chronicles,
chronos,
metrics,
@ -40,7 +40,9 @@ proc emptyResolver*(domain: string): Future[string] {.async, gcsafe.} =
debug "Empty resolver called", domain = domain
return ""
proc findPeers*(wdd: var WakuDnsDiscovery): Result[seq[RemotePeerInfo], cstring] =
proc findPeers*(
wdd: WakuDnsDiscovery
): Future[Result[seq[RemotePeerInfo], cstring]] {.async.} =
## Find peers to connect to using DNS based discovery
info "Finding peers using Waku DNS discovery"
@ -48,14 +50,13 @@ proc findPeers*(wdd: var WakuDnsDiscovery): Result[seq[RemotePeerInfo], cstring]
# Synchronise client tree using configured resolver
var tree: Tree
try:
tree = wdd.client.getTree(wdd.resolver)
# @TODO: this is currently a blocking operation to not violate memory safety
tree = (await syncTree(wdd.resolver, wdd.client.loc)).tryGet()
except Exception:
error "Failed to synchronise client tree"
waku_dnsdisc_errors.inc(labelValues = ["tree_sync_failure"])
return err("Node discovery failed")
let discoveredEnr = wdd.client.getNodeRecords()
let discoveredEnr = tree.getNodes().mapIt(it.record)
if discoveredEnr.len > 0:
info "Successfully discovered ENR", count = discoveredEnr.len
@ -97,7 +98,7 @@ proc init*(
proc retrieveDynamicBootstrapNodes*(
dnsDiscovery: bool, dnsDiscoveryUrl: string, dnsDiscoveryNameServers: seq[IpAddress]
): Result[seq[RemotePeerInfo], string] =
): Future[Result[seq[RemotePeerInfo], string]] {.async.} =
## Retrieve dynamic bootstrap nodes (DNS discovery)
if dnsDiscovery and dnsDiscoveryUrl != "":
@ -113,14 +114,15 @@ proc retrieveDynamicBootstrapNodes*(
proc resolver(domain: string): Future[string] {.async, gcsafe.} =
trace "resolving", domain = domain
let resolved = await dnsResolver.resolveTxt(domain)
return resolved[0] # Use only first answer
if resolved.len > 0:
return resolved[0] # Use only first answer
var wakuDnsDiscovery = WakuDnsDiscovery.init(dnsDiscoveryUrl, resolver)
if wakuDnsDiscovery.isOk():
return wakuDnsDiscovery.get().findPeers().mapErr(
proc(e: cstring): string =
$e
)
return (await wakuDnsDiscovery.get().findPeers()).mapErr(
proc(e: cstring): string =
$e
)
else:
warn "Failed to init Waku DNS discovery"

View File

@ -58,6 +58,7 @@ type Waku* = ref object
wakuDiscv5*: WakuDiscoveryV5
dynamicBootstrapNodes: seq[RemotePeerInfo]
dnsRetryLoopHandle: Future[void]
discoveryMngr: DiscoveryManager
node*: WakuNode
@ -215,17 +216,6 @@ proc new*(T: type Waku, confCopy: var WakuNodeConf): Result[Waku, string] =
return err("Failed to generate key: " & $keyRes.error)
confCopy.nodekey = some(keyRes.get())
debug "Retrieve dynamic bootstrap nodes"
let dynamicBootstrapNodesRes = waku_dnsdisc.retrieveDynamicBootstrapNodes(
confCopy.dnsDiscovery, confCopy.dnsDiscoveryUrl, confCopy.dnsDiscoveryNameServers
)
if dynamicBootstrapNodesRes.isErr():
error "Retrieving dynamic bootstrap nodes failed",
error = dynamicBootstrapNodesRes.error
return err(
"Retrieving dynamic bootstrap nodes failed: " & dynamicBootstrapNodesRes.error
)
var relay = newCircuitRelay(confCopy.isRelayClient)
let nodeRes = setupNode(confCopy, rng, relay)
@ -255,7 +245,6 @@ proc new*(T: type Waku, confCopy: var WakuNodeConf): Result[Waku, string] =
rng: rng,
key: confCopy.nodekey.get(),
node: node,
dynamicBootstrapNodes: dynamicBootstrapNodesRes.get(),
deliveryMonitor: deliveryMonitor,
)
@ -351,7 +340,57 @@ proc updateWaku(waku: ptr Waku): Result[void, string] =
return ok()
proc startWaku*(waku: ptr Waku): Future[Result[void, string]] {.async: (raises: []).} =
proc startDnsDiscoveryRetryLoop(waku: ptr Waku): Future[void] {.async.} =
while true:
await sleepAsync(30.seconds)
let dynamicBootstrapNodesRes = await waku_dnsdisc.retrieveDynamicBootstrapNodes(
waku.conf.dnsDiscovery, waku.conf.dnsDiscoveryUrl,
waku.conf.dnsDiscoveryNameServers,
)
if dynamicBootstrapNodesRes.isErr():
error "Retrieving dynamic bootstrap nodes failed",
error = dynamicBootstrapNodesRes.error
continue
waku[].dynamicBootstrapNodes = dynamicBootstrapNodesRes.get()
if not waku[].wakuDiscv5.isNil():
let dynamicBootstrapEnrs = waku[].dynamicBootstrapNodes
.filterIt(it.hasUdpPort())
.mapIt(it.enr.get().toUri())
var discv5BootstrapEnrs: seq[enr.Record]
# parse enrURIs from the configuration and add the resulting ENRs to the discv5BootstrapEnrs seq
for enrUri in dynamicBootstrapEnrs:
addBootstrapNode(enrUri, discv5BootstrapEnrs)
waku[].wakuDiscv5.updateBootstrapRecords(
waku[].wakuDiscv5.protocol.bootstrapRecords & discv5BootstrapEnrs
)
info "Connecting to dynamic bootstrap peers"
try:
await connectToNodes(
waku[].node, waku[].dynamicBootstrapNodes, "dynamic bootstrap"
)
except CatchableError:
error "failed to connect to dynamic bootstrap nodes: " & getCurrentExceptionMsg()
return
proc startWaku*(waku: ptr Waku): Future[Result[void, string]] {.async.} =
debug "Retrieve dynamic bootstrap nodes"
let dynamicBootstrapNodesRes = await waku_dnsdisc.retrieveDynamicBootstrapNodes(
waku.conf.dnsDiscovery, waku.conf.dnsDiscoveryUrl, waku.conf.dnsDiscoveryNameServers
)
if dynamicBootstrapNodesRes.isErr():
error "Retrieving dynamic bootstrap nodes failed",
error = dynamicBootstrapNodesRes.error
# Start Dns Discovery retry loop
waku[].dnsRetryLoopHandle = waku.startDnsDiscoveryRetryLoop()
else:
waku[].dynamicBootstrapNodes = dynamicBootstrapNodesRes.get()
if not waku[].conf.discv5Only:
(await startNode(waku.node, waku.conf, waku.dynamicBootstrapNodes)).isOkOr:
return err("error while calling startNode: " & $error)
@ -400,3 +439,6 @@ proc stop*(waku: Waku): Future[void] {.async: (raises: [Exception]).} =
if not waku.node.isNil():
await waku.node.stop()
if not waku.dnsRetryLoopHandle.isNil():
await waku.dnsRetryLoopHandle.cancelAndWait()