go-ethereum开发之获取交易中的From字段
go-ethereum版本: v1.12.2
由于以太坊(ethereum)在不断的发展, 所以协议也在不断的改进,如果你看过《用Go来做以太坊开发》,你会发现其中关于查询交易的那一节中的获取交易的发送者From
这部分代码在最新版本中已经失效了,本文主要演示当前以太坊(ethereum)最新版本获取的获取方式。
也因为以太坊(ethereum)在不断的发展,所以其对应的客户端也在不断发展,ethereum的客户端有很多不同的语言实现,这里主要看go语言实现的客户端版本go-ethereum
获取区块
在获取交易前我们需要先获得区块(block)对象
package main
import (
"context"
"fmt"
"github.com/ethereum/go-ethereum/ethclient"
"log"
"math/big"
)
func main() {
ethereumNodeEndpoint := "https://cloudflare-eth.com"
client, err := ethclient.Dial(ethereumNodeEndpoint)
if err != nil {
log.Fatalf("连接以太坊节点[%s]失败: %s", ethereumNodeEndpoint, err)
}
ctx := context.TODO()
latestBlockNumber, err := client.BlockNumber(ctx)
if err != nil {
log.Fatal("获取当前最新区块高度失败: ", err)
}
fmt.Println("当前最新区块高度:", latestBlockNumber)
block, err := client.BlockByNumber(ctx, big.NewInt(int64(latestBlockNumber)))
if err != nil {
log.Fatal("获取当前最新区块失败: ", err)
}
fmt.Println("当前区块hash:", block.Hash().Hex())
fmt.Println("当前区块包含的交易笔数:", len(block.Transactions()))
}
输出如下:
当前最新区块高度: 18054718
当前区块hash: 0xe1122f17b2d7e9f717bdedb7062cf0a2f44b3199e702754b97f9a5832622a87a
当前区块包含的交易笔数: 226
获取交易
当我们获得了区块(block)对象之后就能获得其包含的交易列表, 这里在上面的代码基础上添加一下内容
tx := block.Transactions()[1]
fmt.Println("交易hash:", tx.Hash().Hex())
fmt.Println("交易的以太数量:", tx.Value().String(), "wei")
fmt.Println("Gas数量:", tx.Gas())
fmt.Println("Gas费:", tx.GasPrice().Uint64())
fmt.Println("接受者的Hash:", tx.To().Hex())
输出如下:
交易hash: 0xb9ac9b6a4eb857cfd410d0f3dc5604c5e52382e490acd00a14f9cf69c59391b0
交易的以太数量: 10000000000000000 wei
Gas数量: 274920
Gas费: 59204457483
接受者的Hash: 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D
获取交易的发送者From
如果你查看go-ethereum的源代码,你会发现交易对象并没有From
字段以及获取的方法,这是因为,交易中的发送者From
隐藏在交易的签名当中,而解析签名需要一些额外的计算资源,所以没有直接将From
字段解析出来,想要获得From
字段,我们需要构造一个签名的解析器。
signer := types.MakeSigner(params.MainnetChainConfig, block.Number(), block.Time())
from, err := signer.Sender(tx)
if err != nil {
log.Fatal("获取交易的发送者失败:", err)
}
fmt.Println("交易的发送者是:", from.Hex())
输出如下:
交易的发送者是: 0xCe1a934f373fb7355aFa2895d411A80e38384703
如果你仔细看代码的话,会发现第一个参数params.MainnetChainConfig
的名字是主网配置, 这个参数会基于区块高度来创建对应类型的Singer
, MakeSigner
的代码逻辑如下。
func MakeSigner(config *params.ChainConfig, blockNumber *big.Int, blockTime uint64) Signer {
var signer Signer
switch {
case config.IsCancun(blockNumber, blockTime):
signer = NewCancunSigner(config.ChainID)
case config.IsLondon(blockNumber):
signer = NewLondonSigner(config.ChainID)
case config.IsBerlin(blockNumber):
signer = NewEIP2930Signer(config.ChainID)
case config.IsEIP155(blockNumber):
signer = NewEIP155Signer(config.ChainID)
case config.IsHomestead(blockNumber):
signer = HomesteadSigner{}
default:
signer = FrontierSigner{}
}
return signer
}
上面的代码大致意思就是根据区块高度创建对应的签名器。
如果是自己的开发网络,那么传入这个参数也不能得正确的解析签名发送者From
, 这时候可以创建最新的签名对象,比如types.LatestSigner
。
完整代码
完整代码如下:
package main
import (
"context"
"fmt"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/params"
"log"
"math/big"
)
func main() {
ethereumNodeEndpoint := "https://cloudflare-eth.com"
client, err := ethclient.Dial(ethereumNodeEndpoint)
if err != nil {
log.Fatalf("连接以太坊节点[%s]失败: %s", ethereumNodeEndpoint, err)
}
ctx := context.TODO()
latestBlockNumber, err := client.BlockNumber(ctx)
if err != nil {
log.Fatal("获取当前最新区块高度失败: ", err)
}
fmt.Println("当前最新区块高度:", latestBlockNumber)
block, err := client.BlockByNumber(ctx, big.NewInt(int64(latestBlockNumber)))
if err != nil {
log.Fatal("获取当前最新区块失败: ", err)
}
fmt.Println("当前区块hash:", block.Hash().Hex())
fmt.Println("当前区块包含的交易笔数:", len(block.Transactions()))
tx := block.Transactions()[1]
fmt.Println("交易hash:", tx.Hash().Hex())
fmt.Println("交易的以太数量:", tx.Value().String(), "wei")
fmt.Println("Gas数量:", tx.Gas())
fmt.Println("Gas费:", tx.GasPrice().Uint64())
fmt.Println("接受者的Hash:", tx.To().Hex())
signer := types.MakeSigner(params.MainnetChainConfig, block.Number(), block.Time())
from, err := signer.Sender(tx)
if err != nil {
log.Fatal("获取交易的发送者失败:", err)
}
fmt.Println("交易的发送者是:", from.Hex())
}
参考文档:
- https://goethereumbook.org/zh/transaction-query/
- https://ethereum.org/en/developers/docs/transactions/
总结
随着以太坊(ethereum)的不断发展,交易的签名格式在不断的改变,为了获取签名中的From
字段我们需要使用对应的签名器来解析,当然了,除了从交易中获取From
, To
的字段,我们还可以从callTracer
中获得,callTracer
中还可以获得internal transaction
,这个以后有机会再说。