From be5471c6f355108f4641a3ce715192b46a5843c0 Mon Sep 17 00:00:00 2001 From: gabrielmer <101006718+gabrielmer@users.noreply.github.com> Date: Fri, 10 May 2024 10:56:17 +0200 Subject: [PATCH] feat: adding json string support to bindings config (#2685) --- examples/cbindings/waku_example.c | 10 +- library/waku_thread/config.nim | 272 ------------------ .../requests/node_lifecycle_request.nim | 30 +- waku/factory/external_config.nim | 52 ++-- 4 files changed, 47 insertions(+), 317 deletions(-) delete mode 100644 library/waku_thread/config.nim diff --git a/examples/cbindings/waku_example.c b/examples/cbindings/waku_example.c index 67e1bf277f..69e51687f7 100644 --- a/examples/cbindings/waku_example.c +++ b/examples/cbindings/waku_example.c @@ -269,13 +269,13 @@ int main(int argc, char** argv) { char jsonConfig[2048]; snprintf(jsonConfig, 2048, "{ \ - \"host\": \"%s\", \ - \"port\": %d, \ - \"key\": \"%s\", \ + \"listenAddress\": \"%s\", \ + \"tcpPort\": %d, \ + \"nodekey\": \"%s\", \ \"relay\": %s, \ \"store\": %s, \ - \"storeDbUrl\": \"%s\", \ - \"storeRetentionPolicy\": \"%s\", \ + \"storeMessageDbUrl\": \"%s\", \ + \"storeMessageRetentionPolicy\": \"%s\", \ \"storeMaxNumDbConnections\": %d \ }", cfgNode.host, cfgNode.port, diff --git a/library/waku_thread/config.nim b/library/waku_thread/config.nim deleted file mode 100644 index dc6c880d66..0000000000 --- a/library/waku_thread/config.nim +++ /dev/null @@ -1,272 +0,0 @@ -import std/[json, strformat, options] -import std/sequtils -import - libp2p/crypto/crypto, - libp2p/crypto/secp, - stew/shims/net, - ../../waku/waku_enr/capabilities, - ../../waku/common/utils/nat, - ../../waku/factory/external_config, - ../../waku/waku_core/message/default_values, - ../../waku/node/waku_node, - ../../waku/node/config, - ../events/json_base_event - -proc parsePrivateKey( - jsonNode: JsonNode, conf: var WakuNodeConf, errorResp: var string -): bool = - if not jsonNode.contains("key") or jsonNode["key"].kind == JsonNodeKind.JNull: - conf.nodekey = some(PrivateKey.random(Secp256k1, newRng()[]).tryGet()) - return true - - if jsonNode["key"].kind != JsonNodeKind.JString: - errorResp = "The node key should be a string." - return false - - let key = jsonNode["key"].getStr() - - try: - let skPrivKey = SkPrivateKey.init(crypto.fromHex(key)).tryGet() - conf.nodekey = some(crypto.PrivateKey(scheme: Secp256k1, skkey: skPrivKey)) - except CatchableError: - let msg = "Invalid node key: " & getCurrentExceptionMsg() - errorResp = msg - return false - - return true - -proc parseListenAddr( - jsonNode: JsonNode, conf: var WakuNodeConf, errorResp: var string -): bool = - var listenAddr: IpAddress - if not jsonNode.contains("host"): - conf.listenAddress = defaultListenAddress() - return true - - if jsonNode["host"].kind != JsonNodeKind.JString: - errorResp = "The node host should be a string." - return false - - let host = jsonNode["host"].getStr() - - try: - listenAddr = parseIpAddress(host) - except CatchableError: - let msg = "Invalid host IP address: " & getCurrentExceptionMsg() - errorResp = msg - return false - - return true - -proc parsePort(jsonNode: JsonNode, conf: var WakuNodeConf, errorResp: var string): bool = - if not jsonNode.contains("port"): - conf.tcpPort = Port(60000) - return true - - if jsonNode["port"].kind != JsonNodeKind.JInt: - errorResp = "The node port should be an integer." - return false - - conf.tcpPort = Port(jsonNode["port"].getInt()) - - return true - -proc parseRelay(jsonNode: JsonNode, conf: var WakuNodeConf, errorResp: var string): bool = - if not jsonNode.contains("relay"): - errorResp = "relay attribute is required" - return false - - if jsonNode["relay"].kind != JsonNodeKind.JBool: - errorResp = "The relay config param should be a boolean" - return false - - conf.relay = jsonNode["relay"].getBool() - - return true - -proc parseClusterId(jsonNode: JsonNode, conf: var WakuNodeConf, errorResp: var string): bool = - if not jsonNode.contains("relay"): - errorResp = "relay attribute is required" - return false - - if jsonNode.contains("clusterId"): - if jsonNode["clusterId"].kind != JsonNodeKind.JInt: - errorResp = "The clusterId config param should be an int" - return false - else: - conf.clusterId = uint32(jsonNode["clusterId"].getInt()) - - return true - -proc parseStore( - jsonNode: JsonNode, - conf: var WakuNodeConf, - errorResp: var string, -): bool = - if not jsonNode.contains("store"): - ## the store parameter is not required. By default is is disabled - conf.store = false - return true - - if jsonNode["store"].kind != JsonNodeKind.JBool: - errorResp = "The store config param should be a boolean" - return false - - conf.store = jsonNode["store"].getBool() - - if jsonNode.contains("storeNode"): - if jsonNode["storeNode"].kind != JsonNodeKind.JString: - errorResp = "The storeNode config param should be a string" - return false - - conf.storeNode = jsonNode["storeNode"].getStr() - - if jsonNode.contains("storeRetentionPolicy"): - if jsonNode["storeRetentionPolicy"].kind != JsonNodeKind.JString: - errorResp = "The storeRetentionPolicy config param should be a string" - return false - - conf.storeMessageRetentionPolicy = jsonNode["storeRetentionPolicy"].getStr() - - if jsonNode.contains("storeDbUrl"): - if jsonNode["storeDbUrl"].kind != JsonNodeKind.JString: - errorResp = "The storeDbUrl config param should be a string" - return false - - conf.storeMessageDbUrl = jsonNode["storeDbUrl"].getStr() - - if jsonNode.contains("storeVacuum"): - if jsonNode["storeVacuum"].kind != JsonNodeKind.JBool: - errorResp = "The storeVacuum config param should be a bool" - return false - - conf.storeMessageDbVacuum = jsonNode["storeVacuum"].getBool() - - if jsonNode.contains("storeDbMigration"): - if jsonNode["storeDbMigration"].kind != JsonNodeKind.JBool: - errorResp = "The storeDbMigration config param should be a bool" - return false - - conf.storeMessageDbMigration = jsonNode["storeDbMigration"].getBool() - - if jsonNode.contains("storeMaxNumDbConnections"): - if jsonNode["storeMaxNumDbConnections"].kind != JsonNodeKind.JInt: - errorResp = "The storeMaxNumDbConnections config param should be an int" - return false - - conf.storeMaxNumDbConnections = jsonNode["storeMaxNumDbConnections"].getInt() - - return true - -proc parseTopics(jsonNode: JsonNode, conf: var WakuNodeConf) = - if jsonNode.contains("pubsubTopics"): - for topic in jsonNode["pubsubTopics"].items: - conf.pubsubTopics.add(topic.getStr()) - else: - conf.pubsubTopics = @["/waku/2/default-waku/proto"] - -proc parseRLNRelay(jsonNode: JsonNode, conf: var WakuNodeConf, errorResp: var string): bool = - if not jsonNode.contains("rln-relay"): - return true - - let jsonNode = jsonNode["rln-relay"] - if not jsonNode.contains("enabled"): - errorResp = "rlnRelay.enabled attribute is required" - return false - - conf.rlnRelay = jsonNode["enabled"].getBool() - conf.rlnRelayCredPath = jsonNode{"cred-path"}.getStr() - conf.rlnRelayEthClientAddress = EthRpcUrl.parseCmdArg(jsonNode{"eth-client-address"}.getStr("http://localhost:8540")) - conf.rlnRelayEthContractAddress = jsonNode{"eth-contract-address"}.getStr() - conf.rlnRelayCredPassword = jsonNode{"cred-password"}.getStr() - conf.rlnRelayUserMessageLimit = uint64(jsonNode{"user-message-limit"}.getInt(1)) - conf.rlnEpochSizeSec = uint64(jsonNode{"epoch-sec"}.getInt(1)) - conf.rlnRelayCredIndex = some(uint(jsonNode{"membership-index"}.getInt())) - conf.rlnRelayDynamic = jsonNode{"dynamic"}.getBool() - conf.rlnRelayTreePath = jsonNode{"tree-path"}.getStr() - conf.rlnRelayBandwidthThreshold = jsonNode{"bandwidth-threshold"}.getInt() - - return true - -proc parseConfig*( - configNodeJson: string, - conf: var WakuNodeConf, - errorResp: var string, -): bool {.raises: [].} = - if configNodeJson.len == 0: - errorResp = "The configNodeJson is empty" - return false - - var jsonNode: JsonNode - try: - jsonNode = parseJson(configNodeJson) - except Exception, IOError, JsonParsingError: - errorResp = "Exception: " & getCurrentExceptionMsg() - return false - - # key - try: - if not parsePrivateKey(jsonNode, conf, errorResp): - return false - except Exception, KeyError: - errorResp = "Exception calling parsePrivateKey: " & getCurrentExceptionMsg() - return false - - # listenAddr - var listenAddr: IpAddress - try: - listenAddr = parseIpAddress("127.0.0.1") - if not parseListenAddr(jsonNode, conf, errorResp): - return false - except Exception, ValueError: - errorResp = "Exception calling parseIpAddress: " & getCurrentExceptionMsg() - return false - - # port - try: - if not parsePort(jsonNode, conf, errorResp): - return false - except Exception, ValueError: - errorResp = "Exception calling parsePort: " & getCurrentExceptionMsg() - return false - - # relay - try: - if not parseRelay(jsonNode, conf, errorResp): - return false - except Exception, KeyError: - errorResp = "Exception calling parseRelay: " & getCurrentExceptionMsg() - return false - - # clusterId - try: - if not parseClusterId(jsonNode, conf, errorResp): - return false - except Exception, KeyError: - errorResp = "Exception calling parseClusterId: " & getCurrentExceptionMsg() - return false - - # topics - try: - parseTopics(jsonNode, conf) - except Exception, KeyError: - errorResp = "Exception calling parseTopics: " & getCurrentExceptionMsg() - return false - - # store - try: - if not parseStore(jsonNode, conf, errorResp): - return false - except Exception, KeyError: - errorResp = "Exception calling parseStore: " & getCurrentExceptionMsg() - return false - - # rln - try: - if not parseRLNRelay(jsonNode, conf, errorResp): - return false - except Exception, KeyError: - errorResp = "Exception calling parseRLNRelay: " & getCurrentExceptionMsg() - return false - - return true diff --git a/library/waku_thread/inter_thread_communication/requests/node_lifecycle_request.nim b/library/waku_thread/inter_thread_communication/requests/node_lifecycle_request.nim index b7fb18e7a6..80636f1998 100644 --- a/library/waku_thread/inter_thread_communication/requests/node_lifecycle_request.nim +++ b/library/waku_thread/inter_thread_communication/requests/node_lifecycle_request.nim @@ -1,6 +1,6 @@ -import std/options -import std/sequtils -import chronos, chronicles, stew/results, stew/shims/net +import std/[options, sequtils, json, strutils] +import chronos, chronicles, stew/results, stew/shims/net, confutils, confutils/std/net + import ../../../../waku/common/enr/builder, ../../../../waku/waku_enr/capabilities, @@ -24,8 +24,7 @@ import ../../../../waku/factory/node_factory, ../../../../waku/factory/networks_config, ../../../events/[json_message_event, json_base_event], - ../../../alloc, - ../../config + ../../../alloc type NodeLifecycleMsgType* = enum CREATE_NODE @@ -49,19 +48,22 @@ proc destroyShared(self: ptr NodeLifecycleRequest) = deallocShared(self) proc createWaku(configJson: cstring): Future[Result[Waku, string]] {.async.} = - var conf: WakuNodeConf + var conf = defaultWakuNodeConf().valueOr: + return err("Failed creating node: " & error) + var errorResp: string try: - if not parseConfig($configJson, conf, errorResp): - return err(errorResp) - except Exception: - return err("exception calling parseConfig: " & getCurrentExceptionMsg()) + let jsonNode = parseJson($configJson) - # TODO: figure out how to extract default values from the config pragma - conf.nat = "any" - conf.maxConnections = 50.uint16 - conf.maxMessageSize = default_values.DefaultMaxWakuMessageSizeStr + for confField, confValue in fieldPairs(conf): + if jsonNode.contains(confField): + # Make sure string doesn't contain the leading or trailing " character + let formattedString = ($jsonNode[confField]).strip(chars = {'\"'}) + # Override conf field with the value set in the json-string + confValue = parseCmdArg(typeof(confValue), formattedString) + except Exception: + return err("exception parsing configuration: " & getCurrentExceptionMsg()) # The Waku Network config (cluster-id=1) if conf.clusterId == 1: diff --git a/waku/factory/external_config.nim b/waku/factory/external_config.nim index aa5bd7f381..4dd2f4c500 100644 --- a/waku/factory/external_config.nim +++ b/waku/factory/external_config.nim @@ -1,5 +1,5 @@ import - std/strutils, + std/[strutils, strformat], stew/results, chronos, regex, @@ -12,7 +12,8 @@ import libp2p/crypto/secp, libp2p/multiaddress, nimcrypto/utils, - secp256k1 + secp256k1, + json import ../common/confutils/envvar/defs as confEnvvarDefs, ../common/confutils/envvar/std/net as confEnvvarNet, @@ -613,6 +614,22 @@ proc parseCmdArg*(T: type crypto.PrivateKey, p: string): T = except CatchableError: raise newException(ValueError, "Invalid private key") +proc parseCmdArg*[T](_: type seq[T], s: string): seq[T] {.raises: [ValueError].} = + var + inputSeq: JsonNode + res: seq[T] = @[] + + try: + inputSeq = s.parseJson() + except Exception: + raise newException(ValueError, fmt"Could not parse sequence: {s}") + + for entry in inputSeq: + let formattedString = ($entry).strip(chars = {'\"'}) + res.add(parseCmdArg(T, formattedString)) + + return res + proc completeCmdArg*(T: type crypto.PrivateKey, val: string): seq[string] = return @[] @@ -632,12 +649,6 @@ proc parseCmdArg*(T: type ProtectedTopic, p: string): T = proc completeCmdArg*(T: type ProtectedTopic, val: string): seq[string] = return @[] -proc parseCmdArg*(T: type IpAddress, p: string): T = - try: - parseIpAddress(p) - except CatchableError: - raise newException(ValueError, "Invalid IP address") - proc completeCmdArg*(T: type IpAddress, val: string): seq[string] = return @[] @@ -649,21 +660,9 @@ proc defaultListenAddress*(): IpAddress = proc defaultColocationLimit*(): int = return DefaultColocationLimit -proc parseCmdArg*(T: type Port, p: string): T = - try: - Port(parseInt(p)) - except CatchableError: - raise newException(ValueError, "Invalid Port number") - proc completeCmdArg*(T: type Port, val: string): seq[string] = return @[] -proc parseCmdArg*(T: type Option[int], p: string): T = - try: - some(parseInt(p)) - except CatchableError: - raise newException(ValueError, "Invalid number") - proc completeCmdArg*(T: type ShardIdx, val: string): seq[ShardIdx] = return @[] @@ -673,12 +672,6 @@ proc parseCmdArg*(T: type ShardIdx, p: string): T = except CatchableError: raise newException(ValueError, "Invalid shard index") -proc parseCmdArg*(T: type Option[uint], p: string): T = - try: - some(parseUint(p)) - except CatchableError: - raise newException(ValueError, "Invalid unsigned integer") - proc completeCmdArg*(T: type EthRpcUrl, val: string): seq[string] = return @[] @@ -790,4 +783,11 @@ proc load*(T: type WakuNodeConf, version = ""): ConfResult[T] = except CatchableError: err(getCurrentExceptionMsg()) +proc defaultWakuNodeConf*(): ConfResult[WakuNodeConf] = + try: + let conf = WakuNodeConf.load(version = "", cmdLine = @[]) + return ok(conf) + except CatchableError: + return err("exception in defaultWakuNodeConf: " & getCurrentExceptionMsg()) + {.pop.}