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 }