go语言从零实现以太坊客户端-JSON-RPC
- [1. JSON-RPC](#1. JSON-RPC)
-
- [1.1 JSON-RPC结构](#1.1 JSON-RPC结构)
- [1.2 常规的HTTP API](#1.2 常规的HTTP API)
- [1.3 基于Http的JSON-RPC](#1.3 基于Http的JSON-RPC)
- [2. 以太坊JSON-RPC API](#2. 以太坊JSON-RPC API)
- [3. JSON-RPC API分类](#3. JSON-RPC API分类)
- [4. JSON-RPC数据格式](#4. JSON-RPC数据格式)
- [5. Postman调用以太坊节点JSON-RPC API](#5. Postman调用以太坊节点JSON-RPC API)
-
- [5.1 查询区块链ID](#5.1 查询区块链ID)
-
- [5.1.1 基于hardhat node节点](#5.1.1 基于hardhat node节点)
- [5.1.2 基于sepolia节点](#5.1.2 基于sepolia节点)
- [5.2 查询区块链账户余额](#5.2 查询区块链账户余额)
-
- [5.2.1 基于hardhat node节点](#5.2.1 基于hardhat node节点)
- [5.2.2 基于sepolia节点](#5.2.2 基于sepolia节点)
- [5.3 查询合约代币名称](#5.3 查询合约代币名称)
-
- [5.3.1 基于hardhat node节点](#5.3.1 基于hardhat node节点)
- [5.3.2 基于sepolia节点](#5.3.2 基于sepolia节点)
- [5.4 查询合约中账户余额](#5.4 查询合约中账户余额)
-
- [5.4.1 基于hardhat node节点](#5.4.1 基于hardhat node节点)
- [5.4.2 基于sepolia节点](#5.4.2 基于sepolia节点)
- [5.5 区块链账户转账](#5.5 区块链账户转账)
-
- [5.5.1 基于hardhat node节点](#5.5.1 基于hardhat node节点)
- [5.5.2 基于sepolia节点](#5.5.2 基于sepolia节点)
- [5.6 合约中账户转账](#5.6 合约中账户转账)
-
- [5.6.1 基于hardhat node节点](#5.6.1 基于hardhat node节点)
- [5.6.2 基于sepolia节点](#5.6.2 基于sepolia节点)
- [6. 总结](#6. 总结)
1. JSON-RPC
RPC即远程过程调用,一般用于多进程通信。
JSON-RPC是基于JSON格式来进行RPC通信。
TCP通信传输16进制字节流,接收端要按通信协议对字节流逐段截取,将字节数组转换为实际含义值,再进行条件分支流转。通信协议较低层,数据交互复杂。
HTTP一般为前后端应用系统使用,HTTP协议承载SON格式数据,请求的url中包含了后端接口映射名,前端的请求可以直接访问到后端接口。应用层通信协议,数据交互简单。
JSON-RPC是应用层协议,可以基于HTTP、TCP等底层协议,来承载SON格式数据。以太坊是基于http post。请求的url中的后端接口并不是实际要调用的接口,而是一个通用接口。实际要调用的接口名称在JSON格式的数据中。
1.1 JSON-RPC结构
clike
// 请求
{
"jsonrpc": "2.0",
"method": "method_name",
"params": [...],
"id": 1
}
// 响应(成功)
{
"jsonrpc": "2.0",
"result": ...,
"id": 1
}
// 响应(错误)
{
"jsonrpc": "2.0",
"error": {
"code": -32601,
"message": "Method not found"
},
"id": 1
}
1.2 常规的HTTP API
前后端服务,路由到后端的query(string name, int age)方法,返回数据
clike
const url = 'https://api.example.com/query';
const data = {
name: '张三',
age: 25,
};
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data)
});
1.3 基于Http的JSON-RPC
比如要查询账户余额
(1) http请求路由到后端的query(Data data)方法
(2) 解析data结构,method: 'eth_getBalance'表示要调用的方法是eth_getBalance,方法参数是[address, 'latest']
(3)调用eth_getBalance(address, 'latest')
clike
const url = 'https://api.example.com/query';
const data = {
jsonrpc: '2.0',
method: 'eth_getBalance',
params: [address, 'latest'],
id: 1
};
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data)
});
2. 以太坊JSON-RPC API

web3.js、Go-Ethereum等库使用不同编程语言实现的JSON-RPC规范。
3. JSON-RPC API分类
操作对象区分为区块链上数据、合约数据。
操作方式区分为读操作、写操作。
分为四种:
(1)读区块链数据
(2)写区块链数据
(3)读合约数据
(4)写合约数据
读操作不改变区块链\合约状态,所以不需要签名等操作,JSON数据格式简单,相当于明文传输。
写操作会改变区块链\合约状态,需要签名等操作,JSON数据格式复杂。
4. JSON-RPC数据格式
以太坊使用的JSON-RPC对params有如下规定:
未格式化的字节数组和数量。 两者都使用十六进制编码传递,但对格式化有不同的要求。
数量
当对数量(整数、编号)进行编码时:编码为十六进制(以"0x"为前缀),最紧凑的表示方法。
0 应表示为"0x0"
以下是一些示例:
0x41(十进制中是 65)
0x400(十进制中是 1024)
错误:0x(后面至少有一位,0 是"0x0")
错误:0x0400(不允许有前导零)
错误:ff(必须有前缀 0x)
当对无格式数据(字节数组、帐户地址、哈希、字节码数组)进行编码时:编码为十六进制,以"0x"为前缀,每字节两个十六进制数字。
以下是一些示例:
0x41(大小为 1,"A")
0x004200(大小为 3,"0B0")
0x(大小为 0,"")
错误:0xf0f0f(位数必须是偶数)
错误:004200(必须以 0x 为前缀)
5. Postman调用以太坊节点JSON-RPC API
5.1 查询区块链ID

5.1.1 基于hardhat node节点
clike
method为eth_chainId
params为空数组
即调用eth_chainId()接口
返回16进制字符串"0x539",即1337

5.1.2 基于sepolia节点
返回16进制字符串"0xaa36a7",即11155111

5.2 查询区块链账户余额

5.2.1 基于hardhat node节点
clike
method为eth_getBalance
params为[账户地址, 区块参数]
即调用eth_getBalance(address)接口
返回16进制字符串

5.2.2 基于sepolia节点

5.3 查询合约代币名称

5.3.1 基于hardhat node节点
clike
method为eth_call
params为
[
{
合约地址,
合约方法和参数
},
区块参数
]
即调用eth_getBalance(address)接口
合约地址:"0x5FbDB2315678afecb367f032d93F642f64180aa3"
合约方法:name()。keccak256编码函数name(),取前四字节0x06fdde03
返回16进制字符串:
// 0x
// 0000000000000000000000000000000000000000000000000000000000000020 // 偏移量:32字节
// 0000000000000000000000000000000000000000000000000000000000000007 // 长度:7字节
// 4d79546f6b656e00000000000000000000000000000000000000000000000000 // 数据:"MyToken"

调用

5.3.2 基于sepolia节点

5.4 查询合约中账户余额
5.4.1 基于hardhat node节点
clike
method为eth_call
params为[
{
合约地址,
合约方法和参数
},
区块参数]
即调用balanceOf(address)接口
合约地址:"0x5FbDB2315678afecb367f032d93F642f64180aa3"
合约方法:balanceOf(address)。keccak256编码函数balanceOf(address),取前四字节70a08231
方法参数:address为"0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266",去掉0x,补齐64位为000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb92266。
合约方法和方法参数合并为0x70a08231000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb92266
返回16进制字符串:
// 0x
// 00000000000000000000000000000000000000000000003635c9adc5dea00000 // 数据1000000000000000000000


5.4.2 基于sepolia节点
同上
5.5 区块链账户转账

5.5.1 基于hardhat node节点
clike
method为eth_sendTransaction
params为[
{
转账账户地址,
接收账户地址,
金额
}
]
即调用eth_sendTransaction(address,address,uint64)接口
转账账户地址:"0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266"
接收账户地址:"0x70997970C51812dc3A010C7d01b50e0d17dc79C8"
金额:"0x5F5E100"
返回16进制字符串:
// 0xf7d34f6bc376d100540c4ababe71a01fcaa8a30da7d3c965cb26af95f8a067f9 // 交易哈希

5.5.2 基于sepolia节点
不支持eth_sendTransaction,sepolia网络不持有账户私钥,无法解析出账户地址
需要用eth_sendRawTransaction

5.6 合约中账户转账
5.6.1 基于hardhat node节点
clike
method为eth_sendTransaction
params为[
{
合约地址,
合约方法和参数
}
]
即调用transfer(address,uint256)接口
合约地址:"0x5FbDB2315678afecb367f032d93F642f64180aa3"
合约方法:transfer(address,uint256)。keccak256编码函数transfer(address,uint256),取前四字节a9059cbb
方法参数:
address为"0x70997970C51812dc3A010C7d01b50e0d17dc79C8",去掉0x,补齐64位为00000000000000000000000070997970c51812dc3a010c7d01b50e0d17dc79c8。
value为de0b6b3a7640000(十进制1000000000000000000),补齐64位为0000000000000000000000000000000000000000000000000de0b6b3a7640000
合约方法和方法参数合并为0xa9059cbb00000000000000000000000070997970c51812dc3a010c7d01b50e0d17dc79c80000000000000000000000000000000000000000000000000de0b6b3a7640000
返回16进制字符串:
// "0x1502a7ecf417dc760444dd32e888ab051cf8815669ebf10cf0a9b9a809ed0610" // 交易哈希


5.6.2 基于sepolia节点
不支持eth_sendTransaction,sepolia网络不持有账户私钥,无法解析出账户地址
需要用eth_sendRawTransaction

6. 总结
