github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/app/rpc/types/utils.go (about)

     1  package types
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"errors"
     7  	"fmt"
     8  	"math/big"
     9  
    10  	"github.com/ethereum/go-ethereum/common"
    11  	"github.com/ethereum/go-ethereum/common/hexutil"
    12  	ethtypes "github.com/ethereum/go-ethereum/core/types"
    13  	"github.com/fibonacci-chain/fbc/app/crypto/ethsecp256k1"
    14  	clientcontext "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/client/context"
    15  	"github.com/fibonacci-chain/fbc/libs/cosmos-sdk/codec"
    16  	sdk "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types"
    17  	sdkerrors "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types/errors"
    18  	tmbytes "github.com/fibonacci-chain/fbc/libs/tendermint/libs/bytes"
    19  	ctypes "github.com/fibonacci-chain/fbc/libs/tendermint/rpc/core/types"
    20  	tmtypes "github.com/fibonacci-chain/fbc/libs/tendermint/types"
    21  	evmtypes "github.com/fibonacci-chain/fbc/x/evm/types"
    22  	"github.com/fibonacci-chain/fbc/x/evm/watcher"
    23  )
    24  
    25  var (
    26  	// static gas limit for all blocks
    27  	defaultGasLimit   = hexutil.Uint64(int64(^uint32(0)))
    28  	defaultGasUsed    = hexutil.Uint64(0)
    29  	defaultDifficulty = (*hexutil.Big)(big.NewInt(0))
    30  )
    31  
    32  // RawTxToEthTx returns a evm MsgEthereum transaction from raw tx bytes.
    33  func RawTxToEthTx(clientCtx clientcontext.CLIContext, bz []byte, height int64) (*evmtypes.MsgEthereumTx, error) {
    34  	tx, err := evmtypes.TxDecoder(clientCtx.Codec)(bz, height)
    35  	if err != nil {
    36  		return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error())
    37  	}
    38  
    39  	ethTx, ok := tx.(*evmtypes.MsgEthereumTx)
    40  	if !ok {
    41  		return nil, fmt.Errorf("invalid transaction type %T, expected %T", tx, evmtypes.MsgEthereumTx{})
    42  	}
    43  	return ethTx, nil
    44  }
    45  
    46  func ToTransaction(tx *evmtypes.MsgEthereumTx, from *common.Address) *watcher.Transaction {
    47  	rpcTx := &watcher.Transaction{
    48  		From:     *from,
    49  		Gas:      hexutil.Uint64(tx.Data.GasLimit),
    50  		GasPrice: (*hexutil.Big)(tx.Data.Price),
    51  		Input:    hexutil.Bytes(tx.Data.Payload),
    52  		Nonce:    hexutil.Uint64(tx.Data.AccountNonce),
    53  		To:       tx.To(),
    54  		Value:    (*hexutil.Big)(tx.Data.Amount),
    55  		V:        (*hexutil.Big)(tx.Data.V),
    56  		R:        (*hexutil.Big)(tx.Data.R),
    57  		S:        (*hexutil.Big)(tx.Data.S),
    58  	}
    59  	return rpcTx
    60  }
    61  
    62  // RpcBlockFromTendermint returns a JSON-RPC compatible Ethereum blockfrom a given Tendermint block.
    63  func RpcBlockFromTendermint(clientCtx clientcontext.CLIContext, block *tmtypes.Block, fullTx bool) (*watcher.Block, error) {
    64  	gasLimit, err := BlockMaxGasFromConsensusParams(context.Background(), clientCtx)
    65  	if err != nil {
    66  		return nil, err
    67  	}
    68  
    69  	gasUsed, ethTxs, err := EthTransactionsFromTendermint(clientCtx, block.Txs, common.BytesToHash(block.Hash()), uint64(block.Height))
    70  	if err != nil {
    71  		return nil, err
    72  	}
    73  
    74  	var bloom ethtypes.Bloom
    75  	clientCtx = clientCtx.WithHeight(block.Height)
    76  	res, _, err := clientCtx.Query(fmt.Sprintf("custom/%s/%s/%d", evmtypes.ModuleName, evmtypes.QueryBloom, block.Height))
    77  	if err == nil {
    78  		var bloomRes evmtypes.QueryBloomFilter
    79  		clientCtx.Codec.MustUnmarshalJSON(res, &bloomRes)
    80  		bloom = bloomRes.Bloom
    81  	}
    82  
    83  	return FormatBlock(block.Header, block.Size(), block.Hash(), gasLimit, gasUsed, ethTxs, bloom, fullTx), nil
    84  }
    85  
    86  // EthHeaderFromTendermint is an util function that returns an Ethereum Header
    87  // from a tendermint Header.
    88  func EthHeaderFromTendermint(header tmtypes.Header) *ethtypes.Header {
    89  	return &ethtypes.Header{
    90  		ParentHash:  common.BytesToHash(header.LastBlockID.Hash.Bytes()),
    91  		UncleHash:   ethtypes.EmptyUncleHash,
    92  		Coinbase:    common.BytesToAddress(header.ProposerAddress),
    93  		Root:        common.BytesToHash(header.AppHash),
    94  		TxHash:      common.BytesToHash(header.DataHash),
    95  		ReceiptHash: ethtypes.EmptyRootHash,
    96  		Difficulty:  nil,
    97  		Number:      big.NewInt(header.Height),
    98  		Time:        uint64(header.Time.Unix()),
    99  		Extra:       nil,
   100  		MixDigest:   common.Hash{},
   101  		Nonce:       ethtypes.BlockNonce{},
   102  	}
   103  }
   104  
   105  // EthTransactionsFromTendermint returns a slice of ethereum transaction hashes and the total gas usage from a set of
   106  // tendermint block transactions.
   107  func EthTransactionsFromTendermint(clientCtx clientcontext.CLIContext, txs []tmtypes.Tx, blockHash common.Hash, blockNumber uint64) (*big.Int, []*watcher.Transaction, error) {
   108  	var transactions []*watcher.Transaction
   109  	gasUsed := big.NewInt(0)
   110  	index := uint64(0)
   111  
   112  	for _, tx := range txs {
   113  		ethTx, err := RawTxToEthTx(clientCtx, tx, int64(blockNumber))
   114  		if err != nil {
   115  			// continue to next transaction in case it's not a MsgEthereumTx
   116  			continue
   117  		}
   118  		// TODO: Remove gas usage calculation if saving gasUsed per block
   119  		gasUsed.Add(gasUsed, big.NewInt(int64(ethTx.GetGas())))
   120  		tx, err := watcher.NewTransaction(ethTx, common.BytesToHash(ethTx.Hash), blockHash, blockNumber, index)
   121  		if err == nil {
   122  			transactions = append(transactions, tx)
   123  			index++
   124  		}
   125  	}
   126  
   127  	return gasUsed, transactions, nil
   128  }
   129  
   130  // BlockMaxGasFromConsensusParams returns the gas limit for the latest block from the chain consensus params.
   131  func BlockMaxGasFromConsensusParams(_ context.Context, clientCtx clientcontext.CLIContext) (int64, error) {
   132  	//resConsParams, err := clientCtx.Client.ConsensusParams(nil)
   133  	//if err != nil {
   134  	//	return 0, err
   135  	//}
   136  	//
   137  	//gasLimit := resConsParams.ConsensusParams.Block.MaxGas
   138  	//if gasLimit == -1 {
   139  	//	// Sets gas limit to max uint32 to not error with javascript dev tooling
   140  	//	// This -1 value indicating no block gas limit is set to max uint64 with geth hexutils
   141  	//	// which errors certain javascript dev tooling which only supports up to 53 bits
   142  	//	gasLimit = int64(^uint32(0))
   143  	//}
   144  	//
   145  	//return gasLimit, nil
   146  
   147  	return int64(^uint32(0)), nil
   148  }
   149  
   150  // FormatBlock creates an ethereum block from a tendermint header and ethereum-formatted
   151  // transactions.
   152  func FormatBlock(
   153  	header tmtypes.Header, size int, curBlockHash tmbytes.HexBytes, gasLimit int64,
   154  	gasUsed *big.Int, transactions []*watcher.Transaction, bloom ethtypes.Bloom, fullTx bool,
   155  ) *watcher.Block {
   156  	transactionsRoot := ethtypes.EmptyRootHash
   157  	if len(header.DataHash) > 0 {
   158  		transactionsRoot = common.BytesToHash(header.DataHash)
   159  	}
   160  
   161  	parentHash := header.LastBlockID.Hash
   162  	if parentHash == nil {
   163  		parentHash = ethtypes.EmptyRootHash.Bytes()
   164  	}
   165  	ret := &watcher.Block{
   166  		Number:           hexutil.Uint64(header.Height),
   167  		Hash:             common.BytesToHash(curBlockHash),
   168  		ParentHash:       common.BytesToHash(parentHash),
   169  		Nonce:            watcher.BlockNonce{},    // PoW specific
   170  		UncleHash:        ethtypes.EmptyUncleHash, // No uncles in Tendermint
   171  		LogsBloom:        bloom,
   172  		TransactionsRoot: transactionsRoot,
   173  		StateRoot:        common.BytesToHash(header.AppHash),
   174  		Miner:            common.BytesToAddress(header.ProposerAddress),
   175  		MixHash:          common.Hash{},
   176  		Difficulty:       hexutil.Uint64(0),
   177  		TotalDifficulty:  hexutil.Uint64(0),
   178  		ExtraData:        hexutil.Bytes{},
   179  		Size:             hexutil.Uint64(size),
   180  		GasLimit:         hexutil.Uint64(gasLimit), // Static gas limit
   181  		GasUsed:          (*hexutil.Big)(gasUsed),
   182  		Timestamp:        hexutil.Uint64(header.Time.Unix()),
   183  		Uncles:           []common.Hash{},
   184  		ReceiptsRoot:     ethtypes.EmptyRootHash,
   185  	}
   186  
   187  	if fullTx {
   188  		// return empty slice instead of nil for compatibility with Ethereum
   189  		if len(transactions) == 0 {
   190  			ret.Transactions = []*watcher.Transaction{}
   191  		} else {
   192  			ret.Transactions = transactions
   193  		}
   194  	} else {
   195  		txHashes := make([]common.Hash, len(transactions))
   196  		for i, tx := range transactions {
   197  			txHashes[i] = tx.Hash
   198  		}
   199  		ret.Transactions = txHashes
   200  	}
   201  	return ret
   202  }
   203  
   204  // GetKeyByAddress returns the private key matching the given address. If not found it returns false.
   205  func GetKeyByAddress(keys []ethsecp256k1.PrivKey, address common.Address) (key *ethsecp256k1.PrivKey, exist bool) {
   206  	for _, key := range keys {
   207  		if bytes.Equal(key.PubKey().Address().Bytes(), address.Bytes()) {
   208  			return &key, true
   209  		}
   210  	}
   211  	return nil, false
   212  }
   213  
   214  // GetBlockCumulativeGas returns the cumulative gas used on a block up to a given
   215  // transaction index. The returned gas used includes the gas from both the SDK and
   216  // EVM module transactions.
   217  func GetBlockCumulativeGas(cdc *codec.Codec, block *tmtypes.Block, idx int) uint64 {
   218  	var gasUsed uint64
   219  	txDecoder := evmtypes.TxDecoder(cdc)
   220  
   221  	for i := 0; i < idx && i < len(block.Txs); i++ {
   222  		txi, err := txDecoder(block.Txs[i], block.Height)
   223  		if err != nil {
   224  			continue
   225  		}
   226  
   227  		gasUsed += txi.GetGas()
   228  	}
   229  	return gasUsed
   230  }
   231  
   232  // EthHeaderWithBlockHashFromTendermint gets the eth Header with block hash from Tendermint block inside
   233  func EthHeaderWithBlockHashFromTendermint(tmHeader *tmtypes.Header) (header *EthHeaderWithBlockHash, err error) {
   234  	if tmHeader == nil {
   235  		return header, errors.New("failed. nil tendermint block header")
   236  	}
   237  
   238  	header = &EthHeaderWithBlockHash{
   239  		ParentHash:  common.BytesToHash(tmHeader.LastBlockID.Hash.Bytes()),
   240  		UncleHash:   ethtypes.EmptyUncleHash,
   241  		Coinbase:    common.BytesToAddress(tmHeader.ProposerAddress),
   242  		Root:        common.BytesToHash(tmHeader.AppHash),
   243  		TxHash:      common.BytesToHash(tmHeader.DataHash),
   244  		ReceiptHash: ethtypes.EmptyRootHash,
   245  		Number:      (*hexutil.Big)(big.NewInt(tmHeader.Height)),
   246  		// difficulty is not available for DPOS
   247  		Difficulty: defaultDifficulty,
   248  		GasLimit:   defaultGasLimit,
   249  		GasUsed:    defaultGasUsed,
   250  		Time:       hexutil.Uint64(tmHeader.Time.Unix()),
   251  		Hash:       common.BytesToHash(tmHeader.Hash()),
   252  	}
   253  
   254  	return
   255  }
   256  
   257  func RawTxToRealTx(clientCtx clientcontext.CLIContext, bz tmtypes.Tx,
   258  	blockHash common.Hash, blockNumber, index uint64) (sdk.Tx, error) {
   259  	realTx, err := evmtypes.TxDecoder(clientCtx.CodecProy)(bz, int64(blockNumber))
   260  	if err != nil {
   261  		return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error())
   262  	}
   263  
   264  	return realTx, nil
   265  }
   266  
   267  func RawTxResultToEthReceipt(chainID *big.Int, tr *ctypes.ResultTx, realTx sdk.Tx,
   268  	blockHash common.Hash) (*watcher.TransactionResult, error) {
   269  	// Convert tx bytes to eth transaction
   270  	ethTx, ok := realTx.(*evmtypes.MsgEthereumTx)
   271  	if !ok {
   272  		return nil, fmt.Errorf("invalid transaction type %T, expected %T", realTx, evmtypes.MsgEthereumTx{})
   273  	}
   274  
   275  	// try to get from event
   276  	if from, err := GetEthSender(tr); err == nil {
   277  		ethTx.BaseTx.From = from
   278  	} else {
   279  		// try to get from sig
   280  		err := ethTx.VerifySig(chainID, tr.Height)
   281  		if err != nil {
   282  			return nil, err
   283  		}
   284  	}
   285  
   286  	// Set status codes based on tx result
   287  	var status = hexutil.Uint64(0)
   288  	if tr.TxResult.IsOK() {
   289  		status = hexutil.Uint64(1)
   290  	}
   291  
   292  	txData := tr.TxResult.GetData()
   293  	data, err := evmtypes.DecodeResultData(txData)
   294  	if err != nil {
   295  		status = 0 // transaction failed
   296  	}
   297  
   298  	if len(data.Logs) == 0 {
   299  		data.Logs = []*ethtypes.Log{}
   300  	}
   301  	contractAddr := &data.ContractAddress
   302  	if data.ContractAddress == common.HexToAddress("0x00000000000000000000") {
   303  		contractAddr = nil
   304  	}
   305  
   306  	// fix gasUsed when deliverTx ante handler check sequence invalid
   307  	gasUsed := tr.TxResult.GasUsed
   308  	if tr.TxResult.Code == sdkerrors.ErrInvalidSequence.ABCICode() {
   309  		gasUsed = 0
   310  	}
   311  
   312  	receipt := watcher.TransactionReceipt{
   313  		Status: status,
   314  		//CumulativeGasUsed: hexutil.Uint64(cumulativeGasUsed),
   315  		LogsBloom:        data.Bloom,
   316  		Logs:             data.Logs,
   317  		TransactionHash:  common.BytesToHash(tr.Hash.Bytes()).String(),
   318  		ContractAddress:  contractAddr,
   319  		GasUsed:          hexutil.Uint64(gasUsed),
   320  		BlockHash:        blockHash.String(),
   321  		BlockNumber:      hexutil.Uint64(tr.Height),
   322  		TransactionIndex: hexutil.Uint64(tr.Index),
   323  		From:             ethTx.GetFrom(),
   324  		To:               ethTx.To(),
   325  	}
   326  
   327  	rpcTx, err := watcher.NewTransaction(ethTx, common.BytesToHash(tr.Hash),
   328  		blockHash, uint64(tr.Height), uint64(tr.Index))
   329  	if err != nil {
   330  		return nil, err
   331  	}
   332  
   333  	return &watcher.TransactionResult{TxType: hexutil.Uint64(watcher.EthReceipt),
   334  		Receipt: &receipt, EthTx: rpcTx, EthTxLog: tr.TxResult.Log}, nil
   335  }
   336  
   337  func GetEthSender(tr *ctypes.ResultTx) (string, error) {
   338  	for _, ev := range tr.TxResult.Events {
   339  		if ev.Type == sdk.EventTypeMessage {
   340  			fromAddr := ""
   341  			realEvmTx := false
   342  			for _, attr := range ev.Attributes {
   343  				if string(attr.Key) == sdk.AttributeKeySender {
   344  					fromAddr = string(attr.Value)
   345  				}
   346  				if string(attr.Key) == sdk.AttributeKeyModule &&
   347  					string(attr.Value) == evmtypes.AttributeValueCategory { // to avoid the evm to cm tx enter
   348  					realEvmTx = true
   349  				}
   350  				// find the sender
   351  				if fromAddr != "" && realEvmTx {
   352  					return fromAddr, nil
   353  				}
   354  			}
   355  		}
   356  	}
   357  	return "", errors.New("No sender in Event")
   358  }