mirror of https://github.com/waku-org/nwaku.git
discv5 dns bootstrap (#888)
This commit is contained in:
parent
266d6a7e80
commit
e1146b2df8
|
@ -52,7 +52,7 @@ procSuite "Waku Discovery v5":
|
|||
some(extIp), some(nodeTcpPort1), some(nodeUdpPort1),
|
||||
bindIp,
|
||||
nodeUdpPort1,
|
||||
@[],
|
||||
newSeq[string](),
|
||||
false,
|
||||
keys.PrivateKey(nodeKey1.skkey),
|
||||
flags,
|
||||
|
|
|
@ -45,7 +45,7 @@ proc parseBootstrapAddress(address: TaintedString):
|
|||
else:
|
||||
return err "Ignoring unrecognized bootstrap address type"
|
||||
|
||||
proc addBootstrapNode(bootstrapAddr: string,
|
||||
proc addBootstrapNode*(bootstrapAddr: string,
|
||||
bootstrapEnrs: var seq[enr.Record]) =
|
||||
# Ignore empty lines or lines starting with #
|
||||
if bootstrapAddr.len == 0 or bootstrapAddr[0] == '#':
|
||||
|
@ -103,18 +103,13 @@ proc new*(T: type WakuDiscoveryV5,
|
|||
extTcpPort, extUdpPort: Option[Port],
|
||||
bindIP: ValidIpAddress,
|
||||
discv5UdpPort: Port,
|
||||
bootstrapNodes: seq[string],
|
||||
bootstrapEnrs: seq[enr.Record],
|
||||
enrAutoUpdate = false,
|
||||
privateKey: keys.PrivateKey,
|
||||
flags: WakuEnrBitfield,
|
||||
enrFields: openArray[(string, seq[byte])],
|
||||
rng: ref BrHmacDrbgContext,
|
||||
discv5Config: protocol.DiscoveryConfig = protocol.defaultDiscoveryConfig): T =
|
||||
|
||||
var bootstrapEnrs: seq[enr.Record]
|
||||
for node in bootstrapNodes:
|
||||
addBootstrapNode(node, bootstrapEnrs)
|
||||
|
||||
## TODO: consider loading from a configurable bootstrap file
|
||||
|
||||
## We always add the waku field as specified
|
||||
|
@ -134,6 +129,38 @@ proc new*(T: type WakuDiscoveryV5,
|
|||
|
||||
return WakuDiscoveryV5(protocol: protocol, listening: false)
|
||||
|
||||
# constructor that takes bootstrap Enrs in Enr Uri form
|
||||
proc new*(T: type WakuDiscoveryV5,
|
||||
extIp: Option[ValidIpAddress],
|
||||
extTcpPort, extUdpPort: Option[Port],
|
||||
bindIP: ValidIpAddress,
|
||||
discv5UdpPort: Port,
|
||||
bootstrapNodes: seq[string],
|
||||
enrAutoUpdate = false,
|
||||
privateKey: keys.PrivateKey,
|
||||
flags: WakuEnrBitfield,
|
||||
enrFields: openArray[(string, seq[byte])],
|
||||
rng: ref BrHmacDrbgContext,
|
||||
discv5Config: protocol.DiscoveryConfig = protocol.defaultDiscoveryConfig): T =
|
||||
|
||||
var bootstrapEnrs: seq[enr.Record]
|
||||
for node in bootstrapNodes:
|
||||
addBootstrapNode(node, bootstrapEnrs)
|
||||
|
||||
return WakuDiscoveryV5.new(
|
||||
extIP, extTcpPort, extUdpPort,
|
||||
bindIP,
|
||||
discv5UdpPort,
|
||||
bootstrapEnrs,
|
||||
enrAutoUpdate,
|
||||
privateKey,
|
||||
flags,
|
||||
enrFields,
|
||||
rng,
|
||||
discv5Config
|
||||
)
|
||||
|
||||
|
||||
proc open*(wakuDiscv5: WakuDiscoveryV5) {.raises: [Defect, CatchableError].} =
|
||||
debug "Opening Waku discovery v5 ports"
|
||||
|
||||
|
|
|
@ -963,7 +963,7 @@ when isMainModule:
|
|||
# Setup functions #
|
||||
###################
|
||||
|
||||
# 1/6 Setup storage
|
||||
# 1/7 Setup storage
|
||||
proc setupStorage(conf: WakuNodeConf):
|
||||
SetupResult[tuple[pStorage: WakuPeerStorage, mStorage: WakuMessageStore]] =
|
||||
|
||||
|
@ -1013,9 +1013,35 @@ when isMainModule:
|
|||
|
||||
ok(storeTuple)
|
||||
|
||||
# 2/6 Initialize node
|
||||
# 2/7 Retrieve dynamic bootstrap nodes
|
||||
proc retrieveDynamicBootstrapNodes(conf: WakuNodeConf): SetupResult[seq[RemotePeerInfo]] =
|
||||
# DNS discovery
|
||||
if conf.dnsDiscovery and conf.dnsDiscoveryUrl != "":
|
||||
debug "Discovering nodes using Waku DNS discovery", url=conf.dnsDiscoveryUrl
|
||||
|
||||
var nameServers: seq[TransportAddress]
|
||||
for ip in conf.dnsDiscoveryNameServers:
|
||||
nameServers.add(initTAddress(ip, Port(53))) # Assume all servers use port 53
|
||||
|
||||
let dnsResolver = DnsResolver.new(nameServers)
|
||||
|
||||
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
|
||||
|
||||
var wakuDnsDiscovery = WakuDnsDiscovery.init(conf.dnsDiscoveryUrl,
|
||||
resolver)
|
||||
if wakuDnsDiscovery.isOk:
|
||||
return wakuDnsDiscovery.get().findPeers()
|
||||
.mapErr(proc (e: cstring): string = $e)
|
||||
else:
|
||||
warn "Failed to init Waku DNS discovery"
|
||||
|
||||
# 3/7 Initialize node
|
||||
proc initNode(conf: WakuNodeConf,
|
||||
pStorage: WakuPeerStorage = nil): SetupResult[WakuNode] =
|
||||
pStorage: WakuPeerStorage = nil,
|
||||
dynamicBootstrapNodes: openArray[RemotePeerInfo] = @[]): SetupResult[WakuNode] =
|
||||
|
||||
## Setup a basic Waku v2 node based on a supplied configuration
|
||||
## file. Optionally include persistent peer storage.
|
||||
|
@ -1075,11 +1101,26 @@ when isMainModule:
|
|||
discoveryConfig = DiscoveryConfig.init(
|
||||
conf.discv5TableIpLimit, conf.discv5BucketIpLimit, conf.discv5BitsPerHop)
|
||||
|
||||
# select dynamic bootstrap nodes that have an ENR containing a udp port.
|
||||
# Discv5 only supports UDP https://github.com/ethereum/devp2p/blob/master/discv5/discv5-theory.md)
|
||||
var discv5BootstrapEnrs: seq[enr.Record]
|
||||
for n in dynamicBootstrapNodes:
|
||||
if n.enr.isSome():
|
||||
let
|
||||
enr = n.enr.get()
|
||||
tenrRes = enr.toTypedRecord()
|
||||
if tenrRes.isOk() and (tenrRes.get().udp.isSome() or tenrRes.get().udp6.isSome()):
|
||||
discv5BootstrapEnrs.add(enr)
|
||||
|
||||
# parse enrURIs from the configuration and add the resulting ENRs to the discv5BootstrapEnrs seq
|
||||
for enrUri in conf.discv5BootstrapNodes:
|
||||
addBootstrapNode(enrUri, discv5BootstrapEnrs)
|
||||
|
||||
node.wakuDiscv5 = WakuDiscoveryV5.new(
|
||||
extIP, extPort, some(discv5UdpPort),
|
||||
conf.listenAddress,
|
||||
discv5UdpPort,
|
||||
conf.discv5BootstrapNodes,
|
||||
discv5BootstrapEnrs,
|
||||
conf.discv5EnrAutoUpdate,
|
||||
keys.PrivateKey(conf.nodekey.skkey),
|
||||
wakuFlags,
|
||||
|
@ -1090,7 +1131,7 @@ when isMainModule:
|
|||
|
||||
ok(node)
|
||||
|
||||
# 3/6 Mount and initialize configured protocols
|
||||
# 4/7 Mount and initialize configured protocols
|
||||
proc setupProtocols(node: var WakuNode,
|
||||
conf: WakuNodeConf,
|
||||
mStorage: WakuMessageStore = nil): SetupResult[bool] =
|
||||
|
@ -1161,8 +1202,9 @@ when isMainModule:
|
|||
|
||||
ok(true) # Success
|
||||
|
||||
# 4/6 Start node and mounted protocols
|
||||
proc startNode(node: WakuNode, conf: WakuNodeConf): SetupResult[bool] =
|
||||
# 5/7 Start node and mounted protocols
|
||||
proc startNode(node: WakuNode, conf: WakuNodeConf,
|
||||
dynamicBootstrapNodes: seq[RemotePeerInfo] = @[]): SetupResult[bool] =
|
||||
## Start a configured node and all mounted protocols.
|
||||
## Resume history, connect to static nodes and start
|
||||
## keep-alive, if configured.
|
||||
|
@ -1170,7 +1212,7 @@ when isMainModule:
|
|||
# Start Waku v2 node
|
||||
waitFor node.start()
|
||||
|
||||
# start discv5 and connect to discovered nodes
|
||||
# Start discv5 and connect to discovered nodes
|
||||
if conf.discv5Discovery:
|
||||
if not waitFor node.startDiscv5():
|
||||
error "could not start Discovery v5"
|
||||
|
@ -1183,38 +1225,16 @@ when isMainModule:
|
|||
if conf.staticnodes.len > 0:
|
||||
waitFor connectToNodes(node, conf.staticnodes, "static")
|
||||
|
||||
# Connect to discovered nodes
|
||||
if conf.dnsDiscovery and conf.dnsDiscoveryUrl != "":
|
||||
debug "Discovering nodes using Waku DNS discovery", url=conf.dnsDiscoveryUrl
|
||||
|
||||
var nameServers: seq[TransportAddress]
|
||||
for ip in conf.dnsDiscoveryNameServers:
|
||||
nameServers.add(initTAddress(ip, Port(53))) # Assume all servers use port 53
|
||||
|
||||
let dnsResolver = DnsResolver.new(nameServers)
|
||||
|
||||
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
|
||||
|
||||
var wakuDnsDiscovery = WakuDnsDiscovery.init(conf.dnsDiscoveryUrl,
|
||||
resolver)
|
||||
if wakuDnsDiscovery.isOk:
|
||||
let discoveredPeers = wakuDnsDiscovery.get().findPeers()
|
||||
if discoveredPeers.isOk:
|
||||
info "Connecting to discovered peers"
|
||||
waitFor connectToNodes(node, discoveredPeers.get(), "dnsdisc")
|
||||
else:
|
||||
warn "Failed to init Waku DNS discovery"
|
||||
|
||||
info "Connecting to dynamic bootstrap peers"
|
||||
waitFor connectToNodes(node, dynamicBootstrapNodes, "dynamic bootstrap")
|
||||
|
||||
# Start keepalive, if enabled
|
||||
if conf.keepAlive:
|
||||
node.startKeepalive()
|
||||
|
||||
ok(true) # Success
|
||||
|
||||
# 5/6 Start monitoring tools and external interfaces
|
||||
# 6/7 Start monitoring tools and external interfaces
|
||||
proc startExternal(node: WakuNode, conf: WakuNodeConf): SetupResult[bool] =
|
||||
## Start configured external interfaces and monitoring tools
|
||||
## on a Waku v2 node, including the RPC API and metrics
|
||||
|
@ -1242,7 +1262,7 @@ when isMainModule:
|
|||
# Node setup #
|
||||
##############
|
||||
|
||||
debug "1/6 Setting up storage"
|
||||
debug "1/7 Setting up storage"
|
||||
|
||||
var
|
||||
pStorage: WakuPeerStorage
|
||||
|
@ -1251,44 +1271,53 @@ when isMainModule:
|
|||
let setupStorageRes = setupStorage(conf)
|
||||
|
||||
if setupStorageRes.isErr:
|
||||
error "1/6 Setting up storage failed. Continuing without storage."
|
||||
error "1/7 Setting up storage failed. Continuing without storage."
|
||||
else:
|
||||
(pStorage, mStorage) = setupStorageRes.get()
|
||||
|
||||
debug "2/6 Initializing node"
|
||||
debug "2/7 Retrieve dynamic bootstrap nodes"
|
||||
|
||||
var dynamicBootstrapNodes: seq[RemotePeerInfo]
|
||||
let dynamicBootstrapNodesRes = retrieveDynamicBootstrapNodes(conf)
|
||||
if dynamicBootstrapNodesRes.isErr:
|
||||
error "2/7 Retrieving dynamic bootstrap nodes failed. Continuing without dynamic bootstrap nodes."
|
||||
else:
|
||||
dynamicBootstrapNodes = dynamicBootstrapNodesRes.get()
|
||||
|
||||
let initNodeRes = initNode(conf, pStorage)
|
||||
debug "3/7 Initializing node"
|
||||
|
||||
let initNodeRes = initNode(conf, pStorage, dynamicBootstrapNodes)
|
||||
|
||||
if initNodeRes.isErr:
|
||||
error "2/6 Initializing node failed. Quitting."
|
||||
error "3/7 Initializing node failed. Quitting."
|
||||
quit(QuitFailure)
|
||||
else:
|
||||
node = initNodeRes.get()
|
||||
|
||||
debug "3/6 Mounting protocols"
|
||||
debug "4/7 Mounting protocols"
|
||||
|
||||
let setupProtocolsRes = setupProtocols(node, conf, mStorage)
|
||||
|
||||
if setupProtocolsRes.isErr:
|
||||
error "3/6 Mounting protocols failed. Continuing in current state."
|
||||
error "4/7 Mounting protocols failed. Continuing in current state."
|
||||
|
||||
debug "4/6 Starting node and mounted protocols"
|
||||
debug "5/7 Starting node and mounted protocols"
|
||||
|
||||
let startNodeRes = startNode(node, conf)
|
||||
let startNodeRes = startNode(node, conf, dynamicBootstrapNodes)
|
||||
|
||||
if startNodeRes.isErr:
|
||||
error "4/6 Starting node and mounted protocols failed. Continuing in current state."
|
||||
error "5/7 Starting node and mounted protocols failed. Continuing in current state."
|
||||
|
||||
debug "5/6 Starting monitoring and external interfaces"
|
||||
debug "6/7 Starting monitoring and external interfaces"
|
||||
|
||||
let startExternalRes = startExternal(node, conf)
|
||||
|
||||
if startExternalRes.isErr:
|
||||
error "5/6 Starting monitoring and external interfaces failed. Continuing in current state."
|
||||
error "6/7 Starting monitoring and external interfaces failed. Continuing in current state."
|
||||
|
||||
debug "6/6 Setting up shutdown hooks"
|
||||
debug "7/7 Setting up shutdown hooks"
|
||||
|
||||
# 6/6 Setup graceful shutdown hooks
|
||||
# 7/7 Setup graceful shutdown hooks
|
||||
## Setup shutdown hooks for this process.
|
||||
## Stop node gracefully on shutdown.
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ type
|
|||
RemotePeerInfo* = ref object of RootObj
|
||||
peerId*: PeerID
|
||||
addrs*: seq[MultiAddress]
|
||||
enr*: Option[enr.Record]
|
||||
protocols*: seq[string]
|
||||
|
||||
func `$`*(remotePeerInfo: RemotePeerInfo): string =
|
||||
|
@ -26,11 +27,13 @@ proc init*(
|
|||
p: typedesc[RemotePeerInfo],
|
||||
peerId: PeerID,
|
||||
addrs: seq[MultiAddress] = @[],
|
||||
enr: Option[enr.Record] = none(enr.Record),
|
||||
protocols: seq[string] = @[]): RemotePeerInfo =
|
||||
|
||||
let remotePeerInfo = RemotePeerInfo(
|
||||
peerId: peerId,
|
||||
addrs: addrs,
|
||||
enr: enr,
|
||||
protocols: protocols)
|
||||
|
||||
return remotePeerInfo
|
||||
|
@ -38,12 +41,14 @@ proc init*(
|
|||
proc init*(p: typedesc[RemotePeerInfo],
|
||||
peerId: string,
|
||||
addrs: seq[MultiAddress] = @[],
|
||||
enr: Option[enr.Record] = none(enr.Record),
|
||||
protocols: seq[string] = @[]): RemotePeerInfo
|
||||
{.raises: [Defect, ResultError[cstring], LPError].} =
|
||||
|
||||
let remotePeerInfo = RemotePeerInfo(
|
||||
peerId: PeerID.init(peerId).tryGet(),
|
||||
addrs: addrs,
|
||||
enr: enr,
|
||||
protocols: protocols)
|
||||
|
||||
return remotePeerInfo
|
||||
|
@ -145,11 +150,12 @@ proc toRemotePeerInfo*(enr: enr.Record): Result[RemotePeerInfo, cstring] =
|
|||
if addrs.len == 0:
|
||||
return err("enr: no addresses in record")
|
||||
|
||||
return ok(RemotePeerInfo.init(peerId, addrs))
|
||||
return ok(RemotePeerInfo.init(peerId, addrs, some(enr)))
|
||||
|
||||
## Converts the local peerInfo to dialable RemotePeerInfo
|
||||
## Useful for testing or internal connections
|
||||
proc toRemotePeerInfo*(peerInfo: PeerInfo): RemotePeerInfo =
|
||||
RemotePeerInfo.init(peerInfo.peerId,
|
||||
peerInfo.addrs,
|
||||
none(enr.Record), # we could generate an ENR from PeerInfo
|
||||
peerInfo.protocols)
|
||||
|
|
Loading…
Reference in New Issue