十年網(wǎng)站開(kāi)發(fā)經(jīng)驗(yàn) + 多家企業(yè)客戶 + 靠譜的建站團(tuán)隊(duì)
量身定制 + 運(yùn)營(yíng)維護(hù)+專業(yè)推廣+無(wú)憂售后,網(wǎng)站問(wèn)題一站解決
Geth 中如何使用GraphQL,相信很多沒(méi)有經(jīng)驗(yàn)的人對(duì)此束手無(wú)策,為此本文總結(jié)了問(wèn)題出現(xiàn)的原因和解決方法,通過(guò)這篇文章希望你能解決這個(gè)問(wèn)題。
站在用戶的角度思考問(wèn)題,與客戶深入溝通,找到黃埔網(wǎng)站設(shè)計(jì)與黃埔網(wǎng)站推廣的解決方案,憑借多年的經(jīng)驗(yàn),讓設(shè)計(jì)與互聯(lián)網(wǎng)技術(shù)結(jié)合,創(chuàng)造個(gè)性化、用戶體驗(yàn)好的作品,建站類型包括:網(wǎng)站建設(shè)、成都網(wǎng)站制作、企業(yè)官網(wǎng)、英文網(wǎng)站、手機(jī)端網(wǎng)站、網(wǎng)站推廣、域名注冊(cè)、網(wǎng)頁(yè)空間、企業(yè)郵箱。業(yè)務(wù)覆蓋黃埔地區(qū)。
讓我們先了解下經(jīng)典的JSON-RPC API存在什么問(wèn)題。
正如其名稱所示,JSON-RPC是一種遠(yuǎn)程過(guò)程調(diào)用協(xié)議,它被設(shè)計(jì)用來(lái)調(diào)用遠(yuǎn)端的函數(shù)并返回計(jì)算結(jié)果。JSON-RPC是相當(dāng)寬泛的協(xié)議,你需要在它之上設(shè)計(jì)自己的調(diào)用接口。
但是JSON-RPC的問(wèn)題在于它不支持靈活的查詢,這會(huì)導(dǎo)致計(jì)算資源和數(shù)據(jù)傳輸方面的雙重浪費(fèi):
即使用戶只需要部分?jǐn)?shù)據(jù),RPC調(diào)用也需要返回大量數(shù)據(jù),造成帶寬的 浪費(fèi)。例如你調(diào)用eth_getBlock的目的只是獲取礦工地址,但是它依然 需要返回完整的區(qū)塊數(shù)據(jù)。
如果用戶重復(fù)調(diào)用某個(gè)RPC接口,即使每次調(diào)用只返回一點(diǎn)點(diǎn)數(shù)據(jù),也會(huì) 浪費(fèi)節(jié)點(diǎn)的CPU。例如當(dāng)你調(diào)用eth_getTransactionReceipt接口輪詢某個(gè) 交易的收據(jù)時(shí)。
對(duì)于以太坊的JSON-RPC API,由于區(qū)塊鏈數(shù)據(jù)的結(jié)構(gòu)特點(diǎn),上面的問(wèn)題被進(jìn)一步放大了,多次執(zhí)行一個(gè)查詢(例如eth_getBalance)需要確保查詢是同一世界狀態(tài)甚至是在同一個(gè)節(jié)點(diǎn)上:當(dāng)你使用多個(gè)節(jié)點(diǎn)進(jìn)行負(fù)載均衡處理時(shí),不同的后臺(tái)節(jié)點(diǎn)可能有不同的同步延遲,從而可能對(duì)相同的RPC請(qǐng)求返回不同的內(nèi)容。
為了解決這些問(wèn)題,以太坊EIP 1767提出了以太坊節(jié)點(diǎn)的GraphQL接口建議,Geth在1.9.0版本中引入了對(duì)EIP 1767的支持,實(shí)現(xiàn)了完整的原生GraphQL支持。
GraphSQL是為了解決REST API存在的問(wèn)題而提出的一種新的查詢語(yǔ)言。GraphQL將數(shù)據(jù)對(duì)象關(guān)系 映射到一個(gè)圖(Graph),并設(shè)計(jì)了一種查詢語(yǔ)言(Query Language)來(lái)遍歷圖中關(guān)系 —— 這也是GraphQL 名稱的來(lái)源。
這篇文章非常適合不熟悉GraphQL的開(kāi)發(fā)者快速理解GraphQL的基本概念,以及如何利用NodeJS技術(shù)棧實(shí)現(xiàn)GraphQL的服務(wù)端與客戶端:從SQL到GraphQL。
Geth 1.9.0引入的對(duì)GraphQL的原生支持。在啟動(dòng)geth時(shí),使用--graphql命令行標(biāo)志就可以開(kāi)啟GraphQL API接口了。例如,執(zhí)行下面的命令來(lái)接入以太坊G?rli測(cè)試鏈并開(kāi)啟GraphQL API支持:
~$ geth --goerli --graphql
一旦開(kāi)啟了Geth的GraphQL支持,就可以通過(guò)Geth預(yù)置的GraphQL瀏覽器來(lái)進(jìn)行測(cè)試,GraphQL服務(wù)默認(rèn)在8547端口監(jiān)聽(tīng),API訪問(wèn)路徑為/graphql??梢允褂萌缦耈RL訪問(wèn)Geth的GraphQL瀏覽器:
http://localhost:8547
界面如下所示,最左邊就是輸入的GraphQL查詢:
為了便于查看,這里列出左邊的GraphQL查詢語(yǔ)句:
{
logs(filter: {fromBlock: 0,
addresses: ["0xf105795bf5d1b1894e70bd04dc846898ab19fa62"],
topics: [["0x0f0c27adfd84b60b6f456b0e87cdccb1e5fb9603991588d87fa99f5b6b61e670"]]}
) {
transaction {
hash
from {
address
}
block{
number
timestamp
}
}
}
}你可以這樣理解上面的GraphQL語(yǔ)句:
查詢?nèi)罩緇ogs
查詢條件:使用filter對(duì)象指定
返回字段:transaction,其結(jié)構(gòu)如上所示
假設(shè)我們要查詢最新的10個(gè)區(qū)塊的礦工賬號(hào)以及這些賬號(hào)的余額,使用JSON-RPC的實(shí)現(xiàn)代碼如下:
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);
}我們需要進(jìn)行10次循環(huán),逐個(gè)查詢每個(gè)區(qū)塊的礦工賬號(hào)及其余額。在每次循環(huán)中,我們需要調(diào)用兩次RPC API,分別查詢區(qū)塊數(shù)據(jù)和賬戶余額,因此總共需要10*2 = 20 次調(diào)用。
下面是獲取同樣數(shù)據(jù)的GraphQL查詢:
{
blocks(from:lastBlock-10, to:lastBlock) {
number
miner{
address
balance
}
}
}你可以在Geth GraphQL瀏覽器中輸入并執(zhí)行上面的查詢語(yǔ)句。令人震驚的是,我們只進(jìn)行1次調(diào)用就完成了之前采用JSON-RPC時(shí)20次調(diào)用才完成的任務(wù)!
在Geth源代碼中,schema.go文件中包含了當(dāng)前的GraphQL語(yǔ)法支持。下表列出了Geth GraphQL目前的實(shí)現(xiàn)狀態(tài),其中簡(jiǎn)要說(shuō)明欄目描述了JSON-RPC對(duì)應(yīng)的Geth GraphQL語(yǔ)句:
| JSON-RPC | GraphQL狀態(tài) | 簡(jiǎn)要說(shuō)明 |
|---|---|---|
| eth_blockNumber | 已實(shí)現(xiàn) | { block { number } } |
| eth_call | 已實(shí)現(xiàn) | { call(data: { to: "0x...", data: "0x..." }) { data status gasUsed } } |
| eth_estimateGas | 已實(shí)現(xiàn) | { estimateGas(data: { to: "0x...", data: "0x..." }) } |
| eth_gasPrice | 已實(shí)現(xiàn) | { gasPrice } |
| eth_getBalance | 已實(shí)現(xiàn) | { account(address: "0x...") { balance } } |
| eth_getBlockByHash | 已實(shí)現(xiàn) | { block(hash: "0x...") { ... } } |
| eth_getBlockByNumber | 已實(shí)現(xiàn) | { block(number: 123) { ... } } |
| eth_getBlockTransactionCountByHash | 已實(shí)現(xiàn) | { block(hash: "0x...") { transactionCount } } |
| eth_getBlockTransactionCountByNumber | 已實(shí)現(xiàn) | { block(number: x) { transactionCounnt } } |
| eth_getCode | 已實(shí)現(xiàn) | { account(address: "0x...") { code } } |
| eth_getLogs | 已實(shí)現(xiàn) | { logs(filter: { ... }) { ... } } or { block(...) { logs(filter: { ... }) { ... } } } |
| eth_getStorageAt | 已實(shí)現(xiàn) | { account(address: "0x...") { storage(slot: "0x...") } } |
| eth_getTransactionByBlockHashAndIndex | 已實(shí)現(xiàn) | { block(hash: "0x...") { transactionAt(index: x) { ... } } } |
| eth_getTransactionByBlockNumberAndIndex | 已實(shí)現(xiàn) | { block(number: n) { transactionAt(index: x) { ... } } } |
| eth_getTransactionByHash | 已實(shí)現(xiàn) | { transaction(hash: "0x...") { ... } } |
| eth_getTransactionCount | 已實(shí)現(xiàn) | { account(address: "0x...") { transactionCount } } |
| eth_getTransactionReceipt | 已實(shí)現(xiàn) | { transaction(hash: "0x...") { ... } } |
| eth_getUncleByBlockHashAndIndex | 已實(shí)現(xiàn) | { block(hash: "0x...") { ommerAt(index: x) { ... } } } |
| eth_getUncleByBlockNumberAndIndex | 已實(shí)現(xiàn) | { block(number: n) { ommerAt(index: x) { ... } } } |
| eth_getUncleCountByBlockHash | 已實(shí)現(xiàn) | { block(hash: "0x...") { ommerCount } } |
| eth_getUncleCountByBlockNumber | 已實(shí)現(xiàn) | { block(number: x) { ommerCount } } |
| eth_protocolVersion | 已實(shí)現(xiàn) | { protocolVersion } |
| eth_sendRawTransaction | 已實(shí)現(xiàn) | mutation { sendRawTransaction(data: data) } |
| eth_syncing | 已實(shí)現(xiàn) | { syncing { ... } } |
| eth_getCompilers | 未實(shí)現(xiàn) | JSON-RPC已廢棄編譯器功能 |
| eth_compileLLL | 未實(shí)現(xiàn) | JSON-RPC已廢棄編譯器功能 |
| eth_compileSolidity | 未實(shí)現(xiàn) | JSON-RPC已廢棄編譯器功能 |
| eth_compileSerpent | 未實(shí)現(xiàn) | JSON-RPC已廢棄編譯器功能 |
| eth_newFilter | 未實(shí)現(xiàn) | 過(guò)濾器功能可能在未來(lái)EIP中約定 |
| eth_newBlockFilter | 未實(shí)現(xiàn) | 過(guò)濾器功能可能在未來(lái)EIP中約定 |
| eth_newPendingTransactionFilter | 未實(shí)現(xiàn) | 過(guò)濾器功能可能在未來(lái)EIP中約定 |
| eth_uninstallFilter | 未實(shí)現(xiàn) | 過(guò)濾器功能可能在未來(lái)EIP中約定 |
| eth_getFilterChanges | 未實(shí)現(xiàn) | 過(guò)濾器功能可能在未來(lái)EIP中約定 |
| eth_getFilterLogs | 未實(shí)現(xiàn) | 過(guò)濾器功能可能在未來(lái)EIP中約定 |
| eth_accounts | 未實(shí)現(xiàn) | 賬戶功能不屬于節(jié)點(diǎn)核心API |
| eth_sign | 未實(shí)現(xiàn) | 賬戶功能不屬于節(jié)點(diǎn)核心API |
| eth_sendTransaction | 未實(shí)現(xiàn) | 賬戶功能不屬于節(jié)點(diǎn)核心API |
| eth_coinbase | 未實(shí)現(xiàn) | 挖礦相關(guān)功能將單獨(dú)定義 |
| eth_getWork | 未實(shí)現(xiàn) | 挖礦相關(guān)功能將單獨(dú)定義 |
| eth_hashRate | 未實(shí)現(xiàn) | 挖礦相關(guān)功能將單獨(dú)定義 |
| eth_mining | 未實(shí)現(xiàn) | 挖礦相關(guān)功能將單獨(dú)定義 |
| eth_submitHashrate | 未實(shí)現(xiàn) | 挖礦相關(guān)功能將單獨(dú)定義 |
| eth_submitWork | 未實(shí)現(xiàn) | 挖礦相關(guān)功能將單獨(dú)定義 |
看完上述內(nèi)容,你們掌握Geth 中如何使用GraphQL的方法了嗎?如果還想學(xué)到更多技能或想了解更多相關(guān)內(nèi)容,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝各位的閱讀!