Feature/execution api spec (#69)
* Fix problem with contract out of assets * Add nully value json test * Fix bug with passing null to a few functions * Typesafe U256 parsing * Update readme for the web3 to include runner info * Cleanup commits Strip out unused JSON Update comment Remove echo Added DynamicBytes test More correct naming Remove one extra double check * Add specific object tests * Ensure we cover different status types * Add header comments * Cleanup * Add more tests * Cleanup * Revert docs * Nimpretty file * Fix issue in base stew * Add tests * Sorting * Work on tests * Move items to the correct location * Add TODO * Add all test executables * Work on generating tests * Push latest execution API changes * Work on eth_call test * Fix source type * Latest * Add many more tests * Add more tests * Revert "from" to "source" * Try fix types * Split to types which fail and those which dont * Cleanup * Remove whispher types again * re-remove whisper * Add more eth_api signature * Readding executions-apis submodule * Disable test_execution_api * Nitpick * add handlers * Add last line to handler * Some handler pass * Finally all tests pass * Add the test to all_tests * Consisten style * Turn on submodule downloading in CI * Temporary workaround of get eth_getBlockReceipts for nim 2.0 --------- Co-authored-by: jangko <jangko128@gmail.com>
This commit is contained in:
parent
6c27c9744e
commit
85b3567d94
|
@ -46,6 +46,8 @@ jobs:
|
|||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Install build dependencies (Linux i386)
|
||||
if: runner.os == 'Linux' && matrix.target.cpu == 'i386'
|
||||
|
|
|
@ -16,4 +16,18 @@ node_modules
|
|||
nohup.out
|
||||
hardhat.config.js
|
||||
package-lock.json
|
||||
|
||||
# Individual test executables
|
||||
all_tests
|
||||
test_contract_dsl
|
||||
test_contracts
|
||||
test_deposit_contract
|
||||
test_execution_api
|
||||
test_execution_debug_apis
|
||||
test_execution_types
|
||||
test_json_marshalling
|
||||
test_logs
|
||||
test_null_conversion
|
||||
test_primitives
|
||||
test_signed_tx
|
||||
test_string_decoder
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
[submodule "tests/execution-apis"]
|
||||
path = tests/execution-apis
|
||||
url = https://github.com/ethereum/execution-apis
|
|
@ -19,4 +19,5 @@ import
|
|||
test_signed_tx,
|
||||
test_execution_types,
|
||||
test_string_decoder,
|
||||
test_contract_dsl
|
||||
test_contract_dsl,
|
||||
test_execution_api
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
Subproject commit cea7eeb642052f4c2e03449dc48296def4aafc24
|
|
@ -0,0 +1,159 @@
|
|||
# json-rpc
|
||||
# Copyright (c) 2024 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
|
||||
stint,
|
||||
eth/common,
|
||||
json_rpc/rpcserver,
|
||||
../../web3/conversions,
|
||||
../../web3/eth_api_types,
|
||||
../../web3/primitives as w3
|
||||
|
||||
type
|
||||
Hash256 = w3.Hash256
|
||||
|
||||
proc installHandlers*(server: RpcServer) =
|
||||
server.rpc("eth_syncing") do(x: JsonString, ) -> bool:
|
||||
return false
|
||||
|
||||
server.rpc("eth_sendRawTransaction") do(x: JsonString, data: seq[byte]) -> TxHash:
|
||||
let tx = rlp.decode(data, Transaction)
|
||||
let h = rlpHash(tx)
|
||||
return TxHash(h.data)
|
||||
|
||||
server.rpc("eth_getTransactionReceipt") do(x: JsonString, data: TxHash) -> ReceiptObject:
|
||||
var r: ReceiptObject
|
||||
if x != "-1".JsonString:
|
||||
r = JrpcConv.decode(x.string, ReceiptObject)
|
||||
return r
|
||||
|
||||
server.rpc("eth_getTransactionByHash") do(x: JsonString, data: TxHash) -> TransactionObject:
|
||||
var tx: TransactionObject
|
||||
if x != "-1".JsonString:
|
||||
tx = JrpcConv.decode(x.string, TransactionObject)
|
||||
return tx
|
||||
|
||||
server.rpc("eth_getTransactionByBlockNumberAndIndex") do(x: JsonString, blockId: RtBlockIdentifier, quantity: Quantity) -> TransactionObject:
|
||||
var tx: TransactionObject
|
||||
if x != "-1".JsonString:
|
||||
tx = JrpcConv.decode(x.string, TransactionObject)
|
||||
return tx
|
||||
|
||||
server.rpc("eth_getTransactionByBlockHashAndIndex") do(x: JsonString, data: Hash256, quantity: Quantity) -> TransactionObject:
|
||||
var tx: TransactionObject
|
||||
if x != "-1".JsonString:
|
||||
tx = JrpcConv.decode(x.string, TransactionObject)
|
||||
return tx
|
||||
|
||||
server.rpc("eth_getTransactionCount") do(x: JsonString, data: Address, blockId: RtBlockIdentifier) -> Quantity:
|
||||
if x != "-1".JsonString:
|
||||
result = JrpcConv.decode(x.string, Quantity)
|
||||
|
||||
server.rpc("eth_getStorageAt") do(x: JsonString, data: Address, slot: UInt256, blockId: RtBlockIdentifier) -> FixedBytes[32]:
|
||||
if x != "-1".JsonString:
|
||||
result = JrpcConv.decode(x.string, FixedBytes[32])
|
||||
|
||||
server.rpc("eth_getProof") do(x: JsonString, address: Address, slots: seq[UInt256], blockId: RtBlockIdentifier) -> ProofResponse:
|
||||
var p: ProofResponse
|
||||
if x != "-1".JsonString:
|
||||
p = JrpcConv.decode(x.string, ProofResponse)
|
||||
return p
|
||||
|
||||
server.rpc("eth_getCode") do(x: JsonString, data: Address, blockId: RtBlockIdentifier) -> seq[byte]:
|
||||
if x != "-1".JsonString:
|
||||
result = JrpcConv.decode(x.string, seq[byte])
|
||||
|
||||
server.rpc("eth_getBlockTransactionCountByNumber") do(x: JsonString, blockId: RtBlockIdentifier) -> Quantity:
|
||||
if x != "-1".JsonString:
|
||||
result = JrpcConv.decode(x.string, Quantity)
|
||||
|
||||
server.rpc("eth_getBlockTransactionCountByHash") do(x: JsonString, data: BlockHash) -> Quantity:
|
||||
if x != "-1".JsonString:
|
||||
result = JrpcConv.decode(x.string, Quantity)
|
||||
|
||||
when NimMajor >= 2:
|
||||
server.rpc("eth_getBlockReceipts") do(x: JsonString, blockId: RtBlockIdentifier) -> JsonString:
|
||||
# TODO: cannot prove obj is not nil
|
||||
return x
|
||||
else:
|
||||
server.rpc("eth_getBlockReceipts") do(x: JsonString, blockId: RtBlockIdentifier) -> Option[seq[ReceiptObject]]:
|
||||
if x == "null".JsonString:
|
||||
var n: Option[seq[ReceiptObject]]
|
||||
return n
|
||||
if x != "-1".JsonString:
|
||||
let r = JrpcConv.decode(x.string, seq[ReceiptObject])
|
||||
return some(r)
|
||||
|
||||
server.rpc("eth_getBlockByNumber") do(x: JsonString, blockId: RtBlockIdentifier, fullTransactions: bool) -> BlockObject:
|
||||
var blk: BlockObject
|
||||
if x != "-1".JsonString:
|
||||
blk = JrpcConv.decode(x.string, BlockObject)
|
||||
return blk
|
||||
|
||||
server.rpc("eth_getBlockByHash") do(x: JsonString, data: BlockHash, fullTransactions: bool) -> BlockObject:
|
||||
var blk: BlockObject
|
||||
if x != "-1".JsonString:
|
||||
blk = JrpcConv.decode(x.string, BlockObject)
|
||||
return blk
|
||||
|
||||
server.rpc("eth_getBalance") do(x: JsonString, data: Address, blockId: RtBlockIdentifier) -> UInt256:
|
||||
if x != "-1".JsonString:
|
||||
result = JrpcConv.decode(x.string, UInt256)
|
||||
|
||||
server.rpc("eth_feeHistory") do(x: JsonString, blockCount: Quantity, newestBlock: RtBlockIdentifier, rewardPercentiles: Option[seq[float64]]) -> FeeHistoryResult:
|
||||
var fh: FeeHistoryResult
|
||||
if x != "-1".JsonString:
|
||||
fh = JrpcConv.decode(x.string, FeeHistoryResult)
|
||||
return fh
|
||||
|
||||
server.rpc("eth_estimateGas") do(x: JsonString, call: EthCall) -> Quantity:
|
||||
if x != "-1".JsonString:
|
||||
result = JrpcConv.decode(x.string, Quantity)
|
||||
|
||||
server.rpc("eth_createAccessList") do(x: JsonString, call: EthCall, blockId: RtBlockIdentifier) -> AccessListResult:
|
||||
var z: AccessListResult
|
||||
if x != "-1".JsonString:
|
||||
z = JrpcConv.decode(x.string, AccessListResult)
|
||||
return z
|
||||
|
||||
server.rpc("eth_chainId") do(x: JsonString, ) -> Quantity:
|
||||
if x != "-1".JsonString:
|
||||
result = JrpcConv.decode(x.string, Quantity)
|
||||
|
||||
server.rpc("eth_call") do(x: JsonString, call: EthCall, blockId: RtBlockIdentifier) -> seq[byte]:
|
||||
if x != "-1".JsonString:
|
||||
result = JrpcConv.decode(x.string, seq[byte])
|
||||
|
||||
server.rpc("eth_blockNumber") do(x: JsonString) -> Quantity:
|
||||
if x != "-1".JsonString:
|
||||
result = JrpcConv.decode(x.string, Quantity)
|
||||
|
||||
server.rpc("debug_getRawTransaction") do(x: JsonString, data: TxHash) -> RlpEncodedBytes:
|
||||
var res: seq[byte]
|
||||
if x != "-1".JsonString:
|
||||
res = JrpcConv.decode(x.string, seq[byte])
|
||||
return res.RlpEncodedBytes
|
||||
|
||||
server.rpc("debug_getRawReceipts") do(x: JsonString, blockId: RtBlockIdentifier) -> seq[RlpEncodedBytes]:
|
||||
var res: seq[RlpEncodedBytes]
|
||||
if x != "-1".JsonString:
|
||||
res = JrpcConv.decode(x.string, seq[RlpEncodedBytes])
|
||||
return res
|
||||
|
||||
server.rpc("debug_getRawHeader") do(x: JsonString, blockId: RtBlockIdentifier) -> RlpEncodedBytes:
|
||||
var res: seq[byte]
|
||||
if x != "-1".JsonString:
|
||||
res = JrpcConv.decode(x.string, seq[byte])
|
||||
return res.RlpEncodedBytes
|
||||
|
||||
server.rpc("debug_getRawBlock") do(x: JsonString, blockId: RtBlockIdentifier) -> RlpEncodedBytes:
|
||||
var res: seq[byte]
|
||||
if x != "-1".JsonString:
|
||||
res = JrpcConv.decode(x.string, seq[byte])
|
||||
return res.RlpEncodedBytes
|
|
@ -0,0 +1,110 @@
|
|||
import
|
||||
std/[os, strutils],
|
||||
pkg/unittest2,
|
||||
chronos,
|
||||
json_rpc/[rpcclient, rpcserver],
|
||||
json_rpc/private/jrpc_sys,
|
||||
../web3/conversions,
|
||||
./helpers/handlers
|
||||
|
||||
type
|
||||
TestData = tuple
|
||||
file: string
|
||||
input: RequestTx
|
||||
output: ResponseRx
|
||||
|
||||
const
|
||||
inputPath = "tests/execution-apis/tests"
|
||||
|
||||
func strip(line: string): string =
|
||||
return line[3..^1]
|
||||
|
||||
func toTx(req: RequestRx): RequestTx =
|
||||
RequestTx(
|
||||
id: Opt.some(req.id),
|
||||
`method`: req.`method`.get(),
|
||||
params: req.params.toTx,
|
||||
)
|
||||
|
||||
proc extractTest(fileName: string): TestData =
|
||||
let
|
||||
lines = readFile(fileName).split("\n")
|
||||
input = lines[0].strip()
|
||||
output = lines[1].strip()
|
||||
|
||||
return (
|
||||
file: fileName,
|
||||
input: JrpcSys.decode(input, RequestRx).toTx,
|
||||
output: JrpcSys.decode(output, ResponseRx),
|
||||
)
|
||||
|
||||
proc extractTests(): seq[TestData] =
|
||||
for fileName in walkDirRec(inputPath):
|
||||
if fileName.endsWith(".io"):
|
||||
result.add(fileName.extractTest())
|
||||
|
||||
proc callWithParams(client: RpcClient, data: TestData): Future[bool] {.async.} =
|
||||
let res = data.output
|
||||
|
||||
try:
|
||||
var params = data.input.params
|
||||
if data.output.result.string.len > 0:
|
||||
params.positional.insert(data.output.result, 0)
|
||||
else:
|
||||
params.positional.insert("-1".JsonString, 0)
|
||||
|
||||
let resJson = await client.call(data.input.`method`, params)
|
||||
|
||||
if res.result.string.len > 0:
|
||||
let wantVal = JrpcConv.decode(res.result.string, JsonValueRef[string])
|
||||
let getVal = JrpcConv.decode(resJson.string, JsonValueRef[string])
|
||||
|
||||
if wantVal != getVal:
|
||||
debugEcho data.file
|
||||
debugEcho "EXPECT: ", res.result
|
||||
debugEcho "GET: ", resJson.string
|
||||
return false
|
||||
|
||||
return true
|
||||
except SerializationError as exc:
|
||||
debugEcho data.file
|
||||
debugEcho exc.formatMsg("xxx")
|
||||
return false
|
||||
except CatchableError as exc:
|
||||
if res.error.isSome:
|
||||
return true
|
||||
debugEcho data.file
|
||||
debugEcho exc.msg
|
||||
return false
|
||||
|
||||
const allowedToFail = [
|
||||
"fee-history.io" # float roundtrip not match
|
||||
]
|
||||
|
||||
suite "Ethereum execution api":
|
||||
let testCases = extractTests()
|
||||
if testCases.len < 1:
|
||||
raise newException(ValueError, "execution_api tests not found, did you clone?")
|
||||
|
||||
var srv = newRpcHttpServer(["127.0.0.1:0"])
|
||||
srv.installHandlers()
|
||||
srv.start()
|
||||
|
||||
for idx, item in testCases:
|
||||
let input = item.input
|
||||
let methodName = input.`method`
|
||||
|
||||
test methodName:
|
||||
let (_, fileName, ext) = splitFile(item.file)
|
||||
let client = newRpcHttpClient()
|
||||
waitFor client.connect("http://" & $srv.localAddress()[0])
|
||||
let response = waitFor client.callWithParams(item)
|
||||
let source = fileName & ext
|
||||
if source in allowedToFail:
|
||||
check true
|
||||
else:
|
||||
check response
|
||||
waitFor client.close()
|
||||
|
||||
waitFor srv.stop()
|
||||
waitFor srv.closeWait()
|
|
@ -33,7 +33,7 @@ contract(LoggerContract):
|
|||
proc MyEvent(sender: Address, number: UInt256) {.event.}
|
||||
proc invoke(value: UInt256)
|
||||
|
||||
const LoggerContractCode = "6080604052348015600f57600080fd5b5060bc8061001e6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c80632b30d2b814602d575b600080fd5b604760048036036020811015604157600080fd5b50356049565b005b604080513381526020810183905281517fdf50c7bb3b25f812aedef81bc334454040e7b27e27de95a79451d663013b7e17929181900390910190a15056fea265627a7a723058202ed7f5086297d2a49fbe359f4e489a007b69eb5077f5c76328bffdb63f164b4b64736f6c63430005090032"
|
||||
const LoggerContractCode = "6080604052348015600f57600080fd5b5060fb8061001e6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c80632b30d2b814602d575b600080fd5b605660048036036020811015604157600080fd5b81019080803590602001909291905050506058565b005b7fdf50c7bb3b25f812aedef81bc334454040e7b27e27de95a79451d663013b7e173382604051808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018281526020019250505060405180910390a15056fea265627a7a72315820cb9980a67d78ee2e84fedf080db8463ce4a944fccf8b5512448163aaff0aea8964736f6c63430005110032"
|
||||
|
||||
var contractAddress = Address.fromHex("0xEA255DeA28c84F698Fa195f87fC83D1d4125ef9C")
|
||||
|
||||
|
@ -48,7 +48,6 @@ suite "Logs":
|
|||
# let q = await web3.provider.eth_blockNumber()
|
||||
echo "block: ", uint64(await web3.provider.eth_blockNumber())
|
||||
|
||||
|
||||
block: # LoggerContract
|
||||
let receipt = await web3.deployContract(LoggerContractCode)
|
||||
contractAddress = receipt.contractAddress.get
|
||||
|
|
|
@ -42,6 +42,7 @@ ProofResponse.useDefaultSerializationIn JrpcConv
|
|||
FilterOptions.useDefaultSerializationIn JrpcConv
|
||||
EthSend.useDefaultSerializationIn JrpcConv
|
||||
EthCall.useDefaultSerializationIn JrpcConv
|
||||
FeeHistoryResult.useDefaultSerializationIn JrpcConv
|
||||
|
||||
derefType(BlockHeader).useDefaultSerializationIn JrpcConv
|
||||
derefType(BlockObject).useDefaultSerializationIn JrpcConv
|
||||
|
@ -290,7 +291,7 @@ proc readValue*(r: var JsonReader[JrpcConv], val: var RtBlockIdentifier)
|
|||
val = RtBlockIdentifier(kind: bidNumber, number: fromHex[uint64](hexStr))
|
||||
else:
|
||||
val = RtBlockIdentifier(kind: bidAlias, alias: hexStr)
|
||||
|
||||
|
||||
proc writeValue*(w: var JsonWriter[JrpcConv], v: RtBlockIdentifier)
|
||||
{.gcsafe, raises: [IOError].} =
|
||||
case v.kind
|
||||
|
|
|
@ -35,10 +35,11 @@ createRpcSigsFromNim(RpcClient):
|
|||
proc eth_accounts(): seq[Address]
|
||||
proc eth_blockNumber(): Quantity
|
||||
proc eth_getBalance(data: Address, blockId: BlockIdentifier): UInt256
|
||||
proc eth_getStorageAt(data: Address, slot: UInt256, blockId: BlockIdentifier): UInt256
|
||||
proc eth_getStorageAt(data: Address, slot: UInt256, blockId: BlockIdentifier): FixedBytes[32]
|
||||
proc eth_getTransactionCount(data: Address, blockId: BlockIdentifier): Quantity
|
||||
proc eth_getBlockTransactionCountByHash(data: BlockHash): Quantity
|
||||
proc eth_getBlockTransactionCountByNumber(blockId: BlockIdentifier): Quantity
|
||||
proc eth_getBlockReceipts(blockId: BlockIdentifier): Option[seq[ReceiptObject]]
|
||||
proc eth_getUncleCountByBlockHash(data: BlockHash): Quantity
|
||||
proc eth_getUncleCountByBlockNumber(blockId: BlockIdentifier): Quantity
|
||||
proc eth_getCode(data: Address, blockId: BlockIdentifier): seq[byte]
|
||||
|
@ -47,7 +48,7 @@ createRpcSigsFromNim(RpcClient):
|
|||
proc eth_sendTransaction(obj: EthSend): TxHash
|
||||
proc eth_sendRawTransaction(data: seq[byte]): TxHash
|
||||
proc eth_call(call: EthCall, blockId: BlockIdentifier): seq[byte]
|
||||
proc eth_estimateGas(call: EthCall, blockId: BlockIdentifier): Quantity
|
||||
proc eth_estimateGas(call: EthCall): Quantity
|
||||
proc eth_createAccessList(call: EthCall, blockId: BlockIdentifier): AccessListResult
|
||||
proc eth_getBlockByHash(data: BlockHash, fullTransactions: bool): BlockObject
|
||||
proc eth_getBlockByNumber(blockId: BlockIdentifier, fullTransactions: bool): BlockObject
|
||||
|
@ -82,5 +83,15 @@ createRpcSigsFromNim(RpcClient):
|
|||
slots: seq[UInt256],
|
||||
blockId: BlockIdentifier): ProofResponse
|
||||
|
||||
proc eth_feeHistory(
|
||||
blockCount: Quantity,
|
||||
newestBlock: BlockIdentifier,
|
||||
rewardPercentiles: Option[seq[float64]]): FeeHistoryResult
|
||||
|
||||
proc debug_getRawBlock(blockId: BlockIdentifier): RlpEncodedBytes
|
||||
proc debug_getRawHeader(blockId: BlockIdentifier): RlpEncodedBytes
|
||||
proc debug_getRawReceipts(blockId: BlockIdentifier): seq[RlpEncodedBytes]
|
||||
proc debug_getRawTransaction(data: TxHash): RlpEncodedBytes
|
||||
|
||||
createSingleRpcSig(RpcClient, "eth_getJsonLogs"):
|
||||
proc eth_getLogs(filterOptions: FilterOptions): seq[JsonString]
|
||||
|
|
|
@ -237,6 +237,14 @@ type
|
|||
of bidAlias:
|
||||
alias*: string
|
||||
|
||||
FeeHistoryReward* = array[2, Quantity]
|
||||
|
||||
FeeHistoryResult* = object
|
||||
oldestBlock*: Quantity
|
||||
baseFeePerGas*: seq[Quantity]
|
||||
gasUsedRatio*: seq[float64]
|
||||
reward*: seq[FeeHistoryReward]
|
||||
|
||||
{.push raises: [].}
|
||||
|
||||
func blockId*(n: BlockNumber): RtBlockIdentifier =
|
||||
|
|
Loading…
Reference in New Issue