您好,登錄后才能下訂單哦!
Geth 中如何使用GraphQL,相信很多沒(méi)有經(jīng)驗(yàn)的人對(duì)此束手無(wú)策,為此本文總結(jié)了問(wèn)題出現(xiàn)的原因和解決方法,通過(guò)這篇文章希望你能解決這個(gè)問(wèn)題。
讓我們先了解下經(jīng)典的JSON-RPC API存在什么問(wèn)題。
正如其名稱(chēng)所示,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)題在于它不支持靈活的查詢(xún),這會(huì)導(dǎo)致計(jì)算資源和數(shù)據(jù)傳輸方面的雙重浪費(fèi):
即使用戶(hù)只需要部分?jǐn)?shù)據(jù),RPC調(diào)用也需要返回大量數(shù)據(jù),造成帶寬的 浪費(fèi)。例如你調(diào)用eth_getBlock的目的只是獲取礦工地址,但是它依然 需要返回完整的區(qū)塊數(shù)據(jù)。
如果用戶(hù)重復(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接口輪詢(xún)某個(gè) 交易的收據(jù)時(shí)。
對(duì)于以太坊的JSON-RPC API,由于區(qū)塊鏈數(shù)據(jù)的結(jié)構(gòu)特點(diǎn),上面的問(wèn)題被進(jìn)一步放大了,多次執(zhí)行一個(gè)查詢(xún)(例如eth_getBalance)需要確保查詢(xún)是同一世界狀態(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)題而提出的一種新的查詢(xún)語(yǔ)言。GraphQL將數(shù)據(jù)對(duì)象關(guān)系 映射到一個(gè)圖(Graph),并設(shè)計(jì)了一種查詢(xún)語(yǔ)言(Query Language)來(lái)遍歷圖中關(guān)系 —— 這也是GraphQL 名稱(chēng)的來(lái)源。
這篇文章非常適合不熟悉GraphQL的開(kāi)發(fā)者快速理解GraphQL的基本概念,以及如何利用NodeJS技術(shù)棧實(shí)現(xiàn)GraphQL的服務(wù)端與客戶(hù)端:從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訪(fǎng)問(wèn)路徑為/graphql
??梢允褂萌缦耈RL訪(fǎng)問(wèn)Geth的GraphQL瀏覽器:
http://localhost:8547
界面如下所示,最左邊就是輸入的GraphQL查詢(xún):
為了便于查看,這里列出左邊的GraphQL查詢(xún)語(yǔ)句:
{ logs(filter: {fromBlock: 0, addresses: ["0xf105795bf5d1b1894e70bd04dc846898ab19fa62"], topics: [["0x0f0c27adfd84b60b6f456b0e87cdccb1e5fb9603991588d87fa99f5b6b61e670"]]} ) { transaction { hash from { address } block{ number timestamp } } } }
你可以這樣理解上面的GraphQL語(yǔ)句:
查詢(xún)?nèi)罩緇ogs
查詢(xún)條件:使用filter對(duì)象指定
返回字段:transaction,其結(jié)構(gòu)如上所示
假設(shè)我們要查詢(xún)最新的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è)查詢(xún)每個(gè)區(qū)塊的礦工賬號(hào)及其余額。在每次循環(huán)中,我們需要調(diào)用兩次RPC API,分別查詢(xún)區(qū)塊數(shù)據(jù)和賬戶(hù)余額,因此總共需要10*2 = 20 次調(diào)用。
下面是獲取同樣數(shù)據(jù)的GraphQL查詢(xún):
{ blocks(from:lastBlock-10, to:lastBlock) { number miner{ address balance } } }
你可以在Geth GraphQL瀏覽器中輸入并執(zhí)行上面的查詢(xún)語(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ǔ)句:
<table class="table table-striped"> <thead> <tr> <th>JSON-RPC</th> <th>GraphQL狀態(tài)</th> <th>簡(jiǎn)要說(shuō)明</th> </tr> </thead> <tbody> <tr> <td>eth_blockNumber</td> <td>已實(shí)現(xiàn)</td> <td><code class="highlighter-rouge">{ block { number } }</code></td> </tr> <tr> <td>eth_call</td> <td>已實(shí)現(xiàn)</td> <td><code class="highlighter-rouge">{ call(data: { to: "0x...", data: "0x..." }) { data status gasUsed } }</code></td> </tr> <tr> <td>eth_estimateGas</td> <td>已實(shí)現(xiàn)</td> <td><code class="highlighter-rouge">{ estimateGas(data: { to: "0x...", data: "0x..." }) }</code></td> </tr> <tr> <td>eth_gasPrice</td> <td>已實(shí)現(xiàn)</td> <td><code class="highlighter-rouge">{ gasPrice }</code></td> </tr> <tr> <td>eth_getBalance</td> <td>已實(shí)現(xiàn)</td> <td><code class="highlighter-rouge">{ account(address: "0x...") { balance } }</code></td> </tr> <tr> <td>eth_getBlockByHash</td> <td>已實(shí)現(xiàn)</td> <td><code class="highlighter-rouge">{ block(hash: "0x...") { ... } }</code></td> </tr> <tr> <td>eth_getBlockByNumber</td> <td>已實(shí)現(xiàn)</td> <td><code class="highlighter-rouge">{ block(number: 123) { ... } }</code></td> </tr> <tr> <td>eth_getBlockTransactionCountByHash</td> <td>已實(shí)現(xiàn)</td> <td><code class="highlighter-rouge">{ block(hash: "0x...") { transactionCount } }</code></td> </tr> <tr> <td>eth_getBlockTransactionCountByNumber</td> <td>已實(shí)現(xiàn)</td> <td><code class="highlighter-rouge">{ block(number: x) { transactionCounnt } }</code></td> </tr> <tr> <td>eth_getCode</td> <td>已實(shí)現(xiàn)</td> <td><code class="highlighter-rouge">{ account(address: "0x...") { code } }</code></td> </tr> <tr> <td>eth_getLogs</td> <td>已實(shí)現(xiàn)</td> <td><code class="highlighter-rouge">{ logs(filter: { ... }) { ... } }</code> or <code class="highlighter-rouge">{ block(...) { logs(filter: { ... }) { ... } } }</code></td> </tr> <tr> <td>eth_getStorageAt</td> <td>已實(shí)現(xiàn)</td> <td><code class="highlighter-rouge">{ account(address: "0x...") { storage(slot: "0x...") } }</code></td> </tr> <tr> <td>eth_getTransactionByBlockHashAndIndex</td> <td>已實(shí)現(xiàn)</td> <td><code class="highlighter-rouge">{ block(hash: "0x...") { transactionAt(index: x) { ... } } }</code></td> </tr> <tr> <td>eth_getTransactionByBlockNumberAndIndex</td> <td>已實(shí)現(xiàn)</td> <td><code class="highlighter-rouge">{ block(number: n) { transactionAt(index: x) { ... } } }</code></td> </tr> <tr> <td>eth_getTransactionByHash</td> <td>已實(shí)現(xiàn)</td> <td><code class="highlighter-rouge">{ transaction(hash: "0x...") { ... } }</code></td> </tr> <tr> <td>eth_getTransactionCount</td> <td>已實(shí)現(xiàn)</td> <td><code class="highlighter-rouge">{ account(address: "0x...") { transactionCount } }</code></td> </tr> <tr> <td>eth_getTransactionReceipt</td> <td>已實(shí)現(xiàn)</td> <td><code class="highlighter-rouge">{ transaction(hash: "0x...") { ... } }</code></td> </tr> <tr> <td>eth_getUncleByBlockHashAndIndex</td> <td>已實(shí)現(xiàn)</td> <td><code class="highlighter-rouge">{ block(hash: "0x...") { ommerAt(index: x) { ... } } }</code></td> </tr> <tr> <td>eth_getUncleByBlockNumberAndIndex</td> <td>已實(shí)現(xiàn)</td> <td><code class="highlighter-rouge">{ block(number: n) { ommerAt(index: x) { ... } } }</code></td> </tr> <tr> <td>eth_getUncleCountByBlockHash</td> <td>已實(shí)現(xiàn)</td> <td><code class="highlighter-rouge">{ block(hash: "0x...") { ommerCount } }</code></td> </tr> <tr> <td>eth_getUncleCountByBlockNumber</td> <td>已實(shí)現(xiàn)</td> <td><code class="highlighter-rouge">{ block(number: x) { ommerCount } }</code></td> </tr> <tr> <td>eth_protocolVersion</td> <td>已實(shí)現(xiàn)</td> <td><code class="highlighter-rouge">{ protocolVersion }</code></td> </tr> <tr> <td>eth_sendRawTransaction</td> <td>已實(shí)現(xiàn)</td> <td><code class="highlighter-rouge">mutation { sendRawTransaction(data: data) }</code></td> </tr> <tr> <td>eth_syncing</td> <td>已實(shí)現(xiàn)</td> <td><code class="highlighter-rouge">{ syncing { ... } }</code></td> </tr> <tr> <td>eth_getCompilers</td> <td>未實(shí)現(xiàn)</td> <td>JSON-RPC已廢棄編譯器功能</td> </tr> <tr> <td>eth_compileLLL</td> <td>未實(shí)現(xiàn)</td> <td>JSON-RPC已廢棄編譯器功能</td> </tr> <tr> <td>eth_compileSolidity</td> <td>未實(shí)現(xiàn)</td> <td>JSON-RPC已廢棄編譯器功能</td> </tr> <tr> <td>eth_compileSerpent</td> <td>未實(shí)現(xiàn)</td> <td>JSON-RPC已廢棄編譯器功能</td> </tr> <tr> <td>eth_newFilter</td> <td>未實(shí)現(xiàn)</td> <td>過(guò)濾器功能可能在未來(lái)EIP中約定</td> </tr> <tr> <td>eth_newBlockFilter</td> <td>未實(shí)現(xiàn)</td> <td>過(guò)濾器功能可能在未來(lái)EIP中約定</td> </tr> <tr> <td>eth_newPendingTransactionFilter</td> <td>未實(shí)現(xiàn)</td> <td>過(guò)濾器功能可能在未來(lái)EIP中約定</td> </tr> <tr> <td>eth_uninstallFilter</td> <td>未實(shí)現(xiàn)</td> <td>過(guò)濾器功能可能在未來(lái)EIP中約定</td> </tr> <tr> <td>eth_getFilterChanges</td> <td>未實(shí)現(xiàn)</td> <td>過(guò)濾器功能可能在未來(lái)EIP中約定</td> </tr> <tr> <td>eth_getFilterLogs</td> <td>未實(shí)現(xiàn)</td> <td>過(guò)濾器功能可能在未來(lái)EIP中約定</td> </tr> <tr> <td>eth_accounts</td> <td>未實(shí)現(xiàn)</td> <td>賬戶(hù)功能不屬于節(jié)點(diǎn)核心API</td> </tr> <tr> <td>eth_sign</td> <td>未實(shí)現(xiàn)</td> <td>賬戶(hù)功能不屬于節(jié)點(diǎn)核心API</td> </tr> <tr> <td>eth_sendTransaction</td> <td>未實(shí)現(xiàn)</td> <td>賬戶(hù)功能不屬于節(jié)點(diǎn)核心API</td> </tr> <tr> <td>eth_coinbase</td> <td>未實(shí)現(xiàn)</td> <td>挖礦相關(guān)功能將單獨(dú)定義</td> </tr> <tr> <td>eth_getWork</td> <td>未實(shí)現(xiàn)</td> <td>挖礦相關(guān)功能將單獨(dú)定義</td> </tr> <tr> <td>eth_hashRate</td> <td>未實(shí)現(xiàn)</td> <td>挖礦相關(guān)功能將單獨(dú)定義</td> </tr> <tr> <td>eth_mining</td> <td>未實(shí)現(xiàn)</td> <td>挖礦相關(guān)功能將單獨(dú)定義</td> </tr> <tr> <td>eth_submitHashrate</td> <td>未實(shí)現(xiàn)</td> <td>挖礦相關(guān)功能將單獨(dú)定義</td> </tr> <tr> <td>eth_submitWork</td> <td>未實(shí)現(xiàn)</td> <td>挖礦相關(guān)功能將單獨(dú)定義</td> </tr> </tbody> </table>
看完上述內(nèi)容,你們掌握Geth 中如何使用GraphQL的方法了嗎?如果還想學(xué)到更多技能或想了解更多相關(guān)內(nèi)容,歡迎關(guān)注億速云行業(yè)資訊頻道,感謝各位的閱讀!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。