EIP-1767: GraphQL interface to Ethereum node data


Metadata
Status: StagnantStandards Track: InterfaceCreated: 2019-02-14
Authors
Nick Johnson (@arachnid), Raúl Kripalani (@raulk), Kris Shinn (@kshinn)

Abstract


This EIP specifies a GraphQL schema for accessing data stored on an Ethereum node. It aims to provide a complete replacement to the read-only information exposed via the present JSON-RPC interface, while improving on usability, consistency, efficiency, and future-proofing.

Motivation


The current JSON-RPC interface for Ethereum nodes has a number of shortcomings. It's informally and incompletely specified in areas, which has led to incompatibilities around issues such as representation of empty byte strings ("" vs "0x" vs "0x0"), and it has to make educated guesses about the data a user will request, which often leads to unnecessary work.

For example, the totalDifficulty field is stored separately from the block header in common Ethereum node implementations, and many callers do not require this field. However, every call to eth_getBlock still retrieves this field, requiring a separate disk read, because the RPC server has no way of knowing if the user requires this field or not.

Similarly, transaction receipts in go-ethereum are stored on disk as a single binary blob for each block. Fetching a receipt for a single transaction requires fetching and deserializing this blob, then finding the relevant entry and returning it; this is accomplished by the eth_getTransactionReceipt API call. A common task for API consumers is to fetch all the receipts in a block; as a result, node implementations end up fetching and deserializing the same data repeatedly, leading to O(n^2) effort to fetch all transaction receipts from a block instead of O(n).

Some of these issues could be fixed with changes to the existing JSON-RPC interface, at the cost of complicating the interface somewhat. Instead, we propose adopting a standard query language, GraphQL, which facilitates more efficient API implementations, while also increasing flexibility.

Prior Art


Nick Johnson and EthQL independently developed a GraphQL schema for node data. Once the parties were made aware of the shared effort, they made efforts to bring their schemas into alignment. The current schema proposed in this EIP is derived primarily from the EthQL schema.

Specification


Node API

Compatible nodes MUST provide a GraphQL endpoint available over HTTP. This SHOULD be offered on port 8547 by default. The path to the GraphQL endpoint SHOULD be '/graphql'.

Compatible nodes MAY offer a GraphiQL interactive query explorer on the root path ('/').

Schema

The GraphQL schema for this service is defined as follows:


Nodes MAY offer a superset of this schema, by adding new fields or types. Experimental or client-specific fields MUST be prefixed with 'client' (eg, 'geth' or 'parity'). Unprefixed fields MUST be specified in a new EIP that extends this one.

Rationale


Ethereum nodes have been moving away from providing read-write functionality such as transaction and message signing, and from other services such as code compilation, in favor of a more 'unix-like' approach where each task is performed by a dedicated process. We have thus specified a core set of types and fields that reflects this trend, leaving out functionality that is presently, or intended to be, deprecated:

  • eth_compile* calls are deprecated, and hence not provided here.
  • eth_accounts, eth_sign, and eth_sendTransaction are considered by many to be deprecated, and are not provided here; callers should use local accounts or a separate signing daemon instead.

Further, two areas of the current API interface have been omitted for simplicity in this initial standard, with the intention that they will be defined in a later EIP:

  • Filters will require use of GraphQL subscriptions, and require careful consideration around the desire for nodes without local per-caller state.
  • Mining functionality is less-used and benefits less from reimplementation in GraphQL, and should be specified in a separate EIP.

Backwards Compatibility


This schema implements the bulk of the current read-only functionality provided by the JSON-RPC node interface. Existing RPC calls can be mapped to GraphQL queries as follows:

RPCStatusDescription
eth_blockNumberIMPLEMENTED{ block { number } }
eth_callIMPLEMENTED{ call(data: { to: "0x...", data: "0x..." }) { data status gasUsed } }
eth_estimateGasIMPLEMENTED{ estimateGas(data: { to: "0x...", data: "0x..." }) }
eth_gasPriceIMPLEMENTED{ gasPrice }
eth_getBalanceIMPLEMENTED{ account(address: "0x...") { balance } }
eth_getBlockByHashIMPLEMENTED{ block(hash: "0x...") { ... } }
eth_getBlockByNumberIMPLEMENTED{ block(number: 123) { ... } }
eth_getBlockTransactionCountByHashIMPLEMENTED{ block(hash: "0x...") { transactionCount } }
eth_getBlockTransactionCountByNumberIMPLEMENTED{ block(number: x) { transactionCounnt } }
eth_getCodeIMPLEMENTED{ account(address: "0x...") { code } }
eth_getLogsIMPLEMENTED{ logs(filter: { ... }) { ... } } or { block(...) { logs(filter: { ... }) { ... } } }
eth_getStorageAtIMPLEMENTED{ account(address: "0x...") { storage(slot: "0x...") } }
eth_getTransactionByBlockHashAndIndexIMPLEMENTED{ block(hash: "0x...") { transactionAt(index: x) { ... } } }
eth_getTransactionByBlockNumberAndIndexIMPLEMENTED{ block(number: n) { transactionAt(index: x) { ... } } }
eth_getTransactionByHashIMPLEMENTED{ transaction(hash: "0x...") { ... } }
eth_getTransactionCountIMPLEMENTED{ account(address: "0x...") { transactionCount } }
eth_getTransactionReceiptIMPLEMENTED{ transaction(hash: "0x...") { ... } }
eth_getUncleByBlockHashAndIndexIMPLEMENTED{ block(hash: "0x...") { ommerAt(index: x) { ... } } }
eth_getUncleByBlockNumberAndIndexIMPLEMENTED{ block(number: n) { ommerAt(index: x) { ... } } }
eth_getUncleCountByBlockHashIMPLEMENTED{ block(hash: "0x...") { ommerCount } }
eth_getUncleCountByBlockNumberIMPLEMENTED{ block(number: x) { ommerCount } }
eth_protocolVersionIMPLEMENTED{ protocolVersion }
eth_sendRawTransactionIMPLEMENTEDmutation { sendRawTransaction(data: data) }
eth_syncingIMPLEMENTED{ syncing { ... } }
eth_getCompilersNOT IMPLEMENTEDCompiler functionality is deprecated in JSON-RPC.
eth_compileLLLNOT IMPLEMENTEDCompiler functionality is deprecated in JSON-RPC.
eth_compileSolidityNOT IMPLEMENTEDCompiler functionality is deprecated in JSON-RPC.
eth_compileSerpentNOT IMPLEMENTEDCompiler functionality is deprecated in JSON-RPC.
eth_newFilterNOT IMPLEMENTEDFilter functionality may be specified in a future EIP.
eth_newBlockFilterNOT IMPLEMENTEDFilter functionality may be specified in a future EIP.
eth_newPendingTransactionFilterNOT IMPLEMENTEDFilter functionality may be specified in a future EIP.
eth_uninstallFilterNOT IMPLEMENTEDFilter functionality may be specified in a future EIP.
eth_getFilterChangesNOT IMPLEMENTEDFilter functionality may be specified in a future EIP.
eth_getFilterLogsNOT IMPLEMENTEDFilter functionality may be specified in a future EIP.
eth_accountsNOT IMPLEMENTEDAccounts functionality is not part of the core node API.
eth_signNOT IMPLEMENTEDAccounts functionality is not part of the core node API.
eth_sendTransactionNOT IMPLEMENTEDAccounts functionality is not part of the core node API.
eth_coinbaseNOT IMPLEMENTEDMining functionality to be defined separately.
eth_getWorkNOT IMPLEMENTEDMining functionality to be defined separately.
eth_hashRateNOT IMPLEMENTEDMining functionality to be defined separately.
eth_miningNOT IMPLEMENTEDMining functionality to be defined separately.
eth_submitHashrateNOT IMPLEMENTEDMining functionality to be defined separately.
eth_submitWorkNOT IMPLEMENTEDMining functionality to be defined separately.

For specific reasoning behind omitted functionality, see the Rationale section.

Test Cases


TBD.

Implementation


Copyright


Copyright and related rights waived via CC0.