Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(discv5): advertise custom multiaddresses #1512

Merged
merged 8 commits into from
Feb 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 63 additions & 47 deletions apps/wakunode2/wakunode2.nim
Original file line number Diff line number Diff line change
Expand Up @@ -278,61 +278,77 @@ proc initNode(conf: WakuNodeConf,

let pStorage = if peerStore.isNone(): nil
else: peerStore.get()

let rng = crypto.newRng()
# Wrap in none because NetConfig does not have a default constructor
# TODO: We could change bindIp in NetConfig to be something less restrictive than ValidIpAddress,
# which doesn't allow default construction
var netConfigOpt = none(NetConfig)

try:
node = WakuNode.new(nodekey,
conf.listenAddress, Port(uint16(conf.tcpPort) + conf.portsShift),
extIp, extPort,
extMultiAddrs,
pStorage,
conf.maxConnections.int,
Port(uint16(conf.websocketPort) + conf.portsShift),
conf.websocketSupport,
conf.websocketSecureSupport,
conf.websocketSecureKeyPath,
conf.websocketSecureCertPath,
some(wakuFlags),
dnsResolver,
conf.relayPeerExchange, # We send our own signed peer record when peer exchange enabled
dns4DomainName,
discv5UdpPort,
some(conf.agentString),
conf.peerStoreCapacity,
rng)
netConfigOpt = some(NetConfig.init(
bindIp = conf.listenAddress,
bindPort = Port(uint16(conf.tcpPort) + conf.portsShift),
extIp = extIp,
extPort = extPort,
extMultiAddrs = extMultiAddrs,
wsBindPort = Port(uint16(conf.websocketPort) + conf.portsShift),
wsEnabled = conf.websocketSupport,
wssEnabled = conf.websocketSecureSupport,
dns4DomainName = dns4DomainName,
discv5UdpPort = discv5UdpPort,
wakuFlags = some(wakuFlags),
))
except:
return err("failed to create waku node instance: " & getCurrentExceptionMsg())
return err("failed to create net config instance: " & getCurrentExceptionMsg())

if conf.discv5Discovery:
let
discoveryConfig = DiscoveryConfig.init(
conf.discv5TableIpLimit, conf.discv5BucketIpLimit, conf.discv5BitsPerHop)
let netConfig = netConfigOpt.get()
var wakuDiscv5 = none(WakuDiscoveryV5)

# 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)
if conf.discv5Discovery:
let dynamicBootstrapEnrs = dynamicBootstrapNodes
.filterIt(it.hasUdpPort())
.mapIt(it.enr.get())
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, discv5UdpPort,
conf.listenAddress,
discv5UdpPort.get(),
discv5BootstrapEnrs,
conf.discv5EnrAutoUpdate,
keys.PrivateKey(nodekey.skkey),
wakuFlags,
[], # Empty enr fields, for now
node.rng,
discoveryConfig
)
discv5BootstrapEnrs.add(dynamicBootstrapEnrs)
let discv5Config = DiscoveryConfig.init(conf.discv5TableIpLimit,
conf.discv5BucketIpLimit,
conf.discv5BitsPerHop)
try:
wakuDiscv5 = some(WakuDiscoveryV5.new(
extIp = netConfig.extIp,
extTcpPort = netConfig.extPort,
extUdpPort = netConfig.discv5UdpPort,
bindIp = netConfig.bindIp,
discv5UdpPort = netConfig.discv5UdpPort.get(),
bootstrapEnrs = discv5BootstrapEnrs,
enrAutoUpdate = conf.discv5EnrAutoUpdate,
privateKey = keys.PrivateKey(nodekey.skkey),
flags = netConfig.wakuFlags.get(),
multiaddrs = netConfig.enrMultiaddrs,
rng = rng,
discv5Config = discv5Config,
))
except:
return err("failed to create waku discv5 instance: " & getCurrentExceptionMsg())
try:
node = WakuNode.new(nodekey = nodekey,
netConfig = netConfig,
peerStorage = pStorage,
maxConnections = conf.maxConnections.int,
secureKey = conf.websocketSecureKeyPath,
secureCert = conf.websocketSecureCertPath,
nameResolver = dnsResolver,
sendSignedPeerRecord = conf.relayPeerExchange, # We send our own signed peer record when peer exchange enabled
wakuDiscv5 = wakuDiscv5,
agentString = some(conf.agentString),
peerStoreCapacity = conf.peerStoreCapacity,
rng = rng)
except:
return err("failed to create waku node instance: " & getCurrentExceptionMsg())

ok(node)

Expand Down
1 change: 0 additions & 1 deletion examples/v2/publisher.nim
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ proc setupAndPublish(rng: ref HmacDrbgContext) {.async.} =
bootstrapNodes = bootstrapNodes,
privateKey = keys.PrivateKey(nodeKey.skkey),
flags = flags,
enrFields = [],
rng = node.rng)

await node.start()
Expand Down
1 change: 0 additions & 1 deletion examples/v2/subscriber.nim
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ proc setupAndSubscribe(rng: ref HmacDrbgContext) {.async.} =
bootstrapNodes = bootstrapNodes,
privateKey = keys.PrivateKey(nodeKey.skkey),
flags = flags,
enrFields = [],
rng = node.rng)

await node.start()
Expand Down
54 changes: 27 additions & 27 deletions tests/v2/test_enr_utils.nim
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@ procSuite "ENR utils":
tooShort = "0x000A047F0000010601BADD0301".hexToSeqByte()
gibberish = "0x3270ac4e5011123c".hexToSeqByte()
empty = newSeq[byte]()

## Note: we expect to fail optimistically, i.e. extract
## any addresses we can and ignore other errors.
## Worst case scenario is we return an empty `multiaddrs` seq.
## Worst case scenario is we return an empty `multiaddrs` seq.
check:
# Expected cases
reasonable.toMultiAddresses().contains(MultiAddress.init("/ip4/127.0.0.1/tcp/442/ws")[])
Expand All @@ -50,19 +50,19 @@ procSuite "ENR utils":
MultiAddress.init("/ip4/127.0.0.1/tcp/443/wss")[]]

let
record = initEnr(enrKey, some(enrIp),
record = enr.Record.init(enrKey, some(enrIp),
some(enrTcpPort), some(enrUdpPort),
some(wakuFlags),
multiaddrs)
typedRecord = record.toTypedRecord.get()

# Check EIP-778 ENR fields
check:
@(typedRecord.secp256k1.get()) == enrKey.getPublicKey()[].getRawBytes()[]
ipv4(typedRecord.ip.get()) == enrIp
Port(typedRecord.tcp.get()) == enrTcpPort
Port(typedRecord.udp.get()) == enrUdpPort

# Check Waku ENR fields
let
decodedFlags = record.get(WAKU_ENR_FIELD, seq[byte])[]
Expand All @@ -71,7 +71,7 @@ procSuite "ENR utils":
decodedFlags == @[wakuFlags.byte]
decodedAddrs.contains(MultiAddress.init("/ip4/127.0.0.1/tcp/442/ws")[])
decodedAddrs.contains(MultiAddress.init("/ip4/127.0.0.1/tcp/443/wss")[])

asyncTest "Strip multiaddr peerId":
# Tests that peerId is stripped of multiaddrs as per RFC31
let
Expand All @@ -81,15 +81,15 @@ procSuite "ENR utils":
multiaddrs = @[MultiAddress.init("/ip4/127.0.0.1/tcp/443/wss/p2p/16Uiu2HAm4v86W3bmT1BiH6oSPzcsSr31iDQpSN5Qa882BCjjwgrD")[]]

let
record = initEnr(enrKey, some(enrIp),
record = enr.Record.init(enrKey, some(enrIp),
some(enrTcpPort), some(enrUdpPort),
none(WakuEnrBitfield),
multiaddrs)

# Check Waku ENR fields
let
decodedAddrs = record.get(MULTIADDR_ENR_FIELD, seq[byte])[].toMultiAddresses()

check decodedAddrs.contains(MultiAddress.init("/ip4/127.0.0.1/tcp/443/wss")[]) # Peer Id has been stripped

asyncTest "Decode ENR with multiaddrs field":
Expand All @@ -110,19 +110,19 @@ procSuite "ENR utils":
var enrRecord: Record
check:
enrRecord.fromURI(knownEnr)

let typedRecord = enrRecord.toTypedRecord.get()

# Check EIP-778 ENR fields
check:
ipv4(typedRecord.ip.get()) == knownIp
typedRecord.tcp == knownTcpPort
typedRecord.udp == knownUdpPort

# Check Waku ENR fields
let
decodedAddrs = enrRecord.get(MULTIADDR_ENR_FIELD, seq[byte])[].toMultiAddresses()

for knownMultiaddr in knownMultiaddrs:
check decodedAddrs.contains(knownMultiaddr)

Expand All @@ -132,12 +132,12 @@ procSuite "ENR utils":
enrTcpPort, enrUdpPort = Port(60000)
enrKey = wakuenr.crypto.PrivateKey.random(Secp256k1, rng[])[]
multiaddrs = @[MultiAddress.init("/ip4/127.0.0.1/tcp/442/ws")[]]
# TODO: Refactor initEnr, provide enums as inputs initEnr(capabilites=[Store,Filter])

# TODO: Refactor enr.Record.init, provide enums as inputs enr.Record.init(capabilites=[Store,Filter])
# TODO: safer than a util function and directly using the bits
# test all flag combinations 2^4 = 16 (b0000-b1111)
records = toSeq(0b0000_0000'u8..0b0000_1111'u8)
.mapIt(initEnr(enrKey,
.mapIt(enr.Record.init(enrKey,
some(enrIp),
some(enrTcpPort),
some(enrUdpPort),
Expand All @@ -161,7 +161,7 @@ procSuite "ENR utils":
[true, true, false, true],
[true, true, true, false],
[true, true, true, true]]

for i, record in records:
for j, capability in @[Lightpush, Filter, Store, Relay]:
check expectedCapabilities[i][j] == record.supportsCapability(capability)
Expand All @@ -172,18 +172,18 @@ procSuite "ENR utils":
enrTcpPort, enrUdpPort = Port(60000)
enrKey = wakuenr.crypto.PrivateKey.random(Secp256k1, rng[])[]
multiaddrs = @[MultiAddress.init("/ip4/127.0.0.1/tcp/442/ws")[]]

records = @[0b0000_0000'u8,
0b0000_1111'u8,
0b0000_1001'u8,
0b0000_1110'u8,
0b0000_1000'u8,]
.mapIt(initEnr(enrKey,
some(enrIp),
some(enrTcpPort),
some(enrUdpPort),
some(uint8(it)),
multiaddrs))
.mapIt(enr.Record.init(enrKey,
some(enrIp),
some(enrTcpPort),
some(enrUdpPort),
some(uint8(it)),
multiaddrs))

# expected capabilities, ordered LSB to MSB
expectedCapabilities: seq[seq[Capabilities]] = @[
Expand All @@ -197,14 +197,14 @@ procSuite "ENR utils":
check actualExpetedTuple[0].getCapabilities() == actualExpetedTuple[1]

asyncTest "Get supported capabilities of a non waku node":
# non waku enr, i.e. Ethereum one

# non waku enr, i.e. Ethereum one
let nonWakuEnr = "enr:-KG4QOtcP9X1FbIMOe17QNMKqDxCpm14jcX5tiOE4_TyMrFqbmhPZHK_ZPG2G"&
"xb1GE2xdtodOfx9-cgvNtxnRyHEmC0ghGV0aDKQ9aX9QgAAAAD__________4JpZIJ2NIJpcIQDE8KdiXNl"&
"Y3AyNTZrMaEDhpehBDbZjM_L9ek699Y7vhUJ-eAdMyQW_Fil522Y0fODdGNwgiMog3VkcIIjKA"

var nonWakuEnrRecord: Record

check:
nonWakuEnrRecord.fromURI(nonWakuEnr)

Expand All @@ -214,4 +214,4 @@ procSuite "ENR utils":
nonWakuEnrRecord.supportsCapability(Relay) == false
nonWakuEnrRecord.supportsCapability(Store) == false
nonWakuEnrRecord.supportsCapability(Filter) == false
nonWakuEnrRecord.supportsCapability(Lightpush) == false
nonWakuEnrRecord.supportsCapability(Lightpush) == false
Loading