setup block and state env for more complex eth rpc tests

This commit is contained in:
jangko 2020-07-28 23:48:45 +07:00
parent d089a61539
commit c9802edfce
No known key found for this signature in database
GPG Key ID: 31702AE10541E6B9
6 changed files with 110 additions and 38 deletions

View File

@ -132,7 +132,8 @@ proc addBlockNumberToHashLookup*(self: BaseChainDB; header: BlockHeader) =
self.db.put(blockNumberToHashKey(header.blockNumber).toOpenArray,
rlp.encode(header.hash))
proc persistTransactions*(self: BaseChainDB, blockNumber: BlockNumber, transactions: openArray[Transaction]) =
proc persistTransactions*(self: BaseChainDB, blockNumber:
BlockNumber, transactions: openArray[Transaction]): Hash256 =
var trie = initHexaryTrie(self.db)
for idx, tx in transactions:
let
@ -141,6 +142,7 @@ proc persistTransactions*(self: BaseChainDB, blockNumber: BlockNumber, transacti
txKey: TransactionKey = (blockNumber, idx)
trie.put(rlp.encode(idx), encodedTx)
self.db.put(transactionHashToBlockKey(txHash).toOpenArray, rlp.encode(txKey))
trie.rootHash
iterator getBlockTransactionData*(self: BaseChainDB, transactionRoot: Hash256): seq[byte] =
var transactionDb = initHexaryTrie(self.db, transactionRoot)
@ -245,10 +247,11 @@ proc headerExists*(self: BaseChainDB; blockHash: Hash256): bool =
## Returns True if the header with the given block hash is in our DB.
self.db.contains(genericHashKey(blockHash).toOpenArray)
proc persistReceipts*(self: BaseChainDB, receipts: openArray[Receipt]) =
proc persistReceipts*(self: BaseChainDB, receipts: openArray[Receipt]): Hash256 =
var trie = initHexaryTrie(self.db)
for idx, rec in receipts:
trie.put(rlp.encode(idx), rlp.encode(rec))
trie.rootHash
iterator getReceipts*(self: BaseChainDB; header: BlockHeader): Receipt =
var receiptDb = initHexaryTrie(self.db, header.receiptRoot)

View File

@ -152,8 +152,8 @@ method persistBlocks*(c: Chain, headers: openarray[BlockHeader], bodies: openarr
debug "Stored block header hash doesn't match declared hash"
return ValidationResult.Error
c.db.persistTransactions(headers[i].blockNumber, bodies[i].transactions)
c.db.persistReceipts(vmState.receipts)
discard c.db.persistTransactions(headers[i].blockNumber, bodies[i].transactions)
discard c.db.persistReceipts(vmState.receipts)
# update currentBlock *after* we persist it
# so the rpc return consistent result

View File

@ -37,7 +37,7 @@ type
gas*: Option[HexQuantityStr] # (optional) Integer of the gas provided for the transaction execution. eth_call consumes zero gas, but this parameter may be needed by some executions.
gasPrice*: Option[HexQuantityStr]# (optional) Integer of the gasPrice used for each paid gas.
value*: Option[HexQuantityStr] # (optional) Integer of the value sent with this transaction.
data*: Option[HexDataStr] # (optional) Hash of the method signature and encoded parameters. For details see Ethereum Contract ABI.
data*: Option[EthHashStr] # (optional) Hash of the method signature and encoded parameters. For details see Ethereum Contract ABI.
## A block object, or null when no block was found
## Note that this includes slightly different information from eth/common.BlockHeader

View File

@ -15,13 +15,13 @@ import hexstrings, eth/[common, rlp, keys], stew/byteutils, nimcrypto,
type
UnsignedTx* = object
nonce : AccountNonce
gasPrice: GasInt
gasLimit: GasInt
to {.rlpCustomSerialization.}: EthAddress
value : UInt256
payload : Blob
contractCreation {.rlpIgnore.}: bool
nonce* : AccountNonce
gasPrice*: GasInt
gasLimit*: GasInt
to* {.rlpCustomSerialization.}: EthAddress
value * : UInt256
payload* : Blob
contractCreation* {.rlpIgnore.}: bool
CallData* = object
source: EthAddress
@ -202,13 +202,17 @@ proc setupComputation(vmState: BaseVMState, call: CallData, fork: Fork) : Comput
result = newComputation(vmState, msg)
import json
proc doCall*(call: CallData, header: BlockHeader, chain: BaseChainDB): HexDataStr =
var
# we use current header stateRoot, unlike block validation
# which use previous block stateRoot
vmState = newBaseVMState(header.stateRoot, header, chain)
vmState = newBaseVMState(header.stateRoot, header, chain, {EnableTracing})
fork = toFork(chain.config, header.blockNumber)
comp = setupComputation(vmState, call, fork)
comp.execComputation()
result = hexDataStr(comp.returnData)
result = hexDataStr(comp.output)
# TODO: handle revert and error
# TODO: handle contract ABI

View File

@ -136,6 +136,8 @@ proc generateContractAddress(c: Computation, salt: Uint256): EthAddress =
else:
result = generateSafeAddress(c.msg.sender, salt, c.msg.data)
import stew/byteutils
proc newComputation*(vmState: BaseVMState, message: Message, salt= 0.u256): Computation =
new result
result.vmState = vmState

View File

@ -7,14 +7,14 @@
import
unittest, json, strformat, strutils, options, tables, os,
nimcrypto, stew/byteutils,
nimcrypto, stew/byteutils, times,
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/rpc/[common, p2p, hexstrings, rpc_types, rpc_utils],
../nimbus/[constants, vm_state, config, genesis, utils, transaction],
../nimbus/db/[accounts_cache, db_chain, storage_types],
../nimbus/p2p/chain,
./rpcclient/test_hexstrings, ./test_helpers
../nimbus/db/[accounts_cache, db_chain, storage_types, state_db],
../nimbus/p2p/[chain, executor], ../nimbus/utils/difficulty,
./rpcclient/test_hexstrings, ./test_helpers, ./macro_assembler
from eth/p2p/rlpx_protocols/whisper_protocol import SymKey
@ -30,27 +30,93 @@ template sourceDir: string = currentSourcePath.rsplit(DirSep, 1)[0]
const sigPath = &"{sourceDir}{DirSep}rpcclient{DirSep}ethcallsigs.nim"
createRpcSigs(RpcSocketClient, sigPath)
proc setupEnv(chain: BaseChainDB, signer, ks2: EthAddress, conf: NimbusConfiguration) =
var
parent = chain.getCanonicalHead()
ac = newAccountStateDB(chain.db, parent.stateRoot, chain.pruneTrie)
acc = conf.getAccount(signer).tryGet()
blockNumber = 1.toBlockNumber
parentHash = parent.blockHash
fork = chain.config.toFork(blockNumber)
const code = evmByteCode:
PUSH4 "0xDEADBEEF" # PUSH
PUSH1 "0x00" # MSTORE AT 0x00
MSTORE
PUSH1 "0x04" # RETURN LEN
PUSH1 "0x1C" # RETURN OFFSET at 28
RETURN
ac.setCode(ks2, code)
ac.addBalance(signer, 1_000_000.u256)
var vmState = newBaseVMState(ac.rootHash, BlockHeader(parentHash: parentHash), chain)
let
unsignedTx1 = UnsignedTx(
nonce : 0,
gasPrice: 1_100,
gasLimit: 70_000,
value : 1.u256,
contractCreation: false
)
unsignedTx2 = UnsignedTx(
nonce : 0,
gasPrice: 1_200,
gasLimit: 70_000,
value : 2.u256,
contractCreation: false
)
signedTx1 = signTransaction(unsignedTx1, chain, acc.privateKey)
signedTx2 = signTransaction(unsignedTx2, chain, acc.privateKey)
txs = [signedTx1, signedTx2]
txRoot = chain.persistTransactions(blockNumber, txs)
vmState.receipts = newSeq[Receipt](txs.len)
vmState.cumulativeGasUsed = 0
for txIndex, tx in txs:
let sender = tx.getSender()
discard processTransaction(tx, sender, vmState, fork)
vmState.receipts[txIndex] = makeReceipt(vmState, fork)
let
receiptRoot = chain.persistReceipts(vmState.receipts)
date = initDateTime(30, mMar, 2017, 00, 00, 00, 00, utc())
timeStamp = date.toTime
difficulty = calcDifficulty(chain.config, timeStamp, parent)
let header = BlockHeader(
parentHash : parentHash,
#ommersHash*: Hash256
#coinbase*: EthAddress
stateRoot : vmState.accountDb.rootHash,
txRoot : txRoot,
receiptRoot : receiptRoot,
bloom : createBloom(vmState.receipts),
difficulty : difficulty,
blockNumber : blockNumber,
gasLimit : vmState.cumulativeGasUsed + 1000,
gasUsed : vmState.cumulativeGasUsed,
timestamp : timeStamp
#extraData: Blob
#mixDigest: Hash256
#nonce: BlockNonce
)
discard chain.persistHeaderToDb(header)
proc doTests {.async.} =
# TODO: Include other transports such as Http
var ethNode = setupEthNode(eth)
let
emptyRlpHash = keccak256.digest(rlp.encode(""))
header = BlockHeader(stateRoot: emptyRlpHash)
var
ethNode = setupEthNode(eth)
chain = newBaseChainDB(newMemoryDb())
state = newBaseVMState(emptyRlpHash, header, chain)
ethNode.chain = newChain(chain)
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()
ethNode.chain = newChain(chain)
conf.keyStore = "tests" / "keystore"
let res = conf.loadKeystoreFiles()
if res.isErr:
@ -64,9 +130,8 @@ proc doTests {.async.} =
doAssert(unlock.isOk)
defaultGenesisBlockForNetwork(conf.net.networkId.toPublicNetwork()).commit(chain)
state.mutateStateDB:
db.setBalance(address, balance)
doAssert(canonicalHeadHashKey().toOpenArray in state.chainDb.db)
doAssert(canonicalHeadHashKey().toOpenArray in chain.db)
setupEnv(chain, signer, ks2, conf)
# Create Ethereum RPCs
let RPC_PORT = 8545
@ -144,9 +209,7 @@ proc doTests {.async.} =
test "eth_gasPrice":
let res = await client.eth_gasPrice()
# genesis block doesn't have any transaction
# to generate meaningful prices
check res.string == "0x0"
check res.string == "0x47E"
test "eth_accounts":
let res = await client.eth_accounts()
@ -156,7 +219,7 @@ proc doTests {.async.} =
test "eth_blockNumber":
let res = await client.eth_blockNumber()
check res.string == "0x0"
check res.string == "0x1"
test "eth_getBalance":
let a = await client.eth_getBalance(ethAddressStr("0xfff33a3bd36abdbd412707b8e310d6011454a7ae"), "0x0")
@ -240,11 +303,11 @@ proc doTests {.async.} =
to: ethAddressStr(ks2).some,
gas: encodeQuantity(100000'u).some,
gasPrice: none(HexQuantityStr),
value: encodeQuantity(100'u).some,
data: HexDataStr("0x").some,
value: encodeQuantity(100'u).some
)
let res = await client.eth_call(ec, "latest")
check hexToByteArray[4](res.string) == hexToByteArray[4]("deadbeef")
#test "eth_estimateGas":
# let