From 0ecf9fe1af2ef1be5427d195120a3206bac1da04 Mon Sep 17 00:00:00 2001 From: jangko Date: Mon, 17 May 2021 20:45:37 +0700 Subject: [PATCH] add more query fields and resolvers to graphql api after EIP2718/EIP2930, we have additional fields: type AccessTuple { address: Address! storageKeys : [Bytes32!] } type Transaction { r: BigInt! s: BigInt! v: BigInt! # Envelope transaction support type: Int accessList: [AccessTuple!] } close #606 --- nimbus/graphql/ethapi.nim | 79 ++++++++++++++++++++++++++++++++++---- nimbus/graphql/ethapi.ql | 24 ++++++++++++ tests/graphql/queries.toml | 16 +++++++- 3 files changed, 111 insertions(+), 8 deletions(-) diff --git a/nimbus/graphql/ethapi.nim b/nimbus/graphql/ethapi.nim index 0236f91d3..883580d09 100644 --- a/nimbus/graphql/ethapi.nim +++ b/nimbus/graphql/ethapi.nim @@ -33,6 +33,7 @@ type ethPending = "Pending" ethQuery = "Query" ethMutation = "Mutation" + ethAccessTuple = "AccessTuple" HeaderNode = ref object of Node header: BlockHeader @@ -54,6 +55,9 @@ type index: int tx: TxNode + AclNode = ref object of Node + acl: AccessPair + GraphqlContextRef = ref GraphqlContextObj GraphqlContextObj = object of Graphql ids: array[EthTypes, Name] @@ -104,6 +108,14 @@ proc logNode(ctx: GraphqlContextRef, log: Log, index: int, tx: TxNode): Node = tx: tx ) +proc aclNode(ctx: GraphqlContextRef, accessPair: AccessPair): Node = + AclNode( + kind: nkMap, + typeName: ctx.ids[ethAccessTuple], + pos: Pos(), + acl: accessPair + ) + proc getAccountDb(chainDB: BaseChainDB, header: BlockHeader): ReadOnlyStateDB = ## Retrieves the account db from canonical head ## we don't use accounst_cache here because it's only read operations @@ -638,6 +650,34 @@ proc txLogs(ud: RootRef, params: Args, parent: Node): RespResult {.apiPragma.} = list.add logNode(ctx, n, i, tx) ok(list) +proc txR(ud: RootRef, params: Args, parent: Node): RespResult {.apiPragma.} = + let tx = TxNode(parent) + bigIntNode(tx.tx.R) + +proc txS(ud: RootRef, params: Args, parent: Node): RespResult {.apiPragma.} = + let tx = TxNode(parent) + bigIntNode(tx.tx.S) + +proc txV(ud: RootRef, params: Args, parent: Node): RespResult {.apiPragma.} = + let tx = TxNode(parent) + bigIntNode(tx.tx.V) + +proc txType(ud: RootRef, params: Args, parent: Node): RespResult {.apiPragma.} = + let tx = TxNode(parent) + let typ = resp(ord(tx.tx.txType)) + ok(typ) + +proc txAccessList(ud: RootRef, params: Args, parent: Node): RespResult {.apiPragma.} = + let ctx = GraphqlContextRef(ud) + let tx = TxNode(parent) + if tx.tx.txType == LegacyTxType: + ok(respNull()) + else: + var list = respList() + for x in tx.tx.accessListTx.accessList: + list.add aclNode(ctx, x) + ok(list) + const txProcs = { "from": txFrom, "hash": txHash, @@ -653,7 +693,31 @@ const txProcs = { "gasUsed": txGasUsed, "cumulativeGasUsed": txCumulativeGasUsed, "createdContract": txCreatedContract, - "logs": txLogs + "logs": txLogs, + "r": txR, + "s": txS, + "v": txV, + "type": txType, + "accessList": txAccessList +} + +proc aclAddress(ud: RootRef, params: Args, parent: Node): RespResult {.apiPragma.} = + let acl = AclNode(parent) + resp(acl.acl.address) + +proc aclStorageKeys(ud: RootRef, params: Args, parent: Node): RespResult {.apiPragma.} = + let acl = AclNode(parent) + if acl.acl.storageKeys.len == 0: + ok(respNull()) + else: + var list = respList() + for n in acl.acl.storageKeys: + list.add resp(n).get() + ok(list) + +const aclProcs = { + "address": aclAddress, + "storageKeys": aclStorageKeys } proc blockNumberImpl(ud: RootRef, params: Args, parent: Node): RespResult {.apiPragma.} = @@ -1086,17 +1150,17 @@ type qcFields = "fields" qcBlock = "block" qcTransaction = "Transaction" - + EthQueryComplexity = ref object of QueryComplexity names: array[QcNames, Name] - -proc calcQC(qc: QueryComplexity, field: FieldRef): int {.cdecl, + +proc calcQC(qc: QueryComplexity, field: FieldRef): int {.cdecl, gcsafe, raises: [Defect, CatchableError].} = let qc = EthQueryComplexity(qc) if field.parentType.sym.name == qc.names[qcType] and field.field.name.name == qc.names[qcFields]: return 100 - elif field.parentType.sym.name == qc.names[qcTransaction] and + elif field.parentType.sym.name == qc.names[qcTransaction] and field.field.name.name == qc.names[qcBlock]: return 100 else: @@ -1110,7 +1174,7 @@ proc newQC(ctx: GraphqlContextRef): EthQueryComplexity = let name = ctx.createName($n) qc.names[n] = name qc - + proc initEthApi(ctx: GraphqlContextRef) = ctx.customScalar("Bytes32", scalarBytes32) ctx.customScalar("Address", scalarAddress) @@ -1131,10 +1195,11 @@ proc initEthApi(ctx: GraphqlContextRef) = ctx.addResolvers(ctx, ctx.ids[ethPending ], pendingProcs) ctx.addResolvers(ctx, ctx.ids[ethQuery ], queryProcs) ctx.addResolvers(ctx, ctx.ids[ethMutation ], mutationProcs) + ctx.addResolvers(ctx, ctx.ids[ethAccessTuple], aclProcs) var qc = newQC(ctx) ctx.addInstrument(qc) - + let res = ctx.parseSchema(ethSchema) if res.isErr: echo res.error diff --git a/nimbus/graphql/ethapi.ql b/nimbus/graphql/ethapi.ql index fdff438cc..082951e31 100644 --- a/nimbus/graphql/ethapi.ql +++ b/nimbus/graphql/ethapi.ql @@ -62,6 +62,15 @@ type Log { transaction: Transaction! } +# EIP-2718 Access List +type AccessTuple { + # access list address + address: Address! + + # access list storage keys, null if not present + storageKeys: [Bytes32!] +} + # Transaction is an Ethereum transaction. type Transaction { # Hash is the hash of this transaction. @@ -123,6 +132,21 @@ type Transaction { # Logs is a list of log entries emitted by this transaction. If the # transaction has not yet been mined, this field will be null. logs: [Log!] + + # signature field R + r: BigInt! + + # signature fields S + s: BigInt! + + # signature fields V + v: BigInt! + + # EIP 2718: envelope transaction support + type: Int + + # EIP 2930: optional access list, null if not present + accessList: [AccessTuple!] } # BlockFilterCriteria encapsulates log filter criteria for a filter applied diff --git a/tests/graphql/queries.toml b/tests/graphql/queries.toml index 17212634b..e1824b448 100644 --- a/tests/graphql/queries.toml +++ b/tests/graphql/queries.toml @@ -29,6 +29,7 @@ {"name":"Bytes"}, {"name":"String"}, {"name":"Bytes32"}, + {"name":"AccessTuple"}, {"name":"BlockFilterCriteria"}, {"name":"ID"}, {"name":"Pending"}, @@ -194,6 +195,14 @@ logs { __typename } + r + s + v + type + accessList { + address + storageKeys + } } transactionAt(index: 0) { __typename @@ -230,7 +239,12 @@ "gasUsed":21000, "cumulativeGasUsed":21000, "createdContract":null, - "logs":[] + "logs":[], + "r":"0x77c7cd36820c71821c1aed59de46e70e701c4a8dd89c9ba508ab722210f60da8", + "s":"0x3f29825d40c7c3f7bff3ca69267e0f3fb74b2d18b8c2c4e3c135b5d3b06e288d", + "v":"0x1b", + "type":0, + "accessList":null } ], "transactionAt":{