如何在Golang中解码Tendermint的Base64编码交易(适配go-ethereum)
问题描述
调用Tendermint的/unconfirmed_txs接口获取待处理交易数据,得到如下Base64编码的交易列表:
CsQECpAECh8vZXRoZXJtaW50LmV2bS52MS5Nc2dFdGhlcmV1bVR4EuwDCqUDChovZXRoZX JtaW50LmV2bS52MS5MZWdhY3lUeBKGAwgCEg01MDc3OTQ2NTQxMzY0GPbZDCIqMHgxNDU4 NjNFYjQyQ2Y2Mjg0N0E2Q2E3ODRlNjQxNkMxNjgyYjFiMkFlKhU0MDAwMDAwMDAwMDAwMD AwMDAwMDAy5AF/82q1AAAAAAAAAAAAAAAAAAAAAAAAAAAAfvEOe1XqTQ9Ynq8AAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAK5tNYq1CNfuvRh/6NqF6Z zkg6PtAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMd7WUoAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAFx/ilcNV47YTmP9+nse5y3q4a4jAAAAAA AAAAAAAAAA3XPeoQq8K/+ZxgiC7FsrgbsdxbI6AVZCIBX0RwQW5LaC1LZG0W5mHid2Dsx/ 8FQn0r9IhHrWBAVxSiBLOhtk5OAdcCGBHaLUHK8rKEpL1bgtBRYxG/if+UesIxpCMHhlZG RjOWMxMWVkY2Q1NTY4YWZlOWM0Y2ZmYWNjMmU4ZjBhMGNlNjFiMDczMDE4OTQ1NzEyZDMw NTU3ZDBjMTUw+j8uCiwvZXRoZXJtaW50LmV2bS52MS5FeHRlbnNpb25PcHRpb25zRXRoZX JldW1UeBImEiQKHgoHYmFzZWNybxITMTA1NjgxMjA3ODI5NTU5Mjk1MhD22Qw=
请问如何在Golang中解码这些交易,获取完整交易数据(最好能转换为go-ethereum的Transaction结构体)?
经调研得知需要使用编解码器并注册相关函数,但未找到适配该场景的示例代码,也不清楚具体实现逻辑。
尝试使用github.com/calvinlauyh/cosmosutils库解码交易,却遇到错误:
unable to resolve type URL /ethermint.evm.v1.MsgEthereumTx: tx parse error
解决方案
错误原因
报错是因为cosmosutils库未注册ethermint.evm.v1.MsgEthereumTx这个Protobuf类型,编解码器无法识别该交易类型。这类交易是兼容EVM的Cosmos链(如Evmos)上的以太坊格式交易,必须用Ethermint官方的Protobuf定义和编解码器处理。
步骤1:引入依赖
在Go项目的go.mod中添加必要依赖(版本需匹配目标链的实际版本):
require ( github.com/cosmos/cosmos-sdk v0.47.0 github.com/evmos/ethermint v0.20.0 github.com/ethereum/go-ethereum v1.10.26 google.golang.org/protobuf v1.28.1 )
步骤2:解码Base64交易为Cosmos Tx结构体
先将Base64字符串解码为字节流,再用注册了Ethermint类型的Cosmos编解码器解码:
package main import ( "encoding/base64" "fmt" "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/codec/types" "github.com/evmos/ethermint/x/evm/types" ) func main() { // 移除原Base64字符串中的换行符 base64Tx := "CsQECpAECh8vZXRoZXJtaW50LmV2bS52MS5Nc2dFdGhlcmV1bVR4EuwDCqUDChovZXRoZXJtaW50LmV2bS52MS5MZWdhY3lUeBKGAwgCEg01MDc3OTQ2NTQxMzY0GPbZDCIqMHgxNDU4NjNFYjQyQ2Y2Mjg0N0E2Q2E3ODRlNjQxNkMxNjgyYjFiMkFlKhU0MDAwMDAwMDAwMDAwMDAwMDAwMDAy5AF/82q1AAAAAAAAAAAAAAAAAAAAAAAAAAAAfvEOe1XqTQ9Ynq8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAK5tNYq1CNfuvRh/6NqF6Zzkg6PtAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMd7WUoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAFx/ilcNV47YTmP9+nse5y3q4a4jAAAAAAAAAAAAAAAAAAAAAA3XPeoQq8K/+ZxgiC7FsrgbsdxbI6AVZCIBX0RwQW5LaC1LZG0W5mHid2Dsx/8FQn0r9IhHrWBAVxSiBLOhtk5OAdcCGBHaLUHK8rKEpL1bgtBRYxG/if+UesIxpCMHhlZGRjOWMxMWVkY2Q1NTY4YWZlOWM0Y2ZmYWNjMmU4ZjBhMGNlNjFiMDczMDE4OTQ1NzEyZDMwNTU3ZDBjMTUw+j8uCiwvZXRoZXJtaW50LmV2bS52MS5FeHRlbnNpb25PcHRpb25zRXRoZXJldW1UeBImEiQKHgoHYmFzZWNybxITMTA1NjgxMjA3ODI5NTU5Mjk1MhD22Qw=" // 1. Base64解码为字节 txBytes, err := base64.StdEncoding.DecodeString(base64Tx) if err != nil { panic(err) } // 2. 创建编解码器并注册Ethermint的EVM消息类型 interfaceRegistry := types.NewInterfaceRegistry() types.RegisterInterfaces(interfaceRegistry) cdc := codec.NewProtoCodec(interfaceRegistry) // 3. 解码为Cosmos SDK标准Tx结构体 var tx codec.Tx err = cdc.UnmarshalBinaryLengthPrefixed(txBytes, &tx) if err != nil { panic(err) } // 4. 断言为MsgEthereumTx类型(EVM交易通常仅包含该类型消息) msgTx, ok := tx.GetMsgs()[0].(*types.MsgEthereumTx) if !ok { panic("交易不是MsgEthereumTx类型") } fmt.Printf("解码后的EVM交易消息: %+v\n", msgTx) }
步骤3:转换为go-ethereum的Transaction结构体
MsgEthereumTx的Data字段是RLP编码的以太坊原生交易,直接解码即可得到go-ethereum的Transaction结构体:
import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/rlp" ) // 在步骤2解码得到msgTx后执行以下代码 var ethTx types.Transaction err = rlp.DecodeBytes(msgTx.Data, ðTx) if err != nil { panic(err) } fmt.Printf("转换后的以太坊交易: %+v\n", ethTx) fmt.Printf("交易哈希: %s\n", ethTx.Hash().Hex()) fmt.Printf("发送方地址: %s\n", ethTx.From().Hex())
注意事项
- 依赖版本必须与目标链版本一致,否则会出现类型不兼容问题。
- 若交易包含多个消息,需遍历
tx.GetMsgs()逐个处理,但EVM交易通常仅包含一个MsgEthereumTx。 - 部分链会对交易做额外封装(如签名信息),此时需先解码外层的
AuthInfo和TxBody,再处理内部消息。
内容的提问来源于stack exchange,提问作者Alexis Moret
相关产品推荐
相关产品推荐

