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 }