diff --git a/nimbus/config.nim b/nimbus/config.nim index ab1afa7dd..e3601db32 100644 --- a/nimbus/config.nim +++ b/nimbus/config.nim @@ -97,7 +97,7 @@ type maxPendingPeers*: int ## Maximum allowed pending peers networkId*: NetworkId ## Network ID as integer ident*: string ## Server ident name string - nodeKey*: PrivateKey ## Server private key + nodeKey*: string ## Server private key nat*: NatStrategy ## NAT strategy externalIP*: string ## user-provided external IP protocols*: set[ProtocolFlags]## Enabled subprotocols @@ -125,9 +125,6 @@ type net*: NetConfiguration ## Network configuration debug*: DebugConfiguration ## Debug configuration customGenesis*: CustomGenesis ## Custom Genesis Configuration - # You should only create one instance of the RNG per application / library - # Ref is used so that it can be shared between components - rng*: ref BrHmacDrbgContext importKey*: string importFile*: string verifyFromOk*: bool ## activate `verifyFrom` setting @@ -350,15 +347,6 @@ proc processENodesList(v: string, o: var seq[ENode]): ConfigStatus = else: break -proc processPrivateKey(v: string, o: var PrivateKey): ConfigStatus = - ## Convert hexadecimal string to private key object. - let seckey = PrivateKey.fromHex(v) - if seckey.isOk(): - o = seckey[] - return Success - - result = ErrorParseOption - proc processPruneList(v: string, flags: var PruneMode): ConfigStatus = var list = newSeq[string]() processList(v, list) @@ -556,10 +544,7 @@ proc processNetArguments(key, value: string): ConfigStatus = if result == Success: config.net.maxPendingPeers = res elif skey == "nodekey": - var res: PrivateKey - result = processPrivateKey(value, res) - if result == Success: - config.net.nodeKey = res + config.net.nodeKey = value elif skey == "ident": config.net.ident = value elif skey == "nat": @@ -660,7 +645,6 @@ proc getDefaultKeystoreDir*(): string = proc initConfiguration(): NimbusConfiguration = ## Allocates and initializes `NimbusConfiguration` with default values result = new NimbusConfiguration - result.rng = newRng() ## Graphql defaults result.graphql.enabled = false @@ -685,7 +669,6 @@ proc initConfiguration(): NimbusConfiguration = result.net.ident = NimbusIdent result.net.nat = NatAny result.net.protocols = defaultProtocols - result.net.nodekey = random(PrivateKey, result.rng[]) const dataDir = getDefaultDataDir() diff --git a/nimbus/context.nim b/nimbus/context.nim index 96c53fd19..66bde6b81 100644 --- a/nimbus/context.nim +++ b/nimbus/context.nim @@ -8,15 +8,36 @@ # those terms. import - accounts/manager + accounts/manager, + stew/results, + eth/keys export manager type EthContext* = ref object am*: AccountsManager - + # You should only create one instance of the RNG per application / library + # Ref is used so that it can be shared between components + rng*: ref BrHmacDrbgContext proc newEthContext*(): EthContext = result = new(EthContext) result.am = AccountsManager.init() + result.rng = newRng() + +proc randomPrivateKey*(ctx: EthContext): PrivateKey = + random(PrivateKey, ctx.rng[]) + +proc randomKeyPair*(ctx: EthContext): KeyPair = + random(KeyPair, ctx.rng[]) + +proc hexToKeyPair*(ctx: EthContext, hexPrivateKey: string): Result[KeyPair, string] = + if hexPrivateKey.len == 0: + let privateKey = ctx.randomPrivateKey() + ok(privateKey.toKeyPair()) + else: + let res = PrivateKey.fromHex(hexPrivateKey) + if res.isErr: + return err($res.error) + ok(res.get().toKeyPair()) diff --git a/nimbus/nimbus.nim b/nimbus/nimbus.nim index e4ba8793b..c9d1aff66 100644 --- a/nimbus/nimbus.nim +++ b/nimbus/nimbus.nim @@ -20,7 +20,7 @@ import ./sync/protocol_eth65, config, genesis, rpc/[common, p2p, debug], p2p/chain, eth/trie/db, metrics, metrics/[chronos_httpserver, chronicles_support], - graphql/ethapi, + graphql/ethapi, context, "."/[utils, conf_utils, sealer, constants] ## TODO: @@ -36,12 +36,13 @@ type Starting, Running, Stopping NimbusNode = ref object - rpcServer*: RpcHttpServer - ethNode*: EthereumNode - state*: NimbusState - graphqlServer*: GraphqlHttpServerRef - wsRpcServer*: RpcWebSocketServer - sealingEngine*: SealingEngineRef + rpcServer: RpcHttpServer + ethNode: EthereumNode + state: NimbusState + graphqlServer: GraphqlHttpServerRef + wsRpcServer: RpcWebSocketServer + sealingEngine: SealingEngineRef + ctx: EthContext proc start(nimbus: NimbusNode) = var conf = getConfiguration() @@ -71,10 +72,17 @@ proc start(nimbus: NimbusNode) = else: quit(QuitSuccess) - let res = conf.loadKeystoreFiles() - if res.isErr: - echo res.error() - quit(QuitFailure) + if conf.keyStore.len > 0: + let res = nimbus.ctx.am.loadKeystores(conf.keyStore) + if res.isErr: + echo res.error() + quit(QuitFailure) + + if conf.importKey.len > 0: + let res = nimbus.ctx.am.importPrivateKey(conf.importKey) + if res.isErr: + echo res.error() + quit(QuitFailure) # metrics logging if conf.debug.logMetrics: @@ -88,7 +96,12 @@ proc start(nimbus: NimbusNode) = discard setTimer(Moment.fromNow(conf.debug.logMetricsInterval.seconds), logMetrics) ## Creating P2P Server - let keypair = conf.net.nodekey.toKeyPair() + let kpres = nimbus.ctx.hexToKeyPair(conf.net.nodekey) + if kpres.isErr: + echo kpres.error() + quit(QuitFailure) + + let keypair = kpres.get() var address: Address address.ip = parseIpAddress("0.0.0.0") @@ -136,7 +149,7 @@ proc start(nimbus: NimbusNode) = # 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, chainDB, nimbus.rpcServer) + setupEthRpc(nimbus.ethNode, nimbus.ctx, chainDB, nimbus.rpcServer) if RpcFlags.Debug in conf.rpc.flags: setupDebugRpc(chainDB, nimbus.rpcServer) @@ -148,7 +161,7 @@ proc start(nimbus: NimbusNode) = # 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, chainDB, nimbus.wsRpcServer) + setupEthRpc(nimbus.ethNode, nimbus.ctx, chainDB, nimbus.wsRpcServer) if RpcFlags.Debug in conf.ws.flags: setupDebugRpc(chainDB, nimbus.wsRpcServer) @@ -165,11 +178,13 @@ proc start(nimbus: NimbusNode) = nimbus.graphqlServer.start() if conf.engineSigner != ZERO_ADDRESS: - let rs = validateSealer(chainRef) + let rs = validateSealer(conf, nimbus.ctx, chainRef) if rs.isErr: echo rs.error quit(QuitFailure) - nimbus.sealingEngine = SealingEngineRef.new(chainRef) + nimbus.sealingEngine = SealingEngineRef.new( + chainRef, nimbus.ctx, conf.engineSigner + ) nimbus.sealingEngine.start() # metrics server @@ -224,7 +239,7 @@ proc process*(nimbus: NimbusNode) = waitFor nimbus.stop() when isMainModule: - var nimbus = NimbusNode(state: Starting) + var nimbus = NimbusNode(state: Starting, ctx: newEthContext()) ## Ctrl+C handling proc controlCHandler() {.noconv.} = diff --git a/nimbus/random_keys.nim b/nimbus/random_keys.nim deleted file mode 100644 index 2bad1a16f..000000000 --- a/nimbus/random_keys.nim +++ /dev/null @@ -1,10 +0,0 @@ -import eth/keys, config - -proc getRng*(): ref BrHmacDrbgContext = - getConfiguration().rng - -proc randomPrivateKey*(): PrivateKey = - random(PrivateKey, getRng()[]) - -proc randomKeyPair*(): KeyPair = - random(KeyPair, getRng()[]) diff --git a/nimbus/sealer.nim b/nimbus/sealer.nim index f926b48dc..4e437a179 100644 --- a/nimbus/sealer.nim +++ b/nimbus/sealer.nim @@ -18,8 +18,7 @@ import clique_sealer, clique_snapshot], ./p2p/gaslimit, - ./chain_config, - ./utils + "."/[chain_config, utils, context] type EngineState = enum @@ -30,16 +29,18 @@ type state: EngineState engineLoop: Future[void] chain: Chain + ctx: EthContext + signer: EthAddress -proc validateSealer*(chain: Chain): Result[void, string] = - let conf = getConfiguration() +proc validateSealer*(conf: NimbusConfiguration, 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") - if conf.engineSigner notin conf.accounts: + let res = ctx.am.getAccount(conf.engineSigner) + if res.isErr: return err("signer address not in registered accounts, use --import-key/account to register the account") - let acc = conf.accounts[conf.engineSigner] + let acc = res.get() if not acc.unlocked: return err("signer account not unlocked, please unlock it first via rpc/password file") @@ -119,21 +120,20 @@ proc generateBlock(engine: SealingEngineRef, ethBlock: var EthBlock): Result[voi ok() -proc signerFunc(signer: EthAddress, message: openArray[byte]): - Result[RawSignature, cstring] {.gcsafe.} = - let - hashData = keccakHash(message) - conf = getConfiguration() - acc = conf.accounts[signer] - rawSign = sign(acc.privateKey, SkMessage(hashData.data)).toRaw - - ok(rawSign) - proc sealingLoop(engine: SealingEngineRef): Future[void] {.async.} = let clique = engine.chain.clique - let conf = getConfiguration() - clique.authorize(conf.engineSigner, signerFunc) + proc signerFunc(signer: EthAddress, message: openArray[byte]): + Result[RawSignature, cstring] {.gcsafe.} = + let + hashData = keccakHash(message) + ctx = engine.ctx + acc = ctx.am.getAccount(signer).tryGet() + rawSign = sign(acc.privateKey, SkMessage(hashData.data)).toRaw + + ok(rawSign) + + clique.authorize(engine.signer, signerFunc) # convert times.Duration to chronos.Duration let period = chronos.seconds(clique.cfg.period.inSeconds) @@ -164,9 +164,14 @@ proc sealingLoop(engine: SealingEngineRef): Future[void] {.async.} = info "block generated", number=blk.header.blockNumber -proc new*(_: type SealingEngineRef, chain: Chain): SealingEngineRef = +proc new*(_: type SealingEngineRef, + chain: Chain, + ctx: EthContext, + signer: EthAddress): SealingEngineRef = SealingEngineRef( - chain: chain + chain: chain, + ctx: ctx, + signer: signer ) proc start*(engine: SealingEngineRef) = diff --git a/tests/test_graphql.nim b/tests/test_graphql.nim index d05ac371e..6a748911f 100644 --- a/tests/test_graphql.nim +++ b/tests/test_graphql.nim @@ -13,7 +13,8 @@ import eth/[p2p, common, trie/db, rlp, trie], graphql, ../nimbus/graphql/ethapi, graphql/test_common, ../nimbus/sync/protocol_eth65, - ../nimbus/[genesis, config, chain_config], ../nimbus/db/[db_chain, state_db], + ../nimbus/[genesis, config, chain_config, context], + ../nimbus/db/[db_chain, state_db], ../nimbus/p2p/chain, ../premix/parser, ./test_helpers type @@ -83,7 +84,8 @@ proc graphqlMain*() = ) let - ethNode = setupEthNode(eth) + ethCtx = newEthContext() + ethNode = setupEthNode(conf, ethCtx, eth) chainDB = newBaseChainDB(newMemoryDb(), pruneTrie = false, conf.net.networkId diff --git a/tests/test_helpers.nim b/tests/test_helpers.nim index 7102c6d6b..7339109a8 100644 --- a/tests/test_helpers.nim +++ b/tests/test_helpers.nim @@ -11,7 +11,7 @@ import testutils/markdown_reports, ../nimbus/[constants, config, transaction, utils, errors, forks], ../nimbus/db/accounts_cache, - ../nimbus/random_keys + ../nimbus/context func revmap(x: Table[Fork, string]): Table[string, Fork] = result = initTable[string, Fork]() @@ -202,7 +202,7 @@ proc parseAccessList(n: JsonNode): AccessList = proc getFixtureTransaction*(j: JsonNode, dataIndex, gasIndex, valueIndex: int): Transaction = let dynamicFeeTx = "gasPrice" notin j - let nonce = j["nonce"].getHexadecimalInt.AccountNonce + let nonce = j["nonce"].getHexadecimalInt.AccountNonce let gasLimit = j["gasLimit"][gasIndex].getHexadecimalInt var toAddr: Option[EthAddress] @@ -225,7 +225,7 @@ proc getFixtureTransaction*(j: JsonNode, dataIndex, gasIndex, valueIndex: int): var secretKey = j["secretKey"].getStr removePrefix(secretKey, "0x") let privateKey = PrivateKey.fromHex(secretKey).tryGet() - + if dynamicFeeTx: let accList = j["accessLists"][dataIndex] var tx = Transaction( @@ -241,7 +241,7 @@ proc getFixtureTransaction*(j: JsonNode, dataIndex, gasIndex, valueIndex: int): chainId: ChainId(1) ) return signTransaction(tx, privateKey, ChainId(1), false) - + let gasPrice = j["gasPrice"].getHexadecimalInt if j.hasKey("accessLists"): let accList = j["accessLists"][dataIndex] @@ -272,11 +272,8 @@ proc getFixtureTransaction*(j: JsonNode, dataIndex, gasIndex, valueIndex: int): proc hashLogEntries*(logs: seq[Log]): string = toLowerAscii("0x" & $keccakHash(rlp.encode(logs))) -proc setupEthNode*(capabilities: varargs[ProtocolInfo, `protocolInfo`]): EthereumNode = - var conf = getConfiguration() - conf.net.nodekey = randomPrivateKey() - let keypair = conf.net.nodekey.toKeyPair() - +proc setupEthNode*(conf: NimbusConfiguration, ctx: EthContext, capabilities: varargs[ProtocolInfo, `protocolInfo`]): EthereumNode = + let keypair = ctx.hexToKeyPair(conf.net.nodekey).tryGet() var srvAddress: Address srvAddress.ip = parseIpAddress("0.0.0.0") srvAddress.tcpPort = Port(conf.net.bindPort) diff --git a/tests/test_rpc.nim b/tests/test_rpc.nim index 73e86a15d..2ba5f474a 100644 --- a/tests/test_rpc.nim +++ b/tests/test_rpc.nim @@ -123,16 +123,14 @@ proc setupEnv(chain: BaseChainDB, signer, ks2: EthAddress, ctx: EthContext): Tes proc rpcMain*() = suite "Remote Procedure Calls": # TODO: Include other transports such as Http - var - ethNode = setupEthNode(eth) - chain = newBaseChainDB(newMemoryDb()) - let + conf = getConfiguration() + ctx = newEthContext() + ethNode = setupEthNode(conf, ctx, eth) + chain = newBaseChainDB(newMemoryDb()) signer: EthAddress = hexToByteArray[20]("0x0e69cde81b1aa07a45c32c6cd85d67229d36bb1b") ks2: EthAddress = hexToByteArray[20]("0xa3b2222afa5c987da6ef773fde8d01b9f23d481f") ks3: EthAddress = hexToByteArray[20]("0x597176e9a64aad0845d83afdaf698fbeff77703b") - conf = getConfiguration() - ctx = newEthContext() ethNode.chain = newChain(chain) conf.keyStore = "tests" / "keystore"