diff --git a/apps/wakunode2/wakunode2.nim b/apps/wakunode2/wakunode2.nim index f1192a70d..63860e837 100644 --- a/apps/wakunode2/wakunode2.nim +++ b/apps/wakunode2/wakunode2.nim @@ -28,8 +28,7 @@ import ../../waku/v2/node/peer_manager, ../../waku/v2/node/peer_manager/peer_store/waku_peer_storage, ../../waku/v2/node/peer_manager/peer_store/migrations as peer_store_sqlite_migrations, - ../../waku/v2/node/wakuswitch, - ../../waku/v2/node/waku_node, + ../../waku/v2/waku_node, ../../waku/v2/node/waku_metrics, ../../waku/v2/protocol/waku_archive, ../../waku/v2/protocol/waku_archive/driver/queue_driver, @@ -287,10 +286,7 @@ proc initNode(conf: WakuNodeConf, # 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: - netConfigOpt = some(NetConfig.init( + let netConfigRes = NetConfig.init( bindIp = conf.listenAddress, bindPort = Port(uint16(conf.tcpPort) + conf.portsShift), extIp = extIp, @@ -302,11 +298,11 @@ proc initNode(conf: WakuNodeConf, dns4DomainName = dns4DomainName, discv5UdpPort = discv5UdpPort, wakuFlags = some(wakuFlags), - )) - except CatchableError: - return err("failed to create net config instance: " & getCurrentExceptionMsg()) + ) + if netConfigRes.isErr(): + return err("failed to create net config instance: " & netConfigRes.error) - let netConfig = netConfigOpt.get() + let netConfig = netConfigRes.get() var wakuDiscv5 = none(WakuDiscoveryV5) if conf.discv5Discovery: diff --git a/tests/v2/test_waku_discv5.nim b/tests/v2/test_waku_discv5.nim index 208cb60d0..c23715387 100644 --- a/tests/v2/test_waku_discv5.nim +++ b/tests/v2/test_waku_discv5.nim @@ -1,16 +1,16 @@ {.used.} import + stew/[results, byteutils], + stew/shims/net, chronos, chronicles, testutils/unittests, - stew/byteutils, - stew/shims/net, libp2p/crypto/crypto, eth/keys, eth/p2p/discoveryv5/enr import - ../../waku/v2/node/waku_node, + ../../waku/v2/waku_node, ../../waku/v2/protocol/waku_message, ../../waku/v2/protocol/waku_discv5, ./testlib/common, @@ -146,7 +146,7 @@ procSuite "Waku Discovery v5": bindPort = nodeTcpPort1, extmultiAddrs = @[expectedMultiAddr], wakuFlags = some(flags), - discv5UdpPort = some(nodeUdpPort1)) + discv5UdpPort = some(nodeUdpPort1)).get() node1discV5 = WakuDiscoveryV5.new(extIp = node1NetConfig.extIp, extTcpPort = node1NetConfig.extPort, extUdpPort = node1NetConfig.discv5UdpPort, @@ -170,7 +170,7 @@ procSuite "Waku Discovery v5": extPort = some(nodeTcpPort2), bindPort = nodeTcpPort2, wakuFlags = some(flags), - discv5UdpPort = some(nodeUdpPort2)) + discv5UdpPort = some(nodeUdpPort2)).get() node2discV5 = WakuDiscoveryV5.new(extIp = node2NetConfig.extIp, extTcpPort = node2NetConfig.extPort, extUdpPort = node2NetConfig.discv5UdpPort, diff --git a/tests/v2/test_waku_switch.nim b/tests/v2/test_waku_switch.nim index 79836d499..6189b52ae 100644 --- a/tests/v2/test_waku_switch.nim +++ b/tests/v2/test_waku_switch.nim @@ -9,7 +9,7 @@ import libp2p/protocols/connectivity/relay/client, stew/byteutils import - ../../waku/v2/node/wakuswitch, + ../../waku/v2/node/waku_switch, ./testlib/common, ./testlib/waku2 diff --git a/waku/v2/node/README.md b/waku/v2/node/README.md deleted file mode 100644 index a42f04809..000000000 --- a/waku/v2/node/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Waku Node v2 - -This folder contains code related to running a `wakunode2` process. The main entrypoint is the `wakunode2` file. - -See `../../docs/api/v2/node.md` for more details on the Nim Node API. diff --git a/waku/v2/node/config.nim b/waku/v2/node/config.nim new file mode 100644 index 000000000..3f6f6ba81 --- /dev/null +++ b/waku/v2/node/config.nim @@ -0,0 +1,149 @@ +when (NimMajor, NimMinor) < (1, 4): + {.push raises: [Defect].} +else: + {.push raises: [].} + +import + std/[options, sequtils], + stew/results, + stew/shims/net, + libp2p/multiaddress +import + ../protocol/waku_enr + + +type NetConfig* = object + hostAddress*: MultiAddress + wsHostAddress*: Option[MultiAddress] + hostExtAddress*: Option[MultiAddress] + wsExtAddress*: Option[MultiAddress] + wssEnabled*: bool + extIp*: Option[ValidIpAddress] + extPort*: Option[Port] + dns4DomainName*: Option[string] + announcedAddresses*: seq[MultiAddress] + extMultiAddrs*: seq[MultiAddress] + enrMultiAddrs*: seq[MultiAddress] + enrIp*: Option[ValidIpAddress] + enrPort*: Option[Port] + discv5UdpPort*: Option[Port] + wakuFlags*: Option[CapabilitiesBitfield] + bindIp*: ValidIpAddress + bindPort*: Port + +type NetConfigResult* = Result[NetConfig, string] + + +template ip4TcpEndPoint(address, port): MultiAddress = + MultiAddress.init(address, tcpProtocol, port) + +template dns4Ma(dns4DomainName: string): MultiAddress = + MultiAddress.init("/dns4/" & dns4DomainName).tryGet() + +template tcpPortMa(port: Port): MultiAddress = + MultiAddress.init("/tcp/" & $port).tryGet() + +template dns4TcpEndPoint(dns4DomainName: string, port: Port): MultiAddress = + dns4Ma(dns4DomainName) & tcpPortMa(port) + +template wsFlag(wssEnabled: bool): MultiAddress = + if wssEnabled: MultiAddress.init("/wss").tryGet() + else: MultiAddress.init("/ws").tryGet() + + +proc init*(T: type NetConfig, + bindIp: ValidIpAddress, + bindPort: Port, + extIp = none(ValidIpAddress), + extPort = none(Port), + extMultiAddrs = newSeq[MultiAddress](), + wsBindPort: Port = Port(8000), + wsEnabled: bool = false, + wssEnabled: bool = false, + dns4DomainName = none(string), + discv5UdpPort = none(Port), + wakuFlags = none(CapabilitiesBitfield)): NetConfigResult = + ## Initialize and validate waku node network configuration + + # Bind addresses + let hostAddress = ip4TcpEndPoint(bindIp, bindPort) + + var wsHostAddress = none(MultiAddress) + if wsEnabled or wssEnabled: + try: + wsHostAddress = some(ip4TcpEndPoint(bindIp, wsbindPort) & wsFlag(wssEnabled)) + except CatchableError: + return err(getCurrentExceptionMsg()) + + let enrIp = if extIp.isSome(): extIp else: some(bindIp) + let enrPort = if extPort.isSome(): extPort else: some(bindPort) + + # Setup external addresses, if available + var hostExtAddress, wsExtAddress = none(MultiAddress) + + if dns4DomainName.isSome(): + # Use dns4 for externally announced addresses + try: + hostExtAddress = some(dns4TcpEndPoint(dns4DomainName.get(), extPort.get())) + except CatchableError: + return err(getCurrentExceptionMsg()) + + if wsHostAddress.isSome(): + try: + wsExtAddress = some(dns4TcpEndPoint(dns4DomainName.get(), wsBindPort) & wsFlag(wssEnabled)) + except CatchableError: + return err(getCurrentExceptionMsg()) + else: + # No public domain name, use ext IP if available + if extIp.isSome() and extPort.isSome(): + hostExtAddress = some(ip4TcpEndPoint(extIp.get(), extPort.get())) + + if wsHostAddress.isSome(): + try: + wsExtAddress = some(ip4TcpEndPoint(extIp.get(), wsBindPort) & wsFlag(wssEnabled)) + except CatchableError: + return err(getCurrentExceptionMsg()) + + 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(): + announcedAddresses.add(wsHostAddress.get()) + + let + # 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")) + + ok(NetConfig( + hostAddress: hostAddress, + wsHostAddress: wsHostAddress, + hostExtAddress: hostExtAddress, + wsExtAddress: wsExtAddress, + extIp: extIp, + extPort: extPort, + wssEnabled: wssEnabled, + dns4DomainName: dns4DomainName, + announcedAddresses: announcedAddresses, + extMultiAddrs: extMultiAddrs, + enrMultiaddrs: enrMultiaddrs, + enrIp: enrIp, + enrPort: enrPort, + discv5UdpPort: discv5UdpPort, + bindIp: bindIp, + bindPort: bindPort, + wakuFlags: wakuFlags + )) diff --git a/waku/v2/node/nim.cfg b/waku/v2/node/nim.cfg deleted file mode 100644 index a31adc759..000000000 --- a/waku/v2/node/nim.cfg +++ /dev/null @@ -1,5 +0,0 @@ --d:chronicles_line_numbers --d:"chronicles_runtime_filtering=on" --d:nimDebugDlOpen -# Results in empty output for some reason -#-d:"chronicles_enabled_topics=GossipSub:TRACE,WakuRelay:TRACE" diff --git a/waku/v2/node/waku_node.nim b/waku/v2/node/waku_node.nim index eab95e2b4..89a4095ac 100644 --- a/waku/v2/node/waku_node.nim +++ b/waku/v2/node/waku_node.nim @@ -39,8 +39,9 @@ import ../protocol/waku_peer_exchange, ../utils/peers, ../utils/time, + ./config, ./peer_manager, - ./wakuswitch + ./waku_switch when defined(rln): import @@ -58,6 +59,7 @@ logScope: topics = "waku node" +# TODO: Move to application instance (e.g., `WakuNode2`) # Git version in git describe format (defined compile time) const git_version* {.strdefine.} = "n/a" @@ -70,9 +72,7 @@ const WakuFilterTimeout: Duration = 1.days # key and crypto modules different type - # XXX: Weird type, should probably be using pubsub PubsubTopic object name? - Message* = seq[byte] - + # TODO: Move to application instance (e.g., `WakuNode2`) WakuInfo* = object # NOTE One for simplicity, can extend later as needed listenAddresses*: seq[string] @@ -101,126 +101,6 @@ type announcedAddresses* : seq[MultiAddress] started*: bool # Indicates that node has started listening -template ip4TcpEndPoint(address, port): MultiAddress = - MultiAddress.init(address, tcpProtocol, port) - -template dns4Ma(dns4DomainName: string): MultiAddress = - MultiAddress.init("/dns4/" & dns4DomainName).tryGet() - -template tcpPortMa(port: Port): MultiAddress = - MultiAddress.init("/tcp/" & $port).tryGet() - -template dns4TcpEndPoint(dns4DomainName: string, port: Port): MultiAddress = - dns4Ma(dns4DomainName) & tcpPortMa(port) - -template wsFlag(wssEnabled: bool): MultiAddress = - if wssEnabled: MultiAddress.init("/wss").tryGet() - else: MultiAddress.init("/ws").tryGet() - -type NetConfig* = object - hostAddress*: MultiAddress - wsHostAddress*: Option[MultiAddress] - hostExtAddress*: Option[MultiAddress] - wsExtAddress*: Option[MultiAddress] - wssEnabled*: bool - extIp*: Option[ValidIpAddress] - extPort*: Option[Port] - dns4DomainName*: Option[string] - announcedAddresses*: seq[MultiAddress] - extMultiAddrs*: seq[MultiAddress] - enrMultiAddrs*: seq[MultiAddress] - enrIp*: Option[ValidIpAddress] - enrPort*: Option[Port] - discv5UdpPort*: Option[Port] - wakuFlags*: Option[CapabilitiesBitfield] - bindIp*: ValidIpAddress - bindPort*: Port - -proc init*( - T: type NetConfig, - bindIp: ValidIpAddress, - bindPort: Port, - extIp = none(ValidIpAddress), - extPort = none(Port), - extMultiAddrs = newSeq[MultiAddress](), - wsBindPort: Port = (Port)8000, - wsEnabled: bool = false, - wssEnabled: bool = false, - dns4DomainName = none(string), - discv5UdpPort = none(Port), - wakuFlags = none(CapabilitiesBitfield) -): T {.raises: [LPError]} = - ## Initialize addresses - let - # Bind addresses - hostAddress = ip4TcpEndPoint(bindIp, bindPort) - wsHostAddress = if wsEnabled or wssEnabled: some(ip4TcpEndPoint(bindIp, wsbindPort) & wsFlag(wssEnabled)) - else: none(MultiAddress) - enrIp = if extIp.isSome(): extIp else: some(bindIp) - enrPort = if extPort.isSome(): extPort else: some(bindPort) - - # Setup external addresses, if available - var - hostExtAddress, wsExtAddress = none(MultiAddress) - - if (dns4DomainName.isSome()): - # Use dns4 for externally announced addresses - hostExtAddress = some(dns4TcpEndPoint(dns4DomainName.get(), extPort.get())) - - if (wsHostAddress.isSome()): - wsExtAddress = some(dns4TcpEndPoint(dns4DomainName.get(), wsBindPort) & wsFlag(wssEnabled)) - else: - # No public domain name, use ext IP if available - if extIp.isSome() and extPort.isSome(): - hostExtAddress = some(ip4TcpEndPoint(extIp.get(), extPort.get())) - - if (wsHostAddress.isSome()): - wsExtAddress = some(ip4TcpEndPoint(extIp.get(), wsBindPort) & wsFlag(wssEnabled)) - - 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(): - announcedAddresses.add(wsHostAddress.get()) - - let - # 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")) - - return NetConfig( - hostAddress: hostAddress, - wsHostAddress: wsHostAddress, - hostExtAddress: hostExtAddress, - wsExtAddress: wsExtAddress, - extIp: extIp, - extPort: extPort, - wssEnabled: wssEnabled, - dns4DomainName: dns4DomainName, - announcedAddresses: announcedAddresses, - extMultiAddrs: extMultiAddrs, - enrMultiaddrs: enrMultiaddrs, - enrIp: enrIp, - enrPort: enrPort, - discv5UdpPort: discv5UdpPort, - bindIp: bindIp, - bindPort: bindPort, - wakuFlags: wakuFlags) - - proc getEnr*(netConfig: NetConfig, wakuDiscV5 = none(WakuDiscoveryV5), nodeKey: crypto.PrivateKey): Result[enr.Record, string] = @@ -247,7 +127,7 @@ proc getEnr*(netConfig: NetConfig, return ok(recordRes.get()) -proc getAutonatService*(rng = crypto.newRng()): AutonatService = +proc getAutonatService*(rng: ref HmacDrbgContext): AutonatService = ## AutonatService request other peers to dial us back ## flagging us as Reachable or NotReachable. ## minConfidence is used as threshold to determine the state. @@ -296,7 +176,7 @@ proc new*(T: type WakuNode, # TODO: make this argument required after tests are updated rng: ref HmacDrbgContext = crypto.newRng() ): T {.raises: [Defect, LPError, IOError, TLSStreamProtocolError], deprecated: "Use NetConfig variant".} = - let netConfig = NetConfig.init( + let netConfigRes = NetConfig.init( bindIp = bindIp, bindPort = bindPort, extIp = extIp, @@ -309,10 +189,12 @@ proc new*(T: type WakuNode, dns4DomainName = dns4DomainName, discv5UdpPort = discv5UdpPort, ) + if netConfigRes.isErr(): + raise newException(Defect, "invalid node configuration: " & $netConfigRes.error) return WakuNode.new( nodeKey = nodeKey, - netConfig = netConfig, + netConfig = netConfigRes.get(), peerStorage = peerStorage, maxConnections = maxConnections, secureKey = secureKey, @@ -379,6 +261,7 @@ proc peerInfo*(node: WakuNode): PeerInfo = proc peerId*(node: WakuNode): PeerId = node.peerInfo.peerId +# TODO: Move to application instance (e.g., `WakuNode2`) # TODO: Extend with more relevant info: topics, peers, memory usage, online time, etc proc info*(node: WakuNode): WakuInfo = ## Returns information about the Node, such as what multiaddress it can be reached at. @@ -875,6 +758,7 @@ proc lightpushPublish*(node: WakuNode, pubsubTopic: PubsubTopic, message: WakuMe error "failed to publish message", error=publishRes.error + ## Waku RLN Relay when defined(rln): proc mountRlnRelay*(node: WakuNode, diff --git a/waku/v2/node/wakuswitch.nim b/waku/v2/node/waku_switch.nim similarity index 100% rename from waku/v2/node/wakuswitch.nim rename to waku/v2/node/waku_switch.nim diff --git a/waku/v2/waku_node.nim b/waku/v2/waku_node.nim new file mode 100644 index 000000000..66cff4521 --- /dev/null +++ b/waku/v2/waku_node.nim @@ -0,0 +1,9 @@ +import + ./node/config, + ./node/waku_switch as switch, + ./node/waku_node as node + +export + config, + switch, + node