From 2e368e8918e3b199bd1fc9b5566f48889e0e2f8b Mon Sep 17 00:00:00 2001 From: fryorcraken Date: Fri, 11 Apr 2025 16:23:18 +1000 Subject: [PATCH] getting there --- apps/wakunode2/wakunode2.nim | 23 +- waku/discovery/waku_discv5.nim | 2 +- waku/factory/external_config.nim | 240 +++-------- waku/factory/internal_config.nim | 20 - waku/factory/networks_config.nim | 2 +- waku/factory/waku_conf.nim | 99 +++-- waku/factory/waku_conf_builder.nim | 652 ++++++++++++++++++++--------- waku/node/waku_metrics.nim | 22 +- waku/waku_api/rest/builder.nim | 3 +- waku/waku_api/rest/server.nim | 2 +- waku/waku_rln_relay/rln_relay.nim | 6 +- 11 files changed, 594 insertions(+), 477 deletions(-) diff --git a/apps/wakunode2/wakunode2.nim b/apps/wakunode2/wakunode2.nim index 8e9f246fb..2dcdb0e2a 100644 --- a/apps/wakunode2/wakunode2.nim +++ b/apps/wakunode2/wakunode2.nim @@ -64,20 +64,20 @@ when isMainModule: nodeHealthMonitor = WakuNodeHealthMonitor() nodeHealthMonitor.setOverallHealth(HealthStatus.INITIALIZING) - let wakuConf = wakuNodeConf.toWakuConf().valueOr: + let conf = wakuNodeConf.toWakuConf().valueOr: error "Waku configuration failed", error = error quit(QuitFailure) var restServer: WakuRestServerRef = nil - if wakuConf.restServerConf.isSome: + if conf.restServerConf.isSome: restServer = rest_server_builder.startRestServerEssentials( - nodeHealthMonitor, wakuConf.restServerConf.get(), wakuConf.portsShift + nodeHealthMonitor, conf.restServerConf.get(), conf.portsShift ).valueOr: error "Starting essential REST server failed.", error = $error quit(QuitFailure) - var waku = Waku.new(wakuConf).valueOr: + var waku = Waku.new(conf).valueOr: error "Waku initialization failed", error = error quit(QuitFailure) @@ -89,14 +89,14 @@ when isMainModule: error "Starting waku failed", error = error quit(QuitFailure) - if wakuConf.restServerConf.isSome: + if conf.restServerConf.isSome: rest_server_builder.startRestServerProtocolSupport( restServer, waku.node, waku.wakuDiscv5, - wakuConf.restServerConf.get(), + conf.restServerConf.get(), conf.relay, - conf.lightPuh, + conf.lightPush, conf.clusterId, conf.shards, conf.contentTopics, @@ -104,9 +104,12 @@ when isMainModule: error "Starting protocols support REST server failed.", error = $error quit(QuitFailure) - waku.metricsServer = waku_metrics.startMetricsServerAndLogging(confCopy).valueOr: - error "Starting monitoring and external interfaces failed", error = error - quit(QuitFailure) + if conf.metricsServerConf.isSome: + waku.metricsServer = waku_metrics.startMetricsServerAndLogging( + conf.metricsServerConf.get(), conf.portsShift + ).valueOr: + error "Starting monitoring and external interfaces failed", error = error + quit(QuitFailure) nodeHealthMonitor.setOverallHealth(HealthStatus.READY) diff --git a/waku/discovery/waku_discv5.nim b/waku/discovery/waku_discv5.nim index be651d74e..0c57eb384 100644 --- a/waku/discovery/waku_discv5.nim +++ b/waku/discovery/waku_discv5.nim @@ -23,7 +23,7 @@ logScope: ## Config # TODO: merge both conf -type Discv5Conf* = object +type Discv5Conf* {.requiresInit.} = object # TODO: This should probably be an option on the builder # But translated to everything else "false" on the config discv5Only*: bool diff --git a/waku/factory/external_config.nim b/waku/factory/external_config.nim index b4cef7e24..0d02a4a6b 100644 --- a/waku/factory/external_config.nim +++ b/waku/factory/external_config.nim @@ -1,6 +1,7 @@ import std/[strutils, strformat], results, + chronicles, chronos, regex, confutils, @@ -14,15 +15,17 @@ import nimcrypto/utils, secp256k1, json + import + ./waku_conf, + ./waku_conf_builder, + ./networks_config, ../common/confutils/envvar/defs as confEnvvarDefs, ../common/confutils/envvar/std/net as confEnvvarNet, ../common/logging, ../waku_enr, ../node/peer_manager, ../waku_core/topics/pubsub_topic, - ./waku_conf, - ./waku_conf_builder, ../../tools/rln_keystore_generator/rln_keystore_generator, ../../tools/rln_db_inspector/rln_db_inspector @@ -30,6 +33,9 @@ include ../waku_core/message/default_values export confTomlDefs, confTomlNet, confEnvvarDefs, confEnvvarNet, ProtectedShard +logScope: + topics = "waku external config" + # Git version in git describe format (defined at compile time) const git_version* {.strdefine.} = "n/a" @@ -884,27 +890,23 @@ proc toKeystoreGeneratorConf*(n: WakuNodeConf): ConfResult[RlnKeystoreGeneratorC ) ) - # b.withLogLevel(n.logLevel) - # b.withLogFormat(n.logFormat) - - # b.rlnRelayConf.withCredPath(n.rlnRelayCredPath) - # b.rlnRelayConf.withEthClientAddress(n.rlnRelayEthClientAddress) - # b.rlnRelayConf.withEthContractAddress(n.rlnRelayEthContractAddress) - # b.rlnRelayConf.withChainId(n.rlnRelayChainId) - # b.rlnRelayConf.withCredPassword(n.rlnRelayCredPassword) - - ## TODO: Oh, actually we probably need a different conf object by command :) - # rlnRelayEthPrivateKey* {. - # desc: "Private key for broadcasting transactions", - # defaultValue: "", - # name: "rln-relay-eth-private-key" - # .}: string - - # b.rlnRelayConf.withUserMessageLimit(n.rlnRelayUserMessageLimit) - proc toInspectRlnDbConf*(n: WakuNodeConf): ConfResult[InspectRlnDbConf] = return ok(InspectRlnDbConf(treePath: n.treePath)) +proc toClusterConf(preset: string, clusterId: uint16): ConfResult[ClusterConf] = + var lcPreset = toLowerAscii(preset) + if 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." + ) + lcPreset = "twn" + + case lcPreset + of "twn": + ok(ClusterConf.TheWakuNetworkConf()) + else: + err("Invalid --preset value passed: " & preset) + proc toWakuConf*(n: WakuNodeConf): ConfResult[WakuConf] = var b = WakuConfBuilder.init() b.withLogLevel(n.logLevel) @@ -916,181 +918,41 @@ proc toWakuConf*(n: WakuNodeConf): ConfResult[WakuConf] = b.rlnRelayConf.withChainId(n.rlnRelayChainId) b.rlnRelayConf.withCredPassword(n.rlnRelayCredPassword) b.rlnRelayConf.withUserMessageLimit(n.rlnRelayUserMessageLimit) - # # TODO: Remove "Default is" when it's already visible on the CLI - # rlnRelayUserMessageLimit* {. - # desc: - # "Set a user message limit for the rln membership registration. Must be a positive integer. Default is 1.", - # defaultValue: 1, - # name: "rln-relay-user-message-limit" - # .}: uint64 + b.rlnRelayConf.withEpochSizeSec(n.rlnEpochSizeSec) + b.withMaxMessageSize(n.maxMessageSize) + b.withProtectedShards(n.protectedShards) + b.withClusterId(n.clusterId) - # rlnEpochSizeSec* {. - # desc: - # "Epoch size in seconds used to rate limit RLN memberships. Default is 1 second.", - # defaultValue: 1, - # name: "rln-relay-epoch-sec" - # .}: uint64 + let clusterConf = toClusterConf(n.preset, n.clusterId).valueOr: + return err($error) - # maxMessageSize* {. - # desc: - # "Maximum message size. Accepted units: KiB, KB, and B. e.g. 1024KiB; 1500 B; etc.", - # defaultValue: DefaultMaxWakuMessageSizeStr, - # name: "max-msg-size" - # .}: string + b.withClusterConf(clusterConf) + b.withAgentString(n.agentString) - # case cmd* {.command, defaultValue: noCommand.}: StartUpCommand - # of inspectRlnDb: - # # have to change the name here since it counts as a duplicate, within noCommand - # treePath* {. - # desc: "Path to the RLN merkle tree sled db (https://github.com/spacejam/sled)", - # defaultValue: "", - # name: "rln-relay-tree-path" - # .}: string - # of generateRlnKeystore: - # execute* {. - # desc: "Runs the registration function on-chain. By default, a dry-run will occur", - # defaultValue: false, - # name: "execute" - # .}: bool - # of noCommand: - # ## Application-level configuration - # protectedShards* {. - # desc: - # "Shards and its public keys to be used for message validation, shard:pubkey. Argument may be repeated.", - # defaultValue: newSeq[ProtectedShard](0), - # name: "protected-shard" - # .}: seq[ProtectedShard] + if n.nodeKey.isSome(): + b.withNodeKey(n.nodeKey.get()) - # ## General node config - # preset* {. - # desc: - # "Network preset to use. 'twn' is The RLN-protected Waku Network (cluster 1).", - # defaultValue: "", - # name: "preset" - # .}: string + b.withP2pListenAddress(n.listenAddress) + b.withP2pTcpPort(n.tcpPort) + b.withPortsShift(n.portsShift) + b.withNatStrategy(n.nat) + b.withExtMultiAddrs(n.extMultiAddrs) + b.withExtMultiAddrsOnly(n.extMultiAddrsOnly) + b.withMaxConnections(n.maxConnections) - # clusterId* {. - # desc: - # "Cluster id that the node is running in. Node in a different cluster id is disconnected.", - # defaultValue: 0, - # name: "cluster-id" - # .}: uint16 + if n.maxRelayPeers.isSome(): + b.withMaxRelayPeers(n.maxRelayPeers.get()) - # agentString* {. - # defaultValue: "nwaku-" & external_config.git_version, - # desc: "Node agent string which is used as identifier in network", - # name: "agent-string" - # .}: string - - # nodekey* {.desc: "P2P node private key as 64 char hex string.", name: "nodekey".}: - # Option[PrivateKey] - - # listenAddress* {. - # defaultValue: defaultListenAddress(), - # desc: "Listening address for LibP2P (and Discovery v5, if enabled) traffic.", - # name: "listen-address" - # .}: IpAddress - - # tcpPort* {.desc: "TCP listening port.", defaultValue: 60000, name: "tcp-port".}: - # Port - - # portsShift* {. - # desc: "Add a shift to all port numbers.", defaultValue: 0, name: "ports-shift" - # .}: uint16 - - # nat* {. - # desc: - # "Specify method to use for determining public address. " & - # "Must be one of: any, none, upnp, pmp, extip:.", - # defaultValue: "any" - # .}: string - - # extMultiAddrs* {. - # desc: - # "External multiaddresses to advertise to the network. Argument may be repeated.", - # name: "ext-multiaddr" - # .}: seq[string] - - # extMultiAddrsOnly* {. - # desc: "Only announce external multiaddresses setup with --ext-multiaddr", - # defaultValue: false, - # name: "ext-multiaddr-only" - # .}: bool - - # maxConnections* {. - # desc: "Maximum allowed number of libp2p connections.", - # defaultValue: 50, - # name: "max-connections" - # .}: int - - # maxRelayPeers* {. - # desc: - # "Deprecated. Use relay-service-ratio instead. It represents the maximum allowed number of relay peers.", - # name: "max-relay-peers" - # .}: Option[int] - - # relayServiceRatio* {. - # desc: - # "This percentage ratio represents the relay peers to service peers. For example, 60:40, tells that 60% of the max-connections will be used for relay protocol and the other 40% of max-connections will be reserved for other service protocols (e.g., filter, lightpush, store, metadata, etc.)", - # name: "relay-service-ratio", - # defaultValue: "60:40" # 60:40 ratio of relay to service peers - # .}: string - - # colocationLimit* {. - # desc: - # "Max num allowed peers from the same IP. Set it to 0 to remove the limitation.", - # defaultValue: defaultColocationLimit(), - # name: "ip-colocation-limit" - # .}: int - - # peerStoreCapacity* {. - # desc: "Maximum stored peers in the peerstore.", name: "peer-store-capacity" - # .}: Option[int] - - # peerPersistence* {. - # desc: "Enable peer persistence.", defaultValue: false, name: "peer-persistence" - # .}: bool - - # ## DNS addrs config - # dnsAddrs* {. - # desc: "Enable resolution of `dnsaddr`, `dns4` or `dns6` multiaddrs", - # defaultValue: true, - # name: "dns-addrs" - # .}: bool - - # dnsAddrsNameServers* {. - # desc: - # "DNS name server IPs to query for DNS multiaddrs resolution. Argument may be repeated.", - # defaultValue: @[parseIpAddress("1.1.1.1"), parseIpAddress("1.0.0.1")], - # name: "dns-addrs-name-server" - # .}: seq[IpAddress] - - # dns4DomainName* {. - # desc: "The domain name resolving to the node's public IPv4 address", - # defaultValue: "", - # name: "dns4-domain-name" - # .}: string - - # ## Circuit-relay config - # isRelayClient* {. - # desc: - # """Set the node as a relay-client. - # Set it to true for nodes that run behind a NAT or firewall and - # hence would have reachability issues.""", - # defaultValue: false, - # name: "relay-client" - # .}: bool - - # ## Relay config - # relay* {. - # desc: "Enable relay protocol: true|false", defaultValue: true, name: "relay" - # .}: bool - - # relayPeerExchange* {. - # desc: "Enable gossipsub peer exchange in relay protocol: true|false", - # defaultValue: false, - # name: "relay-peer-exchange" - # .}: bool + b.withRelayServiceRatio(n.relayServiceRatio) + b.withColocationLimit(n.colocationLimit) + b.withPeerStoreCapacity(n.peerStoreCapacity) + b.peerPersistence(n.peerPersistence) + b.withDnsAddrs(n.dnsAddrs) + b.withDnsAddrsNameServers(n.dnsAddrsNameServers) + b.withDns4DomainNanme(n.dns4DomainName) + b.withCircuitRelayClient(n.isRelayClient) + b.withRelay(n.relay) + b.withRelayPeerExchange(n.relayPeerExchange) # relayShardedPeerManagement* {. # desc: diff --git a/waku/factory/internal_config.nim b/waku/factory/internal_config.nim index d6803edad..3e8c79971 100644 --- a/waku/factory/internal_config.nim +++ b/waku/factory/internal_config.nim @@ -149,26 +149,6 @@ proc networkConfiguration*(conf: WakuConf, clientId: string): NetConfigResult = return netConfigRes -# TODO: redefine in the right place -# proc applyPresetConfiguration*( -# srcConf: WakuNodeConf, wakuConfBuilder: var WakuConfBuilder -# ): void = -# var preset = srcConf.preset - -# 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." -# ) -# preset = "twn" - -# case toLowerAscii(preset) -# of "twn": -# let twnClusterConf = ClusterConf.TheWakuNetworkConf() - -# wakuConfBuilder.withClusterConf(twnClusterConf) -# else: -# discard - # TODO: numShardsInNetwork should be mandatory with autosharding, and unneeded otherwise proc getNumShardsInNetwork*(conf: WakuConf): uint32 = if conf.numShardsInNetwork != 0: diff --git a/waku/factory/networks_config.nim b/waku/factory/networks_config.nim index aea96970c..812a4f17a 100644 --- a/waku/factory/networks_config.nim +++ b/waku/factory/networks_config.nim @@ -1,6 +1,6 @@ {.push raises: [].} -# TODO: this file should be called cluster_conf.nim +# TODO: Rename this type to match file name type ClusterConf* = object maxMessageSize*: string diff --git a/waku/factory/waku_conf.nim b/waku/factory/waku_conf.nim index a595f833d..cea4070df 100644 --- a/waku/factory/waku_conf.nim +++ b/waku/factory/waku_conf.nim @@ -4,14 +4,17 @@ import libp2p/crypto/crypto, libp2p/multiaddress, secp256k1, - results, - waku/waku_rln_relay/rln_relay, - waku/waku_api/rest/builder, - waku/discovery/waku_discv5 + results -import ../common/logging +import + ../waku_rln_relay/rln_relay, + ../waku_api/rest/builder, + ../discovery/waku_discv5, + ../node/waku_metrics, + ../common/logging, + ./networks_config -export RlnRelayConf, RlnRelayCreds, RestServerConf, Discv5Conf +export RlnRelayConf, RlnRelayCreds, RestServerConf, Discv5Conf, MetricsServerConf logScope: topics = "waku conf" @@ -21,39 +24,40 @@ type DomainName* = distinct string # TODO: should be defined in validator_signed.nim and imported here -type ProtectedShard* = object +type ProtectedShard* {.requiresInit.} = object shard*: uint16 key*: secp256k1.SkPublicKey -type DnsDiscoveryConf* = object +type DnsDiscoveryConf* {.requiresInit.} = object enrTreeUrl*: string # TODO: should probably only have one set of name servers (see dnsaddrs) nameServers*: seq[IpAddress] -type StoreServiceConf* = object - legacy*: bool - dbURl*: string - dbVacuum*: bool - dbMigration*: bool - maxNumDbConnections*: int - retentionPolicy*: string - resume*: bool - -type FilterServiceConf* = object - maxPeersToServe*: uint32 - subscriptionTimeout*: uint16 - maxCriteria*: uint32 - -type StoreSyncConf* = object +type StoreSyncConf* {.requiresInit.} = object rangeSec*: uint32 intervalSec*: uint32 relayJitterSec*: uint32 -type WebSocketSecureConf* = object +type StoreServiceConf* {.requiresInit.} = object + dbMigration*: bool + dbURl*: string + dbVacuum*: bool + legacy*: bool + maxNumDbConnections*: int + retentionPolicy*: string + resume*: bool + storeSyncConf*: Option[StoreSyncConf] + +type FilterServiceConf* {.requiresInit.} = object + maxPeersToServe*: uint32 + subscriptionTimeout*: uint16 + maxCriteria*: uint32 + +type WebSocketSecureConf* {.requiresInit.} = object keyPath*: string certPath*: string -type WebSocketConf* = ref object +type WebSocketConf* = object port*: Port secureConf*: Option[WebSocketSecureConf] @@ -62,7 +66,8 @@ type WebSocketConf* = ref object ## In this object. A convenient `validate` method enables doing ## sanity checks beyond type enforcement. ## If `Option` is `some` it means the related protocol is enabled. -type WakuConf* = ref object # ref because `getRunningNetConfig` modifies it +type WakuConf* {.requiresInit.} = ref object + # ref because `getRunningNetConfig` modifies it nodeKey*: crypto.PrivateKey clusterId*: uint16 @@ -76,7 +81,7 @@ type WakuConf* = ref object # ref because `getRunningNetConfig` modifies it relay*: bool lightPush*: bool peerExchange*: bool - storeSyncConf*: Option[StoreSyncConf] + # TODO: remove relay peer exchange relayPeerExchange*: bool rendezvous*: bool @@ -85,13 +90,11 @@ type WakuConf* = ref object # ref because `getRunningNetConfig` modifies it discv5Conf*: Option[Discv5Conf] dnsDiscoveryConf*: Option[DnsDiscoveryConf] - filterServiceConf*: Option[FilterServiceConf] storeServiceConf*: Option[StoreServiceConf] - rlnRelayConf*: Option[RlnRelayConf] - restServerConf*: Option[RestServerConf] + metricsServerConf*: Option[MetricsServerConf] # TODO: could probably make it a `PeerRemoteInfo` staticNodes*: seq[string] @@ -100,7 +103,7 @@ type WakuConf* = ref object # ref because `getRunningNetConfig` modifies it remoteFilterNode*: Option[string] remotePeerExchangeNode*: Option[string] - maxMessageSizeBytes*: int + maxMessageSizeBytes*: uint64 logLevel*: logging.LogLevel logFormat*: logging.LogFormat @@ -156,8 +159,8 @@ proc log*(conf: WakuConf) = for i in conf.discv5Conf.get().bootstrapNodes: info "Configuration. Bootstrap nodes", node = i.string - if conf.rlnRelayConf.isSome: - var rlnRelayConf = conf.rlnRelayConf.geT() + if conf.rlnRelayConf.isSome(): + var rlnRelayConf = conf.rlnRelayConf.get() if rlnRelayConf.dynamic: info "Configuration. Validation", mechanism = "onchain rln", @@ -219,16 +222,26 @@ proc validateNoEmptyStrings(wakuConf: WakuConf): Result[void, string] = return err ("dnsDiscoveryConf.enrTreeUrl is an empty string") # TODO: rln relay config should validate itself - if wakuConf.rlnRelayConf.isSome and wakuConf.rlnRelayConf.get().creds.isSome: - let creds = wakuConf.rlnRelayConf.get().creds.get() - if isEmptyOrWhiteSpace(creds.path): - return err ( - "rlnRelayConf.creds.path is an empty string, set rlnRelayConf.creds it to none instead" - ) - if isEmptyOrWhiteSpace(creds.password): - return err ( - "rlnRelayConf.creds.password is an empty string, set rlnRelayConf.creds to none instead" - ) + if wakuConf.rlnRelayConf.isSome(): + let rlnRelayConf = wakuConf.rlnRelayConf.get() + + if isEmptyOrWhiteSpace(rlnRelayConf.treePath): + return err("rlnRelayConf.treepath is an empty string") + if isEmptyOrWhiteSpace(rlnRelayConf.ethClientAddress): + return err("rlnRelayConf.ethClientAddress is an empty string") + if isEmptyOrWhiteSpace(rlnRelayConf.ethContractAddress): + return err("rlnRelayConf.ethContractAddress is an empty string") + + if rlnRelayConf.creds.isSome(): + let creds = rlnRelayConf.creds.get() + if isEmptyOrWhiteSpace(creds.path): + return err ( + "rlnRelayConf.creds.path is an empty string, set rlnRelayConf.creds it to none instead" + ) + if isEmptyOrWhiteSpace(creds.password): + return err ( + "rlnRelayConf.creds.password is an empty string, set rlnRelayConf.creds to none instead" + ) return ok() diff --git a/waku/factory/waku_conf_builder.nim b/waku/factory/waku_conf_builder.nim index 8d52af54f..d316acd35 100644 --- a/waku/factory/waku_conf_builder.nim +++ b/waku/factory/waku_conf_builder.nim @@ -72,103 +72,77 @@ macro with(builderType: untyped, argName: untyped, argType: untyped) = ## RLN Relay Config Builder ## ############################## type RlnRelayConfBuilder = object - rlnRelay: Option[bool] - ethContractAddress: Option[string] + enabled: Option[bool] + chainId: Option[uint] + ethClientAddress: Option[string] + ethContractAddress: Option[string] credIndex: Option[uint] - credPath: Option[string] credPassword: Option[string] + credPath: Option[string] dynamic: Option[bool] epochSizeSec: Option[uint64] userMessageLimit: Option[uint64] - ethClientAddress: Option[string] + treePath: Option[string] proc init*(T: type RlnRelayConfBuilder): RlnRelayConfBuilder = RlnRelayConfBuilder() -with(RlnRelayConfbuilder, rlnRelay, bool) +with(RlnRelayConfbuilder, enabled, bool) + with(RlnRelayConfBuilder, chainId, uint) with(RlnRelayConfBuilder, credIndex, uint) -with(RlnRelayConfBuilder, credPath, string) with(RlnRelayConfBuilder, credPassword, string) +with(RlnRelayConfBuilder, credPath, string) with(RlnRelayConfBuilder, dynamic, bool) +with(RlnRelayConfBuilder, ethClientAddress, string) +with(RlnRelayConfBuilder, ethContractAddress, string) with(RlnRelayConfBuilder, epochSizeSec, uint64) with(RlnRelayConfBuilder, userMessageLimit, uint64) -with(RlnRelayConfBuilder, ethContractAddress, string) -with(RlnRelayConfBuilder, ethClientAddress, string) +with(RlnRelayConfBuilder, treePath, string) -proc build*(builder: RlnRelayConfBuilder): Result[Option[RlnRelayConf], string] = - if builder.rlnRelay.isNone or not builder.rlnRelay.get(): - info "RLN Relay is disabled" +proc build*(b: RlnRelayConfBuilder): Result[Option[RlnRelayConf], string] = + if not b.enabled.get(false): return ok(none(RlnRelayConf)) - let ethContractAddress = - if builder.ethContractAddress.isSome: - builder.ethContractAddress.get() - else: - return err("RLN Eth Contract Address is not specified") - - let chainId = - if builder.chainId.isSome: - builder.chainId.get() - else: - return err("RLN Relay Chain Id is not specified") + if b.chainId.isNone(): + return err("RLN Relay Chain Id is not specified") let creds = - if builder.credPath.isSome and builder.credPassword.isSome: - some( - RlnRelayCreds( - path: builder.credPath.get(), password: builder.credPassword.get() - ) - ) - elif builder.credPath.isSome and builder.credPassword.isNone: + if b.credPath.isSome() and b.credPassword.isSome(): + some(RlnRelayCreds(path: b.credPath.get(), password: b.credPassword.get())) + elif b.credPath.isSome() and b.credPassword.isNone(): return err("RLN Relay Credential Password is not specified but path is") - elif builder.credPath.isNone and builder.credPassword.isSome: + elif b.credPath.isNone() and b.credPassword.isSome(): return err("RLN Relay Credential Path is not specified but password is") else: none(RlnRelayCreds) - let credPassword = - if builder.credPassword.isSome: - builder.credPassword.get() - else: - return err("RLN Relay Credential Password is not specified") - - let dynamic = - if builder.dynamic.isSome: - builder.dynamic.get() - else: - return err("RLN Relay Dynamic is not specified") - - let epochSizeSec = - if builder.epochSizeSec.isSome: - builder.epochSizeSec.get() - else: - return err("RLN Epoch Size was not specified") - - let userMessageLimit = - if builder.userMessageLimit.isSome: - builder.userMessageLimit.get() - else: - return err("RLN Relay User Message Limit was not specified") - - let ethClientAddress = - if builder.ethClientAddress.isSome: - builder.ethClientAddress.get() - else: - return err("RLN Relay Eth Client Address was not specified") + if b.dynamic.isNone(): + return err("rlnRelay.dynamic is not specified") + if b.ethClientAddress.get("") == "": + return err("rlnRelay.ethClientAddress is not specified") + if b.ethContractAddress.get("") == "": + return err("rlnRelay.ethContractAddress is not specified") + if b.epochSizeSec.isNone(): + return err("rlnRelay.epochSizeSec is not specified") + if b.userMessageLimit.isNone(): + return err("rlnRelay.userMessageLimit is not specified") + if b.treePath.isNone(): + return err("rlnRelay.treePath is not specified") return ok( some( RlnRelayConf( - chainId: chainId, - credIndex: builder.credIndex, - dynamic: dynamic, - ethContractAddress: ethContractAddress, + chainId: b.chainId.get(), + credIndex: b.credIndex, creds: creds, - epochSizeSec: epochSizeSec, - userMessageLimit: userMessageLimit, - ethClientAddress: ethClientAddress, + dynamic: b.dynamic.get(), + ethClientAddress: b.ethClientAddress.get(), + ethContractAddress: b.ethContractAddress.get(), + epochSizeSec: b.epochSizeSec.get(), + userMessageLimit: b.userMessageLimit.get(), + treePath: b.treePath.get(), ) ) ) @@ -177,7 +151,7 @@ proc build*(builder: RlnRelayConfBuilder): Result[Option[RlnRelayConf], string] ## Filter Service Config Builder ## ################################### type FilterServiceConfBuilder = object - filter: Option[bool] + enabled: Option[bool] maxPeersToServe: Option[uint32] subscriptionTimeout: Option[uint16] maxCriteria: Option[uint32] @@ -185,41 +159,67 @@ type FilterServiceConfBuilder = object proc init(T: type FilterServiceConfBuilder): FilterServiceConfBuilder = FilterServiceConfBuilder() -with(FilterServiceConfBuilder, filter, bool) +with(FilterServiceConfBuilder, enabled, bool) with(FilterServiceConfBuilder, maxPeersToServe, uint32) with(FilterServiceConfBuilder, subscriptionTimeout, uint16) with(FilterServiceConfBuilder, maxCriteria, uint32) -proc build( - builder: FilterServiceConfBuilder -): Result[Option[FilterServiceConf], string] = - if builder.filter.get(false): +proc build(b: FilterServiceConfBuilder): Result[Option[FilterServiceConf], string] = + if b.enabled.get(false): return ok(none(FilterServiceConf)) - let maxPeersToServe = - if builder.maxPeersToServe.isSome: - builder.maxPeersToServe.get() - else: - return err("maxPeersToServe is not specified") - - let subscriptionTimeout = - if builder.subscriptionTimeout.isSome: - builder.subscriptionTimeout.get() - else: - return err("subscriptionTimeout is not specified") - - let maxCriteria = - if builder.maxCriteria.isSome: - builder.maxCriteria.get() - else: - return err("maxCriteria is not specified") + if b.maxPeersToServe.isNone(): + return err("filter.maxPeersToServe is not specified") + if b.subscriptionTimeout.isNone(): + return err("filter.subscriptionTimeout is not specified") + if b.maxCriteria.isNone(): + return err("filter.maxCriteria is not specified") return ok( some( FilterServiceConf( - maxPeersToServe: maxPeersToServe, - subscriptionTimeout: subscriptionTimeout, - maxCriteria: maxCriteria, + maxPeersToServe: b.maxPeersToServe.get(), + subscriptionTimeout: b.subscriptionTimeout.get(), + maxCriteria: b.maxCriteria.get(), + ) + ) + ) + +################################## +## Store Sync Config Builder ## +################################## +type StoreSyncConfBuilder = object + enabled: Option[bool] + + rangeSec: Option[uint32] + intervalSec: Option[uint32] + relayJitterSec: Option[uint32] + +proc init(T: type StoreSyncConfBuilder): StoreSyncConfBuilder = + StoreSyncConfBuilder() + +with(StoreSyncConfBuilder, enabled, bool) +with(StoreSyncConfBuilder, rangeSec, uint32) +with(StoreSyncConfBuilder, intervalSec, uint32) +with(StoreSyncConfBuilder, relayJitterSec, uint32) + +proc build(b: StoreSyncConfBuilder): Result[Option[StoreSyncConf], string] = + if b.enabled.get(false): + return ok(none(StoreSyncConf)) + + if b.rangeSec.isNone(): + return err "store.rangeSec is not specified" + if b.intervalSec.isNone(): + return err "store.intervalSec is not specified" + if b.relayJitterSec.isNone(): + return err "store.relayJitterSec is not specified" + + return ok( + some( + StoreSyncConf( + rangeSec: b.rangeSec.get(), + intervalSec: b.intervalSec.get(), + relayJitterSec: b.relayJitterSec.get(), ) ) ) @@ -228,116 +228,320 @@ proc build( ## Store Service Config Builder ## ################################## type StoreServiceConfBuilder = object - store: Option[bool] + enabled: Option[bool] + + dbMigration: Option[bool] + dbURl: Option[string] + dbVacuum: Option[bool] legacy: Option[bool] + maxNumDbConnections: Option[int] + retentionPolicy: Option[string] + resume: Option[bool] + storeSyncConf: StoreSyncConfBuilder proc init(T: type StoreServiceConfBuilder): StoreServiceConfBuilder = - StoreServiceConfBuilder() + StoreServiceConfBuilder(storeSyncConf: StoreSyncConfBuilder.init()) -with(StoreServiceConfBuilder, store, bool) +with(StoreServiceConfBuilder, enabled, bool) +with(StoreServiceConfBuilder, dbMigration, bool) +with(StoreServiceConfBuilder, dbURl, string) +with(StoreServiceConfBuilder, dbVacuum, bool) with(StoreServiceConfBuilder, legacy, bool) +with(StoreServiceConfBuilder, maxNumDbConnections, int) +with(StoreServiceConfBuilder, retentionPolicy, string) +with(StoreServiceConfBuilder, resume, bool) -proc build(builder: StoreServiceConfBuilder): Result[Option[StoreServiceConf], string] = - if builder.store.get(false): +proc build(b: StoreServiceConfBuilder): Result[Option[StoreServiceConf], string] = + if b.enabled.get(false): return ok(none(StoreServiceConf)) - return ok(some(StoreServiceConf(legacy: builder.legacy.get(true)))) + if b.dbMigration.isNone(): + return err "store.dbMigration is not specified" + if b.dbUrl.get("") == "": + return err "store.dbUrl is not specified" + if b.dbVacuum.isNone(): + return err "store.dbVacuum is not specified" + if b.legacy.isNone(): + return err "store.legacy is not specified" + if b.maxNumDbConnections.isNone(): + return err "store.maxNumDbConnections is not specified" + if b.retentionPolicy.get("") == "": + return err "store.retentionPolicy is not specified" + if b.resume.isNone(): + return err "store.resume is not specified" + + let storeSyncConf = b.storeSyncConf.build().valueOr: + return err("Store Sync Conf failed to build") + + return ok( + some( + StoreServiceConf( + dbMigration: b.dbMigration.get(), + dbURl: b.dbUrl.get(), + dbVacuum: b.dbVacuum.get(), + legacy: b.legacy.get(), + maxNumDbConnections: b.maxNumDbConnections.get(), + retentionPolicy: b.retentionPolicy.get(), + resume: b.resume.get(), + storeSyncConf: storeSyncConf, + ) + ) + ) + +################################ +## REST Server Config Builder ## +################################ +type RestServerConfBuilder = object + enabled: Option[bool] + + allowOrigin: seq[string] + listenAddress: Option[IpAddress] + port: Option[Port] + admin: Option[bool] + relayCacheCapacity: Option[uint32] + +proc init(T: type RestServerConfBuilder): RestServerConfBuilder = + RestServerConfBuilder() + +proc withAllowOrigin(builder: var RestServerConfBuilder, allowOrigin: seq[string]) = + builder.allowOrigin = concat(builder.allowOrigin, allowOrigin) + +with(RestServerConfBuilder, listenAddress, IpAddress) +with(RestServerConfBuilder, port, Port) +with(RestServerConfBuilder, admin, bool) +with(RestServerConfBuilder, relayCacheCapacity, uint32) + +proc build(b: RestServerConfBuilder): Result[Option[RestServerConf], string] = + if b.enabled.get(false): + return ok(none(RestServerConf)) + + if b.listenAddress.isNone(): + return err("restServer.listenAddress is not specified") + if b.port.isNone(): + return err("restServer.port is not specified") + if b.relayCacheCapacity.isNone(): + return err("restServer.relayCacheCapacity is not specified") + + return ok( + some( + RestServerConf( + allowOrigin: b.allowOrigin, + listenAddress: b.listenAddress.get(), + port: b.port.get(), + admin: b.admin.get(false), + relayCacheCapacity: b.relayCacheCapacity.get(), + ) + ) + ) + +################################## +## DNS Discovery Config Builder ## +################################## +type DnsDiscoveryConfBuilder = object + enabled: Option[bool] + enrTreeUrl: Option[string] + nameServers: seq[IpAddress] + +proc init(T: type DnsDiscoveryConfBuilder): DnsDiscoveryConfBuilder = + DnsDiscoveryConfBuilder() + +with(DnsDiscoveryConfBuilder, enabled, bool) +with(DnsDiscoveryConfBuilder, enrTreeUrl, string) + +proc withNameServers*(b: var DnsDiscoveryConfBuilder, nameServers: seq[IpAddress]) = + b.nameServers = concat(b.nameServers, nameServers) + +proc build(b: DnsDiscoveryConfBuilder): Result[Option[DnsDiscoveryConf], string] = + if not b.enabled.get(false): + return ok(none(DnsDiscoveryConf)) + + if b.nameServers.len == 0: + return err("dnsDiscovery.nameServers is not specified") + if b.enrTreeUrl.isNone(): + return err("dnsDiscovery.enrTreeUrl is not specified") + + return ok( + some(DnsDiscoveryConf(nameServers: b.nameServers, enrTreeUrl: b.enrTreeUrl.get())) + ) ########################### ## Discv5 Config Builder ## ########################### type Discv5ConfBuilder = object - discv5: Option[bool] - bootstrapNodes: Option[seq[string]] + enabled: Option[bool] + + bootstrapNodes: seq[string] + bitsPerHop: Option[int] + bucketIpLimit: Option[uint] + discv5Only: Option[bool] + enrAutoUpdate: Option[bool] + tableIpLimit: Option[uint] udpPort: Option[Port] proc init(T: type Discv5ConfBuilder): Discv5ConfBuilder = Discv5ConfBuilder() -with(Discv5ConfBuilder, discv5, bool) +with(Discv5ConfBuilder, enabled, bool) +with(Discv5ConfBuilder, bitsPerHop, int) +with(Discv5ConfBuilder, bucketIpLimit, uint) +with(Discv5ConfBuilder, discv5Only, bool) +with(Discv5ConfBuilder, enrAutoUpdate, bool) +with(Discv5ConfBuilder, tableIpLimit, uint) with(Discv5ConfBuilder, udpPort, uint16, Port) proc withBootstrapNodes(builder: var Discv5ConfBuilder, bootstrapNodes: seq[string]) = # TODO: validate ENRs? - builder.bootstrapNodes = some(bootstrapNodes) + builder.bootstrapNodes = concat(builder.bootstrapNodes, bootstrapNodes) -proc build(builder: Discv5ConfBuilder): Result[Option[Discv5Conf], string] = - if not builder.discv5.get(false): +proc build(b: Discv5ConfBuilder): Result[Option[Discv5Conf], string] = + if not b.enabled.get(false): return ok(none(Discv5Conf)) - # TODO: Do we need to ensure there are bootstrap nodes? - # Not sure discv5 is of any use without bootstrap nodes - # Confirmed: discv5 is useless without bootstrap node - config valid function? - let bootstrapNodes = builder.bootstrapNodes.get(@[]) + # Discv5 is useless without bootstrap nodes + if b.bootstrapNodes.len == 0: + return err("dicv5.bootstrapNodes is not specified") + if b.bitsPerHop.isNone(): + return err("discv5.bitsPerHop is not specified") + if b.bucketIpLimit.isNone(): + return err("discv5.bucketIpLimit is not specified") + if b.enrAutoUpdate.isNone(): + return err("discv5.enrAutoUpdate is not specified") + if b.tableIpLimit.isNone(): + return err("discv5.tableIpLimit is not specified") + if b.udpPort.isNone(): + return err("discv5.udpPort is not specified") - let udpPort = - if builder.udpPort.isSome: - builder.udpPort.get() - else: - return err("Discv5 UDP Port was not specified") - - return ok(some(Discv5Conf(bootstrapNodes: bootstrapNodes, udpPort: udpPort))) + return ok( + some( + Discv5Conf( + bootstrapNodes: b.bootstrapNodes, + bitsPerHop: b.bitsPerHop.get(), + bucketIpLimit: b.bucketIpLimit.get(), + discv5Only: b.discv5Only.get(false), + enrAutoUpdate: b.enrAutoUpdate.get(), + tableIpLimit: b.tableIpLimit.get(), + udpPort: b.udpPort.get(), + ) + ) + ) ############################## ## WebSocket Config Builder ## ############################## type WebSocketConfBuilder* = object - webSocketSupport: Option[bool] + enabled: Option[bool] webSocketPort: Option[Port] - webSocketSecureSupport: Option[bool] + secureEnabled: Option[bool] webSocketSecureKeyPath: Option[string] webSocketSecureCertPath: Option[string] proc init*(T: type WebSocketConfBuilder): WebSocketConfBuilder = WebSocketConfBuilder() -with(WebSocketConfBuilder, webSocketSupport, bool) -with(WebSocketConfBuilder, webSocketSecureSupport, bool) +with(WebSocketConfBuilder, enabled, bool) +with(WebSocketConfBuilder, secureEnabled, bool) with(WebSocketConfBuilder, webSocketPort, Port) with(WebSocketConfBuilder, webSocketSecureKeyPath, string) with(WebSocketConfBuilder, webSocketSecureCertPath, string) -proc build(builder: WebSocketConfBuilder): Result[Option[WebSocketConf], string] = - if not builder.webSocketSupport.get(false): +proc build(b: WebSocketConfBuilder): Result[Option[WebSocketConf], string] = + if not b.enabled.get(false): return ok(none(WebSocketConf)) - let webSocketPort = - if builder.webSocketPort.isSome: - builder.webSocketPort.get() - else: - warn "WebSocket Port is not specified, defaulting to 8000" - 8000.Port + if b.webSocketPort.isNone(): + return err("websocket.port is not specified") - if not builder.webSocketSecureSupport.get(false): + if not b.secureEnabled.get(false): return ok( - some(WebSocketConf(port: websocketPort, secureConf: none(WebSocketSecureConf))) + some( + WebSocketConf( + port: b.websocketPort.get(), secureConf: none(WebSocketSecureConf) + ) + ) ) - let webSocketSecureKeyPath = builder.webSocketSecureKeyPath.get("") - if webSocketSecureKeyPath == "": + if b.webSocketSecureKeyPath.get("") == "": return err("WebSocketSecure enabled but key path is not specified") - - let webSocketSecureCertPath = builder.webSocketSecureCertPath.get("") - if webSocketSecureCertPath == "": + if b.webSocketSecureCertPath.get("") == "": return err("WebSocketSecure enabled but cert path is not specified") return ok( some( WebSocketConf( - port: webSocketPort, + port: b.webSocketPort.get(), secureConf: some( WebSocketSecureConf( - keyPath: webSocketSecureKeyPath, certPath: webSocketSecureCertPath + keyPath: b.webSocketSecureKeyPath.get(), + certPath: b.webSocketSecureCertPath.get(), ) ), ) ) ) +################################### +## Metrics Server Config Builder ## +################################### +type MetricsServerConfBuilder = object + enabled: Option[bool] + + httpAddress: Option[IpAddress] + httpPort: Option[Port] + logging: Option[bool] + +proc init(T: type MetricsServerConfBuilder): MetricsServerConfBuilder = + MetricsServerConfBuilder() + +with(MetricsServerConfBuilder, enabled, bool) + +proc build(b: MetricsServerConfBuilder): Result[Option[MetricsServerConf], string] = + if b.enabled.get(false): + return ok(none(MetricsServerConf)) + + if b.httpAddress.isNone(): + return err("metricsServer.httpAddress is not specified") + if b.httpPort.isNone(): + return err("metricsServer.httpPort is not specified") + if b.logging.isNone(): + return err("metricsServer.logging is not specified") + + return ok( + some( + MetricsServerConf( + httpAddress: b.httpAddress.get(), + httpPort: b.httpPort.get(), + logging: b.logging.get(), + ) + ) + ) + +type MaxMessageSizeKind = enum + mmskNone + mmskStr + mmskInt + +type MaxMessageSize = object + case kind: MaxMessageSizeKind + of mmskNone: + discard + of mmskStr: + str: string + of mmskInt: + bytes: uint64 + ## `WakuConfBuilder` is a convenient tool to accumulate ## Config parameters to build a `WakuConfig`. ## It provides some type conversion, as well as applying ## defaults in an agnostic manner (for any usage of Waku node) +# +# TODO: Sub protocol builder (eg `StoreServiceConfBuilder` +# is be better defined in the protocol module (eg store) +# and apply good defaults from this protocol PoV and make the +# decision when the dev must specify a value vs when a default +# is fine to have. +# +# TODO: Add default to most values so that when a developer uses +# the builder, it works out-of-the-box type WakuConfBuilder* = object nodeKey: Option[PrivateKey] @@ -347,6 +551,16 @@ type WakuConfBuilder* = object protectedShards: Option[seq[ProtectedShard]] contentTopics: Option[seq[string]] + # Conf builders + dnsDiscoveryConf: DnsDiscoveryConfBuilder + discv5Conf*: Discv5ConfBuilder + filterServiceConf: FilterServiceConfBuilder + metricsServerConf*: MetricsServerConfBuilder + restServerConf*: RestServerConfBuilder + rlnRelayConf*: RlnRelayConfBuilder + storeServiceConf: StoreServiceConfBuilder + webSocketConf*: WebSocketConfBuilder + # End conf builders relay: Option[bool] lightPush: Option[bool] peerExchange: Option[bool] @@ -358,18 +572,14 @@ type WakuConfBuilder* = object clusterConf: Option[ClusterConf] - storeServiceConf: StoreServiceConfBuilder - filterServiceConf: FilterServiceConfBuilder - rlnRelayConf*: RlnRelayConfBuilder + staticNodes: seq[string] remoteStoreNode: Option[string] remoteLightPushNode: Option[string] remoteFilterNode: Option[string] remotePeerExchangeNode: Option[string] - maxMessageSizeBytes: Option[int] - - discv5Conf*: Discv5ConfBuilder + maxMessageSize: MaxMessageSize logLevel: Option[logging.LogLevel] logFormat: Option[logging.LogFormat] @@ -383,9 +593,8 @@ type WakuConfBuilder* = object extMultiAddrs: seq[string] extMultiAddrsOnly: Option[bool] - webSocketConf*: WebSocketConfBuilder - dnsAddrs: Option[bool] + # TODO: Option of an array is probably silly, instead, offer concat utility with `with` dnsAddrsNameServers: Option[seq[IpAddress]] peerPersistence: Option[bool] peerStoreCapacity: Option[int] @@ -399,12 +608,19 @@ type WakuConfBuilder* = object maxRelayPeers: Option[int] relayShardedPeerManagement: Option[bool] relayServiceRatio: Option[string] + circuitRelayClient: Option[bool] + keepAlive: Option[bool] + p2pReliabilityEnabled: Option[bool] proc init*(T: type WakuConfBuilder): WakuConfBuilder = WakuConfBuilder( - storeServiceConf: StoreServiceConfBuilder.init(), - rlnRelayConf: RlnRelayConfBuilder.init(), + dnsDiscoveryConf: DnsDiscoveryConfBuilder.init(), discv5Conf: Discv5ConfBuilder.init(), + filterServiceConf: FilterServiceConfBuilder.init(), + metricsServerConf: MetricsServerConfBuilder.init(), + restServerConf: RestServerConfBuilder.init(), + rlnRelayConf: RlnRelayConfBuilder.init(), + storeServiceConf: StoreServiceConfBuilder.init(), webSocketConf: WebSocketConfBuilder.init(), ) @@ -422,23 +638,34 @@ with(WakuConfBuilder, remoteStoreNode, string) with(WakuConfBuilder, remoteLightPushNode, string) with(WakuConfBuilder, remoteFilterNode, string) with(WakuConfBuilder, remotePeerExchangeNode, string) -with(WakuConfBuilder, maxMessageSizeBytes, int) with(WakuConfBuilder, dnsAddrs, bool) with(WakuConfBuilder, peerPersistence, bool) +with(WakuConfBuilder, peerStoreCapacity, int) with(WakuConfBuilder, maxConnections, int) with(WakuConfBuilder, dnsAddrsNameServers, seq[IpAddress]) with(WakuConfBuilder, logLevel, logging.LogLevel) with(WakuConfBuilder, logFormat, logging.LogFormat) +with(WakuConfBuilder, p2pTcpPort, Port) with(WakuConfBuilder, p2pTcpPort, uint16, Port) +with(WakuConfBuilder, portsShift, uint16) +with(WakuConfBuilder, p2pListenAddress, IpAddress) +with(WakuConfBuilder, extMultiAddrsOnly, bool) with(WakuConfBuilder, dns4DomainName, string, DomainName) +with(WakuConfBuilder, natStrategy, string, NatStrategy) with(WakuConfBuilder, agentString, string) with(WakuConfBuilder, colocationLimit, int) with(WakuConfBuilder, rateLimits, seq[string]) with(WakuConfBuilder, maxRelayPeers, int) with(WakuConfBuilder, relayServiceRatio, string) -proc withExtMultiAddr*(builder: var WakuConfBuilder, extMultiAddr: string) = - builder.extMultiAddrs.add(extMultiAddr) +proc withExtMultiAddrs*(builder: var WakuConfBuilder, extMultiAddrs: seq[string]) = + builder.extMultiAddrs = concat(builder.extMultiAddrs, extMultiAddrs) + +proc withMaxMessageSize*(builder: var WakuConfBuilder, maxMessageSizeBytes: uint64) = + builder.maxMessageSize = MaxMessageSize(kind: mmskInt, bytes: maxMessageSizeBytes) + +proc withMaxMessageSize*(builder: var WakuConfBuilder, maxMessageSize: string) = + builder.maxMessageSize = MaxMessageSize(kind: mmskStr, str: maxMessageSize) proc nodeKey( builder: WakuConfBuilder, rng: ref HmacDrbgContext @@ -452,7 +679,7 @@ proc nodeKey( return err("Failed to generate key: " & $error) return ok(nodeKey) -proc applyPresetConf(builder: var WakuConfBuilder) = +proc applyClusterConf(builder: var WakuConfBuilder) = # Apply cluster conf - values passed manually override cluster conf # Should be applied **first**, before individual values are pulled if builder.clusterConf.isNone: @@ -469,11 +696,11 @@ proc applyPresetConf(builder: var WakuConfBuilder) = if builder.relay.get(false) and clusterConf.rlnRelay: var rlnRelayConf = builder.rlnRelayConf - if rlnRelayConf.rlnRelay.isNone: - rlnRelayConf.withRlnRelay(true) + if rlnRelayConf.enabled.isNone: + rlnRelayConf.withEnabled(true) else: warn "RLN Relay was manually provided alongside a cluster conf", - used = rlnRelayConf.rlnRelay, discarded = clusterConf.rlnRelay + used = rlnRelayConf.enabled, discarded = clusterConf.rlnRelay if rlnRelayConf.ethContractAddress.isNone: rlnRelayConf.withEthContractAddress(clusterConf.rlnRelayEthContractAddress) @@ -508,12 +735,12 @@ proc applyPresetConf(builder: var WakuConfBuilder) = discarded = clusterConf.rlnRelayUserMessageLimit # End Apply relay parameters - if builder.maxMessageSizeBytes.isNone: - builder.maxMessageSizeBytes = - some(int(parseCorrectMsgSize(clusterConf.maxMessageSize))) - else: + case builder.maxMessageSize.kind + of mmskNone: + builder.withMaxMessageSize(parseCorrectMsgSize(clusterConf.maxMessageSize)) + of mmskStr, mmskInt: warn "Max Message Size was manually provided alongside a cluster conf", - used = builder.maxMessageSizeBytes, discarded = clusterConf.maxMessageSize + used = $builder.maxMessageSize, discarded = clusterConf.maxMessageSize if builder.numShardsInNetwork.isNone: builder.numShardsInNetwork = some(clusterConf.numShardsInNetwork) @@ -524,10 +751,10 @@ proc applyPresetConf(builder: var WakuConfBuilder) = if clusterConf.discv5Discovery: var discv5ConfBuilder = builder.discv5Conf - if discv5ConfBuilder.discv5.isNone: - discv5ConfBuilder.withDiscv5(clusterConf.discv5Discovery) + if discv5ConfBuilder.enabled.isNone: + discv5ConfBuilder.withEnabled(clusterConf.discv5Discovery) - if discv5ConfBuilder.bootstrapNodes.isNone and + if discv5ConfBuilder.bootstrapNodes.len == 0 and clusterConf.discv5BootstrapNodes.len > 0: discv5ConfBuilder.withBootstrapNodes(clusterConf.discv5BootstrapNodes) @@ -540,35 +767,35 @@ proc build*( ## default when it is opinionated. let relay = - if builder.relay.isSome: + if builder.relay.isSome(): builder.relay.get() else: warn "whether to mount relay is not specified, defaulting to not mounting" false let lightPush = - if builder.lightPush.isSome: + 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: + if builder.peerExchange.isSome(): builder.peerExchange.get() else: warn "whether to mount peerExchange is not specified, defaulting to not mounting" false let storeSync = - if builder.storeSync.isSome: + if builder.storeSync.isSome(): builder.storeSync.get() else: warn "whether to mount storeSync is not specified, defaulting to not mounting" false let rendezvous = - if builder.rendezvous.isSome: + if builder.rendezvous.isSome(): builder.rendezvous.get() else: warn "whether to mount rendezvous is not specified, defaulting to not mounting" @@ -576,25 +803,22 @@ proc build*( let relayPeerExchange = builder.relayPeerExchange.get(false) - applyPresetConf(builder) + applyClusterConf(builder) let nodeKey = ?nodeKey(builder, rng) - let clusterId = - if builder.clusterId.isSome: - builder.clusterId.get() - else: - return err("Cluster Id was not specified") + if builder.clusterId.isNone(): + return err("Cluster Id was not specified") let numShardsInNetwork = - if builder.numShardsInNetwork.isSome: + 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: + if builder.shards.isSome(): builder.shards.get() else: warn "shards not specified, defaulting to all shards in network" @@ -605,72 +829,86 @@ proc build*( let protectedShards = builder.protectedShards.get(@[]) let maxMessageSizeBytes = - if builder.maxMessageSizeBytes.isSome: - builder.maxMessageSizeBytes.get() + case builder.maxMessageSize.kind + of mmskInt: + builder.maxMessageSize.bytes + of mmskStr: + ?parseMsgSize(builder.maxMessageSize.str) else: return err("Max Message Size was not specified") let contentTopics = builder.contentTopics.get(@[]) + # Build sub-configsdnsDiscoveryConf let discv5Conf = builder.discv5Conf.build().valueOr: return err("Discv5 Conf building failed: " & $error) - let storeServiceConf = builder.storeServiceConf.build().valueOr: - return err("Store Conf building failed: " & $error) + let dnsDiscoveryConf = builder.dnsDiscoveryConf.build().valueOr: + return err("DNS Discovery Conf building failed: " & $error) let filterServiceConf = builder.filterServiceConf.build().valueOr: - return err("Filter Conf building failed: " & $error) + return err("Filter Service Conf building failed: " & $error) + + let metricsServerConf = builder.metricsServerConf.build().valueOr: + return err("Metrics Server Conf building failed: " & $error) + + let restServerConf = builder.restServerConf.build().valueOr: + return err("REST Server Conf building failed: " & $error) let rlnRelayConf = builder.rlnRelayConf.build().valueOr: return err("RLN Relay Conf building failed: " & $error) + let storeServiceConf = builder.storeServiceConf.build().valueOr: + return err("Store Conf building failed: " & $error) + let webSocketConf = builder.webSocketConf.build().valueOr: return err("WebSocket Conf building failed: " & $error) + # End - Build sub-configs let logLevel = - if builder.logLevel.isSome: + 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: + if builder.logFormat.isSome(): builder.logFormat.get() else: warn "Log Format not specified, defaulting to TEXT" logging.LogFormat.TEXT let natStrategy = - if builder.natStrategy.isSome: + if builder.natStrategy.isSome(): builder.natStrategy.get() else: warn "Nat Strategy is not specified, defaulting to none" "none".NatStrategy let p2pTcpPort = - if builder.p2pTcpPort.isSome: + if builder.p2pTcpPort.isSome(): builder.p2pTcpPort.get() else: warn "P2P Listening TCP Port is not specified, listening on 60000" 6000.Port let p2pListenAddress = - if builder.p2pListenAddress.isSome: + if builder.p2pListenAddress.isSome(): builder.p2pListenAddress.get() else: warn "P2P listening address not specified, listening on 0.0.0.0" (static parseIpAddress("0.0.0.0")) let portsShift = - if builder.portsShift.isSome: + if builder.portsShift.isSome(): builder.portsShift.get() else: warn "Ports Shift is not specified, defaulting to 0" 0.uint16 let dns4DomainName = - if builder.dns4DomainName.isSome: + if builder.dns4DomainName.isSome(): let d = builder.dns4DomainName.get() if d.string != "": some(d) @@ -686,41 +924,41 @@ proc build*( extMultiAddrs.add(m) let extMultiAddrsOnly = - if builder.extMultiAddrsOnly.isSome: + if builder.extMultiAddrsOnly.isSome(): builder.extMultiAddrsOnly.get() else: warn "Whether to only announce external multiaddresses is not specified, defaulting to false" false let dnsAddrs = - if builder.dnsAddrs.isSome: + if builder.dnsAddrs.isSome(): builder.dnsAddrs.get() else: warn "Whether to resolve DNS multiaddresses was not specified, defaulting to false." false let dnsAddrsNameServers = - if builder.dnsAddrsNameServers.isSome: + if builder.dnsAddrsNameServers.isSome(): builder.dnsAddrsNameServers.get() else: warn "DNS name servers IPs not provided, defaulting to Cloudflare's." @[parseIpAddress("1.1.1.1"), parseIpAddress("1.0.0.1")] let peerPersistence = - if builder.peerPersistence.isSome: + if builder.peerPersistence.isSome(): builder.peerPersistence.get() else: warn "Peer persistence not specified, defaulting to false" false let maxConnections = - if builder.maxConnections.isSome: + if builder.maxConnections.isSome(): builder.maxConnections.get() else: return err "Max Connections was not specified" let relayServiceRatio = - if builder.relayServiceRatio.isSome: + if builder.relayServiceRatio.isSome(): builder.relayServiceRatio.get() else: return err "Relay Service Ratio was not specified" @@ -736,10 +974,28 @@ proc build*( # TODO: is there a strategy for experimental features? delete vs promote let relayShardedPeerManagement = builder.relayShardedPeerManagement.get(false) + if builder.circuitRelayClient.isNone(): + return err("circuitRelayClient is not specified") + + if builder.keepAlive.isNone(): + return err("keepAlive is not specified") + + if builder.p2pReliabilityEnabled.isNone(): + return err("p2pReliabilityEnabled is not specified") + return ok( WakuConf( + # confs + storeServiceConf: storeServiceConf, + filterServiceConf: filterServiceConf, + discv5Conf: discv5Conf, + rlnRelayConf: rlnRelayConf, + metricsServerConf: metricsServerConf, + restServerConf: restServerConf, + dnsDiscoveryConf: dnsDiscoveryConf, + # end confs nodeKey: nodeKey, - clusterId: clusterId, + clusterId: builder.clusterId.get(), numShardsInNetwork: numShardsInNetwork, contentTopics: contentTopics, shards: shards, @@ -751,11 +1007,8 @@ proc build*( remoteStoreNode: builder.remoteStoreNode, remoteLightPushNode: builder.remoteLightPushNode, remoteFilterNode: builder.remoteFilterNode, - storeServiceConf: storeServiceConf, - filterServiceConf: filterServiceConf, + remotePeerExchangeNode: builder.remotePeerExchangeNode, relayPeerExchange: relayPeerExchange, - discv5Conf: discv5Conf, - rlnRelayConf: rlnRelayConf, maxMessageSizeBytes: maxMessageSizeBytes, logLevel: logLevel, logFormat: logFormat, @@ -777,5 +1030,10 @@ proc build*( maxRelayPeers: builder.maxRelayPeers, relayServiceRatio: relayServiceRatio, rateLimits: rateLimits, + circuitRelayClient: builder.circuitRelayClient.get(), + keepAlive: builder.keepAlive.get(), + staticNodes: builder.staticNodes, + relayShardedPeerManagement: relayShardedPeerManagement, + p2pReliabilityEnabled: builder.p2pReliabilityEnabled.get(), ) ) diff --git a/waku/node/waku_metrics.nim b/waku/node/waku_metrics.nim index c349f0849..ba61f6ef8 100644 --- a/waku/node/waku_metrics.nim +++ b/waku/node/waku_metrics.nim @@ -5,14 +5,18 @@ import ../waku_rln_relay/protocol_metrics as rln_metrics, ../utils/collector, ./peer_manager, - ./waku_node, - ../factory/external_config + ./waku_node const LogInterval = 10.minutes logScope: topics = "waku node metrics" +type MetricsServerConf* = object + httpAddress*: IpAddress + httpPort*: Port + logging*: bool + proc startMetricsLog*() = var logMetrics: CallbackFunc @@ -70,17 +74,15 @@ proc startMetricsServer( return ok(server) proc startMetricsServerAndLogging*( - conf: WakuNodeConf + conf: MetricsServerConf, portsShift: uint16 ): Result[MetricsHttpServerRef, string] = var metricsServer: MetricsHttpServerRef - if conf.metricsServer: - metricsServer = startMetricsServer( - conf.metricsServerAddress, Port(conf.metricsServerPort + conf.portsShift) - ).valueOr: - return - err("Starting metrics server failed. Continuing in current state:" & $error) + metricsServer = startMetricsServer( + conf.httpAddress, Port(conf.httpPort.uint16 + portsShift) + ).valueOr: + return err("Starting metrics server failed. Continuing in current state:" & $error) - if conf.metricsLogging: + if conf.logging: startMetricsLog() return ok(metricsServer) diff --git a/waku/waku_api/rest/builder.nim b/waku/waku_api/rest/builder.nim index dd4bb3715..1afacc92a 100644 --- a/waku/waku_api/rest/builder.nim +++ b/waku/waku_api/rest/builder.nim @@ -5,7 +5,6 @@ import presto import waku/waku_node, waku/discovery/waku_discv5, - waku/factory/external_config, waku/waku_api/message_cache, waku/waku_api/handlers, waku/waku_api/rest/server, @@ -33,7 +32,7 @@ restServerNotInstalledTab = newTable[string, string]() export WakuRestServerRef type RestServerConf* = object - allowOrigin*: string + allowOrigin*: seq[string] listenAddress*: IpAddress port*: Port admin*: bool diff --git a/waku/waku_api/rest/server.nim b/waku/waku_api/rest/server.nim index b8ad405c3..f16dfe83f 100644 --- a/waku/waku_api/rest/server.nim +++ b/waku/waku_api/rest/server.nim @@ -23,7 +23,7 @@ type ### Configuration -type RestServerConf* = object +type RestServerConf* {.requiresInit.} = object cacheSize*: Natural ## \ ## The maximum number of recently accessed states that are kept in \ diff --git a/waku/waku_rln_relay/rln_relay.nim b/waku/waku_rln_relay/rln_relay.nim index 8e6190540..4eb91e364 100644 --- a/waku/waku_rln_relay/rln_relay.nim +++ b/waku/waku_rln_relay/rln_relay.nim @@ -32,11 +32,11 @@ import logScope: topics = "waku rln_relay" -type RlnRelayCreds* = object +type RlnRelayCreds* {.requiresInit.} = object path*: string password*: string -type RlnRelayConf* = object of RootObj +type RlnRelayConf* {.requiresInit.} = object of RootObj dynamic*: bool credIndex*: Option[uint] ethContractAddress*: string @@ -47,7 +47,7 @@ type RlnRelayConf* = object of RootObj epochSizeSec*: uint64 userMessageLimit*: uint64 -type WakuRlnConfig* = object of RlnRelayConf +type WakuRlnConfig* {.requiresInit.} = object of RlnRelayConf onFatalErrorAction*: OnFatalErrorHandler proc createMembershipList*(