mirror of
https://github.com/logos-messaging/logos-messaging-nim.git
synced 2026-06-26 11:29:28 +00:00
feat: QUIC transport support (#3951)
* additive quic transport, off by default (--quic-support) * add QuicConf + QuicConfBuilder, --quic-support / --quic-port flags * net_config announces quic-v1 host/ext/dns4 addrs, adds quic-v1 to enr multiaddrs * newWakuSwitch mounts quic transport when a quic addr is set * toRemotePeerInfo: quic from enr multiaddrs ext, sorted quic-first * BoundPorts.quic, read back the bound quic port at start (handles --quic-port=0) * skip auto quic addr when operator supplies one via --ext-multiaddr * tests: nodes dual-stack by default, tcp-only where single transport asserted * tests: drop hardcoded ephemeral ports (port 0) to fix quic-churn bind flakes * use setupNat to discover NAT-mapped UDP port when QUIC enabled
This commit is contained in:
parent
7e98489a24
commit
c3090fb62f
@ -190,6 +190,7 @@ proc build*(builder: WakuNodeBuilder): Result[WakuNode, string] =
|
||||
privKey = builder.nodekey,
|
||||
address = builder.netConfig.get().hostAddress,
|
||||
wsAddress = builder.netConfig.get().wsHostAddress,
|
||||
quicAddress = builder.netConfig.get().quicHostAddress,
|
||||
transportFlags = {ServerFlags.ReuseAddr, ServerFlags.TcpNoDelay},
|
||||
rng = rng,
|
||||
maxConnections = builder.switchMaxConnections.get(MaxConnections),
|
||||
|
||||
@ -7,6 +7,7 @@ import
|
||||
./dns_discovery_conf_builder,
|
||||
./discv5_conf_builder,
|
||||
./web_socket_conf_builder,
|
||||
./quic_conf_builder,
|
||||
./metrics_server_conf_builder,
|
||||
./rate_limit_conf_builder,
|
||||
./rln_relay_conf_builder,
|
||||
@ -16,6 +17,6 @@ import
|
||||
export
|
||||
waku_conf_builder, filter_service_conf_builder, store_sync_conf_builder,
|
||||
store_service_conf_builder, rest_server_conf_builder, dns_discovery_conf_builder,
|
||||
discv5_conf_builder, web_socket_conf_builder, metrics_server_conf_builder,
|
||||
rate_limit_conf_builder, rln_relay_conf_builder, mix_conf_builder,
|
||||
kademlia_discovery_conf_builder
|
||||
discv5_conf_builder, web_socket_conf_builder, quic_conf_builder,
|
||||
metrics_server_conf_builder, rate_limit_conf_builder, rln_relay_conf_builder,
|
||||
mix_conf_builder, kademlia_discovery_conf_builder
|
||||
|
||||
@ -0,0 +1,33 @@
|
||||
import chronicles, std/[net, options], results
|
||||
import logos_delivery/waku/factory/waku_conf
|
||||
|
||||
logScope:
|
||||
topics = "waku conf builder quic"
|
||||
|
||||
# same value as tcp default port. quic is udp, no conflict.
|
||||
const DefaultQuicPort*: Port = Port(60000)
|
||||
|
||||
#########################
|
||||
## QUIC Config Builder ##
|
||||
#########################
|
||||
type QuicConfBuilder* = object
|
||||
enabled*: Option[bool]
|
||||
quicPort*: Option[Port]
|
||||
|
||||
proc init*(T: type QuicConfBuilder): QuicConfBuilder =
|
||||
QuicConfBuilder()
|
||||
|
||||
proc withEnabled*(b: var QuicConfBuilder, enabled: bool) =
|
||||
b.enabled = some(enabled)
|
||||
|
||||
proc withQuicPort*(b: var QuicConfBuilder, quicPort: Port) =
|
||||
b.quicPort = some(quicPort)
|
||||
|
||||
proc withQuicPort*(b: var QuicConfBuilder, quicPort: uint16) =
|
||||
b.quicPort = some(Port(quicPort))
|
||||
|
||||
proc build*(b: QuicConfBuilder): Result[Option[QuicConf], string] =
|
||||
if not b.enabled.get(false):
|
||||
return ok(none(QuicConf))
|
||||
|
||||
return ok(some(QuicConf(port: b.quicPort.get(DefaultQuicPort))))
|
||||
@ -30,6 +30,7 @@ import
|
||||
./dns_discovery_conf_builder,
|
||||
./discv5_conf_builder,
|
||||
./web_socket_conf_builder,
|
||||
./quic_conf_builder,
|
||||
./metrics_server_conf_builder,
|
||||
./rate_limit_conf_builder,
|
||||
./rln_relay_conf_builder,
|
||||
@ -119,6 +120,7 @@ type WakuConfBuilder* = object
|
||||
storeServiceConf*: StoreServiceConfBuilder
|
||||
mixConf*: MixConfBuilder
|
||||
webSocketConf*: WebSocketConfBuilder
|
||||
quicConf*: QuicConfBuilder
|
||||
rateLimitConf*: RateLimitConfBuilder
|
||||
kademliaDiscoveryConf*: KademliaDiscoveryConfBuilder
|
||||
# End conf builders
|
||||
@ -182,6 +184,7 @@ proc init*(T: type WakuConfBuilder): WakuConfBuilder =
|
||||
rlnRelayConf: RlnRelayConfBuilder.init(),
|
||||
storeServiceConf: StoreServiceConfBuilder.init(),
|
||||
webSocketConf: WebSocketConfBuilder.init(),
|
||||
quicConf: QuicConfBuilder.init(),
|
||||
rateLimitConf: RateLimitConfBuilder.init(),
|
||||
kademliaDiscoveryConf: KademliaDiscoveryConfBuilder.init(),
|
||||
)
|
||||
@ -648,6 +651,9 @@ proc build*(
|
||||
let webSocketConf = builder.webSocketConf.build().valueOr:
|
||||
return err("WebSocket Conf building failed: " & $error)
|
||||
|
||||
let quicConf = builder.quicConf.build().valueOr:
|
||||
return err("QUIC Conf building failed: " & $error)
|
||||
|
||||
let rateLimit = builder.rateLimitConf.build().valueOr:
|
||||
return err("Rate limits Conf building failed: " & $error)
|
||||
|
||||
@ -802,6 +808,7 @@ proc build*(
|
||||
),
|
||||
portsShift: portsShift,
|
||||
webSocketConf: webSocketConf,
|
||||
quicConf: quicConf,
|
||||
dnsAddrsNameServers: dnsAddrsNameServers,
|
||||
peerPersistence: peerPersistence,
|
||||
peerStoreCapacity: builder.peerStoreCapacity,
|
||||
|
||||
@ -87,18 +87,24 @@ proc networkConfiguration*(
|
||||
conf: EndpointConf,
|
||||
discv5Conf: Option[Discv5Conf],
|
||||
webSocketConf: Option[WebSocketConf],
|
||||
quicConf: Option[QuicConf],
|
||||
wakuFlags: CapabilitiesBitfield,
|
||||
dnsAddrsNameServers: seq[IpAddress],
|
||||
portsShift: uint16,
|
||||
clientId: string,
|
||||
): Future[NetConfigResult] {.async.} =
|
||||
## `udpPort` is only supplied to satisfy underlying APIs but is not
|
||||
## actually a supported transport for libp2p traffic.
|
||||
var (extIp, extTcpPort, _) = setupNat(
|
||||
conf.natStrategy.string,
|
||||
clientId,
|
||||
Port(uint16(conf.p2pTcpPort) + portsShift),
|
||||
Port(uint16(conf.p2pTcpPort) + portsShift),
|
||||
let tcpBindPort = Port(uint16(conf.p2pTcpPort) + portsShift)
|
||||
|
||||
let (quicEnabled, quicBindPort) =
|
||||
if quicConf.isSome():
|
||||
let qConf = quicConf.get()
|
||||
(true, some(Port(qConf.port.uint16 + portsShift)))
|
||||
else:
|
||||
(false, none(Port))
|
||||
|
||||
# NAT-map the QUIC UDP port (placeholder when QUIC off)
|
||||
var (extIp, extTcpPort, extUdpPort) = setupNat(
|
||||
conf.natStrategy.string, clientId, tcpBindPort, quicBindPort.get(tcpBindPort)
|
||||
).valueOr:
|
||||
return err("failed to setup NAT: " & $error)
|
||||
|
||||
@ -115,10 +121,16 @@ proc networkConfiguration*(
|
||||
## manual config, the external port is the same as the bind port.
|
||||
extPort =
|
||||
if (extIp.isSome() or conf.dns4DomainName.isSome()) and extTcpPort.isNone():
|
||||
some(Port(uint16(conf.p2pTcpPort) + portsShift))
|
||||
some(tcpBindPort)
|
||||
else:
|
||||
extTcpPort
|
||||
|
||||
extQuicPort =
|
||||
if (extIp.isSome() or conf.dns4DomainName.isSome()) and extUdpPort.isNone():
|
||||
quicBindPort
|
||||
else:
|
||||
extUdpPort
|
||||
|
||||
# Resolve and use DNS domain IP
|
||||
if conf.dns4DomainName.isSome() and extIp.isNone():
|
||||
try:
|
||||
@ -143,7 +155,7 @@ proc networkConfiguration*(
|
||||
let netConfigRes = NetConfig.init(
|
||||
clusterId = clusterId,
|
||||
bindIp = conf.p2pListenAddress,
|
||||
bindPort = Port(uint16(conf.p2pTcpPort) + portsShift),
|
||||
bindPort = tcpBindPort,
|
||||
extIp = extIp,
|
||||
extPort = extPort,
|
||||
extMultiAddrs = conf.extMultiAddrs,
|
||||
@ -151,6 +163,9 @@ proc networkConfiguration*(
|
||||
wsBindPort = wsBindPort,
|
||||
wsEnabled = wsEnabled,
|
||||
wssEnabled = wssEnabled,
|
||||
quicBindPort = quicBindPort,
|
||||
quicEnabled = quicEnabled,
|
||||
extQuicPort = extQuicPort,
|
||||
dns4DomainName = conf.dns4DomainName,
|
||||
discv5UdpPort = discv5UdpPort,
|
||||
wakuFlags = some(wakuFlags),
|
||||
|
||||
@ -465,8 +465,8 @@ proc setupNode*(
|
||||
let netConfig = (
|
||||
await networkConfiguration(
|
||||
wakuConf.clusterId, wakuConf.endpointConf, wakuConf.discv5Conf,
|
||||
wakuConf.webSocketConf, wakuConf.wakuFlags, wakuConf.dnsAddrsNameServers,
|
||||
wakuConf.portsShift, clientId,
|
||||
wakuConf.webSocketConf, wakuConf.quicConf, wakuConf.wakuFlags,
|
||||
wakuConf.dnsAddrsNameServers, wakuConf.portsShift, clientId,
|
||||
)
|
||||
).valueOr:
|
||||
error "failed to create internal config", error = error
|
||||
|
||||
@ -249,8 +249,8 @@ proc new*(
|
||||
|
||||
proc getPorts(
|
||||
listenAddrs: seq[MultiAddress]
|
||||
): Result[tuple[tcpPort, websocketPort: Option[Port]], string] =
|
||||
var tcpPort, websocketPort = none(Port)
|
||||
): Result[tuple[tcpPort, websocketPort, quicPort: Option[Port]], string] =
|
||||
var tcpPort, websocketPort, quicPort = none(Port)
|
||||
|
||||
for a in listenAddrs:
|
||||
if a.isWsAddress():
|
||||
@ -258,16 +258,23 @@ proc getPorts(
|
||||
let wsAddress = initTAddress(a).valueOr:
|
||||
return err("getPorts wsAddr error:" & $error)
|
||||
websocketPort = some(wsAddress.port)
|
||||
elif a.isQuicAddress():
|
||||
if quicPort.isNone():
|
||||
let quicAddress = initTAddress(a).valueOr:
|
||||
return err("getPorts quicAddr error:" & $error)
|
||||
quicPort = some(quicAddress.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))
|
||||
return ok((tcpPort: tcpPort, websocketPort: websocketPort, quicPort: quicPort))
|
||||
|
||||
proc getRunningNetConfig(waku: Waku): Future[Result[NetConfig, string]] {.async.} =
|
||||
let conf = waku.conf
|
||||
let (tcpPort, websocketPort) = getPorts(waku.node.switch.peerInfo.listenAddrs).valueOr:
|
||||
let (tcpPort, websocketPort, quicPort) = getPorts(
|
||||
waku.node.switch.peerInfo.listenAddrs
|
||||
).valueOr:
|
||||
return err("Could not retrieve ports: " & error)
|
||||
|
||||
if tcpPort.isSome():
|
||||
@ -276,11 +283,14 @@ proc getRunningNetConfig(waku: Waku): Future[Result[NetConfig, string]] {.async.
|
||||
if websocketPort.isSome() and conf.webSocketConf.isSome():
|
||||
conf.webSocketConf.get().port = websocketPort.get()
|
||||
|
||||
if quicPort.isSome() and conf.quicConf.isSome():
|
||||
conf.quicConf.get().port = quicPort.get()
|
||||
|
||||
# Rebuild NetConfig with bound port values
|
||||
let netConf = (
|
||||
await networkConfiguration(
|
||||
conf.clusterId, conf.endpointConf, conf.discv5Conf, conf.webSocketConf,
|
||||
conf.wakuFlags, conf.dnsAddrsNameServers, conf.portsShift, clientId,
|
||||
conf.quicConf, conf.wakuFlags, conf.dnsAddrsNameServers, conf.portsShift, clientId,
|
||||
)
|
||||
).valueOr:
|
||||
return err("Could not update NetConfig: " & error)
|
||||
@ -444,6 +454,7 @@ proc start*(waku: Waku): Future[Result[void, string]] {.async: (raises: []).} =
|
||||
return err("failed to read bound ports from switch: " & $error)
|
||||
waku.node.ports.tcp = bound.tcpPort.get(Port(0)).uint16
|
||||
waku.node.ports.webSocket = bound.websocketPort.get(Port(0)).uint16
|
||||
waku.node.ports.quic = bound.quicPort.get(Port(0)).uint16
|
||||
|
||||
## Discv5
|
||||
if conf.discv5Conf.isSome():
|
||||
|
||||
@ -32,6 +32,9 @@ type WebSocketConf* = object
|
||||
port*: Port
|
||||
secureConf*: Option[WebSocketSecureConf]
|
||||
|
||||
type QuicConf* = object
|
||||
port*: Port
|
||||
|
||||
# TODO: should be defined in validator_signed.nim and imported here
|
||||
type ProtectedShard* {.requiresInit.} = object
|
||||
shard*: uint16
|
||||
@ -112,6 +115,7 @@ type WakuConf* {.requiresInit.} = ref object
|
||||
restServerConf*: Option[RestServerConf]
|
||||
metricsServerConf*: Option[MetricsServerConf]
|
||||
webSocketConf*: Option[WebSocketConf]
|
||||
quicConf*: Option[QuicConf]
|
||||
mixConf*: Option[MixConf]
|
||||
kademliaDiscoveryConf*: Option[KademliaDiscoveryConf]
|
||||
|
||||
|
||||
@ -7,13 +7,19 @@ type BoundPorts* {.requiresInit.} = object
|
||||
## A value of 0 means the service was not enabled or did not bind.
|
||||
tcp*: uint16
|
||||
webSocket*: uint16
|
||||
quic*: uint16
|
||||
rest*: uint16
|
||||
discv5Udp*: uint16
|
||||
metrics*: uint16
|
||||
|
||||
proc init*(T: type BoundPorts): BoundPorts =
|
||||
return BoundPorts(
|
||||
tcp: 0'u16, webSocket: 0'u16, rest: 0'u16, discv5Udp: 0'u16, metrics: 0'u16
|
||||
tcp: 0'u16,
|
||||
webSocket: 0'u16,
|
||||
quic: 0'u16,
|
||||
rest: 0'u16,
|
||||
discv5Udp: 0'u16,
|
||||
metrics: 0'u16,
|
||||
)
|
||||
|
||||
proc `$`*(p: BoundPorts): string =
|
||||
|
||||
@ -9,6 +9,7 @@ type NetConfig* = object
|
||||
hostAddress*: MultiAddress
|
||||
clusterId*: uint16
|
||||
wsHostAddress*: Option[MultiAddress]
|
||||
quicHostAddress*: Option[MultiAddress]
|
||||
hostExtAddress*: Option[MultiAddress]
|
||||
wsExtAddress*: Option[MultiAddress]
|
||||
wssEnabled*: bool
|
||||
@ -46,6 +47,18 @@ template wsFlag(wssEnabled: bool): MultiAddress =
|
||||
else:
|
||||
MultiAddress.init("/ws").tryGet()
|
||||
|
||||
template udpPortMa(port: Port): MultiAddress =
|
||||
MultiAddress.init("/udp/" & $port).tryGet()
|
||||
|
||||
template quicFlag(): MultiAddress =
|
||||
MultiAddress.init("/quic-v1").tryGet()
|
||||
|
||||
template ipQuicEndPoint(address: IpAddress, port: Port): MultiAddress =
|
||||
MultiAddress.init(address, udpProtocol, port) & quicFlag()
|
||||
|
||||
template dns4QuicEndPoint(dns4DomainName: string, port: Port): MultiAddress =
|
||||
dns4Ma(dns4DomainName) & udpPortMa(port) & quicFlag()
|
||||
|
||||
proc formatListenAddress(inputMultiAdd: MultiAddress): MultiAddress =
|
||||
let inputStr = $inputMultiAdd
|
||||
# If MultiAddress contains "0.0.0.0", replace it for "127.0.0.1"
|
||||
@ -58,9 +71,15 @@ proc isWsAddress*(ma: MultiAddress): bool =
|
||||
|
||||
return isWs or isWss
|
||||
|
||||
proc isQuicAddress*(ma: MultiAddress): bool =
|
||||
return ma.hasProtocol("quic-v1")
|
||||
|
||||
proc containsWsAddress(extMultiAddrs: seq[MultiAddress]): bool =
|
||||
return extMultiAddrs.filterIt(it.isWsAddress()).len > 0
|
||||
|
||||
proc containsQuicAddress(extMultiAddrs: seq[MultiAddress]): bool =
|
||||
return extMultiAddrs.filterIt(it.isQuicAddress()).len > 0
|
||||
|
||||
const DefaultWsBindPort = static(Port(8000))
|
||||
# TODO: migrate to builder pattern with nested configs
|
||||
proc init*(
|
||||
@ -74,6 +93,9 @@ proc init*(
|
||||
wsBindPort: Option[Port] = some(DefaultWsBindPort),
|
||||
wsEnabled: bool = false,
|
||||
wssEnabled: bool = false,
|
||||
quicBindPort = none(Port),
|
||||
quicEnabled: bool = false,
|
||||
extQuicPort = none(Port),
|
||||
dns4DomainName = none(string),
|
||||
discv5UdpPort = none(Port),
|
||||
clusterId: uint16 = 0,
|
||||
@ -94,6 +116,13 @@ proc init*(
|
||||
except CatchableError:
|
||||
return err(getCurrentExceptionMsg())
|
||||
|
||||
var quicHostAddress = none(MultiAddress)
|
||||
if quicEnabled:
|
||||
try:
|
||||
quicHostAddress = some(ipQuicEndPoint(bindIp, quicBindPort.get(bindPort)))
|
||||
except CatchableError:
|
||||
return err("failed to initialize quic address: " & getCurrentExceptionMsg())
|
||||
|
||||
let enrIp =
|
||||
if extIp.isSome():
|
||||
extIp
|
||||
@ -106,7 +135,7 @@ proc init*(
|
||||
some(bindPort)
|
||||
|
||||
# Setup external addresses, if available
|
||||
var hostExtAddress, wsExtAddress = none(MultiAddress)
|
||||
var hostExtAddress, wsExtAddress, quicExtAddress = none(MultiAddress)
|
||||
|
||||
if dns4DomainName.isSome():
|
||||
# Use dns4 for externally announced addresses
|
||||
@ -123,6 +152,16 @@ proc init*(
|
||||
)
|
||||
except CatchableError:
|
||||
return err(getCurrentExceptionMsg())
|
||||
|
||||
if quicHostAddress.isSome():
|
||||
try:
|
||||
quicExtAddress = some(
|
||||
dns4QuicEndPoint(
|
||||
dns4DomainName.get(), extQuicPort.get(quicBindPort.get(bindPort))
|
||||
)
|
||||
)
|
||||
except CatchableError:
|
||||
return err("failed to set dns quic endpoint: " & getCurrentExceptionMsg())
|
||||
else:
|
||||
# No public domain name, use ext IP if available
|
||||
if extIp.isSome() and extPort.isSome():
|
||||
@ -137,6 +176,14 @@ proc init*(
|
||||
except CatchableError:
|
||||
return err(getCurrentExceptionMsg())
|
||||
|
||||
if quicHostAddress.isSome():
|
||||
try:
|
||||
quicExtAddress = some(
|
||||
ipQuicEndPoint(extIp.get(), extQuicPort.get(quicBindPort.get(bindPort)))
|
||||
)
|
||||
except CatchableError:
|
||||
return err("failed to set ip quic endpoint: " & getCurrentExceptionMsg())
|
||||
|
||||
var announcedAddresses = newSeq[MultiAddress]()
|
||||
|
||||
if not extMultiAddrsOnly:
|
||||
@ -152,6 +199,11 @@ proc init*(
|
||||
# Only publish wsHostAddress if a WS address is not set in extMultiAddrs
|
||||
announcedAddresses.add(wsHostAddress.get())
|
||||
|
||||
if quicExtAddress.isSome():
|
||||
announcedAddresses.add(quicExtAddress.get())
|
||||
elif quicHostAddress.isSome() and not containsQuicAddress(extMultiAddrs):
|
||||
announcedAddresses.add(formatListenAddress(quicHostAddress.get()))
|
||||
|
||||
# External multiaddrs that the operator may have configured
|
||||
if extMultiAddrs.len > 0:
|
||||
announcedAddresses.add(extMultiAddrs)
|
||||
@ -164,7 +216,7 @@ proc init*(
|
||||
enrMultiaddrs = deduplicate(
|
||||
announcedAddresses.filterIt(
|
||||
it.hasProtocol("dns4") or it.hasProtocol("dns6") or it.hasProtocol("ws") or
|
||||
it.hasProtocol("wss")
|
||||
it.hasProtocol("wss") or it.hasProtocol("quic-v1")
|
||||
)
|
||||
)
|
||||
|
||||
@ -173,6 +225,7 @@ proc init*(
|
||||
hostAddress: hostAddress,
|
||||
clusterId: clusterId,
|
||||
wsHostAddress: wsHostAddress,
|
||||
quicHostAddress: quicHostAddress,
|
||||
hostExtAddress: hostExtAddress,
|
||||
wsExtAddress: wsExtAddress,
|
||||
extIp: extIp,
|
||||
|
||||
@ -59,6 +59,7 @@ proc newWakuSwitch*(
|
||||
privKey = none(crypto.PrivateKey),
|
||||
address = MultiAddress.init("/ip4/127.0.0.1/tcp/0").tryGet(),
|
||||
wsAddress = none(MultiAddress),
|
||||
quicAddress = none(MultiAddress),
|
||||
secureManagers: openarray[SecureProtocol] = [SecureProtocol.Noise],
|
||||
transportFlags: set[ServerFlags] = {},
|
||||
rng: crypto.Rng,
|
||||
@ -85,7 +86,6 @@ proc newWakuSwitch*(
|
||||
.withYamux()
|
||||
.withMplex(inTimeout, outTimeout)
|
||||
.withNoise()
|
||||
.withTcpTransport(transportFlags)
|
||||
.withNameResolver(nameResolver)
|
||||
.withSignedPeerRecord(sendSignedPeerRecord)
|
||||
.withCircuitRelay(circuitRelay)
|
||||
@ -109,15 +109,25 @@ proc newWakuSwitch*(
|
||||
b = b.withAgentVersion(agentString.get())
|
||||
if privKey.isSome():
|
||||
b = b.withPrivateKey(privKey.get())
|
||||
# tcp always; ws/quic added when their addr is set
|
||||
var addresses: seq[MultiAddress]
|
||||
if wsAddress.isSome():
|
||||
b = b.withAddresses(@[wsAddress.get(), address])
|
||||
addresses.add(wsAddress.get())
|
||||
addresses.add(address)
|
||||
if quicAddress.isSome():
|
||||
addresses.add(quicAddress.get())
|
||||
b = b.withAddresses(addresses)
|
||||
|
||||
b = b.withTcpTransport(transportFlags)
|
||||
|
||||
if wsAddress.isSome():
|
||||
if wssEnabled:
|
||||
b = b.withWssTransport(secureKeyPath, secureCertPath)
|
||||
else:
|
||||
b = b.withWsTransport()
|
||||
else:
|
||||
b = b.withAddress(address)
|
||||
|
||||
if quicAddress.isSome():
|
||||
b = b.withQuicTransport()
|
||||
|
||||
if not rendezvous.isNil():
|
||||
b = b.withRendezVous()
|
||||
|
||||
@ -239,15 +239,6 @@ proc parsePeerInfo*(maddrs: varargs[string]): Result[RemotePeerInfo, string] =
|
||||
|
||||
parsePeerInfo(multiAddresses)
|
||||
|
||||
func getTransportProtocol(typedR: enr.TypedRecord): Option[IpTransportProtocol] =
|
||||
if typedR.tcp6.isSome() or typedR.tcp.isSome():
|
||||
return some(IpTransportProtocol.tcpProtocol)
|
||||
|
||||
if typedR.udp6.isSome() or typedR.udp.isSome():
|
||||
return some(IpTransportProtocol.udpProtocol)
|
||||
|
||||
return none(IpTransportProtocol)
|
||||
|
||||
proc parseUrlPeerAddr*(
|
||||
peerAddr: Option[string]
|
||||
): Result[Option[RemotePeerInfo], string] =
|
||||
@ -262,9 +253,15 @@ proc parseUrlPeerAddr*(
|
||||
|
||||
return ok(some(parsedPeerInfo))
|
||||
|
||||
proc sortQuicFirst(addrs: seq[MultiAddress]): seq[MultiAddress] =
|
||||
## QUIC addresses first, so they are dialed ahead of TCP.
|
||||
addrs.filterIt("/quic-v1" in $it) & addrs.filterIt("/quic-v1" notin $it)
|
||||
|
||||
proc toRemotePeerInfo*(enrRec: enr.Record): Result[RemotePeerInfo, cstring] =
|
||||
## Converts an ENR to dialable RemotePeerInfo
|
||||
let typedR = enr.TypedRecord.fromRecord(enrRec)
|
||||
## enr to dialable RemotePeerInfo. tcp from tcp/tcp6 fields, quic from the
|
||||
## multiaddrs ext (udp field is discv5, not quic). quic sorted first.
|
||||
let typedR = enrRec.toTyped().valueOr:
|
||||
return err(cstring("enr: failed to construct typed record: " & $error))
|
||||
if not typedR.secp256k1.isSome():
|
||||
return err("enr: no secp256k1 key in record")
|
||||
|
||||
@ -273,41 +270,35 @@ proc toRemotePeerInfo*(enrRec: enr.Record): Result[RemotePeerInfo, cstring] =
|
||||
peerId =
|
||||
?PeerID.init(crypto.PublicKey(scheme: Secp256k1, skkey: secp.SkPublicKey(pubKey)))
|
||||
|
||||
let transportProto = getTransportProtocol(typedR)
|
||||
if transportProto.isNone():
|
||||
return err("enr: could not determine transport protocol")
|
||||
|
||||
var addrs = newSeq[MultiAddress]()
|
||||
case transportProto.get()
|
||||
of tcpProtocol:
|
||||
if typedR.ip.isSome() and typedR.tcp.isSome():
|
||||
let ip = ipv4(typedR.ip.get())
|
||||
addrs.add MultiAddress.init(ip, tcpProtocol, Port(typedR.tcp.get()))
|
||||
|
||||
if typedR.ip6.isSome():
|
||||
let ip = ipv6(typedR.ip6.get())
|
||||
if typedR.tcp6.isSome():
|
||||
addrs.add MultiAddress.init(ip, tcpProtocol, Port(typedR.tcp6.get()))
|
||||
elif typedR.tcp.isSome():
|
||||
addrs.add MultiAddress.init(ip, tcpProtocol, Port(typedR.tcp.get()))
|
||||
else:
|
||||
discard
|
||||
of udpProtocol:
|
||||
if typedR.ip.isSome() and typedR.udp.isSome():
|
||||
let ip = ipv4(typedR.ip.get())
|
||||
addrs.add MultiAddress.init(ip, udpProtocol, Port(typedR.udp.get()))
|
||||
# multiaddrs ext, may include quic
|
||||
let multiaddrsField = typedR.multiaddrs()
|
||||
if multiaddrsField.isSome():
|
||||
addrs.add(multiaddrsField.get())
|
||||
|
||||
if typedR.ip6.isSome():
|
||||
let ip = ipv6(typedR.ip6.get())
|
||||
if typedR.udp6.isSome():
|
||||
addrs.add MultiAddress.init(ip, udpProtocol, Port(typedR.udp6.get()))
|
||||
elif typedR.udp.isSome():
|
||||
addrs.add MultiAddress.init(ip, udpProtocol, Port(typedR.udp.get()))
|
||||
else:
|
||||
discard
|
||||
# tcp/tcp6 fields, skip if already in multiaddrs
|
||||
if typedR.ip.isSome() and typedR.tcp.isSome():
|
||||
let ip = ipv4(typedR.ip.get())
|
||||
let tcpAddr = MultiAddress.init(ip, tcpProtocol, Port(typedR.tcp.get()))
|
||||
if tcpAddr notin addrs:
|
||||
addrs.add(tcpAddr)
|
||||
|
||||
if typedR.ip6.isSome():
|
||||
let ip = ipv6(typedR.ip6.get())
|
||||
if typedR.tcp6.isSome():
|
||||
let tcp6Addr = MultiAddress.init(ip, tcpProtocol, Port(typedR.tcp6.get()))
|
||||
if tcp6Addr notin addrs:
|
||||
addrs.add(tcp6Addr)
|
||||
elif typedR.tcp.isSome():
|
||||
let tcp6Addr = MultiAddress.init(ip, tcpProtocol, Port(typedR.tcp.get()))
|
||||
if tcp6Addr notin addrs:
|
||||
addrs.add(tcp6Addr)
|
||||
|
||||
if addrs.len == 0:
|
||||
return err("enr: no addresses in record")
|
||||
return err("enr: no dialable addresses in record")
|
||||
|
||||
addrs = sortQuicFirst(addrs)
|
||||
|
||||
let protocolsRes = catch:
|
||||
enrRec.getCapabilitiesCodecs()
|
||||
@ -331,7 +322,7 @@ converter toRemotePeerInfo*(peerInfo: PeerInfo): RemotePeerInfo =
|
||||
## Useful for testing or internal connections
|
||||
RemotePeerInfo(
|
||||
peerId: peerInfo.peerId,
|
||||
addrs: peerInfo.listenAddrs,
|
||||
addrs: sortQuicFirst(peerInfo.listenAddrs),
|
||||
enr: none(enr.Record),
|
||||
protocols: peerInfo.protocols,
|
||||
shards: @[],
|
||||
|
||||
@ -137,12 +137,18 @@ asynctest "Start a node based on default test configuration":
|
||||
|
||||
suite "Auto-port retry":
|
||||
asynctest "metrics binds on free TCP port, fails on taken":
|
||||
let takenPort = Port(55100)
|
||||
let freePort = Port(55101)
|
||||
let taken = createStreamServer(initTAddress("127.0.0.1", takenPort))
|
||||
let taken = createStreamServer(initTAddress("127.0.0.1", Port(0)))
|
||||
defer:
|
||||
taken.stop()
|
||||
await taken.closeWait()
|
||||
let takenPort = taken.localAddress().port
|
||||
|
||||
let freePort = block:
|
||||
let probe = createStreamServer(initTAddress("127.0.0.1", Port(0)))
|
||||
let p = probe.localAddress().port
|
||||
probe.stop()
|
||||
await probe.closeWait()
|
||||
p
|
||||
|
||||
proc buildMetricsConf(port: Port): MetricsServerConf =
|
||||
var b = MetricsServerConfBuilder.init()
|
||||
@ -159,25 +165,30 @@ suite "Auto-port retry":
|
||||
await okRes.get().server.close()
|
||||
|
||||
asynctest "discv5 binds on free UDP port, fails on taken":
|
||||
let takenPort = Port(55200)
|
||||
let freePort = Port(55201)
|
||||
|
||||
proc dummyCb(
|
||||
transp: DatagramTransport, raddr: TransportAddress
|
||||
): Future[void] {.async: (raises: []).} =
|
||||
discard
|
||||
|
||||
let takenUdp =
|
||||
newDatagramTransport(dummyCb, local = initTAddress("0.0.0.0", takenPort))
|
||||
defer:
|
||||
await takenUdp.closeWait()
|
||||
|
||||
let nodeKey = generateSecp256k1Key()
|
||||
let node = newTestWakuNode(nodeKey, parseIpAddress("0.0.0.0"), Port(0))
|
||||
await node.start()
|
||||
defer:
|
||||
await node.stop()
|
||||
|
||||
let takenUdp =
|
||||
newDatagramTransport(dummyCb, local = initTAddress("0.0.0.0", Port(0)))
|
||||
defer:
|
||||
await takenUdp.closeWait()
|
||||
let takenPort = takenUdp.localAddress().port
|
||||
|
||||
let freePort = block:
|
||||
let probe =
|
||||
newDatagramTransport(dummyCb, local = initTAddress("0.0.0.0", Port(0)))
|
||||
let p = probe.localAddress().port
|
||||
await probe.closeWait()
|
||||
p
|
||||
|
||||
proc buildDiscv5Conf(port: Port): Discv5Conf =
|
||||
var b = Discv5ConfBuilder.init()
|
||||
b.withEnabled(true)
|
||||
|
||||
@ -307,10 +307,17 @@ procSuite "Peer Manager":
|
||||
let
|
||||
database = SqliteDatabase.new(":memory:")[]
|
||||
storage = WakuPeerStorage.new(database)[]
|
||||
# tcp-only: asserts exact AddressBook contents
|
||||
node1 = newTestWakuNode(
|
||||
generateSecp256k1Key(), getPrimaryIPAddr(), Port(44048), peerStorage = storage
|
||||
generateSecp256k1Key(),
|
||||
getPrimaryIPAddr(),
|
||||
Port(44048),
|
||||
peerStorage = storage,
|
||||
quicEnabled = false,
|
||||
)
|
||||
node2 = newTestWakuNode(
|
||||
generateSecp256k1Key(), getPrimaryIPAddr(), Port(34023), quicEnabled = false
|
||||
)
|
||||
node2 = newTestWakuNode(generateSecp256k1Key(), getPrimaryIPAddr(), Port(34023))
|
||||
|
||||
node1.mountMetadata(0, @[0'u16]).expect("Mounted Waku Metadata")
|
||||
node2.mountMetadata(0, @[0'u16]).expect("Mounted Waku Metadata")
|
||||
@ -349,6 +356,7 @@ procSuite "Peer Manager":
|
||||
parseIpAddress("127.0.0.1"),
|
||||
Port(56037),
|
||||
peerStorage = storage,
|
||||
quicEnabled = false,
|
||||
)
|
||||
|
||||
node3.mountMetadata(0, @[0'u16]).expect("Mounted Waku Metadata")
|
||||
@ -380,10 +388,17 @@ procSuite "Peer Manager":
|
||||
let
|
||||
database = SqliteDatabase.new(":memory:")[]
|
||||
storage = WakuPeerStorage.new(database)[]
|
||||
# tcp-only: asserts exact AddressBook contents
|
||||
node1 = newTestWakuNode(
|
||||
generateSecp256k1Key(), getPrimaryIPAddr(), Port(44048), peerStorage = storage
|
||||
generateSecp256k1Key(),
|
||||
getPrimaryIPAddr(),
|
||||
Port(44048),
|
||||
peerStorage = storage,
|
||||
quicEnabled = false,
|
||||
)
|
||||
node2 = newTestWakuNode(
|
||||
generateSecp256k1Key(), getPrimaryIPAddr(), Port(34023), quicEnabled = false
|
||||
)
|
||||
node2 = newTestWakuNode(generateSecp256k1Key(), getPrimaryIPAddr(), Port(34023))
|
||||
|
||||
node1.mountMetadata(0, @[0'u16]).expect("Mounted Waku Metadata")
|
||||
node2.mountMetadata(0, @[0'u16]).expect("Mounted Waku Metadata")
|
||||
@ -422,6 +437,7 @@ procSuite "Peer Manager":
|
||||
parseIpAddress("127.0.0.1"),
|
||||
Port(56037),
|
||||
peerStorage = storage,
|
||||
quicEnabled = false,
|
||||
)
|
||||
|
||||
node3.mountMetadata(0, @[0'u16]).expect("Mounted Waku Metadata")
|
||||
@ -1234,8 +1250,8 @@ procSuite "Peer Manager":
|
||||
|
||||
asyncTest "Retrieve peer that mounted peer exchange":
|
||||
let
|
||||
node1 = newTestWakuNode(generateSecp256k1Key(), getPrimaryIPAddr(), Port(55048))
|
||||
node2 = newTestWakuNode(generateSecp256k1Key(), getPrimaryIPAddr(), Port(55023))
|
||||
node1 = newTestWakuNode(generateSecp256k1Key(), getPrimaryIPAddr(), Port(0))
|
||||
node2 = newTestWakuNode(generateSecp256k1Key(), getPrimaryIPAddr(), Port(0))
|
||||
|
||||
await allFutures(node1.start(), node2.start())
|
||||
await allFutures(node1.mountRelay(), node2.mountRelay())
|
||||
|
||||
@ -130,6 +130,60 @@ suite "Waku NetConfig":
|
||||
netConfig.announcedAddresses.len == 2 # Bind address + extAddress
|
||||
netConfig.announcedAddresses[1] == extMultiAddrs[0]
|
||||
|
||||
asyncTest "Operator QUIC extMultiAddr suppresses the auto QUIC address":
|
||||
let
|
||||
conf = defaultTestWakuConf()
|
||||
bindPort = conf.endpointConf.p2pTcpPort
|
||||
operatorQuic = ipQuicEndPoint(parseIpAddress("1.2.3.4"), Port(9999))
|
||||
|
||||
# no operator quic addr: node auto-announces its bind-port quic addr
|
||||
let autoRes = NetConfig.init(
|
||||
bindIp = conf.endpointConf.p2pListenAddress,
|
||||
bindPort = bindPort,
|
||||
quicEnabled = true,
|
||||
quicBindPort = some(bindPort),
|
||||
)
|
||||
assert autoRes.isOk(), $autoRes.error
|
||||
check autoRes.get().announcedAddresses.filterIt(it.isQuicAddress()).len == 1
|
||||
|
||||
# operator quic addr given: auto bind-port addr suppressed, only theirs remains
|
||||
let opRes = NetConfig.init(
|
||||
bindIp = conf.endpointConf.p2pListenAddress,
|
||||
bindPort = bindPort,
|
||||
quicEnabled = true,
|
||||
quicBindPort = some(bindPort),
|
||||
extMultiAddrs = @[operatorQuic],
|
||||
)
|
||||
assert opRes.isOk(), $opRes.error
|
||||
let opNetConfig = opRes.get()
|
||||
check:
|
||||
opNetConfig.announcedAddresses.filterIt(it.isQuicAddress()) == @[operatorQuic]
|
||||
opNetConfig.enrMultiaddrs.filterIt(it.isQuicAddress()) == @[operatorQuic]
|
||||
|
||||
asyncTest "Announced QUIC address uses the NAT-mapped external port, not the bind port":
|
||||
let
|
||||
conf = defaultTestWakuConf()
|
||||
extIp = parseIpAddress("1.2.3.4")
|
||||
quicBindPort = Port(60000)
|
||||
natQuicPort = Port(9999) # external UDP port a NAT (UPnP/PMP) mapped for us
|
||||
|
||||
let netConfigRes = NetConfig.init(
|
||||
bindIp = conf.endpointConf.p2pListenAddress,
|
||||
bindPort = conf.endpointConf.p2pTcpPort,
|
||||
extIp = some(extIp),
|
||||
extPort = some(Port(1234)),
|
||||
quicEnabled = true,
|
||||
quicBindPort = some(quicBindPort),
|
||||
extQuicPort = some(natQuicPort),
|
||||
)
|
||||
assert netConfigRes.isOk(), $netConfigRes.error
|
||||
|
||||
let quicAddrs = netConfigRes.get().announcedAddresses.filterIt(it.isQuicAddress())
|
||||
check:
|
||||
quicAddrs.len == 1
|
||||
("/udp/" & $(natQuicPort.uint16) & "/quic-v1") in $quicAddrs[0]
|
||||
("/udp/" & $(quicBindPort.uint16) & "/quic-v1") notin $quicAddrs[0]
|
||||
|
||||
asyncTest "AnnouncedAddresses uses dns4DomainName over extIp when both are provided":
|
||||
let
|
||||
conf = defaultTestWakuConf()
|
||||
@ -441,6 +495,8 @@ suite "Waku NetConfig":
|
||||
let netConfigRes = NetConfig.init(
|
||||
bindIp = conf.endpointConf.p2pListenAddress,
|
||||
bindPort = conf.endpointConf.p2pTcpPort,
|
||||
quicEnabled = true,
|
||||
quicBindPort = some(Port(60000)),
|
||||
extMultiAddrs = extMultiAddrs,
|
||||
extMultiAddrsOnly = true,
|
||||
)
|
||||
@ -452,3 +508,4 @@ suite "Waku NetConfig":
|
||||
check:
|
||||
netConfig.announcedAddresses.len == 1 # ExtAddress
|
||||
netConfig.announcedAddresses[0] == extMultiAddrs[0]
|
||||
netConfig.announcedAddresses.filterIt(it.isQuicAddress()).len == 0
|
||||
|
||||
@ -10,6 +10,8 @@ import
|
||||
libp2p/crypto/secp,
|
||||
libp2p/multiaddress,
|
||||
libp2p/switch,
|
||||
libp2p/muxers/muxer,
|
||||
libp2p/stream/connection,
|
||||
libp2p/protocols/pubsub/rpc/messages,
|
||||
libp2p/protocols/pubsub/pubsub,
|
||||
libp2p/protocols/pubsub/gossipsub,
|
||||
@ -122,10 +124,7 @@ suite "WakuNode":
|
||||
maxConnections = 20
|
||||
nodeKey1 = generateSecp256k1Key()
|
||||
node1 = newTestWakuNode(
|
||||
nodeKey1,
|
||||
parseIpAddress("127.0.0.1"),
|
||||
Port(60010),
|
||||
maxConnections = maxConnections,
|
||||
nodeKey1, parseIpAddress("127.0.0.1"), Port(0), maxConnections = maxConnections
|
||||
)
|
||||
|
||||
# Initialize and start node1
|
||||
@ -137,11 +136,10 @@ suite "WakuNode":
|
||||
var otherNodes: seq[WakuNode] = @[]
|
||||
|
||||
# Create and start 20 other nodes
|
||||
for i in 0 ..< maxConnections + 1:
|
||||
for _ in 0 ..< maxConnections + 1:
|
||||
let
|
||||
nodeKey = generateSecp256k1Key()
|
||||
port = 60012 + i * 2 # Ensure unique ports for each node
|
||||
node = newTestWakuNode(nodeKey, parseIpAddress("127.0.0.1"), Port(port))
|
||||
node = newTestWakuNode(nodeKey, parseIpAddress("127.0.0.1"), Port(0))
|
||||
await node.start()
|
||||
(await node.mountRelay()).isOkOr:
|
||||
assert false, "Failed to mount relay"
|
||||
@ -187,7 +185,9 @@ suite "WakuNode":
|
||||
bindPort = Port(61006)
|
||||
extIp = some(getPrimaryIPAddr())
|
||||
extPort = some(Port(61008))
|
||||
node = newTestWakuNode(nodeKey, bindIp, bindPort, extIp, extPort)
|
||||
# tcp-only: asserts exact single listen addr; quic variant below
|
||||
node =
|
||||
newTestWakuNode(nodeKey, bindIp, bindPort, extIp, extPort, quicEnabled = false)
|
||||
|
||||
let
|
||||
bindEndpoint = MultiAddress.init(bindIp, tcpProtocol, bindPort)
|
||||
@ -216,6 +216,80 @@ suite "WakuNode":
|
||||
|
||||
await node.stop()
|
||||
|
||||
asyncTest "Peer info updates with correct announced addresses (QUIC)":
|
||||
# dual-stack node announces both tcp and quic-v1
|
||||
let
|
||||
nodeKey = generateSecp256k1Key()
|
||||
bindIp = parseIpAddress("0.0.0.0")
|
||||
bindPort = Port(61006)
|
||||
extIp = some(getPrimaryIPAddr())
|
||||
extPort = some(Port(61008))
|
||||
node =
|
||||
newTestWakuNode(nodeKey, bindIp, bindPort, extIp, extPort, quicEnabled = true)
|
||||
|
||||
let tcpAnnounced = MultiAddress.init(extIp.get(), tcpProtocol, extPort.get())
|
||||
|
||||
check:
|
||||
node.announcedAddresses.len >= 2
|
||||
node.announcedAddresses.contains(tcpAnnounced)
|
||||
node.announcedAddresses.anyIt("/quic-v1" in $it)
|
||||
|
||||
await node.start()
|
||||
|
||||
check:
|
||||
node.started
|
||||
node.switch.peerInfo.addrs.len >= 2
|
||||
node.switch.peerInfo.addrs.contains(tcpAnnounced)
|
||||
node.switch.peerInfo.addrs.anyIt("/quic-v1" in $it)
|
||||
|
||||
await node.stop()
|
||||
|
||||
test "toRemotePeerInfo sorts quic-v1 addresses first":
|
||||
let
|
||||
nodeKey = generateSecp256k1Key()
|
||||
node =
|
||||
newTestWakuNode(nodeKey, parseIpAddress("0.0.0.0"), Port(0), quicEnabled = true)
|
||||
|
||||
# peerinfo path
|
||||
let fromPeerInfo = node.switch.peerInfo.toRemotePeerInfo()
|
||||
check:
|
||||
fromPeerInfo.addrs.anyIt("/quic-v1" in $it)
|
||||
"/quic-v1" in $fromPeerInfo.addrs[0]
|
||||
|
||||
# enr path
|
||||
let fromEnr = toRemotePeerInfo(node.enr).valueOr:
|
||||
raiseAssert "toRemotePeerInfo(enr) failed: " & $error
|
||||
check:
|
||||
fromEnr.addrs.anyIt("/quic-v1" in $it)
|
||||
"/quic-v1" in $fromEnr.addrs[0]
|
||||
|
||||
asyncTest "Dual-stack nodes connect over QUIC":
|
||||
let
|
||||
nodeKey1 = generateSecp256k1Key()
|
||||
node1 = newTestWakuNode(
|
||||
nodeKey1, parseIpAddress("0.0.0.0"), Port(0), quicEnabled = true
|
||||
)
|
||||
nodeKey2 = generateSecp256k1Key()
|
||||
node2 = newTestWakuNode(
|
||||
nodeKey2, parseIpAddress("0.0.0.0"), Port(0), quicEnabled = true
|
||||
)
|
||||
|
||||
await allFutures(node1.start(), node2.start())
|
||||
|
||||
await node1.connectToNodes(@[node2.switch.peerInfo.toRemotePeerInfo()])
|
||||
|
||||
let conns = node1.switch.connManager.getConnections().getOrDefault(
|
||||
node2.switch.peerInfo.peerId
|
||||
)
|
||||
check conns.len >= 1
|
||||
let obAddr = conns[0].connection.observedAddr.valueOr:
|
||||
raiseAssert "connection has no observed address"
|
||||
check:
|
||||
"/udp/" in $obAddr
|
||||
"/tcp/" notin $obAddr
|
||||
|
||||
await allFutures(node1.stop(), node2.stop())
|
||||
|
||||
asyncTest "Node can use dns4 in announced addresses":
|
||||
let
|
||||
nodeKey = generateSecp256k1Key()
|
||||
@ -231,7 +305,8 @@ suite "WakuNode":
|
||||
)
|
||||
|
||||
check:
|
||||
node.announcedAddresses.len == 1
|
||||
# dual-stack also adds a dns4 quic addr, don't assert exact count
|
||||
node.announcedAddresses.len >= 1
|
||||
node.announcedAddresses.contains(expectedDns4Addr)
|
||||
|
||||
asyncTest "Node uses dns4 resolved ip in announced addresses if no extIp is provided":
|
||||
|
||||
@ -61,6 +61,8 @@ proc newTestWakuNode*(
|
||||
wsBindPort: Port = (Port) 8000,
|
||||
wsEnabled: bool = false,
|
||||
wssEnabled: bool = false,
|
||||
quicBindPort: Port = (Port) 0,
|
||||
quicEnabled: bool = true,
|
||||
secureKey: string = "",
|
||||
secureCert: string = "",
|
||||
wakuFlags = none(CapabilitiesBitfield),
|
||||
@ -75,6 +77,17 @@ proc newTestWakuNode*(
|
||||
): WakuNode =
|
||||
logging.setupLog(logging.LogLevel.DEBUG, logging.LogFormat.TEXT)
|
||||
|
||||
# quic won't bind 0.0.0.0 wildcard, use loopback for tests
|
||||
let bindIp =
|
||||
if quicEnabled and $bindIp == "0.0.0.0":
|
||||
parseIpAddress("127.0.0.1")
|
||||
else:
|
||||
bindIp
|
||||
|
||||
# reuse bindPort for quic so the enr addr is dialable; port 0 would advertise /udp/0
|
||||
let quicBindPort =
|
||||
if quicEnabled and quicBindPort == Port(0): bindPort else: quicBindPort
|
||||
|
||||
var resolvedExtIp = extIp
|
||||
|
||||
# Update extPort to default value if it's missing and there's an extIp or a DNS domain
|
||||
@ -106,6 +119,8 @@ proc newTestWakuNode*(
|
||||
wsBindPort = some(wsBindPort),
|
||||
wsEnabled = wsEnabled,
|
||||
wssEnabled = wssEnabled,
|
||||
quicBindPort = some(quicBindPort),
|
||||
quicEnabled = quicEnabled,
|
||||
dns4DomainName = dns4DomainName,
|
||||
discv5UdpPort = discv5UdpPort,
|
||||
wakuFlags = wakuFlags,
|
||||
|
||||
@ -400,7 +400,10 @@ suite "WakuNode - Relay":
|
||||
asyncTest "Messages relaying fails with non-overlapping transports (TCP or Websockets)":
|
||||
let
|
||||
nodeKey1 = generateSecp256k1Key()
|
||||
node1 = newTestWakuNode(nodeKey1, parseIpAddress("0.0.0.0"), bindPort = Port(0))
|
||||
# tcp/ws isolation: node1 tcp, node2 ws, no shared transport. quic off or they'd share it.
|
||||
node1 = newTestWakuNode(
|
||||
nodeKey1, parseIpAddress("0.0.0.0"), bindPort = Port(0), quicEnabled = false
|
||||
)
|
||||
nodeKey2 = generateSecp256k1Key()
|
||||
node2 = newTestWakuNode(
|
||||
nodeKey2,
|
||||
@ -408,6 +411,7 @@ suite "WakuNode - Relay":
|
||||
bindPort = Port(0),
|
||||
wsBindPort = Port(0),
|
||||
wsEnabled = true,
|
||||
quicEnabled = false,
|
||||
)
|
||||
shard = DefaultRelayShard
|
||||
contentTopic = ContentTopic("/waku/2/default-content/proto")
|
||||
|
||||
@ -2,7 +2,7 @@ import logos_delivery/waku/compat/option_valueor
|
||||
{.used.}
|
||||
|
||||
import
|
||||
std/json,
|
||||
std/[json, net, sequtils, strutils],
|
||||
testutils/unittests,
|
||||
chronicles,
|
||||
chronos,
|
||||
@ -142,3 +142,31 @@ suite "Wakunode2 - Waku initialization":
|
||||
parsed["rest"].getInt() != 0
|
||||
parsed["discv5Udp"].getInt() != 0
|
||||
parsed["metrics"].getInt() != 0
|
||||
|
||||
test "QUIC port=0 auto-binds and advertises the real port":
|
||||
var builder = defaultTestWakuConfBuilder()
|
||||
builder.withP2pListenAddress(parseIpAddress("127.0.0.1"))
|
||||
builder.withP2pTcpPort(Port(0))
|
||||
builder.quicConf.withEnabled(true)
|
||||
builder.quicConf.withQuicPort(Port(0))
|
||||
|
||||
let conf = builder.build().valueOr:
|
||||
raiseAssert error
|
||||
check conf.quicConf.get().port == Port(0)
|
||||
|
||||
var waku = (waitFor Waku.new(conf)).valueOr:
|
||||
raiseAssert error
|
||||
defer:
|
||||
(waitFor waku.stop()).isOkOr:
|
||||
raiseAssert error
|
||||
|
||||
(waitFor waku.start()).isOkOr:
|
||||
raiseAssert error
|
||||
|
||||
let parsed = parseJson(waku.stateInfo.getNodeInfoItem(NodeInfoId.MyBoundPorts))
|
||||
check parsed["quic"].getInt() != 0
|
||||
|
||||
let quicAddrs = waku.node.announcedAddresses.filterIt("/quic-v1" in $it)
|
||||
check:
|
||||
quicAddrs.len >= 1
|
||||
quicAddrs.allIt("/udp/0/quic-v1" notin $it)
|
||||
|
||||
@ -38,9 +38,9 @@ suite "Waku v2 Rest API - Admin":
|
||||
var client {.threadvar.}: RestClientRef
|
||||
|
||||
asyncSetup:
|
||||
node1 = newTestWakuNode(generateSecp256k1Key(), getPrimaryIPAddr(), Port(60600))
|
||||
node2 = newTestWakuNode(generateSecp256k1Key(), getPrimaryIPAddr(), Port(60602))
|
||||
node3 = newTestWakuNode(generateSecp256k1Key(), getPrimaryIPAddr(), Port(60604))
|
||||
node1 = newTestWakuNode(generateSecp256k1Key(), getPrimaryIPAddr(), Port(0))
|
||||
node2 = newTestWakuNode(generateSecp256k1Key(), getPrimaryIPAddr(), Port(0))
|
||||
node3 = newTestWakuNode(generateSecp256k1Key(), getPrimaryIPAddr(), Port(0))
|
||||
|
||||
let clusterId = 1.uint16
|
||||
let shards: seq[uint16] = @[0]
|
||||
|
||||
@ -714,6 +714,17 @@ hence would have reachability issues.""",
|
||||
name: "websocket-secure-cert-path"
|
||||
.}: string
|
||||
|
||||
## quic config
|
||||
quicSupport* {.
|
||||
desc: "Enable QUIC transport: true|false",
|
||||
defaultValue: false,
|
||||
name: "quic-support"
|
||||
.}: bool
|
||||
|
||||
quicPort* {.
|
||||
desc: "QUIC (UDP) listening port.", defaultValue: 60000, name: "quic-port"
|
||||
.}: Port
|
||||
|
||||
## Rate limitation config, if not set, rate limit checks will not be performed
|
||||
rateLimits* {.
|
||||
desc:
|
||||
@ -1160,6 +1171,9 @@ proc toWakuConf*(n: WakuNodeConf): ConfResult[WakuConf] =
|
||||
b.webSocketConf.withKeyPath(n.websocketSecureKeyPath)
|
||||
b.webSocketConf.withCertPath(n.websocketSecureCertPath)
|
||||
|
||||
b.quicConf.withEnabled(n.quicSupport)
|
||||
b.quicConf.withQuicPort(n.quicPort)
|
||||
|
||||
if n.rateLimits.len > 0:
|
||||
b.rateLimitConf.withRateLimits(n.rateLimits)
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user