diff --git a/rpc/backend/blocks.go b/rpc/backend/blocks.go index d69babbd5..7e2c43b9f 100644 --- a/rpc/backend/blocks.go +++ b/rpc/backend/blocks.go @@ -335,6 +335,31 @@ func (b *Backend) parseDerivedTxFromAdditionalFields( ethMsg.From = additional.Sender.Hex() return ethMsg } +func (b *Backend) parseDerivedTxFromAdditionalFieldsForTrace( + additional *rpctypes.TxResultAdditionalFields, +) *evmtypes.MsgEthereumTx { + recipient := additional.Recipient + t := ethtypes.NewTx(ðtypes.LegacyTx{ + Nonce: additional.Nonce, + Data: additional.Data, + Gas: additional.GasUsed, + To: &recipient, + GasPrice: nil, + Value: additional.Value, + V: big.NewInt(27), + R: big.NewInt(1), + S: big.NewInt(1), + }) + ethMsg := &evmtypes.MsgEthereumTx{} + err := ethMsg.FromEthereumTx(t) + if err != nil { + b.logger.Error("can not create eth msg", err.Error()) + return nil + } + ethMsg.Hash = additional.Hash.Hex() + ethMsg.From = additional.Sender.Hex() + return ethMsg +} // HeaderByNumber returns the block header identified by height. func (b *Backend) HeaderByNumber(blockNum rpctypes.BlockNumber) (*ethtypes.Header, error) { diff --git a/rpc/backend/tracing.go b/rpc/backend/tracing.go index 3788ff88d..2e068333a 100644 --- a/rpc/backend/tracing.go +++ b/rpc/backend/tracing.go @@ -18,7 +18,7 @@ import ( // 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) + transaction, additional, err := b.GetTxByEthHash(hash) if err != nil { b.logger.Debug("tx not found", "hash", hash) return nil, err @@ -46,19 +46,39 @@ func (b *Backend) TraceTransaction(hash common.Hash, config *evmtypes.TraceConfi } var predecessors []*evmtypes.MsgEthereumTx - for _, txBz := range blk.Block.Txs[:transaction.TxIndex] { - tx, err := b.clientCtx.TxConfig.TxDecoder()(txBz) + for i := 0; i < int(transaction.TxIndex); i++ { + _, txAdditional, err := b.GetTxByTxIndex(blk.Block.Height, uint(i)) if err != nil { - b.logger.Debug("failed to decode transaction in block", "height", blk.Block.Height, "error", err.Error()) + b.logger.Debug("failed to get tx by index", + "height", blk.Block.Height, + "index", i, + "error", err.Error()) continue } - for _, msg := range tx.GetMsgs() { - ethMsg, ok := msg.(*evmtypes.MsgEthereumTx) - if !ok { - continue + + if txAdditional != nil { + // Handle synthetic EVM transaction + ethMsg := b.parseDerivedTxFromAdditionalFieldsForTrace(txAdditional) + if ethMsg != nil { + predecessors = append(predecessors, ethMsg) } + continue + } - predecessors = append(predecessors, ethMsg) + // Fallback: decode as normal Cosmos tx + tx, err := b.clientCtx.TxConfig.TxDecoder()(blk.Block.Txs[i]) + if err != nil { + b.logger.Debug("failed to decode transaction in block", + "height", blk.Block.Height, + "index", i, + "error", err.Error()) + continue + } + + for _, msg := range tx.GetMsgs() { + if ethMsg, ok := msg.(*evmtypes.MsgEthereumTx); ok { + predecessors = append(predecessors, ethMsg) + } } } @@ -70,18 +90,43 @@ func (b *Backend) TraceTransaction(hash common.Hash, config *evmtypes.TraceConfi // add predecessor messages in current cosmos tx index := int(transaction.MsgIndex) // #nosec G115 + for i := 0; i < index; i++ { - ethMsg, ok := tx.GetMsgs()[i].(*evmtypes.MsgEthereumTx) - if !ok { + msg := tx.GetMsgs()[i] + // Check if it’s a normal Ethereum tx + if ethMsg, ok := msg.(*evmtypes.MsgEthereumTx); ok { + predecessors = append(predecessors, ethMsg) continue } - predecessors = append(predecessors, ethMsg) + // Fetch additional data for predecessors + _, txAdditional, err := b.GetTxByEthHashAndMsgIndex(hash, i) + if err != nil { + b.logger.Debug("failed to get tx additional info", "error", err.Error()) + continue + } + + if txAdditional != nil { + ethMsg := b.parseDerivedTxFromAdditionalFieldsForTrace(txAdditional) + if ethMsg != nil { + predecessors = append(predecessors, ethMsg) + } + } } + var ethMessage *evmtypes.MsgEthereumTx + var ok bool - 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) + if additional == nil { + ethMessage, ok = tx.GetMsgs()[transaction.MsgIndex].(*evmtypes.MsgEthereumTx) + if !ok { + b.logger.Debug("invalid transaction type", "type", fmt.Sprintf("%T", tx.GetMsgs()[transaction.MsgIndex])) + return nil, fmt.Errorf("invalid transaction type %T", tx.GetMsgs()[transaction.MsgIndex]) + } + } else { + ethMessage = b.parseDerivedTxFromAdditionalFieldsForTrace(additional) + if ethMessage == nil { + b.logger.Error("failed to get derived eth msg from additional fields") + return nil, fmt.Errorf("failed to get derived eth msg from additional fields") + } } nc, ok := b.clientCtx.Client.(tmrpcclient.NetworkClient) diff --git a/rpc/backend/tx_info.go b/rpc/backend/tx_info.go index 889922f4b..7c0635de4 100644 --- a/rpc/backend/tx_info.go +++ b/rpc/backend/tx_info.go @@ -424,6 +424,26 @@ func (b *Backend) GetTxByEthHash(hash common.Hash) (*types.TxResult, *rpctypes.T return txResult, txAdditional, nil } +func (b *Backend) GetTxByEthHashAndMsgIndex(hash common.Hash, index int) (*types.TxResult, *rpctypes.TxResultAdditionalFields, error) { + if b.indexer != nil { + txRes, err := b.indexer.GetByTxHash(hash) + if err != nil { + return nil, nil, err + } + return txRes, nil, nil + } + + // fallback to tendermint tx indexer + query := fmt.Sprintf("%s.%s='%s'", evmtypes.TypeMsgEthereumTx, evmtypes.AttributeKeyEthereumTxHash, hash.Hex()) + txResult, txAdditional, err := b.queryTendermintTxIndexer(query, func(txs *rpctypes.ParsedTxs) *rpctypes.ParsedTx { + return txs.GetTxByMsgIndex(index) + }) + if err != nil { + return nil, nil, errorsmod.Wrapf(err, "GetTxByEthHash %s", hash.Hex()) + } + return txResult, txAdditional, nil +} + // GetTxByTxIndex uses `/tx_query` to find transaction by tx index of valid ethereum txs func (b *Backend) GetTxByTxIndex(height int64, index uint) (*types.TxResult, *rpctypes.TxResultAdditionalFields, error) { int32Index := int32(index) //#nosec G115 -- checked for int overflow already