feat(wakunode): advertise custom multiaddresses (#1509)

* feat(wakunode2): ability to advertise custom multiaddresses

* test(wakunode): test the feature

* fix(wakunode): remove rln diff

* revert(rln-relay): change that creeped into the diff

* fix(wakunode): move extMultiaddrs closer to nat

* fix(waku_node): idiomatic default arg

* fix(config): shortened validation

* fix(wakunode): discoverable via discv5 and dnsdisc
This commit is contained in:
Aaryamann Challani 2023-01-26 15:48:30 +05:30 committed by GitHub
parent ea4703e9a2
commit d09ec815ce
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 89 additions and 14 deletions

View File

@ -10,6 +10,7 @@ import
confutils/toml/std/net as confTomlNet,
libp2p/crypto/crypto,
libp2p/crypto/secp,
libp2p/multiaddress,
nimcrypto/utils
import
../../waku/common/confutils/envvar/defs as confEnvvarDefs,
@ -75,6 +76,10 @@ type
"Must be one of: any, none, upnp, pmp, extip:<IP>."
defaultValue: "any" }: string
extMultiAddrs* {.
desc: "External multiaddresses to advertise to the network. Argument may be repeated."
name: "ext-multiaddr" }: seq[string]
maxConnections* {.
desc: "Maximum allowed number of libp2p connections."
defaultValue: 50
@ -513,6 +518,12 @@ proc validateStoreMessageRetentionPolicy*(val: string): ConfResult[string] =
else:
err("invalid 'store message retention policy' option format: " & val)
proc validateExtMultiAddrs*(vals: seq[string]): ConfResult[seq[MultiAddress]] =
var multiaddrs: seq[MultiAddress]
for val in vals:
let multiaddr = ? MultiAddress.init(val)
multiaddrs.add(multiaddr)
ok(multiaddrs)
## Load

View File

@ -252,6 +252,14 @@ proc initNode(conf: WakuNodeConf,
some(Port(uint16(conf.tcpPort) + conf.portsShift))
else:
extTcpPort
extMultiAddrs = if (conf.extMultiAddrs.len > 0):
let extMultiAddrsValidationRes = validateExtMultiAddrs(conf.extMultiAddrs)
if extMultiAddrsValidationRes.isErr():
return err("invalid external multiaddress: " & extMultiAddrsValidationRes.error)
else:
extMultiAddrsValidationRes.get()
else:
@[]
wakuFlags = initWakuFlags(conf.lightpush,
conf.filter,
@ -266,6 +274,7 @@ proc initNode(conf: WakuNodeConf,
node = WakuNode.new(conf.nodekey,
conf.listenAddress, Port(uint16(conf.tcpPort) + conf.portsShift),
extIp, extPort,
extMultiAddrs,
pStorage,
conf.maxConnections.int,
Port(uint16(conf.websocketPort) + conf.portsShift),
@ -279,8 +288,7 @@ proc initNode(conf: WakuNodeConf,
dns4DomainName,
discv5UdpPort,
some(conf.agentString),
some(conf.peerStoreCapacity),
)
some(conf.peerStoreCapacity))
except:
return err("failed to create waku node instance: " & getCurrentExceptionMsg())

View File

@ -4,8 +4,8 @@ import
stew/byteutils,
stew/shims/net as stewNet,
testutils/unittests,
chronicles,
chronos,
chronicles,
chronos,
libp2p/crypto/crypto,
libp2p/crypto/secp,
libp2p/multiaddress,
@ -24,7 +24,7 @@ import
procSuite "WakuNode":
let rng = crypto.newRng()
asyncTest "Protocol matcher works as expected":
let
nodeKey1 = crypto.PrivateKey.random(Secp256k1, rng[])[]
@ -153,9 +153,9 @@ procSuite "WakuNode":
expect IOError:
# gibberish
discard WakuNode.new(nodeKey1, ValidIpAddress.init("0.0.0.0"),
bindPort = Port(61004),
wsBindPort = Port(8000),
wssEnabled = true,
bindPort = Port(61004),
wsBindPort = Port(8000),
wssEnabled = true,
secureKey = "../../waku/v2/node/key_dummy.txt")
asyncTest "Peer info updates with correct announced addresses":
@ -216,7 +216,7 @@ procSuite "WakuNode":
node.announcedAddresses.len == 1
node.announcedAddresses.contains(expectedDns4Addr)
asyncTest "Agent string is set and advertised correctly":
let
# custom agent string
@ -250,4 +250,37 @@ procSuite "WakuNode":
node1Agent == expectedAgentString1
node2Agent == expectedAgentString2
await allFutures(node1.stop(), node2.stop())
await allFutures(node1.stop(), node2.stop())
asyncTest "Custom multiaddresses are set and advertised correctly":
let
# custom multiaddress
expectedMultiaddress1 = MultiAddress.init("/ip4/200.200.200.200/tcp/1234").get()
# Note: this could have been done with a single node, but it is useful to
# have two nodes to check that the multiaddress is advertised correctly
let
# node with custom multiaddress
nodeKey1 = crypto.PrivateKey.random(Secp256k1, rng[])[]
node1 = WakuNode.new(nodeKey1, ValidIpAddress.init("0.0.0.0"), Port(61018),
extMultiAddrs = @[expectedMultiaddress1])
# node with default multiaddress
nodeKey2 = crypto.PrivateKey.random(Secp256k1, rng[])[]
node2 = WakuNode.new(nodeKey2, ValidIpAddress.init("0.0.0.0"), Port(61020))
await node1.start()
await node1.mountRelay()
await node2.start()
await node2.mountRelay()
await node1.connectToNodes(@[node2.switch.peerInfo.toRemotePeerInfo()])
await node2.connectToNodes(@[node1.switch.peerInfo.toRemotePeerInfo()])
let node1MultiAddrs = node2.switch.peerStore[AddressBook][node1.switch.peerInfo.toRemotePeerInfo().peerId]
check:
node1MultiAddrs.contains(expectedMultiaddress1)
await allFutures(node1.stop(), node2.stop())

View File

@ -136,6 +136,7 @@ proc new*(T: type WakuNode,
bindPort: Port,
extIp = none(ValidIpAddress),
extPort = none(Port),
extMultiAddrs = newSeq[MultiAddress](),
peerStorage: PeerStorage = nil,
maxConnections = builders.MaxConnections,
wsBindPort: Port = (Port)8000,
@ -178,12 +179,17 @@ proc new*(T: type WakuNode,
if (wsHostAddress.isSome()):
wsExtAddress = some(ip4TcpEndPoint(extIp.get(), wsBindPort) & wsFlag(wssEnabled))
var announcedAddresses: seq[MultiAddress]
var announcedAddresses = newSeq[MultiAddress]()
if hostExtAddress.isSome():
announcedAddresses.add(hostExtAddress.get())
else:
announcedAddresses.add(hostAddress) # We always have at least a bind address for the host
# External multiaddrs that the operator may have configured
if extMultiAddrs.len > 0:
announcedAddresses.add(extMultiAddrs)
if wsExtAddress.isSome():
announcedAddresses.add(wsExtAddress.get())
elif wsHostAddress.isSome():
@ -196,9 +202,12 @@ proc new*(T: type WakuNode,
else: some(bindIp)
enrTcpPort = if extPort.isSome(): extPort
else: some(bindPort)
enrMultiaddrs = if wsExtAddress.isSome(): @[wsExtAddress.get()] # Only add ws/wss to `multiaddrs` field
elif wsHostAddress.isSome(): @[wsHostAddress.get()]
else: @[]
# enrMultiaddrs are just addresses which cannot be represented in ENR, as described in
# https://rfc.vac.dev/spec/31/#many-connection-types
enrMultiaddrs = announcedAddresses.filterIt(it.hasProtocol("dns4") or
it.hasProtocol("dns6") or
it.hasProtocol("ws") or
it.hasProtocol("wss"))
enr = initEnr(nodeKey,
enrIp,
enrTcpPort,
@ -816,6 +825,7 @@ when defined(rln):
return
node.wakuRlnRelay = rlnRelayRes.get()
## Waku peer-exchange
proc mountPeerExchange*(node: WakuNode) {.async, raises: [Defect, LPError].} =

View File

@ -13,6 +13,7 @@ import
libp2p/crypto/[crypto, secp],
libp2p/[errors,
multiaddress,
multicodec,
peerid,
peerinfo,
routing_record]
@ -169,3 +170,15 @@ proc toRemotePeerInfo*(peerInfo: PeerInfo): RemotePeerInfo =
peerInfo.listenAddrs,
none(enr.Record), # we could generate an ENR from PeerInfo
peerInfo.protocols)
## Checks if a multiaddress contains a given protocol
## Useful for filtering multiaddresses based on their protocols
proc hasProtocol*(ma: MultiAddress, proto: string): bool =
## Returns ``true`` if ``ma`` contains protocol ``proto``.
let protos = ma.protocols()
if protos.isErr():
return false
for p in protos.get():
if p == MultiCodec.codec(proto):
return true
return false