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())
}

参考文档:

总结

随着以太坊(ethereum)的不断发展,交易的签名格式在不断的改变,为了获取签名中的From字段我们需要使用对应的签名器来解析,当然了,除了从交易中获取From, To的字段,我们还可以从callTracer中获得,callTracer中还可以获得internal transaction,这个以后有机会再说。