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: implementing port 0 support #2125

Merged
merged 26 commits into from
Oct 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
2bbfd13
Adding initial debug logs
gabrielmer Oct 2, 2023
aaaa384
Adding more logs
gabrielmer Oct 2, 2023
579e45b
Starting implementing updateAddresses
gabrielmer Oct 5, 2023
a0215fd
Adding comments and initial implementation of getPorts
gabrielmer Oct 5, 2023
9f4704e
Reorganizing code
gabrielmer Oct 6, 2023
9a6f7cb
Add prints and comments
gabrielmer Oct 6, 2023
1a73379
Added function for ENR creation
gabrielmer Oct 9, 2023
26cfd7c
Updating enr
gabrielmer Oct 9, 2023
9a97033
Not changing user entered configuration
gabrielmer Oct 10, 2023
e33eb17
Moving updateApp call
gabrielmer Oct 10, 2023
b113e0b
Imporving getPorts()
gabrielmer Oct 10, 2023
a20c9ec
Removing debug logs
gabrielmer Oct 10, 2023
c79cd95
Cleaning diffs
gabrielmer Oct 10, 2023
435f394
Removing empty spaces
gabrielmer Oct 10, 2023
eaec402
Setting up discovery with updated ENR
gabrielmer Oct 12, 2023
0935c78
Moving prints
gabrielmer Oct 13, 2023
8e399d5
Fixing test after making startApp synchronous
gabrielmer Oct 16, 2023
9f508fa
Adding missing error handling
gabrielmer Oct 16, 2023
18f245e
Improving if condition and error message
gabrielmer Oct 16, 2023
dfaef08
Adding test case for dynamic port allocation
gabrielmer Oct 19, 2023
7a25d49
Reusing isWsAddress proc
gabrielmer Oct 19, 2023
10b589a
refactor getPorts
gabrielmer Oct 24, 2023
e2b7da1
Improving error messages and deleting white lines
gabrielmer Oct 26, 2023
38985fb
Adding return keyworkd
gabrielmer Oct 26, 2023
338cbcd
Merge branch 'master' into feat-implementing-port-0-support
gabrielmer Oct 26, 2023
1382f0c
Fix typo
gabrielmer Oct 26, 2023
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
121 changes: 83 additions & 38 deletions apps/wakunode2/app.nim
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import
stew/results,
chronicles,
chronos,
libp2p/wire,
libp2p/multicodec,
libp2p/crypto/crypto,
libp2p/nameresolving/dnsresolver,
libp2p/protocols/pubsub/gossipsub,
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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():
gabrielmer marked this conversation as resolved.
Show resolved Hide resolved
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

Expand Down Expand Up @@ -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:
gabrielmer marked this conversation as resolved.
Show resolved Hide resolved
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()

Expand All @@ -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
Expand Down
48 changes: 47 additions & 1 deletion apps/wakunode2/internal_config.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
2 changes: 1 addition & 1 deletion apps/wakunode2/wakunode2.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
48 changes: 45 additions & 3 deletions tests/wakunode2/test_app.nim
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ import
import
../testlib/common,
../testlib/wakucore,
../testlib/wakunode,
../testlib/wakunode

include
../../apps/wakunode2/app

suite "Wakunode2 - App":
Expand All @@ -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":
Expand All @@ -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
Expand All @@ -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()
2 changes: 1 addition & 1 deletion waku/node/config.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
33 changes: 26 additions & 7 deletions waku/node/waku_node.nim
Original file line number Diff line number Diff line change
Expand Up @@ -1073,17 +1073,19 @@ 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

proc start*(node: WakuNode) {.async.} =
## Starts a created Waku Node and
## all its mounted protocols.

waku_version.set(1, labelValues=[git_version])
info "Starting Waku node", version=git_version
return false

proc printNodeNetworkInfo*(node: WakuNode): void =
let peerInfo = node.switch.peerInfo
info "PeerInfo", peerId = peerInfo.peerId, addrs = peerInfo.addrs
var listenStr = ""

info "PeerInfo", peerId = peerInfo.peerId, addrs = peerInfo.addrs

for address in node.announcedAddresses:
var fulladdr = "[" & $address & "/p2p/" & $peerInfo.peerId & "]"
listenStr &= fulladdr
Expand All @@ -1092,6 +1094,23 @@ proc start*(node: WakuNode) {.async.} =
info "Listening on", full = listenStr
info "DNS: discoverable ENR ", enr = node.enr.toUri()

proc start*(node: WakuNode) {.async.} =
## Starts a created Waku Node and
## all its mounted protocols.

waku_version.set(1, labelValues=[git_version])
info "Starting Waku node", version=git_version

var zeroPortPresent = false
for address in node.announcedAddresses:
if isBindIpWithZeroPort(address):
zeroPortPresent = true

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():
await node.startRelay()
Expand Down