2022-08-09 05:52:28 +00:00
|
|
|
package backend
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
|
|
|
|
"github.com/ethereum/go-ethereum/common"
|
|
|
|
rpctypes "github.com/evmos/ethermint/rpc/types"
|
|
|
|
evmtypes "github.com/evmos/ethermint/x/evm/types"
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
tmrpctypes "github.com/tendermint/tendermint/rpc/core/types"
|
|
|
|
)
|
|
|
|
|
|
|
|
// TraceTransaction returns the structured logs created during the execution of EVM
|
|
|
|
// and returns them as a JSON object.
|
|
|
|
func (b *Backend) TraceTransaction(hash common.Hash, config *evmtypes.TraceConfig) (interface{}, error) {
|
|
|
|
// Get transaction by hash
|
|
|
|
transaction, err := b.GetTxByEthHash(hash)
|
|
|
|
if err != nil {
|
|
|
|
b.logger.Debug("tx not found", "hash", hash)
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// check if block number is 0
|
|
|
|
if transaction.Height == 0 {
|
|
|
|
return nil, errors.New("genesis is not traceable")
|
|
|
|
}
|
|
|
|
|
|
|
|
blk, err := b.GetTendermintBlockByNumber(rpctypes.BlockNumber(transaction.Height))
|
|
|
|
if err != nil {
|
|
|
|
b.logger.Debug("block not found", "height", transaction.Height)
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// check tx index is not out of bound
|
2022-08-11 20:49:05 +00:00
|
|
|
if uint32(len(blk.Block.Txs)) < transaction.TxIndex {
|
|
|
|
b.logger.Debug("tx index out of bounds", "index", transaction.TxIndex, "hash", hash.String(), "height", blk.Block.Height)
|
2022-08-09 05:52:28 +00:00
|
|
|
return nil, fmt.Errorf("transaction not included in block %v", blk.Block.Height)
|
|
|
|
}
|
|
|
|
|
|
|
|
var predecessors []*evmtypes.MsgEthereumTx
|
2022-08-11 20:49:05 +00:00
|
|
|
for _, txBz := range blk.Block.Txs[:transaction.TxIndex] {
|
2022-08-09 05:52:28 +00:00
|
|
|
tx, err := b.clientCtx.TxConfig.TxDecoder()(txBz)
|
|
|
|
if err != nil {
|
|
|
|
b.logger.Debug("failed to decode transaction in block", "height", blk.Block.Height, "error", err.Error())
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
for _, msg := range tx.GetMsgs() {
|
|
|
|
ethMsg, ok := msg.(*evmtypes.MsgEthereumTx)
|
|
|
|
if !ok {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
predecessors = append(predecessors, ethMsg)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-11 20:49:05 +00:00
|
|
|
tx, err := b.clientCtx.TxConfig.TxDecoder()(blk.Block.Txs[transaction.TxIndex])
|
2022-08-09 05:52:28 +00:00
|
|
|
if err != nil {
|
|
|
|
b.logger.Debug("tx not found", "hash", hash)
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// add predecessor messages in current cosmos tx
|
2022-08-11 20:49:05 +00:00
|
|
|
for i := 0; i < int(transaction.MsgIndex); i++ {
|
2022-08-09 05:52:28 +00:00
|
|
|
ethMsg, ok := tx.GetMsgs()[i].(*evmtypes.MsgEthereumTx)
|
|
|
|
if !ok {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
predecessors = append(predecessors, ethMsg)
|
|
|
|
}
|
|
|
|
|
2022-08-11 20:49:05 +00:00
|
|
|
ethMessage, ok := tx.GetMsgs()[transaction.MsgIndex].(*evmtypes.MsgEthereumTx)
|
2022-08-09 05:52:28 +00:00
|
|
|
if !ok {
|
|
|
|
b.logger.Debug("invalid transaction type", "type", fmt.Sprintf("%T", tx))
|
|
|
|
return nil, fmt.Errorf("invalid transaction type %T", tx)
|
|
|
|
}
|
|
|
|
|
|
|
|
traceTxRequest := evmtypes.QueryTraceTxRequest{
|
|
|
|
Msg: ethMessage,
|
|
|
|
Predecessors: predecessors,
|
|
|
|
BlockNumber: blk.Block.Height,
|
|
|
|
BlockTime: blk.Block.Time,
|
|
|
|
BlockHash: common.Bytes2Hex(blk.BlockID.Hash),
|
|
|
|
}
|
|
|
|
|
|
|
|
if config != nil {
|
|
|
|
traceTxRequest.TraceConfig = config
|
|
|
|
}
|
|
|
|
|
|
|
|
// minus one to get the context of block beginning
|
|
|
|
contextHeight := transaction.Height - 1
|
|
|
|
if contextHeight < 1 {
|
|
|
|
// 0 is a special value in `ContextWithHeight`
|
|
|
|
contextHeight = 1
|
|
|
|
}
|
|
|
|
traceResult, err := b.queryClient.TraceTx(rpctypes.ContextWithHeight(contextHeight), &traceTxRequest)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Response format is unknown due to custom tracer config param
|
|
|
|
// More information can be found here https://geth.ethereum.org/docs/dapp/tracing-filtered
|
|
|
|
var decodedResult interface{}
|
|
|
|
err = json.Unmarshal(traceResult.Data, &decodedResult)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return decodedResult, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// traceBlock configures a new tracer according to the provided configuration, and
|
|
|
|
// executes all the transactions contained within. The return value will be one item
|
|
|
|
// per transaction, dependent on the requested tracer.
|
|
|
|
func (b *Backend) TraceBlock(height rpctypes.BlockNumber, config *evmtypes.TraceConfig, block *tmrpctypes.ResultBlock) ([]*evmtypes.TxTraceResult, error) {
|
|
|
|
txs := block.Block.Txs
|
|
|
|
txsLength := len(txs)
|
|
|
|
|
|
|
|
if txsLength == 0 {
|
|
|
|
// If there are no transactions return empty array
|
|
|
|
return []*evmtypes.TxTraceResult{}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
txDecoder := b.clientCtx.TxConfig.TxDecoder()
|
|
|
|
|
|
|
|
var txsMessages []*evmtypes.MsgEthereumTx
|
|
|
|
for i, tx := range txs {
|
|
|
|
decodedTx, err := txDecoder(tx)
|
|
|
|
if err != nil {
|
|
|
|
b.logger.Error("failed to decode transaction", "hash", txs[i].Hash(), "error", err.Error())
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, msg := range decodedTx.GetMsgs() {
|
|
|
|
ethMessage, ok := msg.(*evmtypes.MsgEthereumTx)
|
|
|
|
if !ok {
|
|
|
|
// Just considers Ethereum transactions
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
txsMessages = append(txsMessages, ethMessage)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// minus one to get the context at the beginning of the block
|
|
|
|
contextHeight := height - 1
|
|
|
|
if contextHeight < 1 {
|
|
|
|
// 0 is a special value for `ContextWithHeight`.
|
|
|
|
contextHeight = 1
|
|
|
|
}
|
|
|
|
ctxWithHeight := rpctypes.ContextWithHeight(int64(contextHeight))
|
|
|
|
|
|
|
|
traceBlockRequest := &evmtypes.QueryTraceBlockRequest{
|
|
|
|
Txs: txsMessages,
|
|
|
|
TraceConfig: config,
|
|
|
|
BlockNumber: block.Block.Height,
|
|
|
|
BlockTime: block.Block.Time,
|
|
|
|
BlockHash: common.Bytes2Hex(block.BlockID.Hash),
|
|
|
|
}
|
|
|
|
|
|
|
|
res, err := b.queryClient.TraceBlock(ctxWithHeight, traceBlockRequest)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
decodedResults := make([]*evmtypes.TxTraceResult, txsLength)
|
|
|
|
if err := json.Unmarshal(res.Data, &decodedResults); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return decodedResults, nil
|
|
|
|
}
|