feat: adding json string support to bindings config (#2685)

This commit is contained in:
gabrielmer 2024-05-10 10:56:17 +02:00 committed by GitHub
parent 026d804a0d
commit be5471c6f3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 47 additions and 317 deletions

View File

@ -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,

View File

@ -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

View File

@ -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:

View File

@ -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.}