77ed4aa754
* Store eth tx index separately Closes: #1075 Solution: - run a optional indexer service - adapt the json-rpc to the more efficient query changelog changelog fix lint fix backward compatibility fix lint timeout better strconv fix linter fix package name add cli command to index old tx fix for loop indexer cmd don't have access to local rpc workaround exceed block gas limit situation add unit tests for indexer refactor polish the indexer module Update server/config/toml.go Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> improve comments share code between GetTxByEthHash and GetTxByIndex fix unit test Update server/indexer.go Co-authored-by: Freddy Caceres <facs95@gmail.com> * Apply suggestions from code review * test enable-indexer in integration test * fix go lint * address review suggestions * fix linter * address review suggestions - test indexer in backend unit test - add comments * fix build * fix test * service name Co-authored-by: Freddy Caceres <facs95@gmail.com> Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com>
173 lines
5.0 KiB
Go
173 lines
5.0 KiB
Go
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
|
|
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)
|
|
return nil, fmt.Errorf("transaction not included in block %v", blk.Block.Height)
|
|
}
|
|
|
|
var predecessors []*evmtypes.MsgEthereumTx
|
|
for _, txBz := range blk.Block.Txs[:transaction.TxIndex] {
|
|
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)
|
|
}
|
|
}
|
|
|
|
tx, err := b.clientCtx.TxConfig.TxDecoder()(blk.Block.Txs[transaction.TxIndex])
|
|
if err != nil {
|
|
b.logger.Debug("tx not found", "hash", hash)
|
|
return nil, err
|
|
}
|
|
|
|
// add predecessor messages in current cosmos tx
|
|
for i := 0; i < int(transaction.MsgIndex); i++ {
|
|
ethMsg, ok := tx.GetMsgs()[i].(*evmtypes.MsgEthereumTx)
|
|
if !ok {
|
|
continue
|
|
}
|
|
predecessors = append(predecessors, ethMsg)
|
|
}
|
|
|
|
ethMessage, ok := tx.GetMsgs()[transaction.MsgIndex].(*evmtypes.MsgEthereumTx)
|
|
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
|
|
}
|