使用 Go 与以太坊区块链交互全攻略(一):实战入门

·

前言:为什么要用 Go 驱动以太坊?

以太坊区块链开发 遇到 高性能需求 时,JavaScript 往往显得力不从心。我最近的项目也面临同样困境:原来用 web3.js 实现的功能在低并发测试中还游刃有余,一旦流量上来,事件监听、批量签名、快速确认就出现瓶颈。于是,我将目光投向 Go,以求在读写高频、交易并发、延迟敏感等场景里获得更高吞吐与更低内存占用。

本文将通过 go-ethereum 原生库,手把手教你用 Golang 与 以太坊网络 JSON-RPC API 直接交互。示例直给、无需花哨框架,10 分钟就能跑通「查询区块高度 → 本地私钥签 → 链上广播 → 异步监控」完整链路。

假设你已装 Go 1.20+,对以太坊地址、Gas、私钥概念有基础。环境配置就留给搜索引擎啦,咱们直奔主题。

创建客户端:两行代码连入节点

go-ethereum 提供两种姿势快速接入:

方法一:直接 ethclient.Dial

client, err := ethclient.Dial("https://mainnet.infura.io/v3/YOUR_PROJECT_ID")

缺点:ethclient.Client 只封装常用接口,部分冷门 JSON-RPC 方法掉链子。

方法二:rpc + ethclient 组合拳

func Connect(host string) (*Client, error) {
    rpcClient, err := rpc.Dial(host)
    if err != nil {
        return nil, err
    }
    ethClient := ethclient.NewClient(rpcClient)
    return &Client{rpcClient, ethClient}, nil
}

👉 点此查看 ethclient 完整 API 列表,收藏不迷路


查询区块高度:迈出第一小步

项目诊断、系统告警、数据刷库,都要第一时间知道最新区块号。官方 ethclient 没有现成函数,自己封装:

func (c *Client) GetBlockNumber(ctx context.Context) (*big.Int, error) {
    var result hexutil.Big
    if err := c.rpcClient.CallContext(ctx, &result, "eth_blockNumber"); err != nil {
        return nil, err
    }
    return (*big.Int)(&result), nil
}

调用示例:

blockNum, _ := client.GetBlockNumber(context.TODO())
fmt.Printf("最新区块号 = %d\n", blockNum)

恭喜你,正式拿到链上时间戳!


发起交易:从构造到签名广播

交易结构定义

想向节点托管的私钥发起 eth_sendTransaction,得先构造兼容 JSON-RPC 的字段:

type Message struct {
    To       *common.Address `json:"to"`
    From     common.Address  `json:"from"`
    Value    string          `json:"value"`
    GasLimit string          `json:"gas"`
    GasPrice string          `json:"gasPrice"`
    Data     []byte          `json:"data"`
}

便捷构造器

big.Int 转成带 0x 的十六进制字符串:

func toHexInt(i *big.Int) string { return hexutil.EncodeBig(i) }

func NewMessage(from common.Address, to *common.Address,
    value *big.Int, gasLimit *big.Int, gasPrice *big.Int, data []byte) Message {

    return Message{
        From:     from,
        To:       to,
        Value:    toHexInt(value),
        GasLimit: toHexInt(gasLimit),
        GasPrice: toHexInt(gasPrice),
        Data:     data,
    }
}

发起 & 返回交易哈希

func (c *Client) SendTransaction(ctx context.Context, msg *Message) (common.Hash, error) {
    var txHash common.Hash
    err := c.rpcClient.CallContext(ctx, &txHash, "eth_sendTransaction", msg)
    return txHash, err
}

实战调用:

from := common.HexToAddress("0xYourFromAddress")
to := common.HexToAddress("0xYourToAddress")
amount := big.NewInt(1000000000000000000) // 1 ETH
gasLimit := big.NewInt(21000)
gasPrice, _ := client.EthClient.SuggestGasPrice(context.TODO()) // 动态查询

msg := NewMessage(from, &to, amount, gasLimit, gasPrice, []byte{})
txHash, _ := client.SendTransaction(context.TODO(), &msg)
fmt.Println("已广播,哈希:", txHash.Hex())

👉 想看完完整代码?这里还有并发、事件监听等你解锁


异步确认:让主线程不等人

监听收据

简单 for-loop 轮询,配合 Go Channel 不阻塞:

receiptChan := make(chan *types.Receipt, 1)

go func() {
    ticker := time.NewTicker(2 * time.Second)
    defer ticker.Stop()
    for {
        receipt, _ := client.EthClient.TransactionReceipt(context.TODO(), txHash)
        if receipt != nil {
            receiptChan <- receipt
            return
        }
        <-ticker.C
    }
}()

receipt := <-receiptChan
fmt.Printf("区块高度=%d,交易状态=%d(0成功)\n", receipt.BlockNumber.Uint64(), receipt.Status)

心得


FAQ:90% 初学者踩过的坑

Q1:私钥托管在节点会不会不安全?
A:任何“解锁账户”方式都有风险。生产环境推荐使用 离线签名 + sendRawTransaction,私钥永不出本地。

Q2:Gas 价格手填怕被抢矿被拒绝怎么办?
A:使用 SuggestGasPrice() + 乘以 1.1-1.3 系数,或订阅实时 Gas 估算服务。

Q3:一次发给多个地址必须用 for 循环吗?
A:目前单条交易只能单地址,批量场景可用 EIP-1153 多重调用合约 或打包后链下结算。

Q4:ethclient 与 go-web3/web3.go 优劣在哪?
A:后两者多年无人维护,对伦敦分叉后 EIP-1559 支持薄弱。go-ethereum 官方背书,资料最全。

Q5:本地测试能跳过同步主网吗?
A:必装 Ganache/HardhatAnvil 模拟器;对接 JSON-RPC 即可零成本调试。

Q6:如何验证交易真的进了不可篡改区?
A:拿到 receipt.BlockHash 后做 eth_getBlockByHash 校验即可;或者通过第三方区块浏览器两次校验。


写在最后

本篇只掀开 Go 与以太坊交互 的冰山一角:从区块号查询、私钥签名到交易监听,我们已能在不依赖 web3.js 的前提下完成所有核心动作。下一篇将深入 事件监听、过滤器、日志解析,并加入 离线签名 + EIP-1559,让你用 Go 打造真正的企业级区块链后端。

希望这份指南能帮你迈出坚实第一步。遇到问题欢迎在评论区留言,也可以在 GitHub repo 提 issue,一起把 以太坊 Go 语言开发 玩的更漂亮!