From a5d4759bfd060ef379aca09b3152f6d9c8087e8c Mon Sep 17 00:00:00 2001 From: jangko Date: Wed, 27 Jul 2022 23:07:54 +0700 Subject: [PATCH] enhance net-key command line option to accept random, hex, and path - previously it only accept hex - fix #587 --- nimbus/config.nim | 13 ++++++---- nimbus/context.nim | 48 ++++++++++++++++++++++++++++++------ nimbus/nimbus.nim | 26 ++++++++++--------- tests/test_configuration.nim | 39 +++++++++++++++++++++++++++-- tests/test_helpers.nim | 2 +- vendor/nim-json-rpc | 2 +- 6 files changed, 103 insertions(+), 27 deletions(-) diff --git a/nimbus/config.nim b/nimbus/config.nim index d9bf72df3..be5da6fea 100644 --- a/nimbus/config.nim +++ b/nimbus/config.nim @@ -317,11 +317,14 @@ type defaultValueDesc: $DiscoveryType.V4 name: "discovery" .}: DiscoveryType - nodeKeyHex* {. - desc: "P2P node private key (as 32 bytes hex string)" - defaultValue: "" - defaultValueDesc: "random" - name: "node-key" .}: string + netKey* {. + desc: "P2P ethereum node (secp256k1) private key (random, path, hex)" + longDesc: + "- random: generate random network key for this node instance\n" & + "- path : path to where the private key will be loaded or auto generated\n" & + "- hex : 32 bytes hex of network private key" + defaultValue: "random" + name: "net-key" .}: string agentString* {. desc: "Node agent string which is used as identifier in network" diff --git a/nimbus/context.nim b/nimbus/context.nim index 70ec4ed46..46bd6817d 100644 --- a/nimbus/context.nim +++ b/nimbus/context.nim @@ -8,8 +8,9 @@ # those terms. import + std/[strutils, os], accounts/manager, - stew/results, + stew/[results, io2, byteutils], eth/keys export manager @@ -32,12 +33,45 @@ proc randomPrivateKey*(ctx: EthContext): PrivateKey = proc randomKeyPair*(ctx: EthContext): KeyPair = random(KeyPair, ctx.rng[]) -proc hexToKeyPair*(ctx: EthContext, hexPrivateKey: string): Result[KeyPair, string] = - if hexPrivateKey.len == 0: +proc containsOnlyHexDigits(hex: string): bool = + const HexDigitsX = HexDigits + {'x'} + for c in hex: + if c notin HexDigitsX: + return false + true + +proc getNetKeys*(ctx: EthContext, netKey, dataDir: string): Result[KeyPair, string] = + if netKey.len == 0 or netKey == "random": let privateKey = ctx.randomPrivateKey() - ok(privateKey.toKeyPair()) - else: - let res = PrivateKey.fromHex(hexPrivateKey) + return ok(privateKey.toKeyPair()) + elif netKey.len in {64, 66} and netKey.containsOnlyHexDigits: + let res = PrivateKey.fromHex(netKey) if res.isErr: return err($res.error) - ok(res.get().toKeyPair()) + return ok(res.get().toKeyPair()) + else: + # TODO: should we secure the private key with + # keystore encryption? + if fileAccessible(netKey, {AccessFlags.Find}): + try: + let lines = netKey.readLines(1) + if lines.len == 0: + return err("empty network key file") + let rc = PrivateKey.fromHex(lines[0]) + if rc.isErr: + return err($rc.error) + return ok(rc.get().toKeyPair()) + except IOError as e: + return err("cannot open network key file: " & e.msg) + except ValueError as ex: + return err("invalid hex string in network key file: " & ex.msg) + else: + let privateKey = ctx.randomPrivateKey() + + try: + createDir(netKey.splitFile.dir) + netKey.writeFile(privateKey.toRaw.to0xHex) + except IOError as e: + return err("could not write network key file: " & e.msg) + + return ok(privateKey.toKeyPair()) diff --git a/nimbus/nimbus.nim b/nimbus/nimbus.nim index 8deca12e6..2ecb245f8 100644 --- a/nimbus/nimbus.nim +++ b/nimbus/nimbus.nim @@ -22,7 +22,7 @@ import metrics, metrics/[chronos_httpserver, chronicles_support], stew/shims/net as stewNet, - websock/types as ws, + websock/websock as ws, "."/[conf_utils, config, constants, context, genesis, sealer, utils, version], ./db/[storage_types, db_chain, select_backend], ./graphql/ethapi, @@ -69,21 +69,21 @@ proc manageAccounts(nimbus: NimbusNode, conf: NimbusConf) = if string(conf.keyStore).len > 0: let res = nimbus.ctx.am.loadKeystores(string conf.keyStore) if res.isErr: - echo res.error() + fatal "Load keystore error", msg = res.error() quit(QuitFailure) if string(conf.importKey).len > 0: let res = nimbus.ctx.am.importPrivateKey(string conf.importKey) if res.isErr: - echo res.error() + fatal "Import private key error", msg = res.error() quit(QuitFailure) proc setupP2P(nimbus: NimbusNode, conf: NimbusConf, chainDB: BaseChainDB, protocols: set[ProtocolFlag]) = ## Creating P2P Server - let kpres = nimbus.ctx.hexToKeyPair(conf.nodeKeyHex) + let kpres = nimbus.ctx.getNetKeys(conf.netKey, conf.dataDir.string) if kpres.isErr: - echo kpres.error() + fatal "Get network keys error", msg = kpres.error quit(QuitFailure) let keypair = kpres.get() @@ -117,7 +117,8 @@ proc setupP2P(nimbus: NimbusNode, conf: NimbusConf, addAllCapabilities = false, minPeers = conf.maxPeers, bootstrapNodes = bootstrapNodes, bindUdpPort = conf.udpPort, bindTcpPort = conf.tcpPort, - bindIp = conf.listenAddress) + bindIp = conf.listenAddress, + rng = nimbus.ctx.rng) # Add protocol capabilities based on protocol flags if ProtocolFlag.Eth in protocols: @@ -187,12 +188,12 @@ proc localServices(nimbus: NimbusNode, conf: NimbusConf, discard setTimer(Moment.fromNow(conf.logMetricsInterval.seconds), logMetrics) discard setTimer(Moment.fromNow(conf.logMetricsInterval.seconds), logMetrics) - # Provide JWT authentication handler for websockets + # Provide JWT authentication handler for rpcHttpServer let jwtKey = block: # Create or load shared secret let rc = nimbus.ctx.rng.jwtSharedSecret(conf) if rc.isErr: - error "Failed create or load shared secret", + fatal "Failed create or load shared secret", msg = $(rc.unsafeError) # avoid side effects quit(QuitFailure) rc.value @@ -250,7 +251,10 @@ proc localServices(nimbus: NimbusNode, conf: NimbusConf, # Construct server object nimbus.wsRpcServer = newRpcWebSocketServer( initTAddress(conf.wsAddress, conf.wsPort), - authHooks = hooks + authHooks = hooks, + # yuck, we should remove this ugly cast when + # we fix nim-websock + rng = cast[ws.Rng](nimbus.ctx.rng) ) setupCommonRpc(nimbus.ethNode, conf, nimbus.wsRpcServer) @@ -284,7 +288,7 @@ proc localServices(nimbus: NimbusNode, conf: NimbusConf, let rs = validateSealer(conf, nimbus.ctx, nimbus.chainRef) if rs.isErr: - echo rs.error + fatal "Engine signer validation error", msg = rs.error quit(QuitFailure) proc signFunc(signer: EthAddress, message: openArray[byte]): Result[RawSignature, cstring] {.gcsafe.} = @@ -297,7 +301,7 @@ proc localServices(nimbus: NimbusNode, conf: NimbusConf, nimbus.chainRef.clique.authorize(conf.engineSigner, signFunc) - # always create sealing engine instanca but not always run it + # always create sealing engine instance but not always run it # e.g. engine api need sealing engine without it running var initialState = EngineStopped if chainDB.headTotalDifficulty() > chainDB.ttd: diff --git a/tests/test_configuration.nim b/tests/test_configuration.nim index 64db3fe8e..4a092ac5f 100644 --- a/tests/test_configuration.nim +++ b/tests/test_configuration.nim @@ -1,8 +1,9 @@ import std/[os], pkg/[unittest2, confutils], - eth/[p2p, common], - ../nimbus/[config, chain_config], + eth/[p2p, common, keys], + stew/byteutils, + ../nimbus/[config, chain_config, context], ./test_helpers proc `==`(a, b: ChainId): bool = @@ -208,5 +209,39 @@ proc configurationMain*() = check conf.engineApiEnabled == false check conf.rpcEnabled == false + let ctx = newEthContext() + test "net-key random": + let conf = makeConfig(@["--net-key:random"]) + check conf.netKey == "random" + let rc = ctx.getNetKeys(conf.netKey, conf.dataDir.string) + check rc.isOk + + test "net-key hex without 0x prefix": + let conf = makeConfig(@["--net-key:9c647b8b7c4e7c3490668fb6c11473619db80c93704c70893d3813af4090c39c"]) + check conf.netKey == "9c647b8b7c4e7c3490668fb6c11473619db80c93704c70893d3813af4090c39c" + let rc = ctx.getNetKeys(conf.netKey, conf.dataDir.string) + check rc.isOk + let pkhex = rc.get.seckey.toRaw.to0xHex + check pkhex == "0x9c647b8b7c4e7c3490668fb6c11473619db80c93704c70893d3813af4090c39c" + + test "net-key hex with 0x prefix": + let conf = makeConfig(@["--net-key:0x9c647b8b7c4e7c3490668fb6c11473619db80c93704c70893d3813af4090c39c"]) + check conf.netKey == "0x9c647b8b7c4e7c3490668fb6c11473619db80c93704c70893d3813af4090c39c" + let rc = ctx.getNetKeys(conf.netKey, conf.dataDir.string) + check rc.isOk + let pkhex = rc.get.seckey.toRaw.to0xHex + check pkhex == "0x9c647b8b7c4e7c3490668fb6c11473619db80c93704c70893d3813af4090c39c" + + test "net-key path": + let conf = makeConfig(@["--net-key:nimcache/key.txt"]) + check conf.netKey == "nimcache/key.txt" + let rc1 = ctx.getNetKeys(conf.netKey, conf.dataDir.string) + check rc1.isOk + let pkhex1 = rc1.get.seckey.toRaw.to0xHex + let rc2 = ctx.getNetKeys(conf.netKey, conf.dataDir.string) + check rc2.isOk + let pkhex2 = rc2.get.seckey.toRaw.to0xHex + check pkhex1 == pkhex2 + when isMainModule: configurationMain() diff --git a/tests/test_helpers.nim b/tests/test_helpers.nim index fb1d2c72e..8297f0b54 100644 --- a/tests/test_helpers.nim +++ b/tests/test_helpers.nim @@ -278,7 +278,7 @@ proc hashLogEntries*(logs: seq[Log]): string = proc setupEthNode*( conf: NimbusConf, ctx: EthContext, capabilities: varargs[ProtocolInfo, `protocolInfo`]): EthereumNode = - let keypair = ctx.hexToKeyPair(conf.nodeKeyHex).tryGet() + let keypair = ctx.getNetKeys(conf.netKey, conf.dataDir.string).tryGet() let srvAddress = Address( ip: conf.listenAddress, tcpPort: conf.tcpPort, udpPort: conf.udpPort) diff --git a/vendor/nim-json-rpc b/vendor/nim-json-rpc index 0fee4be2c..5ccdaed0a 160000 --- a/vendor/nim-json-rpc +++ b/vendor/nim-json-rpc @@ -1 +1 @@ -Subproject commit 0fee4be2ccbbfa3f158b741c3a34b04130c8b424 +Subproject commit 5ccdaed0adfb1534ee1d193fc6f97e7961008bbe