feat: implementing port 0 support (#2125)

This commit is contained in:
gabrielmer 2023-10-27 10:11:47 +03:00 committed by GitHub
parent 3be6163639
commit f7b9afc26f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 204 additions and 52 deletions

View File

@ -8,6 +8,8 @@ import
stew/results,
chronicles,
chronos,
libp2p/wire,
libp2p/multicodec,
libp2p/crypto/crypto,
libp2p/nameresolving/dnsresolver,
libp2p/protocols/pubsub/gossipsub,
@ -117,39 +119,8 @@ proc init*(T: type App, rng: ref HmacDrbgContext, conf: WakuNodeConf): T =
quit(QuitFailure)
else: netConfigRes.get()
var enrBuilder = EnrBuilder.init(key)
let recordRes = enrConfiguration(conf, netConfig, key)
enrBuilder.withIpAddressAndPorts(
netConfig.enrIp,
netConfig.enrPort,
netConfig.discv5UdpPort
)
if netConfig.wakuFlags.isSome():
enrBuilder.withWakuCapabilities(netConfig.wakuFlags.get())
enrBuilder.withMultiaddrs(netConfig.enrMultiaddrs)
let topics =
if conf.pubsubTopics.len > 0 or conf.contentTopics.len > 0:
let shardsRes = conf.contentTopics.mapIt(getShard(it))
for res in shardsRes:
if res.isErr():
error "failed to shard content topic", error=res.error
quit(QuitFailure)
let shards = shardsRes.mapIt(it.get())
conf.pubsubTopics & shards
else:
conf.topics
let addShardedTopics = enrBuilder.withShardedTopics(topics)
if addShardedTopics.isErr():
error "failed to add sharded topics to ENR", error=addShardedTopics.error
quit(QuitFailure)
let recordRes = enrBuilder.build()
let record =
if recordRes.isErr():
error "failed to create record", error=recordRes.error
@ -329,6 +300,72 @@ proc setupWakuApp*(app: var App): AppResult[void] =
ok()
proc getPorts(listenAddrs: seq[MultiAddress]):
AppResult[tuple[tcpPort, websocketPort: Option[Port]]] =
var tcpPort, websocketPort = none(Port)
for a in listenAddrs:
if a.isWsAddress():
if websocketPort.isNone():
let wsAddress = initTAddress(a).valueOr:
return err("getPorts wsAddr error:" & $error)
websocketPort = some(wsAddress.port)
elif tcpPort.isNone():
let tcpAddress = initTAddress(a).valueOr:
return err("getPorts tcpAddr error:" & $error)
tcpPort = some(tcpAddress.port)
return ok((tcpPort: tcpPort, websocketPort: websocketPort))
proc updateNetConfig(app: var App): AppResult[void] =
var conf = app.conf
let (tcpPort, websocketPort) = getPorts(app.node.switch.peerInfo.listenAddrs).valueOr:
return err("Could not retrieve ports " & error)
if tcpPort.isSome():
conf.tcpPort = tcpPort.get()
if websocketPort.isSome():
conf.websocketPort = websocketPort.get()
# Rebuild NetConfig with bound port values
let netConf = networkConfiguration(conf, clientId).valueOr:
return err("Could not update NetConfig: " & error)
app.netConf = netConf
return ok()
proc updateEnr(app: var App): AppResult[void] =
let record = enrConfiguration(app.conf, app.netConf, app.key).valueOr:
return err(error)
app.record = record
app.node.enr = record
if app.conf.discv5Discovery:
app.wakuDiscV5 = some(app.setupDiscoveryV5())
return ok()
proc updateApp(app: var App): AppResult[void] =
if app.conf.tcpPort == Port(0) or app.conf.websocketPort == Port(0):
updateNetConfig(app).isOkOr:
return err("error calling updateNetConfig: " & $error)
updateEnr(app).isOkOr:
return err("error calling updateEnr: " & $error)
app.node.announcedAddresses = app.netConf.announcedAddresses
printNodeNetworkInfo(app.node)
return ok()
## Mount protocols
@ -548,7 +585,18 @@ proc startNode(node: WakuNode, conf: WakuNodeConf,
return ok()
proc startApp*(app: App): Future[AppResult[void]] {.async.} =
proc startApp*(app: var App): AppResult[void] =
try:
(waitFor startNode(app.node,app.conf,app.dynamicBootstrapNodes)).isOkOr:
return err(error)
except CatchableError:
return err("exception starting node: " & getCurrentExceptionMsg())
# Update app data that is set dynamically on node start
app.updateApp().isOkOr:
return err("Error in updateApp: " & $error)
if app.wakuDiscv5.isSome():
let wakuDiscv5 = app.wakuDiscv5.get()
@ -559,11 +607,8 @@ proc startApp*(app: App): Future[AppResult[void]] {.async.} =
asyncSpawn wakuDiscv5.searchLoop(app.node.peerManager)
asyncSpawn wakuDiscv5.subscriptionsListener(app.node.topicSubscriptionQueue)
return await startNode(
app.node,
app.conf,
app.dynamicBootstrapNodes
)
return ok()
## Monitoring and external interfaces

View File

@ -4,15 +4,61 @@ import
libp2p/crypto/crypto,
libp2p/multiaddress,
libp2p/nameresolving/dnsresolver,
std/options,
std/[options, sequtils],
stew/results,
stew/shims/net
import
../../waku/common/utils/nat,
../../waku/node/config,
../../waku/waku_enr/capabilities,
../../waku/waku_enr,
../../waku/waku_core,
./external_config
proc enrConfiguration*(conf: WakuNodeConf, netConfig: NetConfig, key: crypto.PrivateKey):
Result[enr.Record, string] =
var enrBuilder = EnrBuilder.init(key)
enrBuilder.withIpAddressAndPorts(
netConfig.enrIp,
netConfig.enrPort,
netConfig.discv5UdpPort
)
if netConfig.wakuFlags.isSome():
enrBuilder.withWakuCapabilities(netConfig.wakuFlags.get())
enrBuilder.withMultiaddrs(netConfig.enrMultiaddrs)
let topics =
if conf.pubsubTopics.len > 0 or conf.contentTopics.len > 0:
let shardsRes = conf.contentTopics.mapIt(getShard(it))
for res in shardsRes:
if res.isErr():
error "failed to shard content topic", error=res.error
return err($res.error)
let shards = shardsRes.mapIt(it.get())
conf.pubsubTopics & shards
else:
conf.topics
let addShardedTopics = enrBuilder.withShardedTopics(topics)
if addShardedTopics.isErr():
error "failed to add sharded topics to ENR", error=addShardedTopics.error
return err($addShardedTopics.error)
let recordRes = enrBuilder.build()
let record =
if recordRes.isErr():
error "failed to create record", error=recordRes.error
return err($recordRes.error)
else: recordRes.get()
return ok(record)
proc validateExtMultiAddrs*(vals: seq[string]):
Result[seq[MultiAddress], string] =
var multiaddrs: seq[MultiAddress]

View File

@ -87,7 +87,7 @@ when isMainModule:
debug "5/7 Starting node and mounted protocols"
let res6 = waitFor wakunode2.startApp()
let res6 = wakunode2.startApp()
if res6.isErr():
error "5/7 Starting node and protocols failed", error=res6.error
quit(QuitFailure)

View File

@ -12,7 +12,9 @@ import
import
../testlib/common,
../testlib/wakucore,
../testlib/wakunode,
../testlib/wakunode
include
../../apps/wakunode2/app
suite "Wakunode2 - App":
@ -27,7 +29,7 @@ suite "Wakunode2 - App":
## Then
check:
version == app.git_version
version == git_version
suite "Wakunode2 - App initialization":
test "peer persistence setup should be successfully mounted":
@ -53,7 +55,7 @@ suite "Wakunode2 - App initialization":
require wakunode2.setupDyamicBootstrapNodes().isOk()
require wakunode2.setupWakuApp().isOk()
require isOk(waitFor wakunode2.setupAndMountProtocols())
require isOk(waitFor wakunode2.startApp())
require isOk(wakunode2.startApp())
require wakunode2.setupMonitoringAndExternalInterfaces().isOk()
## Then
@ -67,3 +69,43 @@ suite "Wakunode2 - App initialization":
## Cleanup
waitFor wakunode2.stop()
test "app properly handles dynamic port configuration":
## Given
var conf = defaultTestWakuNodeConf()
conf.tcpPort = Port(0)
## When
var wakunode2 = App.init(rng(), conf)
require wakunode2.setupPeerPersistence().isOk()
require wakunode2.setupDyamicBootstrapNodes().isOk()
require wakunode2.setupWakuApp().isOk()
require isOk(waitFor wakunode2.setupAndMountProtocols())
require isOk(wakunode2.startApp())
require wakunode2.setupMonitoringAndExternalInterfaces().isOk()
## Then
let
node = wakunode2.node
typedNodeEnr = node.enr.toTypedRecord()
typedAppEnr = wakunode2.record.toTypedRecord()
assert typedNodeEnr.isOk(), $typedNodeEnr.error
assert typedAppEnr.isOk(), $typedAppEnr.error
check:
# App started properly
not node.isNil()
node.wakuArchive.isNil()
node.wakuStore.isNil()
not node.wakuStoreClient.isNil()
not node.rendezvous.isNil()
# DS structures are updated with dynamic ports
wakunode2.netConf.bindPort != Port(0)
wakunode2.netConf.enrPort.get() != Port(0)
typedNodeEnr.get().tcp.get() != 0
typedAppEnr.get().tcp.get() != 0
## Cleanup
waitFor wakunode2.stop()

View File

@ -59,7 +59,7 @@ proc formatListenAddress(inputMultiAdd: MultiAddress): MultiAddress =
# If MultiAddress contains "0.0.0.0", replace it for "127.0.0.1"
return MultiAddress.init(inputStr.replace("0.0.0.0", "127.0.0.1")).get()
proc isWsAddress(ma: MultiAddress): bool =
proc isWsAddress*(ma: MultiAddress): bool =
let
isWs = ma.contains(multiCodec("ws")).get()
isWss = ma.contains(multiCodec("wss")).get()

View File

@ -1073,6 +1073,26 @@ proc mountRendezvous*(node: WakuNode) {.async, raises: [Defect, LPError].} =
node.switch.mount(node.rendezvous)
proc isBindIpWithZeroPort(inputMultiAdd: MultiAddress): bool =
let inputStr = $inputMultiAdd
if inputStr.contains("0.0.0.0/tcp/0") or inputStr.contains("127.0.0.1/tcp/0"):
return true
return false
proc printNodeNetworkInfo*(node: WakuNode): void =
let peerInfo = node.switch.peerInfo
var listenStr = ""
info "PeerInfo", peerId = peerInfo.peerId, addrs = peerInfo.addrs
for address in node.announcedAddresses:
var fulladdr = "[" & $address & "/p2p/" & $peerInfo.peerId & "]"
listenStr &= fulladdr
## XXX: this should be /ip4..., / stripped?
info "Listening on", full = listenStr
info "DNS: discoverable ENR ", enr = node.enr.toUri()
proc start*(node: WakuNode) {.async.} =
## Starts a created Waku Node and
@ -1081,16 +1101,15 @@ proc start*(node: WakuNode) {.async.} =
waku_version.set(1, labelValues=[git_version])
info "Starting Waku node", version=git_version
let peerInfo = node.switch.peerInfo
info "PeerInfo", peerId = peerInfo.peerId, addrs = peerInfo.addrs
var listenStr = ""
var zeroPortPresent = false
for address in node.announcedAddresses:
var fulladdr = "[" & $address & "/p2p/" & $peerInfo.peerId & "]"
listenStr &= fulladdr
if isBindIpWithZeroPort(address):
zeroPortPresent = true
## XXX: this should be /ip4..., / stripped?
info "Listening on", full = listenStr
info "DNS: discoverable ENR ", enr = node.enr.toUri()
if not zeroPortPresent:
printNodeNetworkInfo(node)
else:
info "Listening port is dynamically allocated, address and ENR generation postponed"
# Perform relay-specific startup tasks TODO: this should be rethought
if not node.wakuRelay.isNil():