config: remove accounts management from NimbusConfiguration
a new AccountsManager and EthContext is created for managing keystore and accounts this is a preparation for new config using ConfUtils
This commit is contained in:
parent
07b2116f6b
commit
34972c6cea
|
@ -0,0 +1,104 @@
|
|||
# Nimbus
|
||||
# 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))
|
||||
# at your option.
|
||||
# This file may not be copied, modified, or distributed except according to
|
||||
# those terms.
|
||||
|
||||
import
|
||||
std/[os, json, tables],
|
||||
stew/[byteutils, results],
|
||||
eth/[keyfile, common, keys],
|
||||
chronicles
|
||||
|
||||
from nimcrypto/utils import burnMem
|
||||
|
||||
type
|
||||
NimbusAccount* = object
|
||||
privateKey*: PrivateKey
|
||||
keystore*: JsonNode
|
||||
unlocked*: bool
|
||||
|
||||
AccountsManager* = object
|
||||
accounts: Table[EthAddress, NimbusAccount]
|
||||
|
||||
proc init*(_: type AccountsManager): AccountsManager =
|
||||
discard
|
||||
|
||||
proc loadKeystores*(am: var AccountsManager, path: string): Result[void, string] =
|
||||
try:
|
||||
createDir(path)
|
||||
except OSError, IOError:
|
||||
return err("keystore: cannot create directory")
|
||||
|
||||
for filename in walkDirRec(path):
|
||||
try:
|
||||
var data = json.parseFile(filename)
|
||||
let address: EthAddress = hexToByteArray[20](data["address"].getStr())
|
||||
am.accounts[address] = NimbusAccount(keystore: data, unlocked: false)
|
||||
except JsonParsingError:
|
||||
return err("keystore: json parsing error " & filename)
|
||||
except ValueError:
|
||||
return err("keystore: data parsing error")
|
||||
except Exception: # json raises Exception
|
||||
return err("keystore: " & getCurrentExceptionMsg())
|
||||
|
||||
ok()
|
||||
|
||||
proc getAccount*(am: var AccountsManager, address: EthAddress): Result[NimbusAccount, string] =
|
||||
am.accounts.withValue(address, value) do:
|
||||
return ok(value[])
|
||||
do:
|
||||
return err("getAccount: not available " & address.toHex)
|
||||
|
||||
proc unlockAccount*(am: var AccountsManager, address: EthAddress, password: string): Result[void, string] =
|
||||
let accRes = am.getAccount(address)
|
||||
if accRes.isErr:
|
||||
return err(accRes.error)
|
||||
|
||||
var acc = accRes.get()
|
||||
let res = decodeKeyFileJson(acc.keystore, password)
|
||||
if res.isOk:
|
||||
acc.privateKey = res.get()
|
||||
acc.unlocked = true
|
||||
am.accounts[address] = acc
|
||||
return ok()
|
||||
|
||||
err($res.error)
|
||||
|
||||
proc lockAccount*(am: var AccountsManager, address: EthAddress): Result[void, string] =
|
||||
am.accounts.withValue(address, acc) do:
|
||||
acc.unlocked = false
|
||||
burnMem(acc.privateKey)
|
||||
am.accounts[address] = acc[]
|
||||
return ok()
|
||||
do:
|
||||
return err("getAccount: not available " & address.toHex)
|
||||
|
||||
proc numAccounts*(am: AccountsManager): int =
|
||||
am.accounts.len
|
||||
|
||||
iterator addresses*(am: AccountsManager): EthAddress =
|
||||
for a in am.accounts.keys:
|
||||
yield a
|
||||
|
||||
proc importPrivateKey*(am: var AccountsManager, fileName: string): Result[void, string] =
|
||||
try:
|
||||
let pkhex = readFile(fileName)
|
||||
let res = PrivateKey.fromHex(pkhex)
|
||||
if res.isErr:
|
||||
return err("not a valid private key, expect 32 bytes hex")
|
||||
|
||||
let seckey = res.get()
|
||||
let acc = seckey.toPublicKey().toCanonicalAddress()
|
||||
|
||||
am.accounts[acc] = NimbusAccount(
|
||||
privateKey: seckey,
|
||||
unlocked: true
|
||||
)
|
||||
|
||||
return ok()
|
||||
except CatchableError as ex:
|
||||
return err(ex.msg)
|
|
@ -114,11 +114,6 @@ type
|
|||
Full
|
||||
Archive
|
||||
|
||||
NimbusAccount* = object
|
||||
privateKey*: PrivateKey
|
||||
keystore*: JsonNode
|
||||
unlocked*: bool
|
||||
|
||||
NimbusConfiguration* = ref object
|
||||
## Main Nimbus configuration object
|
||||
dataDir*: string
|
||||
|
@ -133,7 +128,7 @@ type
|
|||
# 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
|
||||
accounts*: Table[EthAddress, NimbusAccount]
|
||||
importKey*: string
|
||||
importFile*: string
|
||||
verifyFromOk*: bool ## activate `verifyFrom` setting
|
||||
verifyFrom*: uint64 ## verification start block, 0 for disable
|
||||
|
@ -383,26 +378,6 @@ proc processEthAddress(value: string, address: var EthAddress): ConfigStatus =
|
|||
except CatchableError:
|
||||
return ErrorParseOption
|
||||
|
||||
proc importPrivateKey(conf: NimbusConfiguration, fileName: string): ConfigStatus =
|
||||
|
||||
try:
|
||||
let pkhex = readFile(fileName)
|
||||
let res = PrivateKey.fromHex(pkhex)
|
||||
if res.isErr:
|
||||
error "not a valid private key, expect 32 bytes hex"
|
||||
return ErrorParseOption
|
||||
|
||||
let seckey = res.get()
|
||||
let acc = seckey.toPublicKey().toCanonicalAddress()
|
||||
|
||||
conf.accounts[acc] = NimbusAccount(
|
||||
privateKey: seckey,
|
||||
unlocked: true
|
||||
)
|
||||
|
||||
except CatchableError:
|
||||
return ErrorParseOption
|
||||
|
||||
proc processEthArguments(key, value: string): ConfigStatus =
|
||||
result = Success
|
||||
let config = getConfiguration()
|
||||
|
@ -423,7 +398,7 @@ proc processEthArguments(key, value: string): ConfigStatus =
|
|||
of "engine-signer":
|
||||
result = processEthAddress(value, config.engineSigner)
|
||||
of "import-key":
|
||||
result = config.importPrivateKey(value)
|
||||
config.importKey = value
|
||||
else:
|
||||
result = EmptyOption
|
||||
|
||||
|
@ -686,7 +661,6 @@ proc initConfiguration(): NimbusConfiguration =
|
|||
## Allocates and initializes `NimbusConfiguration` with default values
|
||||
result = new NimbusConfiguration
|
||||
result.rng = newRng()
|
||||
result.accounts = initTable[EthAddress, NimbusAccount]()
|
||||
|
||||
## Graphql defaults
|
||||
result.graphql.enabled = false
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
# Nimbus
|
||||
# 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))
|
||||
# at your option.
|
||||
# This file may not be copied, modified, or distributed except according to
|
||||
# those terms.
|
||||
|
||||
import
|
||||
accounts/manager
|
||||
|
||||
export manager
|
||||
|
||||
type
|
||||
EthContext* = ref object
|
||||
am*: AccountsManager
|
||||
|
||||
|
||||
proc newEthContext*(): EthContext =
|
||||
result = new(EthContext)
|
||||
result.am = AccountsManager.init()
|
|
@ -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, ../db/[db_chain, state_db],
|
||||
".."/[transaction, config, vm_state, constants, utils, context],
|
||||
../db/[db_chain, state_db],
|
||||
rpc_types, rpc_utils,
|
||||
../transaction/call_evm
|
||||
|
||||
|
@ -25,7 +25,7 @@ import
|
|||
type cast to avoid extra processing.
|
||||
]#
|
||||
|
||||
proc setupEthRpc*(node: EthereumNode, chain: BaseChainDB , server: RpcServer) =
|
||||
proc setupEthRpc*(node: EthereumNode, ctx: EthContext, chain: BaseChainDB , server: RpcServer) =
|
||||
|
||||
proc getAccountDb(header: BlockHeader): ReadOnlyStateDB =
|
||||
## Retrieves the account db from canonical head
|
||||
|
@ -83,10 +83,9 @@ proc setupEthRpc*(node: EthereumNode, chain: BaseChainDB , server: RpcServer) =
|
|||
result = encodeQuantity(calculateMedianGasPrice(chain).uint64)
|
||||
|
||||
server.rpc("eth_accounts") do() -> seq[EthAddressStr]:
|
||||
## Returns a list of addresses owned by client.
|
||||
let conf = getConfiguration()
|
||||
result = newSeqOfCap[EthAddressStr](conf.accounts.len)
|
||||
for k in keys(conf.accounts):
|
||||
## Returns a list of addresses owned by client.
|
||||
result = newSeqOfCap[EthAddressStr](ctx.am.numAccounts)
|
||||
for k in ctx.am.addresses:
|
||||
result.add ethAddressStr(k)
|
||||
|
||||
server.rpc("eth_blockNumber") do() -> HexQuantityStr:
|
||||
|
@ -199,9 +198,8 @@ proc setupEthRpc*(node: EthereumNode, chain: BaseChainDB , server: RpcServer) =
|
|||
## message: message to sign.
|
||||
## Returns signature.
|
||||
let
|
||||
address = data.toAddress
|
||||
conf = getConfiguration()
|
||||
acc = conf.getAccount(address).tryGet()
|
||||
address = data.toAddress
|
||||
acc = ctx.am.getAccount(address).tryGet()
|
||||
msg = hexToSeqByte(message.string)
|
||||
|
||||
if not acc.unlocked:
|
||||
|
@ -213,8 +211,7 @@ proc setupEthRpc*(node: EthereumNode, chain: BaseChainDB , server: RpcServer) =
|
|||
## eth_sendRawTransaction
|
||||
let
|
||||
address = data.source.toAddress
|
||||
conf = getConfiguration()
|
||||
acc = conf.getAccount(address).tryGet()
|
||||
acc = ctx.am.getAccount(address).tryGet()
|
||||
|
||||
if not acc.unlocked:
|
||||
raise newException(ValueError, "Account locked, please unlock it first")
|
||||
|
@ -237,8 +234,7 @@ proc setupEthRpc*(node: EthereumNode, chain: BaseChainDB , server: RpcServer) =
|
|||
# TODO: Relies on pending pool implementation
|
||||
let
|
||||
address = data.source.toAddress
|
||||
conf = getConfiguration()
|
||||
acc = conf.getAccount(address).tryGet()
|
||||
acc = ctx.am.getAccount(address).tryGet()
|
||||
|
||||
if not acc.unlocked:
|
||||
raise newException(ValueError, "Account locked, please unlock it first")
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import
|
||||
os, tables, json, ./config, stew/[results, byteutils],
|
||||
eth/trie/db, eth/[trie, rlp, common, keyfile], nimcrypto
|
||||
pkg/[nimcrypto],
|
||||
eth/[trie, rlp, common, trie/db]
|
||||
|
||||
export nimcrypto.`$`
|
||||
|
||||
|
@ -58,40 +58,3 @@ proc crc32*(crc: uint32, buf: openArray[byte]): uint32 =
|
|||
crcu32 = (crcu32 shr 4) xor kcrc32[int((crcu32 and 0xF) xor (uint32(b) shr 4'u32))]
|
||||
|
||||
result = not crcu32
|
||||
|
||||
proc loadKeystoreFiles*(conf: NimbusConfiguration): Result[void, string] =
|
||||
try:
|
||||
createDir(conf.keyStore)
|
||||
except OSError, IOError:
|
||||
return err("keystore: cannot create directory")
|
||||
|
||||
for filename in walkDirRec(conf.keyStore):
|
||||
try:
|
||||
var data = json.parseFile(filename)
|
||||
let address: EthAddress = hexToByteArray[20](data["address"].getStr())
|
||||
conf.accounts[address] = NimbusAccount(keystore: data, unlocked: false)
|
||||
except JsonParsingError:
|
||||
return err("keystore: json parsing error " & filename)
|
||||
except ValueError:
|
||||
return err("keystore: data parsing error")
|
||||
except Exception: # json raises Exception
|
||||
return err("keystore: " & getCurrentExceptionMsg())
|
||||
|
||||
result = ok()
|
||||
|
||||
proc getAccount*(conf: NimbusConfiguration, address: EthAddress): Result[NimbusAccount, string] =
|
||||
conf.accounts.withValue(address, val) do:
|
||||
result = ok(val[])
|
||||
do:
|
||||
result = err("getAccount: not available " & address.toHex)
|
||||
|
||||
proc unlockAccount*(conf: NimbusConfiguration, address: EthAddress, password: string): Result[void, string] =
|
||||
var acc = conf.getAccount(address).tryGet()
|
||||
let res = decodeKeyFileJson(acc.keystore, password)
|
||||
if res.isOk:
|
||||
acc.privateKey = res.get()
|
||||
acc.unlocked = true
|
||||
conf.accounts[address] = acc
|
||||
result = ok()
|
||||
else:
|
||||
result = err($res.error)
|
||||
|
|
|
@ -19,7 +19,7 @@ import
|
|||
clique/clique_helpers
|
||||
],
|
||||
../nimbus/utils/ec_recover,
|
||||
../nimbus/[config, utils, constants],
|
||||
../nimbus/[config, utils, constants, context],
|
||||
./test_clique/[pool, undump],
|
||||
eth/[common, keys],
|
||||
stint, stew/byteutils,
|
||||
|
@ -248,15 +248,19 @@ proc cliqueMiscTests() =
|
|||
var opt = initOptParser("--engine-signer:$1 --import-key:$2" % [engineSigner, privateKey])
|
||||
let res = processArguments(msg, opt)
|
||||
check res == Success
|
||||
let signer = hexToByteArray[20](engineSigner)
|
||||
let conf = getConfiguration()
|
||||
check signer in conf.accounts
|
||||
|
||||
let
|
||||
signer = hexToByteArray[20](engineSigner)
|
||||
ctx = newEthContext()
|
||||
conf = getConfiguration()
|
||||
|
||||
check ctx.am.importPrivateKey(conf.importKey).isOk()
|
||||
check ctx.am.getAccount(signer).isOk()
|
||||
|
||||
proc signFunc(signer: EthAddress, message: openArray[byte]): Result[RawSignature, cstring] {.gcsafe.} =
|
||||
let
|
||||
hashData = keccakHash(message)
|
||||
conf = getConfiguration()
|
||||
acc = conf.accounts[signer]
|
||||
hashData = keccakHash(message)
|
||||
acc = ctx.am.getAccount(signer).tryGet()
|
||||
rawSign = sign(acc.privateKey, SkMessage(hashData.data)).toRaw
|
||||
|
||||
ok(rawSign)
|
||||
|
|
|
@ -16,6 +16,7 @@ import
|
|||
../nimbus/p2p/[chain, executor, executor/executor_helpers],
|
||||
../nimbus/sync/protocol_eth65,
|
||||
../nimbus/utils/difficulty,
|
||||
../nimbus/context,
|
||||
./rpcclient/test_hexstrings, ./test_helpers, ./macro_assembler
|
||||
|
||||
# Perform checks for hex string validation
|
||||
|
@ -35,11 +36,11 @@ type
|
|||
txHash: Hash256
|
||||
blockHash: HAsh256
|
||||
|
||||
proc setupEnv(chain: BaseChainDB, signer, ks2: EthAddress, conf: NimbusConfiguration): TestEnv =
|
||||
proc setupEnv(chain: BaseChainDB, signer, ks2: EthAddress, ctx: EthContext): TestEnv =
|
||||
var
|
||||
parent = chain.getCanonicalHead()
|
||||
ac = newAccountStateDB(chain.db, parent.stateRoot, chain.pruneTrie)
|
||||
acc = conf.getAccount(signer).tryGet()
|
||||
acc = ctx.am.getAccount(signer).tryGet()
|
||||
blockNumber = 1.toBlockNumber
|
||||
parentHash = parent.blockHash
|
||||
|
||||
|
@ -131,23 +132,24 @@ proc rpcMain*() =
|
|||
ks2: EthAddress = hexToByteArray[20]("0xa3b2222afa5c987da6ef773fde8d01b9f23d481f")
|
||||
ks3: EthAddress = hexToByteArray[20]("0x597176e9a64aad0845d83afdaf698fbeff77703b")
|
||||
conf = getConfiguration()
|
||||
ctx = newEthContext()
|
||||
|
||||
ethNode.chain = newChain(chain)
|
||||
conf.keyStore = "tests" / "keystore"
|
||||
let res = conf.loadKeystoreFiles()
|
||||
let res = ctx.am.loadKeystores(conf.keyStore)
|
||||
if res.isErr:
|
||||
debugEcho res.error
|
||||
doAssert(res.isOk)
|
||||
|
||||
let acc1 = conf.getAccount(signer).tryGet()
|
||||
let unlock = conf.unlockAccount(signer, acc1.keystore["password"].getStr())
|
||||
let acc1 = ctx.am.getAccount(signer).tryGet()
|
||||
let unlock = ctx.am.unlockAccount(signer, acc1.keystore["password"].getStr())
|
||||
if unlock.isErr:
|
||||
debugEcho unlock.error
|
||||
doAssert(unlock.isOk)
|
||||
|
||||
defaultGenesisBlockForNetwork(conf.net.networkId).commit(chain)
|
||||
doAssert(canonicalHeadHashKey().toOpenArray in chain.db)
|
||||
let env = setupEnv(chain, signer, ks2, conf)
|
||||
let env = setupEnv(chain, signer, ks2, ctx)
|
||||
|
||||
# Create Ethereum RPCs
|
||||
let RPC_PORT = 8545
|
||||
|
@ -155,7 +157,7 @@ proc rpcMain*() =
|
|||
rpcServer = newRpcSocketServer(["localhost:" & $RPC_PORT])
|
||||
client = newRpcSocketClient()
|
||||
setupCommonRpc(ethNode, rpcServer)
|
||||
setupEthRpc(ethNode, chain, rpcServer)
|
||||
setupEthRpc(ethNode, ctx, chain, rpcServer)
|
||||
|
||||
# Begin tests
|
||||
rpcServer.start()
|
||||
|
|
Loading…
Reference in New Issue