mirror of
https://github.com/logos-messaging/logos-messaging-nim.git
synced 2026-08-25 23:41:06 +00:00
* remove entry-layer/mode fields from wakunode2's renamed kernel_cli_args.nim * logosdeliverynode's new cli_args.nim allows mode and optional protocol flags * logosdeliverynode rejects --mode with --entry-layer=kernel * delete EntryLayer from library API new/init (redundant with proc signatures)
1004 lines
32 KiB
Nim
1004 lines
32 KiB
Nim
## CLI configuration of logosdeliverynode.
|
|
|
|
import
|
|
std/[strutils, strformat, sequtils],
|
|
results,
|
|
chronicles,
|
|
chronos,
|
|
regex,
|
|
stew/endians2,
|
|
stint,
|
|
confutils,
|
|
confutils/defs,
|
|
confutils/std/net,
|
|
confutils/toml/defs as confTomlDefs,
|
|
confutils/toml/std/net as confTomlNet,
|
|
libp2p/crypto/curve25519,
|
|
libp2p/crypto/crypto,
|
|
libp2p/crypto/secp,
|
|
libp2p/multiaddress,
|
|
libp2p/multicodec,
|
|
nimcrypto/utils,
|
|
secp256k1,
|
|
json
|
|
|
|
import
|
|
logos_delivery/api/conf/modes,
|
|
logos_delivery/waku/factory/[waku_conf, conf_builder/conf_builder, networks_config],
|
|
logos_delivery/waku/common/[logging],
|
|
logos_delivery/waku/[
|
|
waku_enr,
|
|
node/peer_manager,
|
|
waku_core/topics/pubsub_topic,
|
|
waku_core/message/default_values,
|
|
waku_mix,
|
|
],
|
|
./entry_nodes
|
|
|
|
import ./envvar as confEnvvarDefs, ./envvar_net as confEnvvarNet
|
|
import ./kernel_cli_args
|
|
|
|
export modes, kernel_cli_args
|
|
|
|
logScope:
|
|
topics = "delivery cli args"
|
|
|
|
proc `$`*(u: EthRpcUrl): string =
|
|
## `load` and `defaultLogosDeliveryNodeConf` are generic over the conf type,
|
|
## so confutils expands its default-value printing in the caller's scope;
|
|
## this `$` must be exported for those expansions to resolve.
|
|
string(u)
|
|
|
|
type LogosDeliveryNodeConf* = object
|
|
## LogosDeliveryNodeConf is the kernel configuration with two additional
|
|
## fields.
|
|
##
|
|
## `entryLayer` lets logosdeliverynode users specify the layer-mount height,
|
|
## which is never guessed from given CLI parameters.
|
|
##
|
|
## `mode` is a messaging-only option. The kernel layer has no mode: its
|
|
## entire configuration must be given explicitly and it has its own
|
|
## per-config-option defaults which add up to what you'd expect for
|
|
## fleet-node use.
|
|
configFile* {.
|
|
desc: "Loads configuration from a TOML file (cmd-line parameters take precedence)",
|
|
name: "config-file"
|
|
.}: Opt[InputFile]
|
|
|
|
## Log configuration
|
|
logLevel* {.
|
|
desc:
|
|
"Sets the log level for process. Supported levels: TRACE, DEBUG, INFO, NOTICE, WARN, ERROR or FATAL",
|
|
defaultValue: logging.LogLevel.INFO,
|
|
name: "log-level"
|
|
.}: logging.LogLevel
|
|
|
|
logFormat* {.
|
|
desc:
|
|
"Specifies what kind of logs should be written to stdout. Supported formats: TEXT, JSON",
|
|
defaultValue: logging.LogFormat.TEXT,
|
|
name: "log-format"
|
|
.}: logging.LogFormat
|
|
|
|
rlnRelayCredPath* {.
|
|
desc: "The path for persisting rln-relay credential",
|
|
defaultValue: "",
|
|
name: "rln-relay-cred-path"
|
|
.}: string
|
|
|
|
ethClientUrls* {.
|
|
desc:
|
|
"HTTP address of an Ethereum testnet client e.g., http://localhost:8540/. Argument may be repeated.",
|
|
defaultValue: @[EthRpcUrl("http://localhost:8540/")],
|
|
name: "rln-relay-eth-client-address"
|
|
.}: seq[EthRpcUrl]
|
|
|
|
rlnRelayEthContractAddress* {.
|
|
desc: "Address of membership contract on an Ethereum testnet.",
|
|
defaultValue: "",
|
|
name: "rln-relay-eth-contract-address"
|
|
.}: string
|
|
|
|
rlnRelayChainId* {.
|
|
desc:
|
|
"Chain ID of the provided contract (optional, will fetch from RPC provider if not used)",
|
|
defaultValue: 0,
|
|
name: "rln-relay-chain-id"
|
|
.}: uint
|
|
|
|
rlnRelayCredPassword* {.
|
|
desc: "Password for encrypting RLN credentials",
|
|
defaultValue: "",
|
|
name: "rln-relay-cred-password"
|
|
.}: string
|
|
|
|
rlnRelayEthPrivateKey* {.
|
|
desc: "Private key for broadcasting transactions",
|
|
defaultValue: "",
|
|
name: "rln-relay-eth-private-key"
|
|
.}: string
|
|
|
|
# Opt-typed; desc states the default since the CLI can't auto-show it for Opt.none().
|
|
rlnRelayUserMessageLimit* {.
|
|
desc:
|
|
"Set a user message limit for the rln membership registration. Must be a positive integer. Default is " &
|
|
$DefaultRlnRelayUserMessageLimit & ".",
|
|
defaultValue: Opt.none(uint64),
|
|
name: "rln-relay-user-message-limit"
|
|
.}: Opt[uint64]
|
|
|
|
# Opt-typed; desc states the default since the CLI can't auto-show it for Opt.none().
|
|
rlnEpochSizeSec* {.
|
|
desc:
|
|
"Epoch size in seconds used to rate limit RLN memberships. Default is " &
|
|
$DefaultRlnRelayEpochSizeSec & " second.",
|
|
defaultValue: Opt.none(uint64),
|
|
name: "rln-relay-epoch-sec"
|
|
.}: Opt[uint64]
|
|
|
|
maxMessageSize* {.
|
|
desc:
|
|
"Maximum message size. Accepted units: KiB, KB, and B. e.g. 1024KiB; 1500 B; etc.",
|
|
defaultValue: DefaultMaxWakuMessageSizeStr,
|
|
name: "max-msg-size"
|
|
.}: string
|
|
|
|
case cmd* {.command, defaultValue: noCommand.}: StartUpCommand
|
|
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]
|
|
|
|
## General node config
|
|
preset* {.
|
|
desc:
|
|
"Network preset to use. 'twn' is The RLN-protected Waku Network (cluster 1). 'logos.dev' is the Logos Dev Network (cluster 3). 'logos.test' is the Logos Test Network (cluster 2). 'status.prod' is the Status Production Network (cluster 16, RLN off, auto-sharding with 1 shard). Overrides other values.",
|
|
defaultValue: "",
|
|
name: "preset"
|
|
.}: string
|
|
|
|
entryLayer* {.
|
|
desc:
|
|
"Top API layer to run: kernel (transport only), messaging, or channels (messaging + reliable channels).",
|
|
defaultValue: EntryLayer.channels,
|
|
name: "entry-layer"
|
|
.}: EntryLayer
|
|
|
|
mode* {.
|
|
desc:
|
|
"Node operating mode: Edge (client-only) or Core (full service node). Applies to --entry-layer=messaging|channels; rejected for --entry-layer=kernel. Default is Core.",
|
|
defaultValue: Opt.none(LogosDeliveryMode),
|
|
name: "mode"
|
|
.}: Opt[LogosDeliveryMode]
|
|
|
|
# Opt-typed; desc states the default since the CLI can't auto-show it for Opt.none().
|
|
clusterId* {.
|
|
desc: static(
|
|
"Cluster id that the node is running in. Node in a different cluster id is disconnected. Default is " &
|
|
$DefaultClusterId & "."
|
|
),
|
|
defaultValue: Opt.none(uint16),
|
|
name: "cluster-id"
|
|
.}: Opt[uint16]
|
|
|
|
agentString* {.
|
|
defaultValue: DefaultAgentString,
|
|
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".}:
|
|
Opt[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:<IP>.",
|
|
defaultValue: DefaultCLINat,
|
|
name: "nat"
|
|
.}: 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. (Default: 150) that's recommended value for better connectivity",
|
|
defaultValue: 150,
|
|
name: "max-connections"
|
|
.}: 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.)",
|
|
defaultValue: "50:50",
|
|
name: "relay-service-ratio"
|
|
.}: 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"
|
|
.}: Opt[int]
|
|
|
|
peerPersistence* {.
|
|
desc: "Enable peer persistence.", defaultValue: false, name: "peer-persistence"
|
|
.}: bool
|
|
|
|
## DNS addrs config
|
|
dnsAddrsNameServers* {.
|
|
desc:
|
|
"DNS name server IPs to query for DNS multiaddrs resolution. Argument may be repeated.",
|
|
defaultValue: @[
|
|
IpAddress(family: IpAddressFamily.IPv4, address_v4: [1'u8, 1, 1, 1]),
|
|
IpAddress(family: IpAddressFamily.IPv4, address_v4: [1'u8, 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. Default is true.",
|
|
defaultValue: Opt.none(bool),
|
|
name: "relay"
|
|
.}: Opt[bool]
|
|
|
|
relayPeerExchange* {.
|
|
desc: "Enable gossipsub peer exchange in relay protocol: true|false",
|
|
defaultValue: false,
|
|
name: "relay-peer-exchange"
|
|
.}: bool
|
|
|
|
relayShardedPeerManagement* {.
|
|
desc:
|
|
"Enable experimental shard aware peer manager for relay protocol: true|false",
|
|
defaultValue: false,
|
|
name: "relay-shard-manager"
|
|
.}: bool
|
|
|
|
# Opt-typed; desc states the default since the CLI can't auto-show it for Opt.none().
|
|
rlnRelay* {.
|
|
desc:
|
|
"Enable spam protection through rln-relay: true|false. Default is " &
|
|
$DefaultRlnRelayEnabled & ".",
|
|
defaultValue: Opt.none(bool),
|
|
name: "rln-relay"
|
|
.}: Opt[bool]
|
|
|
|
rlnRelayCredIndex* {.
|
|
desc: "the index of the onchain commitment to use",
|
|
name: "rln-relay-membership-index"
|
|
.}: Opt[uint]
|
|
|
|
rlnRelayDynamic* {.
|
|
desc: "Enable waku-rln-relay with on-chain dynamic group management: true|false.",
|
|
defaultValue: Opt.none(bool),
|
|
name: "rln-relay-dynamic"
|
|
.}: Opt[bool]
|
|
|
|
entryNodes* {.
|
|
desc:
|
|
"Entry node address (enrtree:, enr:, or multiaddr). " &
|
|
"Automatically classified and distributed to DNS discovery, discv5 bootstrap, " &
|
|
"and static nodes. Argument may be repeated.",
|
|
name: "entry-node"
|
|
.}: seq[string]
|
|
|
|
staticnodes* {.
|
|
desc: "Peer multiaddr to directly connect with. Argument may be repeated.",
|
|
name: "staticnode"
|
|
.}: seq[string]
|
|
|
|
keepAlive* {.
|
|
desc:
|
|
"Deprecated since >=v0.37. This param is ignored and keep alive is always active",
|
|
defaultValue: true,
|
|
name: "keep-alive"
|
|
.}: bool
|
|
|
|
numShardsInNetwork* {.
|
|
desc:
|
|
"Enables autosharding and set number of shards in the cluster, set to `0` to use static sharding",
|
|
defaultValue: 0,
|
|
name: "num-shards-in-network"
|
|
.}: uint16
|
|
|
|
shards* {.
|
|
desc:
|
|
"Shards index to subscribe to [0..NUM_SHARDS_IN_NETWORK-1]. Argument may be repeated. Subscribes to all shards by default in auto-sharding, no shard for static sharding",
|
|
name: "shard"
|
|
.}: seq[uint16]
|
|
|
|
contentTopics* {.
|
|
desc: "Default content topic to subscribe to. Argument may be repeated.",
|
|
name: "content-topic"
|
|
.}: seq[string]
|
|
|
|
## Store and message store config
|
|
store* {.
|
|
desc: "Enable/disable waku store protocol. Default is false.",
|
|
defaultValue: Opt.none(bool),
|
|
name: "store"
|
|
.}: Opt[bool]
|
|
|
|
storenode* {.
|
|
desc: "Peer multiaddress to query for storage",
|
|
defaultValue: "",
|
|
name: "storenode"
|
|
.}: string
|
|
|
|
storeMessageRetentionPolicy* {.
|
|
desc:
|
|
"Message store retention policy. Multiple policies may be provided as a semicolon-separated string and are applied as a union. Time retention policy: 'time:<seconds>'. Capacity retention policy: 'capacity:<count>'. Size retention policy: 'size:<xMB/xGB>'. Set to 'none' to disable. Example: 'time:3600;size:1GB;capacity:100'.",
|
|
defaultValue: "time:" & $2.days.seconds,
|
|
name: "store-message-retention-policy"
|
|
.}: string
|
|
|
|
storeMessageDbUrl* {.
|
|
desc: "The database connection URL for peristent storage.",
|
|
defaultValue: "sqlite://store.sqlite3",
|
|
name: "store-message-db-url"
|
|
.}: string
|
|
|
|
storeMessageDbVacuum* {.
|
|
desc:
|
|
"Enable database vacuuming at start. Only supported by SQLite database engine.",
|
|
defaultValue: false,
|
|
name: "store-message-db-vacuum"
|
|
.}: bool
|
|
|
|
storeMessageDbMigration* {.
|
|
desc: "Enable database migration at start.",
|
|
defaultValue: true,
|
|
name: "store-message-db-migration"
|
|
.}: bool
|
|
|
|
storeMaxNumDbConnections* {.
|
|
desc: "Maximum number of simultaneous Postgres connections.",
|
|
defaultValue: 50,
|
|
name: "store-max-num-db-connections"
|
|
.}: int
|
|
|
|
storeResume* {.
|
|
desc: "Enable store resume functionality",
|
|
defaultValue: false,
|
|
name: "store-resume"
|
|
.}: bool
|
|
|
|
## Sync config
|
|
storeSync* {.
|
|
desc: "Enable store sync protocol: true|false",
|
|
defaultValue: false,
|
|
name: "store-sync"
|
|
.}: bool
|
|
|
|
storeSyncInterval* {.
|
|
desc: "Interval between store sync attempts. In seconds.",
|
|
defaultValue: 300, # 5 minutes
|
|
name: "store-sync-interval"
|
|
.}: uint32
|
|
|
|
storeSyncRange* {.
|
|
desc: "Amount of time to sync. In seconds.",
|
|
defaultValue: 3600, # 1 hours
|
|
name: "store-sync-range"
|
|
.}: uint32
|
|
|
|
storeSyncRelayJitter* {.
|
|
hidden,
|
|
desc: "Time offset to account for message propagation jitter. In seconds.",
|
|
defaultValue: 20,
|
|
name: "store-sync-relay-jitter"
|
|
.}: uint32
|
|
|
|
## Filter config
|
|
filter* {.
|
|
desc: "Enable filter protocol: true|false. Default is true.",
|
|
defaultValue: Opt.none(bool),
|
|
name: "filter"
|
|
.}: Opt[bool]
|
|
|
|
filternode* {.
|
|
desc: "Peer multiaddr to request content filtering of messages.",
|
|
defaultValue: "",
|
|
name: "filternode"
|
|
.}: string
|
|
|
|
filterSubscriptionTimeout* {.
|
|
desc:
|
|
"Timeout for filter subscription without ping or refresh it, in seconds. Only for v2 filter protocol.",
|
|
defaultValue: 300, # 5 minutes
|
|
name: "filter-subscription-timeout"
|
|
.}: uint16
|
|
|
|
filterMaxPeersToServe* {.
|
|
desc: "Maximum number of peers to serve at a time. Only for v2 filter protocol.",
|
|
defaultValue: 1000,
|
|
name: "filter-max-peers-to-serve"
|
|
.}: uint32
|
|
|
|
filterMaxCriteria* {.
|
|
desc:
|
|
"Maximum number of pubsub- and content topic combination per peers at a time. Only for v2 filter protocol.",
|
|
defaultValue: 1000,
|
|
name: "filter-max-criteria"
|
|
.}: uint32
|
|
|
|
## Lightpush config
|
|
lightpush* {.
|
|
desc: "Enable lightpush protocol: true|false. Default is true.",
|
|
defaultValue: Opt.none(bool),
|
|
name: "lightpush"
|
|
.}: Opt[bool]
|
|
|
|
lightpushnode* {.
|
|
desc: "Peer multiaddr to request lightpush of published messages.",
|
|
defaultValue: "",
|
|
name: "lightpushnode"
|
|
.}: string
|
|
|
|
## REST HTTP config
|
|
rest* {.
|
|
desc: "Enable Waku REST HTTP server: true|false",
|
|
defaultValue: false,
|
|
name: "rest"
|
|
.}: bool
|
|
|
|
restAddress* {.
|
|
desc: "Listening address of the REST HTTP server.",
|
|
defaultValue:
|
|
IpAddress(family: IpAddressFamily.IPv4, address_v4: [127'u8, 0, 0, 1]),
|
|
name: "rest-address"
|
|
.}: IpAddress
|
|
|
|
restPort* {.
|
|
desc: "Listening port of the REST HTTP server.",
|
|
defaultValue: 8645,
|
|
name: "rest-port"
|
|
.}: uint16
|
|
|
|
restRelayCacheCapacity* {.
|
|
desc: "Capacity of the Relay REST API message cache.",
|
|
defaultValue: 50,
|
|
name: "rest-relay-cache-capacity"
|
|
.}: uint32
|
|
|
|
restAdmin* {.
|
|
desc: "Enable access to REST HTTP Admin API: true|false",
|
|
defaultValue: false,
|
|
name: "rest-admin"
|
|
.}: bool
|
|
|
|
restAllowOrigin* {.
|
|
desc:
|
|
"Allow cross-origin requests from the specified origin." &
|
|
"Argument may be repeated." & "Wildcards: * or ? allowed." &
|
|
"Ex.: \"localhost:*\" or \"127.0.0.1:8080\"",
|
|
defaultValue: newSeq[string](),
|
|
name: "rest-allow-origin"
|
|
.}: seq[string]
|
|
|
|
## Metrics config
|
|
metricsServer* {.
|
|
desc: "Enable the metrics server: true|false",
|
|
defaultValue: false,
|
|
name: "metrics-server"
|
|
.}: bool
|
|
|
|
metricsServerAddress* {.
|
|
desc: "Listening address of the metrics server.",
|
|
defaultValue:
|
|
IpAddress(family: IpAddressFamily.IPv4, address_v4: [127'u8, 0, 0, 1]),
|
|
name: "metrics-server-address"
|
|
.}: IpAddress
|
|
|
|
metricsServerPort* {.
|
|
desc: "Listening HTTP port of the metrics server.",
|
|
defaultValue: 8008,
|
|
name: "metrics-server-port"
|
|
.}: uint16
|
|
|
|
metricsLogging* {.
|
|
desc: "Enable metrics logging: true|false",
|
|
defaultValue: true,
|
|
name: "metrics-logging"
|
|
.}: bool
|
|
|
|
## DNS discovery config
|
|
dnsDiscovery* {.
|
|
desc:
|
|
"Deprecated, please set dns-discovery-url instead. Enable discovering nodes via DNS",
|
|
defaultValue: false,
|
|
name: "dns-discovery"
|
|
.}: bool
|
|
|
|
dnsDiscoveryUrl* {.
|
|
desc:
|
|
"URL for DNS node list in format 'enrtree://<key>@<fqdn>', enables DNS Discovery",
|
|
defaultValue: "",
|
|
name: "dns-discovery-url"
|
|
.}: string
|
|
|
|
## Discovery v5 config
|
|
discv5Discovery* {.
|
|
desc: "Enable discovering nodes via Node Discovery v5. Default is true.",
|
|
defaultValue: Opt.none(bool),
|
|
name: "discv5-discovery"
|
|
.}: Opt[bool]
|
|
|
|
discv5UdpPort* {.
|
|
desc: "Listening UDP port for Node Discovery v5.",
|
|
defaultValue: 9000,
|
|
name: "discv5-udp-port"
|
|
.}: Port
|
|
|
|
discv5BootstrapNodes* {.
|
|
desc:
|
|
"Text-encoded ENR for bootstrap node. Used when connecting to the network. Argument may be repeated.",
|
|
name: "discv5-bootstrap-node"
|
|
.}: seq[string]
|
|
|
|
discv5EnrAutoUpdate* {.
|
|
desc:
|
|
"Discovery can automatically update its ENR with the IP address " &
|
|
"and UDP port as seen by other nodes it communicates with. " &
|
|
"This option allows to enable/disable this functionality",
|
|
defaultValue: false,
|
|
name: "discv5-enr-auto-update"
|
|
.}: bool
|
|
|
|
discv5TableIpLimit* {.
|
|
hidden,
|
|
desc: "Maximum amount of nodes with the same IP in discv5 routing tables",
|
|
defaultValue: 10,
|
|
name: "discv5-table-ip-limit"
|
|
.}: uint
|
|
|
|
discv5BucketIpLimit* {.
|
|
hidden,
|
|
desc: "Maximum amount of nodes with the same IP in discv5 routing table buckets",
|
|
defaultValue: 2,
|
|
name: "discv5-bucket-ip-limit"
|
|
.}: uint
|
|
|
|
discv5BitsPerHop* {.
|
|
hidden,
|
|
desc: "Kademlia's b variable, increase for less hops per lookup",
|
|
defaultValue: 1,
|
|
name: "discv5-bits-per-hop"
|
|
.}: int
|
|
|
|
## waku peer exchange config
|
|
peerExchange* {.
|
|
desc:
|
|
"Enable waku peer exchange protocol (responder side): true|false. Default is true.",
|
|
defaultValue: Opt.none(bool),
|
|
name: "peer-exchange"
|
|
.}: Opt[bool]
|
|
|
|
peerExchangeNode* {.
|
|
desc:
|
|
"Peer multiaddr to send peer exchange requests to. (enables peer exchange protocol requester side)",
|
|
defaultValue: "",
|
|
name: "peer-exchange-node"
|
|
.}: string
|
|
|
|
## Rendez vous
|
|
rendezvous* {.
|
|
desc: "Enable waku rendezvous discovery server. Default is true.",
|
|
defaultValue: Opt.none(bool),
|
|
name: "rendezvous"
|
|
.}: Opt[bool]
|
|
|
|
#Mix config
|
|
# Opt-typed; desc states the default since the CLI can't auto-show it for Opt.none().
|
|
mix* {.
|
|
desc: "Enable mix protocol: true|false. Default is " & $DefaultMix & ".",
|
|
defaultValue: Opt.none(bool),
|
|
name: "mix"
|
|
.}: Opt[bool]
|
|
|
|
mixkey* {.
|
|
desc:
|
|
"ED25519 private key as 64 char hex string , without 0x. If not provided, a random key will be generated.",
|
|
name: "mixkey"
|
|
.}: Opt[string]
|
|
|
|
mixnodes* {.
|
|
desc:
|
|
"Multiaddress and mix-key of mix node to be statically specified in format multiaddr:mixPubKey. Argument may be repeated.",
|
|
name: "mixnode"
|
|
.}: seq[MixNodePubInfo]
|
|
|
|
# Kademlia Discovery config
|
|
# Opt-typed; desc states the default since the CLI can't auto-show it for Opt.none().
|
|
enableKadDiscovery* {.
|
|
desc:
|
|
"Enable extended kademlia discovery. Can be enabled without bootstrap nodes for the first node in the network. Default is " &
|
|
$DefaultKadEnabled & ".",
|
|
defaultValue: Opt.none(bool),
|
|
name: "enable-kad-discovery"
|
|
.}: Opt[bool]
|
|
|
|
kadBootstrapNodes* {.
|
|
desc:
|
|
"Peer multiaddr for kademlia discovery bootstrap node (must include /p2p/<peerID>). Argument may be repeated.",
|
|
name: "kad-bootstrap-node"
|
|
.}: seq[string]
|
|
|
|
kadRandomLookupIntervalSec* {.
|
|
desc: "Interval seconds between random kademlia lookups.",
|
|
defaultValue: 60,
|
|
name: "kad-random-lookup-interval"
|
|
.}: uint32
|
|
|
|
kadServiceLookupIntervalSec* {.
|
|
desc: "Interval seconds between service-specific kademlia lookups.",
|
|
defaultValue: 60,
|
|
name: "kad-service-lookup-interval"
|
|
.}: uint32
|
|
|
|
## websocket config
|
|
websocketSupport* {.
|
|
desc: "Enable websocket: true|false",
|
|
defaultValue: false,
|
|
name: "websocket-support"
|
|
.}: bool
|
|
|
|
websocketPort* {.
|
|
desc: "WebSocket listening port.", defaultValue: 8000, name: "websocket-port"
|
|
.}: Port
|
|
|
|
websocketSecureSupport* {.
|
|
desc: "Enable secure websocket: true|false",
|
|
defaultValue: false,
|
|
name: "websocket-secure-support"
|
|
.}: bool
|
|
|
|
websocketSecureKeyPath* {.
|
|
desc: "Secure websocket key path: '/path/to/key.txt' ",
|
|
defaultValue: "",
|
|
name: "websocket-secure-key-path"
|
|
.}: string
|
|
|
|
websocketSecureCertPath* {.
|
|
desc: "Secure websocket Certificate path: '/path/to/cert.txt' ",
|
|
defaultValue: "",
|
|
name: "websocket-secure-cert-path"
|
|
.}: string
|
|
|
|
## quic config
|
|
quicSupport* {.
|
|
desc: "Enable QUIC transport: true|false",
|
|
defaultValue: false,
|
|
name: "quic-support"
|
|
.}: bool
|
|
|
|
quicPort* {.
|
|
desc: "QUIC (UDP) listening port.", defaultValue: 60000, name: "quic-port"
|
|
.}: Port
|
|
|
|
## Rate limitation config, if not set, rate limit checks will not be performed
|
|
rateLimits* {.
|
|
desc:
|
|
"Rate limit settings for different protocols." &
|
|
"Format: protocol:volume/period<unit>" &
|
|
" Where 'protocol' can be one of: <store|storev3|lightpush|px|filter> if not defined it means a global setting" &
|
|
" 'volume' and period must be an integer value. " &
|
|
" 'unit' must be one of <h|m|s|ms> - hours, minutes, seconds, milliseconds respectively. " &
|
|
"Argument may be repeated.",
|
|
defaultValue: newSeq[string](0),
|
|
name: "rate-limit"
|
|
.}: seq[string]
|
|
|
|
localStoragePath* {.
|
|
desc: "Path to store local data.",
|
|
defaultValue: "./data",
|
|
name: "local-storage-path"
|
|
.}: string
|
|
|
|
proc parseCmdArg*(T: type LogosDeliveryMode, s: string): T {.raises: [ValueError].} =
|
|
## The generic `Opt[T]` flag parser dispatches to `parseCmdArg(T, ...)` for
|
|
## the inner type, so `Opt[LogosDeliveryMode]` needs this overload (bare enum
|
|
## fields parse through confutils' internal enum support instead).
|
|
case s.strip().toLowerAscii()
|
|
of "edge":
|
|
LogosDeliveryMode.Edge
|
|
of "core":
|
|
LogosDeliveryMode.Core
|
|
else:
|
|
raise
|
|
newException(ValueError, "Invalid mode: '" & s & "' (expected 'Edge' or 'Core')")
|
|
|
|
proc completeCmdArg*(T: type LogosDeliveryMode, val: string): seq[string] =
|
|
return @[]
|
|
|
|
proc load*(T: type LogosDeliveryNodeConf, version = ""): ConfResult[T] =
|
|
try:
|
|
let conf = LogosDeliveryNodeConf.load(
|
|
version = version,
|
|
secondarySources = proc(
|
|
conf: LogosDeliveryNodeConf, sources: auto
|
|
) {.gcsafe, raises: [ConfigurationError].} =
|
|
sources.addConfigFile(Envvar, InputFile("wakunode2"))
|
|
|
|
if conf.configFile.isSome():
|
|
sources.addConfigFile(Toml, conf.configFile.get())
|
|
,
|
|
)
|
|
|
|
ok(conf)
|
|
except CatchableError:
|
|
err(getCurrentExceptionMsg())
|
|
|
|
proc defaultLogosDeliveryNodeConf*(): ConfResult[LogosDeliveryNodeConf] =
|
|
try:
|
|
let conf = LogosDeliveryNodeConf.load(version = "", cmdLine = @[])
|
|
return ok(conf)
|
|
except CatchableError:
|
|
return err("exception in defaultLogosDeliveryNodeConf: " & getCurrentExceptionMsg())
|
|
|
|
proc toWakuConf*(n: LogosDeliveryNodeConf): ConfResult[WakuConf] =
|
|
var b = WakuConfBuilder.init()
|
|
|
|
let networkPresetConf = toNetworkPresetConf(n.preset, n.clusterId).valueOr:
|
|
return err("Error determining cluster from preset: " & $error)
|
|
|
|
if networkPresetConf.isSome():
|
|
b.withNetworkPresetConf(networkPresetConf.get())
|
|
|
|
b.withLogLevel(n.logLevel)
|
|
b.withLogFormat(n.logFormat)
|
|
|
|
if n.rlnRelay.isSome():
|
|
b.rlnRelayConf.withEnabled(n.rlnRelay.get())
|
|
if n.rlnRelayCredPath != "":
|
|
b.rlnRelayConf.withCredPath(n.rlnRelayCredPath)
|
|
if n.rlnRelayCredPassword != "":
|
|
b.rlnRelayConf.withCredPassword(n.rlnRelayCredPassword)
|
|
if n.ethClientUrls.len > 0:
|
|
b.rlnRelayConf.withEthClientUrls(n.ethClientUrls.mapIt(string(it)))
|
|
if n.rlnRelayEthContractAddress != "":
|
|
b.rlnRelayConf.withEthContractAddress(n.rlnRelayEthContractAddress)
|
|
|
|
if n.rlnRelayChainId != 0:
|
|
b.rlnRelayConf.withChainId(n.rlnRelayChainId)
|
|
if n.rlnRelayUserMessageLimit.isSome():
|
|
b.rlnRelayConf.withUserMessageLimit(n.rlnRelayUserMessageLimit.get())
|
|
if n.rlnEpochSizeSec.isSome():
|
|
b.rlnRelayConf.withEpochSizeSec(n.rlnEpochSizeSec.get())
|
|
|
|
if n.rlnRelayCredIndex.isSome():
|
|
b.rlnRelayConf.withCredIndex(n.rlnRelayCredIndex.get())
|
|
if n.rlnRelayDynamic.isSome():
|
|
b.rlnRelayConf.withDynamic(n.rlnRelayDynamic.get())
|
|
|
|
if n.maxMessageSize != "":
|
|
b.withMaxMessageSize(n.maxMessageSize)
|
|
|
|
b.withProtectedShards(n.protectedShards)
|
|
if n.clusterId.isSome():
|
|
b.withClusterId(n.clusterId.get())
|
|
|
|
b.withAgentString(n.agentString)
|
|
|
|
if n.nodeKey.isSome():
|
|
b.withNodeKey(n.nodeKey.get())
|
|
|
|
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)
|
|
|
|
if n.relayServiceRatio != "":
|
|
b.withRelayServiceRatio(n.relayServiceRatio)
|
|
b.withColocationLimit(n.colocationLimit)
|
|
|
|
if n.peerStoreCapacity.isSome:
|
|
b.withPeerStoreCapacity(n.peerStoreCapacity.get())
|
|
|
|
b.withPeerPersistence(n.peerPersistence)
|
|
b.withDnsAddrsNameServers(n.dnsAddrsNameServers)
|
|
b.withDns4DomainName(n.dns4DomainName)
|
|
b.withCircuitRelayClient(n.isRelayClient)
|
|
if n.relay.isSome():
|
|
b.withRelay(n.relay.get())
|
|
b.withRelayPeerExchange(n.relayPeerExchange)
|
|
b.withRelayShardedPeerManagement(n.relayShardedPeerManagement)
|
|
b.withStaticNodes(n.staticNodes)
|
|
|
|
# Process entry nodes - supports enrtree:, enr:, and multiaddress formats
|
|
if n.entryNodes.len > 0:
|
|
let (enrTreeUrls, bootstrapEnrs, staticNodesFromEntry) = processEntryNodes(
|
|
n.entryNodes
|
|
).valueOr:
|
|
return err("Failed to process entry nodes: " & error)
|
|
|
|
# Set ENRTree URLs for DNS discovery
|
|
if enrTreeUrls.len > 0:
|
|
for url in enrTreeUrls:
|
|
b.dnsDiscoveryConf.withEnrTreeUrl(url)
|
|
|
|
# Set ENR records as bootstrap nodes for discv5
|
|
if bootstrapEnrs.len > 0:
|
|
b.discv5Conf.withBootstrapNodes(bootstrapEnrs)
|
|
|
|
# Add static nodes (multiaddrs and those extracted from ENR entries)
|
|
if staticNodesFromEntry.len > 0:
|
|
b.withStaticNodes(staticNodesFromEntry)
|
|
|
|
if n.numShardsInNetwork != 0:
|
|
b.withNumShardsInCluster(n.numShardsInNetwork)
|
|
b.withShardingConf(AutoSharding)
|
|
elif networkPresetConf.isNone():
|
|
b.withShardingConf(StaticSharding)
|
|
|
|
# It is not possible to pass an empty sequence on the CLI
|
|
# If this is empty, it means the user did not specify any shards
|
|
if n.shards.len != 0:
|
|
b.withSubscribeShards(n.shards)
|
|
|
|
b.withContentTopics(n.contentTopics)
|
|
|
|
if n.store.isSome():
|
|
b.storeServiceConf.withEnabled(n.store.get())
|
|
b.storeServiceConf.withRetentionPolicies(n.storeMessageRetentionPolicy)
|
|
b.storeServiceConf.withDbUrl(n.storeMessageDbUrl)
|
|
b.storeServiceConf.withDbVacuum(n.storeMessageDbVacuum)
|
|
b.storeServiceConf.withDbMigration(n.storeMessageDbMigration)
|
|
b.storeServiceConf.withMaxNumDbConnections(n.storeMaxNumDbConnections)
|
|
b.storeServiceConf.withResume(n.storeResume)
|
|
|
|
# TODO: can we just use `Opt` on the CLI?
|
|
if n.storenode != "":
|
|
b.withRemoteStoreNode(n.storenode)
|
|
if n.filternode != "":
|
|
b.withRemoteFilterNode(n.filternode)
|
|
if n.lightpushnode != "":
|
|
b.withRemoteLightPushNode(n.lightpushnode)
|
|
if n.peerExchangeNode != "":
|
|
b.withRemotePeerExchangeNode(n.peerExchangeNode)
|
|
|
|
b.storeServiceConf.storeSyncConf.withEnabled(n.storeSync)
|
|
b.storeServiceConf.storeSyncConf.withIntervalSec(n.storeSyncInterval)
|
|
b.storeServiceConf.storeSyncConf.withRangeSec(n.storeSyncRange)
|
|
b.storeServiceConf.storeSyncConf.withRelayJitterSec(n.storeSyncRelayJitter)
|
|
|
|
if n.mix.isSome():
|
|
b.mixConf.withEnabled(n.mix.get())
|
|
b.withMix(n.mix.get())
|
|
b.mixConf.withMixNodes(n.mixnodes)
|
|
if n.mixkey.isSome():
|
|
b.mixConf.withMixKey(n.mixkey.get())
|
|
|
|
if n.filter.isSome():
|
|
b.filterServiceConf.withEnabled(n.filter.get())
|
|
b.filterServiceConf.withSubscriptionTimeout(n.filterSubscriptionTimeout)
|
|
b.filterServiceConf.withMaxPeersToServe(n.filterMaxPeersToServe)
|
|
b.filterServiceConf.withMaxCriteria(n.filterMaxCriteria)
|
|
|
|
if n.lightpush.isSome():
|
|
b.withLightPush(n.lightpush.get())
|
|
|
|
b.restServerConf.withEnabled(n.rest)
|
|
b.restServerConf.withListenAddress(n.restAddress)
|
|
b.restServerConf.withPort(n.restPort)
|
|
b.restServerConf.withRelayCacheCapacity(n.restRelayCacheCapacity)
|
|
b.restServerConf.withAdmin(n.restAdmin)
|
|
b.restServerConf.withAllowOrigin(n.restAllowOrigin)
|
|
|
|
b.metricsServerConf.withEnabled(n.metricsServer)
|
|
b.metricsServerConf.withHttpAddress(n.metricsServerAddress)
|
|
b.metricsServerConf.withHttpPort(n.metricsServerPort)
|
|
b.metricsServerConf.withLogging(n.metricsLogging)
|
|
|
|
if n.dnsDiscoveryUrl != "":
|
|
b.dnsDiscoveryConf.withEnrTreeUrl(n.dnsDiscoveryUrl)
|
|
b.dnsDiscoveryConf.withNameServers(n.dnsAddrsNameServers)
|
|
|
|
if n.discv5Discovery.isSome():
|
|
b.discv5Conf.withEnabled(n.discv5Discovery.get())
|
|
|
|
b.discv5Conf.withUdpPort(n.discv5UdpPort)
|
|
b.discv5Conf.withBootstrapNodes(n.discv5BootstrapNodes)
|
|
b.discv5Conf.withEnrAutoUpdate(n.discv5EnrAutoUpdate)
|
|
b.discv5Conf.withTableIpLimit(n.discv5TableIpLimit)
|
|
b.discv5Conf.withBucketIpLimit(n.discv5BucketIpLimit)
|
|
b.discv5Conf.withBitsPerHop(n.discv5BitsPerHop)
|
|
|
|
if n.peerExchange.isSome():
|
|
b.withPeerExchange(n.peerExchange.get())
|
|
|
|
if n.rendezvous.isSome():
|
|
b.withRendezvous(n.rendezvous.get())
|
|
|
|
b.webSocketConf.withEnabled(n.websocketSupport)
|
|
b.webSocketConf.withWebSocketPort(n.websocketPort)
|
|
b.webSocketConf.withSecureEnabled(n.websocketSecureSupport)
|
|
b.webSocketConf.withKeyPath(n.websocketSecureKeyPath)
|
|
b.webSocketConf.withCertPath(n.websocketSecureCertPath)
|
|
|
|
b.quicConf.withEnabled(n.quicSupport)
|
|
b.quicConf.withQuicPort(n.quicPort)
|
|
|
|
if n.rateLimits.len > 0:
|
|
b.rateLimitConf.withRateLimits(n.rateLimits)
|
|
else:
|
|
# The default protocol posture serves filter/lightpush, so default service
|
|
# rate limits must apply unless the operator sets their own.
|
|
b.rateLimitConf.withRateLimitsIfNotAssigned(
|
|
@["filter:100/1s", "lightpush:5/1s", "px:5/1s"]
|
|
)
|
|
|
|
b.withLocalStoragePath(n.localStoragePath)
|
|
|
|
if n.enableKadDiscovery.isSome():
|
|
b.kademliaDiscoveryConf.withEnabled(n.enableKadDiscovery.get())
|
|
b.kademliaDiscoveryConf.withBootstrapNodes(n.kadBootstrapNodes)
|
|
|
|
if n.kadRandomLookupIntervalSec > 0:
|
|
b.kademliaDiscoveryConf.withRandomLookupInterval(
|
|
chronos.seconds(n.kadRandomLookupIntervalSec.int64)
|
|
)
|
|
if n.kadServiceLookupIntervalSec > 0:
|
|
b.kademliaDiscoveryConf.withServiceLookupInterval(
|
|
chronos.seconds(n.kadServiceLookupIntervalSec.int64)
|
|
)
|
|
|
|
return b.build()
|