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

     1  package rest
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"net/http"
     7  
     8  	"github.com/gorilla/mux"
     9  
    10  	"github.com/fibonacci-chain/fbc/libs/cosmos-sdk/client/context"
    11  	sdk "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types"
    12  	"github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types/rest"
    13  	gcutils "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/x/gov/client/utils"
    14  	"github.com/fibonacci-chain/fbc/libs/cosmos-sdk/x/gov/types"
    15  )
    16  
    17  func registerQueryRoutes(cliCtx context.CLIContext, r *mux.Router) {
    18  	r.HandleFunc(fmt.Sprintf("/gov/parameters/{%s}", RestParamsType), queryParamsHandlerFn(cliCtx)).Methods("GET")
    19  	r.HandleFunc("/gov/proposals", queryProposalsWithParameterFn(cliCtx)).Methods("GET")
    20  	r.HandleFunc(fmt.Sprintf("/gov/proposals/{%s}", RestProposalID), queryProposalHandlerFn(cliCtx)).Methods("GET")
    21  	r.HandleFunc(fmt.Sprintf("/gov/proposals/{%s}/proposer", RestProposalID), queryProposerHandlerFn(cliCtx)).Methods("GET")
    22  	r.HandleFunc(fmt.Sprintf("/gov/proposals/{%s}/deposits", RestProposalID), queryDepositsHandlerFn(cliCtx)).Methods("GET")
    23  	r.HandleFunc(fmt.Sprintf("/gov/proposals/{%s}/deposits/{%s}", RestProposalID, RestDepositor), queryDepositHandlerFn(cliCtx)).Methods("GET")
    24  	r.HandleFunc(fmt.Sprintf("/gov/proposals/{%s}/tally", RestProposalID), queryTallyOnProposalHandlerFn(cliCtx)).Methods("GET")
    25  	r.HandleFunc(fmt.Sprintf("/gov/proposals/{%s}/votes", RestProposalID), queryVotesOnProposalHandlerFn(cliCtx)).Methods("GET")
    26  	r.HandleFunc(fmt.Sprintf("/gov/proposals/{%s}/votes/{%s}", RestProposalID, RestVoter), queryVoteHandlerFn(cliCtx)).Methods("GET")
    27  }
    28  
    29  func queryParamsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
    30  	return func(w http.ResponseWriter, r *http.Request) {
    31  		vars := mux.Vars(r)
    32  		paramType := vars[RestParamsType]
    33  
    34  		cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r)
    35  		if !ok {
    36  			return
    37  		}
    38  
    39  		res, height, err := cliCtx.QueryWithData(fmt.Sprintf("custom/gov/%s/%s", types.QueryParams, paramType), nil)
    40  		if err != nil {
    41  			rest.WriteErrorResponse(w, http.StatusNotFound, err.Error())
    42  			return
    43  		}
    44  
    45  		cliCtx = cliCtx.WithHeight(height)
    46  		rest.PostProcessResponse(w, cliCtx, res)
    47  	}
    48  }
    49  
    50  func queryProposalHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
    51  	return func(w http.ResponseWriter, r *http.Request) {
    52  		vars := mux.Vars(r)
    53  		strProposalID := vars[RestProposalID]
    54  
    55  		if len(strProposalID) == 0 {
    56  			err := errors.New("proposalId required but not specified")
    57  			rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
    58  			return
    59  		}
    60  
    61  		proposalID, ok := rest.ParseUint64OrReturnBadRequest(w, strProposalID)
    62  		if !ok {
    63  			return
    64  		}
    65  
    66  		cliCtx, ok = rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r)
    67  		if !ok {
    68  			return
    69  		}
    70  
    71  		params := types.NewQueryProposalParams(proposalID)
    72  
    73  		bz, err := cliCtx.Codec.MarshalJSON(params)
    74  		if err != nil {
    75  			rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
    76  			return
    77  		}
    78  
    79  		res, height, err := cliCtx.QueryWithData("custom/gov/proposal", bz)
    80  		if err != nil {
    81  			rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
    82  			return
    83  		}
    84  
    85  		cliCtx = cliCtx.WithHeight(height)
    86  		rest.PostProcessResponse(w, cliCtx, res)
    87  	}
    88  }
    89  
    90  func queryDepositsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
    91  	return func(w http.ResponseWriter, r *http.Request) {
    92  		vars := mux.Vars(r)
    93  		strProposalID := vars[RestProposalID]
    94  
    95  		proposalID, ok := rest.ParseUint64OrReturnBadRequest(w, strProposalID)
    96  		if !ok {
    97  			return
    98  		}
    99  
   100  		cliCtx, ok = rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r)
   101  		if !ok {
   102  			return
   103  		}
   104  
   105  		params := types.NewQueryProposalParams(proposalID)
   106  
   107  		bz, err := cliCtx.Codec.MarshalJSON(params)
   108  		if err != nil {
   109  			rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
   110  			return
   111  		}
   112  
   113  		res, _, err := cliCtx.QueryWithData("custom/gov/proposal", bz)
   114  		if err != nil {
   115  			rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
   116  			return
   117  		}
   118  
   119  		var proposal types.Proposal
   120  		if err := cliCtx.Codec.UnmarshalJSON(res, &proposal); err != nil {
   121  			rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
   122  			return
   123  		}
   124  
   125  		// For inactive proposals we must query the txs directly to get the deposits
   126  		// as they're no longer in state.
   127  		propStatus := proposal.Status
   128  		if !(propStatus == types.StatusVotingPeriod || propStatus == types.StatusDepositPeriod) {
   129  			res, err = gcutils.QueryDepositsByTxQuery(cliCtx, params)
   130  		} else {
   131  			res, _, err = cliCtx.QueryWithData("custom/gov/deposits", bz)
   132  		}
   133  
   134  		if err != nil {
   135  			rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
   136  			return
   137  		}
   138  
   139  		rest.PostProcessResponse(w, cliCtx, res)
   140  	}
   141  }
   142  
   143  func queryProposerHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
   144  	return func(w http.ResponseWriter, r *http.Request) {
   145  		vars := mux.Vars(r)
   146  		strProposalID := vars[RestProposalID]
   147  
   148  		proposalID, ok := rest.ParseUint64OrReturnBadRequest(w, strProposalID)
   149  		if !ok {
   150  			return
   151  		}
   152  
   153  		cliCtx, ok = rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r)
   154  		if !ok {
   155  			return
   156  		}
   157  
   158  		res, err := gcutils.QueryProposerByTxQuery(cliCtx, proposalID)
   159  		if err != nil {
   160  			rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
   161  			return
   162  		}
   163  
   164  		rest.PostProcessResponse(w, cliCtx, res)
   165  	}
   166  }
   167  
   168  func queryDepositHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
   169  	return func(w http.ResponseWriter, r *http.Request) {
   170  		vars := mux.Vars(r)
   171  		strProposalID := vars[RestProposalID]
   172  		bechDepositorAddr := vars[RestDepositor]
   173  
   174  		if len(strProposalID) == 0 {
   175  			err := errors.New("proposalId required but not specified")
   176  			rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
   177  			return
   178  		}
   179  
   180  		proposalID, ok := rest.ParseUint64OrReturnBadRequest(w, strProposalID)
   181  		if !ok {
   182  			return
   183  		}
   184  
   185  		if len(bechDepositorAddr) == 0 {
   186  			err := errors.New("depositor address required but not specified")
   187  			rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
   188  			return
   189  		}
   190  
   191  		depositorAddr, err := sdk.AccAddressFromBech32(bechDepositorAddr)
   192  		if err != nil {
   193  			rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
   194  			return
   195  		}
   196  
   197  		cliCtx, ok = rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r)
   198  		if !ok {
   199  			return
   200  		}
   201  
   202  		params := types.NewQueryDepositParams(proposalID, depositorAddr)
   203  
   204  		bz, err := cliCtx.Codec.MarshalJSON(params)
   205  		if err != nil {
   206  			rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
   207  			return
   208  		}
   209  
   210  		res, _, err := cliCtx.QueryWithData("custom/gov/deposit", bz)
   211  		if err != nil {
   212  			rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
   213  			return
   214  		}
   215  
   216  		var deposit types.Deposit
   217  		if err := cliCtx.Codec.UnmarshalJSON(res, &deposit); err != nil {
   218  			rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
   219  			return
   220  		}
   221  
   222  		// For an empty deposit, either the proposal does not exist or is inactive in
   223  		// which case the deposit would be removed from state and should be queried
   224  		// for directly via a txs query.
   225  		if deposit.Empty() {
   226  			bz, err := cliCtx.Codec.MarshalJSON(types.NewQueryProposalParams(proposalID))
   227  			if err != nil {
   228  				rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
   229  				return
   230  			}
   231  
   232  			res, _, err = cliCtx.QueryWithData("custom/gov/proposal", bz)
   233  			if err != nil || len(res) == 0 {
   234  				err := fmt.Errorf("proposalID %d does not exist", proposalID)
   235  				rest.WriteErrorResponse(w, http.StatusNotFound, err.Error())
   236  				return
   237  			}
   238  
   239  			res, err = gcutils.QueryDepositByTxQuery(cliCtx, params)
   240  			if err != nil {
   241  				rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
   242  				return
   243  			}
   244  		}
   245  
   246  		rest.PostProcessResponse(w, cliCtx, res)
   247  	}
   248  }
   249  
   250  func queryVoteHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
   251  	return func(w http.ResponseWriter, r *http.Request) {
   252  		vars := mux.Vars(r)
   253  		strProposalID := vars[RestProposalID]
   254  		bechVoterAddr := vars[RestVoter]
   255  
   256  		if len(strProposalID) == 0 {
   257  			err := errors.New("proposalId required but not specified")
   258  			rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
   259  			return
   260  		}
   261  
   262  		proposalID, ok := rest.ParseUint64OrReturnBadRequest(w, strProposalID)
   263  		if !ok {
   264  			return
   265  		}
   266  
   267  		if len(bechVoterAddr) == 0 {
   268  			err := errors.New("voter address required but not specified")
   269  			rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
   270  			return
   271  		}
   272  
   273  		voterAddr, err := sdk.AccAddressFromBech32(bechVoterAddr)
   274  		if err != nil {
   275  			rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
   276  			return
   277  		}
   278  
   279  		cliCtx, ok = rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r)
   280  		if !ok {
   281  			return
   282  		}
   283  
   284  		params := types.NewQueryVoteParams(proposalID, voterAddr)
   285  
   286  		bz, err := cliCtx.Codec.MarshalJSON(params)
   287  		if err != nil {
   288  			rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
   289  			return
   290  		}
   291  
   292  		res, _, err := cliCtx.QueryWithData("custom/gov/vote", bz)
   293  		if err != nil {
   294  			rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
   295  			return
   296  		}
   297  
   298  		var vote types.Vote
   299  		if err := cliCtx.Codec.UnmarshalJSON(res, &vote); err != nil {
   300  			rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
   301  			return
   302  		}
   303  
   304  		// For an empty vote, either the proposal does not exist or is inactive in
   305  		// which case the vote would be removed from state and should be queried for
   306  		// directly via a txs query.
   307  		if vote.Empty() {
   308  			bz, err := cliCtx.Codec.MarshalJSON(types.NewQueryProposalParams(proposalID))
   309  			if err != nil {
   310  				rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
   311  				return
   312  			}
   313  
   314  			res, _, err = cliCtx.QueryWithData("custom/gov/proposal", bz)
   315  			if err != nil || len(res) == 0 {
   316  				err := fmt.Errorf("proposalID %d does not exist", proposalID)
   317  				rest.WriteErrorResponse(w, http.StatusNotFound, err.Error())
   318  				return
   319  			}
   320  
   321  			res, err = gcutils.QueryVoteByTxQuery(cliCtx, params)
   322  			if err != nil {
   323  				rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
   324  				return
   325  			}
   326  		}
   327  
   328  		rest.PostProcessResponse(w, cliCtx, res)
   329  	}
   330  }
   331  
   332  // todo: Split this functionality into helper functions to remove the above
   333  func queryVotesOnProposalHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
   334  	return func(w http.ResponseWriter, r *http.Request) {
   335  		_, page, limit, err := rest.ParseHTTPArgsWithLimit(r, 0)
   336  		if err != nil {
   337  			rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
   338  			return
   339  		}
   340  
   341  		vars := mux.Vars(r)
   342  		strProposalID := vars[RestProposalID]
   343  
   344  		if len(strProposalID) == 0 {
   345  			err := errors.New("proposalId required but not specified")
   346  			rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
   347  			return
   348  		}
   349  
   350  		proposalID, ok := rest.ParseUint64OrReturnBadRequest(w, strProposalID)
   351  		if !ok {
   352  			return
   353  		}
   354  
   355  		cliCtx, ok = rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r)
   356  		if !ok {
   357  			return
   358  		}
   359  
   360  		bz, err := cliCtx.Codec.MarshalJSON(types.NewQueryProposalParams(proposalID))
   361  		if err != nil {
   362  			rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
   363  			return
   364  		}
   365  
   366  		res, _, err := cliCtx.QueryWithData("custom/gov/proposal", bz)
   367  		if err != nil {
   368  			rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
   369  			return
   370  		}
   371  
   372  		var proposal types.Proposal
   373  		if err := cliCtx.Codec.UnmarshalJSON(res, &proposal); err != nil {
   374  			rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
   375  			return
   376  		}
   377  
   378  		// For inactive proposals we must query the txs directly to get the votes
   379  		// as they're no longer in state.
   380  		params := types.NewQueryProposalVotesParams(proposalID, page, limit)
   381  
   382  		propStatus := proposal.Status
   383  		if !(propStatus == types.StatusVotingPeriod || propStatus == types.StatusDepositPeriod) {
   384  			res, err = gcutils.QueryVotesByTxQuery(cliCtx, params)
   385  		} else {
   386  			bz, err = cliCtx.Codec.MarshalJSON(params)
   387  			if err != nil {
   388  				rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
   389  				return
   390  			}
   391  
   392  			res, _, err = cliCtx.QueryWithData("custom/gov/votes", bz)
   393  		}
   394  
   395  		if err != nil {
   396  			rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
   397  			return
   398  		}
   399  
   400  		rest.PostProcessResponse(w, cliCtx, res)
   401  	}
   402  }
   403  
   404  // HTTP request handler to query list of governance proposals
   405  func queryProposalsWithParameterFn(cliCtx context.CLIContext) http.HandlerFunc {
   406  	return func(w http.ResponseWriter, r *http.Request) {
   407  		_, page, limit, err := rest.ParseHTTPArgsWithLimit(r, 0)
   408  		if err != nil {
   409  			rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
   410  			return
   411  		}
   412  
   413  		cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r)
   414  		if !ok {
   415  			return
   416  		}
   417  
   418  		var (
   419  			voterAddr      sdk.AccAddress
   420  			depositorAddr  sdk.AccAddress
   421  			proposalStatus types.ProposalStatus
   422  		)
   423  
   424  		if v := r.URL.Query().Get(RestVoter); len(v) != 0 {
   425  			voterAddr, err = sdk.AccAddressFromBech32(v)
   426  			if err != nil {
   427  				rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
   428  				return
   429  			}
   430  		}
   431  
   432  		if v := r.URL.Query().Get(RestDepositor); len(v) != 0 {
   433  			depositorAddr, err = sdk.AccAddressFromBech32(v)
   434  			if err != nil {
   435  				rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
   436  				return
   437  			}
   438  		}
   439  
   440  		if v := r.URL.Query().Get(RestProposalStatus); len(v) != 0 {
   441  			proposalStatus, err = types.ProposalStatusFromString(gcutils.NormalizeProposalStatus(v))
   442  			if err != nil {
   443  				rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
   444  				return
   445  			}
   446  		}
   447  
   448  		params := types.NewQueryProposalsParams(page, limit, proposalStatus, voterAddr, depositorAddr)
   449  		bz, err := cliCtx.Codec.MarshalJSON(params)
   450  		if err != nil {
   451  			rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
   452  			return
   453  		}
   454  
   455  		route := fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryProposals)
   456  		res, height, err := cliCtx.QueryWithData(route, bz)
   457  		if err != nil {
   458  			rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
   459  			return
   460  		}
   461  
   462  		cliCtx = cliCtx.WithHeight(height)
   463  		rest.PostProcessResponse(w, cliCtx, res)
   464  	}
   465  }
   466  
   467  // todo: Split this functionality into helper functions to remove the above
   468  func queryTallyOnProposalHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
   469  	return func(w http.ResponseWriter, r *http.Request) {
   470  		vars := mux.Vars(r)
   471  		strProposalID := vars[RestProposalID]
   472  
   473  		if len(strProposalID) == 0 {
   474  			err := errors.New("proposalId required but not specified")
   475  			rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
   476  			return
   477  		}
   478  
   479  		proposalID, ok := rest.ParseUint64OrReturnBadRequest(w, strProposalID)
   480  		if !ok {
   481  			return
   482  		}
   483  
   484  		cliCtx, ok = rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r)
   485  		if !ok {
   486  			return
   487  		}
   488  
   489  		params := types.NewQueryProposalParams(proposalID)
   490  
   491  		bz, err := cliCtx.Codec.MarshalJSON(params)
   492  		if err != nil {
   493  			rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
   494  			return
   495  		}
   496  
   497  		res, height, err := cliCtx.QueryWithData("custom/gov/tally", bz)
   498  		if err != nil {
   499  			rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
   500  			return
   501  		}
   502  
   503  		cliCtx = cliCtx.WithHeight(height)
   504  		rest.PostProcessResponse(w, cliCtx, res)
   505  	}
   506  }