setup block and state env for more complex eth rpc tests
This commit is contained in:
parent
d089a61539
commit
c9802edfce
|
@ -132,7 +132,8 @@ proc addBlockNumberToHashLookup*(self: BaseChainDB; header: BlockHeader) =
|
||||||
self.db.put(blockNumberToHashKey(header.blockNumber).toOpenArray,
|
self.db.put(blockNumberToHashKey(header.blockNumber).toOpenArray,
|
||||||
rlp.encode(header.hash))
|
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)
|
var trie = initHexaryTrie(self.db)
|
||||||
for idx, tx in transactions:
|
for idx, tx in transactions:
|
||||||
let
|
let
|
||||||
|
@ -141,6 +142,7 @@ proc persistTransactions*(self: BaseChainDB, blockNumber: BlockNumber, transacti
|
||||||
txKey: TransactionKey = (blockNumber, idx)
|
txKey: TransactionKey = (blockNumber, idx)
|
||||||
trie.put(rlp.encode(idx), encodedTx)
|
trie.put(rlp.encode(idx), encodedTx)
|
||||||
self.db.put(transactionHashToBlockKey(txHash).toOpenArray, rlp.encode(txKey))
|
self.db.put(transactionHashToBlockKey(txHash).toOpenArray, rlp.encode(txKey))
|
||||||
|
trie.rootHash
|
||||||
|
|
||||||
iterator getBlockTransactionData*(self: BaseChainDB, transactionRoot: Hash256): seq[byte] =
|
iterator getBlockTransactionData*(self: BaseChainDB, transactionRoot: Hash256): seq[byte] =
|
||||||
var transactionDb = initHexaryTrie(self.db, transactionRoot)
|
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.
|
## Returns True if the header with the given block hash is in our DB.
|
||||||
self.db.contains(genericHashKey(blockHash).toOpenArray)
|
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)
|
var trie = initHexaryTrie(self.db)
|
||||||
for idx, rec in receipts:
|
for idx, rec in receipts:
|
||||||
trie.put(rlp.encode(idx), rlp.encode(rec))
|
trie.put(rlp.encode(idx), rlp.encode(rec))
|
||||||
|
trie.rootHash
|
||||||
|
|
||||||
iterator getReceipts*(self: BaseChainDB; header: BlockHeader): Receipt =
|
iterator getReceipts*(self: BaseChainDB; header: BlockHeader): Receipt =
|
||||||
var receiptDb = initHexaryTrie(self.db, header.receiptRoot)
|
var receiptDb = initHexaryTrie(self.db, header.receiptRoot)
|
||||||
|
|
|
@ -152,8 +152,8 @@ method persistBlocks*(c: Chain, headers: openarray[BlockHeader], bodies: openarr
|
||||||
debug "Stored block header hash doesn't match declared hash"
|
debug "Stored block header hash doesn't match declared hash"
|
||||||
return ValidationResult.Error
|
return ValidationResult.Error
|
||||||
|
|
||||||
c.db.persistTransactions(headers[i].blockNumber, bodies[i].transactions)
|
discard c.db.persistTransactions(headers[i].blockNumber, bodies[i].transactions)
|
||||||
c.db.persistReceipts(vmState.receipts)
|
discard c.db.persistReceipts(vmState.receipts)
|
||||||
|
|
||||||
# update currentBlock *after* we persist it
|
# update currentBlock *after* we persist it
|
||||||
# so the rpc return consistent result
|
# so the rpc return consistent result
|
||||||
|
|
|
@ -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.
|
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.
|
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.
|
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
|
## A block object, or null when no block was found
|
||||||
## Note that this includes slightly different information from eth/common.BlockHeader
|
## Note that this includes slightly different information from eth/common.BlockHeader
|
||||||
|
|
|
@ -15,13 +15,13 @@ import hexstrings, eth/[common, rlp, keys], stew/byteutils, nimcrypto,
|
||||||
|
|
||||||
type
|
type
|
||||||
UnsignedTx* = object
|
UnsignedTx* = object
|
||||||
nonce : AccountNonce
|
nonce* : AccountNonce
|
||||||
gasPrice: GasInt
|
gasPrice*: GasInt
|
||||||
gasLimit: GasInt
|
gasLimit*: GasInt
|
||||||
to {.rlpCustomSerialization.}: EthAddress
|
to* {.rlpCustomSerialization.}: EthAddress
|
||||||
value : UInt256
|
value * : UInt256
|
||||||
payload : Blob
|
payload* : Blob
|
||||||
contractCreation {.rlpIgnore.}: bool
|
contractCreation* {.rlpIgnore.}: bool
|
||||||
|
|
||||||
CallData* = object
|
CallData* = object
|
||||||
source: EthAddress
|
source: EthAddress
|
||||||
|
@ -202,13 +202,17 @@ proc setupComputation(vmState: BaseVMState, call: CallData, fork: Fork) : Comput
|
||||||
|
|
||||||
result = newComputation(vmState, msg)
|
result = newComputation(vmState, msg)
|
||||||
|
|
||||||
|
import json
|
||||||
|
|
||||||
proc doCall*(call: CallData, header: BlockHeader, chain: BaseChainDB): HexDataStr =
|
proc doCall*(call: CallData, header: BlockHeader, chain: BaseChainDB): HexDataStr =
|
||||||
var
|
var
|
||||||
# we use current header stateRoot, unlike block validation
|
# we use current header stateRoot, unlike block validation
|
||||||
# which use previous block stateRoot
|
# which use previous block stateRoot
|
||||||
vmState = newBaseVMState(header.stateRoot, header, chain)
|
vmState = newBaseVMState(header.stateRoot, header, chain, {EnableTracing})
|
||||||
fork = toFork(chain.config, header.blockNumber)
|
fork = toFork(chain.config, header.blockNumber)
|
||||||
comp = setupComputation(vmState, call, fork)
|
comp = setupComputation(vmState, call, fork)
|
||||||
|
|
||||||
comp.execComputation()
|
comp.execComputation()
|
||||||
result = hexDataStr(comp.returnData)
|
result = hexDataStr(comp.output)
|
||||||
|
# TODO: handle revert and error
|
||||||
|
# TODO: handle contract ABI
|
||||||
|
|
|
@ -136,6 +136,8 @@ proc generateContractAddress(c: Computation, salt: Uint256): EthAddress =
|
||||||
else:
|
else:
|
||||||
result = generateSafeAddress(c.msg.sender, salt, c.msg.data)
|
result = generateSafeAddress(c.msg.sender, salt, c.msg.data)
|
||||||
|
|
||||||
|
import stew/byteutils
|
||||||
|
|
||||||
proc newComputation*(vmState: BaseVMState, message: Message, salt= 0.u256): Computation =
|
proc newComputation*(vmState: BaseVMState, message: Message, salt= 0.u256): Computation =
|
||||||
new result
|
new result
|
||||||
result.vmState = vmState
|
result.vmState = vmState
|
||||||
|
|
|
@ -7,14 +7,14 @@
|
||||||
|
|
||||||
import
|
import
|
||||||
unittest, json, strformat, strutils, options, tables, os,
|
unittest, json, strformat, strutils, options, tables, os,
|
||||||
nimcrypto, stew/byteutils,
|
nimcrypto, stew/byteutils, times,
|
||||||
json_rpc/[rpcserver, rpcclient], eth/common as eth_common,
|
json_rpc/[rpcserver, rpcclient], eth/common as eth_common,
|
||||||
eth/[rlp, keys], eth/trie/db, eth/p2p/rlpx_protocols/eth_protocol,
|
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/[constants, vm_state, config, genesis, utils, transaction],
|
||||||
../nimbus/db/[accounts_cache, db_chain, storage_types],
|
../nimbus/db/[accounts_cache, db_chain, storage_types, state_db],
|
||||||
../nimbus/p2p/chain,
|
../nimbus/p2p/[chain, executor], ../nimbus/utils/difficulty,
|
||||||
./rpcclient/test_hexstrings, ./test_helpers
|
./rpcclient/test_hexstrings, ./test_helpers, ./macro_assembler
|
||||||
|
|
||||||
from eth/p2p/rlpx_protocols/whisper_protocol import SymKey
|
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"
|
const sigPath = &"{sourceDir}{DirSep}rpcclient{DirSep}ethcallsigs.nim"
|
||||||
createRpcSigs(RpcSocketClient, sigPath)
|
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.} =
|
proc doTests {.async.} =
|
||||||
# TODO: Include other transports such as Http
|
# TODO: Include other transports such as Http
|
||||||
var ethNode = setupEthNode(eth)
|
|
||||||
let
|
|
||||||
emptyRlpHash = keccak256.digest(rlp.encode(""))
|
|
||||||
header = BlockHeader(stateRoot: emptyRlpHash)
|
|
||||||
var
|
var
|
||||||
|
ethNode = setupEthNode(eth)
|
||||||
chain = newBaseChainDB(newMemoryDb())
|
chain = newBaseChainDB(newMemoryDb())
|
||||||
state = newBaseVMState(emptyRlpHash, header, chain)
|
|
||||||
ethNode.chain = newChain(chain)
|
|
||||||
|
|
||||||
let
|
let
|
||||||
balance = 100.u256
|
|
||||||
address: EthAddress = hexToByteArray[20]("0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6")
|
|
||||||
|
|
||||||
signer: EthAddress = hexToByteArray[20]("0x0e69cde81b1aa07a45c32c6cd85d67229d36bb1b")
|
signer: EthAddress = hexToByteArray[20]("0x0e69cde81b1aa07a45c32c6cd85d67229d36bb1b")
|
||||||
ks2: EthAddress = hexToByteArray[20]("0xa3b2222afa5c987da6ef773fde8d01b9f23d481f")
|
ks2: EthAddress = hexToByteArray[20]("0xa3b2222afa5c987da6ef773fde8d01b9f23d481f")
|
||||||
ks3: EthAddress = hexToByteArray[20]("0x597176e9a64aad0845d83afdaf698fbeff77703b")
|
ks3: EthAddress = hexToByteArray[20]("0x597176e9a64aad0845d83afdaf698fbeff77703b")
|
||||||
|
|
||||||
conf = getConfiguration()
|
conf = getConfiguration()
|
||||||
|
|
||||||
|
ethNode.chain = newChain(chain)
|
||||||
conf.keyStore = "tests" / "keystore"
|
conf.keyStore = "tests" / "keystore"
|
||||||
let res = conf.loadKeystoreFiles()
|
let res = conf.loadKeystoreFiles()
|
||||||
if res.isErr:
|
if res.isErr:
|
||||||
|
@ -64,9 +130,8 @@ proc doTests {.async.} =
|
||||||
doAssert(unlock.isOk)
|
doAssert(unlock.isOk)
|
||||||
|
|
||||||
defaultGenesisBlockForNetwork(conf.net.networkId.toPublicNetwork()).commit(chain)
|
defaultGenesisBlockForNetwork(conf.net.networkId.toPublicNetwork()).commit(chain)
|
||||||
state.mutateStateDB:
|
doAssert(canonicalHeadHashKey().toOpenArray in chain.db)
|
||||||
db.setBalance(address, balance)
|
setupEnv(chain, signer, ks2, conf)
|
||||||
doAssert(canonicalHeadHashKey().toOpenArray in state.chainDb.db)
|
|
||||||
|
|
||||||
# Create Ethereum RPCs
|
# Create Ethereum RPCs
|
||||||
let RPC_PORT = 8545
|
let RPC_PORT = 8545
|
||||||
|
@ -144,9 +209,7 @@ proc doTests {.async.} =
|
||||||
|
|
||||||
test "eth_gasPrice":
|
test "eth_gasPrice":
|
||||||
let res = await client.eth_gasPrice()
|
let res = await client.eth_gasPrice()
|
||||||
# genesis block doesn't have any transaction
|
check res.string == "0x47E"
|
||||||
# to generate meaningful prices
|
|
||||||
check res.string == "0x0"
|
|
||||||
|
|
||||||
test "eth_accounts":
|
test "eth_accounts":
|
||||||
let res = await client.eth_accounts()
|
let res = await client.eth_accounts()
|
||||||
|
@ -156,7 +219,7 @@ proc doTests {.async.} =
|
||||||
|
|
||||||
test "eth_blockNumber":
|
test "eth_blockNumber":
|
||||||
let res = await client.eth_blockNumber()
|
let res = await client.eth_blockNumber()
|
||||||
check res.string == "0x0"
|
check res.string == "0x1"
|
||||||
|
|
||||||
test "eth_getBalance":
|
test "eth_getBalance":
|
||||||
let a = await client.eth_getBalance(ethAddressStr("0xfff33a3bd36abdbd412707b8e310d6011454a7ae"), "0x0")
|
let a = await client.eth_getBalance(ethAddressStr("0xfff33a3bd36abdbd412707b8e310d6011454a7ae"), "0x0")
|
||||||
|
@ -240,11 +303,11 @@ proc doTests {.async.} =
|
||||||
to: ethAddressStr(ks2).some,
|
to: ethAddressStr(ks2).some,
|
||||||
gas: encodeQuantity(100000'u).some,
|
gas: encodeQuantity(100000'u).some,
|
||||||
gasPrice: none(HexQuantityStr),
|
gasPrice: none(HexQuantityStr),
|
||||||
value: encodeQuantity(100'u).some,
|
value: encodeQuantity(100'u).some
|
||||||
data: HexDataStr("0x").some,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
let res = await client.eth_call(ec, "latest")
|
let res = await client.eth_call(ec, "latest")
|
||||||
|
check hexToByteArray[4](res.string) == hexToByteArray[4]("deadbeef")
|
||||||
|
|
||||||
#test "eth_estimateGas":
|
#test "eth_estimateGas":
|
||||||
# let
|
# let
|
||||||
|
|
Loading…
Reference in New Issue