golang实现以太坊协议官方Go语言插件库go-ethereum的使用
Golang实现以太坊协议官方Go语言插件库go-ethereum的使用
Go Ethereum介绍
Go Ethereum是以太坊协议的Golang执行层实现。它是进入以太坊网络(主网、测试网或私有网络)的主要入口点,可以作为全节点(默认)、归档节点(保留所有历史状态)或轻节点(实时检索数据)运行。
构建源代码
构建geth
需要Go(1.23或更高版本)和C编译器。安装依赖后运行:
make geth
或者构建完整的实用程序套件:
make all
可执行文件
go-ethereum项目提供了几个可执行文件:
命令 | 描述 |
---|---|
geth |
主要的以太坊CLI客户端 |
clef |
独立的签名工具,可作为geth 的后端签名器 |
devp2p |
与网络层节点交互的实用程序 |
abigen |
将以太坊合约定义转换为易于使用的Go包的源代码生成器 |
evm |
EVM的开发工具版本 |
rlpdump |
将二进制RLP转储转换为用户友好的层次表示的工具 |
运行geth
硬件要求
最低配置:
- 4核以上CPU
- 8GB内存
- 1TB可用存储空间
- 8Mbps下载带宽
推荐配置:
- 8核以上快速CPU
- 16GB以上内存
- 高性能SSD,至少1TB可用空间
- 25Mbps以上下载带宽
主网全节点
最常见的场景是用户希望简单地与以太坊网络交互:
geth console
Holesky测试网络节点
geth --holesky console
配置
可以通过配置文件传递配置:
geth --config /path/to/your_config.toml
Docker快速启动
docker run -d --name ethereum-node -v /Users/alice/ethereum:/root \
-p 8545:8545 -p 30303:30303 \
ethereum/client-go
编程接口
geth
内置支持基于JSON-RPC的API,可以通过HTTP、WebSockets和IPC暴露。
HTTP JSON-RPC API选项:
--http
启用HTTP-RPC服务器--http.addr
HTTP-RPC服务器监听接口--http.port
HTTP-RPC服务器监听端口--http.api
通过HTTP-RPC接口提供的API--http.corsdomain
接受跨域请求的域列表
私有网络
维护自己的私有网络需要更多配置。有三种解决方案:
- 使用模拟后端测试智能合约
- 使用开发模式进行单节点测试
- 使用Kurtosis设置多节点测试网络
示例代码
以下是一个使用go-ethereum连接以太坊节点并查询区块高度的简单示例:
package main
import (
"context"
"fmt"
"log"
"github.com/ethereum/go-ethereum/ethclient"
)
func main() {
// 连接到本地geth节点
client, err := ethclient.Dial("http://localhost:8545")
if err != nil {
log.Fatal(err)
}
// 获取最新区块号
blockNumber, err := client.BlockNumber(context.Background())
if err != nil {
log.Fatal(err)
}
fmt.Println("Latest block number:", blockNumber)
}
贡献
欢迎任何人为go-ethereum贡献代码。请遵循以下准则:
- 代码必须符合Go官方格式指南
- 代码必须文档化
- 提交消息应前缀修改的包名
许可证
go-ethereum库(即cmd
目录外的所有代码)在GNU Lesser General Public License v3.0下授权。go-ethereum二进制文件(即cmd
目录内的所有代码)在GNU General Public License v3.0下授权。
更多关于golang实现以太坊协议官方Go语言插件库go-ethereum的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html
更多关于golang实现以太坊协议官方Go语言插件库go-ethereum的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
go-ethereum 使用指南
go-ethereum (简称 Geth) 是以太坊协议的官方 Go 语言实现,它提供了与以太坊区块链交互的全套功能。下面我将介绍如何使用 go-ethereum 进行常见的区块链操作。
1. 安装 go-ethereum
首先需要安装 go-ethereum 库:
go get -u github.com/ethereum/go-ethereum
2. 连接以太坊节点
连接到本地节点
package main
import (
"context"
"fmt"
"log"
"github.com/ethereum/go-ethereum/ethclient"
)
func main() {
// 连接到本地节点 (默认IPC路径)
client, err := ethclient.Dial("/home/user/.ethereum/geth.ipc")
if err != nil {
log.Fatal(err)
}
// 或者连接到远程节点
// client, err := ethclient.Dial("https://mainnet.infura.io/v3/YOUR_PROJECT_ID")
// 获取最新区块号
header, err := client.HeaderByNumber(context.Background(), nil)
if err != nil {
log.Fatal(err)
}
fmt.Println("Latest block number:", header.Number.String())
}
3. 查询账户余额
func checkBalance() {
client, err := ethclient.Dial("https://mainnet.infura.io/v3/YOUR_PROJECT_ID")
if err != nil {
log.Fatal(err)
}
// 替换为你要查询的地址
account := common.HexToAddress("0x71c7656ec7ab88b098defb751b7401b5f6d8976f")
balance, err := client.BalanceAt(context.Background(), account, nil)
if err != nil {
log.Fatal(err)
}
// 将wei转换为ether
ethBalance := new(big.Float).Quo(new(big.Float).SetInt(balance), big.NewFloat(math.Pow10(18)))
fmt.Printf("Balance: %f ETH\n", ethBalance)
}
4. 发送交易
func sendTransaction() {
// 连接到节点
client, err := ethclient.Dial("https://ropsten.infura.io/v3/YOUR_PROJECT_ID")
if err != nil {
log.Fatal(err)
}
// 加载私钥 (实际应用中应从安全的地方获取)
privateKey, err := crypto.HexToECDSA("YOUR_PRIVATE_KEY")
if err != nil {
log.Fatal(err)
}
// 获取账户地址
publicKey := privateKey.Public()
publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey)
if !ok {
log.Fatal("cannot assert type: publicKey is not of type *ecdsa.PublicKey")
}
fromAddress := crypto.PubkeyToAddress(*publicKeyECDSA)
// 获取nonce
nonce, err := client.PendingNonceAt(context.Background(), fromAddress)
if err != nil {
log.Fatal(err)
}
// 设置转账金额 (1 ETH)
value := big.NewInt(1000000000000000000) // in wei (1 eth)
gasLimit := uint64(21000) // in units
gasPrice, err := client.SuggestGasPrice(context.Background())
if err != nil {
log.Fatal(err)
}
// 目标地址
toAddress := common.HexToAddress("0xRecipientAddress")
// 创建交易
tx := types.NewTransaction(nonce, toAddress, value, gasLimit, gasPrice, nil)
// 获取链ID
chainID, err := client.NetworkID(context.Background())
if err != nil {
log.Fatal(err)
}
// 签名交易
signedTx, err := types.SignTx(tx, types.NewEIP155Signer(chainID), privateKey)
if err != nil {
log.Fatal(err)
}
// 发送交易
err = client.SendTransaction(context.Background(), signedTx)
if err != nil {
log.Fatal(err)
}
fmt.Printf("tx sent: %s", signedTx.Hash().Hex())
}
5. 与智能合约交互
读取合约数据
func readContract() {
// 连接到节点
client, err := ethclient.Dial("https://mainnet.infura.io/v3/YOUR_PROJECT_ID")
if err != nil {
log.Fatal(err)
}
// 合约地址 (这里使用USDT合约作为示例)
contractAddress := common.HexToAddress("0xdac17f958d2ee523a2206206994597c13d831ec7")
// 合约ABI (简化版)
contractABI := `[{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"balance","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"}]`
// 创建合约实例
contract, err := abi.JSON(strings.NewReader(contractABI))
if err != nil {
log.Fatal(err)
}
// 要查询的地址
account := common.HexToAddress("0xAddressToQuery")
// 调用balanceOf方法
data, err := contract.Pack("balanceOf", account)
if err != nil {
log.Fatal(err)
}
// 执行call
msg := ethereum.CallMsg{
To: &contractAddress,
Data: data,
}
result, err := client.CallContract(context.Background(), msg, nil)
if err != nil {
log.Fatal(err)
}
// 解析结果
var balance *big.Int
err = contract.UnpackIntoInterface(&balance, "balanceOf", result)
if err != nil {
log.Fatal(err)
}
// USDT有6位小数
usdtBalance := new(big.Float).Quo(new(big.Float).SetInt(balance), big.NewFloat(math.Pow10(6)))
fmt.Printf("USDT Balance: %f\n", usdtBalance)
}
6. 监听新区块和事件
func watchBlocks() {
client, err := ethclient.Dial("wss://mainnet.infura.io/ws/v3/YOUR_PROJECT_ID")
if err != nil {
log.Fatal(err)
}
headers := make(chan *types.Header)
sub, err := client.SubscribeNewHead(context.Background(), headers)
if err != nil {
log.Fatal(err)
}
for {
select {
case err := <-sub.Err():
log.Fatal(err)
case header := <-headers:
fmt.Println("New block:", header.Number.String())
// 获取完整区块信息
block, err := client.BlockByHash(context.Background(), header.Hash())
if err != nil {
log.Fatal(err)
}
fmt.Println("Block hash:", block.Hash().Hex())
fmt.Println("Block transactions:", len(block.Transactions()))
}
}
}
7. 部署智能合约
func deployContract() {
// 连接到测试网络
client, err := ethclient.Dial("https://ropsten.infura.io/v3/YOUR_PROJECT_ID")
if err != nil {
log.Fatal(err)
}
// 加载私钥
privateKey, err := crypto.HexToECDSA("YOUR_PRIVATE_KEY")
if err != nil {
log.Fatal(err)
}
// 获取账户地址和nonce
publicKey := privateKey.Public()
publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey)
if !ok {
log.Fatal("cannot assert type: publicKey is not of type *ecdsa.PublicKey")
}
fromAddress := crypto.PubkeyToAddress(*publicKeyECDSA)
nonce, err := client.PendingNonceAt(context.Background(), fromAddress)
if err != nil {
log.Fatal(err)
}
// 设置gas价格和限制
gasPrice, err := client.SuggestGasPrice(context.Background())
if err != nil {
log.Fatal(err)
}
auth := bind.NewKeyedTransactor(privateKey)
auth.Nonce = big.NewInt(int64(nonce))
auth.Value = big.NewInt(0) // in wei
auth.GasLimit = uint64(300000) // in units
auth.GasPrice = gasPrice
// 编译合约 (这里使用简单示例)
// 实际项目中应该使用solc编译合约获取bytecode和abi
contractBytecode := "0x606060...合约字节码..."
contractABI := `[{"constant":false,"inputs":[{"name":"x","type":"uint256"}],"name":"set","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"get","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"_x","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"}]`
// 解析ABI
parsedABI, err := abi.JSON(strings.NewReader(contractABI))
if err != nil {
log.Fatal(err)
}
// 部署合约
address, tx, _, err := bind.DeployContract(auth, parsedABI, common.FromHex(contractBytecode), client, big.NewInt(123)) // 123是构造函数参数
if err != nil {
log.Fatal(err)
}
fmt.Println("Contract deployed at:", address.Hex())
fmt.Println("Transaction hash:", tx.Hash().Hex())
}
注意事项
- 私钥管理:实际应用中不应将私钥硬编码在代码中,应使用安全的存储方式
- 错误处理:生产代码中需要更完善的错误处理
- 网络选择:开发时建议使用测试网络(Ropsten, Rinkeby等)而非主网
- 资源管理:记得关闭连接和释放资源
- 异步处理:大量数据查询时考虑使用goroutine提高效率
go-ethereum 提供了丰富的功能,以上只是基础用法。根据你的具体需求,可以进一步探索其文档和源码实现更复杂的功能。