From 81b7a8c68256c710ac8d4e0d09f29476f31aab4f Mon Sep 17 00:00:00 2001 From: jangko Date: Fri, 28 Jul 2023 17:32:49 +0700 Subject: [PATCH] graphql: add Shanghai and Cancun fields --- .../nodocker/graphql/graphql_sim.nim | 9 +- .../nodocker/graphql/init/blocks.rlp | Bin 23287 -> 24039 bytes .../nodocker/graphql/init/genesis.json | 16 ++ nimbus/common/chain_config.nim | 16 ++ nimbus/core/tx_pool.nim | 8 + nimbus/db/db_chain.nim | 19 +- nimbus/graphql/ethapi.nim | 197 ++++++++++++++++-- nimbus/graphql/ethapi.ql | 80 ++++++- tests/graphql/README.md | 5 - tests/graphql/{eth66 => }/queries.toml | 79 +++---- tests/test_graphql.nim | 8 +- 11 files changed, 345 insertions(+), 92 deletions(-) delete mode 100644 tests/graphql/README.md rename tests/graphql/{eth66 => }/queries.toml (91%) diff --git a/hive_integration/nodocker/graphql/graphql_sim.nim b/hive_integration/nodocker/graphql/graphql_sim.nim index 997180f24..b17ff2ec2 100644 --- a/hive_integration/nodocker/graphql/graphql_sim.nim +++ b/hive_integration/nodocker/graphql/graphql_sim.nim @@ -91,7 +91,11 @@ proc main() = var stat: SimStat let start = getTime() - for fileName {.inject.} in walkDirRec( + + #let fileName = caseFolder & "/37_eth_sendRawTransaction_nonceTooLow.json" + #block: + + for fileName in walkDirRec( caseFolder, yieldFilter = {pcFile,pcLinkToFile}): if not fileName.endsWith(".json"): continue @@ -101,6 +105,9 @@ proc main() = let status = ctx.processNode(node, fileName) stat.inc(name, status) + # simulate the real simulator + txPool.disposeAll() + let elpd = getTime() - start print(stat, elpd, "graphql") diff --git a/hive_integration/nodocker/graphql/init/blocks.rlp b/hive_integration/nodocker/graphql/init/blocks.rlp index d29453d3e53b4382cc3ff4254e1931182a6c630d..480efc87d9fcf4bb02cebf943897e5fec7d18226 100644 GIT binary patch delta 377 zcmeyqmGSv*#tq-2)PFL){mCS=pz!Jp&$~BUo%jBJ5z-&`Hgq=oj#VG0na#Hpc4c_A ztuJ`;K@p{if+EQacx_y>x3y*Nx)8f~;qtUwtCe%+e!SIR^7iZNmlpO696k#S=O%At zJ}!L3&TpnrvE1TJHukC0pIQrbOnGr&uHw@FYZu)3VK_tC^<R!tY@8KSYSRS zYHP2$(4yQ=i+dI8n$)IDs?*%e8#(#?>b89=tJ(QP_jOpzIdJc81J@LoGZ8fJ L+E%6z?k5ZY*TSoo delta 9 QcmaF9oALWr#tq-202{UiQUCw| diff --git a/hive_integration/nodocker/graphql/init/genesis.json b/hive_integration/nodocker/graphql/init/genesis.json index d26036f5a..50c586e53 100644 --- a/hive_integration/nodocker/graphql/init/genesis.json +++ b/hive_integration/nodocker/graphql/init/genesis.json @@ -1,4 +1,20 @@ { + "config": { + "chainId": 1, + "homesteadBlock": 33, + "eip150Block": 33, + "eip155Block": 33, + "eip158Block": 33, + "byzantiumBlock": 33, + "constantinopleBlock": 33, + "petersburgBlock": 33, + "istanbulBlock": 33, + "muirGlacierBlock": 33, + "berlinBlock": 33, + "londonBlock": 33, + "terminalTotalDifficulty": 4357120, + "shanghaiTime": 1444660030 + }, "genesis": { "coinbase" : "0x8888f1f195afa192cfee860698584c030f4c9db1", "difficulty" : "0x020000", diff --git a/nimbus/common/chain_config.nim b/nimbus/common/chain_config.nim index 4f55339bb..da073f899 100644 --- a/nimbus/common/chain_config.nim +++ b/nimbus/common/chain_config.nim @@ -166,6 +166,7 @@ proc readValue(reader: var JsonReader, value: var BlockNonce) except ValueError as ex: reader.raiseUnexpectedValue(ex.msg) +# genesis timestamp is in hex proc readValue(reader: var JsonReader, value: var EthTime) {.gcsafe, raises: [SerializationError, IOError].} = try: @@ -173,6 +174,21 @@ proc readValue(reader: var JsonReader, value: var EthTime) except ValueError as ex: reader.raiseUnexpectedValue(ex.msg) +# but shanghaiTime and cancunTime in config is in int literal +proc readValue(reader: var JsonReader, value: var Option[EthTime]) + {.gcsafe, raises: [SerializationError, IOError].} = + let tok = reader.lexer.lazyTok + if tok == tkNull: + reset value + reader.lexer.next() + else: + # both readValue(GasInt/AccountNonce) will be called if + # we use readValue(int64/uint64) + let tok {.used.} = reader.lexer.tok # resove lazy token + let val = reader.lexer.absIntVal.int64 + value = some val.fromUnix + reader.lexer.next() + proc readValue(reader: var JsonReader, value: var seq[byte]) {.gcsafe, raises: [SerializationError, IOError].} = try: diff --git a/nimbus/core/tx_pool.nim b/nimbus/core/tx_pool.nim index d11b0f379..bbf25f424 100644 --- a/nimbus/core/tx_pool.nim +++ b/nimbus/core/tx_pool.nim @@ -804,6 +804,14 @@ iterator okPairs*(xp: TxPoolRef): (Hash256, TxItemRef) = proc numTxs*(xp: TxPoolRef): int = xp.txDB.byItemID.len +proc disposeAll*(xp: TxpoolRef) {.gcsafe,raises: [CatchableError].} = + let numTx = xp.numTxs + var list = newSeqOfCap[TxItemRef](numTx) + for x in nextPairs(xp.txDB.byItemID): + list.add x.data + for x in list: + xp.disposeItems(x) + # ------------------------------------------------------------------------------ # Public functions, local/remote accounts # ------------------------------------------------------------------------------ diff --git a/nimbus/db/db_chain.nim b/nimbus/db/db_chain.nim index fe67ee03e..e1794daba 100644 --- a/nimbus/db/db_chain.nim +++ b/nimbus/db/db_chain.nim @@ -227,10 +227,12 @@ proc getTransactionCount*(chain: ChainDBRef, txRoot: Hash256): int = var txCount = 0 while true: let txKey = rlp.encode(txCount) - if txKey notin trie: - break - inc txCount - txCount + if txKey in trie: + inc txCount + else: + return txCount + + doAssert(false, "unreachable") proc getUnclesCount*(db: ChainDBRef, ommersHash: Hash256): int = if ommersHash != EMPTY_UNCLE_HASH: @@ -263,6 +265,10 @@ iterator getWithdrawalsData*(db: ChainDBRef, withdrawalsRoot: Hash256): seq[byte break inc idx +proc getWithdrawals*(db: ChainDBRef, withdrawalsRoot: Hash256): seq[Withdrawal] = + for encodedWd in db.getWithdrawalsData(withdrawalsRoot): + result.add(rlp.decode(encodedWd, Withdrawal)) + proc getBlockBody*(db: ChainDBRef, header: BlockHeader, output: var BlockBody): bool = result = true output.transactions = @[] @@ -278,10 +284,7 @@ proc getBlockBody*(db: ChainDBRef, header: BlockHeader, output: var BlockBody): result = false if header.withdrawalsRoot.isSome: - var withdrawals: seq[Withdrawal] - for encodedWd in db.getWithdrawalsData(header.withdrawalsRoot.get): - withdrawals.add(rlp.decode(encodedWd, Withdrawal)) - output.withdrawals = some(withdrawals) + output.withdrawals = some(db.getWithdrawals(header.withdrawalsRoot.get)) proc getBlockBody*(db: ChainDBRef, blockHash: Hash256, output: var BlockBody): bool = var header: BlockHeader diff --git a/nimbus/graphql/ethapi.nim b/nimbus/graphql/ethapi.nim index b52a55654..2e1876e53 100644 --- a/nimbus/graphql/ethapi.nim +++ b/nimbus/graphql/ethapi.nim @@ -10,7 +10,7 @@ import std/[strutils, times], stew/[results, byteutils], stint, - eth/[rlp], chronos, + eth/common/eth_types_rlp, chronos, stew/shims/net, graphql, graphql/graphql as context, graphql/common/types, graphql/httpserver, @@ -19,7 +19,7 @@ import ".."/[transaction, vm_state, config, constants], ../common/common, ../transaction/call_evm, - ../core/tx_pool, + ../core/[tx_pool, tx_pool/tx_item], ../utils/utils from eth/p2p import EthereumNode @@ -37,6 +37,7 @@ type ethQuery = "Query" ethMutation = "Mutation" ethAccessTuple = "AccessTuple" + ethWithdrawal = "Withdrawal" HeaderNode = ref object of Node header: BlockHeader @@ -62,6 +63,9 @@ type AclNode = ref object of Node acl: AccessPair + WdNode = ref object of Node + wd: Withdrawal + GraphqlContextRef = ref GraphqlContextObj GraphqlContextObj = object of Graphql ids: array[EthTypes, Name] @@ -79,7 +83,12 @@ proc toHash(n: Node): Hash256 = result.data = hexToByteArray[32](n.stringVal) proc toBlockNumber(n: Node): BlockNumber = - result = parse(n.intVal, UInt256, radix = 10) + if n.kind == nkInt: + result = parse(n.intVal, UInt256, radix = 10) + elif n.kind == nkString: + result = parse(n.stringVal, UInt256, radix = 16) + else: + doAssert(false, "unknown node type: " & $n.kind) proc headerNode(ctx: GraphqlContextRef, header: BlockHeader): Node = HeaderNode( @@ -128,6 +137,14 @@ proc aclNode(ctx: GraphqlContextRef, accessPair: AccessPair): Node = acl: accessPair ) +proc wdNode(ctx: GraphqlContextRef, wd: Withdrawal): Node = + WdNode( + kind: nkMap, + typeName: ctx.ids[ethWithdrawal], + pos: Pos(), + wd: wd + ) + proc getStateDB(com: CommonRef, header: BlockHeader): ReadOnlyStateDB = ## Retrieves the account db from canonical head ## we don't use accounst_cache here because it's read only operations @@ -190,6 +207,10 @@ proc bigIntNode(x: uint64 | int64): RespResult = # stdlib toHex is not suitable for hive const HexChars = "0123456789abcdef" + + if x == 0: + return ok(Node(kind: nkString, stringVal: "0x0", pos: Pos())) + var n = cast[uint64](x) r: array[2*sizeof(uint64), char] @@ -278,6 +299,19 @@ proc getTxs(ctx: GraphqlContextRef, header: BlockHeader): RespResult = except CatchableError as e: err("can't get transactions: " & e.msg) +proc getWithdrawals(ctx: GraphqlContextRef, header: BlockHeader): RespResult = + try: + if header.withdrawalsRoot.isSome: + let wds = getWithdrawals(ctx.chainDB, header.withdrawalsRoot.get) + var list = respList() + for wd in wds: + list.add wdNode(ctx, wd) + ok(list) + else: + ok(respNull()) + except CatchableError as e: + err("can't get transactions: " & e.msg) + proc getTxAt(ctx: GraphqlContextRef, header: BlockHeader, index: int): RespResult = try: var tx: Transaction @@ -323,9 +357,25 @@ proc accountNode(ctx: GraphqlContextRef, header: BlockHeader, address: EthAddres except RlpError as ex: err(ex.msg) +func hexCharToInt(c: char): uint64 = + case c + of 'a'..'f': return c.uint64 - 'a'.uint64 + 10'u64 + of 'A'..'F': return c.uint64 - 'A'.uint64 + 10'u64 + of '0'..'9': return c.uint64 - '0'.uint64 + else: doAssert(false, "invalid hex digit: " & $c) + proc parseU64(node: Node): uint64 = - for c in node.intVal: - result = result * 10 + uint64(c.int - '0'.int) + if node.kind == nkString: + if node.stringVal.len > 2 and node.stringVal[1] == 'x': + for i in 2.. maxU64: return err("long value overflow") - ok(Node(kind: nkInt, pos: node.pos, intVal: $val)) + ok(node) else: let val = parse(node.stringVal, UInt256, radix = 10) if val > maxU64: return err("long value overflow") - ok(Node(kind: nkInt, pos: node.pos, intVal: node.stringVal)) + ok(Node(kind: nkString, pos: node.pos, stringVal: "0x" & val.toHex)) of nkInt: let val = parse(node.intVal, UInt256, radix = 10) if val > maxU64: return err("long value overflow") - ok(node) + ok(Node(kind: nkString, pos: node.pos, stringVal: "0x" & val.toHex)) else: err("expect int, but got '$1'" % [$node.kind]) except CatchableError as e: @@ -583,25 +633,35 @@ proc txIndex(ud: RootRef, params: Args, parent: Node): RespResult {.apiPragma.} ok(resp(tx.index)) proc txFrom(ud: RootRef, params: Args, parent: Node): RespResult {.apiPragma.} = - # TODO: with block param let ctx = GraphqlContextRef(ud) let tx = TxNode(parent) + + let blockNumber = if params[0].val.kind != nkEmpty: + parseU64(params[0].val).toBlockNumber + else: + tx.blockNumber + var sender: EthAddress if not getSender(tx.tx, sender): return ok(respNull()) - let hres = ctx.getBlockByNumber(tx.blockNumber) + let hres = ctx.getBlockByNumber(blockNumber) if hres.isErr: return hres let h = HeaderNode(hres.get()) ctx.accountNode(h.header, sender) proc txTo(ud: RootRef, params: Args, parent: Node): RespResult {.apiPragma.} = - # TODO: with block param let ctx = GraphqlContextRef(ud) let tx = TxNode(parent) + + let blockNumber = if params[0].val.kind != nkEmpty: + parseU64(params[0].val).toBlockNumber + else: + tx.blockNumber + if tx.tx.contractCreation: return ok(respNull()) - let hres = ctx.getBlockByNumber(tx.blockNumber) + let hres = ctx.getBlockByNumber(blockNumber) if hres.isErr: return hres let h = HeaderNode(hres.get()) @@ -741,6 +801,35 @@ proc txAccessList(ud: RootRef, params: Args, parent: Node): RespResult {.apiPrag list.add aclNode(ctx, x) ok(list) +proc txMaxFeePerBlobGas(ud: RootRef, params: Args, parent: Node): RespResult {.apiPragma.} = + let ctx = GraphqlContextRef(ud) + let tx = TxNode(parent) + if tx.tx.txType < TxEIP4844: + ok(respNull()) + else: + longNode(tx.tx.maxFeePerDataGas) + +proc txVersionedHashes(ud: RootRef, params: Args, parent: Node): RespResult {.apiPragma.} = + let ctx = GraphqlContextRef(ud) + let tx = TxNode(parent) + if tx.tx.txType < TxEIP4844: + ok(respNull()) + else: + var list = respList() + for hs in tx.tx.versionedHashes: + list.add resp("0x" & hs.data.toHex) + ok(list) + +proc txRaw(ud: RootRef, params: Args, parent: Node): RespResult {.apiPragma.} = + let tx = TxNode(parent) + let txBytes = rlp.encode(tx.tx) + resp(txBytes) + +proc txRawReceipt(ud: RootRef, params: Args, parent: Node): RespResult {.apiPragma.} = + let tx = TxNode(parent) + let recBytes = rlp.encode(tx.receipt) + resp(recBytes) + const txProcs = { "from": txFrom, "hash": txHash, @@ -765,7 +854,11 @@ const txProcs = { "maxFeePerGas": txMaxFeePerGas, "maxPriorityFeePerGas": txMaxPriorityFeePerGas, "effectiveGasPrice": txEffectiveGasPrice, - "chainID": txChainId + "chainID": txChainId, + "maxFeePerBlobGas": txmaxFeePerBlobGas, + "versionedHashes": txVersionedHashes, + "raw": txRaw, + "rawReceipt": txRawReceipt } proc aclAddress(ud: RootRef, params: Args, parent: Node): RespResult {.apiPragma.} = @@ -787,6 +880,29 @@ const aclProcs = { "storageKeys": aclStorageKeys } +proc wdIndex(ud: RootRef, params: Args, parent: Node): RespResult {.apiPragma.} = + let w = WdNode(parent) + longNode(w.wd.index) + +proc wdValidator(ud: RootRef, params: Args, parent: Node): RespResult {.apiPragma.} = + let w = WdNode(parent) + longNode(w.wd.validatorIndex) + +proc wdAddress(ud: RootRef, params: Args, parent: Node): RespResult {.apiPragma.} = + let w = WdNode(parent) + resp(w.wd.address) + +proc wdAmount(ud: RootRef, params: Args, parent: Node): RespResult {.apiPragma.} = + let w = WdNode(parent) + longNode(w.wd.amount) + +const wdProcs = { + "index": wdIndex, + "validator": wdValidator, + "address": wdAddress, + "amount": wdAmount +} + proc blockNumberImpl(ud: RootRef, params: Args, parent: Node): RespResult {.apiPragma.} = let ctx = GraphqlContextRef(ud) let h = HeaderNode(parent) @@ -1012,6 +1128,32 @@ proc blockBaseFeePerGas(ud: RootRef, params: Args, parent: Node): RespResult {.a else: ok(respNull()) +proc blockWithdrawalsRoot(ud: RootRef, params: Args, parent: Node): RespResult {.apiPragma.} = + let h = HeaderNode(parent) + if h.header.withdrawalsRoot.isSome: + resp(h.header.withdrawalsRoot.get) + else: + ok(respNull()) + +proc blockWithdrawals(ud: RootRef, params: Args, parent: Node): RespResult {.apiPragma.} = + let ctx = GraphqlContextRef(ud) + let h = HeaderNode(parent) + getWithdrawals(ctx, h.header) + +proc blockBlobGasUsed(ud: RootRef, params: Args, parent: Node): RespResult {.apiPragma.} = + let h = HeaderNode(parent) + if h.header.dataGasUsed.isSome: + longNode(h.header.dataGasUsed.get) + else: + ok(respNull()) + +proc blockexcessBlobGas(ud: RootRef, params: Args, parent: Node): RespResult {.apiPragma.} = + let h = HeaderNode(parent) + if h.header.excessDataGas.isSome: + longNode(h.header.excessDataGas.get) + else: + ok(respNull()) + const blockProcs = { "parent": blockParent, "number": blockNumberImpl, @@ -1040,7 +1182,11 @@ const blockProcs = { "account": blockAccount, "call": blockCall, "estimateGas": blockEstimateGas, - "baseFeePerGas": blockBaseFeePerGas + "baseFeePerGas": blockBaseFeePerGas, + "withdrawalsRoot": blockWithdrawalsRoot, + "withdrawals": blockWithdrawals, + "blobGasUsed": blockBlobGasUsed, + "excessBlobGas": blockExcessBlobGas } proc callResultData(ud: RootRef, params: Args, parent: Node): RespResult {.apiPragma.} = @@ -1151,6 +1297,12 @@ proc queryBlock(ud: RootRef, params: Args, parent: Node): RespResult {.apiPragma err("only one param allowed, number or hash, not both") elif number.kind == nkInt: getBlockByNumber(ctx, number) + elif number.kind == nkString: + try: + let blockNumber = toBlockNumber(number) + getBlockByNumber(ctx, blockNumber) + except ValueError as ex: + err(ex.msg) elif hash.kind == nkString: getBlockByHash(ctx, hash) else: @@ -1241,16 +1393,26 @@ const queryProcs = { "chainID": queryChainId } +proc inPoolAndOk(ctx: GraphqlContextRef, txHash: Hash256): bool = + let res = ctx.txPool.getItem(txHash) + if res.isErr: return false + res.get().reject == txInfoOk + proc sendRawTransaction(ud: RootRef, params: Args, parent: Node): RespResult {.apiPragma.} = - # TODO: add tx validation and tx processing - # probably goes to tx pool # if tx validation failed, the result will be null let ctx = GraphqlContextRef(ud) try: let data = hexToSeqByte(params[0].val.stringVal) let tx = decodeTx(data) # we want to know if it is a valid tx blob let txHash = rlpHash(tx) # beware EIP-4844 - resp(txHash) + + ctx.txPool.add(tx) + + if ctx.inPoolAndOk(txHash): + return resp(txHash) + else: + return err("transaction rejected by txpool") + except CatchableError as em: return err("failed to process raw transaction: " & em.msg) @@ -1315,6 +1477,7 @@ proc initEthApi(ctx: GraphqlContextRef) = ctx.addResolvers(ctx, ctx.ids[ethQuery ], queryProcs) ctx.addResolvers(ctx, ctx.ids[ethMutation ], mutationProcs) ctx.addResolvers(ctx, ctx.ids[ethAccessTuple], aclProcs) + ctx.addResolvers(ctx, ctx.ids[ethWithdrawal ], wdProcs) var qc = newQC(ctx) ctx.addInstrument(qc) diff --git a/nimbus/graphql/ethapi.ql b/nimbus/graphql/ethapi.ql index 0e68f910c..5329209c4 100644 --- a/nimbus/graphql/ethapi.ql +++ b/nimbus/graphql/ethapi.ql @@ -5,7 +5,7 @@ scalar Bytes32 scalar Address # Bytes is an arbitrary length binary string, represented as 0x-prefixed hexadecimal. -# An empty byte string is represented as '0x'. Byte strings must have an even number of hexadecimal nybbles. +# An empty byte string is represented as '0x'. Byte strings must have an even number of hexadecimal nibbles. scalar Bytes # BigInt is a large integer. Input is accepted as either a JSON number or as a string. @@ -13,7 +13,9 @@ scalar Bytes # 0x-prefixed hexadecimal. scalar BigInt -# Long is a 64 bit unsigned integer. +# Long is a 64 bit unsigned integer. Input is accepted as either a JSON number or as a string. +# Strings may be either decimal or 0x-prefixed hexadecimal. Output values are all +# 0x-prefixed hexadecimal. scalar Long schema { @@ -46,7 +48,7 @@ type Account { # Log is an Ethereum event log. type Log { # Index is the index of this log in the block. - index: Int! + index: Long! # Account is the account which generated this log - this will always # be a contract account. @@ -64,13 +66,28 @@ type Log { # EIP-2718 Access List type AccessTuple { - # access list address + # access list address. address: Address! - # access list storage keys, null if not present + # access list storage keys, null if not present. storageKeys: [Bytes32!] } +# EIP-4895 +type Withdrawal { + # Index is a monotonically increasing identifier issued by consensus layer. + index: Long! + + # Validator is index of the validator associated with withdrawal. + validator: Long! + + # Recipient address of the withdrawn amount. + address: Address! + + # Amount is the withdrawal value in Gwei. + amount: Long! +} + # Transaction is an Ethereum transaction. type Transaction { # Hash is the hash of this transaction. @@ -81,7 +98,7 @@ type Transaction { # Index is the index of this transaction in the parent block. This will # be null if the transaction has not yet been mined. - index: Int + index: Long # From is the account that sent this transaction - this will always be # an externally owned account. @@ -157,14 +174,31 @@ type Transaction { v: BigInt! # EIP 2718: envelope transaction support - type: Int + type: Long # EIP 2930: optional access list, null if not present accessList: [AccessTuple!] + # EIP-4844: blob gas a user willing to pay + maxFeePerBlobGas: Long + + # EIP-4844: represents a list of hash outputs from kzg_to_versioned_hash + versionedHashes: [Bytes32!] + + #--------------------------Extensions------------------------------- + # If type == 0, chainID returns null. # If type > 0, chainID returns replay protection chainID chainID: Long + + # Raw is the canonical encoding of the transaction. + # For legacy transactions, it returns the RLP encoding. + # For EIP-2718 typed transactions, it returns the type and payload. + raw: Bytes! + + # RawReceipt is the canonical encoding of the receipt. For post EIP-2718 typed transactions + # this is equivalent to TxType || ReceiptEncoding. + rawReceipt: Bytes! } # BlockFilterCriteria encapsulates log filter criteria for a filter applied @@ -207,7 +241,7 @@ type Block { # TransactionCount is the number of transactions in this block. if # transactions are not available for this block, this field will be null. - transactionCount: Int + transactionCount: Long # StateRoot is the keccak256 hash of the state trie after this block was processed. stateRoot: Bytes32! @@ -249,7 +283,7 @@ type Block { # OmmerCount is the number of ommers (AKA uncles) associated with this # block. If ommers are unavailable, this field will be null. - ommerCount: Int + ommerCount: Long # Ommers is a list of ommer (AKA uncle) blocks associated with this block. # If ommers are unavailable, this field will be null. Depending on your @@ -286,6 +320,32 @@ type Block { # EstimateGas estimates the amount of gas that will be required for # successful execution of a transaction at the current block's state. estimateGas(data: CallData!): Long! + + # WithdrawalsRoot is the withdrawals trie root in this block. + # If withdrawals are unavailable for this block, this field will be null. + withdrawalsRoot: Bytes32 + + # Withdrawals is a list of withdrawals associated with this block. If + # withdrawals are unavailable for this block, this field will be null. + withdrawals: [Withdrawal!] + + # EIP-4844: is the total amount of blob gas consumed by the transactions + # within the block. + blobGasUsed: Long + + # EIP-4844: is a running total of blob gas consumed in excess of the target, + # prior to the block. Blocks with above-target blob gas consumption increase + # this value, blocks with below-target blob gas consumption decrease it + # (bounded at 0). + excessBlobGas: Long + + #--------------------------Extensions------------------------------- + + # RawHeader is the RLP encoding of the block's header. + rawHeader: Bytes! + + # Raw is the RLP encoding of the block. + raw: Bytes! } # CallData represents the data associated with a local contract call. @@ -379,7 +439,7 @@ type SyncState{ # Pending represents the current pending state. type Pending { # TransactionCount is the number of transactions in the pending state. - transactionCount: Int! + transactionCount: Long! # Transactions is a list of transactions in the current pending state. transactions: [Transaction!] diff --git a/tests/graphql/README.md b/tests/graphql/README.md deleted file mode 100644 index 87a3f9d30..000000000 --- a/tests/graphql/README.md +++ /dev/null @@ -1,5 +0,0 @@ -per protocool folders apply, e.g. - -- eth65/queries.toml (obsoleted *eth65* test specs) -- eth66/queries.toml -- ... diff --git a/tests/graphql/eth66/queries.toml b/tests/graphql/queries.toml similarity index 91% rename from tests/graphql/eth66/queries.toml rename to tests/graphql/queries.toml index ce605ea1d..4299c6278 100644 --- a/tests/graphql/eth66/queries.toml +++ b/tests/graphql/queries.toml @@ -23,6 +23,7 @@ {"name":"Address"}, {"name":"Block"}, {"name":"CallResult"}, + {"name":"Withdrawal"}, {"name":"Query"}, {"name":"Boolean"}, {"name":"FilterCriteria"}, @@ -106,38 +107,38 @@ """ result = """ { - "chainID":1, + "chainID":"0x1", "block":{ "__typename":"Block", - "number":3, + "number":"0x3", "hash":"0x72d69cc3c74c2740c27f4becae6c9f9fc524c702a9a48d23c564b13dce5fe0a1", "parent":{ "__typename":"Block", - "number":2 + "number":"0x2" }, "nonce":"0x0000000000000000", "transactionsRoot":"0x8c8775e959d553f9f991a21a8502dbe6819c53c518711f2cbbbd7ed998f65647", - "transactionCount":1, + "transactionCount":"0x1", "stateRoot":"0x81b1df384897709e96a4b6319fda4e8f7682a1c9d753b1fb772ed4fb19f1e443", "receiptsRoot":"0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2", "miner":{ "__typename":"Account", "address":"0x8888f1f195afa192cfee860698584c030f4c9db1", "balance":"0x542253a12a8f8dc0", - "transactionCount":0, + "transactionCount":"0x0", "code":"0x", "storage":"0x0000000000000000000000000000000000000000000000000000000000000000" }, "extraData":"0x42", - "gasLimit":3141592, + "gasLimit":"0x2fefd8", "baseFeePerGas":null, - "gasUsed":21000, + "gasUsed":"0x5208", "timestamp":"0x54c99839", "logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000", "difficulty":"0x20000", "totalDifficulty":"0x80000", - "ommerCount":1, + "ommerCount":"0x1", "ommers":[ { "__typename":"Block", @@ -154,7 +155,7 @@ "__typename":"Account", "address":"0x095e7baea6a6c7c4c2dfeb977efac326af552d87", "balance":"0x1e", - "transactionCount":0, + "transactionCount":"0x0", "code":"0x", "storage":"0x0000000000000000000000000000000000000000000000000000000000000000" }, @@ -226,8 +227,8 @@ { "__typename":"Transaction", "hash":"0xbb8e2ffb7276bc688c0305d5c35ae219cf036ac7a3b058ffa9d32275cc75f31b", - "nonce":0, - "index":0, + "nonce":"0x0", + "index":"0x0", "from":{ "__typename":"Account", "address":"0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b" @@ -238,21 +239,21 @@ }, "value":"0xa", "gasPrice":"0x3e8", - "gas":314159, + "gas":"0x4cb2f", "inputData":"0x", "block":{ "__typename":"Block", - "number":1 + "number":"0x1" }, - "status":1, - "gasUsed":21000, - "cumulativeGasUsed":21000, + "status":"0x1", + "gasUsed":"0x5208", + "cumulativeGasUsed":"0x5208", "createdContract":null, "logs":[], "r":"0x25d49a54362b5ae38cf895fa9a1d3ded6f7d5577e572c9a93cdebff6e33ceaf7", "s":"0x773806df18e22db29acde1dd96c0418e28738af7f520e5e2c5c673494029e5", "v":"0x1b", - "type":0, + "type":"0x0", "accessList":null, "maxFeePerGas":null, "maxPriorityFeePerGas":null, @@ -283,7 +284,7 @@ "block":{ "__typename":"Block", "hash":"0x72d69cc3c74c2740c27f4becae6c9f9fc524c702a9a48d23c564b13dce5fe0a1", - "number":3 + "number":"0x3" } } """ @@ -304,7 +305,7 @@ "block":{ "__typename":"Block", "hash":"0x72d69cc3c74c2740c27f4becae6c9f9fc524c702a9a48d23c564b13dce5fe0a1", - "number":3 + "number":"0x3" } } """ @@ -361,9 +362,9 @@ { "syncing":{ "__typename":"SyncState", - "startingBlock":0, - "currentBlock":3, - "highestBlock":0, + "startingBlock":"0x0", + "currentBlock":"0x3", + "highestBlock":"0x0", "pulledStates":null, "knownStates":null } @@ -386,22 +387,22 @@ "blocks":[ { "__typename":"Block", - "number":0, + "number":"0x0", "hash":"0x2b253498ad5e63a16978753398bad1fde371a3e513438297b52d65dc98e1db29" }, { "__typename":"Block", - "number":1, + "number":"0x1", "hash":"0xa4b71270e83c38d941d61fc2d8f3842f98a83203b82c3dea3176f1feb5a67b17" }, { "__typename":"Block", - "number":2, + "number":"0x2", "hash":"0x579d2bdb721bde7a6bc56b53f4962a58493ee11482e8f738702ddb3d65888a74" }, { "__typename":"Block", - "number":3, + "number":"0x3", "hash":"0x72d69cc3c74c2740c27f4becae6c9f9fc524c702a9a48d23c564b13dce5fe0a1" } ] @@ -426,7 +427,7 @@ result = """ { "block":{ - "estimateGas":21000 + "estimateGas":"0x5208" } } """ @@ -457,8 +458,8 @@ "call":{ "__typename":"CallResult", "data":"0x", - "gasUsed":21000, - "status":1 + "gasUsed":"0x5208", + "status":"0x1" } } } @@ -471,24 +472,8 @@ mutation { sendRawTransaction(data: "0xf86080018304cb2f94095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba077c7cd36820c71821c1aed59de46e70e701c4a8dd89c9ba508ab722210f60da8a03f29825d40c7c3f7bff3ca69267e0f3fb74b2d18b8c2c4e3c135b5d3b06e288d") } """ - result = """ -{ - "sendRawTransaction":"0x4ffa559ae277813fb886d1fa8743a590ba6b699f9893de99f5d1bcea9620b278" -} -""" - -[[units]] - name = "query.protocolVersion" - code = """ -{ - protocolVersion -} -""" - result = """ -{ - "protocolVersion":66 -} -""" + errors = ["[2, 3]: Fatal: Field 'sendRawTransaction' cannot be resolved: \"transaction rejected by txpool\": @[\"sendRawTransaction\"]"] + result = """null""" [[units]] name = "query.block(number) logs" diff --git a/tests/test_graphql.nim b/tests/test_graphql.nim index d608386d3..6786f4aef 100644 --- a/tests/test_graphql.nim +++ b/tests/test_graphql.nim @@ -8,7 +8,7 @@ # those terms. import - std/[os, json], + std/[json], stew/byteutils, eth/[p2p, rlp], graphql, ../nimbus/graphql/ethapi, graphql/test_common, @@ -25,8 +25,8 @@ type uncles: seq[BlockHeader] const - caseFolder = "tests" / "graphql" / "eth" & $ethVersion - dataFolder = "tests" / "fixtures" / "eth_tests" / "BlockchainTests" / "ValidBlocks" / "bcUncleTest" + caseFolder = "tests/graphql" + dataFolder = "tests/fixtures/eth_tests/BlockchainTests/ValidBlocks/bcUncleTest" proc toBlock(n: JsonNode, key: string): EthBlock = let rlpBlob = hexToSeqByte(n[key].str) @@ -43,7 +43,7 @@ proc setupChain(): CommonRef = berlinBlock : some(10.toBlockNumber) ) - var jn = json.parseFile(dataFolder / "oneUncle.json") + var jn = json.parseFile(dataFolder & "/oneUncle.json") for k, v in jn: if v["network"].str == "Istanbul": jn = v