From 0c16f28d0dbff2cd8c7cc683ccd6dbb340e712b5 Mon Sep 17 00:00:00 2001 From: jangko Date: Sat, 11 Sep 2021 21:58:01 +0700 Subject: [PATCH] config: replace stdlib parseOpt with nim-confutils fixes #581 --- .../nodocker/consensus/consensus_sim.nim | 14 +- .../nodocker/graphql/graphql_sim.nim | 15 +- nimbus/chain_config.nim | 110 +- nimbus/conf_utils.nim | 3 +- nimbus/config.nim | 1241 +++++++---------- nimbus/db/db_chain.nim | 2 +- nimbus/genesis.nim | 4 +- nimbus/graphql/ethapi.nim | 6 +- nimbus/nimbus.nim | 191 ++- nimbus/p2p/executor/executor_helpers.nim | 2 +- nimbus/p2p/gaslimit.nim | 3 +- nimbus/rpc/common.nim | 6 +- nimbus/rpc/p2p.nim | 12 +- nimbus/sealer.nim | 5 +- nimbus/transaction/call_evm.nim | 2 +- nimbus/vm/state.nim | 2 +- premix/configuration.nim | 30 +- premix/parser.nim | 2 +- premix/persist.nim | 3 +- tests/all_tests.nim | 3 +- tests/persistBlockTestGen.nim | 18 +- tests/test_clique.nim | 24 +- tests/test_configuration.nim | 76 + tests/test_difficulty.nim | 3 +- tests/test_forkid.nim | 2 +- tests/test_graphql.nim | 9 +- tests/test_helpers.nim | 12 +- tests/test_misc.nim | 39 +- tests/test_rpc.nim | 26 +- tests/tracerTestGen.nim | 17 +- 30 files changed, 865 insertions(+), 1017 deletions(-) create mode 100644 tests/test_configuration.nim diff --git a/hive_integration/nodocker/consensus/consensus_sim.nim b/hive_integration/nodocker/consensus/consensus_sim.nim index 738183597c..2d9019d615 100644 --- a/hive_integration/nodocker/consensus/consensus_sim.nim +++ b/hive_integration/nodocker/consensus/consensus_sim.nim @@ -8,7 +8,7 @@ # those terms. import - std/[os, parseopt, strformat, json], + std/[os, strformat, json], eth/[common, trie/db], stew/byteutils, ../../../nimbus/db/db_chain, ../../../nimbus/[genesis, config, conf_utils], @@ -16,19 +16,13 @@ import proc processNode(genesisFile, chainFile, lastBlockHash: string, testStatusIMPL: var TestStatus) = - var msg: string - var opt = initOptParser("--customnetwork:" & genesisFile) - let res = processArguments(msg, opt) - if res != Success: - echo msg - quit(QuitFailure) let - conf = getConfiguration() + conf = makeConfig(@["--customnetwork:" & genesisFile]) chainDB = newBaseChainDB(newMemoryDb(), pruneTrie = false, - conf.net.networkId, - conf.customNetwork + conf.networkId.get, + conf.customNetwork.get() ) initializeEmptyDb(chainDB) diff --git a/hive_integration/nodocker/graphql/graphql_sim.nim b/hive_integration/nodocker/graphql/graphql_sim.nim index 27d8f03bdd..ae5b1105e1 100644 --- a/hive_integration/nodocker/graphql/graphql_sim.nim +++ b/hive_integration/nodocker/graphql/graphql_sim.nim @@ -8,7 +8,7 @@ # those terms. import - std/[os, parseopt, json], + std/[os, json], eth/[p2p, trie/db], ../../../nimbus/db/db_chain, ../../../nimbus/sync/protocol_eth65, ../../../nimbus/[genesis, config, conf_utils, context], @@ -66,21 +66,14 @@ proc processNode(ctx: GraphqlRef, node: JsonNode, fileName: string, testStatusIM ctx.purgeNames(savePoint) proc main() = - var msg: string - var opt = initOptParser("--customnetwork:" & genesisFile) - let res = processArguments(msg, opt) - if res != Success: - echo msg - quit(QuitFailure) - let + conf = makeConfig(@["--customnetwork:" & genesisFile]) ethCtx = newEthContext() - conf = getConfiguration() ethNode = setupEthNode(conf, ethCtx, eth) chainDB = newBaseChainDB(newMemoryDb(), pruneTrie = false, - conf.net.networkId, - conf.customNetwork + conf.networkId.get, + conf.customNetwork.get ) initializeEmptyDb(chainDB) diff --git a/nimbus/chain_config.nim b/nimbus/chain_config.nim index 144a3190b1..31186ffa8f 100644 --- a/nimbus/chain_config.nim +++ b/nimbus/chain_config.nim @@ -9,12 +9,13 @@ import std/[tables, strutils, options, times], - eth/[common, rlp], stint, stew/[byteutils], + eth/[common, rlp, p2p], stint, stew/[byteutils], nimcrypto/hash, json_serialization, chronicles, json_serialization/std/options as jsoptions, json_serialization/std/tables as jstable, - json_serialization/lexer + json_serialization/lexer, + ./forks type CliqueOptions = object @@ -94,6 +95,16 @@ type config : ChainOptions genesis: Genesis +const + CustomNet* = 0.NetworkId + # these are public network id + MainNet* = 1.NetworkId + # No longer used: MordenNet = 2 + RopstenNet* = 3.NetworkId + RinkebyNet* = 4.NetworkId + GoerliNet* = 5.NetworkId + KovanNet* = 42.NetworkId + proc read(rlp: var Rlp, x: var AddressBalance, _: type EthAddress): EthAddress {.inline.} = let val = rlp.read(UInt256).toByteArrayBE() result[0 .. ^1] = val.toOpenArray(12, val.high) @@ -213,3 +224,98 @@ proc parseGenesisAlloc*(data: string, ga: var GenesisAlloc): bool = return false return true + +proc toFork*(c: ChainConfig, number: BlockNumber): Fork = + if number >= c.londonBlock: FkLondon + elif number >= c.berlinBlock: FkBerlin + elif number >= c.istanbulBlock: FkIstanbul + elif number >= c.petersburgBlock: FkPetersburg + elif number >= c.constantinopleBlock: FkConstantinople + elif number >= c.byzantiumBlock: FkByzantium + elif number >= c.eip158Block: FkSpurious + elif number >= c.eip150Block: FkTangerine + elif number >= c.homesteadBlock: FkHomestead + else: FkFrontier + +proc chainConfig*(id: NetworkId, cn: CustomNetwork): ChainConfig = + # For some public networks, NetworkId and ChainId value are identical + # but that is not always the case + + result = case id + of MainNet: + ChainConfig( + poaEngine: false, # TODO: use real engine conf: PoW + chainId: MainNet.ChainId, + homesteadBlock: 1_150_000.toBlockNumber, # 14/03/2016 20:49:53 + daoForkBlock: 1_920_000.toBlockNumber, + daoForkSupport: true, + eip150Block: 2_463_000.toBlockNumber, # 18/10/2016 17:19:31 + eip150Hash: toDigest("2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0"), + eip155Block: 2_675_000.toBlockNumber, # 22/11/2016 18:15:44 + eip158Block: 2_675_000.toBlockNumber, + byzantiumBlock: 4_370_000.toBlockNumber, # 16/10/2017 09:22:11 + constantinopleBlock: 7_280_000.toBlockNumber, # Never Occured in MainNet + petersburgBlock:7_280_000.toBlockNumber, # 28/02/2019 07:52:04 + istanbulBlock: 9_069_000.toBlockNumber, # 08/12/2019 12:25:09 + muirGlacierBlock: 9_200_000.toBlockNumber, # 02/01/2020 08:30:49 + berlinBlock: 12_244_000.toBlockNumber, # 15/04/2021 10:07:03 + londonBlock: 12_965_000.toBlockNumber, # 05/08/2021 12:33:42 + ) + of RopstenNet: + ChainConfig( + poaEngine: false, # TODO: use real engine conf: PoW + chainId: RopstenNet.ChainId, + homesteadBlock: 0.toBlockNumber, + daoForkSupport: false, + eip150Block: 0.toBlockNumber, + eip150Hash: toDigest("41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d"), + eip155Block: 10.toBlockNumber, + eip158Block: 10.toBlockNumber, + byzantiumBlock: 1_700_000.toBlockNumber, + constantinopleBlock: 4_230_000.toBlockNumber, + petersburgBlock:4_939_394.toBlockNumber, + istanbulBlock: 6_485_846.toBlockNumber, + muirGlacierBlock: 7_117_117.toBlockNumber, + berlinBlock: 9_812_189.toBlockNumber, + londonBlock: 10_499_401.toBlockNumber # June 24, 2021 + ) + of RinkebyNet: + ChainConfig( + poaEngine: true, # TODO: use real engine conf: PoA + chainId: RinkebyNet.ChainId, + homesteadBlock: 1.toBlockNumber, + daoForkSupport: false, + eip150Block: 2.toBlockNumber, + eip150Hash: toDigest("9b095b36c15eaf13044373aef8ee0bd3a382a5abb92e402afa44b8249c3a90e9"), + eip155Block: 3.toBlockNumber, + eip158Block: 3.toBlockNumber, + byzantiumBlock: 1_035_301.toBlockNumber, + constantinopleBlock: 3_660_663.toBlockNumber, + petersburgBlock:4_321_234.toBlockNumber, + istanbulBlock: 5_435_345.toBlockNumber, + muirGlacierBlock: 8_290_928.toBlockNumber, # never occured in rinkeby network + berlinBlock: 8_290_928.toBlockNumber, + londonBlock: 8_897_988.toBlockNumber # July 7, 2021 + ) + of GoerliNet: + ChainConfig( + poaEngine: true, # TODO: use real engine conf: PoA + chainId: GoerliNet.ChainId, + homesteadBlock: 0.toBlockNumber, + daoForkSupport: false, + eip150Block: 0.toBlockNumber, + eip150Hash: toDigest("0000000000000000000000000000000000000000000000000000000000000000"), + eip155Block: 0.toBlockNumber, + eip158Block: 0.toBlockNumber, + byzantiumBlock: 0.toBlockNumber, + constantinopleBlock: 0.toBlockNumber, + petersburgBlock: 0.toBlockNumber, + istanbulBlock: 1_561_651.toBlockNumber, + muirGlacierBlock: 4_460_644.toBlockNumber, # never occured in goerli network + berlinBlock: 4_460_644.toBlockNumber, + londonBlock: 5_062_605.toBlockNumber # June 30, 2021 + ) + else: + # everything else will use CustomNet config + trace "Custom genesis block configuration loaded", conf=cn.config + cn.config diff --git a/nimbus/conf_utils.nim b/nimbus/conf_utils.nim index 1a1b67335b..1fa9252f85 100644 --- a/nimbus/conf_utils.nim +++ b/nimbus/conf_utils.nim @@ -9,8 +9,7 @@ import chronicles, eth/[common, rlp], stew/io2, - ./p2p/chain, ./db/[db_chain, select_backend], - config + ./p2p/chain, ./db/[db_chain, select_backend] type # trick the rlp decoder diff --git a/nimbus/config.nim b/nimbus/config.nim index 83f17d237d..31e7a068f0 100644 --- a/nimbus/config.nim +++ b/nimbus/config.nim @@ -1,5 +1,5 @@ # Nimbus -# Copyright (c) 2018-2019 Status Research & Development GmbH +# Copyright (c) 2021 Status Research & Development GmbH # Licensed under either of # * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE)) # * MIT license ([LICENSE-MIT](LICENSE-MIT)) @@ -8,21 +8,28 @@ # those terms. import - parseopt, strutils, macros, os, times, json, tables, stew/[byteutils], - chronos, eth/[keys, common, p2p, net/nat], chronicles, nimcrypto/hash, - eth/p2p/bootnodes, ./db/select_backend, eth/keys, ./chain_config, ./forks + std/[options, strutils, times, os], + pkg/[ + confutils, + confutils/defs, + stew/byteutils, + confutils/std/net + ], + stew/shims/net as stewNet, + eth/[p2p, common, net/nat, p2p/bootnodes], + "."/[db/select_backend, chain_config, constants] const - NimbusName* = "Nimbus" + NimbusName* = "nimbus-eth1" ## project name string NimbusMajor*: int = 0 ## is the major number of Nimbus' version. - NimbusMinor*: int = 0 + NimbusMinor*: int = 1 ## is the minor number of Nimbus' version. - NimbusPatch*: int = 1 + NimbusPatch*: int = 0 ## is the patch number of Nimbus' version. NimbusVersion* = $NimbusMajor & "." & $NimbusMinor & "." & $NimbusPatch @@ -33,221 +40,392 @@ const GitRevision = staticExec("git rev-parse --short HEAD").replace("\n") # remove CR - NimVersion = staticExec("nim --version") + NimVersion = staticExec("nim --version").strip() let - NimbusCopyright* = "Copyright (c) 2018-" & $(now().utc.year) & " Status Research & Development GmbH" - NimbusHeader* = "$# Version $# [$#: $#, $#, $#]\p$#\p\p$#\p" % - [NimbusName, NimbusVersion, hostOS, hostCPU, nimbus_db_backend, GitRevision, NimbusCopyright, NimVersion] + # e.g.: Copyright (c) 2018-2021 Status Research & Development GmbH + NimbusCopyright* = "Copyright (c) 2018-" & + $(now().utc.year) & + " Status Research & Development GmbH" + + # e.g.: + # Nimbus Version 0.1.0 [windows: amd64, rocksdb, dda8914f] + # Copyright (c) 2018-2021 Status Research & Development GmbH + NimbusBuild* = "$# Version $# [$#: $#, $#, $#]\p$#" % [ + NimbusName, + NimbusVersion, + hostOS, + hostCPU, + nimbus_db_backend, + GitRevision, + NimbusCopyright, + ] + + NimbusHeader* = "$#\p\p$#" % [ + NimbusBuild, + NimVersion + ] + +proc defaultDataDir*(): string = + when defined(windows): + getHomeDir() / "AppData" / "Roaming" / "Nimbus" + elif defined(macosx): + getHomeDir() / "Library" / "Application Support" / "Nimbus" + else: + getHomeDir() / ".cache" / "nimbus" + +proc defaultKeystoreDir*(): string = + defaultDataDir() / "keystore" + +proc getLogLevels(): string = + var logLevels: seq[string] + for level in LogLevel: + if level < enabledLogLevel: + continue + logLevels.add($level) + join(logLevels, ", ") + +const + defaultDataDirDesc = defaultDataDir() + defaultPort = 30303 + defaultMetricsServerPort = 9093 + defaultEthRpcPort = 8545 + defaultEthWsPort = 8546 + defaultEthGraphqlPort = 8547 + defaultListenAddress = (static ValidIpAddress.init("0.0.0.0")) + defaultAdminListenAddress = (static ValidIpAddress.init("127.0.0.1")) + defaultListenAddressDesc = $defaultListenAddress + defaultAdminListenAddressDesc = $defaultAdminListenAddress + logLevelDesc = getLogLevels() type - ConfigStatus* = enum - ## Configuration status flags - Success, ## Success - EmptyOption, ## No options in category - ErrorUnknownOption, ## Unknown option in command line found - ErrorParseOption, ## Error in parsing command line option - ErrorIncorrectOption, ## Option has incorrect value - Error ## Unspecified error - - RpcFlags* {.pure.} = enum - ## RPC flags - Enabled ## RPC enabled - Eth ## enable eth_ set of RPC API - Debug ## enable debug_ set of RPC API + PruneMode* {.pure.} = enum + Full + Archive + + NimbusCmd* = enum + noCommand - ProtocolFlags* {.pure.} = enum + ProtocolFlag* {.pure.} = enum ## Protocol flags Eth ## enable eth subprotocol Les ## enable les subprotocol - RpcConfiguration* = object - ## JSON-RPC configuration object - flags*: set[RpcFlags] ## RPC flags - binds*: seq[TransportAddress] ## RPC bind address - - GraphqlConfiguration* = object - enabled*: bool - address*: TransportAddress - - NetworkFlags* = enum - ## Ethereum network flags - NoDiscover ## Peer discovery disabled - V5Discover ## Dicovery V5 enabled - NetworkIdSet ## prevent CustomNetwork replacement - - DebugFlags* {.pure.} = enum - ## Debug selection flags - Enabled, ## Debugging enabled - Test1, ## Test1 enabled - Test2, ## Test2 enabled - Test3 ## Test3 enabled - - NetConfiguration* = object - ## Network configuration object - flags*: set[NetworkFlags] ## Network flags - bootNodes*: seq[ENode] ## List of bootnodes - staticNodes*: seq[ENode] ## List of static nodes to connect to - customBootNodes*: seq[ENode] - bindPort*: uint16 ## Main TCP bind port - discPort*: uint16 ## Discovery UDP bind port - metricsServer*: bool ## Enable metrics server - metricsServerPort*: uint16 ## metrics HTTP server port - maxPeers*: int ## Maximum allowed number of peers - maxPendingPeers*: int ## Maximum allowed pending peers - networkId*: NetworkId ## Network ID as integer - ident*: string ## Server ident name string - nodeKey*: string ## Server private key - nat*: NatStrategy ## NAT strategy - externalIP*: string ## user-provided external IP - protocols*: set[ProtocolFlags]## Enabled subprotocols - - DebugConfiguration* = object - ## Debug configuration object - flags*: set[DebugFlags] ## Debug flags - logLevel*: LogLevel ## Log level - logFile*: string ## Log file - logMetrics*: bool ## Enable metrics logging - logMetricsInterval*: int ## Metrics logging interval + RpcFlag* {.pure.} = enum + ## RPC flags + Eth ## enable eth_ set of RPC API + Debug ## enable debug_ set of RPC API + + EnodeList* = object + value*: seq[Enode] - PruneMode* {.pure.} = enum - Full - Archive + Protocols* = object + value*: set[ProtocolFlag] - NimbusConfiguration* = ref object + RpcApi* = object + value*: set[RpcFlag] + + NimbusConf* = object of RootObj ## Main Nimbus configuration object - dataDir*: string - keyStore*: string - prune*: PruneMode - graphql*: GraphqlConfiguration - rpc*: RpcConfiguration ## JSON-RPC configuration - ws*: RpcConfiguration ## Websocket JSON-RPC configuration - net*: NetConfiguration ## Network configuration - debug*: DebugConfiguration ## Debug configuration - customNetwork*: CustomNetwork ## Custom Genesis Configuration - importKey*: string - importFile*: string - verifyFromOk*: bool ## activate `verifyFrom` setting - verifyFrom*: uint64 ## verification start block, 0 for disable - engineSigner*: EthAddress ## Miner account -const - # these are public network id - CustomNet* = 0.NetworkId - MainNet* = 1.NetworkId - # No longer used: MordenNet = 2 - RopstenNet* = 3.NetworkId - RinkebyNet* = 4.NetworkId - GoerliNet* = 5.NetworkId - KovanNet* = 42.NetworkId + dataDir* {. + separator: "ETHEREUM OPTIONS:" + desc: "The directory where nimbus will store all blockchain data" + defaultValue: defaultDataDir() + defaultValueDesc: $defaultDataDirDesc + abbr: "d" + name: "data-dir" }: OutDir + + keyStore* {. + desc: "Directory for the keystore files" + defaultValue: defaultKeystoreDir() + defaultValueDesc: "inside datadir" + abbr: "k" + name: "key-store" }: OutDir + + pruneMode* {. + desc: "Blockchain prune mode (Full or Archive)" + defaultValue: PruneMode.Full + defaultValueDesc: $PruneMode.Full + abbr : "p" + name: "prune-mode" }: PruneMode + + importBlocks* {. + desc: "Import RLP encoded block(s) in a file, validate, write to database and quit" + defaultValue: "" + abbr: "b" + name: "import-blocks" }: InputFile + + importKey* {. + desc: "Import unencrypted 32 bytes hex private key file" + defaultValue: "" + abbr: "e" + name: "import-key" }: InputFile + + engineSigner* {. + desc: "Enable sealing engine to run and producing blocks at specified interval (only PoA/Clique supported)" + defaultValue: ZERO_ADDRESS + defaultValueDesc: "" + abbr: "s" + name: "engine-signer" }: EthAddress + + verifyFrom* {. + desc: "Enable extra verification when current block number greater than verify-from" + defaultValueDesc: "" + name: "verify-from" }: Option[uint64] + + case cmd* {. + command + defaultValue: noCommand }: NimbusCmd + + of noCommand: + mainnet* {. + separator: "\pETHEREUM NETWORK OPTIONS:" + defaultValue: false + defaultValueDesc: "" + desc: "Use Ethereum main network (default)" + }: bool + + ropsten* {. + desc: "Use Ropsten test network (proof-of-work, the one most like Ethereum mainnet)" + defaultValue: false + defaultValueDesc: "" + }: bool + + rinkeby* {. + desc: "Use Rinkeby test network (proof-of-authority, for those running Geth clients)" + defaultValue: false + defaultValueDesc: "" + }: bool + + goerli* {. + desc: "Use Görli test network (proof-of-authority, works across all clients)" + defaultValue: false + defaultValueDesc: "" + }: bool + + kovan* {. + desc: "Use Kovan test network (proof-of-authority, for those running OpenEthereum clients)" + defaultValue: false + defaultValueDesc: "" + }: bool + + networkId* {. + desc: "Network id (1=mainnet, 3=ropsten, 4=rinkeby, 5=goerli, 42=kovan, other=custom)" + defaultValueDesc: "mainnet" + abbr: "i" + name: "network-id" }: Option[NetworkId] + + customNetwork* {. + desc: "Use custom genesis block for private Ethereum Network (as /path/to/genesis.json)" + defaultValueDesc: "" + abbr: "c" + name: "custom-network" }: Option[CustomNetwork] + + bootNodes* {. + separator: "\pNETWORKING OPTIONS:" + desc: "Comma separated enode URLs for P2P discovery bootstrap (set v4+v5 instead for light servers)" + defaultValue: EnodeList() + defaultValueDesc: "" + name: "boot-nodes" }: EnodeList + + bootNodesv4* {. + desc: "Comma separated enode URLs for P2P v4 discovery bootstrap (light server, full nodes)" + defaultValue: EnodeList() + defaultValueDesc: "" + name: "boot-nodes-v4" }: EnodeList + + bootNodesv5* {. + desc: "Comma separated enode URLs for P2P v5 discovery bootstrap (light server, light nodes)" + defaultValue: EnodeList() + defaultValueDesc: "" + name: "boot-nodes-v5" }: EnodeList + + staticNodes* {. + desc: "Comma separated enode URLs to connect with" + defaultValue: EnodeList() + defaultValueDesc: "" + name: "static-nodes" }: EnodeList + + listenAddress* {. + desc: "Listening address for the Ethereum P2P and Discovery traffic" + defaultValue: defaultListenAddress + defaultValueDesc: $defaultListenAddressDesc + name: "listen-address" }: ValidIpAddress + + tcpPort* {. + desc: "Network listening TCP port" + defaultValue: defaultPort + defaultValueDesc: $defaultPort + name: "tcp-port" }: Port + + udpPort* {. + desc: "Network listening UDP port" + defaultValue: 0 # set udpPort defaultValue in `makeConfig` + defaultValueDesc: "default to --tcp-port" + name: "udp-port" }: Port + + maxPeers* {. + desc: "The maximum number of peers to connect to" + defaultValue: 25 + name: "max-peers" }: int + + maxEndPeers* {. + desc: "Maximum number of pending connection attempts" + defaultValue: 0 + name: "max-end-peers" }: int + + nat* {. + desc: "Specify method to use for determining public address. " & + "Must be one of: any, none, upnp, pmp, extip:" + defaultValue: NatConfig(hasExtIp: false, nat: NatAny) + defaultValueDesc: "any" + name: "nat" .}: NatConfig + + noDiscover* {. + desc: "Disables the peer discovery mechanism (manual peer addition)" + defaultValue: false + name: "no-discover" .}: bool + + discv5Enabled* {. + desc: "Enable Discovery v5" + defaultValue: false + name: "discv5" .}: bool + + nodeKeyHex* {. + desc: "P2P node private key (as hexadecimal string)" + defaultValue: "" + defaultValueDesc: "random" + name: "node-key" .}: string + + agentString* {. + desc: "Node agent string which is used as identifier in network" + defaultValue: NimbusIdent + defaultValueDesc: $NimbusIdent + name: "agent-string" .}: string + + protocols* {. + desc: "Enable specific set of protocols (Eth, Les)" + defaultValue: Protocols(value: {ProtocolFlag.Eth}) + defaultValueDesc: $ProtocolFlag.Eth + name: "protocols" .}: Protocols + + metricsEnabled* {. + separator: "\pLOCAL SERVICE OPTIONS:" + desc: "Enable the metrics server" + defaultValue: false + name: "metrics" }: bool + + metricsPort* {. + desc: "Listening HTTP port of the metrics server" + defaultValue: defaultMetricsServerPort + defaultValueDesc: $defaultMetricsServerPort + name: "metrics-port" }: Port + + metricsAddress* {. + desc: "Listening address of the metrics server" + defaultValue: defaultAdminListenAddress + defaultValueDesc: $defaultAdminListenAddressDesc + name: "metrics-address" }: ValidIpAddress + + rpcEnabled* {. + desc: "Enable the JSON-RPC server" + defaultValue: false + name: "rpc" }: bool + + rpcPort* {. + desc: "HTTP port for the JSON-RPC service" + defaultValue: defaultEthRpcPort + defaultValueDesc: $defaultEthRpcPort + name: "rpc-port" }: Port + + rpcAddress* {. + desc: "Listening address of the RPC server" + defaultValue: defaultAdminListenAddress + defaultValueDesc: $defaultAdminListenAddressDesc + name: "rpc-address" }: ValidIpAddress + + rpcApi* {. + desc: "Enable specific set of RPC API from list (comma-separated) (available: eth, debug)" + defaultValue: RpcApi(value: {RpcFlag.Eth}) + defaultValueDesc: $RpcFlag.Eth + name: "rpc-api" }: RpcApi + + wsEnabled* {. + desc: "Enable the Websocket JSON-RPC server" + defaultValue: false + name: "ws" }: bool + + wsPort* {. + desc: "Websocket port for the JSON-RPC service" + defaultValue: defaultEthWsPort + defaultValueDesc: $defaultEthWsPort + name: "ws-port" }: Port + + wsAddress* {. + desc: "Listening address of the Websocket JSON-RPC server" + defaultValue: defaultAdminListenAddress + defaultValueDesc: $defaultAdminListenAddressDesc + name: "ws-address" }: ValidIpAddress + + wsApi* {. + desc: "Enable specific set of Websocket RPC API from list (comma-separated) (available: eth, debug)" + defaultValue: RpcApi(value: {RpcFlag.Eth}) + defaultValueDesc: $RpcFlag.Eth + name: "ws-api" }: RpcApi + + graphqlEnabled* {. + desc: "Enable the HTTP-GraphQL server" + defaultValue: false + name: "graphql" }: bool + + graphqlPort* {. + desc: "HTTP port for the GraphQL service" + defaultValue: defaultEthGraphqlPort + defaultValueDesc: $defaultEthGraphqlPort + name: "graphql-port" }: Port + + graphqlAddress* {. + desc: "Listening address of the GraphQL server" + defaultValue: defaultAdminListenAddress + defaultValueDesc: $defaultAdminListenAddressDesc + name: "graphql-address" }: ValidIpAddress + + logLevel* {. + separator: "\pLOGGING AND DEBUGGING OPTIONS:" + desc: "Sets the log level for process and topics (" & logLevelDesc & ")" + defaultValue: LogLevel.INFO + defaultValueDesc: $LogLevel.INFO + name: "log-level" }: LogLevel + + logFile* {. + desc: "Specifies a path for the written Json log file" + name: "log-file" }: Option[OutFile] + + logMetricsEnabled* {. + desc: "Enable metrics logging" + defaultValue: false + name: "log-metrics" .}: bool + + logMetricsInterval* {. + desc: "Interval at which to log metrics, in seconds" + defaultValue: 10 + name: "log-metrics-interval" .}: int + +proc parseCmdArg(T: type NetworkId, p: TaintedString): T = + parseInt(p.string).T + +proc completeCmdArg(T: type NetworkId, val: TaintedString): seq[string] = + return @[] + +proc parseCmdArg(T: type EthAddress, p: TaintedString): T = + try: + result = hexToByteArray(p.string, 20) + except CatchableError: + raise newException(ValueError, "failed to parse EthAddress") -const - defaultRpcApi = {RpcFlags.Eth} - defaultProtocols = {ProtocolFlags.Eth} - defaultLogLevel = LogLevel.WARN - defaultNetwork = MainNet - -var nimbusConfig {.threadvar.}: NimbusConfiguration - -proc getConfiguration*(): NimbusConfiguration {.gcsafe.} - -proc `$`*(c: ChainId): string = - $(c.int) - -proc toFork*(c: ChainConfig, number: BlockNumber): Fork = - if number >= c.londonBlock: FkLondon - elif number >= c.berlinBlock: FkBerlin - elif number >= c.istanbulBlock: FkIstanbul - elif number >= c.petersburgBlock: FkPetersburg - elif number >= c.constantinopleBlock: FkConstantinople - elif number >= c.byzantiumBlock: FkByzantium - elif number >= c.eip158Block: FkSpurious - elif number >= c.eip150Block: FkTangerine - elif number >= c.homesteadBlock: FkHomestead - else: FkFrontier - -proc chainConfig*(id: NetworkId, cn: CustomNetwork): ChainConfig = - # For some public networks, NetworkId and ChainId value are identical - # but that is not always the case - - result = case id - of MainNet: - ChainConfig( - poaEngine: false, # TODO: use real engine conf: PoW - chainId: MainNet.ChainId, - homesteadBlock: 1_150_000.toBlockNumber, # 14/03/2016 20:49:53 - daoForkBlock: 1_920_000.toBlockNumber, - daoForkSupport: true, - eip150Block: 2_463_000.toBlockNumber, # 18/10/2016 17:19:31 - eip150Hash: toDigest("2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0"), - eip155Block: 2_675_000.toBlockNumber, # 22/11/2016 18:15:44 - eip158Block: 2_675_000.toBlockNumber, - byzantiumBlock: 4_370_000.toBlockNumber, # 16/10/2017 09:22:11 - constantinopleBlock: 7_280_000.toBlockNumber, # Never Occured in MainNet - petersburgBlock:7_280_000.toBlockNumber, # 28/02/2019 07:52:04 - istanbulBlock: 9_069_000.toBlockNumber, # 08/12/2019 12:25:09 - muirGlacierBlock: 9_200_000.toBlockNumber, # 02/01/2020 08:30:49 - berlinBlock: 12_244_000.toBlockNumber, # 15/04/2021 10:07:03 - londonBlock: 12_965_000.toBlockNumber, # 05/08/2021 12:33:42 - ) - of RopstenNet: - ChainConfig( - poaEngine: false, # TODO: use real engine conf: PoW - chainId: RopstenNet.ChainId, - homesteadBlock: 0.toBlockNumber, - daoForkSupport: false, - eip150Block: 0.toBlockNumber, - eip150Hash: toDigest("41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d"), - eip155Block: 10.toBlockNumber, - eip158Block: 10.toBlockNumber, - byzantiumBlock: 1_700_000.toBlockNumber, - constantinopleBlock: 4_230_000.toBlockNumber, - petersburgBlock:4_939_394.toBlockNumber, - istanbulBlock: 6_485_846.toBlockNumber, - muirGlacierBlock: 7_117_117.toBlockNumber, - berlinBlock: 9_812_189.toBlockNumber, - londonBlock: 10_499_401.toBlockNumber # June 24, 2021 - ) - of RinkebyNet: - ChainConfig( - poaEngine: true, # TODO: use real engine conf: PoA - chainId: RinkebyNet.ChainId, - homesteadBlock: 1.toBlockNumber, - daoForkSupport: false, - eip150Block: 2.toBlockNumber, - eip150Hash: toDigest("9b095b36c15eaf13044373aef8ee0bd3a382a5abb92e402afa44b8249c3a90e9"), - eip155Block: 3.toBlockNumber, - eip158Block: 3.toBlockNumber, - byzantiumBlock: 1_035_301.toBlockNumber, - constantinopleBlock: 3_660_663.toBlockNumber, - petersburgBlock:4_321_234.toBlockNumber, - istanbulBlock: 5_435_345.toBlockNumber, - muirGlacierBlock: 8_290_928.toBlockNumber, # never occured in rinkeby network - berlinBlock: 8_290_928.toBlockNumber, - londonBlock: 8_897_988.toBlockNumber # July 7, 2021 - ) - of GoerliNet: - ChainConfig( - poaEngine: true, # TODO: use real engine conf: PoA - chainId: GoerliNet.ChainId, - homesteadBlock: 0.toBlockNumber, - daoForkSupport: false, - eip150Block: 0.toBlockNumber, - eip150Hash: toDigest("0000000000000000000000000000000000000000000000000000000000000000"), - eip155Block: 0.toBlockNumber, - eip158Block: 0.toBlockNumber, - byzantiumBlock: 0.toBlockNumber, - constantinopleBlock: 0.toBlockNumber, - petersburgBlock: 0.toBlockNumber, - istanbulBlock: 1_561_651.toBlockNumber, - muirGlacierBlock: 4_460_644.toBlockNumber, # never occured in goerli network - berlinBlock: 4_460_644.toBlockNumber, - londonBlock: 5_062_605.toBlockNumber # June 30, 2021 - ) - else: - # everything else will use CustomNet config - trace "Custom genesis block configuration loaded", conf=cn.config - cn.config +proc completeCmdArg(T: type EthAddress, val: TaintedString): seq[string] = + return @[] proc processList(v: string, o: var seq[string]) = ## Process comma-separated list of strings. @@ -256,564 +434,123 @@ proc processList(v: string, o: var seq[string]) = if len(n) > 0: o.add(n) -proc processInteger*(v: string, o: var int): ConfigStatus = - ## Convert string to integer. - try: - o = parseInt(v) - result = Success - except ValueError: - result = ErrorParseOption - -proc processUInt64*(v: string, o: var uint64): ConfigStatus = - ## Convert string to integer. - try: - o = parseBiggestUInt(v).uint64 - result = Success - except ValueError: - result = ErrorParseOption - -proc processFloat*(v: string, o: var float): ConfigStatus = - ## Convert string to float. - try: - o = parseFloat(v) - result = Success - except ValueError: - result = ErrorParseOption - -proc processAddressPort(addrStr: string, ta: var TransportAddress): ConfigStatus = - try: - ta = initTAddress(addrStr) - return Success - except CatchableError: - return ErrorParseOption - -proc processAddressPortsList(v: string, - o: var seq[TransportAddress]): ConfigStatus = - ## Convert ;...; to list of `TransportAddress`. +proc parseCmdArg(T: type EnodeList, p: TaintedString): T = var list = newSeq[string]() - processList(v, list) + processList(p.string, list) for item in list: - var ta: TransportAddress - if processAddressPort(item, ta) == Success: - o.add ta - else: - return ErrorParseOption - result = Success + let res = ENode.fromString(item) + if res.isErr: + raise newException(ValueError, "failed to parse EnodeList") + result.value.add res.get() -proc processRpcApiList(v: string, flags: var set[RpcFlags]): ConfigStatus = - var list = newSeq[string]() - processList(v, list) - result = Success - for item in list: - case item.toLowerAscii() - of "eth": flags.incl RpcFlags.Eth - of "debug": flags.incl RpcFlags.Debug - else: - warn "unknown rpc api", name = item - result = ErrorIncorrectOption +proc completeCmdArg(T: type EnodeList, val: TaintedString): seq[string] = + return @[] -proc processProtocolList(v: string, flags: var set[ProtocolFlags]): ConfigStatus = +proc parseCmdArg(T: type Protocols, p: TaintedString): T = var list = newSeq[string]() - processList(v, list) - result = Success + processList(p.string, list) for item in list: case item.toLowerAscii() - of "eth": flags.incl ProtocolFlags.Eth - of "les": flags.incl ProtocolFlags.Les + of "eth": result.value.incl ProtocolFlag.Eth + of "les": result.value.incl ProtocolFlag.Les else: - warn "unknown protocol", name = item - result = ErrorIncorrectOption - -proc processENode(v: string, o: var ENode): ConfigStatus = - ## Convert string to ENode. - let res = ENode.fromString(v) - if res.isOk: - o = res[] - result = Success - else: - result = ErrorParseOption - -proc processENodesList(v: string, o: var seq[ENode]): ConfigStatus = - ## Convert comma-separated list of strings to list of ENode. - var - node: ENode - list = newSeq[string]() - processList(v, list) - for item in list: - result = processENode(item, node) - if result == Success: - o.add(node) - else: - break + raise newException(ValueError, "unknown protocol: " & item) + +proc completeCmdArg(T: type Protocols, val: TaintedString): seq[string] = + return @[] -proc processPruneList(v: string, flags: var PruneMode): ConfigStatus = +proc parseCmdArg(T: type RpcApi, p: TaintedString): T = var list = newSeq[string]() - processList(v, list) - result = Success + processList(p.string, list) for item in list: case item.toLowerAscii() - of "full": flags = PruneMode.Full - of "archive": flags = PruneMode.Archive + of "eth": result.value.incl RpcFlag.Eth + of "debug": result.value.incl RpcFlag.Debug else: - warn "unknown prune flags", name = item - result = ErrorIncorrectOption + raise newException(ValueError, "unknown rpc api: " & item) -proc processEthAddress(value: string, address: var EthAddress): ConfigStatus = - try: - address = hexToByteArray[20](value) - return Success - except CatchableError: - return ErrorParseOption - -proc processEthArguments(key, value: string): ConfigStatus = - result = Success - let config = getConfiguration() - case key.toLowerAscii() - of "keystore": - config.keyStore = value - of "datadir": - config.dataDir = value - of "prune": - result = processPruneList(value, config.prune) - of "import": - config.importFile = value - of "verifyfrom": - var res = 0u64 - result = processUInt64(value, res) - config.verifyFrom = uint64(result) - config.verifyFromOk = true - of "engine-signer": - result = processEthAddress(value, config.engineSigner) - of "import-key": - config.importKey = value - else: - result = EmptyOption - -proc processRpcArguments(key, value: string): ConfigStatus = - ## Processes only `RPC` related command line options - result = Success - let config = getConfiguration() - let skey = key.toLowerAscii() - if skey == "rpc": - if RpcFlags.Enabled notin config.rpc.flags: - config.rpc.flags.incl(RpcFlags.Enabled) - config.rpc.flags.incl(defaultRpcApi) - elif skey == "rpcbind": - config.rpc.binds.setLen(0) - result = processAddressPortsList(value, config.rpc.binds) - elif skey == "rpcapi": - if RpcFlags.Enabled in config.rpc.flags: - config.rpc.flags.excl(defaultRpcApi) - else: - config.rpc.flags.incl(RpcFlags.Enabled) - result = processRpcApiList(value, config.rpc.flags) - else: - result = EmptyOption - -proc processGraphqlArguments(key, value: string): ConfigStatus = - ## Processes only `Graphql` related command line options - result = Success - let conf = getConfiguration() - case key.toLowerAscii() - of "graphql": - conf.graphql.enabled = true - of "graphqlbind": - result = processAddressPort(value, conf.graphql.address) - else: - result = EmptyOption - -proc processWsArguments(key, value: string): ConfigStatus = - ## Processes only `Websocket RPC` related command line options - result = Success - let config = getConfiguration() - let skey = key.toLowerAscii() - if skey == "ws": - if RpcFlags.Enabled notin config.ws.flags: - config.ws.flags.incl(RpcFlags.Enabled) - config.ws.flags.incl(defaultRpcApi) - elif skey == "wsbind": - config.ws.binds.setLen(0) - result = processAddressPortsList(value, config.ws.binds) - elif skey == "wsapi": - if RpcFlags.Enabled in config.ws.flags: - config.ws.flags.excl(defaultRpcApi) - else: - config.ws.flags.incl(RpcFlags.Enabled) - result = processRpcApiList(value, config.ws.flags) - else: - result = EmptyOption +proc completeCmdArg(T: type RpcApi, val: TaintedString): seq[string] = + return @[] -proc setBootnodes(onodes: var seq[ENode], nodeUris: openarray[string]) = - var node: ENode - onodes = newSeqOfCap[ENode](nodeUris.len) +proc parseCmdArg(T: type CustomNetwork, p: TaintedString): T = + try: + if not loadCustomNetwork(p.string, result): + raise newException(ValueError, "failed to load customNetwork") + except Exception as exc: + # on linux/mac, nim compiler refuse to compile + # with unlisted exception error + raise newException(ValueError, "failed to load customNetwork") + +proc completeCmdArg(T: type CustomNetwork, val: TaintedString): seq[string] = + return @[] + +proc setBootnodes(output: var seq[ENode], nodeUris: openarray[string]) = + output = newSeqOfCap[ENode](nodeUris.len) for item in nodeUris: - doAssert(processENode(item, node) == Success) - onodes.add(node) - - -proc setNetwork(conf: var NetConfiguration, id: NetworkId) = - ## Set network id and default network bootnodes - conf.networkId = id - case id - of MainNet: - conf.bootNodes.setBootnodes(MainnetBootnodes) - of RopstenNet: - conf.bootNodes.setBootnodes(RopstenBootnodes) - of RinkebyNet: - conf.bootNodes.setBootnodes(RinkebyBootnodes) - of GoerliNet: - conf.bootNodes.setBootnodes(GoerliBootnodes) - of KovanNet: - conf.bootNodes.setBootnodes(KovanBootnodes) + output.add(ENode.fromString(item).tryGet()) + +proc getBootNodes*(conf: NimbusConf): seq[Enode] = + if conf.mainnet: + result.setBootnodes(MainnetBootnodes) + elif conf.ropsten: + result.setBootnodes(RopstenBootnodes) + elif conf.rinkeby: + result.setBootnodes(RinkebyBootnodes) + elif conf.goerli: + result.setBootnodes(GoerliBootnodes) + elif conf.kovan: + result.setBootnodes(KovanBootnodes) + elif conf.bootnodes.value.len > 0: + result = conf.bootnodes.value + elif conf.bootnodesv4.value.len > 0: + result = conf.bootnodesv4.value + elif conf.bootnodesv5.value.len > 0: + result = conf.bootnodesv5.value + +proc getNetworkId(conf: NimbusConf): Option[NetworkId] = + if conf.mainnet: + some MainNet + elif conf.ropsten: + some RopstenNet + elif conf.rinkeby: + some RinkebyNet + elif conf.goerli: + some GoerliNet + elif conf.kovan: + some KovanNet else: - # everything else will use bootnodes - # from --bootnodes switch - discard - -proc processNetArguments(key, value: string): ConfigStatus = - ## Processes only `Networking` related command line options - result = Success - let config = getConfiguration() - let skey = key.toLowerAscii() - if skey == "bootnodes": - result = processENodesList(value, config.net.customBootNodes) - elif skey == "bootnodesv4": - result = processENodesList(value, config.net.customBootNodes) - elif skey == "bootnodesv5": - result = processENodesList(value, config.net.customBootNodes) - elif skey == "staticnodes": - result = processENodesList(value, config.net.staticNodes) - elif skey == "testnet": - config.net.setNetwork(RopstenNet) - elif skey == "mainnet": - config.net.setNetwork(MainNet) - elif skey == "ropsten": - config.net.setNetwork(RopstenNet) - elif skey == "rinkeby": - config.net.setNetwork(RinkebyNet) - elif skey == "goerli": - config.net.setNetwork(GoerliNet) - elif skey == "kovan": - config.net.setNetwork(KovanNet) - elif skey == "customnetwork": - if not loadCustomNetwork(value, config.customNetwork): - result = Error - if NetworkIdSet notin config.net.flags: - # prevent clash with --networkid if it already set - # because any --networkid value that is not - # in the public network will also translated as - # CustomNetwork - config.net.networkId = CustomNet - elif skey == "networkid": - var res = 0 - result = processInteger(value, res) - if result == Success: - config.net.setNetwork(NetworkId(res)) - config.net.flags.incl NetworkIdSet - elif skey == "nodiscover": - config.net.flags.incl(NoDiscover) - elif skey == "v5discover": - config.net.flags.incl(V5Discover) - config.net.customBootNodes.setBootnodes(DiscoveryV5Bootnodes) - elif skey == "port": - var res = 0 - result = processInteger(value, res) - if result == Success: - config.net.bindPort = uint16(res and 0xFFFF) - elif skey == "discport": - var res = 0 - result = processInteger(value, res) - if result == Success: - config.net.discPort = uint16(res and 0xFFFF) - elif skey == "metrics": - config.net.metricsServer = true - elif skey == "metricsport": - var res = 0 - result = processInteger(value, res) - if result == Success: - config.net.metricsServerPort = uint16(res and 0xFFFF) - elif skey == "maxpeers": - var res = 0 - result = processInteger(value, res) - if result == Success: - config.net.maxPeers = res - elif skey == "maxpendpeers": - var res = 0 - result = processInteger(value, res) - if result == Success: - config.net.maxPendingPeers = res - elif skey == "nodekey": - config.net.nodeKey = value - elif skey == "ident": - config.net.ident = value - elif skey == "nat": - case value.toLowerAscii: - of "any": - config.net.nat = NatAny - of "upnp": - config.net.nat = NatUpnp - of "pmp": - config.net.nat = NatPmp - of "none": - config.net.nat = NatNone - else: - if isIpAddress(value): - config.net.externalIP = value - config.net.nat = NatNone - else: - error "not a valid NAT mechanism, nor a valid IP address", value - result = ErrorParseOption - elif skey == "protocols": - config.net.protocols = {} - result = processProtocolList(value, config.net.protocols) - else: - result = EmptyOption - -proc processDebugArguments(key, value: string): ConfigStatus = - ## Processes only `Debug` related command line options - let config = getConfiguration() - result = Success - let skey = key.toLowerAscii() - if skey == "debug": - config.debug.flags.incl(DebugFlags.Enabled) - elif skey == "test": - var res = newSeq[string]() - processList(value, res) - for item in res: - if item == "test1": - config.debug.flags.incl(DebugFlags.Test1) - elif item == "test2": - config.debug.flags.incl(DebugFlags.Test2) - elif item == "test3": - config.debug.flags.incl(DebugFlags.Test3) - elif skey == "log-level": - try: - let logLevel = parseEnum[LogLevel](value) - if logLevel >= enabledLogLevel: - config.debug.logLevel = logLevel - else: - result = ErrorIncorrectOption - except ValueError: - result = ErrorIncorrectOption - elif skey == "log-file": - if len(value) == 0: - result = ErrorIncorrectOption - else: - config.debug.logFile = value - elif skey == "logmetrics": - config.debug.logMetrics = true - elif skey == "logmetricsinterval": - var res = 0 - result = processInteger(value, res) - if result == Success: - config.debug.logMetricsInterval = res - else: - result = EmptyOption - -proc dumpConfiguration*(): string = - ## Dumps current configuration as string - let config = getConfiguration() - result = repr config - -template processArgument(processor, key, value, msg: untyped) = - ## Checks if arguments got processed successfully - var res = processor(string(key), string(value)) - if res == Success: - result = res - continue - elif res == ErrorParseOption: - msg = "Error processing option '" & key & "' with value '" & value & "'." - result = res - break - elif res == ErrorIncorrectOption: - msg = "Incorrect value for option '" & key & "': '" & value & "'." - result = res - break - -proc getDefaultDataDir*(): string = - when defined(windows): - "AppData" / "Roaming" / "Nimbus" - elif defined(macosx): - "Library" / "Application Support" / "Nimbus" - else: - ".cache" / "nimbus" - -proc getDefaultKeystoreDir*(): string = - getDefaultDataDir() / "keystore" - -proc initConfiguration(): NimbusConfiguration = - ## Allocates and initializes `NimbusConfiguration` with default values - result = new NimbusConfiguration - - ## Graphql defaults - result.graphql.enabled = false - result.graphql.address = initTAddress("127.0.0.1:8547") - - ## RPC defaults - result.rpc.flags = {} - result.rpc.binds = @[initTAddress("127.0.0.1:8545")] - - ## Websocket RPC defaults - result.ws.flags = {} - result.ws.binds = @[initTAddress("127.0.0.1:8546")] - - ## Network defaults - result.net.setNetwork(defaultNetwork) - result.net.maxPeers = 25 - result.net.maxPendingPeers = 0 - result.net.bindPort = 30303'u16 - result.net.discPort = 30303'u16 - result.net.metricsServer = false - result.net.metricsServerPort = 9093'u16 - result.net.ident = NimbusIdent - result.net.nat = NatAny - result.net.protocols = defaultProtocols - - const - dataDir = getDefaultDataDir() - keystore = getDefaultKeystoreDir() - - result.dataDir = getHomeDir() / dataDir - result.keystore = getHomeDir() / keystore - result.prune = PruneMode.Full - - ## Debug defaults - result.debug.flags = {} - result.debug.logLevel = defaultLogLevel - result.debug.logMetrics = false - result.debug.logMetricsInterval = 10 - -proc getConfiguration*(): NimbusConfiguration = - ## Retreive current configuration object `NimbusConfiguration`. - if isNil(nimbusConfig): - nimbusConfig = initConfiguration() - result = nimbusConfig - -proc getHelpString*(): string = - var logLevels: seq[string] - for level in LogLevel: - if level < enabledLogLevel: - continue - logLevels.add($level) - - result = """ - -USAGE: - nimbus [options] - -ETHEREUM OPTIONS: - --datadir: Base directory for all blockchain-related data - --keystore: Directory for the keystore (default: inside datadir) - --prune: Blockchain prune mode (full or archive, default: full) - --import: Import RLP encoded block(s), validate, write to database and quit - --engine-signer: Enables mining. value is EthAddress in hex - --import-key: Import unencrypted 32 bytes hex private key file - -NETWORKING OPTIONS: - --bootnodes: Comma separated enode URLs for P2P discovery bootstrap (set v4+v5 instead for light servers) - --bootnodesv4: Comma separated enode URLs for P2P v4 discovery bootstrap (light server, full nodes) - --bootnodesv5: Comma separated enode URLs for P2P v5 discovery bootstrap (light server, light nodes) - --staticnodes: Comma separated enode URLs to connect with - --port: Network listening TCP port (default: 30303) - --discport: Network listening UDP port (defaults to --port argument) - --maxpeers: Maximum number of network peers (default: 25) - --maxpendpeers: Maximum number of pending connection attempts (default: 0) - --nat: NAT port mapping mechanism (any|none|upnp|pmp|) (default: "any") - --nodiscover Disables the peer discovery mechanism (manual peer addition) - --v5discover Enables the experimental RLPx V5 (topic discovery) mechanism - --nodekey: P2P node private key (as hexadecimal string) - --ident: Client identifier (default is '$1') - --protocols: Enable specific set of protocols (default: $4) - -ETHEREUM NETWORK OPTIONS: - --mainnet Use Ethereum main network (default) - --testnet Use Ropsten test network - --ropsten Use Ropsten test network (proof-of-work, the one most like Ethereum mainnet) - --goerli Use Görli test network (proof-of-authority, works across all clients) - --rinkeby Use Rinkeby test network (proof-of-authority, for those running Geth clients) - --kovan Use Kovan test network (proof-of-authority, for those running OpenEthereum clients) - --networkid: Network id (0=custom, 1=mainnet, 3=ropsten, 4=rinkeby, 5=goerli, 42=kovan, other...) - --customnetwork: Use custom genesis block for private Ethereum Network (as /path/to/genesis.json) - -LOCAL SERVICE OPTIONS: - --metrics Enable the metrics HTTP server - --metricsport: Set port (always on localhost) metrics HTTP server will bind to (default: 9093) - --rpc Enable the HTTP-RPC server - --rpcbind: Set address:port pair(s) (comma-separated) HTTP-RPC server will bind to (default: localhost:8545) - --rpcapi: Enable specific set of RPC API from list (comma-separated) (available: eth, debug) - --graphql Enable the HTTP-GraphQL server - --graphqlbind: Set address:port pair GraphQL server will bind (default: localhost:8547) - --ws Enable the Websocket JSON-RPC server - --wsbind: Set address:port pair Websocket JSON-RPC server will bind to (default: localhost:8546) - --wsapi: Enable specific set of Websocket RPC API from list (comma-separated) (available: eth, debug) - -LOGGING AND DEBUGGING OPTIONS: - --log-level: One of: $2 (default: $3) - --log-file: Optional log file, replacing stdout - --logmetrics Enable metrics logging - --logmetricsinterval: Interval at which to log metrics, in seconds (default: 10) - --debug Enable debug mode - --test: Perform specified test -""" % [ - NimbusIdent, - join(logLevels, ", "), - $defaultLogLevel, - strip($defaultProtocols, chars = {'{','}'}), - ] - -when declared(os.paramCount): # not available with `--app:lib` - proc processArguments*(msg: var string, opt: var OptParser): ConfigStatus = - ## Process command line argument and update `NimbusConfiguration`. - let config = getConfiguration() - - # At this point `config.net.discPort` is likely populated with network default - # discPort. We want to override those if it is specified on the command line. - config.net.discPort = 0 - - var length = 0 - for kind, key, value in opt.getopt(): - result = Error - case kind - of cmdArgument: - discard - of cmdLongOption, cmdShortOption: - inc(length) - case key.toLowerAscii() - of "help", "h": - msg = getHelpString() - result = Success - break - of "version", "ver", "v": - msg = NimbusVersion - result = Success - break - else: - processArgument processEthArguments, key, value, msg - processArgument processRpcArguments, key, value, msg - processArgument processNetArguments, key, value, msg - processArgument processDebugArguments, key, value, msg - processArgument processGraphqlArguments, key, value, msg - processArgument processWsArguments, key, value, msg - if result != Success: - msg = "Unknown option: '" & key & "'." - break - of cmdEnd: - doAssert(false) # we're never getting this kind here - - if config.net.discPort == 0: - config.net.discPort = config.net.bindPort - - if NetworkIdSet notin config.net.flags and config.net.networkId == CustomNet: - # WARNING: networkId and chainId are two distinct things - # they usage should not be mixed in other places - # we only set networkId to chainId if networkId not set in cli and - # we are using custom genesis/custom chain config via json file. - config.net.networkId = NetworkId(config.customNetwork.config.chainId) - - proc processArguments*(msg: var string): ConfigStatus = - var opt = initOptParser() - processArguments(msg, opt) - -proc processConfiguration*(pathname: string): ConfigStatus = - ## Process configuration file `pathname` and update `NimbusConfiguration`. - result = Success + conf.networkId + +proc makeConfig*(cmdLine = commandLineParams()): NimbusConf = + result = NimbusConf.load( + cmdLine, + version = NimbusBuild, + copyrightBanner = NimbusHeader + ) + + result.networkId = result.getNetworkId() + + if result.networkId.isNone and result.customNetwork.isSome: + # WARNING: networkId and chainId are two distinct things + # they usage should not be mixed in other places. + # We only set networkId to chainId if networkId not set in cli and + # --custom-network is set. + # If chainId is not defined in config file, it's ok because + # the default networkId `CustomNetwork` has 0 value too. + result.networkId = some(NetworkId(result.customNetwork.get().config.chainId)) + + if result.networkId.isNone: + # bootnodes is set via getBootNodes + result.networkId = some MainNet + result.mainnet = true + + if result.udpPort == Port(0): + # if udpPort not set in cli, then + result.udpPort = result.tcpPort + + if result.customNetwork.isNone: + result.customNetwork = some CustomNetwork() + +when isMainModule: + # for testing purpose + discard makeConfig() diff --git a/nimbus/db/db_chain.nim b/nimbus/db/db_chain.nim index a745384191..1d26476efa 100644 --- a/nimbus/db/db_chain.nim +++ b/nimbus/db/db_chain.nim @@ -10,7 +10,7 @@ import stew/[byteutils], eth/trie/[hexary, db], eth/[common, rlp, p2p], chronicles, ../errors, ../constants, ./storage_types, - ../utils, ../config, ../chain_config + ../utils, ../chain_config type BaseChainDB* = ref object diff --git a/nimbus/genesis.nim b/nimbus/genesis.nim index 1db978ef80..459980bf82 100644 --- a/nimbus/genesis.nim +++ b/nimbus/genesis.nim @@ -1,9 +1,9 @@ import - std/[json, times, tables], + std/[times, tables], eth/[common, rlp, trie, p2p], stew/[byteutils], chronicles, eth/trie/db, ./db/[db_chain, state_db], - ./genesis_alloc, ./config, ./constants, + ./genesis_alloc, ./constants, ./chain_config, ./forks, ./p2p/gaslimit proc genesisBlockForNetwork*(id: NetworkId, cn: CustomNetwork): Genesis = diff --git a/nimbus/graphql/ethapi.nim b/nimbus/graphql/ethapi.nim index f6c966d224..7c2c9b8eed 100644 --- a/nimbus/graphql/ethapi.nim +++ b/nimbus/graphql/ethapi.nim @@ -11,6 +11,7 @@ import std/[strutils, times], stew/[results, byteutils], stint, eth/[common, rlp], chronos, + stew/shims/net, graphql, graphql/graphql as context, graphql/common/types, graphql/httpserver, graphql/instruments/query_complexity, @@ -1221,11 +1222,12 @@ proc setupGraphqlContext*(chainDB: BaseChainDB, ethNode: EthereumNode): GraphqlC ctx.initEthApi() ctx -proc setupGraphqlHttpServer*(conf: NimbusConfiguration, +proc setupGraphqlHttpServer*(conf: NimbusConf, chainDB: BaseChainDB, ethNode: EthereumNode): GraphqlHttpServerRef = let socketFlags = {ServerFlags.TcpNoDelay, ServerFlags.ReuseAddr} let ctx = setupGraphqlContext(chainDB, ethNode) - let sres = GraphqlHttpServerRef.new(ctx, conf.graphql.address, socketFlags = socketFlags) + let address = initTAddress(conf.graphqlAddress, conf.graphqlPort) + let sres = GraphqlHttpServerRef.new(ctx, address, socketFlags = socketFlags) if sres.isErr(): echo sres.error quit(QuitFailure) diff --git a/nimbus/nimbus.nim b/nimbus/nimbus.nim index 4ec15f21f5..8539cd3268 100644 --- a/nimbus/nimbus.nim +++ b/nimbus/nimbus.nim @@ -12,6 +12,7 @@ import import os, strutils, net, options, + stew/shims/net as stewNet, eth/keys, db/[storage_types, db_chain, select_backend], eth/common as eth_common, eth/p2p as eth_p2p, chronos, json_rpc/rpcserver, chronicles, @@ -21,7 +22,7 @@ import config, genesis, rpc/[common, p2p, debug], p2p/chain, eth/trie/db, metrics, metrics/[chronos_httpserver, chronicles_support], graphql/ethapi, context, - "."/[conf_utils, sealer, constants] + "."/[conf_utils, sealer, constants, chain_config] ## TODO: ## * No IPv6 support @@ -41,21 +42,28 @@ type sealingEngine: SealingEngineRef ctx: EthContext -proc start(nimbus: NimbusNode) = - var conf = getConfiguration() +template init(T: type RpcHttpServer, ip: ValidIpAddress, port: Port): T = + newRpcHttpServer([initTAddress(ip, port)]) +template init(T: type RpcWebSocketServer, ip: ValidIpAddress, port: Port): T = + newRpcWebSocketServer(initTAddress(ip, port)) + +proc start(nimbus: NimbusNode, conf: NimbusConf) = ## logging - setLogLevel(conf.debug.logLevel) - if len(conf.debug.logFile) != 0: + setLogLevel(conf.logLevel) + if conf.logFile.isSome: + let logFile = string conf.logFile.get() defaultChroniclesStream.output.outFile = nil # to avoid closing stdout - discard defaultChroniclesStream.output.open(conf.debug.logFile, fmAppend) + discard defaultChroniclesStream.output.open(logFile, fmAppend) - createDir(conf.dataDir) - let trieDB = trieDB newChainDb(conf.dataDir) + createDir(string conf.dataDir) + let trieDB = trieDB newChainDb(string conf.dataDir) + let networkId = conf.networkId.get() + let customNetwork = conf.customNetwork.get() var chainDB = newBaseChainDB(trieDB, - conf.prune == PruneMode.Full, - conf.net.networkId, - conf.customNetwork + conf.pruneMode == PruneMode.Full, + networkId, + customNetwork ) chainDB.populateProgress() @@ -63,115 +71,116 @@ proc start(nimbus: NimbusNode) = initializeEmptyDb(chainDb) doAssert(canonicalHeadHashKey().toOpenArray in trieDB) - if conf.importFile.len > 0: + if string(conf.importBlocks).len > 0: # success or not, we quit after importing blocks - if not importRlpBlock(conf.importFile, chainDB): + if not importRlpBlock(string conf.importBlocks, chainDB): quit(QuitFailure) else: quit(QuitSuccess) - if conf.keyStore.len > 0: - let res = nimbus.ctx.am.loadKeystores(conf.keyStore) + if string(conf.keyStore).len > 0: + let res = nimbus.ctx.am.loadKeystores(string conf.keyStore) if res.isErr: echo res.error() quit(QuitFailure) - if conf.importKey.len > 0: - let res = nimbus.ctx.am.importPrivateKey(conf.importKey) + if string(conf.importKey).len > 0: + let res = nimbus.ctx.am.importPrivateKey(string conf.importKey) if res.isErr: echo res.error() quit(QuitFailure) # metrics logging - if conf.debug.logMetrics: + if conf.logMetricsEnabled: # https://github.com/nim-lang/Nim/issues/17369 var logMetrics: proc(udata: pointer) {.gcsafe, raises: [Defect].} logMetrics = proc(udata: pointer) = {.gcsafe.}: let registry = defaultRegistry info "metrics", registry - discard setTimer(Moment.fromNow(conf.debug.logMetricsInterval.seconds), logMetrics) - discard setTimer(Moment.fromNow(conf.debug.logMetricsInterval.seconds), logMetrics) + discard setTimer(Moment.fromNow(conf.logMetricsInterval.seconds), logMetrics) + discard setTimer(Moment.fromNow(conf.logMetricsInterval.seconds), logMetrics) ## Creating P2P Server - let kpres = nimbus.ctx.hexToKeyPair(conf.net.nodekey) + let kpres = nimbus.ctx.hexToKeyPair(conf.nodeKeyHex) if kpres.isErr: echo kpres.error() quit(QuitFailure) let keypair = kpres.get() - - var address: Address - address.ip = parseIpAddress("0.0.0.0") - address.tcpPort = Port(conf.net.bindPort) - address.udpPort = Port(conf.net.discPort) - if conf.net.nat == NatNone: - if conf.net.externalIP != "": - # any required port redirection is assumed to be done by hand - address.ip = parseIpAddress(conf.net.externalIP) + var address = Address( + ip: conf.listenAddress, + tcpPort: conf.tcpPort, + udpPort: conf.udpPort + ) + + if conf.nat.hasExtIp: + # any required port redirection is assumed to be done by hand + address.ip = conf.nat.extIp else: # automated NAT traversal - let extIP = getExternalIP(conf.net.nat) + let extIP = getExternalIP(conf.nat.nat) # This external IP only appears in the logs, so don't worry about dynamic # IPs. Don't remove it either, because the above call does initialisation # and discovery for NAT-related objects. if extIP.isSome: address.ip = extIP.get() let extPorts = redirectPorts(tcpPort = address.tcpPort, - udpPort = address.udpPort, - description = NIMBUS_NAME & " " & NIMBUS_VERSION) + udpPort = address.udpPort, + description = NIMBUS_NAME & " " & NIMBUS_VERSION) if extPorts.isSome: (address.tcpPort, address.udpPort) = extPorts.get() - nimbus.ethNode = newEthereumNode(keypair, address, conf.net.networkId, - nil, conf.net.ident, + nimbus.ethNode = newEthereumNode(keypair, address, networkId, + nil, conf.agentString, addAllCapabilities = false, - minPeers = conf.net.maxPeers) + minPeers = conf.maxPeers) # Add protocol capabilities based on protocol flags - if ProtocolFlags.Eth in conf.net.protocols: + if ProtocolFlag.Eth in conf.protocols.value: nimbus.ethNode.addCapability eth - if ProtocolFlags.Les in conf.net.protocols: + if ProtocolFlag.Les in conf.protocols.value: nimbus.ethNode.addCapability les # chainRef: some name to avoid module-name/filed/function misunderstandings let chainRef = newChain(chainDB) nimbus.ethNode.chain = chainRef - if conf.verifyFromOk: - chainRef.extraValidation = 0 < conf.verifyFrom - chainRef.verifyFrom = conf.verifyFrom - - ## Creating RPC Server - if RpcFlags.Enabled in conf.rpc.flags: - nimbus.rpcServer = newRpcHttpServer(conf.rpc.binds) + if conf.verifyFrom.isSome: + let verifyFrom = conf.verifyFrom.get() + chainRef.extraValidation = 0 < verifyFrom + chainRef.verifyFrom = verifyFrom + + # Creating RPC Server + if conf.rpcEnabled: + nimbus.rpcServer = RpcHttpServer.init(conf.rpcAddress, conf.rpcPort) setupCommonRpc(nimbus.ethNode, conf, nimbus.rpcServer) - # Enable RPC APIs based on RPC flags and protocol flags - if RpcFlags.Eth in conf.rpc.flags and ProtocolFlags.Eth in conf.net.protocols: - setupEthRpc(nimbus.ethNode, nimbus.ctx, chainDB, nimbus.rpcServer) - if RpcFlags.Debug in conf.rpc.flags: - setupDebugRpc(chainDB, nimbus.rpcServer) - - # Creating Websocket RPC Server - if RpcFlags.Enabled in conf.ws.flags: - doAssert(conf.ws.binds.len > 0) - nimbus.wsRpcServer = newRpcWebSocketServer(conf.ws.binds[0]) - setupCommonRpc(nimbus.ethNode, conf, nimbus.wsRpcServer) - - # Enable Websocket RPC APIs based on RPC flags and protocol flags - if RpcFlags.Eth in conf.ws.flags and ProtocolFlags.Eth in conf.net.protocols: - setupEthRpc(nimbus.ethNode, nimbus.ctx, chainDB, nimbus.wsRpcServer) - if RpcFlags.Debug in conf.ws.flags: - setupDebugRpc(chainDB, nimbus.wsRpcServer) + # Enable RPC APIs based on RPC flags and protocol flags + if RpcFlag.Eth in conf.rpcApi.value and ProtocolFlag.Eth in conf.protocols.value: + setupEthRpc(nimbus.ethNode, nimbus.ctx, chainDB, nimbus.rpcServer) + if RpcFlag.Debug in conf.rpcApi.value: + setupDebugRpc(chainDB, nimbus.rpcServer) - ## Starting servers - if RpcFlags.Enabled in conf.rpc.flags: nimbus.rpcServer.rpc("admin_quit") do() -> string: {.gcsafe.}: nimbus.state = Stopping result = "EXITING" + nimbus.rpcServer.start() - if conf.graphql.enabled: + # Creating Websocket RPC Server + if conf.wsEnabled: + nimbus.wsRpcServer = RpcWebSocketServer.init(conf.wsAddress, conf.wsPort) + setupCommonRpc(nimbus.ethNode, conf, nimbus.wsRpcServer) + + # Enable Websocket RPC APIs based on RPC flags and protocol flags + if RpcFlag.Eth in conf.wsApi.value and ProtocolFlag.Eth in conf.protocols.value: + setupEthRpc(nimbus.ethNode, nimbus.ctx, chainDB, nimbus.wsRpcServer) + if RpcFlag.Debug in conf.wsApi.value: + setupDebugRpc(chainDB, nimbus.wsRpcServer) + + nimbus.wsRpcServer.start() + + if conf.graphqlEnabled: nimbus.graphqlServer = setupGraphqlHttpServer(conf, chainDB, nimbus.ethNode) nimbus.graphqlServer.start() @@ -186,25 +195,20 @@ proc start(nimbus: NimbusNode) = nimbus.sealingEngine.start() # metrics server - if conf.net.metricsServer: - let metricsAddress = "127.0.0.1" - info "Starting metrics HTTP server", address = metricsAddress, port = conf.net.metricsServerPort - startMetricsHttpServer(metricsAddress, Port(conf.net.metricsServerPort)) + if conf.metricsEnabled: + info "Starting metrics HTTP server", address = conf.metricsAddress, port = conf.metricsPort + startMetricsHttpServer($conf.metricsAddress, conf.metricsPort) # Connect directly to the static nodes - for enode in conf.net.staticNodes: + for enode in conf.staticNodes.value: asyncCheck nimbus.ethNode.peerPool.connectToNode(newNode(enode)) # Connect via discovery - if conf.net.customBootNodes.len > 0: - # override the default bootnodes from public network - waitFor nimbus.ethNode.connectToNetwork(conf.net.customBootNodes, - enableDiscovery = NoDiscover notin conf.net.flags) - else: - waitFor nimbus.ethNode.connectToNetwork(conf.net.bootNodes, - enableDiscovery = NoDiscover notin conf.net.flags) + let bootNodes = conf.getBootNodes() + waitFor nimbus.ethNode.connectToNetwork(bootNodes, + enableDiscovery = not conf.noDiscover) - if ProtocolFlags.Eth in conf.net.protocols: + if ProtocolFlag.Eth in conf.protocols.value: # TODO: temp code until the CLI/RPC interface is fleshed out let status = waitFor nimbus.ethNode.fastBlockchainSync() if status != syncSuccess: @@ -214,17 +218,18 @@ proc start(nimbus: NimbusNode) = # it might have been set to "Stopping" with Ctrl+C nimbus.state = Running -proc stop*(nimbus: NimbusNode) {.async, gcsafe.} = +proc stop*(nimbus: NimbusNode, conf: NimbusConf) {.async, gcsafe.} = trace "Graceful shutdown" - var conf = getConfiguration() - if RpcFlags.Enabled in conf.rpc.flags: + if conf.rpcEnabled: nimbus.rpcServer.stop() - if conf.graphql.enabled: + if conf.wsEnabled: + nimbus.wsRpcServer.start() + if conf.graphqlEnabled: await nimbus.graphqlServer.stop() if conf.engineSigner != ZERO_ADDRESS: await nimbus.sealingEngine.stop() -proc process*(nimbus: NimbusNode) = +proc process*(nimbus: NimbusNode, conf: NimbusConf) = # Main event loop while nimbus.state == Running: try: @@ -234,7 +239,7 @@ proc process*(nimbus: NimbusNode) = discard e # silence warning when chronicles not activated # Stop loop - waitFor nimbus.stop() + waitFor nimbus.stop(conf) when isMainModule: var nimbus = NimbusNode(state: Starting, ctx: newEthContext()) @@ -248,23 +253,11 @@ when isMainModule: echo "\nCtrl+C pressed. Waiting for a graceful shutdown." setControlCHook(controlCHandler) - var message: string - - ## Print Nimbus header - echo NimbusHeader - ## Show logs on stdout until we get the user's logging choice discard defaultChroniclesStream.output.open(stdout) ## Processing command line arguments - if processArguments(message) != ConfigStatus.Success: - echo message - quit(QuitFailure) - else: - if len(message) > 0: - echo message - quit(QuitSuccess) - - nimbus.start() - nimbus.process() + let conf = makeConfig() + nimbus.start(conf) + nimbus.process(conf) diff --git a/nimbus/p2p/executor/executor_helpers.nim b/nimbus/p2p/executor/executor_helpers.nim index b960641c66..cad9e308ef 100644 --- a/nimbus/p2p/executor/executor_helpers.nim +++ b/nimbus/p2p/executor/executor_helpers.nim @@ -10,7 +10,7 @@ import std/[strformat], - ../../config, + ../../chain_config, ../../db/accounts_cache, ../../forks, ../../vm_state, diff --git a/nimbus/p2p/gaslimit.nim b/nimbus/p2p/gaslimit.nim index cc3b2985b2..b5d8429a1f 100644 --- a/nimbus/p2p/gaslimit.nim +++ b/nimbus/p2p/gaslimit.nim @@ -16,8 +16,7 @@ import ../constants, ../errors, ../chain_config, - ../forks, - ../config + ../forks const EIP1559_BASE_FEE_CHANGE_DENOMINATOR* = ##\ diff --git a/nimbus/rpc/common.nim b/nimbus/rpc/common.nim index d426e35a37..027e51fac0 100644 --- a/nimbus/rpc/common.nim +++ b/nimbus/rpc/common.nim @@ -25,7 +25,7 @@ type ip : string # address string ports : NodePorts -proc setupCommonRPC*(node: EthereumNode, conf: NimbusConfiguration, server: RpcServer) = +proc setupCommonRPC*(node: EthereumNode, conf: NimbusConf, server: RpcServer) = server.rpc("web3_clientVersion") do() -> string: result = NimbusIdent @@ -34,11 +34,11 @@ proc setupCommonRPC*(node: EthereumNode, conf: NimbusConfiguration, server: RpcS result = "0x" & $keccak_256.digest(rawdata) server.rpc("net_version") do() -> string: - result = $conf.net.networkId + result = $conf.networkId.get() server.rpc("net_listening") do() -> bool: let numPeers = node.peerPool.connectedNodes.len - result = numPeers < conf.net.maxPeers + result = numPeers < conf.maxPeers server.rpc("net_peerCount") do() -> HexQuantityStr: let peerCount = uint node.peerPool.connectedNodes.len diff --git a/nimbus/rpc/p2p.nim b/nimbus/rpc/p2p.nim index c228cfccf0..ede02bb1ca 100644 --- a/nimbus/rpc/p2p.nim +++ b/nimbus/rpc/p2p.nim @@ -11,8 +11,8 @@ import times, options, tables, json_rpc/rpcserver, hexstrings, stint, stew/byteutils, eth/[common, keys, rlp, p2p], nimcrypto, - ".."/[transaction, config, vm_state, constants, utils, context], - ../db/[db_chain, state_db], + ".."/[transaction, vm_state, constants, utils, context], + ../db/[db_chain, state_db], rpc_types, rpc_utils, ../transaction/call_evm @@ -83,7 +83,7 @@ proc setupEthRpc*(node: EthereumNode, ctx: EthContext, chain: BaseChainDB , serv result = encodeQuantity(calculateMedianGasPrice(chain).uint64) server.rpc("eth_accounts") do() -> seq[EthAddressStr]: - ## Returns a list of addresses owned by client. + ## Returns a list of addresses owned by client. result = newSeqOfCap[EthAddressStr](ctx.am.numAccounts) for k in ctx.am.addresses: result.add ethAddressStr(k) @@ -198,7 +198,7 @@ proc setupEthRpc*(node: EthereumNode, ctx: EthContext, chain: BaseChainDB , serv ## message: message to sign. ## Returns signature. let - address = data.toAddress + address = data.toAddress acc = ctx.am.getAccount(address).tryGet() msg = hexToSeqByte(message.string) @@ -222,7 +222,7 @@ proc setupEthRpc*(node: EthereumNode, ctx: EthContext, chain: BaseChainDB , serv eip155 = chain.currentBlock >= chain.config.eip155Block signedTx = signTransaction(tx, acc.privateKey, chain.config.chainId, eip155) rlpTx = rlp.encode(signedTx) - + result = hexDataStr(rlpTx) server.rpc("eth_sendTransaction") do(data: TxSend) -> EthHashStr: @@ -245,7 +245,7 @@ proc setupEthRpc*(node: EthereumNode, ctx: EthContext, chain: BaseChainDB , serv eip155 = chain.currentBlock >= chain.config.eip155Block signedTx = signTransaction(tx, acc.privateKey, chain.config.chainId, eip155) rlpTx = rlp.encode(signedTx) - + result = keccak_256.digest(rlpTx).ethHashStr server.rpc("eth_sendRawTransaction") do(data: HexDataStr) -> EthHashStr: diff --git a/nimbus/sealer.nim b/nimbus/sealer.nim index 4e437a1799..367f973858 100644 --- a/nimbus/sealer.nim +++ b/nimbus/sealer.nim @@ -15,8 +15,7 @@ import "."/p2p/clique/[clique_defs, clique_desc, clique_cfg, - clique_sealer, - clique_snapshot], + clique_sealer], ./p2p/gaslimit, "."/[chain_config, utils, context] @@ -32,7 +31,7 @@ type ctx: EthContext signer: EthAddress -proc validateSealer*(conf: NimbusConfiguration, ctx: EthContext, chain: Chain): Result[void, string] = +proc validateSealer*(conf: NimbusConf, ctx: EthContext, chain: Chain): Result[void, string] = if conf.engineSigner == ZERO_ADDRESS: return err("signer address should not zero, use --engine-signer to set signer address") diff --git a/nimbus/transaction/call_evm.nim b/nimbus/transaction/call_evm.nim index fbafa9d058..4a88fa09d3 100644 --- a/nimbus/transaction/call_evm.nim +++ b/nimbus/transaction/call_evm.nim @@ -10,7 +10,7 @@ import eth/common/eth_types, stint, options, stew/byteutils, ".."/[vm_types, vm_state, vm_internals, vm_gas_costs, forks], ".."/[db/db_chain, db/accounts_cache, transaction], eth/trie/db, - ".."/[config, utils, rpc/hexstrings], + ".."/[chain_config, utils, rpc/hexstrings], ./call_common type diff --git a/nimbus/vm/state.nim b/nimbus/vm/state.nim index e422a183c6..141ca97c58 100644 --- a/nimbus/vm/state.nim +++ b/nimbus/vm/state.nim @@ -11,7 +11,7 @@ import std/[json, macros, options, sets, strformat, tables], ../../stateless/[witness_from_tree, witness_types], - ../config, + ../chain_config, ../constants, ../db/[db_chain, accounts_cache], ../errors, diff --git a/premix/configuration.nim b/premix/configuration.nim index ecbaf9b1da..09969ebc95 100644 --- a/premix/configuration.nim +++ b/premix/configuration.nim @@ -1,20 +1,24 @@ import std/[os, parseopt, strutils], - eth/p2p, stint + eth/p2p, stint, ../nimbus/config -from ../nimbus/config import - getDefaultDataDir, - ConfigStatus, - processInteger, +from ../nimbus/chain_config import MainNet, RopstenNet, RinkebyNet, GoerliNet, KovanNet -export ConfigStatus - type + ConfigStatus* = enum + ## Configuration status flags + Success, ## Success + EmptyOption, ## No options in category + ErrorUnknownOption, ## Unknown option in command line found + ErrorParseOption, ## Error in parsing command line option + ErrorIncorrectOption, ## Option has incorrect value + Error ## Unspecified error + PremixConfiguration* = ref object dataDir*: string head*: Uint256 @@ -26,12 +30,20 @@ var premixConfig {.threadvar.}: PremixConfiguration proc getConfiguration*(): PremixConfiguration {.gcsafe.} +proc processInteger(v: string, o: var int): ConfigStatus = + ## Convert string to integer. + try: + o = parseInt(v) + result = Success + except ValueError: + result = ErrorParseOption + proc initConfiguration(): PremixConfiguration = result = new PremixConfiguration - const dataDir = getDefaultDataDir() + const dataDir = defaultDataDir() - result.dataDir = getHomeDir() / dataDir + result.dataDir = dataDir result.head = 0.u256 result.maxBlocks = 0 result.numCommits = 128 diff --git a/premix/parser.nim b/premix/parser.nim index 982a935832..05290f4d45 100644 --- a/premix/parser.nim +++ b/premix/parser.nim @@ -1,6 +1,6 @@ import json, strutils, times, options, os, - eth/[rlp, common], httputils, nimcrypto, chronicles, + eth/[rlp, common], httputils, nimcrypto, stint, stew/byteutils import ../nimbus/transaction diff --git a/premix/persist.nim b/premix/persist.nim index c690c9cc76..afa2f05b9c 100644 --- a/premix/persist.nim +++ b/premix/persist.nim @@ -9,8 +9,7 @@ import eth/trie/[hexary, db], ../nimbus/db/[storage_types, db_chain, select_backend], ../nimbus/[genesis], - ../nimbus/p2p/chain, - ../nimbus/chain_config + ../nimbus/p2p/chain const manualCommit = nimbus_db_backend == "lmdb" diff --git a/tests/all_tests.nim b/tests/all_tests.nim index 7af487c8b8..f7f7f468fe 100644 --- a/tests/all_tests.nim +++ b/tests/all_tests.nim @@ -37,4 +37,5 @@ cliBuilder: ./test_misc, ./test_graphql, ./test_lru_cache, - ./test_clique + ./test_clique, + ./test_configuration diff --git a/tests/persistBlockTestGen.nim b/tests/persistBlockTestGen.nim index 73e1d346a4..79af7d1fa5 100644 --- a/tests/persistBlockTestGen.nim +++ b/tests/persistBlockTestGen.nim @@ -2,8 +2,7 @@ import json, eth/common, stint, chronicles, eth/rlp, eth/trie/db, ../nimbus/db/[db_chain, capturedb, select_backend], ../nimbus/[tracer, config], - ../nimbus/p2p/chain, - ../nimbus/chain_config + ../nimbus/p2p/chain proc dumpTest(chainDB: BaseChainDB, blockNumber: int) = let @@ -49,8 +48,8 @@ proc main() {.used.} = # nimbus --rpcapi: eth, debug --prune: archive - var conf = getConfiguration() - let db = newChainDb(conf.dataDir) + var conf = makeConfig() + let db = newChainDb(string conf.dataDir) let trieDB = trieDB db let chainDB = newBaseChainDB(trieDB, false) @@ -105,17 +104,6 @@ proc main() {.used.} = chainDB.dumpTest(4_370_000) # Byzantium first block when isMainModule: - var message: string - - ## Processing command line arguments - if processArguments(message) != Success: - echo message - quit(QuitFailure) - else: - if len(message) > 0: - echo message - quit(QuitSuccess) - try: main() except: diff --git a/tests/test_clique.nim b/tests/test_clique.nim index 2d2571c3be..61eec30d91 100644 --- a/tests/test_clique.nim +++ b/tests/test_clique.nim @@ -244,23 +244,17 @@ proc cliqueMiscTests() = engineSigner = "658bdf435d810c91414ec09147daa6db62406379" privateKey = "tests" / "test_clique" / "private.key" - var msg: string - var opt = initOptParser("--engine-signer:$1 --import-key:$2" % [engineSigner, privateKey]) - let res = processArguments(msg, opt) - check res == Success - let - signer = hexToByteArray[20](engineSigner) - ctx = newEthContext() - conf = getConfiguration() - - check ctx.am.importPrivateKey(conf.importKey).isOk() - check ctx.am.getAccount(signer).isOk() + conf = makeConfig(@["--engine-signer:" & engineSigner, "--import-key:" & privateKey]) + ctx = newEthContext() + + check ctx.am.importPrivateKey(string conf.importKey).isOk() + check ctx.am.getAccount(conf.engineSigner).isOk() proc signFunc(signer: EthAddress, message: openArray[byte]): Result[RawSignature, cstring] {.gcsafe.} = let - hashData = keccakHash(message) - acc = ctx.am.getAccount(signer).tryGet() + hashData = keccakHash(message) + acc = ctx.am.getAccount(conf.engineSigner).tryGet() rawSign = sign(acc.privateKey, SkMessage(hashData.data)).toRaw ok(rawSign) @@ -270,7 +264,7 @@ proc cliqueMiscTests() = header.extraData.setLen(EXTRA_VANITY) header.extraData.add 0.byte.repeat(EXTRA_SEAL) - let signature = signerFn(signer, header.encodeSealHeader).get() + let signature = signerFn(conf.engineSigner, header.encodeSealHeader).get() let extraLen = header.extraData.len if EXTRA_SEAL < extraLen: header.extraData.setLen(extraLen - EXTRA_SEAL) @@ -278,7 +272,7 @@ proc cliqueMiscTests() = let resAddr = ecRecover(header) check resAddr.isOk - check resAddr.value == signer + check resAddr.value == conf.engineSigner # ------------------------------------------------------------------------------ # Main function(s) diff --git a/tests/test_configuration.nim b/tests/test_configuration.nim new file mode 100644 index 0000000000..8ceab1c8ad --- /dev/null +++ b/tests/test_configuration.nim @@ -0,0 +1,76 @@ +import + std/[os], + pkg/[unittest2, confutils], + eth/[p2p, common], + ../nimbus/[config, chain_config] + +proc `==`(a, b: ChainId): bool = + a.int == b.int + +proc configurationMain*() = + suite "configuration test suite": + const genesisFile = "tests" / "customgenesis" / "calaveras.json" + + test "data-dir and key-store": + let conf = makeConfig() + check conf.dataDir.string == defaultDataDir() + check conf.keyStore.string == defaultKeystoreDir() + + let cc = makeConfig(@["-d:apple\\bin", "-k:banana/bin"]) + check cc.dataDir.string == "apple\\bin" + check cc.keyStore.string == "banana/bin" + + let dd = makeConfig(@["--data-dir:apple\\bin", "--key-store:banana/bin"]) + check dd.dataDir.string == "apple\\bin" + check dd.keyStore.string == "banana/bin" + + test "prune-mode": + let aa = makeConfig() + check aa.pruneMode == PruneMode.Full + + let bb = makeConfig(@["--prune-mode:full"]) + check bb.pruneMode == PruneMode.Full + + let cc = makeConfig(@["--prune-mode:archive"]) + check cc.pruneMode == PruneMode.Archive + + let dd = makeConfig(@["-p:archive"]) + check dd.pruneMode == PruneMode.Archive + + test "import": + let aa = makeConfig() + check aa.importBlocks.string == "" + + let bb = makeConfig(@["--import-blocks:" & genesisFile]) + check bb.importBlocks.string == genesisFile + + let cc = makeConfig(@["-b:" & genesisFile]) + check cc.importBlocks.string == genesisFile + + test "network-id": + let aa = makeConfig() + check aa.networkId.get() == MainNet + check aa.mainnet == true + check aa.customNetwork.get() == CustomNetwork() + + let conf = makeConfig(@["--custom-network:" & genesisFile, "--network-id:345"]) + check conf.networkId.get() == 345.NetworkId + + test "network-id first, custom-network next": + let conf = makeConfig(@["--network-id:678", "--custom-network:" & genesisFile]) + check conf.networkId.get() == 678.NetworkId + + test "network-id not set, copy from chainId of customnetwork": + let conf = makeConfig(@["--custom-network:" & genesisFile]) + check conf.networkId.get() == 123.NetworkId + + test "network-id not set, goerli set": + let conf = makeConfig(@["--goerli"]) + check conf.networkId.get() == GoerliNet + + test "network-id set, goerli set": + let conf = makeConfig(@["--goerli", "--network-id:123"]) + check conf.networkId.get() == GoerliNet + +when isMainModule: + configurationMain() diff --git a/tests/test_difficulty.nim b/tests/test_difficulty.nim index 5bbf5663b6..2e141e9376 100644 --- a/tests/test_difficulty.nim +++ b/tests/test_difficulty.nim @@ -1,8 +1,7 @@ import unittest2, strutils, tables, os, json, ../nimbus/utils/difficulty, stint, times, eth/common, test_helpers, stew/byteutils, - ../nimbus/constants, ../nimbus/config, - ../nimbus/chain_config + ../nimbus/constants, ../nimbus/chain_config type Tester = object diff --git a/tests/test_forkid.nim b/tests/test_forkid.nim index eea578c9e7..855b53bf4e 100644 --- a/tests/test_forkid.nim +++ b/tests/test_forkid.nim @@ -1,7 +1,7 @@ import unittest2, eth/[common, p2p], eth/trie/db, ../nimbus/db/db_chain, ../nimbus/p2p/chain, - ../nimbus/config + ../nimbus/chain_config const MainNetIDs = [ diff --git a/tests/test_graphql.nim b/tests/test_graphql.nim index 45c42c5ffc..b5dbaf85bf 100644 --- a/tests/test_graphql.nim +++ b/tests/test_graphql.nim @@ -10,12 +10,12 @@ import std/[os, json], stew/byteutils, unittest2, - eth/[p2p, common, trie/db, rlp, trie], + eth/[p2p, common, trie/db, rlp], graphql, ../nimbus/graphql/ethapi, graphql/test_common, ../nimbus/sync/protocol_eth65, ../nimbus/[genesis, config, chain_config, context], - ../nimbus/db/[db_chain, state_db], - ../nimbus/p2p/chain, ../premix/parser, ./test_helpers + ../nimbus/db/[db_chain], + ../nimbus/p2p/chain, ./test_helpers type EthBlock = object @@ -95,7 +95,7 @@ proc setupChain(): BaseChainDB = proc graphqlMain*() = let - conf = getConfiguration() + conf = makeConfig() ethCtx = newEthContext() ethNode = setupEthNode(conf, ethCtx, eth) chainDB = setupChain() @@ -108,5 +108,4 @@ proc graphqlMain*() = ctx.executeCases(caseFolder, purgeSchema = false) when isMainModule: - processArguments() graphqlMain() diff --git a/tests/test_helpers.nim b/tests/test_helpers.nim index 7339109a85..b3f9533d80 100644 --- a/tests/test_helpers.nim +++ b/tests/test_helpers.nim @@ -272,14 +272,16 @@ proc getFixtureTransaction*(j: JsonNode, dataIndex, gasIndex, valueIndex: int): proc hashLogEntries*(logs: seq[Log]): string = toLowerAscii("0x" & $keccakHash(rlp.encode(logs))) -proc setupEthNode*(conf: NimbusConfiguration, ctx: EthContext, capabilities: varargs[ProtocolInfo, `protocolInfo`]): EthereumNode = - let keypair = ctx.hexToKeyPair(conf.net.nodekey).tryGet() +proc setupEthNode*(conf: NimbusConf, ctx: EthContext, capabilities: varargs[ProtocolInfo, `protocolInfo`]): EthereumNode = + let keypair = ctx.hexToKeyPair(conf.nodeKeyHex).tryGet() var srvAddress: Address srvAddress.ip = parseIpAddress("0.0.0.0") - srvAddress.tcpPort = Port(conf.net.bindPort) - srvAddress.udpPort = Port(conf.net.discPort) + srvAddress.tcpPort = conf.tcpPort + srvAddress.udpPort = conf.udpPort result = newEthereumNode( - keypair, srvAddress, conf.net.networkId, nil, "nimbus 0.1.0", + keypair, srvAddress, + conf.networkId.get(), + nil, conf.agentString, addAllCapabilities = false) for capability in capabilities: result.addCapability capability diff --git a/tests/test_misc.nim b/tests/test_misc.nim index 2e89c6a1d9..94b4139bdf 100644 --- a/tests/test_misc.nim +++ b/tests/test_misc.nim @@ -1,10 +1,8 @@ import - std/[os, parseopt], + std/[os], unittest2, stew/byteutils, eth/common/eth_types, - eth/p2p, ../nimbus/vm_internals, - ../nimbus/config, ../nimbus/utils/header func toAddress(n: int): EthAddress = @@ -26,41 +24,6 @@ proc miscMain*() = check toAddress(0x10, 0x0).toInt == 0x1000 check toAddress(0x10, 0x0, 0x0).toInt == 0x100000 - const genesisFile = "tests" / "customgenesis" / "calaveras.json" - test "networkid cli": - var msg: string - var opt = initOptParser("--customnetwork:" & genesisFile & " --networkid:345") - let res = processArguments(msg, opt) - if res != Success: - echo msg - quit(QuitFailure) - - let conf = getConfiguration() - check conf.net.networkId == 345.NetworkId - - test "networkid first, customnetwork next": - var msg: string - var opt = initOptParser("--networkid:678 --customnetwork:" & genesisFile) - let res = processArguments(msg, opt) - if res != Success: - echo msg - quit(QuitFailure) - - let conf = getConfiguration() - check conf.net.networkId == 678.NetworkId - - test "networkid not set, copy from chainId of customnetwork": - let conf = getConfiguration() - conf.net.flags.excl NetworkIdSet - var msg: string - var opt = initOptParser("--customnetwork:" & genesisFile) - let res = processArguments(msg, opt) - if res != Success: - echo msg - quit(QuitFailure) - - check conf.net.networkId == 123.NetworkId - test "calcGasLimitEIP1559": type GLT = object diff --git a/tests/test_rpc.nim b/tests/test_rpc.nim index 6002f86056..6ae620c2e1 100644 --- a/tests/test_rpc.nim +++ b/tests/test_rpc.nim @@ -12,12 +12,12 @@ import eth/[rlp, keys, trie/db, p2p/private/p2p_types], ../nimbus/rpc/[common, p2p, hexstrings, rpc_types, rpc_utils], ../nimbus/[constants, vm_state, config, genesis, utils, transaction], - ../nimbus/db/[accounts_cache, db_chain, storage_types, state_db], + ../nimbus/db/[accounts_cache, db_chain, state_db], ../nimbus/p2p/[chain, executor, executor/executor_helpers], ../nimbus/sync/protocol_eth65, ../nimbus/utils/difficulty, - ../nimbus/context, - ./rpcclient/test_hexstrings, ./test_helpers, ./macro_assembler + ../nimbus/[context, chain_config], + ./test_helpers, ./macro_assembler # Perform checks for hex string validation #doHexStrTests() @@ -124,17 +124,22 @@ proc rpcMain*() = suite "Remote Procedure Calls": # TODO: Include other transports such as Http let - conf = getConfiguration() + conf = makeConfig() ctx = newEthContext() ethNode = setupEthNode(conf, ctx, eth) - chain = newBaseChainDB(newMemoryDb()) + chain = newBaseChainDB( + newMemoryDb(), + conf.pruneMode == PruneMode.Full, + conf.networkId.get(), + conf.customNetwork.get() + ) signer: EthAddress = hexToByteArray[20]("0x0e69cde81b1aa07a45c32c6cd85d67229d36bb1b") ks2: EthAddress = hexToByteArray[20]("0xa3b2222afa5c987da6ef773fde8d01b9f23d481f") ks3: EthAddress = hexToByteArray[20]("0x597176e9a64aad0845d83afdaf698fbeff77703b") ethNode.chain = newChain(chain) - conf.keyStore = "tests" / "keystore" - let res = ctx.am.loadKeystores(conf.keyStore) + let keyStore = "tests" / "keystore" + let res = ctx.am.loadKeystores(keyStore) if res.isErr: debugEcho res.error doAssert(res.isOk) @@ -145,8 +150,7 @@ proc rpcMain*() = debugEcho unlock.error doAssert(unlock.isOk) - genesisBlockForNetwork(conf.net.networkId, conf.customNetwork).commit(chain) - doAssert(canonicalHeadHashKey().toOpenArray in chain.db) + initializeEmptyDb(chain) let env = setupEnv(chain, signer, ks2, ctx) # Create Ethereum RPCs @@ -178,11 +182,11 @@ proc rpcMain*() = test "net_version": let res = await client.net_version() - check res == $conf.net.networkId + check res == $conf.networkId.get() test "net_listening": let res = await client.net_listening() - let listening = ethNode.peerPool.connectedNodes.len < conf.net.maxPeers + let listening = ethNode.peerPool.connectedNodes.len < conf.maxPeers check res == listening test "net_peerCount": diff --git a/tests/tracerTestGen.nim b/tests/tracerTestGen.nim index 05eb1f4d10..64bfb50ff2 100644 --- a/tests/tracerTestGen.nim +++ b/tests/tracerTestGen.nim @@ -1,7 +1,7 @@ import json, eth/common, stint, chronicles, eth/trie/db, ../nimbus/db/[db_chain, capturedb, select_backend], - ../nimbus/[tracer, vm_types, config] + ../nimbus/[tracer, config, vm_types] proc dumpTest(chainDB: BaseChainDB, blockNumber: int) = let @@ -47,8 +47,8 @@ proc main() {.used.} = # nimbus --rpcapi: eth, debug --prune: archive - var conf = getConfiguration() - let db = newChainDb(conf.dataDir) + var conf = makeConfig() + let db = newChainDb(string conf.dataDir) let trieDB = trieDB db let chainDB = newBaseChainDB(trieDB, false) @@ -62,17 +62,6 @@ proc main() {.used.} = chainDB.dumpTest(49018) when isMainModule: - var message: string - - ## Processing command line arguments - if processArguments(message) != Success: - echo message - quit(QuitFailure) - else: - if len(message) > 0: - echo message - quit(QuitSuccess) - try: main() except: