diff --git a/tests/factory/test_waku_conf.nim b/tests/factory/test_waku_conf.nim new file mode 100644 index 000000000..eeca495e7 --- /dev/null +++ b/tests/factory/test_waku_conf.nim @@ -0,0 +1,248 @@ +{.used.} + +import + libp2p/crypto/[crypto, secp], + nimcrypto/utils, + std/[options, sequtils], + results, + testutils/unittests +import + waku/factory/waku_conf, + waku/factory/networks_config, + waku/common/utils/parse_size_units + +suite "Waku Conf - build with cluster conf": + test "Cluster Conf is passed and relay is enabled": + ## Setup + let clusterConf = ClusterConf.TheWakuNetworkConf() + var builder = WakuConfBuilder.init() + # Mount all shards in network + let expectedShards = toSeq[0.uint16 .. 7.uint16] + + ## Given + builder.withRlnRelayEthClientAddress("https://my_eth_rpc_url/") + builder.withClusterConf(clusterConf) + builder.withRelay(true) + + ## When + let res = builder.build() + assert res.isOk(), $res.error + + ## Then + let conf = res.get() + assert conf.clusterId == clusterConf.clusterId + assert conf.numShardsInNetwork == clusterConf.numShardsInNetwork + assert conf.shards == expectedShards + assert conf.maxMessageSizeBytes == + int(parseCorrectMsgSize(clusterConf.maxMessageSize)) + + assert conf.discv5BootstrapNodes.map( + proc(e: TextEnr): string = + e.string + ) == clusterConf.discv5BootstrapNodes + + if clusterConf.rlnRelay: + assert conf.rlnRelayConf.isSome + + let rlnRelayConf = conf.rlnRelayConf.get() + assert rlnRelayConf.rlnRelayEthContractAddress.string == + clusterConf.rlnRelayEthContractAddress + assert rlnRelayConf.rlnRelayDynamic == clusterConf.rlnRelayDynamic + assert rlnRelayConf.rlnRelayChainId == clusterConf.rlnRelayChainId + assert rlnRelayConf.rlnRelayBandwidthThreshold == + clusterConf.rlnRelayBandwidthThreshold + assert rlnRelayConf.rlnEpochSizeSec == clusterConf.rlnEpochSizeSec + assert rlnRelayConf.rlnRelayUserMessageLimit == + clusterConf.rlnRelayUserMessageLimit + + test "Cluster Conf is passed, but relay is disabled": + ## Setup + let clusterConf = ClusterConf.TheWakuNetworkConf() + var builder = WakuConfBuilder.init() + + let # Mount all shards in network + expectedShards = toSeq[0.uint16 .. 7.uint16] + + ## Given + builder.withRlnRelayEthClientAddress("https://my_eth_rpc_url/") + builder.withClusterConf(clusterConf) + builder.withRelay(false) + + ## When + let res = builder.build() + assert res.isOk(), $res.error + + ## Then + let conf = res.get() + assert conf.clusterId == clusterConf.clusterId + assert conf.numShardsInNetwork == clusterConf.numShardsInNetwork + assert conf.shards == expectedShards + assert conf.maxMessageSizeBytes == + int(parseCorrectMsgSize(clusterConf.maxMessageSize)) + assert conf.discv5BootstrapNodes.map( + proc(e: TextEnr): string = + e.string + ) == clusterConf.discv5BootstrapNodes + + assert conf.rlnRelayConf.isNone + + test "Cluster Conf is passed, but rln relay is disabled": + ## Setup + let clusterConf = ClusterConf.TheWakuNetworkConf() + var builder = WakuConfBuilder.init() + + let # Mount all shards in network + expectedShards = toSeq[0.uint16 .. 7.uint16] + + ## Given + builder.withRlnRelayEthClientAddress("https://my_eth_rpc_url/") + builder.withClusterConf(clusterConf) + builder.withRlnRelay(false) + + ## When + let res = builder.build() + assert res.isOk(), $res.error + + ## Then + let conf = res.get() + assert conf.clusterId == clusterConf.clusterId + assert conf.numShardsInNetwork == clusterConf.numShardsInNetwork + assert conf.shards == expectedShards + assert conf.maxMessageSizeBytes == + int(parseCorrectMsgSize(clusterConf.maxMessageSize)) + assert conf.discv5BootstrapNodes.map( + proc(e: TextEnr): string = + e.string + ) == clusterConf.discv5BootstrapNodes + + assert conf.rlnRelayConf.isNone + + test "Cluster Conf is passed and valid shards are specified": + ## Setup + let clusterConf = ClusterConf.TheWakuNetworkConf() + var builder = WakuConfBuilder.init() + let shards = @[2.uint16, 3.uint16] + + ## Given + builder.withRlnRelayEthClientAddress("https://my_eth_rpc_url/") + builder.withClusterConf(clusterConf) + builder.withShards(shards) + + ## When + let res = builder.build() + assert res.isOk(), $res.error + + ## Then + let conf = res.get() + assert conf.clusterId == clusterConf.clusterId + assert conf.numShardsInNetwork == clusterConf.numShardsInNetwork + assert conf.shards == shards + assert conf.maxMessageSizeBytes == + int(parseCorrectMsgSize(clusterConf.maxMessageSize)) + assert conf.discv5BootstrapNodes.map( + proc(e: TextEnr): string = + e.string + ) == clusterConf.discv5BootstrapNodes + + test "Cluster Conf is passed and invalid shards are specified": + ## Setup + let clusterConf = ClusterConf.TheWakuNetworkConf() + var builder = WakuConfBuilder.init() + let shards = @[2.uint16, 10.uint16] + + ## Given + builder.withRlnRelayEthClientAddress("https://my_eth_rpc_url/") + builder.withClusterConf(clusterConf) + builder.withShards(shards) + + ## When + let res = builder.build() + + ## Then + assert res.isErr(), "Invalid shard was accepted" + + test "Cluster Conf is passed and RLN contract is overridden": + ## Setup + let clusterConf = ClusterConf.TheWakuNetworkConf() + var builder = WakuConfBuilder.init() + # Mount all shards in network + let expectedShards = toSeq[0.uint16 .. 7.uint16] + let contractAddress = "0x0123456789ABCDEF" + + ## Given + builder.withRlnRelayEthClientAddress("https://my_eth_rpc_url/") + builder.withRlnRelayEthContractAddress(contractAddress) + builder.withClusterConf(clusterConf) + builder.withRelay(true) + + ## When + let res = builder.build() + assert res.isOk(), $res.error + + ## Then + let conf = res.get() + assert conf.clusterId == clusterConf.clusterId + assert conf.numShardsInNetwork == clusterConf.numShardsInNetwork + assert conf.shards == expectedShards + assert conf.maxMessageSizeBytes == + int(parseCorrectMsgSize(clusterConf.maxMessageSize)) + + assert conf.discv5BootstrapNodes.map( + proc(e: TextEnr): string = + e.string + ) == clusterConf.discv5BootstrapNodes + + if clusterConf.rlnRelay: + assert conf.rlnRelayConf.isSome + + let rlnRelayConf = conf.rlnRelayConf.get() + assert rlnRelayConf.rlnRelayEthContractAddress.string == contractAddress + assert rlnRelayConf.rlnRelayDynamic == clusterConf.rlnRelayDynamic + assert rlnRelayConf.rlnRelayChainId == clusterConf.rlnRelayChainId + assert rlnRelayConf.rlnRelayBandwidthThreshold == + clusterConf.rlnRelayBandwidthThreshold + assert rlnRelayConf.rlnEpochSizeSec == clusterConf.rlnEpochSizeSec + assert rlnRelayConf.rlnRelayUserMessageLimit == + clusterConf.rlnRelayUserMessageLimit + +suite "Waku Conf - node key": + test "Node key is generated": + ## Setup + var builder = WakuConfBuilder.init() + builder.withClusterId(1) + builder.withMaxMessageSizeBytes(1) + + ## Given + + ## When + let res = builder.build() + assert res.isOk(), $res.error + let conf = res.get() + + ## Then + let pubkey = getPublicKey(conf.nodeKey) + assert pubkey.isOk() + + test "Passed node key is used": + ## Setup + let nodeKeyStr = + "0011223344556677889900aabbccddeeff0011223344556677889900aabbccddeeff" + let nodeKey = block: + let key = SkPrivateKey.init(utils.fromHex(nodeKeyStr)).tryGet() + crypto.PrivateKey(scheme: Secp256k1, skkey: key) + var builder = WakuConfBuilder.init() + builder.withClusterId(1) + builder.withMaxMessageSizeBytes(1) + + ## Given + builder.withNodeKey(nodeKey) + + ## When + let res = builder.build() + assert res.isOk(), $res.error + let conf = res.get() + + ## Then + assert utils.toHex(conf.nodeKey.getRawBytes().get()) == + utils.toHex(nodeKey.getRawBytes().get()), + "Passed node key isn't in config:" & $nodeKey & $conf.nodeKey diff --git a/waku/factory/internal_config.nim b/waku/factory/internal_config.nim index 08f11f1c5..af2f5d4e8 100644 --- a/waku/factory/internal_config.nim +++ b/waku/factory/internal_config.nim @@ -13,7 +13,8 @@ import ../waku_enr/capabilities, ../waku_enr, ../waku_core, - ./networks_config + ./networks_config, + ./waku_conf proc enrConfiguration*( conf: WakuNodeConf, netConfig: NetConfig, key: crypto.PrivateKey @@ -159,41 +160,25 @@ proc networkConfiguration*(conf: WakuNodeConf, clientId: string): NetConfigResul return netConfigRes -proc applyPresetConfiguration*(srcConf: WakuNodeConf): Result[WakuNodeConf, string] = - var resConf = srcConf +proc applyPresetConfiguration*( + srcConf: WakuNodeConf, wakuConfBuilder: var WakuConfBuilder +): void = + var preset = srcConf.preset - if resConf.clusterId == 1: + if srcConf.clusterId == 1: warn( "TWN - The Waku Network configuration will not be applied when `--cluster-id=1` is passed in future releases. Use `--preset=twn` instead." ) - resConf.preset = "twn" + preset = "twn" - case toLowerAscii(resConf.preset) + case toLowerAscii(preset) of "twn": let twnClusterConf = ClusterConf.TheWakuNetworkConf() - # 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.discv5Discovery = twnClusterConf.discv5Discovery - resConf.discv5BootstrapNodes = - resConf.discv5BootstrapNodes & twnClusterConf.discv5BootstrapNodes - resConf.rlnEpochSizeSec = twnClusterConf.rlnEpochSizeSec - resConf.rlnRelayUserMessageLimit = twnClusterConf.rlnRelayUserMessageLimit - resConf.numShardsInNetwork = twnClusterConf.numShardsInNetwork - - if resConf.relay: - resConf.rlnRelay = twnClusterConf.rlnRelay + wakuConfBuilder.withClusterConf(twnClusterConf) else: discard - return ok(resConf) - # TODO: numShardsInNetwork should be mandatory with autosharding, and unneeded otherwise proc getNumShardsInNetwork*(conf: WakuNodeConf): uint32 = if conf.numShardsInNetwork != 0: @@ -201,28 +186,3 @@ proc getNumShardsInNetwork*(conf: WakuNodeConf): uint32 = # If conf.numShardsInNetwork is not set, use 1024 - the maximum possible as per the static sharding spec # https://github.com/waku-org/specs/blob/master/standards/core/relay-sharding.md#static-sharding return uint32(MaxShardIndex + 1) - -proc validateShards*(conf: WakuNodeConf): Result[void, string] = - let numShardsInNetwork = getNumShardsInNetwork(conf) - - for shard in conf.shards: - if shard >= numShardsInNetwork: - let msg = - "validateShards invalid shard: " & $shard & " when numShardsInNetwork: " & - $numShardsInNetwork # fmt doesn't work - error "validateShards failed", error = msg - return err(msg) - - return ok() - -proc getNodeKey*( - conf: WakuNodeConf, rng: ref HmacDrbgContext = crypto.newRng() -): Result[PrivateKey, string] = - if conf.nodekey.isSome(): - return ok(conf.nodekey.get()) - - warn "missing node key, generating new set" - let key = crypto.PrivateKey.random(Secp256k1, rng[]).valueOr: - error "Failed to generate key", error = error - return err("Failed to generate key: " & $error) - return ok(key) diff --git a/waku/factory/waku.nim b/waku/factory/waku.nim index 0c2748333..4dffe319d 100644 --- a/waku/factory/waku.nim +++ b/waku/factory/waku.nim @@ -145,28 +145,18 @@ proc setupAppCallbacks( return ok() proc new*( - T: type Waku, wakuConf: var WakuConf, appCallbacks: AppCallbacks = nil + T: type Waku, wakuNodeConf: WakuNodeConf, appCallbacks: AppCallbacks = nil ): Result[Waku, string] = let rng = crypto.newRng() - logging.setupLog(wakuConf.logLevel, wakuConf.logFormat) + logging.setupLog(wakuNodeConf.logLevel, wakuNodeConf.logFormat) - confCopy = block: - let res = applyPresetConfiguration(confCopy) - if res.isErr(): - error "Failed to complete the config", error = res.error - return err("Failed to complete the config:" & $res.error) - res.get() + var confBuilder = WakuConfBuilder.init() - wakuConf.log() + applyPresetConfiguration(wakuNodeConf, confBuilder) info "Running nwaku node", version = git_version - let validateShardsRes = validateShards(confCopy) - if validateShardsRes.isErr(): - error "Failed validating shards", error = $validateShardsRes.error - return err("Failed validating shards: " & $validateShardsRes.error) - let keyRes = getNodeKey(confCopy, rng) if keyRes.isErr(): error "Failed to generate key", error = $keyRes.error diff --git a/waku/factory/waku_conf.nim b/waku/factory/waku_conf.nim new file mode 100644 index 000000000..0587a093a --- /dev/null +++ b/waku/factory/waku_conf.nim @@ -0,0 +1,453 @@ +import std/[options, sequtils], chronicles, libp2p/crypto/crypto, results + +import ../common/logging, ../common/utils/parse_size_units + +import waku/factory/networks_config + +logScope: + topics = "wakunode conf" + +type + TextEnr* = distinct string + ContractAddress* = distinct string + EthRpcUrl* = distinct string + +# TODO: this should come from RLN relay module +type RlnRelayConf* = ref object + rlnRelayEthContractAddress*: ContractAddress + rlnRelayChainId*: uint + rlnRelayDynamic*: bool + rlnRelayBandwidthThreshold*: int + rlnEpochSizeSec*: uint64 + rlnRelayUserMessageLimit*: uint64 + rlnRelayEthClientAddress*: EthRpcUrl + +type WakuConf* = ref object + nodeKey*: PrivateKey + + clusterId*: uint16 + numShardsInNetwork*: uint32 + shards*: seq[uint16] + + relay*: bool + store*: bool + filter*: bool + lightPush*: bool + peerExchange*: bool + + discv5BootstrapNodes*: seq[TextEnr] + + rlnRelayConf*: Option[RlnRelayConf] + + maxMessageSizeBytes*: int + + logLevel*: logging.LogLevel + logFormat*: logging.LogFormat + +proc log*(conf: WakuConf) = + info "Configuration: Enabled protocols", + relay = conf.relay, + rlnRelay = conf.rlnRelayConf.isSome, + store = conf.store, + filter = conf.filter, + lightPush = conf.lightPush, + peerExchange = conf.peerExchange + + info "Configuration. Network", cluster = conf.clusterId + + for shard in conf.shards: + info "Configuration. Shards", shard = shard + + for i in conf.discv5BootstrapNodes: + info "Configuration. Bootstrap nodes", node = i.string + + if conf.rlnRelayConf.isSome: + var rlnRelayConf = conf.rlnRelayConf.geT() + if rlnRelayConf.rlnRelayDynamic: + info "Configuration. Validation", + mechanism = "onchain rln", + contract = rlnRelayConf.rlnRelayEthContractAddress.string, + maxMessageSize = conf.maxMessageSizeBytes, + rlnEpochSizeSec = rlnRelayConf.rlnEpochSizeSec, + rlnRelayUserMessageLimit = rlnRelayConf.rlnRelayUserMessageLimit, + rlnRelayEthClientAddress = string(rlnRelayConf.rlnRelayEthClientAddress) + +type RlnRelayConfBuilder = ref object + rlnRelay: Option[bool] + rlnRelayEthContractAddress: Option[ContractAddress] + rlnRelayChainId: Option[uint] + rlnRelayDynamic: Option[bool] + rlnRelayBandwidthThreshold: Option[int] + rlnEpochSizeSec: Option[uint64] + rlnRelayUserMessageLimit: Option[uint64] + rlnRelayEthClientAddress: Option[EthRpcUrl] + +proc init(T: type RlnRelayConfBuilder): RlnRelayConfBuilder = + RlnRelayConfBuilder() + +proc withRlnRelay(builder: var RlnRelayConfBuilder, rlnRelay: bool) = + builder.rlnRelay = some(rlnRelay) + +proc withRlnRelayEthClientAddress( + builder: var RlnRelayConfBuilder, rlnRelayEthClientAddress: EthRpcUrl +) = + builder.rlnRelayEthClientAddress = some(rlnRelayEthClientAddress) + +proc withRlnRelayEthContractAddress( + builder: var RlnRelayConfBuilder, withRlnRelayEthContractAddress: ContractAddress +) = + builder.rlnRelayEthContractAddress = some(withRlnRelayEthContractAddress) + +proc build(builder: RlnRelayConfBuilder): Result[Option[RlnRelayConf], string] = + if builder.rlnRelay.isNone or not builder.rlnRelay.get(): + info "RLN Relay is disabled" + return ok(none(RlnRelayConf)) + + let rlnRelayEthContractAddress = + if builder.rlnRelayEthContractAddress.isSome: + builder.rlnRelayEthContractAddress.get() + else: + return err("RLN Eth Contract Address was not specified") + + let rlnRelayChainId = + if builder.rlnRelayChainId.isSome: + builder.rlnRelayChainId.get() + else: + return err("RLN Relay Chain Id was not specified") + + let rlnRelayDynamic = + if builder.rlnRelayDynamic.isSome: + builder.rlnRelayDynamic.get() + else: + return err("RLN Relay Dynamic was not specified") + + let rlnRelayBandwidthThreshold = + if builder.rlnRelayBandwidthThreshold.isSome: + builder.rlnRelayBandwidthThreshold.get() + else: + return err("RLN Relay Bandwidth Threshold was not specified") + + let rlnEpochSizeSec = + if builder.rlnEpochSizeSec.isSome: + builder.rlnEpochSizeSec.get() + else: + return err("RLN Epoch Size was not specified") + + let rlnRelayUserMessageLimit = + if builder.rlnRelayUserMessageLimit.isSome: + builder.rlnRelayUserMessageLimit.get() + else: + return err("RLN Relay User Message Limit was not specified") + + let rlnRelayEthClientAddress = + if builder.rlnRelayEthClientAddress.isSome: + builder.rlnRelayEthClientAddress.get() + else: + return err("RLN Relay Eth Client Address was not specified") + + return ok( + some( + RlnRelayConf( + rlnRelayChainId: rlnRelayChainId, + rlnRelayDynamic: rlnRelayDynamic, + rlnRelayEthContractAddress: rlnRelayEthContractAddress, + rlnEpochSizeSec: rlnEpochSizeSec, + rlnRelayUserMessageLimit: rlnRelayUserMessageLimit, + rlnRelayEthClientAddress: rlnRelayEthClientAddress, + ) + ) + ) + +type WakuConfBuilder* = ref object + nodeKey*: Option[PrivateKey] + + clusterId: Option[uint16] + numShardsInNetwork: Option[uint32] + shards: Option[seq[uint16]] + + relay: Option[bool] + store: Option[bool] + filter: Option[bool] + lightPush: Option[bool] + peerExchange: Option[bool] + + clusterConf: Option[ClusterConf] + + rlnRelayConf: RlnRelayConfBuilder + + maxMessageSizeBytes: Option[int] + + discv5Discovery: Option[bool] + discv5BootstrapNodes: Option[seq[TextEnr]] + + logLevel: Option[logging.LogLevel] + logFormat: Option[logging.LogFormat] + +proc init*(T: type WakuConfBuilder): WakuConfBuilder = + WakuConfBuilder(rlnRelayConf: RlnRelayConfBuilder.init()) + +proc withClusterConf*(builder: var WakuConfBuilder, clusterConf: ClusterConf) = + builder.clusterConf = some(clusterConf) + +proc withNodeKey*(builder: var WakuConfBuilder, nodeKey: PrivateKey) = + builder.nodeKey = some(nodeKey) + +proc withClusterId*(builder: var WakuConfBuilder, clusterId: uint16) = + builder.clusterid = some(clusterId) + +proc withShards*(builder: var WakuConfBuilder, shards: seq[uint16]) = + builder.shards = some(shards) + +proc withRelay*(builder: var WakuConfBuilder, relay: bool) = + builder.relay = some(relay) + +proc withRlnRelay*(builder: var WakuConfBuilder, rlnRelay: bool) = + builder.rlnRelayConf.withRlnRelay(rlnRelay) + +proc withRlnRelayEthClientAddress*( + builder: var WakuConfBuilder, rlnRelayEthClientAddress: string +) = + builder.rlnRelayConf.withRlnRelayEthClientAddress(rlnRelayEthClientAddress.EthRpcUrl) + +proc withRlnRelayEthContractAddress*( + builder: var WakuConfBuilder, rlnRelayEthContractAddress: string +) = + builder.rlnRelayConf.withRlnRelayEthContractAddress( + rlnRelayEthContractAddress.ContractAddress + ) + +proc withMaxMessageSizeBytes*(builder: WakuConfBuilder, maxMessageSizeBytes: int) = + builder.maxMessageSizeBytes = some(maxMessageSizeBytes) + +proc validateShards*(wakuConf: WakuConf): Result[void, string] = + let numShardsInNetwork = wakuConf.numShardsInNetwork + + for shard in wakuConf.shards: + if shard >= numShardsInNetwork: + let msg = + "validateShards invalid shard: " & $shard & " when numShardsInNetwork: " & + $numShardsInNetwork # fmt doesn't work + error "validateShards failed", error = msg + return err(msg) + + return ok() + +proc validate(wakuConf: WakuConf): Result[void, string] = + ?validateShards(wakuConf) + + return ok() + +proc nodeKey( + builder: WakuConfBuilder, rng: ref HmacDrbgContext +): Result[PrivateKey, string] = + if builder.nodeKey.isSome(): + return ok(builder.nodeKey.get()) + else: + warn "missing node key, generating new set" + let nodeKey = crypto.PrivateKey.random(Secp256k1, rng[]).valueOr: + error "Failed to generate key", error = error + return err("Failed to generate key: " & $error) + return ok(nodeKey) + +proc build*( + builder: var WakuConfBuilder, rng: ref HmacDrbgContext = crypto.newRng() +): Result[WakuConf, string] = + ## Return a WakuConf that contains all mandatory parameters + ## Applies some sane defaults that are applicable across any usage + ## of libwaku. It aims to be agnostic so it does not apply a + ## default when it is opinionated. + + let nodeKey = ?nodeKey(builder, rng) + + let relay = + if builder.relay.isSome: + builder.relay.get() + else: + warn "whether to mount relay is not specified, defaulting to not mounting" + false + + let store = + if builder.store.isSome: + builder.store.get() + else: + warn "whether to mount store is not specified, defaulting to not mounting" + false + + let filter = + if builder.filter.isSome: + builder.filter.get() + else: + warn "whether to mount filter is not specified, defaulting to not mounting" + false + + let lightPush = + if builder.lightPush.isSome: + builder.lightPush.get() + else: + warn "whether to mount lightPush is not specified, defaulting to not mounting" + false + + let peerExchange = + if builder.peerExchange.isSome: + builder.peerExchange.get() + else: + warn "whether to mount peerExchange is not specified, defaulting to not mounting" + false + + # Apply cluster conf - values passed manually override cluster conf + if builder.clusterConf.isSome: + var clusterConf = builder.clusterConf.get() + + if builder.clusterId.isNone: + builder.clusterId = some(clusterConf.clusterId) + else: + warn "Cluster id was manually provided alongside a cluster conf", + used = builder.clusterId, discarded = clusterConf.clusterId + + if relay and clusterConf.rlnRelay: + var rlnRelayConf = builder.rlnRelayConf + + if rlnRelayConf.rlnRelay.isNone: + rlnRelayConf.rlnRelay = some(true) + else: + warn "RLN Relay was manually provided alongside a cluster conf", + used = rlnRelayConf.rlnRelay, discarded = clusterConf.rlnRelay + + if rlnRelayConf.rlnRelayEthContractAddress.isNone: + rlnRelayConf.rlnRelayEthContractAddress = + some(clusterConf.rlnRelayEthContractAddress.ContractAddress) + else: + warn "RLN Relay ETH Contract Address was manually provided alongside a cluster conf", + used = rlnRelayConf.rlnRelayEthContractAddress.get().string, + discarded = clusterConf.rlnRelayEthContractAddress.string + + if rlnRelayConf.rlnRelayChainId.isNone: + rlnRelayConf.rlnRelayChainId = some(clusterConf.rlnRelayChainId) + else: + warn "RLN Relay Chain Id was manually provided alongside a cluster conf", + used = rlnRelayConf.rlnRelayChainId, discarded = clusterConf.rlnRelayChainId + + if rlnRelayConf.rlnRelayDynamic.isNone: + rlnRelayConf.rlnRelayDynamic = some(clusterConf.rlnRelayDynamic) + else: + warn "RLN Relay Dynamic was manually provided alongside a cluster conf", + used = rlnRelayConf.rlnRelayDynamic, discarded = clusterConf.rlnRelayDynamic + + if rlnRelayConf.rlnRelayBandwidthThreshold.isNone: + rlnRelayConf.rlnRelayBandwidthThreshold = + some(clusterConf.rlnRelayBandwidthThreshold) + else: + warn "RLN Relay Bandwidth Threshold was manually provided alongside a cluster conf", + used = rlnRelayConf.rlnRelayBandwidthThreshold, + discarded = clusterConf.rlnRelayBandwidthThreshold + + if rlnRelayConf.rlnEpochSizeSec.isNone: + rlnRelayConf.rlnEpochSizeSec = some(clusterConf.rlnEpochSizeSec) + else: + warn "RLN Epoch Size in Seconds was manually provided alongside a cluster conf", + used = rlnRelayConf.rlnEpochSizeSec, discarded = clusterConf.rlnEpochSizeSec + + if rlnRelayConf.rlnRelayUserMessageLimit.isNone: + rlnRelayConf.rlnRelayUserMessageLimit = + some(clusterConf.rlnRelayUserMessageLimit) + else: + warn "RLN Relay Dynamic was manually provided alongside a cluster conf", + used = rlnRelayConf.rlnRelayUserMessageLimit, + discarded = clusterConf.rlnRelayUserMessageLimit + + if builder.maxMessageSizeBytes.isNone: + builder.maxMessageSizeBytes = + some(int(parseCorrectMsgSize(clusterConf.maxMessageSize))) + else: + warn "Max Message Size was manually provided alongside a cluster conf", + used = builder.maxMessageSizeBytes, discarded = clusterConf.maxMessageSize + + if builder.numShardsInNetwork.isNone: + builder.numShardsInNetwork = some(clusterConf.numShardsInNetwork) + else: + warn "Num Shards In Network was manually provided alongside a cluster conf", + used = builder.numShardsInNetwork, discarded = clusterConf.numShardsInNetwork + + if builder.discv5Discovery.isNone: + builder.discv5Discovery = some(clusterConf.discv5Discovery) + + if builder.discv5BootstrapNodes.isNone: + builder.discv5BootstrapNodes = some( + clusterConf.discv5BootstrapNodes.map( + proc(e: string): TextEnr = + e.TextEnr + ) + ) + # Apply preset - end + + let clusterId = + if builder.clusterId.isSome: + builder.clusterId.get() + else: + return err("Cluster Id is missing") + + let numShardsInNetwork = + if builder.numShardsInNetwork.isSome: + builder.numShardsInNetwork.get() + else: + warn "Number of shards in network not specified, defaulting to one shard" + 1 + + let shards = + if builder.shards.isSome: + builder.shards.get() + else: + warn "shards not specified, defaulting to all shards in network" + # TODO: conversion should not be needed + let upperShard: uint16 = uint16(numShardsInNetwork - 1) + toSeq(0.uint16 .. upperShard) + + let discv5BootstrapNodes = + if builder.discv5BootstrapNodes.isSome: + builder.discv5BootstrapNodes.get() + else: + @[] + + let rlnRelayConf = builder.rlnRelayConf.build().valueOr: + return err("RLN Relay Conf building failed: " & $error) + + let maxMessageSizeBytes = + if builder.maxMessageSizeBytes.isSome: + builder.maxMessageSizeBytes.get() + else: + return err("Max Message Size is missing") + + let logLevel = + if builder.logLevel.isSome: + builder.logLevel.get() + else: + warn "Log Level not specified, defaulting to INFO" + logging.LogLevel.INFO + + let logFormat = + if builder.logFormat.isSome: + builder.logFormat.get() + else: + warn "Log Format not specified, defaulting to TEXT" + logging.LogFormat.TEXT + + let wakuConf = WakuConf( + nodeKey: nodeKey, + clusterId: clusterId, + numShardsInNetwork: numShardsInNetwork, + shards: shards, + relay: relay, + store: store, + filter: filter, + lightPush: lightPush, + peerExchange: peerExchange, + discv5BootstrapNodes: discv5BootstrapNodes, + rlnRelayConf: rlnRelayConf, + maxMessageSizeBytes: maxMessageSizeBytes, + logLevel: logLevel, + logFormat: logFormat, + ) + + ?validate(wakuConf) + + return ok(wakuConf)