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

     1  package rest
     2  
     3  import (
     4  	"encoding/hex"
     5  	"encoding/json"
     6  	"fmt"
     7  	"net/http"
     8  	"strconv"
     9  	"strings"
    10  	"time"
    11  
    12  	authrest "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/x/auth/client/rest"
    13  
    14  	"github.com/fibonacci-chain/fbc/x/evm/client/utils"
    15  	"github.com/fibonacci-chain/fbc/x/evm/watcher"
    16  
    17  	ethcommon "github.com/ethereum/go-ethereum/common"
    18  	"github.com/fibonacci-chain/fbc/libs/cosmos-sdk/client/context"
    19  	"github.com/fibonacci-chain/fbc/libs/cosmos-sdk/client/rpc"
    20  	"github.com/fibonacci-chain/fbc/libs/cosmos-sdk/codec"
    21  	sdk "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types"
    22  	sdkerrors "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types/errors"
    23  	"github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types/rest"
    24  	"github.com/fibonacci-chain/fbc/libs/cosmos-sdk/x/auth/types"
    25  	tmliteProxy "github.com/fibonacci-chain/fbc/libs/tendermint/lite/proxy"
    26  	"github.com/fibonacci-chain/fbc/libs/tendermint/rpc/client"
    27  	ctypes "github.com/fibonacci-chain/fbc/libs/tendermint/rpc/core/types"
    28  	"github.com/fibonacci-chain/fbc/x/common"
    29  	evmtypes "github.com/fibonacci-chain/fbc/x/evm/types"
    30  	govRest "github.com/fibonacci-chain/fbc/x/gov/client/rest"
    31  	"github.com/gorilla/mux"
    32  )
    33  
    34  // RegisterRoutes - Central function to define routes that get registered by the main application
    35  func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router) {
    36  	r.HandleFunc("/txs/{hash}", QueryTxRequestHandlerFn(cliCtx)).Methods("GET")
    37  	r.HandleFunc("/txs", authrest.QueryTxsRequestHandlerFn(cliCtx)).Methods("GET")         // default from auth
    38  	r.HandleFunc("/txs", authrest.BroadcastTxRequest(cliCtx)).Methods("POST")              // default from auth
    39  	r.HandleFunc("/txs/encode", authrest.EncodeTxRequestHandlerFn(cliCtx)).Methods("POST") // default from auth
    40  	r.HandleFunc("/txs/decode", authrest.DecodeTxRequestHandlerFn(cliCtx)).Methods("POST")
    41  	r.HandleFunc("/section", QuerySectionFn(cliCtx)).Methods("GET")
    42  	r.HandleFunc("/contract/blocked_list", QueryContractBlockedListHandlerFn(cliCtx)).Methods("GET")
    43  	r.HandleFunc("/contract/method_blocked_list", QueryContractMethodBlockedListHandlerFn(cliCtx)).Methods("GET")
    44  	r.HandleFunc("/block_tx_hashes/{blockHeight}", blockTxHashesHandler(cliCtx)).Methods("GET")
    45  	r.HandleFunc("/latestheight", latestHeightHandler(cliCtx)).Methods("GET")
    46  
    47  	registerQueryRoutes(cliCtx, r)
    48  }
    49  
    50  func QueryTxRequestHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
    51  	return func(w http.ResponseWriter, r *http.Request) {
    52  		vars := mux.Vars(r)
    53  		hashHexStr := vars["hash"]
    54  
    55  		cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r)
    56  		if !ok {
    57  			return
    58  		}
    59  		var output interface{}
    60  		output, err := QueryTx(cliCtx, hashHexStr)
    61  		if err != nil {
    62  			rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
    63  			return
    64  
    65  		}
    66  
    67  		rest.PostProcessResponseBare(w, cliCtx, output)
    68  	}
    69  }
    70  
    71  // QueryTx queries for a single transaction by a hash string in hex format. An
    72  // error is returned if the transaction does not exist or cannot be queried.
    73  func QueryTx(cliCtx context.CLIContext, hashHexStr string) (interface{}, error) {
    74  	// strip 0x prefix
    75  	if strings.HasPrefix(hashHexStr, "0x") {
    76  		hashHexStr = hashHexStr[2:]
    77  	}
    78  
    79  	hash, err := hex.DecodeString(hashHexStr)
    80  	if err != nil {
    81  		return sdk.TxResponse{}, err
    82  	}
    83  
    84  	node, err := cliCtx.GetNode()
    85  	if err != nil {
    86  		return sdk.TxResponse{}, err
    87  	}
    88  
    89  	resTx, err := node.Tx(hash, !cliCtx.TrustNode)
    90  	if err != nil {
    91  		return sdk.TxResponse{}, err
    92  	}
    93  
    94  	if !cliCtx.TrustNode {
    95  		if err = ValidateTxResult(cliCtx, resTx); err != nil {
    96  			return sdk.TxResponse{}, err
    97  		}
    98  	}
    99  
   100  	tx, err := evmtypes.TxDecoder(cliCtx.CodecProy)(resTx.Tx, resTx.Height)
   101  	if err != nil {
   102  		return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error())
   103  	}
   104  	if realTx, ok := tx.(*evmtypes.MsgEthereumTx); ok {
   105  		return getEthTxResponse(node, resTx, realTx)
   106  	}
   107  
   108  	// not eth Tx
   109  	resBlocks, err := getBlocksForTxResults(cliCtx, []*ctypes.ResultTx{resTx})
   110  	if err != nil {
   111  		return sdk.TxResponse{}, err
   112  	}
   113  	var ret interface{}
   114  	switch tx.(type) {
   115  	case *types.IbcTx:
   116  		jsonTx, err := types.FromProtobufTx(cliCtx.CodecProy, tx.(*types.IbcTx))
   117  		if nil != err {
   118  			return nil, err
   119  		}
   120  		return sdk.NewResponseResultTx(resTx, jsonTx, resBlocks[resTx.Height].Block.Time.Format(time.RFC3339)), nil
   121  	default:
   122  		ret, err = formatTxResult(cliCtx.Codec, resTx, resBlocks[resTx.Height])
   123  	}
   124  
   125  	return ret, err
   126  
   127  }
   128  
   129  func getEthTxResponse(node client.Client, resTx *ctypes.ResultTx, ethTx *evmtypes.MsgEthereumTx) (interface{}, error) {
   130  	// Can either cache or just leave this out if not necessary
   131  	block, err := node.Block(&resTx.Height)
   132  	if err != nil {
   133  		return nil, err
   134  	}
   135  	blockHash := ethcommon.BytesToHash(block.Block.Hash())
   136  	height := uint64(resTx.Height)
   137  	res, err := watcher.NewTransaction(ethTx, ethcommon.BytesToHash(resTx.Tx.Hash(resTx.Height)), blockHash, height, uint64(resTx.Index))
   138  	if err != nil {
   139  		return nil, err
   140  	}
   141  	return json.Marshal(res)
   142  }
   143  
   144  // ValidateTxResult performs transaction verification.
   145  func ValidateTxResult(cliCtx context.CLIContext, resTx *ctypes.ResultTx) error {
   146  	if !cliCtx.TrustNode {
   147  		check, err := cliCtx.Verify(resTx.Height)
   148  		if err != nil {
   149  			return err
   150  		}
   151  		err = resTx.Proof.Validate(check.Header.DataHash, resTx.Height)
   152  		if err != nil {
   153  			return err
   154  		}
   155  	}
   156  	return nil
   157  }
   158  
   159  func getBlocksForTxResults(cliCtx context.CLIContext, resTxs []*ctypes.ResultTx) (map[int64]*ctypes.ResultBlock, error) {
   160  	node, err := cliCtx.GetNode()
   161  	if err != nil {
   162  		return nil, err
   163  	}
   164  
   165  	resBlocks := make(map[int64]*ctypes.ResultBlock)
   166  
   167  	for _, resTx := range resTxs {
   168  		if _, ok := resBlocks[resTx.Height]; !ok {
   169  			resBlock, err := node.Block(&resTx.Height)
   170  			if err != nil {
   171  				return nil, err
   172  			}
   173  
   174  			resBlocks[resTx.Height] = resBlock
   175  		}
   176  	}
   177  
   178  	return resBlocks, nil
   179  }
   180  
   181  func formatTxResult(cdc *codec.Codec, resTx *ctypes.ResultTx, resBlock *ctypes.ResultBlock) (sdk.TxResponse, error) {
   182  	tx, err := parseTx(cdc, resTx.Tx)
   183  	if err != nil {
   184  		return sdk.TxResponse{}, err
   185  	}
   186  
   187  	return sdk.NewResponseResultTx(resTx, tx, resBlock.Block.Time.Format(time.RFC3339)), nil
   188  }
   189  
   190  func parseTx(cdc *codec.Codec, txBytes []byte) (sdk.Tx, error) {
   191  	var tx types.StdTx
   192  
   193  	err := cdc.UnmarshalBinaryLengthPrefixed(txBytes, &tx)
   194  	if err != nil {
   195  		return nil, err
   196  	}
   197  
   198  	return &tx, nil
   199  }
   200  
   201  // ManageContractDeploymentWhitelistProposalRESTHandler defines evm proposal handler
   202  func ManageContractDeploymentWhitelistProposalRESTHandler(context.CLIContext) govRest.ProposalRESTHandler {
   203  	return govRest.ProposalRESTHandler{}
   204  }
   205  
   206  // ManageContractBlockedListProposalRESTHandler defines evm proposal handler
   207  func ManageContractBlockedListProposalRESTHandler(context.CLIContext) govRest.ProposalRESTHandler {
   208  	return govRest.ProposalRESTHandler{}
   209  }
   210  
   211  // ManageContractMethodBlockedListProposalRESTHandler defines evm proposal handler
   212  func ManageContractMethodBlockedListProposalRESTHandler(context.CLIContext) govRest.ProposalRESTHandler {
   213  	return govRest.ProposalRESTHandler{}
   214  }
   215  
   216  // ManageContractBytecodeProposalRESTHandler defines evm proposal handler
   217  func ManageContractBytecodeProposalRESTHandler(context.CLIContext) govRest.ProposalRESTHandler {
   218  	return govRest.ProposalRESTHandler{}
   219  }
   220  
   221  func QuerySectionFn(cliCtx context.CLIContext) http.HandlerFunc {
   222  	return func(w http.ResponseWriter, r *http.Request) {
   223  		res, _, err := cliCtx.Query(fmt.Sprintf("custom/%s/%s", evmtypes.RouterKey, evmtypes.QuerySection))
   224  		if err != nil {
   225  			sdkErr := common.ParseSDKError(err.Error())
   226  			common.HandleErrorMsg(w, cliCtx, sdkErr.Code, sdkErr.Message)
   227  			return
   228  		}
   229  
   230  		rest.PostProcessResponseBare(w, cliCtx, res)
   231  	}
   232  }
   233  
   234  // QueryContractBlockedListHandlerFn defines evm contract blocked list handler
   235  func QueryContractBlockedListHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
   236  	return func(w http.ResponseWriter, r *http.Request) {
   237  		path := fmt.Sprintf("custom/%s/%s", evmtypes.ModuleName, evmtypes.QueryContractBlockedList)
   238  
   239  		cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r)
   240  		if !ok {
   241  			return
   242  		}
   243  		bz, _, err := cliCtx.QueryWithData(path, nil)
   244  		if err != nil {
   245  			common.HandleErrorResponseV2(w, http.StatusInternalServerError, common.ErrorABCIQueryFails)
   246  			return
   247  		}
   248  
   249  		var blockedList evmtypes.AddressList
   250  		cliCtx.Codec.MustUnmarshalJSON(bz, &blockedList)
   251  
   252  		var ethAddrs []string
   253  		for _, accAddr := range blockedList {
   254  			ethAddrs = append(ethAddrs, ethcommon.BytesToAddress(accAddr.Bytes()).Hex())
   255  		}
   256  
   257  		rest.PostProcessResponseBare(w, cliCtx, ethAddrs)
   258  	}
   259  }
   260  
   261  // QueryContractMethodBlockedListHandlerFn defines evm contract method blocked list handler
   262  func QueryContractMethodBlockedListHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
   263  	return func(w http.ResponseWriter, r *http.Request) {
   264  		path := fmt.Sprintf("custom/%s/%s", evmtypes.ModuleName, evmtypes.QueryContractMethodBlockedList)
   265  
   266  		cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r)
   267  		if !ok {
   268  			return
   269  		}
   270  		bz, _, err := cliCtx.QueryWithData(path, nil)
   271  		if err != nil {
   272  			common.HandleErrorResponseV2(w, http.StatusInternalServerError, common.ErrorABCIQueryFails)
   273  			return
   274  		}
   275  
   276  		var blockedList evmtypes.BlockedContractList
   277  		cliCtx.Codec.MustUnmarshalJSON(bz, &blockedList)
   278  
   279  		results := make([]utils.ResponseBlockContract, 0)
   280  		for i, _ := range blockedList {
   281  			ethAddr := ethcommon.BytesToAddress(blockedList[i].Address.Bytes()).Hex()
   282  			result := utils.ResponseBlockContract{Address: ethAddr, BlockMethods: blockedList[i].BlockMethods}
   283  			results = append(results, result)
   284  		}
   285  
   286  		rest.PostProcessResponseBare(w, cliCtx, results)
   287  	}
   288  }
   289  func blockTxHashesHandler(cliCtx context.CLIContext) http.HandlerFunc {
   290  	return func(w http.ResponseWriter, r *http.Request) {
   291  		vars := mux.Vars(r)
   292  		blockHeightStr := vars["blockHeight"]
   293  		blockHeight, err := strconv.ParseInt(blockHeightStr, 10, 64)
   294  		if err != nil {
   295  			common.HandleErrorMsg(w, cliCtx, common.CodeStrconvFailed, err.Error())
   296  			return
   297  		}
   298  		res, err := GetBlockTxHashes(cliCtx, blockHeight)
   299  		if err != nil {
   300  			common.HandleErrorMsg(w, cliCtx, evmtypes.CodeGetBlockTxHashesFailed,
   301  				fmt.Sprintf("failed to get block tx hash: %s", err.Error()))
   302  			return
   303  		}
   304  
   305  		rest.PostProcessResponse(w, cliCtx, res)
   306  	}
   307  }
   308  func latestHeightHandler(cliCtx context.CLIContext) http.HandlerFunc {
   309  	return func(w http.ResponseWriter, r *http.Request) {
   310  		h, err := rpc.GetChainHeight(cliCtx)
   311  		if err != nil {
   312  			common.HandleErrorMsg(w, cliCtx, evmtypes.CodeGetChainHeightFailed,
   313  				fmt.Sprintf("failed to get chain height: %s", err.Error()))
   314  			return
   315  		}
   316  		res := common.GetBaseResponse(h)
   317  		bz, err := json.Marshal(res)
   318  		if err != nil {
   319  			common.HandleErrorMsg(w, cliCtx, common.CodeMarshalJSONFailed, err.Error())
   320  		}
   321  		rest.PostProcessResponse(w, cliCtx, bz)
   322  	}
   323  }
   324  
   325  // GetBlockTxHashes return tx hashes in the block of the given height
   326  func GetBlockTxHashes(cliCtx context.CLIContext, height int64) ([]string, error) {
   327  	// get the node
   328  	node, err := cliCtx.GetNode()
   329  	if err != nil {
   330  		return nil, err
   331  	}
   332  
   333  	// header -> BlockchainInfo
   334  	// header, tx -> Block
   335  	// results -> BlockResults
   336  	res, err := node.Block(&height)
   337  	if err != nil {
   338  		return nil, err
   339  	}
   340  
   341  	if !cliCtx.TrustNode {
   342  		check, err := cliCtx.Verify(res.Block.Height)
   343  		if err != nil {
   344  			return nil, err
   345  		}
   346  
   347  		err = tmliteProxy.ValidateBlock(res.Block, check)
   348  		if err != nil {
   349  			return nil, err
   350  		}
   351  	}
   352  
   353  	txs := res.Block.Txs
   354  	txLen := len(txs)
   355  	txHashes := make([]string, txLen)
   356  	for i, txBytes := range txs {
   357  		txHashes[i] = fmt.Sprintf("%X", txBytes.Hash(height))
   358  	}
   359  	return txHashes, nil
   360  }