laconicd-deprecated/rpc/backend/tracing.go
Vladislav Varadinov 9e5f4aaa73
tests: Add unit tests for rpc client endpoints (#1409)
* test: add preliminary unit tests and additional mocks for chain_info, account_info and filters

* tests: added additional mocked client calls

* tests: bumped coverage of call_tx to 56% and chain_info to 77%

* tests: bumped call_tx coverage to 70.2% and added additional mock client calls

* tests: tx_info preliminary tests added for debugging.

* tests: added test coverage for sign_tx and additional mocks

* tests: tx_info test coverage bumped to 60.3%

* test: coverage for tracing_tests now at 72%

* tests: added fee makert query client mocks and bumped chain_info to 87.6% coverage.

* tests: failing Cosmos auth module account query.

* tests: added FeeMarket Params mock to call_tx_test

* cleanup some unused code

* tests: added helper function to test suite for signing a Tx and bumped coverage of tx_info to 71.2%

* test: commented GetAccount error case and bumped chain_info to 90.3% coverage

* test: cleanup of tests in node_info, sign_tx and account_info

* Clean up print

Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com>

* Apply suggestions from code review

* fix import issues

Co-authored-by: Vladislav Varadinov <vlad@evmos.org>
Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com>
Co-authored-by: Freddy Caceres <facs95@gmail.com>
2022-11-17 01:35:15 +00:00

179 lines
5.2 KiB
Go

package backend
import (
"encoding/json"
"fmt"
sdk "github.com/cosmos/cosmos-sdk/types"
"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.TendermintBlockByNumber(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),
ProposerAddress: sdk.ConsAddress(blk.Block.ProposerAddress),
}
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),
ProposerAddress: sdk.ConsAddress(block.Block.ProposerAddress),
}
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
}