github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/x/evm/watcher/tx.go (about)

     1  package watcher
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/ethereum/go-ethereum/common"
     7  	sdk "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types"
     8  	tm "github.com/fibonacci-chain/fbc/libs/tendermint/abci/types"
     9  	ctypes "github.com/fibonacci-chain/fbc/libs/tendermint/rpc/core/types"
    10  	"github.com/fibonacci-chain/fbc/x/evm/types"
    11  )
    12  
    13  type WatchTx interface {
    14  	GetTxWatchMessage() WatchMessage
    15  	GetTransaction() *Transaction
    16  	GetTxHash() common.Hash
    17  	GetFailedReceipts(cumulativeGas, gasUsed uint64) *TransactionReceipt
    18  	GetIndex() uint64
    19  }
    20  
    21  func (w *Watcher) RecordTxAndFailedReceipt(tx tm.TxEssentials, resp *tm.ResponseDeliverTx, txDecoder sdk.TxDecoder) {
    22  	if !w.Enabled() {
    23  		return
    24  	}
    25  
    26  	// record ResultTx always
    27  	if resp != nil {
    28  		txResult := &ctypes.ResultTx{
    29  			Hash:     tx.TxHash(),
    30  			Height:   int64(w.height),
    31  			TxResult: *resp,
    32  			Tx:       tx.GetRaw(),
    33  		}
    34  		w.saveStdTxResponse(txResult)
    35  	}
    36  
    37  	realTx, err := w.getRealTx(tx, txDecoder)
    38  	if err != nil {
    39  		return
    40  	}
    41  	watchTx := w.createWatchTx(realTx)
    42  	switch realTx.GetType() {
    43  	case sdk.EvmTxType:
    44  		if watchTx == nil {
    45  			return
    46  		}
    47  		w.saveTx(watchTx)
    48  		if resp != nil && !resp.IsOK() {
    49  			w.saveFailedReceipts(watchTx, uint64(resp.GasUsed))
    50  			return
    51  		}
    52  		if resp != nil && resp.IsOK() && !w.IsRealEvmTx(resp) { // for evm2cm
    53  			msgs := realTx.GetMsgs()
    54  			if len(msgs) == 0 {
    55  				return
    56  			}
    57  			evmTx, ok := msgs[0].(*types.MsgEthereumTx)
    58  			if !ok {
    59  				return
    60  			}
    61  			w.SaveTransactionReceipt(TransactionSuccess, evmTx, watchTx.GetTxHash(), watchTx.GetIndex(), &types.ResultData{}, uint64(resp.GasUsed))
    62  		}
    63  	case sdk.StdTxType:
    64  		w.blockStdTxs = append(w.blockStdTxs, common.BytesToHash(realTx.TxHash()))
    65  	}
    66  }
    67  
    68  func (w *Watcher) IsRealEvmTx(resp *tm.ResponseDeliverTx) bool {
    69  	for _, ev := range resp.Events {
    70  		if ev.Type == sdk.EventTypeMessage {
    71  			for _, attr := range ev.Attributes {
    72  				if string(attr.Key) == sdk.AttributeKeyModule &&
    73  					string(attr.Value) == types.AttributeValueCategory {
    74  					return true
    75  				}
    76  			}
    77  		}
    78  	}
    79  	return false
    80  }
    81  
    82  func (w *Watcher) getRealTx(tx tm.TxEssentials, txDecoder sdk.TxDecoder) (sdk.Tx, error) {
    83  	var err error
    84  	realTx, _ := tx.(sdk.Tx)
    85  	if realTx == nil {
    86  		realTx, err = txDecoder(tx.GetRaw())
    87  		if err != nil {
    88  			return nil, err
    89  		}
    90  	}
    91  
    92  	return realTx, nil
    93  }
    94  
    95  func (w *Watcher) createWatchTx(realTx sdk.Tx) WatchTx {
    96  	var txMsg WatchTx
    97  	switch realTx.GetType() {
    98  	case sdk.EvmTxType:
    99  		evmTx, err := w.extractEvmTx(realTx)
   100  		if err != nil {
   101  			return nil
   102  		}
   103  		txMsg = NewEvmTx(evmTx, common.BytesToHash(evmTx.TxHash()), w.blockHash, w.height, w.evmTxIndex)
   104  		w.evmTxIndex++
   105  	}
   106  
   107  	return txMsg
   108  }
   109  
   110  func (w *Watcher) extractEvmTx(sdkTx sdk.Tx) (*types.MsgEthereumTx, error) {
   111  	var ok bool
   112  	var evmTx *types.MsgEthereumTx
   113  	// stdTx should only have one tx
   114  	msg := sdkTx.GetMsgs()
   115  	if len(msg) <= 0 {
   116  		return nil, fmt.Errorf("can not extract evm tx, len(msg) <= 0")
   117  	}
   118  	if evmTx, ok = msg[0].(*types.MsgEthereumTx); !ok {
   119  		return nil, fmt.Errorf("sdktx is not evm tx %v", sdkTx)
   120  	}
   121  
   122  	return evmTx, nil
   123  }
   124  
   125  func (w *Watcher) saveTx(tx WatchTx) {
   126  	if w == nil || tx == nil {
   127  		return
   128  	}
   129  	if w.InfuraKeeper != nil {
   130  		ethTx := tx.GetTransaction()
   131  		if ethTx != nil {
   132  			w.InfuraKeeper.OnSaveTransaction(*ethTx)
   133  		}
   134  	}
   135  	if txWatchMessage := tx.GetTxWatchMessage(); txWatchMessage != nil {
   136  		w.batch = append(w.batch, txWatchMessage)
   137  	}
   138  	w.blockTxs = append(w.blockTxs, tx.GetTxHash())
   139  }
   140  
   141  func (w *Watcher) saveFailedReceipts(watchTx WatchTx, gasUsed uint64) {
   142  	if w == nil || watchTx == nil {
   143  		return
   144  	}
   145  	w.UpdateCumulativeGas(watchTx.GetIndex(), gasUsed)
   146  	receipt := watchTx.GetFailedReceipts(w.cumulativeGas[watchTx.GetIndex()], gasUsed)
   147  	if w.InfuraKeeper != nil {
   148  		w.InfuraKeeper.OnSaveTransactionReceipt(*receipt)
   149  	}
   150  	wMsg := NewMsgTransactionReceipt(*receipt, watchTx.GetTxHash())
   151  	if wMsg != nil {
   152  		w.batch = append(w.batch, wMsg)
   153  	}
   154  }
   155  
   156  // SaveParallelTx saves parallel transactions and transactionReceipts to watcher
   157  func (w *Watcher) SaveParallelTx(realTx sdk.Tx, resultData *types.ResultData, resp tm.ResponseDeliverTx) {
   158  
   159  	if !w.Enabled() {
   160  		return
   161  	}
   162  
   163  	// record ResultTx always
   164  	txResult := &ctypes.ResultTx{
   165  		Hash:     realTx.TxHash(),
   166  		Height:   int64(w.height),
   167  		TxResult: resp,
   168  		Tx:       realTx.GetRaw(),
   169  	}
   170  	w.saveStdTxResponse(txResult)
   171  
   172  	switch realTx.GetType() {
   173  	case sdk.EvmTxType:
   174  		msgs := realTx.GetMsgs()
   175  		evmTx, ok := msgs[0].(*types.MsgEthereumTx)
   176  		if !ok {
   177  			return
   178  		}
   179  		watchTx := NewEvmTx(evmTx, common.BytesToHash(evmTx.TxHash()), w.blockHash, w.height, w.evmTxIndex)
   180  		w.evmTxIndex++
   181  		w.saveTx(watchTx)
   182  
   183  		// save transactionReceipts
   184  		if resp.IsOK() {
   185  			if resultData == nil {
   186  				resultData = &types.ResultData{}
   187  			}
   188  			w.SaveTransactionReceipt(TransactionSuccess, evmTx, watchTx.GetTxHash(), watchTx.GetIndex(), resultData, uint64(resp.GasUsed))
   189  		} else {
   190  			w.saveFailedReceipts(watchTx, uint64(resp.GasUsed))
   191  		}
   192  	case sdk.StdTxType:
   193  		w.blockStdTxs = append(w.blockStdTxs, common.BytesToHash(realTx.TxHash()))
   194  	}
   195  }