以太坊中GraphQL简介及使用
source link: https://learnblockchain.cn/article/1062
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
以太坊在去年升级的go-ethereum(geth)1.9.0大版本,除了性能得到大幅提升之外,引入了GraphQL,一种节点接口查询机制,用以补充JSON-RPC。
以太坊在去年升级的go-ethereum(geth)1.9.0大版本,除了性能得到大幅提升之外,引入了GraphQL,一种节点接口查询机制,用以补充JSON-RPC。
本文将会介绍GraphQL是什么,Geth为什么要引入GraphQL以及如何使用GraphQL三个方面对以太坊的GraphQL做一个介绍。
一、GraphQL是什么
GraphQL官网对GraphQL的介绍是:GraphQL 是一个用于 API 的查询语言,是一个使用基于类型系统来执行查询的服务端运行时(类型系统由你的数据定义)。GraphQL 并没有和任何特定数据库或者存储引擎绑定,而是依靠你现有的代码和数据支撑。
GraphQL有以下特点:
1、请求你所要的数据,不多不少
目前大部分的项目都是采用前后端分离的开发模式,前后端采用API进行数据交流。API返回数据往往是前后端协商定义的,而后端为了满足不同的客户端,减小后端请求的复杂性,往往会给出一些冗余数据。GraphQL很强大的一个功能就是能够指定所需要的API数据并获得可预测的结果。
2、获取多个资源,只用一个请求
在使用REST API时,我们如果需要多个资源,则会分别请求不同的接口,而使用GraphQL 可以通过一次请求就获取你应用所需的所有数据。
3、使用类型描述所有的可能
GraphQL API 基于类型和字段的方式进行组织,而非入口端点。你可以通过一个单一入口端点得到你所有的数据能力。GraphQL 使用类型来保证应用只请求可能的数据,还提供了清晰的辅助性错误信息。应用可以使用类型,而避免编写手动解析代码。
二、以太坊为什么要使用GraphQL
以太坊在EIP1767中描述了“在以太坊节点中使用GraphQL”的提案,在以太坊节点中使用GraphQL模式目的时完全替代使用JSON-RPC获取只读信息,使用GraphQL具有高可用性、一致性、高效率和面向未来的优势。
之所以引入GraphQL,是因为使用JSON-RPC有一些不足,这些不足包括:
1、对一些异常请求数据的判断的复杂性
如对空字符的判断,不同的地方对(""、"0x"、"0x0")的判断是不同的,会导致一些不必要的工作。
2、为了返回数据全面而额外增加资源消耗
例如,我们在调用eth_getBlock时会返回totalDifficulty字段,而该字段与块头是分开存储,需要单独读取磁盘,许多调用者不需要此字段,但是RPC服务器无法知道用户是否需要此字段,只能对每次调用 eth_getBlock
检索此字段。
3、接口重复调用,重复浪费资源
例如,我们在发起一笔交易后,通常会以轮询的方式调用eth_getTransactionReceipt接口,来判断交易是否上链。以太坊中的交易收据作为每个块的单个二进制Blob存储在磁盘上,获取单个交易的收据需要获取并反序列化此blob,然后找到相关条目并返回,重复调用时,节点实现要重复获取和反序列化相同数据,造成资源浪费。
针对JSON-RPC的这些不足,有的同学会说,那我通过修改JSON-RPC的接口,也可以避免上边的问题,但是这样增加接口的复杂性。而API查询语言GraphQL就能很好的解决上边的问题。
三、如何使用GraphQL
3.1 开启Geth对GraphQL的支持
Geth在1.9.0及以上版本支持了GraphQL,要开启GraphQL支持,在启动Geth客户端时增加 --graphql
。
Geth与GraphQL相关的配置命令有:
- --graphql ,在节点中开启GraphQL服务
- --graphql.addr value,GraphQL服务地址,默认时localhost
- --graphql.port value,GraphQL服务端口号,默认8547
- --graphql.corsdomain value,GraphQL服务访问的跨域配置
- --graphql.vhosts value,主机名白名单配置
默认配置启动GraphQL服务后,在浏览器中访问 http://localhost:8547 会看到如下界面。
3.2 GraphQL使用示例
GraphQL的语法详见官网,这里不赘述。 https://spec.graphql.cn 另外,在GraphQL浏览器中,也有请求的示例和补全,使用起来相对比较简单。
1、查询网络区块同步状态
query { syncing { currentBlock highestBlock knownStates pulledStates startingBlock } }
2、查询事件
{ logs(filter: {fromBlock: 0, addresses: ["0xf105795bf5d1b1894e70bd04dc846898ab19fa62"], topics: [["0x0f0c27adfd84b60b6f456b0e87cdccb1e5fb9603991588d87fa99f5b6b61e670"]]} ) { transaction { hash from { address } block{ number timestamp } } } }
3、查询区块信息
query c{ blocks(from:100, to:120) { number hash timestamp } }
4、查询交易
{ transaction(hash:"0x0f0c27adfd84b60b6f456b0e87cdccb1e5fb9603991588d87fa99f5b6b61e670") { nonce from { address } to { address } } }
3.3 GraphQL对JSON-RPC的向后兼容
GraphQL实现了JSON-RPC节点接口提供的大部分只读功能。可以将现有的RPC调用映射到GraphQL查询,如下所示:
| RPC | 状态 | 描述 | | - | - | - | | eth_blockNumber | 已实施 | { block { number } }
| | eth_call | 已实施 | { call(data: { to: "0x...", data: "0x..." }) { data status gasUsed } }
| | eth_estimateGas | 已实施 | { estimateGas(data: { to: "0x...", data: "0x..." }) }
| | eth_gasPrice | 已实施 | { gasPrice }
| | eth_getBalance | 已实施 | { account(address: "0x...") { balance } }
| | eth_getBlockByHash | 已实施 | { block(hash: "0x...") { ... } }
| | eth_getBlockByNumber | 已实施 | { block(number: 123) { ... } }
| | eth_getBlockTransactionCountByHash | 已实施 | { block(hash: "0x...") { transactionCount } }
| | eth_getBlockTransactionCountByNumber | 已实施 | { block(number: x) { transactionCounnt } }
| | eth_getCode | 已实施 | { account(address: "0x...") { code } }
| | eth_getLogs | 已实施 | { logs(filter: { ... }) { ... } }
要么 { block(...) { logs(filter: { ... }) { ... } } }
| | eth_getStorageAt | 已实施 | { account(address: "0x...") { storage(slot: "0x...") } }
| | eth_getTransactionByBlockHashAndIndex | 已实施 | { block(hash: "0x...") { transactionAt(index: x) { ... } } }
| | eth_getTransactionByBlockNumberAndIndex | 已实施 | { block(number: n) { transactionAt(index: x) { ... } } }
| | eth_getTransactionByHash | 已实施 | { transaction(hash: "0x...") { ... } }
| | eth_getTransactionCount | 已实施 | { account(address: "0x...") { transactionCount } }
| | eth_getTransactionReceipt | 已实施 | { transaction(hash: "0x...") { ... } }
| | eth_getUncleByBlockHashAndIndex | 已实施 | { block(hash: "0x...") { ommerAt(index: x) { ... } } }
| | eth_getUncleByBlockNumberAndIndex | 已实施 | { block(number: n) { ommerAt(index: x) { ... } } }
| | eth_getUncleCountByBlockHash | 已实施 | { block(hash: "0x...") { ommerCount } }
| | eth_getUncleCountByBlockNumber | 已实施 | { block(number: x) { ommerCount } }
| | eth_protocolVersion | 已实施 | { protocolVersion }
| | eth_sendRawTransaction | 已实施 | mutation { sendRawTransaction(data: data) }
| | eth_syncing | 已实施 | { syncing { ... } }
| | eth_getCompilers | 未实现 | JSON-RPC中不推荐使用编译器功能。 | | eth_compileLLL | 未实现 | JSON-RPC中不推荐使用编译器功能。 | | eth_compileSolidity | 未实现 | JSON-RPC中不推荐使用编译器功能。 | | eth_compileSerpent | 未实现 | JSON-RPC中不推荐使用编译器功能。 | | eth_newFilter | 未实现 | 可以在以后的EIP中指定过滤器功能。 | | eth_newBlockFilter | 未实现 | 可以在以后的EIP中指定过滤器功能。 | | eth_newPendingTransactionFilter | 未实现 | 可以在以后的EIP中指定过滤器功能。 | | eth_uninstallFilter | 未实现 | 可以在以后的EIP中指定过滤器功能。 | | eth_getFilterChanges | 未实现 | 可以在以后的EIP中指定过滤器功能。 | | eth_getFilterLogs | 未实现 | 可以在以后的EIP中指定过滤器功能。 | | eth_accounts | 未实现 | 帐户功能不是核心节点API的一部分。 | | eth_sign | 未实现 | 帐户功能不是核心节点API的一部分。 | | eth_sendTransaction | 未实现 | 帐户功能不是核心节点API的一部分。 | | eth_coinbase | 未实现 | 挖掘功能将单独定义。 | | eth_getWork | 未实现 | 挖掘功能将单独定义。 | | eth_hashRate | 未实现 | 挖掘功能将单独定义。 | | eth_mining | 未实现 | 挖掘功能将单独定义。 | | eth_submitHashrate | 未实现 | 挖掘功能将单独定义。 | | eth_submitWork | 未实现 | 挖掘功能将单独定义。 |
3.4 Quorum对GraphQL的支持
Quorum在v2.6.0版本中将Geth升级到了1.9.7,并支持GraphQL。在以太坊GraphQL服务的基础上,增加了对隐私交易的支持。
# Transaction is an Ethereum transaction. type Transaction { ... # IsPrivate is an indicator of Quorum private transaction isPrivate: Boolean # PrivateInputData is the actual payload of Quorum private transaction privateInputData: Bytes }
查询隐私交易的语法:
transaction(hash: "0x58462fa0b6074a8feb5d9b8cd0e6bb7ef4d1528471396070d9ae617c5dee40a8") { isPrivate inputData privateInputData }
四、参考
GraphQL官网: https://graphql.cn/learn/
以太坊GraphQL提案: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1767.md
Ethereum, meet GraphQL: https://medium.com/dsys/ethereum-meet-graphql-c28f3402fe8f
以太坊在去年升级的go-ethereum(geth)1.9.0大版本,除了性能得到大幅提升之外,引入了GraphQL,一种节点接口查询机制,用以补充JSON-RPC。
本文将会介绍GraphQL是什么,Geth为什么要引入GraphQL以及如何使用GraphQL三个方面对以太坊的GraphQL做一个介绍。
一、GraphQL是什么
GraphQL官网对GraphQL的介绍是:GraphQL 是一个用于 API 的查询语言,是一个使用基于类型系统来执行查询的服务端运行时(类型系统由你的数据定义)。GraphQL 并没有和任何特定数据库或者存储引擎绑定,而是依靠你现有的代码和数据支撑。
GraphQL有以下特点:
1、请求你所要的数据,不多不少
目前大部分的项目都是采用前后端分离的开发模式,前后端采用API进行数据交流。API返回数据往往是前后端协商定义的,而后端为了满足不同的客户端,减小后端请求的复杂性,往往会给出一些冗余数据。GraphQL很强大的一个功能就是能够指定所需要的API数据并获得可预测的结果。
2、获取多个资源,只用一个请求
在使用REST API时,我们如果需要多个资源,则会分别请求不同的接口,而使用GraphQL 可以通过一次请求就获取你应用所需的所有数据。
3、使用类型描述所有的可能
GraphQL API 基于类型和字段的方式进行组织,而非入口端点。你可以通过一个单一入口端点得到你所有的数据能力。GraphQL 使用类型来保证应用只请求可能的数据,还提供了清晰的辅助性错误信息。应用可以使用类型,而避免编写手动解析代码。
二、以太坊为什么要使用GraphQL
以太坊在EIP1767中描述了“在以太坊节点中使用GraphQL”的提案,在以太坊节点中使用GraphQL模式目的时完全替代使用JSON-RPC获取只读信息,使用GraphQL具有高可用性、一致性、高效率和面向未来的优势。
之所以引入GraphQL,是因为使用JSON-RPC有一些不足,这些不足包括:
1、对一些异常请求数据的判断的复杂性
如对空字符的判断,不同的地方对(""、"0x"、"0x0")的判断是不同的,会导致一些不必要的工作。
2、为了返回数据全面而额外增加资源消耗
例如,我们在调用eth_getBlock时会返回totalDifficulty字段,而该字段与块头是分开存储,需要单独读取磁盘,许多调用者不需要此字段,但是RPC服务器无法知道用户是否需要此字段,只能对每次调用 eth_getBlock
检索此字段。
3、接口重复调用,重复浪费资源
例如,我们在发起一笔交易后,通常会以轮询的方式调用eth_getTransactionReceipt接口,来判断交易是否上链。以太坊中的交易收据作为每个块的单个二进制Blob存储在磁盘上,获取单个交易的收据需要获取并反序列化此blob,然后找到相关条目并返回,重复调用时,节点实现要重复获取和反序列化相同数据,造成资源浪费。
针对JSON-RPC的这些不足,有的同学会说,那我通过修改JSON-RPC的接口,也可以避免上边的问题,但是这样增加接口的复杂性。而API查询语言GraphQL就能很好的解决上边的问题。
三、如何使用GraphQL
3.1 开启Geth对GraphQL的支持
Geth在1.9.0及以上版本支持了GraphQL,要开启GraphQL支持,在启动Geth客户端时增加 --graphql
。
Geth与GraphQL相关的配置命令有:
- --graphql ,在节点中开启GraphQL服务
- --graphql.addr value,GraphQL服务地址,默认时localhost
- --graphql.port value,GraphQL服务端口号,默认8547
- --graphql.corsdomain value,GraphQL服务访问的跨域配置
- --graphql.vhosts value,主机名白名单配置
默认配置启动GraphQL服务后,在浏览器中访问 http://localhost:8547 会看到如下界面。
3.2 GraphQL使用示例
GraphQL的语法详见官网,这里不赘述。 https://spec.graphql.cn 另外,在GraphQL浏览器中,也有请求的示例和补全,使用起来相对比较简单。
1、查询网络区块同步状态
query { syncing { currentBlock highestBlock knownStates pulledStates startingBlock } }
2、查询事件
{ logs(filter: {fromBlock: 0, addresses: ["0xf105795bf5d1b1894e70bd04dc846898ab19fa62"], topics: [["0x0f0c27adfd84b60b6f456b0e87cdccb1e5fb9603991588d87fa99f5b6b61e670"]]} ) { transaction { hash from { address } block{ number timestamp } } } }
3、查询区块信息
query c{ blocks(from:100, to:120) { number hash timestamp } }
4、查询交易
{ transaction(hash:"0x0f0c27adfd84b60b6f456b0e87cdccb1e5fb9603991588d87fa99f5b6b61e670") { nonce from { address } to { address } } }
3.3 GraphQL对JSON-RPC的向后兼容
GraphQL实现了JSON-RPC节点接口提供的大部分只读功能。可以将现有的RPC调用映射到GraphQL查询,如下所示:
RPC 状态 描述 eth_blockNumber 已实施{ block { number } }
eth_call
已实施
{ call(data: { to: "0x...", data: "0x..." }) { data status gasUsed } }
eth_estimateGas
已实施
{ estimateGas(data: { to: "0x...", data: "0x..." }) }
eth_gasPrice
已实施
{ gasPrice }
eth_getBalance
已实施
{ account(address: "0x...") { balance } }
eth_getBlockByHash
已实施
{ block(hash: "0x...") { ... } }
eth_getBlockByNumber
已实施
{ block(number: 123) { ... } }
eth_getBlockTransactionCountByHash
已实施
{ block(hash: "0x...") { transactionCount } }
eth_getBlockTransactionCountByNumber
已实施
{ block(number: x) { transactionCounnt } }
eth_getCode
已实施
{ account(address: "0x...") { code } }
eth_getLogs
已实施
{ logs(filter: { ... }) { ... } }
要么 { block(...) { logs(filter: { ... }) { ... } } }
eth_getStorageAt
已实施
{ account(address: "0x...") { storage(slot: "0x...") } }
eth_getTransactionByBlockHashAndIndex
已实施
{ block(hash: "0x...") { transactionAt(index: x) { ... } } }
eth_getTransactionByBlockNumberAndIndex
已实施
{ block(number: n) { transactionAt(index: x) { ... } } }
eth_getTransactionByHash
已实施
{ transaction(hash: "0x...") { ... } }
eth_getTransactionCount
已实施
{ account(address: "0x...") { transactionCount } }
eth_getTransactionReceipt
已实施
{ transaction(hash: "0x...") { ... } }
eth_getUncleByBlockHashAndIndex
已实施
{ block(hash: "0x...") { ommerAt(index: x) { ... } } }
eth_getUncleByBlockNumberAndIndex
已实施
{ block(number: n) { ommerAt(index: x) { ... } } }
eth_getUncleCountByBlockHash
已实施
{ block(hash: "0x...") { ommerCount } }
eth_getUncleCountByBlockNumber
已实施
{ block(number: x) { ommerCount } }
eth_protocolVersion
已实施
{ protocolVersion }
eth_sendRawTransaction
已实施
mutation { sendRawTransaction(data: data) }
eth_syncing
已实施
{ syncing { ... } }
eth_getCompilers
未实现
JSON-RPC中不推荐使用编译器功能。
eth_compileLLL
未实现
JSON-RPC中不推荐使用编译器功能。
eth_compileSolidity
未实现
JSON-RPC中不推荐使用编译器功能。
eth_compileSerpent
未实现
JSON-RPC中不推荐使用编译器功能。
eth_newFilter
未实现
可以在以后的EIP中指定过滤器功能。
eth_newBlockFilter
未实现
可以在以后的EIP中指定过滤器功能。
eth_newPendingTransactionFilter
未实现
可以在以后的EIP中指定过滤器功能。
eth_uninstallFilter
未实现
可以在以后的EIP中指定过滤器功能。
eth_getFilterChanges
未实现
可以在以后的EIP中指定过滤器功能。
eth_getFilterLogs
未实现
可以在以后的EIP中指定过滤器功能。
eth_accounts
未实现
帐户功能不是核心节点API的一部分。
eth_sign
未实现
帐户功能不是核心节点API的一部分。
eth_sendTransaction
未实现
帐户功能不是核心节点API的一部分。
eth_coinbase
未实现
挖掘功能将单独定义。
eth_getWork
未实现
挖掘功能将单独定义。
eth_hashRate
未实现
挖掘功能将单独定义。
eth_mining
未实现
挖掘功能将单独定义。
eth_submitHashrate
未实现
挖掘功能将单独定义。
eth_submitWork
未实现
挖掘功能将单独定义。
3.4 Quorum对GraphQL的支持
Quorum在v2.6.0版本中将Geth升级到了1.9.7,并支持GraphQL。在以太坊GraphQL服务的基础上,增加了对隐私交易的支持。
# Transaction is an Ethereum transaction. type Transaction { ... # IsPrivate is an indicator of Quorum private transaction isPrivate: Boolean # PrivateInputData is the actual payload of Quorum private transaction privateInputData: Bytes }
查询隐私交易的语法:
transaction(hash: "0x58462fa0b6074a8feb5d9b8cd0e6bb7ef4d1528471396070d9ae617c5dee40a8") { isPrivate inputData privateInputData }
四、参考
GraphQL官网: https://graphql.cn/learn/
以太坊GraphQL提案: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1767.md
Ethereum, meet GraphQL: https://medium.com/dsys/ethereum-meet-graphql-c28f3402fe8f
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。
- 发表于 12分钟前
- 阅读 ( 6 )
- 学分 ( 0 )
- 分类:Geth
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK