From c9802edfce2eea619dbe9f9b6d1360b49f32737c Mon Sep 17 00:00:00 2001 From: jangko Date: Tue, 28 Jul 2020 23:48:45 +0700 Subject: [PATCH] setup block and state env for more complex eth rpc tests --- nimbus/db/db_chain.nim | 7 ++- nimbus/p2p/chain.nim | 4 +- nimbus/rpc/rpc_types.nim | 2 +- nimbus/rpc/rpc_utils.nim | 22 ++++---- nimbus/vm/computation.nim | 2 + tests/test_rpc.nim | 111 +++++++++++++++++++++++++++++--------- 6 files changed, 110 insertions(+), 38 deletions(-) diff --git a/nimbus/db/db_chain.nim b/nimbus/db/db_chain.nim index d72eca457..bc55cc0b6 100644 --- a/nimbus/db/db_chain.nim +++ b/nimbus/db/db_chain.nim @@ -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) diff --git a/nimbus/p2p/chain.nim b/nimbus/p2p/chain.nim index 3234d06b4..7aebc9c49 100644 --- a/nimbus/p2p/chain.nim +++ b/nimbus/p2p/chain.nim @@ -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 diff --git a/nimbus/rpc/rpc_types.nim b/nimbus/rpc/rpc_types.nim index db572405e..366ba012c 100644 --- a/nimbus/rpc/rpc_types.nim +++ b/nimbus/rpc/rpc_types.nim @@ -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 diff --git a/nimbus/rpc/rpc_utils.nim b/nimbus/rpc/rpc_utils.nim index ad9b35901..078641408 100644 --- a/nimbus/rpc/rpc_utils.nim +++ b/nimbus/rpc/rpc_utils.nim @@ -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 diff --git a/nimbus/vm/computation.nim b/nimbus/vm/computation.nim index dce1ca4e5..f81707008 100644 --- a/nimbus/vm/computation.nim +++ b/nimbus/vm/computation.nim @@ -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 diff --git a/tests/test_rpc.nim b/tests/test_rpc.nim index 9e5202ce4..1202a9316 100644 --- a/tests/test_rpc.nim +++ b/tests/test_rpc.nim @@ -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