|
| 1 | +# JSON-RPC |
| 2 | + |
| 3 | +JSON-RPC 规范是一种基于[OpenRPC](https://open-rpc.org/getting-started)的、使用 JSON 编码的远程过程调用协议。它允许在服务器上远程调用函数,并返回结果。 |
| 4 | + |
| 5 | +它是执行 API 规范的一部分,该规范提供了一套与以太坊区块链交互的方法。 |
| 6 | + |
| 7 | +更为人所熟知的是,该规范阐述了用户如何通过客户端与网络进行交互,以及共识层(CL)与执行层(EL)如何通过引擎 API 相互作用的方式。 |
| 8 | + |
| 9 | +本节将详细介绍 JSON-RPC 方法。 |
| 10 | + |
| 11 | +## API 规范 |
| 12 | + |
| 13 | +JSON-RPC 方法按照指定为方法前缀的命名空间进行分组。尽管它们各有不同的用途,但所有方法都共享一个通用结构,并且在所有实现中必须表现出相同的行为: |
| 14 | + |
| 15 | +```json |
| 16 | +{ |
| 17 | + "id": 1, |
| 18 | + "jsonrpc": "2.0", |
| 19 | + "method": "<prefix_methodName>", |
| 20 | + "params": [...] |
| 21 | +} |
| 22 | +``` |
| 23 | + |
| 24 | +其中: |
| 25 | + |
| 26 | +- `id`: 请求的唯一标识符。 |
| 27 | +- `jsonrpc`: JSON-RPC 协议的版本。 |
| 28 | +- `method`: 将要调用的方法。 |
| 29 | +- `params`: 方法的参数。如果该方法不需要任何参数,它可以是一个空数组。其他参数如果没有提供,可能会有默认值。 |
| 30 | + |
| 31 | +### 命名空间 |
| 32 | + |
| 33 | +每个方法由一个命名空间前缀和方法名称组成,二者之间用下划线分隔。 |
| 34 | + |
| 35 | +以太坊客户端必须实现规范所要求的基本 RPC 方法集,以便与区块链网络进行交互。此外,还有一些特定于客户端的方法,用于控制节点或实现额外的独特功能。请始终参阅客户端文档,查看可用的方法和命名空间。例如,请注意 [Geth](https://geth.ethereum.org/docs/interacting-with-geth/rpc) 和 [Reth](https://paradigmxyz.github.io/reth/jsonrpc/intro.html) 文档中不同命名空间的区别。 |
| 36 | + |
| 37 | +以下是一些常见命名空间的示例: |
| 38 | + |
| 39 | +| **命名空间** | **描述** | **敏感性** | |
| 40 | +| ------------ | ---------------------------------------------------------------- | ---------- | |
| 41 | +| eth | eth API 允许你与以太坊进行交互。 | 可能 | |
| 42 | +| web3 | web3 API 为 web3 客户端提供实用功能。 | 否 | |
| 43 | +| net | net API 提供节点网络信息访问能力。 | 否 | |
| 44 | +| txpool | txpool API 允许你检查交易池。 | 否 | |
| 45 | +| debug | debug API 提供多种方法来检查以太坊状态,包括 Geth 风格的追踪。 | 否 | |
| 46 | +| trace | trace API 提供多种方法来检查以太坊状态,包括 Parity 风格的追踪。 | 否 | |
| 47 | +| admin | admin API 允许你配置自己的节点。 | 是 | |
| 48 | +| rpc | rpc API 提供关于 RPC 服务器及其模块的信息。 | 否 | |
| 49 | + |
| 50 | +“敏感性”意味着接口可以用来设置节点,比如 _admin_,或访问存储在节点中的账户数据,就像 _eth_ 那样。 |
| 51 | + |
| 52 | +现在,让我们来看看一些方法,了解它们是如何构建的以及它们的作用: |
| 53 | + |
| 54 | +#### Eth |
| 55 | + |
| 56 | +Eth 可能是最常用的命名空间,它提供了对以太坊网络的基本访问,例如,钱包需要使用它来读取余额和创建交易。 |
| 57 | +这里只列出了一些方法,完整的列表可以在 [Ethereum JSON-RPC specification](https://ethereum.github.io/execution-apis/api-documentation/) 中找到。 |
| 58 | + |
| 59 | +| **方法** | **参数** | **描述** | |
| 60 | +| ------------------------------------ | :-----------------------------: | ------------------------------------------------------------------------------------------------------------------------------------------- | |
| 61 | +| eth_blockNumber | 无必须参数 | returns the number of the most recent block | |
| 62 | +| eth_call | transaction object | executes a new message call immediately without creating a transaction on the block chain | |
| 63 | +| eth_chainId | 无必须参数 | returns the current chain id | |
| 64 | +| eth_estimateGas | transaction object | makes a call or transaction, which won't be added to the blockchain and returns the used gas, which can be used for estimating the used gas | |
| 65 | +| eth_gasPrice | 无必须参数 | returns the current price per gas in wei | |
| 66 | +| eth_getBalance | address, block number | returns the balance of the account of the given address | |
| 67 | +| eth_getBlockByHash | block hash, full txs | returns information about a block by hash | |
| 68 | +| eth_getBlockByNumber | block number, full txs | returns information about a block by block number | |
| 69 | +| eth_getBlockTransactionCountByHash | block hash | returns the number of transactions in a block from a block matching the given block hash | |
| 70 | +| eth_getBlockTransactionCountByNumber | block number | returns the number of transactions in a block from a block matching the given block number | |
| 71 | +| eth_getCode | address, block number | returns code at a given address in the blockchain | |
| 72 | +| eth_getLogs | filter object | returns an array of all logs matching a given filter object | |
| 73 | +| eth_getStorageAt | address, position, block number | returns the value from a storage position at a given address | |
| 74 | + |
| 75 | +| **方法** | **参数** | **描述** | |
| 76 | +| ------------------------------------ | :-----------------------------: | ------------------------------------------------------------------------------ | |
| 77 | +| eth_blockNumber | 无必须参数 | 返回最新区块的编号 | |
| 78 | +| eth_call | transaction object | 立即执行一个新的消息调用,不在区块链上创建交易 | |
| 79 | +| eth_chainId | 无必须参数 | 返回当前链的 ID | |
| 80 | +| eth_estimateGas | transaction object | 执行一个调用或交易,不会添加到区块链上,并返回使用的 gas,可用于估算消耗的 gas | |
| 81 | +| eth_gasPrice | 无必须参数 | 返回当前每单位 gas 的价格,以 wei 为单位 | |
| 82 | +| eth_getBalance | address, block number | 返回给定地址的账户余额 | |
| 83 | +| eth_getBlockByHash | block hash, full txs | 通过区块哈希返回区块信息 | |
| 84 | +| eth_getBlockByNumber | block number, full txs | 通过区块编号返回区块信息 | |
| 85 | +| eth_getBlockTransactionCountByHash | block hash | 通过区块哈希返回指定区块的交易数量 | |
| 86 | +| eth_getBlockTransactionCountByNumber | block number | 通过区块编号返回指定区块的交易数量 | |
| 87 | +| eth_getCode | address, block number | 返回区块链中指定地址处的代码 | |
| 88 | +| eth_getLogs | filter object | 返回与给定过滤器对象匹配的所有日志的数组 | |
| 89 | +| eth_getStorageAt | address, position, block number | 返回指定存储位置的值 | |
| 90 | + |
| 91 | +#### Debug |
| 92 | + |
| 93 | +_debug_ 命名空间提供了一些方法来检查以太坊的状态。通过它可以直接访问原始数据,这对于某些用例(如区块浏览器或研究目的)可能是必需的。这些方法中的一些可能需要在节点上进行大量计算,而在非存档节点上请求历史状态通常是不可行的。因此,公共 RPC 的提供者通常会限制这个命名空间或只允许安全的方法。这些方法中有些可能需要在节点上进行大量的计算,而且在非存档节点上查询历史状态多数情况下是不可行的。因此,公共 RPC 的提供者通常对这一命名空间加以限制,或只允许使用安全的方法。 |
| 94 | + |
| 95 | +以下是调试方法的基本示例: |
| 96 | + |
| 97 | +| **方法** | **参数** | **描述** | |
| 98 | +| ------------------------ | :----------: | -------------------------------------- | |
| 99 | +| debug_getBadBlocks | 无必须参数 | 返回客户端最近看到的坏区块的数组 | |
| 100 | +| debug_getRawBlock | block_number | 返回一个 RLP 编码的区块 | |
| 101 | +| debug_getRawHeader | block_number | 返回一个 RLP 编码的头 | |
| 102 | +| debug_getRawReceipts | block_number | 返回一个 EIP-2718 二进制编码的收据数组 | |
| 103 | +| debug_getRawTransactions | tx_hash | 返回一个 EIP-2718 二进制编码的交易数组 | |
| 104 | + |
| 105 | +#### Engine |
| 106 | + |
| 107 | +[Engine API](https://hackmd.io/@danielrachi/engine_api) 与上述方法不同。客户端在一个不同的、经过认证的端点上提供 Engine API,而不是普通的 http JSON RPC,因为它不是面向用户的 API。它主要用于共识层与执行层客户端之间的连接,基本上是一个内部节点通信过程。客户端之间的通信涉及关于共识、分叉选择、区块验证等信息的交换: |
| 108 | + |
| 109 | +| **方法** | **参数** | **描述** | |
| 110 | +| ---------------------------------------- | :----------------------------------: | -------------------------------- | |
| 111 | +| engine_exchangeTransitionConfigurationV1 | Consensus client config | 交换客户端配置 | |
| 112 | +| engine_forkchoiceUpdatedV1\* | forkchoice_state, payload attributes | 更新分叉选择状态 | |
| 113 | +| engine_getPayloadBodiesByHashV1\* | block_hash (array) | 给定区块哈希返回对应的执行负载体 | |
| 114 | +| engine_getPayloadV1\* | forkchoice_state, payload attributes | 从负载构建过程中获取执行负载 | |
| 115 | +| debug_newPayloadV1\* | tx_hash | 返回执行负载验证 | |
| 116 | + |
| 117 | +那些标有星号(\*)的方法具有多个版本,[Ethereum JSON-RPC specification](https://ethereum.github.io/execution-apis/api-documentation/) 提供了详细的描述。 |
| 118 | + |
| 119 | +## 编码 |
| 120 | + |
| 121 | +JSON-RPC 方法的参数编码遵循十六进制编码的约定。 |
| 122 | + |
| 123 | +- 数量使用 "0x" 前缀表示为十六进制值。 |
| 124 | + - 例如,数字 65 表示为 "0x41"。 |
| 125 | + - 数字 0 表示为 "0x0"。 |
| 126 | + - 一些无效的用法包括 "0x" 和 "ff"。前者没有后续数字,后者没有以 "0x" 前缀。 |
| 127 | +- 未格式化的数据,如哈希值、账户地址或字节数组,也使用“0x”前缀进行十六进制编码。 |
| 128 | + - 例如:0x400(十进制中为 1014) |
| 129 | + - 一个无效的例子是 0x400,因为不允许前导零。 |
| 130 | + |
| 131 | +## 传输协议无关 |
| 132 | + |
| 133 | +值得一提的是,JSON-RPC 是传输协议无关的,这意味着它可以使用任何传输协议,如 HTTP、WebSockets (WSS),甚至进程间通信 (IPC)。传输协议之间的差异总结如下: |
| 134 | + |
| 135 | +- **HTTP** 传输提供单向的响应-请求模型,发送响应后连接会被关闭。 |
| 136 | +- **WSS** 是双向协议,这意味着连接会一直保持,直到节点或用户显式关闭。支持基于订阅的模型通信,如事件驱动交互。 |
| 137 | +- **IPC** 传输协议用于同一台机器上运行的进程之间的通信。它比 HTTP 和 WSS 更快,但不适合远程通信,例如,可以通过本地 JS 控制台使用。 |
| 138 | + |
| 139 | +## 工具使用 |
| 140 | + |
| 141 | +有多种方法可以使用 JSON-RPC 方法。其中一种是使用 `curl` 命令。例如,要获取最新的区块编号,可以使用以下命令: |
| 142 | + |
| 143 | +```bash |
| 144 | +curl <node-endpoint> \ |
| 145 | +-X POST \ |
| 146 | +-H "Content-Type: application/json" \ |
| 147 | +-d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' |
| 148 | +``` |
| 149 | + |
| 150 | +请注意,_params_ 字段为空,因为方法默认传递 "latest" 作为值。 |
| 151 | + |
| 152 | +另一种方法是使用 Javascript/TypeScript 中的 `axios` 库。例如,要获取地址余额,可以使用以下代码: |
| 153 | + |
| 154 | +```typescript |
| 155 | +import axios from "axios"; |
| 156 | + |
| 157 | +const node = "<node-endpoint>"; |
| 158 | +const address = "<address>"; |
| 159 | + |
| 160 | +const response = await axios.post(node, { |
| 161 | + jsonrpc: "2.0", |
| 162 | + method: "eth_getBalance", |
| 163 | + params: [address, "latest"], |
| 164 | + id: 1, |
| 165 | + headers: { |
| 166 | + "Content-Type": "application/json", |
| 167 | + }, |
| 168 | +}); |
| 169 | +``` |
| 170 | + |
| 171 | +如你所见,JSON-RPC 方法在 POST 请求中,参数在请求体中传递。 |
| 172 | +这是客户端和服务器之间使用 OSI 的应用层协议(HTTP 协议)交换数据的一种不同方式。 |
| 173 | + |
| 174 | +无论哪种方式,与以太坊网络交互的最常见方法是使用 web3 库,例如 web3py 用于 python 或 web3.js/ethers.js 用于 JS/TS: |
| 175 | + |
| 176 | +#### web3py |
| 177 | + |
| 178 | +```python |
| 179 | +from web3 import Web3 |
| 180 | + |
| 181 | +# Set up HTTPProvider |
| 182 | +w3 = Web3(Web3.HTTPProvider('http://localhost:8545')) |
| 183 | + |
| 184 | +# API |
| 185 | +w3.eth.get_balance('0xaddress') |
| 186 | +``` |
| 187 | + |
| 188 | +#### ethers.js |
| 189 | + |
| 190 | +```typescript |
| 191 | +import { ethers } from "ethers"; |
| 192 | + |
| 193 | +const provider = new ethers.providers.JsonRpcProvider("http://localhost:8545"); |
| 194 | + |
| 195 | +await provider.getBlockNumber(); |
| 196 | +``` |
| 197 | + |
| 198 | +通常,所有的 web3 库都会封装 JSON-RPC 方法,提供一种更友好的方式与执行层进行交互。可以根据偏好的编程语言查看相关信息,因为不同语言的语法可能会有所不同。 |
| 199 | + |
| 200 | +### 进一步阅读 |
| 201 | + |
| 202 | +- [Ethereum JSON-RPC Specification](https://ethereum.github.io/execution-apis/api-documentation/) |
| 203 | +- [Execution API Specification](https://github.com/ethereum/execution-apis/tree/main) |
| 204 | +- [JSON-RPC | Infura docs](https://docs.infura.io/api/networks/ethereum/json-rpc-methods) |
| 205 | +- [reth book | JSON-RPC](https://paradigmxyz.github.io/reth/jsonrpc/intro.html) |
| 206 | +- [OpenRPC](https://open-rpc.org/getting-started) |
0 commit comments