From 887a5c76b2cf8cf3e6c9cd5cd2ac9904fa643604 Mon Sep 17 00:00:00 2001 From: Simon-Pierre Vivier Date: Thu, 22 Jun 2023 16:58:14 -0400 Subject: [PATCH] chore: discv5 re-org setup (#1815) Key, netconfig, enr are created at App initialization and config files has been renamed. --- apps/wakunode2/app.nim | 127 +++++++----------- .../{config.nim => external_config.nim} | 3 +- apps/wakunode2/internal_config.nim | 107 +++++++++++++++ apps/wakunode2/wakunode2.nim | 2 +- waku/v2/node/builder.nim | 5 + waku/v2/node/config.nim | 2 + waku/v2/node/waku_node.nim | 15 ++- waku/v2/waku_discv5.nim | 21 --- waku/v2/waku_enr/sharding.nim | 20 +++ 9 files changed, 198 insertions(+), 104 deletions(-) rename apps/wakunode2/{config.nim => external_config.nim} (99%) create mode 100644 apps/wakunode2/internal_config.nim diff --git a/apps/wakunode2/app.nim b/apps/wakunode2/app.nim index ce33b15de..bbc46a7cf 100644 --- a/apps/wakunode2/app.nim +++ b/apps/wakunode2/app.nim @@ -41,7 +41,8 @@ import ../../waku/v2/waku_lightpush, ../../waku/v2/waku_filter, ./wakunode2_validator_signed, - ./config + ./internal_config, + ./external_config import ../../waku/v2/node/message_cache, ../../waku/v2/node/rest/server, @@ -69,8 +70,11 @@ type App* = object version: string conf: WakuNodeConf - + netConf: NetConfig rng: ref HmacDrbgContext + key: crypto.PrivateKey + record: Record + peerStore: Option[WakuPeerStorage] archiveDriver: Option[ArchiveDriver] archiveRetentionPolicy: Option[RetentionPolicy] @@ -95,7 +99,41 @@ func version*(app: App): string = ## Initialisation proc init*(T: type App, rng: ref HmacDrbgContext, conf: WakuNodeConf): T = - App(version: git_version, conf: conf, rng: rng, node: nil) + let key = + if conf.nodeKey.isSome(): + conf.nodeKey.get() + else: + let keyRes = crypto.PrivateKey.random(Secp256k1, rng[]) + + if keyRes.isErr(): + error "failed to generate key", error=keyRes.error + quit(QuitFailure) + + keyRes.get() + + let netConfigRes = networkConfiguration(conf) + let netConfig = + if netConfigRes.isErr(): + error "failed to create internal config", error=netConfigRes.error + quit(QuitFailure) + else: netConfigRes.get() + + let recordRes = createRecord(conf, netConfig, key) + let record = + if recordRes.isErr(): + error "failed to create record", error=recordRes.error + quit(QuitFailure) + else: recordRes.get() + + App( + version: git_version, + conf: conf, + netConf: netConfig, + rng: rng, + key: key, + record: record, + node: nil + ) ## SQLite database @@ -318,7 +356,10 @@ proc setupDyamicBootstrapNodes*(app: var App): AppResult[void] = ## Init waku node instance proc initNode(conf: WakuNodeConf, + netConfig: NetConfig, rng: ref HmacDrbgContext, + nodeKey: crypto.PrivateKey, + record: enr.Record, peerStore: Option[WakuPeerStorage], dynamicBootstrapNodes: openArray[RemotePeerInfo] = @[]): AppResult[WakuNode] = @@ -335,83 +376,11 @@ proc initNode(conf: WakuNodeConf, dnsResolver = DnsResolver.new(nameServers) - let - nodekey = if conf.nodekey.isSome(): - conf.nodekey.get() - else: - let nodekeyRes = crypto.PrivateKey.random(Secp256k1, rng[]) - if nodekeyRes.isErr(): - return err("failed to generate nodekey: " & $nodekeyRes.error) - nodekeyRes.get() - - - ## `udpPort` is only supplied to satisfy underlying APIs but is not - ## actually a supported transport for libp2p traffic. - let udpPort = conf.tcpPort - let natRes = setupNat(conf.nat, clientId, - Port(uint16(conf.tcpPort) + conf.portsShift), - Port(uint16(udpPort) + conf.portsShift)) - if natRes.isErr(): - return err("failed to setup NAT: " & $natRes.error) - - let (extIp, extTcpPort, _) = natRes.get() - - let - dns4DomainName = if conf.dns4DomainName != "": some(conf.dns4DomainName) - else: none(string) - - discv5UdpPort = if conf.discv5Discovery: some(Port(uint16(conf.discv5UdpPort) + conf.portsShift)) - else: none(Port) - - ## TODO: the NAT setup assumes a manual port mapping configuration if extIp config is set. This probably - ## implies adding manual config item for extPort as well. The following heuristic assumes that, in absence of manual - ## config, the external port is the same as the bind port. - extPort = if (extIp.isSome() or dns4DomainName.isSome()) and extTcpPort.isNone(): - some(Port(uint16(conf.tcpPort) + conf.portsShift)) - else: - extTcpPort - extMultiAddrs = if (conf.extMultiAddrs.len > 0): - let extMultiAddrsValidationRes = validateExtMultiAddrs(conf.extMultiAddrs) - if extMultiAddrsValidationRes.isErr(): - return err("invalid external multiaddress: " & extMultiAddrsValidationRes.error) - else: - extMultiAddrsValidationRes.get() - else: - @[] - - wakuFlags = CapabilitiesBitfield.init( - lightpush = conf.lightpush, - filter = conf.filter, - store = conf.store, - relay = conf.relay - ) - var node: WakuNode let pStorage = if peerStore.isNone(): nil else: peerStore.get() - let rng = crypto.newRng() - # 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 - let netConfigRes = NetConfig.init( - bindIp = conf.listenAddress, - bindPort = Port(uint16(conf.tcpPort) + conf.portsShift), - extIp = extIp, - extPort = extPort, - extMultiAddrs = extMultiAddrs, - wsBindPort = Port(uint16(conf.websocketPort) + conf.portsShift), - wsEnabled = conf.websocketSupport, - wssEnabled = conf.websocketSecureSupport, - dns4DomainName = dns4DomainName, - discv5UdpPort = discv5UdpPort, - wakuFlags = some(wakuFlags), - ) - if netConfigRes.isErr(): - return err("failed to create net config instance: " & netConfigRes.error) - - let netConfig = netConfigRes.get() var wakuDiscv5 = none(WakuDiscoveryV5) if conf.discv5Discovery: @@ -449,6 +418,7 @@ proc initNode(conf: WakuNodeConf, var builder = WakuNodeBuilder.init() builder.withRng(rng) builder.withNodeKey(nodekey) + builder.withRecord(record) builder.withNetworkConfiguration(netConfig) builder.withPeerStorage(pStorage, capacity = conf.peerStoreCapacity) builder.withSwitchConfiguration( @@ -467,7 +437,7 @@ proc initNode(conf: WakuNodeConf, proc setupWakuNode*(app: var App): AppResult[void] = ## Waku node - let initNodeRes = initNode(app.conf, app.rng, app.peerStore, app.dynamicBootstrapNodes) + let initNodeRes = initNode(app.conf, app.netConf, app.rng, app.key, app.record, app.peerStore, app.dynamicBootstrapNodes) if initNodeRes.isErr(): return err("failed to init node: " & initNodeRes.error) @@ -477,7 +447,9 @@ proc setupWakuNode*(app: var App): AppResult[void] = ## Mount protocols -proc setupProtocols(node: WakuNode, conf: WakuNodeConf, +proc setupProtocols(node: WakuNode, + conf: WakuNodeConf, + nodeKey: crypto.PrivateKey, archiveDriver: Option[ArchiveDriver], archiveRetentionPolicy: Option[RetentionPolicy]): Future[AppResult[void]] {.async.} = ## Setup configured protocols on an existing Waku v2 node. @@ -626,6 +598,7 @@ proc setupAndMountProtocols*(app: App): Future[AppResult[void]] {.async.} = return await setupProtocols( app.node, app.conf, + app.key, app.archiveDriver, app.archiveRetentionPolicy ) diff --git a/apps/wakunode2/config.nim b/apps/wakunode2/external_config.nim similarity index 99% rename from apps/wakunode2/config.nim rename to apps/wakunode2/external_config.nim index e04fcce29..5ccfd76c4 100644 --- a/apps/wakunode2/config.nim +++ b/apps/wakunode2/external_config.nim @@ -16,7 +16,8 @@ import import ../../waku/common/confutils/envvar/defs as confEnvvarDefs, ../../waku/common/confutils/envvar/std/net as confEnvvarNet, - ../../waku/common/logging + ../../waku/common/logging, + ../../waku/v2/waku_enr export confTomlDefs, diff --git a/apps/wakunode2/internal_config.nim b/apps/wakunode2/internal_config.nim new file mode 100644 index 000000000..be1f9fd48 --- /dev/null +++ b/apps/wakunode2/internal_config.nim @@ -0,0 +1,107 @@ +import + stew/results, + libp2p/crypto/crypto +import + ../../waku/common/utils/nat, + ../../waku/v2/node/config, + ../../waku/v2/node/waku_node, + ../../waku/v2/waku_enr, + ./external_config + +proc networkConfiguration*(conf: WakuNodeConf): NetConfigResult = + + ## `udpPort` is only supplied to satisfy underlying APIs but is not + ## actually a supported transport for libp2p traffic. + let udpPort = conf.tcpPort + let natRes = setupNat(conf.nat, clientId, + Port(uint16(conf.tcpPort) + conf.portsShift), + Port(uint16(udpPort) + conf.portsShift)) + if natRes.isErr(): + return err("failed to setup NAT: " & $natRes.error) + + + let (extIp, extTcpPort, _) = natRes.get() + + let + dns4DomainName = if conf.dns4DomainName != "": some(conf.dns4DomainName) + else: none(string) + + discv5UdpPort = if conf.discv5Discovery: some(Port(uint16(conf.discv5UdpPort) + conf.portsShift)) + else: none(Port) + + ## TODO: the NAT setup assumes a manual port mapping configuration if extIp config is set. This probably + ## implies adding manual config item for extPort as well. The following heuristic assumes that, in absence of manual + ## config, the external port is the same as the bind port. + extPort = if (extIp.isSome() or dns4DomainName.isSome()) and extTcpPort.isNone(): + some(Port(uint16(conf.tcpPort) + conf.portsShift)) + else: + extTcpPort + extMultiAddrs = if (conf.extMultiAddrs.len > 0): + let extMultiAddrsValidationRes = validateExtMultiAddrs(conf.extMultiAddrs) + if extMultiAddrsValidationRes.isErr(): + return err("invalid external multiaddress: " & $extMultiAddrsValidationRes.error) + + else: + extMultiAddrsValidationRes.get() + else: + @[] + + wakuFlags = CapabilitiesBitfield.init( + lightpush = conf.lightpush, + filter = conf.filter, + store = conf.store, + relay = conf.relay + ) + + # 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 + let netConfigRes = NetConfig.init( + bindIp = conf.listenAddress, + bindPort = Port(uint16(conf.tcpPort) + conf.portsShift), + extIp = extIp, + extPort = extPort, + extMultiAddrs = extMultiAddrs, + wsBindPort = Port(uint16(conf.websocketPort) + conf.portsShift), + wsEnabled = conf.websocketSupport, + wssEnabled = conf.websocketSecureSupport, + dns4DomainName = dns4DomainName, + discv5UdpPort = discv5UdpPort, + wakuFlags = some(wakuFlags), + ) + + netConfigRes + +proc createRecord*(conf: WakuNodeConf, netConf: NetConfig, key: crypto.PrivateKey): Result[enr.Record, string] = + let relayShardsRes = topicsToRelayShards(conf.topics) + + let relayShardOp = + if relayShardsRes.isErr(): + return err("building ENR with relay sharding failed: " & $relayShardsRes.error) + else: relayShardsRes.get() + + var builder = EnrBuilder.init(key) + + builder.withIpAddressAndPorts( + ipAddr = netConf.extIp, + tcpPort = netConf.extPort, + udpPort = netConf.discv5UdpPort, + ) + + if netConf.wakuFlags.isSome(): + builder.withWakuCapabilities(netConf.wakuFlags.get()) + + builder.withMultiaddrs(netConf.enrMultiaddrs) + + if relayShardOp.isSome(): + let res = builder.withWakuRelaySharding(relayShardOp.get()) + + if res.isErr(): + return err("building ENR with relay sharding failed: " & $res.error) + + let res = builder.build() + + if res.isErr(): + return err("building ENR failed: " & $res.error) + + ok(res.get()) diff --git a/apps/wakunode2/wakunode2.nim b/apps/wakunode2/wakunode2.nim index 767e97227..585dd1253 100644 --- a/apps/wakunode2/wakunode2.nim +++ b/apps/wakunode2/wakunode2.nim @@ -14,7 +14,7 @@ import libp2p/crypto/crypto import ../../waku/common/logging, - ./config, + ./external_config, ./app logScope: diff --git a/waku/v2/node/builder.nim b/waku/v2/node/builder.nim index baeaaee08..04461c2b4 100644 --- a/waku/v2/node/builder.nim +++ b/waku/v2/node/builder.nim @@ -25,6 +25,7 @@ type nodeRng: Option[ref crypto.HmacDrbgContext] nodeKey: Option[crypto.PrivateKey] netConfig: Option[NetConfig] + record: Option[enr.Record] # Peer storage and peer manager peerStorage: Option[PeerStorage] @@ -59,6 +60,9 @@ proc withRng*(builder: var WakuNodeBuilder, rng: ref crypto.HmacDrbgContext) = proc withNodeKey*(builder: var WakuNodeBuilder, nodeKey: crypto.PrivateKey) = builder.nodeKey = some(nodeKey) +proc withRecord*(builder: var WakuNodeBuilder, record: enr.Record) = + builder.record = some(record) + proc withNetworkConfiguration*(builder: var WakuNodeBuilder, config: NetConfig) = builder.netConfig = some(config) @@ -151,6 +155,7 @@ proc build*(builder: WakuNodeBuilder): Result[WakuNode, string] = rng = rng, nodeKey = builder.nodeKey.get(), netConfig = builder.netConfig.get(), + enr = builder.record, peerStorage = builder.peerStorage.get(nil), peerStoreCapacity = builder.peerStorageCapacity, maxConnections = builder.switchMaxConnections.get(builders.MaxConnections), diff --git a/waku/v2/node/config.nim b/waku/v2/node/config.nim index 5763e38c7..f38d5b3ea 100644 --- a/waku/v2/node/config.nim +++ b/waku/v2/node/config.nim @@ -8,6 +8,8 @@ import stew/results, stew/shims/net, libp2p/multiaddress +import + ../../waku/v2/waku_core/peers import ../waku_enr diff --git a/waku/v2/node/waku_node.nim b/waku/v2/node/waku_node.nim index 8a663ca14..04d2d988a 100644 --- a/waku/v2/node/waku_node.nim +++ b/waku/v2/node/waku_node.nim @@ -158,6 +158,7 @@ proc getAutonatService*(rng: ref HmacDrbgContext): AutonatService = proc new*(T: type WakuNode, nodeKey: crypto.PrivateKey, netConfig: NetConfig, + enr: Option[enr.Record], peerStorage: PeerStorage = nil, maxConnections = builders.MaxConnections, secureKey: string = "", @@ -191,15 +192,21 @@ proc new*(T: type WakuNode, services = @[Service(getAutonatService(rng))], ) - let nodeEnrRes = getEnr(netConfig, wakuDiscv5, nodekey) - if nodeEnrRes.isErr(): - raise newException(Defect, "failed to generate the node ENR record: " & $nodeEnrRes.error) + let enr = + if enr.isNone(): + let nodeEnrRes = getEnr(netConfig, wakuDiscv5, nodekey) + + if nodeEnrRes.isErr(): + raise newException(Defect, "failed to generate the node ENR record: " & $nodeEnrRes.error) + + nodeEnrRes.get() + else: enr.get() return WakuNode( peerManager: PeerManager.new(switch, peerStorage), switch: switch, rng: rng, - enr: nodeEnrRes.get(), + enr: enr, announcedAddresses: netConfig.announcedAddresses, wakuDiscv5: if wakuDiscV5.isSome(): wakuDiscV5.get() else: nil, ) diff --git a/waku/v2/waku_discv5.nim b/waku/v2/waku_discv5.nim index 7f5637f12..8e8b94137 100644 --- a/waku/v2/waku_discv5.nim +++ b/waku/v2/waku_discv5.nim @@ -48,27 +48,6 @@ type WakuDiscoveryV5* = ref object protocol*: protocol.Protocol listening*: bool -func topicsToRelayShards*(topics: seq[string]): Result[Option[RelayShards], string] = - if topics.len < 1: - return ok(none(RelayShards)) - - let parsedTopicsRes = topics.mapIt(NsPubsubTopic.parse(it)) - - for res in parsedTopicsRes: - if res.isErr(): - return err("failed to parse topic: " & $res.error) - - if parsedTopicsRes.allIt(it.get().kind == NsPubsubTopicKind.NamedSharding): - return ok(none(RelayShards)) - - if parsedTopicsRes.anyIt(it.get().kind == NsPubsubTopicKind.NamedSharding): - return err("use named topics OR sharded ones not both.") - - if parsedTopicsRes.anyIt(it.get().cluster != parsedTopicsRes[0].get().cluster): - return err("use sharded topics within the same cluster.") - - return ok(some(RelayShards.init(parsedTopicsRes[0].get().cluster, parsedTopicsRes.mapIt(it.get().shard)))) - proc new*(T: type WakuDiscoveryV5, rng: ref HmacDrbgContext, conf: WakuDiscoveryV5Config, record: Option[waku_enr.Record]): T = let protocol = newProtocol( rng = rng, diff --git a/waku/v2/waku_enr/sharding.nim b/waku/v2/waku_enr/sharding.nim index 672907576..78a4363a6 100644 --- a/waku/v2/waku_enr/sharding.nim +++ b/waku/v2/waku_enr/sharding.nim @@ -68,6 +68,26 @@ func init*(T: type RelayShards, cluster: uint16, indices: seq[uint16]): T = RelayShards(cluster: cluster, indices: indicesSeq) +func topicsToRelayShards*(topics: seq[string]): Result[Option[RelayShards], string] = + if topics.len < 1: + return ok(none(RelayShards)) + + let parsedTopicsRes = topics.mapIt(NsPubsubTopic.parse(it)) + + for res in parsedTopicsRes: + if res.isErr(): + return err("failed to parse topic: " & $res.error) + + if parsedTopicsRes.allIt(it.get().kind == NsPubsubTopicKind.NamedSharding): + return ok(none(RelayShards)) + + if parsedTopicsRes.anyIt(it.get().kind == NsPubsubTopicKind.NamedSharding): + return err("use named topics OR sharded ones not both.") + + if parsedTopicsRes.anyIt(it.get().cluster != parsedTopicsRes[0].get().cluster): + return err("use sharded topics within the same cluster.") + + return ok(some(RelayShards.init(parsedTopicsRes[0].get().cluster, parsedTopicsRes.mapIt(it.get().shard)))) func contains*(rs: RelayShards, cluster, index: uint16): bool = rs.cluster == cluster and rs.indices.contains(index)