2020-04-29 04:49:27 +00:00
import
2022-11-02 14:55:09 +00:00
std / strutils ,
2022-10-26 15:10:30 +00:00
stew / results ,
chronos ,
2022-11-02 14:55:09 +00:00
regex ,
2022-11-03 09:45:06 +00:00
confutils ,
confutils / defs ,
2022-10-18 12:06:54 +00:00
confutils / std / net ,
2022-05-17 15:48:08 +00:00
confutils / toml / defs as confTomlDefs ,
confutils / toml / std / net as confTomlNet ,
2020-05-21 04:16:00 +00:00
libp2p / crypto / crypto ,
libp2p / crypto / secp ,
2023-01-26 10:18:30 +00:00
libp2p / multiaddress ,
2023-05-02 09:45:50 +00:00
nimcrypto / utils ,
secp256k1
2022-11-03 16:58:48 +00:00
import
.. / .. / waku / common / confutils / envvar / defs as confEnvvarDefs ,
2022-12-07 11:30:32 +00:00
.. / .. / waku / common / confutils / envvar / std / net as confEnvvarNet ,
2023-06-22 20:58:14 +00:00
.. / .. / waku / common / logging ,
2023-08-09 17:11:50 +00:00
.. / .. / waku / waku_enr
2022-05-17 15:48:08 +00:00
export
confTomlDefs ,
2022-11-03 16:58:48 +00:00
confTomlNet ,
confEnvvarDefs ,
confEnvvarNet
2022-10-26 15:10:30 +00:00
type ConfResult * [ T ] = Result [ T , string ]
2023-05-02 09:45:50 +00:00
type ProtectedTopic * = object
topic * : string
key * : secp256k1 . SkPublicKey
2022-11-23 09:08:00 +00:00
2023-11-09 09:48:39 +00:00
type StartUpCommand * = enum
noCommand # default, runs waku
generateRlnKeystore # generates a new RLN keystore
2020-04-29 04:49:27 +00:00
type
WakuNodeConf * = object
2022-05-17 15:48:08 +00:00
configFile * {.
desc : " Loads configuration from a TOML file (cmd-line parameters take precedence) "
name : " config-file " } : Option [ InputFile ]
2022-12-07 11:30:32 +00:00
## Log configuration
2020-04-29 04:49:27 +00:00
logLevel * {.
2022-12-07 11:30:32 +00:00
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
2022-05-17 20:11:07 +00:00
2022-12-07 11:30:32 +00:00
logFormat * {.
desc : " Specifies what kind of logs should be written to stdout. Suported formats: TEXT, JSON " ,
defaultValue : logging . LogFormat . TEXT ,
name : " log-format " . } : logging . LogFormat
2022-10-28 13:12:06 +00:00
2022-08-18 17:35:02 +00:00
rlnRelayCredPath * {.
desc : " The path for peristing rln-relay credential " ,
2023-11-09 09:48:39 +00:00
defaultValue : " " ,
2022-08-18 17:35:02 +00:00
name : " rln-relay-cred-path " } : string
2022-06-17 22:00:19 +00:00
rlnRelayEthClientAddress * {.
2022-09-24 04:57:28 +00:00
desc : " WebSocket address of an Ethereum testnet client e.g., ws://localhost:8540/ " ,
2023-11-09 09:48:39 +00:00
defaultValue : " ws://localhost:8540/ " ,
2022-10-03 20:25:56 +00:00
name : " rln-relay-eth-client-address " } : string
2022-11-23 09:08:00 +00:00
2022-10-03 20:25:56 +00:00
rlnRelayEthContractAddress * {.
2022-11-23 09:08:00 +00:00
desc : " Address of membership contract on an Ethereum testnet " ,
2023-11-09 09:48:39 +00:00
defaultValue : " " ,
2022-10-03 20:25:56 +00:00
name : " rln-relay-eth-contract-address " } : string
2022-10-28 09:13:05 +00:00
2023-08-29 12:16:21 +00:00
rlnRelayCredPassword * {.
2022-11-23 09:08:00 +00:00
desc : " Password for encrypting RLN credentials " ,
2023-11-09 09:48:39 +00:00
defaultValue : " " ,
2022-10-28 09:13:05 +00:00
name : " rln-relay-cred-password " } : string
2022-11-23 09:08:00 +00:00
2023-11-09 09:48:39 +00:00
rlnRelayEthPrivateKey * {.
desc : " Private key for broadcasting transactions " ,
2022-10-27 22:05:02 +00:00
defaultValue : " " ,
2023-11-09 09:48:39 +00:00
name : " rln-relay-eth-private-key " } : 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
protectedTopics * {.
desc : " Topics and its public key to be used for message validation, topic:pubkey. Argument may be repeated. "
defaultValue : newSeq [ ProtectedTopic ] ( 0 )
name : " protected-topic " . } : seq [ ProtectedTopic ]
## General node config
clusterId * {.
desc : " Cluster id that the node is running in. Node in a different cluster id is disconnected. "
defaultValue : 0
name : " cluster-id " } : uint32
agentString * {.
defaultValue : " nwaku " ,
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 " } : ValidIpAddress
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 : " 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 " ,
defaultValue : false ,
name : " ext-multiaddr-only " } : bool
maxConnections * {.
desc : " Maximum allowed number of libp2p connections. "
defaultValue : 50
name : " max-connections " } : uint16
maxRelayPeers * {.
desc : " Maximum allowed number of relay peers. "
name : " max-relay-peers " } : Option [ 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 : @ [ ValidIpAddress . init ( " 1.1.1.1 " ) , ValidIpAddress . init ( " 1.0.0.1 " ) ]
name : " dns-addrs-name-server " } : seq [ ValidIpAddress ]
dns4DomainName * {.
desc : " The domain name resolving to the node ' s public IPv4 address " ,
defaultValue : " "
name : " dns4-domain-name " } : string
## 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
rlnRelay * {.
desc : " Enable spam protection through rln-relay: true|false " ,
defaultValue : false
name : " rln-relay " } : bool
rlnRelayCredIndex * {.
desc : " the index of the onchain commitment to use " ,
name : " rln-relay-membership-index " } : Option [ uint ]
rlnRelayDynamic * {.
desc : " Enable waku-rln-relay with on-chain dynamic group management: true|false " ,
defaultValue : false
name : " rln-relay-dynamic " } : bool
rlnRelayIdKey * {.
desc : " Rln relay identity secret key as a Hex string " ,
defaultValue : " "
name : " rln-relay-id-key " } : string
rlnRelayIdCommitmentKey * {.
desc : " Rln relay identity commitment key as a Hex string " ,
defaultValue : " "
name : " rln-relay-id-commitment-key " } : string
rlnRelayTreePath * {.
desc : " Path to the RLN merkle tree sled db (https://github.com/spacejam/sled) " ,
defaultValue : " "
name : " rln-relay-tree-path " } : string
rlnRelayBandwidthThreshold * {.
desc : " Message rate in bytes/sec after which verification of proofs should happen " ,
defaultValue : 0 # to maintain backwards compatibility
name : " rln-relay-bandwidth-threshold " } : int
staticnodes * {.
desc : " Peer multiaddr to directly connect with. Argument may be repeated. "
name : " staticnode " } : seq [ string ]
keepAlive * {.
desc : " Enable keep-alive for idle connections: true|false " ,
defaultValue : false
name : " keep-alive " } : bool
topics * {.
desc : " Default topic to subscribe to. Argument may be repeated. Deprecated! Please use pubsub-topic and/or content-topic instead. "
defaultValue : @ [ " /waku/2/default-waku/proto " ]
name : " topic " . } : seq [ string ]
pubsubTopics * {.
desc : " Default pubsub topic to subscribe to. Argument may be repeated. "
name : " pubsub-topic " . } : seq [ string ]
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 " ,
defaultValue : false ,
name : " store " } : bool
storenode * {.
desc : " Peer multiaddress to query for storage " ,
defaultValue : " " ,
name : " storenode " } : string
storeMessageRetentionPolicy * {.
desc : " Message store retention policy. Time retention policy: ' time:<seconds> ' . Capacity retention policy: ' capacity:<count> ' . Size retention policy: ' size:<xMB/xGB> ' . Set to ' none ' to disable. " ,
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
2023-11-24 15:21:22 +00:00
storeMaxNumDbConnections * {.
desc : " Maximum number of simultaneous Postgres connections. " ,
defaultValue : 50 ,
name : " store-max-num-db-connections " } : int
2023-11-09 09:48:39 +00:00
## Filter config
filter * {.
desc : " Enable filter protocol: true|false " ,
defaultValue : false
name : " filter " } : bool
filternode * {.
desc : " Peer multiaddr to request content filtering of messages. " ,
defaultValue : " "
name : " filternode " } : string
filterTimeout * {.
desc : " Timeout for filter node in seconds. " ,
defaultValue : 14400 # 4 hours
name : " filter-timeout " } : int64
## Lightpush config
lightpush * {.
desc : " Enable lightpush protocol: true|false " ,
defaultValue : false
name : " lightpush " } : bool
lightpushnode * {.
desc : " Peer multiaddr to request lightpush of published messages. " ,
defaultValue : " "
name : " lightpushnode " } : string
## JSON-RPC config
rpc * {.
desc : " Enable Waku JSON-RPC server: true|false " ,
defaultValue : true
name : " rpc " } : bool
rpcAddress * {.
desc : " Listening address of the JSON-RPC server. " ,
defaultValue : ValidIpAddress . init ( " 127.0.0.1 " )
name : " rpc-address " } : ValidIpAddress
rpcPort * {.
desc : " Listening port of the JSON-RPC server. " ,
defaultValue : 8545
name : " rpc-port " } : uint16
rpcAdmin * {.
desc : " Enable access to JSON-RPC Admin API: true|false " ,
defaultValue : false
name : " rpc-admin " } : bool
rpcPrivate * {.
desc : " Enable access to JSON-RPC Private API: true|false " ,
defaultValue : false
name : " rpc-private " } : bool
## 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 : ValidIpAddress . init ( " 127.0.0.1 " )
name : " rest-address " } : ValidIpAddress
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 : 30
name : " rest-relay-cache-capacity " } : uint32
restAdmin * {.
desc : " Enable access to REST HTTP Admin API: true|false " ,
defaultValue : false
name : " rest-admin " } : bool
restPrivate * {.
desc : " Enable access to REST HTTP Private API: true|false " ,
defaultValue : false
name : " rest-private " } : bool
## 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 : ValidIpAddress . init ( " 127.0.0.1 " )
name : " metrics-server-address " } : ValidIpAddress
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 : " Enable discovering nodes via DNS "
defaultValue : false
name : " dns-discovery " } : bool
dnsDiscoveryUrl * {.
desc : " URL for DNS node list in format ' enrtree://<key>@<fqdn> ' " ,
defaultValue : " "
name : " dns-discovery-url " } : string
dnsDiscoveryNameServers * {.
desc : " DNS name server IPs to query. Argument may be repeated. "
defaultValue : @ [ ValidIpAddress . init ( " 1.1.1.1 " ) , ValidIpAddress . init ( " 1.0.0.1 " ) ]
name : " dns-discovery-name-server " } : seq [ ValidIpAddress ]
## Discovery v5 config
discv5Discovery * {.
desc : " Enable discovering nodes via Node Discovery v5 "
defaultValue : false
name : " discv5-discovery " } : 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 " ,
defaultValue : false
name : " peer-exchange " } : bool
peerExchangeNode * {.
desc : " Peer multiaddr to send peer exchange requests to. (enables peer exchange protocol requester side) " ,
defaultValue : " "
name : " peer-exchange-node " } : string
## 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
2021-11-02 10:29:11 +00:00
2022-12-07 11:30:32 +00:00
## Parsing
2020-05-21 04:16:00 +00:00
# NOTE: Keys are different in nim-libp2p
2022-11-02 13:55:48 +00:00
proc parseCmdArg * ( T : type crypto . PrivateKey , p : string ) : T =
2020-04-29 04:49:27 +00:00
try :
2020-05-26 03:55:53 +00:00
let key = SkPrivateKey . init ( utils . fromHex ( p ) ) . tryGet ( )
2022-11-03 09:45:06 +00:00
crypto . PrivateKey ( scheme : Secp256k1 , skkey : key )
2023-04-04 13:34:53 +00:00
except CatchableError :
2023-09-21 11:12:14 +00:00
raise newException ( ValueError , " Invalid private key " )
2020-04-29 04:49:27 +00:00
2022-11-02 13:55:48 +00:00
proc completeCmdArg * ( T : type crypto . PrivateKey , val : string ) : seq [ string ] =
2020-04-29 04:49:27 +00:00
return @ [ ]
2023-05-02 09:45:50 +00:00
proc parseCmdArg * ( T : type ProtectedTopic , p : string ) : T =
let elements = p . split ( " : " )
if elements . len ! = 2 :
2023-09-21 11:12:14 +00:00
raise newException ( ValueError , " Invalid format for protected topic expected topic:publickey " )
2023-05-02 09:45:50 +00:00
let publicKey = secp256k1 . SkPublicKey . fromHex ( elements [ 1 ] )
if publicKey . isErr :
2023-09-21 11:12:14 +00:00
raise newException ( ValueError , " Invalid public key " )
2023-05-02 09:45:50 +00:00
return ProtectedTopic ( topic : elements [ 0 ] , key : publicKey . get ( ) )
proc completeCmdArg * ( T : type ProtectedTopic , val : string ) : seq [ string ] =
return @ [ ]
2022-12-07 11:30:32 +00:00
2022-11-02 13:55:48 +00:00
proc parseCmdArg * ( T : type ValidIpAddress , p : string ) : T =
2020-04-29 04:49:27 +00:00
try :
2022-11-03 09:45:06 +00:00
ValidIpAddress . init ( p )
2023-04-04 13:34:53 +00:00
except CatchableError :
2023-09-21 11:12:14 +00:00
raise newException ( ValueError , " Invalid IP address " )
2020-04-29 04:49:27 +00:00
2022-11-02 13:55:48 +00:00
proc completeCmdArg * ( T : type ValidIpAddress , val : string ) : seq [ string ] =
2020-04-29 04:49:27 +00:00
return @ [ ]
2020-07-24 01:39:58 +00:00
2022-12-07 11:30:32 +00:00
proc defaultListenAddress * ( ) : ValidIpAddress =
# TODO: How should we select between IPv4 and IPv6
# Maybe there should be a config option for this.
( static ValidIpAddress . init ( " 0.0.0.0 " ) )
2022-11-02 13:55:48 +00:00
proc parseCmdArg * ( T : type Port , p : string ) : T =
2020-07-24 01:39:58 +00:00
try :
2022-11-03 09:45:06 +00:00
Port ( parseInt ( p ) )
2023-04-04 13:34:53 +00:00
except CatchableError :
2023-09-21 11:12:14 +00:00
raise newException ( ValueError , " Invalid Port number " )
2020-07-24 01:39:58 +00:00
2022-11-02 13:55:48 +00:00
proc completeCmdArg * ( T : type Port , val : string ) : seq [ string ] =
2020-07-24 01:39:58 +00:00
return @ [ ]
2023-01-31 16:26:22 +00:00
proc parseCmdArg * ( T : type Option [ int ] , p : string ) : T =
try :
some ( parseInt ( p ) )
2023-04-04 13:34:53 +00:00
except CatchableError :
2023-09-21 11:12:14 +00:00
raise newException ( ValueError , " Invalid number " )
2022-10-26 15:10:30 +00:00
2023-09-04 10:16:44 +00:00
proc parseCmdArg * ( T : type Option [ uint ] , p : string ) : T =
try :
some ( parseUint ( p ) )
except CatchableError :
2023-09-21 11:12:14 +00:00
raise newException ( ValueError , " Invalid unsigned integer " )
2023-09-04 10:16:44 +00:00
2022-11-03 09:45:06 +00:00
## Load
proc readValue * ( r : var TomlReader , value : var crypto . PrivateKey ) {. raises : [ SerializationError ] . } =
2022-11-23 09:08:00 +00:00
try :
2022-11-03 09:45:06 +00:00
value = parseCmdArg ( crypto . PrivateKey , r . readValue ( string ) )
except CatchableError :
raise newException ( SerializationError , getCurrentExceptionMsg ( ) )
2022-11-03 16:58:48 +00:00
proc readValue * ( r : var EnvvarReader , value : var crypto . PrivateKey ) {. raises : [ SerializationError ] . } =
2022-11-23 09:08:00 +00:00
try :
2022-11-03 16:58:48 +00:00
value = parseCmdArg ( crypto . PrivateKey , r . readValue ( string ) )
except CatchableError :
raise newException ( SerializationError , getCurrentExceptionMsg ( ) )
2023-05-02 09:45:50 +00:00
proc readValue * ( r : var TomlReader , value : var ProtectedTopic ) {. raises : [ SerializationError ] . } =
try :
value = parseCmdArg ( ProtectedTopic , r . readValue ( string ) )
except CatchableError :
raise newException ( SerializationError , getCurrentExceptionMsg ( ) )
proc readValue * ( r : var EnvvarReader , value : var ProtectedTopic ) {. raises : [ SerializationError ] . } =
try :
value = parseCmdArg ( ProtectedTopic , r . readValue ( string ) )
except CatchableError :
raise newException ( SerializationError , getCurrentExceptionMsg ( ) )
2022-11-03 09:45:06 +00:00
{. push warning [ ProveInit ] : off . }
proc load * ( T : type WakuNodeConf , version = " " ) : ConfResult [ T ] =
try :
let conf = WakuNodeConf . load (
version = version ,
2023-09-21 11:12:14 +00:00
secondarySources = proc ( conf : WakuNodeConf , sources : auto )
{. gcsafe , raises : [ ConfigurationError ] . } =
2022-11-03 16:58:48 +00:00
sources . addConfigFile ( Envvar , InputFile ( " wakunode2 " ) )
2022-11-03 09:45:06 +00:00
if conf . configFile . isSome ( ) :
sources . addConfigFile ( Toml , conf . configFile . get ( ) )
)
ok ( conf )
except CatchableError :
err ( getCurrentExceptionMsg ( ) )
2022-11-23 09:08:00 +00:00
{. pop . }