diff --git a/tests/all_tests_waku.nim b/tests/all_tests_waku.nim index 658adfa25..c0ed64327 100644 --- a/tests/all_tests_waku.nim +++ b/tests/all_tests_waku.nim @@ -30,7 +30,7 @@ import const os* {.strdefine.} = "" when os == "Linux" and # GitHub only supports container actions on Linux - # and we need to start a postgress database in a docker container + # and we need to start a postgres database in a docker container defined(postgres): import ./waku_archive/test_driver_postgres_query, @@ -102,3 +102,4 @@ import import ./waku_rln_relay/test_all # Node Factory +import ./factory/test_config diff --git a/tests/factory/test_config.nim b/tests/factory/test_config.nim new file mode 100644 index 000000000..c2938721a --- /dev/null +++ b/tests/factory/test_config.nim @@ -0,0 +1,114 @@ +{.used.} + +import + std/options, + testutils/unittests, + chronos, + libp2p/crypto/[crypto, secp], + libp2p/multiaddress, + nimcrypto/utils, + secp256k1 +import + ../../waku/factory/external_config, + ../../waku/factory/internal_config, + ../../waku/factory/networks_config + +suite "Waku config - apply preset": + test "Default preset is TWN": + ## Setup + let twnConf = ClusterConf.TheWakuNetworkConf() + + ## Given + let preConfig = WakuNodeConf(cmd: noCommand, preset: "default") + + ## When + let res = applyPresetConfiguration(preConfig) + assert res.isOk(), $res.error + + ## Then + let conf = res.get() + assert conf.maxMessageSize == twnConf.maxMessageSize + assert conf.clusterId == twnConf.clusterId + assert conf.rlnRelay == twnConf.rlnRelay + assert conf.rlnRelayEthContractAddress == twnConf.rlnRelayEthContractAddress + assert conf.rlnRelayChainId == twnConf.rlnRelayChainId + assert conf.rlnRelayDynamic == twnConf.rlnRelayDynamic + assert conf.rlnRelayBandwidthThreshold == twnConf.rlnRelayBandwidthThreshold + assert conf.rlnEpochSizeSec == twnConf.rlnEpochSizeSec + assert conf.rlnRelayUserMessageLimit == twnConf.rlnRelayUserMessageLimit + assert conf.pubsubTopics == twnConf.pubsubTopics + assert conf.discv5Discovery == twnConf.discv5Discovery + assert conf.discv5BootstrapNodes == twnConf.discv5BootstrapNodes + + test "Subscribes to all valid shards in default network": + ## Setup + let twnConf = ClusterConf.TheWakuNetworkConf() + + ## Given + let shards: seq[uint16] = @[0, 1, 2, 3, 4, 5, 6, 7] + let preConfig = WakuNodeConf(cmd: noCommand, preset: "default", shards: shards) + + ## When + let res = applyPresetConfiguration(preConfig) + assert res.isOk(), $res.error + + ## Then + let conf = res.get() + assert conf.pubsubTopics == twnConf.pubsubTopics + + test "Subscribes to some valid shards in default network": + ## Setup + let twnConf = ClusterConf.TheWakuNetworkConf() + + ## Given + let shards: seq[uint16] = @[0, 4, 7] + let preConfig = WakuNodeConf(cmd: noCommand, preset: "default", shards: shards) + + ## When + let res = applyPresetConfiguration(preConfig) + assert res.isOk(), $res.error + + ## Then + let conf = res.get() + assert conf.pubsubTopics.len() == shards.len() + assert twnConf.pubsubTopics[0] in conf.pubsubTopics + assert twnConf.pubsubTopics[4] in conf.pubsubTopics + assert twnConf.pubsubTopics[7] in conf.pubsubTopics + + test "Subscribes to invalid shards in default network": + ## Setup + + ## Given + let shards: seq[uint16] = @[0, 4, 7, 10] + let preConfig = WakuNodeConf(cmd: noCommand, preset: "default", shards: shards) + + ## When + let res = applyPresetConfiguration(preConfig) + + ## Then + assert res.isErr(), "Invalid shard was accepted" + +suite "Waku config - node key": + test "Passed node key is used": + ## Setup + let nodekey = block: + let key = SkPrivateKey + .init( + utils.fromHex( + "0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff" + ) + ) + .tryGet() + crypto.PrivateKey(scheme: Secp256k1, skkey: key) + + ## Given + let preConfig = WakuNodeConf(cmd: noCommand, nodekey: some(nodekey)) + + ## When + let res = nodeKeyConfiguration(preConfig) + assert res.isOk(), $res.error + + ## Then + let resKey = res.get() + assert utils.toHex(resKey.getRawBytes().get()) == + utils.toHex(nodekey.getRawBytes().get()) diff --git a/waku/factory/external_config.nim b/waku/factory/external_config.nim index 2c13fda9c..2a1bc206a 100644 --- a/waku/factory/external_config.nim +++ b/waku/factory/external_config.nim @@ -72,7 +72,7 @@ type WakuNodeConf* = object .}: EthRpcUrl rlnRelayEthContractAddress* {. - desc: "Address of membership contract on an Ethereum testnet", + desc: "Address of membership contract on an Ethereum testnet. Part of presets.", defaultValue: "", name: "rln-relay-eth-contract-address" .}: string @@ -98,14 +98,14 @@ type WakuNodeConf* = object rlnRelayUserMessageLimit* {. desc: - "Set a user message limit for the rln membership registration. Must be a positive integer. Default is 1.", + "Set a user message limit for the rln membership registration. Must be a positive integer. Default is 1. Part of presets.", defaultValue: 1, name: "rln-relay-user-message-limit" .}: uint64 rlnEpochSizeSec* {. desc: - "Epoch size in seconds used to rate limit RLN memberships. Default is 1 second.", + "Epoch size in seconds used to rate limit RLN memberships. Default is 1 second. Part of presets.", defaultValue: 1, name: "rln-relay-epoch-sec" .}: uint64 @@ -134,9 +134,15 @@ type WakuNodeConf* = object .}: seq[ProtectedTopic] ## General node config + preset* {. + desc: "Network preset to use." & "Must be one of 'default', ''", + defaultValue: "", + name: "preset" + .}: string + clusterId* {. desc: - "Cluster id that the node is running in. Node in a different cluster id is disconnected.", + "Cluster id that the node is running in. Node in a different cluster id is disconnected. Part of presets.", defaultValue: 0, name: "cluster-id" .}: uint16 @@ -146,7 +152,7 @@ type WakuNodeConf* = object maxMessageSize* {. desc: - "Maximum message size. Accepted units: KiB, KB, and B. e.g. 1024KiB; 1500 B; etc.", + "Maximum message size. Accepted units: KiB, KB, and B. e.g. 1024KiB; 1500 B; etc. Part of presets.", defaultValue: DefaultMaxWakuMessageSizeStr, name: "max-msg-size" .}: string @@ -261,7 +267,9 @@ type WakuNodeConf* = object .}: seq[string] shards* {. - desc: "Shards index to subscribe to [0.." & $MaxShardIndex & "]. Argument may be repeated.", + desc: + "Shards index to subscribe to [0.." & $MaxShardIndex & + "]. Argument may be repeated.", defaultValue: @[ uint16(0), @@ -283,7 +291,7 @@ type WakuNodeConf* = object ## RLN Relay config rlnRelay* {. - desc: "Enable spam protection through rln-relay: true|false", + desc: "Enable spam protection through rln-relay: true|false. Part of presets.", defaultValue: false, name: "rln-relay" .}: bool @@ -294,7 +302,8 @@ type WakuNodeConf* = object .}: Option[uint] rlnRelayDynamic* {. - desc: "Enable waku-rln-relay with on-chain dynamic group management: true|false", + desc: + "Enable waku-rln-relay with on-chain dynamic group management: true|false. Part of presets.", defaultValue: false, name: "rln-relay-dynamic" .}: bool @@ -318,7 +327,8 @@ type WakuNodeConf* = object .}: string rlnRelayBandwidthThreshold* {. - desc: "Message rate in bytes/sec after which verification of proofs should happen", + desc: + "Message rate in bytes/sec after which verification of proofs should happen. Part of presets.", defaultValue: 0, # to maintain backwards compatibility name: "rln-relay-bandwidth-threshold" .}: int @@ -546,7 +556,7 @@ type WakuNodeConf* = object ## Discovery v5 config discv5Discovery* {. - desc: "Enable discovering nodes via Node Discovery v5", + desc: "Enable discovering nodes via Node Discovery v5. Part of presets.", defaultValue: false, name: "discv5-discovery" .}: bool @@ -559,7 +569,7 @@ type WakuNodeConf* = object discv5BootstrapNodes* {. desc: - "Text-encoded ENR for bootstrap node. Used when connecting to the network. Argument may be repeated.", + "Text-encoded ENR for bootstrap node. Used when connecting to the network. Argument may be repeated. Part of presets.", name: "discv5-bootstrap-node" .}: seq[string] diff --git a/waku/factory/internal_config.nim b/waku/factory/internal_config.nim index 0d2b6ef5b..d8c618770 100644 --- a/waku/factory/internal_config.nim +++ b/waku/factory/internal_config.nim @@ -12,7 +12,8 @@ import ../node/config, ../waku_enr/capabilities, ../waku_enr, - ../waku_core + ../waku_core, + ./networks_config proc enrConfiguration*( conf: WakuNodeConf, netConfig: NetConfig, key: crypto.PrivateKey @@ -178,3 +179,60 @@ proc networkConfiguration*(conf: WakuNodeConf, clientId: string): NetConfigResul ) return netConfigRes + +proc applyPresetConfiguration*(srcConf: WakuNodeConf): Result[WakuNodeConf, string] = + var resConf = srcConf + + if resConf.clusterId == 1: + warn( + "TWN configuration will not be applied when `--cluster-id=1` is passed in future releases. Use `--preset=default` instead." + ) + resConf.preset = "default" + + case resConf.preset + of "default": + let twnClusterConf = ClusterConf.TheWakuNetworkConf() + for shard in resConf.shards: + if not (shard.int < twnClusterConf.pubsubTopics.len): + return err( + "Invalid shard received: " & $shard & " is not part of the available shards: " & + $toSeq(0 .. twnClusterConf.pubsubTopics.len - 1) + ) + else: + resConf.pubsubTopics.add(twnClusterConf.pubsubTopics[shard.uint16]) + + # Override configuration + resConf.maxMessageSize = twnClusterConf.maxMessageSize + resConf.clusterId = twnClusterConf.clusterId + resConf.rlnRelay = twnClusterConf.rlnRelay + resConf.rlnRelayEthContractAddress = twnClusterConf.rlnRelayEthContractAddress + resConf.rlnRelayChainId = twnClusterConf.rlnRelayChainId + resConf.rlnRelayDynamic = twnClusterConf.rlnRelayDynamic + resConf.rlnRelayBandwidthThreshold = twnClusterConf.rlnRelayBandwidthThreshold + resConf.rlnEpochSizeSec = twnClusterConf.rlnEpochSizeSec + resConf.rlnRelayUserMessageLimit = twnClusterConf.rlnRelayUserMessageLimit + resConf.discv5Discovery = twnClusterConf.discv5Discovery + resConf.discv5BootstrapNodes = + resConf.discv5BootstrapNodes & twnClusterConf.discv5BootstrapNodes + else: + discard + + return ok(resConf) + +proc nodeKeyConfiguration*( + conf: WakuNodeConf, rng: Option[ref HmacDrbgContext] = none(ref HmacDrbgContext) +): Result[PrivateKey, string] = + if conf.nodeKey.isSome: + return ok(conf.nodeKey.get()) + + var nodeRng = + if rng.isSome(): + rng.get() + else: + crypto.newRng() + + let key = crypto.PrivateKey.random(Secp256k1, nodeRng[]).valueOr: + error "Failed to generate key", error = $error + return err("Failed to generate key: " & $error) + + return ok(key) diff --git a/waku/factory/node_factory.nim b/waku/factory/node_factory.nim index 511056303..077e10b3e 100644 --- a/waku/factory/node_factory.nim +++ b/waku/factory/node_factory.nim @@ -399,15 +399,9 @@ proc setupNode*( else: crypto.newRng() - # Use provided key only if corresponding rng is also provided - let key = - if conf.nodeKey.isSome() and rng.isSome(): - conf.nodeKey.get() - else: - warn "missing key or rng, generating new set" - crypto.PrivateKey.random(Secp256k1, nodeRng[]).valueOr: - error "Failed to generate key", error = error - return err("Failed to generate key: " & $error) + let key = nodeKeyConfiguration(conf).valueOr: + error "Failed to generate key", error = error + return err("Failed to generate key: " & error) let netConfig = networkConfiguration(conf, clientId).valueOr: error "failed to create internal config", error = error diff --git a/waku/factory/waku.nim b/waku/factory/waku.nim index c77ea5237..a3d726e85 100644 --- a/waku/factory/waku.nim +++ b/waku/factory/waku.nim @@ -85,55 +85,25 @@ func version*(waku: Waku): string = ## Initialisation -proc init*(T: type Waku, conf: WakuNodeConf): Result[Waku, string] = - var confCopy = conf +proc init*(T: type Waku, srcConf: WakuNodeConf): Result[Waku, string] = let rng = crypto.newRng() - logging.setupLog(conf.logLevel, conf.logFormat) + logConfig(srcConf) + logging.setupLog(srcConf.logLevel, srcConf.logFormat) - case confCopy.clusterId - - # cluster-id=1 (aka The Waku Network) - of 1: - let twnClusterConf = ClusterConf.TheWakuNetworkConf() - if len(confCopy.shards) != 0: - confCopy.pubsubTopics = - confCopy.shards.mapIt(twnClusterConf.pubsubTopics[it.uint16]) - else: - confCopy.pubsubTopics = twnClusterConf.pubsubTopics - - # Override configuration - confCopy.maxMessageSize = twnClusterConf.maxMessageSize - confCopy.clusterId = twnClusterConf.clusterId - confCopy.rlnRelayEthContractAddress = twnClusterConf.rlnRelayEthContractAddress - confCopy.rlnRelayChainId = twnClusterConf.rlnRelayChainId - confCopy.rlnRelayDynamic = twnClusterConf.rlnRelayDynamic - confCopy.rlnRelayBandwidthThreshold = twnClusterConf.rlnRelayBandwidthThreshold - confCopy.discv5Discovery = twnClusterConf.discv5Discovery - confCopy.discv5BootstrapNodes = - confCopy.discv5BootstrapNodes & twnClusterConf.discv5BootstrapNodes - confCopy.rlnEpochSizeSec = twnClusterConf.rlnEpochSizeSec - confCopy.rlnRelayUserMessageLimit = twnClusterConf.rlnRelayUserMessageLimit - - # Only set rlnRelay to true if relay is configured - if confCopy.relay: - confCopy.rlnRelay = twnClusterConf.rlnRelay - else: - discard + # Why can't I replace this block with a concise `.valueOr`? + let finalConf = block: + let res = applyPresetConfiguration(srcConf) + if res.isErr(): + error "Failed to complete the config", error = res.error + return err("Failed to complete the config:" & $res.error) + res.get() info "Running nwaku node", version = git_version - logConfig(confCopy) - - if not confCopy.nodekey.isSome(): - let keyRes = crypto.PrivateKey.random(Secp256k1, rng[]) - if keyRes.isErr(): - error "Failed to generate key", error = $keyRes.error - return err("Failed to generate key: " & $keyRes.error) - confCopy.nodekey = some(keyRes.get()) debug "Retrieve dynamic bootstrap nodes" let dynamicBootstrapNodesRes = waku_dnsdisc.retrieveDynamicBootstrapNodes( - confCopy.dnsDiscovery, confCopy.dnsDiscoveryUrl, confCopy.dnsDiscoveryNameServers + finalConf.dnsDiscovery, finalConf.dnsDiscoveryUrl, finalConf.dnsDiscoveryNameServers ) if dynamicBootstrapNodesRes.isErr(): error "Retrieving dynamic bootstrap nodes failed", @@ -142,16 +112,16 @@ proc init*(T: type Waku, conf: WakuNodeConf): Result[Waku, string] = "Retrieving dynamic bootstrap nodes failed: " & dynamicBootstrapNodesRes.error ) - let nodeRes = setupNode(confCopy, some(rng)) + let nodeRes = setupNode(finalConf, some(rng)) if nodeRes.isErr(): error "Failed setting up node", error = nodeRes.error return err("Failed setting up node: " & nodeRes.error) var waku = Waku( version: git_version, - conf: confCopy, + conf: finalConf, rng: rng, - key: confCopy.nodekey.get(), + key: finalConf.nodekey.get(), node: nodeRes.get(), dynamicBootstrapNodes: dynamicBootstrapNodesRes.get(), )