Geth 中如何使用GraphQL,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。
站在用户的角度思考问题,与客户深入沟通,找到黄埔网站设计与黄埔网站推广的解决方案,凭借多年的经验,让设计与互联网技术结合,创造个性化、用户体验好的作品,建站类型包括:网站建设、成都网站制作、企业官网、英文网站、手机端网站、网站推广、域名注册、网页空间、企业邮箱。业务覆盖黄埔地区。
让我们先了解下经典的JSON-RPC API存在什么问题。
正如其名称所示,JSON-RPC是一种远程过程调用协议,它被设计用来调用远端的函数并返回计算结果。JSON-RPC是相当宽泛的协议,你需要在它之上设计自己的调用接口。
但是JSON-RPC的问题在于它不支持灵活的查询,这会导致计算资源和数据传输方面的双重浪费:
即使用户只需要部分数据,RPC调用也需要返回大量数据,造成带宽的 浪费。例如你调用eth_getBlock的目的只是获取矿工地址,但是它依然 需要返回完整的区块数据。
如果用户重复调用某个RPC接口,即使每次调用只返回一点点数据,也会 浪费节点的CPU。例如当你调用eth_getTransactionReceipt接口轮询某个 交易的收据时。
对于以太坊的JSON-RPC API,由于区块链数据的结构特点,上面的问题被进一步放大了,多次执行一个查询(例如eth_getBalance)需要确保查询是同一世界状态甚至是在同一个节点上:当你使用多个节点进行负载均衡处理时,不同的后台节点可能有不同的同步延迟,从而可能对相同的RPC请求返回不同的内容。
为了解决这些问题,以太坊EIP 1767提出了以太坊节点的GraphQL接口建议,Geth在1.9.0版本中引入了对EIP 1767的支持,实现了完整的原生GraphQL支持。
GraphSQL是为了解决REST API存在的问题而提出的一种新的查询语言。GraphQL将数据对象关系 映射到一个图(Graph),并设计了一种查询语言(Query Language)来遍历图中关系 —— 这也是GraphQL 名称的来源。
这篇文章非常适合不熟悉GraphQL的开发者快速理解GraphQL的基本概念,以及如何利用NodeJS技术栈实现GraphQL的服务端与客户端:从SQL到GraphQL。
Geth 1.9.0引入的对GraphQL的原生支持。在启动geth时,使用--graphql
命令行标志就可以开启GraphQL API接口了。例如,执行下面的命令来接入以太坊Görli测试链并开启GraphQL API支持:
~$ geth --goerli --graphql
一旦开启了Geth的GraphQL支持,就可以通过Geth预置的GraphQL浏览器来进行测试,GraphQL服务默认在8547端口监听,API访问路径为/graphql
。可以使用如下URL访问Geth的GraphQL浏览器:
http://localhost:8547
界面如下所示,最左边就是输入的GraphQL查询:
为了便于查看,这里列出左边的GraphQL查询语句:
{ logs(filter: {fromBlock: 0, addresses: ["0xf105795bf5d1b1894e70bd04dc846898ab19fa62"], topics: [["0x0f0c27adfd84b60b6f456b0e87cdccb1e5fb9603991588d87fa99f5b6b61e670"]]} ) { transaction { hash from { address } block{ number timestamp } } } }
你可以这样理解上面的GraphQL语句:
查询日志logs
查询条件:使用filter对象指定
返回字段:transaction,其结构如上所示
假设我们要查询最新的10个区块的矿工账号以及这些账号的余额,使用JSON-RPC的实现代码如下:
async function main() { const lastBlock = await web3.eth.getBlockNumber() result = [] for (let i = lastBlock; i >= lastBlock - 10; i--) { let block = await web3.eth.getBlock(i) let blockRes = {} blockRes.number = i blockRes.miner = {} blockRes.miner.address = block.miner blockRes.miner.balance = await web3.eth.getBalance(block.miner) result.push(blockRes) } console.log(result); }
我们需要进行10次循环,逐个查询每个区块的矿工账号及其余额。在每次循环中,我们需要调用两次RPC API,分别查询区块数据和账户余额,因此总共需要10*2 = 20 次调用。
下面是获取同样数据的GraphQL查询:
{ blocks(from:lastBlock-10, to:lastBlock) { number miner{ address balance } } }
你可以在Geth GraphQL浏览器中输入并执行上面的查询语句。令人震惊的是,我们只进行1次调用就完成了之前采用JSON-RPC时20次调用才完成的任务!
在Geth源代码中,schema.go文件中包含了当前的GraphQL语法支持。下表列出了Geth GraphQL目前的实现状态,其中简要说明栏目描述了JSON-RPC对应的Geth GraphQL语句:
JSON-RPC | GraphQL状态 | 简要说明 |
---|---|---|
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: { ... }) { ... } } or { 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 | 未实现 | 挖矿相关功能将单独定义 |
看完上述内容,你们掌握Geth 中如何使用GraphQL的方法了吗?如果还想学到更多技能或想了解更多相关内容,欢迎关注创新互联行业资讯频道,感谢各位的阅读!