implement more eth rpc and keystore management
This commit is contained in:
parent
336efdb0c3
commit
f82dff64fa
|
@ -8,7 +8,7 @@
|
|||
# those terms.
|
||||
|
||||
import
|
||||
parseopt, strutils, macros, os, times, json, stew/[byteutils],
|
||||
parseopt, strutils, macros, os, times, json, tables, stew/[byteutils],
|
||||
chronos, eth/[keys, common, p2p, net/nat], chronicles, nimcrypto/hash,
|
||||
eth/p2p/bootnodes, eth/p2p/rlpx_protocols/whisper_protocol,
|
||||
./db/select_backend, eth/keys,
|
||||
|
@ -121,6 +121,11 @@ type
|
|||
Full
|
||||
Archive
|
||||
|
||||
NimbusAccount* = object
|
||||
privateKey*: PrivateKey
|
||||
keystore*: JsonNode
|
||||
unlocked*: bool
|
||||
|
||||
ChainConfig* = object
|
||||
chainId*: uint
|
||||
homesteadBlock*: BlockNumber
|
||||
|
@ -143,7 +148,7 @@ type
|
|||
NimbusConfiguration* = ref object
|
||||
## Main Nimbus configuration object
|
||||
dataDir*: string
|
||||
keyFile*: string
|
||||
keyStore*: string
|
||||
prune*: PruneMode
|
||||
rpc*: RpcConfiguration ## JSON-RPC configuration
|
||||
net*: NetConfiguration ## Network configuration
|
||||
|
@ -153,6 +158,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]
|
||||
|
||||
CustomGenesisConfig = object
|
||||
chainId*: uint
|
||||
|
@ -557,11 +563,8 @@ proc processEthArguments(key, value: string): ConfigStatus =
|
|||
result = Success
|
||||
let config = getConfiguration()
|
||||
case key.toLowerAscii()
|
||||
of "keyfile":
|
||||
if fileExists(value):
|
||||
config.keyFile = value
|
||||
else:
|
||||
result = ErrorIncorrectOption
|
||||
of "keystore":
|
||||
config.keyStore = value
|
||||
of "datadir":
|
||||
config.dataDir = value
|
||||
of "prune":
|
||||
|
@ -825,16 +828,20 @@ template processArgument(processor, key, value, msg: untyped) =
|
|||
|
||||
proc getDefaultDataDir*(): string =
|
||||
when defined(windows):
|
||||
"AppData" / "Roaming" / "Nimbus" / "DB"
|
||||
"AppData" / "Roaming" / "Nimbus"
|
||||
elif defined(macosx):
|
||||
"Library" / "Application Support" / "Nimbus" / "DB"
|
||||
"Library" / "Application Support" / "Nimbus"
|
||||
else:
|
||||
".cache" / "nimbus" / "db"
|
||||
".cache" / "nimbus"
|
||||
|
||||
proc getDefaultKeystoreDir*(): string =
|
||||
getDefaultDataDir() / "keystore"
|
||||
|
||||
proc initConfiguration(): NimbusConfiguration =
|
||||
## Allocates and initializes `NimbusConfiguration` with default values
|
||||
result = new NimbusConfiguration
|
||||
result.rng = newRng()
|
||||
result.accounts = initTable[EthAddress, NimbusAccount]()
|
||||
|
||||
## RPC defaults
|
||||
result.rpc.flags = {}
|
||||
|
@ -853,9 +860,12 @@ proc initConfiguration(): NimbusConfiguration =
|
|||
result.net.protocols = defaultProtocols
|
||||
result.net.nodekey = random(PrivateKey, result.rng[])
|
||||
|
||||
const dataDir = getDefaultDataDir()
|
||||
const
|
||||
dataDir = getDefaultDataDir()
|
||||
keystore = getDefaultKeystoreDir()
|
||||
|
||||
result.dataDir = getHomeDir() / dataDir
|
||||
result.keystore = getHomeDir() / keystore
|
||||
result.prune = PruneMode.Full
|
||||
|
||||
## Whisper defaults
|
||||
|
@ -897,7 +907,7 @@ USAGE:
|
|||
nimbus [options]
|
||||
|
||||
ETHEREUM OPTIONS:
|
||||
--keyfile:<value> Use keyfile storage file
|
||||
--keystore:<value> Directory for the keystore (default = inside the datadir)
|
||||
--datadir:<value> Base directory for all blockchain-related data
|
||||
--prune:<value> Blockchain prune mode(full or archive)
|
||||
|
||||
|
|
|
@ -159,10 +159,8 @@ iterator getBlockTransactionHashes(self: BaseChainDB, blockHeader: BlockHeader):
|
|||
for encodedTx in self.getBlockTransactionData(blockHeader.txRoot):
|
||||
yield keccakHash(encodedTx)
|
||||
|
||||
proc getTransactionCount*(chain: BaseChainDB, blockHash: Hash256): int =
|
||||
var header: BlockHeader
|
||||
if chain.getBlockHeader(blockHash, header):
|
||||
var trie = initHexaryTrie(chain.db, header.txRoot)
|
||||
proc getTransactionCount*(chain: BaseChainDB, txRoot: Hash256): int =
|
||||
var trie = initHexaryTrie(chain.db, txRoot)
|
||||
var txCount = 0
|
||||
while true:
|
||||
let txKey = rlp.encode(txCount)
|
||||
|
@ -170,6 +168,13 @@ proc getTransactionCount*(chain: BaseChainDB, blockHash: Hash256): int =
|
|||
break
|
||||
inc txCount
|
||||
|
||||
proc getUnclesCount*(self: BaseChainDB, ommersHash: Hash256): int =
|
||||
if ommersHash != EMPTY_UNCLE_HASH:
|
||||
let encodedUncles = self.db.get(genericHashKey(ommersHash).toOpenArray)
|
||||
if encodedUncles.len != 0:
|
||||
let r = rlpFromBytes(encodedUncles)
|
||||
result = r.listLen
|
||||
|
||||
proc getBlockBody*(self: BaseChainDB, blockHash: Hash256, output: var BlockBody): bool =
|
||||
var header: BlockHeader
|
||||
if self.getBlockHeader(blockHash, header):
|
||||
|
|
|
@ -15,7 +15,7 @@ import
|
|||
eth/p2p/rlpx_protocols/[eth_protocol, les_protocol, whisper_protocol],
|
||||
eth/p2p/blockchain_sync, eth/net/nat, eth/p2p/peer_pool,
|
||||
config, genesis, rpc/[common, p2p, debug, whisper, key_storage], p2p/chain,
|
||||
eth/trie/db, metrics, metrics/chronicles_support
|
||||
eth/trie/db, metrics, metrics/chronicles_support, utils
|
||||
|
||||
## TODO:
|
||||
## * No IPv6 support
|
||||
|
@ -36,6 +36,7 @@ type
|
|||
|
||||
proc start(nimbus: NimbusNode) =
|
||||
var conf = getConfiguration()
|
||||
conf.loadKeystoreFiles()
|
||||
|
||||
## logging
|
||||
setLogLevel(conf.debug.logLevel)
|
||||
|
|
|
@ -96,15 +96,13 @@ proc setupEthRpc*(node: EthereumNode, chain: BaseChainDB , server: RpcServer) =
|
|||
proc accountDbFromTag(tag: string, readOnly = true): ReadOnlyStateDB =
|
||||
result = getAccountDb(chain.headerFromTag(tag))
|
||||
|
||||
#[proc getBlockBody(hash: KeccakHash): BlockBody =
|
||||
if not chain.getBlockBody(hash, result):
|
||||
raise newException(ValueError, "Cannot find hash")]#
|
||||
|
||||
server.rpc("eth_protocolVersion") do() -> string:
|
||||
result = $eth_protocol.protocolVersion
|
||||
|
||||
server.rpc("eth_syncing") do() -> JsonNode:
|
||||
## Returns SyncObject or false when not syncing.
|
||||
# TODO: make sure we are not syncing
|
||||
# when we reach the recent block
|
||||
let numPeers = node.peerPool.connectedNodes.len
|
||||
if numPeers > 0:
|
||||
var sync = SyncState(
|
||||
|
@ -137,7 +135,10 @@ proc setupEthRpc*(node: EthereumNode, chain: BaseChainDB , server: RpcServer) =
|
|||
|
||||
server.rpc("eth_accounts") do() -> seq[EthAddressStr]:
|
||||
## Returns a list of addresses owned by client.
|
||||
result = @[]
|
||||
let conf = getConfiguration()
|
||||
result = newSeqOfCap[EthAddressStr](conf.accounts.len)
|
||||
for k in keys(conf.accounts):
|
||||
result.add ethAddressStr(k)
|
||||
|
||||
server.rpc("eth_blockNumber") do() -> HexQuantityStr:
|
||||
## Returns integer of the current block number the client is on.
|
||||
|
@ -150,9 +151,9 @@ proc setupEthRpc*(node: EthereumNode, chain: BaseChainDB , server: RpcServer) =
|
|||
## quantityTag: integer block number, or the string "latest", "earliest" or "pending", see the default block parameter.
|
||||
## Returns integer of the current balance in wei.
|
||||
let
|
||||
accountDb = accountDbFromTag(quantityTag)
|
||||
accDB = accountDbFromTag(quantityTag)
|
||||
address = data.toAddress
|
||||
balance = accountDb.getBalance(address)
|
||||
balance = accDB.getBalance(address)
|
||||
result = encodeQuantity(balance)
|
||||
|
||||
server.rpc("eth_getStorageAt") do(data: EthAddressStr, quantity: HexQuantityStr, quantityTag: string) -> HexDataStr:
|
||||
|
@ -163,10 +164,10 @@ proc setupEthRpc*(node: EthereumNode, chain: BaseChainDB , server: RpcServer) =
|
|||
## quantityTag: integer block number, or the string "latest", "earliest" or "pending", see the default block parameter.
|
||||
## Returns: the value at this storage position.
|
||||
let
|
||||
accountDb = accountDbFromTag(quantityTag)
|
||||
accDB = accountDbFromTag(quantityTag)
|
||||
address = data.toAddress
|
||||
key = fromHex(Uint256, quantity.string)
|
||||
value = accountDb.getStorage(address, key)[0]
|
||||
value = accDB.getStorage(address, key)[0]
|
||||
result = hexDataStr(value)
|
||||
|
||||
server.rpc("eth_getTransactionCount") do(data: EthAddressStr, quantityTag: string) -> HexQuantityStr:
|
||||
|
@ -177,8 +178,8 @@ proc setupEthRpc*(node: EthereumNode, chain: BaseChainDB , server: RpcServer) =
|
|||
## Returns integer of the number of transactions send from this address.
|
||||
let
|
||||
address = data.toAddress
|
||||
accountDb = accountDbFromTag(quantityTag)
|
||||
result = encodeQuantity(accountDb.getNonce(address))
|
||||
accDB = accountDbFromTag(quantityTag)
|
||||
result = encodeQuantity(accDB.getNonce(address))
|
||||
|
||||
server.rpc("eth_getBlockTransactionCountByHash") do(data: EthHashStr) -> HexQuantityStr:
|
||||
## Returns the number of transactions in a block from a block matching the given block hash.
|
||||
|
@ -186,33 +187,41 @@ proc setupEthRpc*(node: EthereumNode, chain: BaseChainDB , server: RpcServer) =
|
|||
## data: hash of a block
|
||||
## Returns integer of the number of transactions in this block.
|
||||
let
|
||||
hashData = data.toHash
|
||||
txCount = chain.getTransactionCount(hashData)
|
||||
blockHash = data.toHash
|
||||
header = chain.getBlockHeader(blockHash)
|
||||
txCount = chain.getTransactionCount(header.txRoot)
|
||||
result = encodeQuantity(txCount.uint)
|
||||
#[
|
||||
server.rpc("eth_getBlockTransactionCountByNumber") do(quantityTag: string) -> int:
|
||||
|
||||
server.rpc("eth_getBlockTransactionCountByNumber") do(quantityTag: string) -> HexQuantityStr:
|
||||
## Returns the number of transactions in a block matching the given block number.
|
||||
##
|
||||
## data: integer of a block number, or the string "earliest", "latest" or "pending", as in the default block parameter.
|
||||
## Returns integer of the number of transactions in this block.
|
||||
let header = chain.headerFromTag(quantityTag)
|
||||
result = getBlockBody(header.hash).transactions.len
|
||||
let
|
||||
header = chain.headerFromTag(quantityTag)
|
||||
txCount = chain.getTransactionCount(header.txRoot)
|
||||
result = encodeQuantity(txCount.uint)
|
||||
|
||||
server.rpc("eth_getUncleCountByBlockHash") do(data: EthHashStr) -> int:
|
||||
server.rpc("eth_getUncleCountByBlockHash") do(data: EthHashStr) -> HexQuantityStr:
|
||||
## Returns the number of uncles in a block from a block matching the given block hash.
|
||||
##
|
||||
## data: hash of a block.
|
||||
## Returns integer of the number of uncles in this block.
|
||||
var hashData = data.toHash
|
||||
result = getBlockBody(hashData).uncles.len
|
||||
let
|
||||
blockHash = data.toHash
|
||||
header = chain.getBlockHeader(blockHash)
|
||||
unclesCount = chain.getUnclesCount(header.ommersHash)
|
||||
result = encodeQuantity(unclesCount.uint)
|
||||
|
||||
server.rpc("eth_getUncleCountByBlockNumber") do(quantityTag: string) -> int:
|
||||
server.rpc("eth_getUncleCountByBlockNumber") do(quantityTag: string) -> HexQuantityStr:
|
||||
## Returns the number of uncles in a block from a block matching the given block number.
|
||||
##
|
||||
## quantityTag: integer of a block number, or the string "latest", "earliest" or "pending", see the default block parameter.
|
||||
## Returns integer of uncles in this block.
|
||||
let header = chain.headerFromTag(quantityTag)
|
||||
result = getBlockBody(header.hash).uncles.len
|
||||
let
|
||||
header = chain.headerFromTag(quantityTag)
|
||||
unclesCount = chain.getUnclesCount(header.ommersHash)
|
||||
result = encodeQuantity(unclesCount.uint)
|
||||
|
||||
server.rpc("eth_getCode") do(data: EthAddressStr, quantityTag: string) -> HexDataStr:
|
||||
## Returns code at a given address.
|
||||
|
@ -221,14 +230,13 @@ proc setupEthRpc*(node: EthereumNode, chain: BaseChainDB , server: RpcServer) =
|
|||
## quantityTag: integer block number, or the string "latest", "earliest" or "pending", see the default block parameter.
|
||||
## Returns the code from the given address.
|
||||
let
|
||||
accountDb = accountDbFromTag(quantityTag)
|
||||
address = toAddress(data)
|
||||
storage = accountDb.getCode(address)
|
||||
# Easier to return the string manually here rather than expect ByteRange to be marshalled
|
||||
result = byteutils.toHex(storage).HexDataStr
|
||||
accDB = accountDbFromTag(quantityTag)
|
||||
address = data.toAddress
|
||||
storage = accDB.getCode(address)
|
||||
result = hexDataStr(storage)
|
||||
|
||||
template sign(privateKey: PrivateKey, message: string): string =
|
||||
# TODO: Is message length encoded as bytes or characters?
|
||||
# message length encoded as ASCII representation of decimal
|
||||
let msgData = "\x19Ethereum Signed Message:\n" & $message.len & message
|
||||
$sign(privateKey, msgData.toBytes())
|
||||
|
||||
|
@ -241,9 +249,16 @@ proc setupEthRpc*(node: EthereumNode, chain: BaseChainDB , server: RpcServer) =
|
|||
## data: address.
|
||||
## message: message to sign.
|
||||
## Returns signature.
|
||||
var privateKey: PrivateKey # TODO: Get from key store
|
||||
result = ("0x" & sign(privateKey, message.string)).HexDataStr
|
||||
let
|
||||
address = data.toAddress
|
||||
conf = getConfiguration()
|
||||
acc = conf.getAccount(address).tryGet()
|
||||
msg = hexToSeqByte(message.string)
|
||||
|
||||
if not acc.unlocked:
|
||||
raise newException(ValueError, "Account locked, please unlock it first")
|
||||
result = ("0x" & sign(acc.privateKey, cast[string](msg))).HexDataStr
|
||||
#[
|
||||
# proc setupTransaction(send: EthSend): Transaction =
|
||||
# let
|
||||
# source = send.source.toAddress
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
import eth/trie/db, eth/[trie, rlp, common], nimcrypto
|
||||
import
|
||||
os, tables, json, ./config, stew/[results, byteutils],
|
||||
eth/trie/db, eth/[trie, rlp, common, keyfile], nimcrypto
|
||||
|
||||
export nimcrypto.`$`
|
||||
|
||||
|
@ -50,3 +52,40 @@ 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 @@ proc prefixHex*(x: Hash256): string =
|
|||
"0x" & toLowerAscii($x)
|
||||
|
||||
proc prefixHex*(x: int64 | uint64 | byte | int): string =
|
||||
encodeQuantity(x.uint64).toLowerAscii
|
||||
toLowerAscii(encodeQuantity(x.uint64).string)
|
||||
|
||||
proc prefixHex*(x: openArray[byte]): string =
|
||||
"0x" & toHex(x, true)
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
{"password":"applebanana","address":"0e69cde81b1aa07a45c32c6cd85d67229d36bb1b","crypto":{"cipher":"aes-128-ctr","cipherparams":{"iv":"7ead94c4f16f3bf31e1cdffd44227403"},"ciphertext":"f023cbdd0ebb26bea0342e2c5719da6a0499e9f675fff3d3c4beb84e1071174f","kdf":"pbkdf2","kdfparams":{"dklen":32,"c":1000000,"prf":"hmac-sha256","salt":"91edacb4f74698e6516062b230064385"},"mac":"f37290005290f56c71cce329f61945d9b2a1c719bc84e966d30c8c395477d625"},"id":"29014ab9-38c0-42fa-b5cf-f049d8568a41","version":3}
|
|
@ -0,0 +1 @@
|
|||
{"password":"bananamonkey","address":"597176e9a64aad0845d83afdaf698fbeff77703b","crypto":{"cipher":"aes-128-ctr","cipherparams":{"iv":"5ad7a73aa1b46b336bd9640a8cf19436"},"ciphertext":"068c556204f86b9e0d6670e1e80f8d34dfb55416b605c0b9f9d833ba962cc64c","kdf":"pbkdf2","kdfparams":{"dklen":32,"c":1000000,"prf":"hmac-sha256","salt":"aea9c4e3dd598d41926268ed2358965e"},"mac":"30cd4c38da212a85aa277c60ee8be7ae4e9cceea75cedeae516827d339d90550"},"id":"7667da26-1e5b-4281-98a3-c9aad03e8491","version":3}
|
|
@ -0,0 +1 @@
|
|||
{"password":"monkeyelephant","address":"a3b2222afa5c987da6ef773fde8d01b9f23d481f","crypto":{"cipher":"aes-128-ctr","cipherparams":{"iv":"3ca1d5c5151fba8281f8880ece920740"},"ciphertext":"ec8b7626f3494605e4b66a889c3dbf9bae4d8fe249718d25f010441af3cf6c61","kdf":"pbkdf2","kdfparams":{"dklen":32,"c":1000000,"prf":"hmac-sha256","salt":"364a67e8bc0a782d715832946662850c"},"mac":"7001a9fb0f63db2b7538bab2a34d2103c67035f4754a3bcf4760359f530d5c21"},"id":"5098841b-9060-4ce7-baf7-c748b12d79c9","version":3}
|
|
@ -14,7 +14,7 @@ import
|
|||
../../nimbus/rpc/hexstrings, ../../nimbus/rpc/rpc_types
|
||||
|
||||
proc web3_clientVersion(): string
|
||||
proc web3_sha3(data: string): string
|
||||
proc web3_sha3(data: HexDataStr): string
|
||||
proc net_version(): string
|
||||
proc net_peerCount(): HexQuantityStr
|
||||
proc net_listening(): bool
|
||||
|
@ -30,9 +30,9 @@ proc eth_getBalance(data: EthAddressStr, quantityTag: string): HexQuantityStr
|
|||
proc eth_getStorageAt(data: EthAddressStr, quantity: HexQuantityStr, quantityTag: string): seq[byte]
|
||||
proc eth_getTransactionCount(data: EthAddressStr, quantityTag: string): HexQuantityStr
|
||||
proc eth_getBlockTransactionCountByHash(data: Hash256): HexQuantityStr
|
||||
proc eth_getBlockTransactionCountByNumber(quantityTag: string)
|
||||
proc eth_getUncleCountByBlockHash(data: array[32, byte])
|
||||
proc eth_getUncleCountByBlockNumber(quantityTag: string)
|
||||
proc eth_getBlockTransactionCountByNumber(quantityTag: string): HexQuantityStr
|
||||
proc eth_getUncleCountByBlockHash(data: Hash256): HexQuantityStr
|
||||
proc eth_getUncleCountByBlockNumber(quantityTag: string): HexQuantityStr
|
||||
proc eth_getCode(data: EthAddressStr, quantityTag: string): HexDataStr
|
||||
proc eth_sign(data:EthAddressStr, message: HexDataStr): HexDataStr
|
||||
#proc eth_sendRawTransaction(data: string, quantityTag: int): UInt256
|
||||
|
|
|
@ -6,11 +6,12 @@
|
|||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
import
|
||||
unittest, json, strformat, strutils, options, tables, nimcrypto, stew/byteutils,
|
||||
unittest, json, strformat, strutils, options, tables, os,
|
||||
nimcrypto, stew/byteutils,
|
||||
json_rpc/[rpcserver, rpcclient], eth/common as eth_common,
|
||||
eth/[rlp, keys], eth/trie/db, eth/p2p/rlpx_protocols/eth_protocol,
|
||||
../nimbus/rpc/[common, p2p, hexstrings, rpc_types],
|
||||
../nimbus/[constants, vm_state, config, genesis],
|
||||
../nimbus/[constants, vm_state, config, genesis, utils],
|
||||
../nimbus/db/[accounts_cache, db_chain, storage_types],
|
||||
../nimbus/p2p/chain,
|
||||
./rpcclient/test_hexstrings, ./test_helpers
|
||||
|
@ -29,10 +30,6 @@ template sourceDir: string = currentSourcePath.rsplit(DirSep, 1)[0]
|
|||
const sigPath = &"{sourceDir}{DirSep}rpcclient{DirSep}ethcallsigs.nim"
|
||||
createRpcSigs(RpcSocketClient, sigPath)
|
||||
|
||||
proc toEthAddressStr(address: EthAddress): EthAddressStr =
|
||||
result = ("0x" & address.toHex).ethAddressStr
|
||||
|
||||
|
||||
proc doTests {.async.} =
|
||||
# TODO: Include other transports such as Http
|
||||
var ethNode = setupEthNode(eth)
|
||||
|
@ -47,7 +44,25 @@ proc doTests {.async.} =
|
|||
let
|
||||
balance = 100.u256
|
||||
address: EthAddress = hexToByteArray[20]("0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6")
|
||||
|
||||
signer: EthAddress = hexToByteArray[20]("0x0e69cde81b1aa07a45c32c6cd85d67229d36bb1b")
|
||||
ks2: EthAddress = hexToByteArray[20]("0xa3b2222afa5c987da6ef773fde8d01b9f23d481f")
|
||||
ks3: EthAddress = hexToByteArray[20]("0x597176e9a64aad0845d83afdaf698fbeff77703b")
|
||||
|
||||
conf = getConfiguration()
|
||||
|
||||
conf.keyStore = "tests" / "keystore"
|
||||
let res = conf.loadKeystoreFiles()
|
||||
if res.isErr:
|
||||
debugEcho res.error
|
||||
doAssert(res.isOk)
|
||||
|
||||
let acc1 = conf.getAccount(signer).tryGet()
|
||||
let unlock = conf.unlockAccount(signer, acc1.keystore["password"].getStr())
|
||||
if unlock.isErr:
|
||||
debugEcho unlock.error
|
||||
doAssert(unlock.isOk)
|
||||
|
||||
defaultGenesisBlockForNetwork(conf.net.networkId.toPublicNetwork()).commit(chain)
|
||||
state.mutateStateDB:
|
||||
db.setBalance(address, balance)
|
||||
|
@ -73,10 +88,10 @@ proc doTests {.async.} =
|
|||
|
||||
test "web3_sha3":
|
||||
expect ValueError:
|
||||
discard await client.web3_sha3(NimbusName)
|
||||
discard await client.web3_sha3(NimbusName.HexDataStr)
|
||||
|
||||
let data = "0x" & byteutils.toHex(NimbusName.toOpenArrayByte(0, NimbusName.len-1))
|
||||
let res = await client.web3_sha3(data)
|
||||
let res = await client.web3_sha3(data.hexDataStr)
|
||||
let rawdata = nimcrypto.fromHex(data[2 .. ^1])
|
||||
let hash = "0x" & $keccak_256.digest(rawdata)
|
||||
check hash == res
|
||||
|
@ -135,8 +150,9 @@ proc doTests {.async.} =
|
|||
|
||||
test "eth_accounts":
|
||||
let res = await client.eth_accounts()
|
||||
# we do not own any accounts, yet
|
||||
check res.len == 0
|
||||
check signer.ethAddressStr in res
|
||||
check ks2.ethAddressStr in res
|
||||
check ks3.ethAddressStr in res
|
||||
|
||||
test "eth_blockNumber":
|
||||
let res = await client.eth_blockNumber()
|
||||
|
@ -163,6 +179,41 @@ proc doTests {.async.} =
|
|||
let res = await client.eth_getBlockTransactionCountByHash(hash)
|
||||
check res.string == "0x0"
|
||||
|
||||
test "eth_getBlockTransactionCountByNumber":
|
||||
let res = await client.eth_getBlockTransactionCountByNumber("0x0")
|
||||
check res.string == "0x0"
|
||||
|
||||
test "eth_getUncleCountByBlockHash":
|
||||
let hash = chain.getBlockHash(0.toBlockNumber)
|
||||
let res = await client.eth_getUncleCountByBlockHash(hash)
|
||||
check res.string == "0x0"
|
||||
|
||||
test "eth_getUncleCountByBlockNumber":
|
||||
let res = await client.eth_getUncleCountByBlockNumber("0x0")
|
||||
check res.string == "0x0"
|
||||
|
||||
test "eth_getCode":
|
||||
let res = await client.eth_getCode(ethAddressStr("0xfff7ac99c8e4feb60c9750054bdc14ce1857f181"), "0x0")
|
||||
check res.string == "0x"
|
||||
|
||||
test "eth_sign":
|
||||
let msg = "hello world"
|
||||
let msgHex = hexDataStr(msg.toOpenArrayByte(0, msg.len-1))
|
||||
|
||||
expect ValueError:
|
||||
discard await client.eth_sign(ethAddressStr(ks2), msgHex)
|
||||
|
||||
let res = await client.eth_sign(ethAddressStr(signer), msgHex)
|
||||
let sig = Signature.fromHex(res.string).tryGet()
|
||||
|
||||
# now let us try to verify signature
|
||||
let msgData = "\x19Ethereum Signed Message:\n" & $msg.len & msg
|
||||
let msgDataHex = hexDataStr(msgData.toOpenArrayByte(0, msgData.len-1))
|
||||
let sha3Data = await client.web3_sha3(msgDataHex)
|
||||
let msgHash = hexToByteArray[32](sha3Data)
|
||||
let pubkey = recover(sig, SkMessage(msgHash)).tryGet()
|
||||
let recoveredAddr = pubkey.toCanonicalAddress()
|
||||
check recoveredAddr == signer # verified
|
||||
|
||||
#test "eth_call":
|
||||
# let
|
||||
|
|
Loading…
Reference in New Issue