From e931fa5dc8165e810068e85d9698bcf627cb3bff Mon Sep 17 00:00:00 2001 From: Lorenzo Delgado Date: Wed, 5 Apr 2023 14:27:11 +0200 Subject: [PATCH] feat(node): added waku node builder type --- apps/chat2/chat2.nim | 17 +- apps/chat2bridge/chat2bridge.nim | 9 +- apps/wakubridge/wakubridge.nim | 11 +- apps/wakunode2/wakunode2.nim | 33 ++-- examples/v2/publisher.nim | 6 +- examples/v2/subscriber.nim | 6 +- tools/networkmonitor/networkmonitor.nim | 6 +- tools/networkmonitor/networkmonitor_utils.nim | 2 +- tools/wakucanary/wakucanary.nim | 13 +- waku/v2/node/builder.nim | 167 ++++++++++++++++++ waku/v2/utils/peers.nim | 2 +- waku/v2/waku_node.nim | 2 + 12 files changed, 234 insertions(+), 40 deletions(-) create mode 100644 waku/v2/node/builder.nim diff --git a/apps/chat2/chat2.nim b/apps/chat2/chat2.nim index 1ca40ecee..75ac5f963 100644 --- a/apps/chat2/chat2.nim +++ b/apps/chat2/chat2.nim @@ -390,12 +390,17 @@ proc processInput(rfd: AsyncFD, rng: ref HmacDrbgContext) {.async.} = (extIp, extTcpPort, extUdpPort) = setupNat(conf.nat, clientId, Port(uint16(conf.tcpPort) + conf.portsShift), Port(uint16(conf.udpPort) + conf.portsShift)) - node = WakuNode.new(nodekey, conf.listenAddress, - Port(uint16(conf.tcpPort) + conf.portsShift), - extIp, extTcpPort, - wsBindPort = Port(uint16(conf.websocketPort) + conf.portsShift), - wsEnabled = conf.websocketSupport, - wssEnabled = conf.websocketSecureSupport) + + let node = block: + var builder = WakuNodeBuilder.init() + builder.withNodeKey(nodeKey) + builder.withNetworkConfigurationDetails(conf.listenAddress, Port(uint16(conf.tcpPort) + conf.portsShift), + extIp, extTcpPort, + wsBindPort = Port(uint16(conf.websocketPort) + conf.portsShift), + wsEnabled = conf.websocketSupport, + wssEnabled = conf.websocketSecureSupport).tryGet() + builder.build().tryGet() + await node.start() if conf.rlnRelayEthAccountPrivateKey == "" and conf.rlnRelayCredPath == "": diff --git a/apps/chat2bridge/chat2bridge.nim b/apps/chat2bridge/chat2bridge.nim index e7c24e876..ce1be3f28 100644 --- a/apps/chat2bridge/chat2bridge.nim +++ b/apps/chat2bridge/chat2bridge.nim @@ -163,10 +163,11 @@ proc new*(T: type Chat2MatterBridge, raise newException(ValueError, "Matterbridge client not reachable/healthy") # Setup Waku v2 node - let - nodev2 = WakuNode.new(nodev2Key, - nodev2BindIp, nodev2BindPort, - nodev2ExtIp, nodev2ExtPort) + let nodev2 = block: + var builder = WakuNodeBuilder.init() + builder.withNodeKey(nodev2Key) + builder.withNetworkConfigurationDetails(nodev2BindIp, nodev2BindPort, nodev2ExtIp, nodev2ExtPort).tryGet() + builder.build().tryGet() return Chat2MatterBridge(mbClient: mbClient, nodev2: nodev2, diff --git a/apps/wakubridge/wakubridge.nim b/apps/wakubridge/wakubridge.nim index 61255fc4b..f9736dd8a 100644 --- a/apps/wakubridge/wakubridge.nim +++ b/apps/wakubridge/wakubridge.nim @@ -230,11 +230,12 @@ proc new*(T: type WakuBridge, nodev1.configureWaku(wakuConfig) # Setup Waku v2 node - let - nodev2 = WakuNode.new(nodev2Key, - nodev2BindIp, nodev2BindPort, - nodev2ExtIp, nodev2ExtPort, - nameResolver = nameResolver) + let nodev2 = block: + var builder = WakuNodeBuilder.init() + builder.withNodeKey(nodev2Key) + builder.withNetworkConfigurationDetails(nodev2BindIp, nodev2BindPort, nodev2ExtIp, nodev2ExtPort).tryGet() + builder.withSwitchConfiguration(nameResolver=nameResolver) + builder.build().tryGet() return WakuBridge(nodev1: nodev1, nodev2: nodev2, diff --git a/apps/wakunode2/wakunode2.nim b/apps/wakunode2/wakunode2.nim index 63860e837..6a4917aa6 100644 --- a/apps/wakunode2/wakunode2.nim +++ b/apps/wakunode2/wakunode2.nim @@ -334,21 +334,24 @@ proc initNode(conf: WakuNodeConf, )) except CatchableError: 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 CatchableError: - return err("failed to create waku node instance: " & getCurrentExceptionMsg()) + + # Build waku node instance + var builder = WakuNodeBuilder.init() + builder.withRng(rng) + builder.withNodeKey(nodekey) + builder.withNetworkConfiguration(netConfig) + builder.withPeerStorage(pStorage, capacity = conf.peerStoreCapacity) + builder.withSwitchConfiguration( + maxConnections = some(conf.maxConnections.int), + secureKey = some(conf.websocketSecureKeyPath), + secureCert = some(conf.websocketSecureCertPath), + nameResolver = dnsResolver, + sendSignedPeerRecord = conf.relayPeerExchange, # We send our own signed peer record when peer exchange enabled + agentString = some(conf.agentString) + ) + builder.withWakuDiscv5(wakuDiscv5.get(nil)) + + node = ? builder.build().mapErr(proc (err: string): string = "failed to create waku node instance: " & err) ok(node) diff --git a/examples/v2/publisher.nim b/examples/v2/publisher.nim index cefde1208..e19b46965 100644 --- a/examples/v2/publisher.nim +++ b/examples/v2/publisher.nim @@ -42,9 +42,13 @@ proc setupAndPublish(rng: ref HmacDrbgContext) {.async.} = let nodeKey = crypto.PrivateKey.random(Secp256k1, rng[]).get() ip = ValidIpAddress.init("0.0.0.0") - node = WakuNode.new(nodeKey, ip, Port(wakuPort)) flags = CapabilitiesBitfield.init(lightpush = false, filter = false, store = false, relay = true) + var builder = WakuNodeBuilder.init() + builder.withNodeKey(nodeKey) + builder.withNetworkConfigurationDetails(ip, Port(wakuPort)).tryGet() + let node = builder.build().tryGet() + var bootstrapNodeEnr: enr.Record discard bootstrapNodeEnr.fromURI(bootstrapNode) diff --git a/examples/v2/subscriber.nim b/examples/v2/subscriber.nim index 21dbd1268..f9b135bdb 100644 --- a/examples/v2/subscriber.nim +++ b/examples/v2/subscriber.nim @@ -36,9 +36,13 @@ proc setupAndSubscribe(rng: ref HmacDrbgContext) {.async.} = let nodeKey = crypto.PrivateKey.random(Secp256k1, rng[])[] ip = ValidIpAddress.init("0.0.0.0") - node = WakuNode.new(nodeKey, ip, Port(wakuPort)) flags = CapabilitiesBitfield.init(lightpush = false, filter = false, store = false, relay = true) + var builder = WakuNodeBuilder.init() + builder.withNodeKey(nodeKey) + builder.withNetworkConfigurationDetails(ip, Port(wakuPort)).tryGet() + let node = builder.build().tryGet() + var bootstrapNodeEnr: enr.Record discard bootstrapNodeEnr.fromURI(bootstrapNode) diff --git a/tools/networkmonitor/networkmonitor.nim b/tools/networkmonitor/networkmonitor.nim index 67f4804ed..a1cd3e440 100644 --- a/tools/networkmonitor/networkmonitor.nim +++ b/tools/networkmonitor/networkmonitor.nim @@ -230,7 +230,11 @@ proc initAndStartNode(conf: NetworkMonitorConf): Result[WakuNode, string] = let bindIp = ValidIpAddress.init("0.0.0.0") extIp = ValidIpAddress.init("127.0.0.1") - node = WakuNode.new(nodeKey, bindIp, nodeTcpPort) + + var builder = WakuNodeBuilder.init() + builder.withNodeKey(nodeKey) + ? builder.withNetworkConfigurationDetails(bindIp, nodeTcpPort) + let node = ? builder.build() var discv5BootstrapEnrsRes = getBootstrapFromDiscDns(conf) if not discv5BootstrapEnrsRes.isOk(): diff --git a/tools/networkmonitor/networkmonitor_utils.nim b/tools/networkmonitor/networkmonitor_utils.nim index 30e51ac09..27d9fbc21 100644 --- a/tools/networkmonitor/networkmonitor_utils.nim +++ b/tools/networkmonitor/networkmonitor_utils.nim @@ -44,7 +44,7 @@ proc decodeBytes*(t: typedesc[NodeLocation], value: openArray[byte], long: $jsonContent["lon"].getFloat(), isp: jsonContent["isp"].getStr() )) - except CatchableError: + except Exception: return err("failed to get the location: " & getCurrentExceptionMsg()) proc encodeString*(value: string): RestResult[string] = diff --git a/tools/wakucanary/wakucanary.nim b/tools/wakucanary/wakucanary.nim index 6a1366612..1d9608cb4 100644 --- a/tools/wakucanary/wakucanary.nim +++ b/tools/wakucanary/wakucanary.nim @@ -111,11 +111,14 @@ proc main(rng: ref HmacDrbgContext): Future[int] {.async.} = let peer: RemotePeerInfo = parseRemotePeerInfo(conf.address) nodeKey = crypto.PrivateKey.random(Secp256k1, rng[])[] - node = WakuNode.new( - nodeKey, - ValidIpAddress.init("0.0.0.0"), - Port(conf.nodePort), - nameResolver = resolver) + bindIp = ValidIpAddress.init("0.0.0.0") + nodeTcpPort = Port(conf.nodePort) + + var builder = WakuNodeBuilder.init() + builder.withNodeKey(nodeKey) + builder.withNetworkConfigurationDetails(bindIp, nodeTcpPort).tryGet() + builder.withSwitchConfiguration(nameResolver=resolver) + let node = builder.build().tryGet() await node.start() diff --git a/waku/v2/node/builder.nim b/waku/v2/node/builder.nim new file mode 100644 index 000000000..5c7308cc2 --- /dev/null +++ b/waku/v2/node/builder.nim @@ -0,0 +1,167 @@ +when (NimMajor, NimMinor) < (1, 4): + {.push raises: [Defect].} +else: + {.push raises: [].} + +import + std/options, + stew/results, + stew/shims/net, + chronicles, + libp2p/crypto/crypto, + libp2p/builders, + libp2p/nameresolving/nameresolver +import + ../protocol/waku_enr, + ../protocol/waku_discv5, + ./config, + ./peer_manager, + ./waku_node + + +type + WakuNodeBuilder* = object + # General + nodeRng: Option[ref crypto.HmacDrbgContext] + nodeKey: Option[crypto.PrivateKey] + netConfig: Option[NetConfig] + + # Peer storage and peer manager + peerStorage: Option[PeerStorage] + peerStorageCapacity: Option[int] + peerManager: Option[PeerManager] + + # Libp2p switch + switchMaxConnections: Option[int] + switchNameResolver: Option[NameResolver] + switchAgentString: Option[string] + switchSslSecureKey: Option[string] + switchSslSecureCert: Option[string] + switchSendSignedPeerRecord: Option[bool] + + # Waku discv5 + wakuDiscv5: Option[WakuDiscoveryV5] + + WakuNodeBuilderResult* = Result[void, string] + + +## Init + +proc init*(T: type WakuNodeBuilder): WakuNodeBuilder = + WakuNodeBuilder() + + +## General + +proc withRng*(builder: var WakuNodeBuilder, rng: ref crypto.HmacDrbgContext) = + builder.nodeRng = some(rng) + +proc withNodeKey*(builder: var WakuNodeBuilder, nodeKey: crypto.PrivateKey) = + builder.nodeKey = some(nodeKey) + +proc withNetworkConfiguration*(builder: var WakuNodeBuilder, config: NetConfig) = + builder.netConfig = some(config) + +proc withNetworkConfigurationDetails*(builder: var WakuNodeBuilder, + bindIp: ValidIpAddress, + bindPort: Port, + extIp = none(ValidIpAddress), + extPort = none(Port), + extMultiAddrs = newSeq[MultiAddress](), + wsBindPort: Port = Port(8000), + wsEnabled: bool = false, + wssEnabled: bool = false, + wakuFlags = none(CapabilitiesBitfield), + dns4DomainName = none(string), + discv5UdpPort = none(Port)): WakuNodeBuilderResult {. + deprecated: "use 'builder.withNetworkConfiguration()' instead".} = + let netConfig = ? NetConfig.init( + bindIp = bindIp, + bindPort = bindPort, + extIp = extIp, + extPort = extPort, + extMultiAddrs = extMultiAddrs, + wsBindPort = wsBindPort, + wsEnabled = wsEnabled, + wssEnabled = wssEnabled, + wakuFlags = wakuFlags, + dns4DomainName = dns4DomainName, + discv5UdpPort = discv5UdpPort, + ) + builder.withNetworkConfiguration(netConfig) + ok() + + +## Peer storage and peer manager + +proc withPeerStorage*(builder: var WakuNodeBuilder, peerStorage: PeerStorage, capacity = none(int)) = + if not peerStorage.isNil(): + builder.peerStorage = some(peerStorage) + + builder.peerStorageCapacity = capacity + +proc withPeerManager*(builder: var WakuNodeBuilder, peerManager: PeerManager) = + builder.peerManager = some(peerManager) + + +## Waku switch + +proc withSwitchConfiguration*(builder: var WakuNodeBuilder, + maxConnections = none(int), + nameResolver: NameResolver = nil, + sendSignedPeerRecord = false, + secureKey = none(string), + secureCert = none(string), + agentString = none(string)) = + builder.switchMaxConnections = maxConnections + builder.switchSendSignedPeerRecord = some(sendSignedPeerRecord) + builder.switchSslSecureKey = secureKey + builder.switchSslSecureCert = secureCert + builder.switchAgentString = agentString + + if not nameResolver.isNil(): + builder.switchNameResolver = some(nameResolver) + + +## Waku discv5 + +proc withWakuDiscv5*(builder: var WakuNodeBuilder, instance: WakuDiscoveryV5) = + if not instance.isNil(): + builder.wakuDiscv5 = some(instance) + + +## Build + +proc build*(builder: WakuNodeBuilder): Result[WakuNode, string] = + var rng: ref crypto.HmacDrbgContext + if builder.nodeRng.isNone(): + rng = crypto.newRng() + else: + rng = builder.nodeRng.get() + + if builder.nodeKey.isNone(): + return err("node key is required") + + if builder.netConfig.isNone(): + return err("network configuration is required") + + var node: WakuNode + try: + node = WakuNode.new( + rng = rng, + nodeKey = builder.nodeKey.get(), + netConfig = builder.netConfig.get(), + peerStorage = builder.peerStorage.get(nil), + peerStoreCapacity = builder.peerStorageCapacity, + maxConnections = builder.switchMaxConnections.get(builders.MaxConnections), + nameResolver = builder.switchNameResolver.get(nil), + agentString = builder.switchAgentString, + secureKey = builder.switchSslSecureKey.get(""), + secureCert = builder.switchSslSecureCert.get(""), + sendSignedPeerRecord = builder.switchSendSignedPeerRecord.get(false), + wakuDiscv5 = builder.wakuDiscv5, + ) + except Exception: + return err("failed to build WakuNode instance: " & getCurrentExceptionMsg()) + + ok(node) diff --git a/waku/v2/utils/peers.nim b/waku/v2/utils/peers.nim index ac4ab5259..a009a000f 100644 --- a/waku/v2/utils/peers.nim +++ b/waku/v2/utils/peers.nim @@ -5,7 +5,7 @@ else: # Collection of utilities related to Waku peers import - std/[options, sequtils, strutils, times], + std/[options, sequtils, strutils], chronos, stew/results, stew/shims/net, diff --git a/waku/v2/waku_node.nim b/waku/v2/waku_node.nim index 66cff4521..32dbfb174 100644 --- a/waku/v2/waku_node.nim +++ b/waku/v2/waku_node.nim @@ -1,9 +1,11 @@ import ./node/config, + ./node/builder, ./node/waku_switch as switch, ./node/waku_node as node export config, + builder, switch, node