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

     1  package rest
     2  
     3  import (
     4  	"context"
     5  	"encoding/base64"
     6  	"encoding/hex"
     7  	"encoding/json"
     8  	"fmt"
     9  	"net/http"
    10  	"strconv"
    11  	"strings"
    12  
    13  	clientCtx "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/client/context"
    14  
    15  	"github.com/gorilla/mux"
    16  
    17  	sdk "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types"
    18  	"github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types/rest"
    19  
    20  	"github.com/fibonacci-chain/fbc/x/wasm/keeper"
    21  	"github.com/fibonacci-chain/fbc/x/wasm/types"
    22  )
    23  
    24  func registerQueryRoutes(cliCtx clientCtx.CLIContext, r *mux.Router) {
    25  	r.HandleFunc("/wasm/code", listCodesHandlerFn(cliCtx)).Methods("GET")
    26  	r.HandleFunc("/wasm/code/{codeID}", queryCodeHandlerFn(cliCtx)).Methods("GET")
    27  	r.HandleFunc("/wasm/code/{codeID}/contracts", listContractsByCodeHandlerFn(cliCtx)).Methods("GET")
    28  	r.HandleFunc("/wasm/contract/{contractAddr}", queryContractHandlerFn(cliCtx)).Methods("GET")
    29  	r.HandleFunc("/wasm/contract/{contractAddr}/state", queryContractStateAllHandlerFn(cliCtx)).Methods("GET")
    30  	r.HandleFunc("/wasm/contract/{contractAddr}/history", queryContractHistoryFn(cliCtx)).Methods("GET")
    31  	r.HandleFunc("/wasm/contract/{contractAddr}/smart/{query}", queryContractStateSmartHandlerFn(cliCtx)).Queries("encoding", "{encoding}").Methods("GET")
    32  	r.HandleFunc("/wasm/contract/{contractAddr}/raw/{key}", queryContractStateRawHandlerFn(cliCtx)).Queries("encoding", "{encoding}").Methods("GET")
    33  	r.HandleFunc("/wasm/contract/{contractAddr}/blocked_methods", queryContractBlockedMethodsHandlerFn(cliCtx)).Methods("GET")
    34  	r.HandleFunc("/wasm/params", queryParamsHandlerFn(cliCtx)).Methods("GET")
    35  	r.HandleFunc("/wasm/whitelist", queryContractWhitelistHandlerFn(cliCtx)).Methods("GET")
    36  }
    37  
    38  func queryParamsHandlerFn(cliCtx clientCtx.CLIContext) http.HandlerFunc {
    39  	return func(w http.ResponseWriter, r *http.Request) {
    40  		cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r)
    41  		if !ok {
    42  			return
    43  		}
    44  		route := fmt.Sprintf("custom/%s/%s", types.QuerierRoute, keeper.QueryParams)
    45  
    46  		res, height, err := cliCtx.Query(route)
    47  		if err != nil {
    48  			rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
    49  			return
    50  		}
    51  
    52  		cliCtx = cliCtx.WithHeight(height)
    53  		rest.PostProcessResponse(w, cliCtx, res)
    54  	}
    55  }
    56  
    57  func queryContractWhitelistHandlerFn(cliCtx clientCtx.CLIContext) http.HandlerFunc {
    58  	return func(w http.ResponseWriter, r *http.Request) {
    59  		cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r)
    60  		if !ok {
    61  			return
    62  		}
    63  		route := fmt.Sprintf("custom/%s/%s", types.QuerierRoute, keeper.QueryParams)
    64  
    65  		res, height, err := cliCtx.Query(route)
    66  		if err != nil {
    67  			rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
    68  			return
    69  		}
    70  		var params types.Params
    71  		cliCtx.Codec.MustUnmarshalJSON(res, &params)
    72  		var whitelist []string
    73  		whitelist = strings.Split(params.CodeUploadAccess.Address, ",")
    74  		// When params.CodeUploadAccess.Address == "", whitelist == []string{""} and len(whitelist) == 1.
    75  		// Above case should be avoided.
    76  		if len(whitelist) == 1 && whitelist[0] == "" {
    77  			whitelist = []string{}
    78  		}
    79  		response := types.NewQueryAddressWhitelistResponse(whitelist)
    80  		cliCtx = cliCtx.WithHeight(height)
    81  		rest.PostProcessResponse(w, cliCtx, response)
    82  	}
    83  }
    84  
    85  func listCodesHandlerFn(cliCtx clientCtx.CLIContext) http.HandlerFunc {
    86  	return func(w http.ResponseWriter, r *http.Request) {
    87  		cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r)
    88  		if !ok {
    89  			return
    90  		}
    91  		queryClient := types.NewQueryClient(cliCtx)
    92  		pageReq, err := rest.ParseGRPCWasmPageRequest(r)
    93  		if err != nil {
    94  			rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
    95  			return
    96  		}
    97  		var reverse bool
    98  		reverseStr := r.FormValue("reverse")
    99  		if reverseStr == "" {
   100  			reverse = false
   101  		} else {
   102  			reverse, err = strconv.ParseBool(reverseStr)
   103  			if err != nil {
   104  				rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
   105  				return
   106  			}
   107  		}
   108  
   109  		res, err := queryClient.Codes(
   110  			context.Background(),
   111  			&types.QueryCodesRequest{
   112  				Pagination: pageReq,
   113  			},
   114  		)
   115  
   116  		if reverse {
   117  			for i, j := 0, len(res.CodeInfos)-1; i < j; i, j = i+1, j-1 {
   118  				res.CodeInfos[i], res.CodeInfos[j] = res.CodeInfos[j], res.CodeInfos[i]
   119  			}
   120  		}
   121  
   122  		if err != nil {
   123  			rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
   124  			return
   125  		}
   126  
   127  		rest.PostProcessResponse(w, cliCtx, res)
   128  
   129  	}
   130  }
   131  
   132  func queryCodeHandlerFn(cliCtx clientCtx.CLIContext) http.HandlerFunc {
   133  	return func(w http.ResponseWriter, r *http.Request) {
   134  		codeID, err := strconv.ParseUint(mux.Vars(r)["codeID"], 10, 64)
   135  		if err != nil {
   136  			rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
   137  			return
   138  		}
   139  
   140  		cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r)
   141  		if !ok {
   142  			return
   143  		}
   144  
   145  		route := fmt.Sprintf("custom/%s/%s/%d", types.QuerierRoute, keeper.QueryGetCode, codeID)
   146  		res, height, err := cliCtx.Query(route)
   147  		if err != nil {
   148  			rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
   149  			return
   150  		}
   151  		if len(res) == 0 {
   152  			rest.WriteErrorResponse(w, http.StatusNotFound, "contract not found")
   153  			return
   154  		}
   155  
   156  		cliCtx = cliCtx.WithHeight(height)
   157  		rest.PostProcessResponse(w, cliCtx, json.RawMessage(res))
   158  	}
   159  }
   160  
   161  func listContractsByCodeHandlerFn(cliCtx clientCtx.CLIContext) http.HandlerFunc {
   162  	return func(w http.ResponseWriter, r *http.Request) {
   163  		codeID, err := strconv.ParseUint(mux.Vars(r)["codeID"], 10, 64)
   164  		if err != nil {
   165  			rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
   166  			return
   167  		}
   168  		cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r)
   169  		if !ok {
   170  			return
   171  		}
   172  
   173  		queryClient := types.NewQueryClient(cliCtx)
   174  		pageReq, err := rest.ParseGRPCWasmPageRequest(r)
   175  		if err != nil {
   176  			rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
   177  			return
   178  		}
   179  		var reverse bool
   180  		reverseStr := r.FormValue("reverse")
   181  		if reverseStr == "" {
   182  			reverse = false
   183  		} else {
   184  			reverse, err = strconv.ParseBool(reverseStr)
   185  			if err != nil {
   186  				rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
   187  				return
   188  			}
   189  		}
   190  
   191  		res, err := queryClient.ContractsByCode(
   192  			context.Background(),
   193  			&types.QueryContractsByCodeRequest{
   194  				CodeId:     codeID,
   195  				Pagination: pageReq,
   196  			},
   197  		)
   198  
   199  		if reverse {
   200  			for i, j := 0, len(res.Contracts)-1; i < j; i, j = i+1, j-1 {
   201  				res.Contracts[i], res.Contracts[j] = res.Contracts[j], res.Contracts[i]
   202  			}
   203  		}
   204  
   205  		if err != nil {
   206  			rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
   207  			return
   208  		}
   209  
   210  		rest.PostProcessResponse(w, cliCtx, res)
   211  	}
   212  }
   213  
   214  func queryContractHandlerFn(cliCtx clientCtx.CLIContext) http.HandlerFunc {
   215  	return func(w http.ResponseWriter, r *http.Request) {
   216  		addr, err := sdk.AccAddressFromBech32(mux.Vars(r)["contractAddr"])
   217  		if err != nil {
   218  			rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
   219  			return
   220  		}
   221  		cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r)
   222  		if !ok {
   223  			return
   224  		}
   225  
   226  		route := fmt.Sprintf("custom/%s/%s/%s", types.QuerierRoute, keeper.QueryGetContract, addr.String())
   227  		res, height, err := cliCtx.Query(route)
   228  		if err != nil {
   229  			rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
   230  			return
   231  		}
   232  
   233  		cliCtx = cliCtx.WithHeight(height)
   234  		rest.PostProcessResponse(w, cliCtx, json.RawMessage(res))
   235  	}
   236  }
   237  
   238  func queryContractBlockedMethodsHandlerFn(cliCtx clientCtx.CLIContext) http.HandlerFunc {
   239  	return func(w http.ResponseWriter, r *http.Request) {
   240  		addr, err := sdk.AccAddressFromBech32(mux.Vars(r)["contractAddr"])
   241  		if err != nil {
   242  			rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
   243  			return
   244  		}
   245  		cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r)
   246  		if !ok {
   247  			return
   248  		}
   249  
   250  		route := fmt.Sprintf("custom/%s/%s/%s", types.QuerierRoute, keeper.QueryListContractBlockedMethod, addr.String())
   251  		res, height, err := cliCtx.Query(route)
   252  		if err != nil {
   253  			rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
   254  			return
   255  		}
   256  
   257  		cliCtx = cliCtx.WithHeight(height)
   258  		rest.PostProcessResponse(w, cliCtx, json.RawMessage(res))
   259  	}
   260  }
   261  
   262  func queryContractStateAllHandlerFn(cliCtx clientCtx.CLIContext) http.HandlerFunc {
   263  	return func(w http.ResponseWriter, r *http.Request) {
   264  		addr := mux.Vars(r)["contractAddr"]
   265  
   266  		cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r)
   267  		if !ok {
   268  			return
   269  		}
   270  
   271  		queryClient := types.NewQueryClient(cliCtx)
   272  		pageReq, err := rest.ParseGRPCWasmPageRequest(r)
   273  		if err != nil {
   274  			rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
   275  			return
   276  		}
   277  		var reverse bool
   278  		reverseStr := r.FormValue("reverse")
   279  		if reverseStr == "" {
   280  			reverse = false
   281  		} else {
   282  			reverse, err = strconv.ParseBool(reverseStr)
   283  			if err != nil {
   284  				rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
   285  				return
   286  			}
   287  		}
   288  
   289  		res, err := queryClient.AllContractState(
   290  			context.Background(),
   291  			&types.QueryAllContractStateRequest{
   292  				Address:    addr,
   293  				Pagination: pageReq,
   294  			},
   295  		)
   296  
   297  		if reverse {
   298  			for i, j := 0, len(res.Models)-1; i < j; i, j = i+1, j-1 {
   299  				res.Models[i], res.Models[j] = res.Models[j], res.Models[i]
   300  			}
   301  		}
   302  
   303  		if err != nil {
   304  			rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
   305  			return
   306  		}
   307  
   308  		rest.PostProcessResponse(w, cliCtx, res)
   309  	}
   310  }
   311  
   312  func queryContractStateRawHandlerFn(cliCtx clientCtx.CLIContext) http.HandlerFunc {
   313  	return func(w http.ResponseWriter, r *http.Request) {
   314  		decoder := newArgDecoder(hex.DecodeString)
   315  		addr, err := sdk.AccAddressFromBech32(mux.Vars(r)["contractAddr"])
   316  		if err != nil {
   317  			rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
   318  			return
   319  		}
   320  		decoder.encoding = mux.Vars(r)["encoding"]
   321  		queryData, err := decoder.DecodeString(mux.Vars(r)["key"])
   322  		if err != nil {
   323  			rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
   324  			return
   325  		}
   326  		cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r)
   327  		if !ok {
   328  			return
   329  		}
   330  
   331  		route := fmt.Sprintf("custom/%s/%s/%s/%s", types.QuerierRoute, keeper.QueryGetContractState, addr.String(), keeper.QueryMethodContractStateRaw)
   332  		res, height, err := cliCtx.QueryWithData(route, queryData)
   333  		if err != nil {
   334  			rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
   335  			return
   336  		}
   337  		cliCtx = cliCtx.WithHeight(height)
   338  		// ensure this is base64 encoded
   339  		encoded := base64.StdEncoding.EncodeToString(res)
   340  		rest.PostProcessResponse(w, cliCtx, encoded)
   341  	}
   342  }
   343  
   344  type smartResponse struct {
   345  	Smart []byte `json:"smart"`
   346  }
   347  
   348  func queryContractStateSmartHandlerFn(cliCtx clientCtx.CLIContext) http.HandlerFunc {
   349  	return func(w http.ResponseWriter, r *http.Request) {
   350  		decoder := newArgDecoder(hex.DecodeString)
   351  		addr, err := sdk.AccAddressFromBech32(mux.Vars(r)["contractAddr"])
   352  		if err != nil {
   353  			rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
   354  			return
   355  		}
   356  		decoder.encoding = mux.Vars(r)["encoding"]
   357  		cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r)
   358  		if !ok {
   359  			return
   360  		}
   361  
   362  		route := fmt.Sprintf("custom/%s/%s/%s/%s", types.QuerierRoute, keeper.QueryGetContractState, addr.String(), keeper.QueryMethodContractStateSmart)
   363  
   364  		queryData, err := decoder.DecodeString(mux.Vars(r)["query"])
   365  		if err != nil {
   366  			rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
   367  			return
   368  		}
   369  		res, height, err := cliCtx.QueryWithData(route, queryData)
   370  		if err != nil {
   371  			rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
   372  			return
   373  		}
   374  		// return as raw bytes (to be base64-encoded)
   375  		responseData := smartResponse{Smart: res}
   376  
   377  		cliCtx = cliCtx.WithHeight(height)
   378  		rest.PostProcessResponse(w, cliCtx, responseData)
   379  	}
   380  }
   381  
   382  func queryContractHistoryFn(cliCtx clientCtx.CLIContext) http.HandlerFunc {
   383  	return func(w http.ResponseWriter, r *http.Request) {
   384  		addr := mux.Vars(r)["contractAddr"]
   385  
   386  		cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r)
   387  		if !ok {
   388  			return
   389  		}
   390  
   391  		queryClient := types.NewQueryClient(cliCtx)
   392  		pageReq, err := rest.ParseGRPCWasmPageRequest(r)
   393  		if err != nil {
   394  			rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
   395  			return
   396  		}
   397  		var reverse bool
   398  		reverseStr := r.FormValue("reverse")
   399  		if reverseStr == "" {
   400  			reverse = false
   401  		} else {
   402  			reverse, err = strconv.ParseBool(reverseStr)
   403  			if err != nil {
   404  				rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
   405  				return
   406  			}
   407  		}
   408  
   409  		res, err := queryClient.ContractHistory(
   410  			context.Background(),
   411  			&types.QueryContractHistoryRequest{
   412  				Address:    addr,
   413  				Pagination: pageReq,
   414  			},
   415  		)
   416  
   417  		if reverse {
   418  			for i, j := 0, len(res.Entries)-1; i < j; i, j = i+1, j-1 {
   419  				res.Entries[i], res.Entries[j] = res.Entries[j], res.Entries[i]
   420  			}
   421  		}
   422  
   423  		if err != nil {
   424  			rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
   425  			return
   426  		}
   427  
   428  		rest.PostProcessResponse(w, cliCtx, res)
   429  	}
   430  }
   431  
   432  type argumentDecoder struct {
   433  	// dec is the default decoder
   434  	dec      func(string) ([]byte, error)
   435  	encoding string
   436  }
   437  
   438  func newArgDecoder(def func(string) ([]byte, error)) *argumentDecoder {
   439  	return &argumentDecoder{dec: def}
   440  }
   441  
   442  func (a *argumentDecoder) DecodeString(s string) ([]byte, error) {
   443  	switch a.encoding {
   444  	case "hex":
   445  		return hex.DecodeString(s)
   446  	case "base64":
   447  		return base64.StdEncoding.DecodeString(s)
   448  	default:
   449  		return a.dec(s)
   450  	}
   451  }