以太坊中GraphQL简介及使用

以太坊在去年升级的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
我来评几句
登录后评论

已发表评论数()

相关站点

热门文章