2022-11-04 09:52:27 +00:00
|
|
|
when (NimMajor, NimMinor) < (1, 4):
|
|
|
|
{.push raises: [Defect].}
|
|
|
|
else:
|
|
|
|
{.push raises: [].}
|
2021-11-01 18:02:39 +00:00
|
|
|
|
|
|
|
import
|
2022-06-28 23:59:38 +00:00
|
|
|
std/[strutils, options],
|
2021-11-01 18:02:39 +00:00
|
|
|
stew/results,
|
2023-04-17 13:21:20 +00:00
|
|
|
stew/shims/net,
|
2023-02-06 10:36:37 +00:00
|
|
|
chronos,
|
|
|
|
chronicles,
|
|
|
|
metrics,
|
2023-04-17 13:21:20 +00:00
|
|
|
libp2p/multiaddress,
|
2023-02-06 10:36:37 +00:00
|
|
|
eth/keys,
|
|
|
|
eth/p2p/discoveryv5/enr,
|
|
|
|
eth/p2p/discoveryv5/node,
|
|
|
|
eth/p2p/discoveryv5/protocol
|
|
|
|
import
|
2023-04-24 14:37:54 +00:00
|
|
|
./waku_core,
|
2023-03-07 09:52:12 +00:00
|
|
|
./waku_enr
|
2021-11-01 18:02:39 +00:00
|
|
|
|
2023-03-07 09:52:12 +00:00
|
|
|
export protocol, waku_enr
|
2021-11-01 18:02:39 +00:00
|
|
|
|
2023-03-06 16:18:41 +00:00
|
|
|
|
2021-11-01 18:02:39 +00:00
|
|
|
declarePublicGauge waku_discv5_discovered, "number of nodes discovered"
|
|
|
|
declarePublicGauge waku_discv5_errors, "number of waku discv5 errors", ["type"]
|
|
|
|
|
|
|
|
logScope:
|
2022-11-03 15:36:24 +00:00
|
|
|
topics = "waku discv5"
|
2021-11-01 18:02:39 +00:00
|
|
|
|
2023-02-06 10:36:37 +00:00
|
|
|
|
2023-03-30 07:35:13 +00:00
|
|
|
type WakuDiscoveryV5* = ref object
|
2021-11-01 18:02:39 +00:00
|
|
|
protocol*: protocol.Protocol
|
|
|
|
listening*: bool
|
|
|
|
|
2023-02-06 10:36:37 +00:00
|
|
|
|
2021-11-12 14:10:54 +00:00
|
|
|
####################
|
|
|
|
# Helper functions #
|
|
|
|
####################
|
|
|
|
|
2023-02-06 10:36:37 +00:00
|
|
|
proc parseBootstrapAddress(address: string): Result[enr.Record, cstring] =
|
2021-11-01 18:02:39 +00:00
|
|
|
logScope:
|
2023-02-06 10:36:37 +00:00
|
|
|
address = address
|
2021-11-01 18:02:39 +00:00
|
|
|
|
|
|
|
if address[0] == '/':
|
2023-02-06 10:36:37 +00:00
|
|
|
return err("MultiAddress bootstrap addresses are not supported")
|
|
|
|
|
|
|
|
let lowerCaseAddress = toLowerAscii(address)
|
|
|
|
if lowerCaseAddress.startsWith("enr:"):
|
|
|
|
var enrRec: enr.Record
|
|
|
|
if not enrRec.fromURI(address):
|
|
|
|
return err("Invalid ENR bootstrap record")
|
|
|
|
|
|
|
|
return ok(enrRec)
|
|
|
|
|
|
|
|
elif lowerCaseAddress.startsWith("enode:"):
|
|
|
|
return err("ENode bootstrap addresses are not supported")
|
|
|
|
|
2021-11-01 18:02:39 +00:00
|
|
|
else:
|
2023-02-06 10:36:37 +00:00
|
|
|
return err("Ignoring unrecognized bootstrap address type")
|
2021-11-01 18:02:39 +00:00
|
|
|
|
2022-03-17 16:33:17 +00:00
|
|
|
proc addBootstrapNode*(bootstrapAddr: string,
|
2021-11-01 18:02:39 +00:00
|
|
|
bootstrapEnrs: var seq[enr.Record]) =
|
|
|
|
# Ignore empty lines or lines starting with #
|
|
|
|
if bootstrapAddr.len == 0 or bootstrapAddr[0] == '#':
|
|
|
|
return
|
|
|
|
|
|
|
|
let enrRes = parseBootstrapAddress(bootstrapAddr)
|
2023-03-30 07:35:13 +00:00
|
|
|
if enrRes.isErr():
|
|
|
|
debug "ignoring invalid bootstrap address", reason = enrRes.error
|
|
|
|
return
|
2022-12-07 11:30:32 +00:00
|
|
|
|
2023-03-30 07:35:13 +00:00
|
|
|
bootstrapEnrs.add(enrRes.value)
|
2021-11-12 14:10:54 +00:00
|
|
|
|
|
|
|
|
2021-11-01 18:02:39 +00:00
|
|
|
####################
|
|
|
|
# Discovery v5 API #
|
|
|
|
####################
|
|
|
|
|
|
|
|
proc new*(T: type WakuDiscoveryV5,
|
|
|
|
extIp: Option[ValidIpAddress],
|
2023-03-30 07:35:13 +00:00
|
|
|
extTcpPort: Option[Port],
|
|
|
|
extUdpPort: Option[Port],
|
2021-11-01 18:02:39 +00:00
|
|
|
bindIP: ValidIpAddress,
|
|
|
|
discv5UdpPort: Port,
|
2023-02-07 13:06:50 +00:00
|
|
|
bootstrapEnrs = newSeq[enr.Record](),
|
2021-11-01 18:02:39 +00:00
|
|
|
enrAutoUpdate = false,
|
2021-12-06 19:51:37 +00:00
|
|
|
privateKey: keys.PrivateKey,
|
2023-03-08 14:44:10 +00:00
|
|
|
flags: CapabilitiesBitfield,
|
2023-02-07 13:06:50 +00:00
|
|
|
multiaddrs = newSeq[MultiAddress](),
|
2022-09-07 15:31:27 +00:00
|
|
|
rng: ref HmacDrbgContext,
|
2022-03-01 14:11:56 +00:00
|
|
|
discv5Config: protocol.DiscoveryConfig = protocol.defaultDiscoveryConfig): T =
|
2022-12-07 11:30:32 +00:00
|
|
|
|
2023-03-30 07:35:13 +00:00
|
|
|
# Add the waku capabilities field
|
2023-03-08 14:44:10 +00:00
|
|
|
var enrInitFields = @[(CapabilitiesEnrField, @[flags.byte])]
|
2023-02-07 13:06:50 +00:00
|
|
|
|
2023-03-30 07:35:13 +00:00
|
|
|
# Add the waku multiaddrs field
|
2023-02-14 08:11:48 +00:00
|
|
|
if multiaddrs.len > 0:
|
2023-03-30 07:35:13 +00:00
|
|
|
let value = waku_enr.encodeMultiaddrs(multiaddrs)
|
|
|
|
enrInitFields.add((MultiaddrEnrField, value))
|
2022-12-07 11:30:32 +00:00
|
|
|
|
2021-11-01 18:02:39 +00:00
|
|
|
let protocol = newProtocol(
|
|
|
|
privateKey,
|
2023-03-30 07:35:13 +00:00
|
|
|
enrIp = extIp,
|
|
|
|
enrTcpPort = extTcpPort,
|
|
|
|
enrUdpPort = extUdpPort,
|
2021-11-12 14:10:54 +00:00
|
|
|
enrInitFields,
|
2021-11-01 18:02:39 +00:00
|
|
|
bootstrapEnrs,
|
|
|
|
bindPort = discv5UdpPort,
|
|
|
|
bindIp = bindIP,
|
|
|
|
enrAutoUpdate = enrAutoUpdate,
|
2022-03-01 14:11:56 +00:00
|
|
|
config = discv5Config,
|
2023-03-30 07:35:13 +00:00
|
|
|
rng = rng
|
|
|
|
)
|
2022-12-07 11:30:32 +00:00
|
|
|
|
2023-03-30 07:35:13 +00:00
|
|
|
WakuDiscoveryV5(protocol: protocol, listening: false)
|
2021-11-01 18:02:39 +00:00
|
|
|
|
2023-03-30 07:35:13 +00:00
|
|
|
# TODO: Do not raise an exception, return a result
|
|
|
|
proc open*(wd: WakuDiscoveryV5) {.raises: [CatchableError].} =
|
2021-11-01 18:02:39 +00:00
|
|
|
debug "Opening Waku discovery v5 ports"
|
2023-03-30 07:35:13 +00:00
|
|
|
if wd.listening:
|
|
|
|
return
|
|
|
|
|
|
|
|
wd.protocol.open()
|
|
|
|
wd.listening = true
|
|
|
|
|
|
|
|
proc start*(wd: WakuDiscoveryV5) =
|
|
|
|
debug "starting Waku discovery v5 service"
|
|
|
|
wd.protocol.start()
|
|
|
|
|
|
|
|
proc closeWait*(wd: WakuDiscoveryV5) {.async.} =
|
|
|
|
debug "closing Waku discovery v5 node"
|
|
|
|
if not wd.listening:
|
|
|
|
return
|
|
|
|
|
|
|
|
wd.listening = false
|
|
|
|
await wd.protocol.closeWait()
|
2021-11-01 18:02:39 +00:00
|
|
|
|
2023-03-30 07:35:13 +00:00
|
|
|
proc findRandomPeers*(wd: WakuDiscoveryV5): Future[Result[seq[RemotePeerInfo], cstring]] {.async.} =
|
|
|
|
## Find random peers to connect to using Discovery v5
|
|
|
|
|
|
|
|
# Query for a random target and collect all discovered nodes
|
|
|
|
let discoveredNodes = await wd.protocol.queryRandom()
|
|
|
|
|
|
|
|
## Filter based on our needs
|
|
|
|
# let filteredNodes = discoveredNodes.filter(isWakuNode) # Currently only a single predicate
|
|
|
|
# TODO: consider node filtering based on ENR; we do not filter based on ENR in the first waku discv5 beta stage
|
2021-11-01 18:02:39 +00:00
|
|
|
|
2023-03-30 07:35:13 +00:00
|
|
|
var discoveredPeers: seq[RemotePeerInfo]
|
|
|
|
|
|
|
|
for node in discoveredNodes:
|
|
|
|
let res = node.record.toRemotePeerInfo()
|
|
|
|
if res.isErr():
|
|
|
|
error "failed to convert ENR to peer info", enr= $node.record, err=res.error
|
|
|
|
waku_discv5_errors.inc(labelValues = ["peer_info_failure"])
|
|
|
|
continue
|
2021-11-01 18:02:39 +00:00
|
|
|
|
2023-03-30 07:35:13 +00:00
|
|
|
discoveredPeers.add(res.value)
|
2021-11-01 18:02:39 +00:00
|
|
|
|
|
|
|
|
2023-03-30 07:35:13 +00:00
|
|
|
return ok(discoveredPeers)
|