github.com/cosmos/cosmos-sdk@v0.50.10/x/gov/keeper/grpc_query.go (about) 1 package keeper 2 3 import ( 4 "context" 5 6 "google.golang.org/grpc/codes" 7 "google.golang.org/grpc/status" 8 9 "cosmossdk.io/collections" 10 "cosmossdk.io/errors" 11 sdkmath "cosmossdk.io/math" 12 13 sdk "github.com/cosmos/cosmos-sdk/types" 14 "github.com/cosmos/cosmos-sdk/types/query" 15 v3 "github.com/cosmos/cosmos-sdk/x/gov/migrations/v3" 16 v1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" 17 "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" 18 ) 19 20 var _ v1.QueryServer = queryServer{} 21 22 type queryServer struct{ k *Keeper } 23 24 func NewQueryServer(k *Keeper) v1.QueryServer { 25 return queryServer{k: k} 26 } 27 28 func (q queryServer) Constitution(ctx context.Context, _ *v1.QueryConstitutionRequest) (*v1.QueryConstitutionResponse, error) { 29 constitution, err := q.k.Constitution.Get(ctx) 30 if err != nil { 31 return nil, err 32 } 33 return &v1.QueryConstitutionResponse{Constitution: constitution}, nil 34 } 35 36 // Proposal returns proposal details based on ProposalID 37 func (q queryServer) Proposal(ctx context.Context, req *v1.QueryProposalRequest) (*v1.QueryProposalResponse, error) { 38 if req == nil { 39 return nil, status.Error(codes.InvalidArgument, "invalid request") 40 } 41 42 if req.ProposalId == 0 { 43 return nil, status.Error(codes.InvalidArgument, "proposal id can not be 0") 44 } 45 46 proposal, err := q.k.Proposals.Get(ctx, req.ProposalId) 47 if err != nil { 48 if errors.IsOf(err, collections.ErrNotFound) { 49 return nil, status.Errorf(codes.NotFound, "proposal %d doesn't exist", req.ProposalId) 50 } 51 return nil, status.Error(codes.Internal, err.Error()) 52 } 53 54 return &v1.QueryProposalResponse{Proposal: &proposal}, nil 55 } 56 57 // Proposals implements the Query/Proposals gRPC method 58 func (q queryServer) Proposals(ctx context.Context, req *v1.QueryProposalsRequest) (*v1.QueryProposalsResponse, error) { 59 filteredProposals, pageRes, err := query.CollectionFilteredPaginate(ctx, q.k.Proposals, req.Pagination, func(key uint64, p v1.Proposal) (include bool, err error) { 60 matchVoter, matchDepositor, matchStatus := true, true, true 61 62 // match status (if supplied/valid) 63 if v1.ValidProposalStatus(req.ProposalStatus) { 64 matchStatus = p.Status == req.ProposalStatus 65 } 66 67 // match voter address (if supplied) 68 if len(req.Voter) > 0 { 69 voter, err := q.k.authKeeper.AddressCodec().StringToBytes(req.Voter) 70 if err != nil { 71 return false, err 72 } 73 74 has, err := q.k.Votes.Has(ctx, collections.Join(p.Id, sdk.AccAddress(voter))) 75 // if no error, vote found, matchVoter = true 76 matchVoter = err == nil && has 77 } 78 79 // match depositor (if supplied) 80 if len(req.Depositor) > 0 { 81 depositor, err := q.k.authKeeper.AddressCodec().StringToBytes(req.Depositor) 82 if err != nil { 83 return false, err 84 } 85 has, err := q.k.Deposits.Has(ctx, collections.Join(p.Id, sdk.AccAddress(depositor))) 86 // if no error, deposit found, matchDepositor = true 87 matchDepositor = err == nil && has 88 } 89 90 // if all match, append to results 91 if matchVoter && matchDepositor && matchStatus { 92 return true, nil 93 } 94 // continue to next item, do not include because we're appending results above. 95 return false, nil 96 }, func(_ uint64, value v1.Proposal) (*v1.Proposal, error) { 97 return &value, nil 98 }) 99 100 if err != nil && !errors.IsOf(err, collections.ErrInvalidIterator) { 101 return nil, status.Error(codes.Internal, err.Error()) 102 } 103 104 return &v1.QueryProposalsResponse{Proposals: filteredProposals, Pagination: pageRes}, nil 105 } 106 107 // Vote returns Voted information based on proposalID, voterAddr 108 func (q queryServer) Vote(ctx context.Context, req *v1.QueryVoteRequest) (*v1.QueryVoteResponse, error) { 109 if req == nil { 110 return nil, status.Error(codes.InvalidArgument, "invalid request") 111 } 112 113 if req.ProposalId == 0 { 114 return nil, status.Error(codes.InvalidArgument, "proposal id can not be 0") 115 } 116 117 if req.Voter == "" { 118 return nil, status.Error(codes.InvalidArgument, "empty voter address") 119 } 120 121 voter, err := q.k.authKeeper.AddressCodec().StringToBytes(req.Voter) 122 if err != nil { 123 return nil, err 124 } 125 vote, err := q.k.Votes.Get(ctx, collections.Join(req.ProposalId, sdk.AccAddress(voter))) 126 if err != nil { 127 if errors.IsOf(err, collections.ErrNotFound) { 128 return nil, status.Errorf(codes.InvalidArgument, 129 "voter: %v not found for proposal: %v", req.Voter, req.ProposalId) 130 } 131 return nil, status.Error(codes.Internal, err.Error()) 132 } 133 134 return &v1.QueryVoteResponse{Vote: &vote}, nil 135 } 136 137 // Votes returns single proposal's votes 138 func (q queryServer) Votes(ctx context.Context, req *v1.QueryVotesRequest) (*v1.QueryVotesResponse, error) { 139 if req == nil { 140 return nil, status.Error(codes.InvalidArgument, "invalid request") 141 } 142 143 if req.ProposalId == 0 { 144 return nil, status.Error(codes.InvalidArgument, "proposal id can not be 0") 145 } 146 147 votes, pageRes, err := query.CollectionPaginate(ctx, q.k.Votes, req.Pagination, func(_ collections.Pair[uint64, sdk.AccAddress], value v1.Vote) (vote *v1.Vote, err error) { 148 return &value, nil 149 }, query.WithCollectionPaginationPairPrefix[uint64, sdk.AccAddress](req.ProposalId)) 150 if err != nil { 151 return nil, status.Error(codes.Internal, err.Error()) 152 } 153 154 return &v1.QueryVotesResponse{Votes: votes, Pagination: pageRes}, nil 155 } 156 157 // Params queries all params 158 func (q queryServer) Params(ctx context.Context, req *v1.QueryParamsRequest) (*v1.QueryParamsResponse, error) { 159 if req == nil { 160 return nil, status.Error(codes.InvalidArgument, "invalid request") 161 } 162 163 params, err := q.k.Params.Get(ctx) 164 if err != nil { 165 return nil, err 166 } 167 response := &v1.QueryParamsResponse{} 168 169 //nolint:staticcheck // needed for legacy parameters 170 switch req.ParamsType { 171 case v1.ParamDeposit: 172 depositParams := v1.NewDepositParams(params.MinDeposit, params.MaxDepositPeriod) 173 response.DepositParams = &depositParams 174 175 case v1.ParamVoting: 176 votingParams := v1.NewVotingParams(params.VotingPeriod) 177 response.VotingParams = &votingParams 178 179 case v1.ParamTallying: 180 tallyParams := v1.NewTallyParams(params.Quorum, params.Threshold, params.VetoThreshold) 181 response.TallyParams = &tallyParams 182 default: 183 if len(req.ParamsType) > 0 { 184 return nil, status.Errorf(codes.InvalidArgument, "unknown params type: %s", req.ParamsType) 185 } 186 } 187 response.Params = ¶ms 188 189 return response, nil 190 } 191 192 // Deposit queries single deposit information based on proposalID, depositAddr. 193 func (q queryServer) Deposit(ctx context.Context, req *v1.QueryDepositRequest) (*v1.QueryDepositResponse, error) { 194 if req == nil { 195 return nil, status.Error(codes.InvalidArgument, "invalid request") 196 } 197 198 if req.ProposalId == 0 { 199 return nil, status.Error(codes.InvalidArgument, "proposal id can not be 0") 200 } 201 202 if req.Depositor == "" { 203 return nil, status.Error(codes.InvalidArgument, "empty depositor address") 204 } 205 206 depositor, err := q.k.authKeeper.AddressCodec().StringToBytes(req.Depositor) 207 if err != nil { 208 return nil, err 209 } 210 deposit, err := q.k.Deposits.Get(ctx, collections.Join(req.ProposalId, sdk.AccAddress(depositor))) 211 if err != nil { 212 return nil, status.Error(codes.NotFound, err.Error()) 213 } 214 215 return &v1.QueryDepositResponse{Deposit: &deposit}, nil 216 } 217 218 // Deposits returns single proposal's all deposits 219 func (q queryServer) Deposits(ctx context.Context, req *v1.QueryDepositsRequest) (*v1.QueryDepositsResponse, error) { 220 if req == nil { 221 return nil, status.Error(codes.InvalidArgument, "invalid request") 222 } 223 224 if req.ProposalId == 0 { 225 return nil, status.Error(codes.InvalidArgument, "proposal id can not be 0") 226 } 227 228 var deposits []*v1.Deposit 229 deposits, pageRes, err := query.CollectionPaginate(ctx, q.k.Deposits, req.Pagination, func(_ collections.Pair[uint64, sdk.AccAddress], deposit v1.Deposit) (*v1.Deposit, error) { 230 return &deposit, nil 231 }, query.WithCollectionPaginationPairPrefix[uint64, sdk.AccAddress](req.ProposalId)) 232 if err != nil { 233 return nil, status.Error(codes.Internal, err.Error()) 234 } 235 236 return &v1.QueryDepositsResponse{Deposits: deposits, Pagination: pageRes}, nil 237 } 238 239 // TallyResult queries the tally of a proposal vote 240 func (q queryServer) TallyResult(ctx context.Context, req *v1.QueryTallyResultRequest) (*v1.QueryTallyResultResponse, error) { 241 if req == nil { 242 return nil, status.Error(codes.InvalidArgument, "invalid request") 243 } 244 245 if req.ProposalId == 0 { 246 return nil, status.Error(codes.InvalidArgument, "proposal id can not be 0") 247 } 248 249 proposal, err := q.k.Proposals.Get(ctx, req.ProposalId) 250 if err != nil { 251 if errors.IsOf(err, collections.ErrNotFound) { 252 return nil, status.Errorf(codes.NotFound, "proposal %d doesn't exist", req.ProposalId) 253 } 254 return nil, status.Error(codes.Internal, err.Error()) 255 } 256 257 var tallyResult v1.TallyResult 258 259 switch { 260 case proposal.Status == v1.StatusDepositPeriod: 261 tallyResult = v1.EmptyTallyResult() 262 263 case proposal.Status == v1.StatusPassed || proposal.Status == v1.StatusRejected || proposal.Status == v1.StatusFailed: 264 tallyResult = *proposal.FinalTallyResult 265 266 default: 267 // proposal is in voting period 268 var err error 269 _, _, tallyResult, err = q.k.Tally(ctx, proposal) 270 if err != nil { 271 return nil, err 272 } 273 } 274 275 return &v1.QueryTallyResultResponse{Tally: &tallyResult}, nil 276 } 277 278 var _ v1beta1.QueryServer = legacyQueryServer{} 279 280 type legacyQueryServer struct{ qs v1.QueryServer } 281 282 // NewLegacyQueryServer returns an implementation of the v1beta1 legacy QueryServer interface. 283 func NewLegacyQueryServer(k *Keeper) v1beta1.QueryServer { 284 return &legacyQueryServer{qs: NewQueryServer(k)} 285 } 286 287 func (q legacyQueryServer) Proposal(ctx context.Context, req *v1beta1.QueryProposalRequest) (*v1beta1.QueryProposalResponse, error) { 288 resp, err := q.qs.Proposal(ctx, &v1.QueryProposalRequest{ 289 ProposalId: req.ProposalId, 290 }) 291 if err != nil { 292 return nil, err 293 } 294 295 proposal, err := v3.ConvertToLegacyProposal(*resp.Proposal) 296 if err != nil { 297 return nil, err 298 } 299 300 return &v1beta1.QueryProposalResponse{Proposal: proposal}, nil 301 } 302 303 func (q legacyQueryServer) Proposals(ctx context.Context, req *v1beta1.QueryProposalsRequest) (*v1beta1.QueryProposalsResponse, error) { 304 resp, err := q.qs.Proposals(ctx, &v1.QueryProposalsRequest{ 305 ProposalStatus: v1.ProposalStatus(req.ProposalStatus), 306 Voter: req.Voter, 307 Depositor: req.Depositor, 308 Pagination: req.Pagination, 309 }) 310 if err != nil { 311 return nil, err 312 } 313 314 legacyProposals := make([]v1beta1.Proposal, len(resp.Proposals)) 315 for idx, proposal := range resp.Proposals { 316 legacyProposals[idx], err = v3.ConvertToLegacyProposal(*proposal) 317 if err != nil { 318 return nil, err 319 } 320 } 321 322 return &v1beta1.QueryProposalsResponse{ 323 Proposals: legacyProposals, 324 Pagination: resp.Pagination, 325 }, nil 326 } 327 328 func (q legacyQueryServer) Vote(ctx context.Context, req *v1beta1.QueryVoteRequest) (*v1beta1.QueryVoteResponse, error) { 329 resp, err := q.qs.Vote(ctx, &v1.QueryVoteRequest{ 330 ProposalId: req.ProposalId, 331 Voter: req.Voter, 332 }) 333 if err != nil { 334 return nil, err 335 } 336 337 vote, err := v3.ConvertToLegacyVote(*resp.Vote) 338 if err != nil { 339 return nil, err 340 } 341 342 return &v1beta1.QueryVoteResponse{Vote: vote}, nil 343 } 344 345 func (q legacyQueryServer) Votes(ctx context.Context, req *v1beta1.QueryVotesRequest) (*v1beta1.QueryVotesResponse, error) { 346 resp, err := q.qs.Votes(ctx, &v1.QueryVotesRequest{ 347 ProposalId: req.ProposalId, 348 Pagination: req.Pagination, 349 }) 350 if err != nil { 351 return nil, err 352 } 353 354 votes := make([]v1beta1.Vote, len(resp.Votes)) 355 for i, v := range resp.Votes { 356 votes[i], err = v3.ConvertToLegacyVote(*v) 357 if err != nil { 358 return nil, err 359 } 360 } 361 362 return &v1beta1.QueryVotesResponse{ 363 Votes: votes, 364 Pagination: resp.Pagination, 365 }, nil 366 } 367 368 //nolint:staticcheck // this is needed for legacy param support 369 func (q legacyQueryServer) Params(ctx context.Context, req *v1beta1.QueryParamsRequest) (*v1beta1.QueryParamsResponse, error) { 370 resp, err := q.qs.Params(ctx, &v1.QueryParamsRequest{ 371 ParamsType: req.ParamsType, 372 }) 373 if err != nil { 374 return nil, err 375 } 376 377 response := &v1beta1.QueryParamsResponse{} 378 379 if resp.DepositParams == nil && resp.VotingParams == nil && resp.TallyParams == nil { 380 return nil, status.Errorf(codes.InvalidArgument, "%s is not a valid parameter type", req.ParamsType) 381 } 382 383 if resp.DepositParams != nil { 384 minDeposit := sdk.NewCoins(resp.DepositParams.MinDeposit...) 385 response.DepositParams = v1beta1.NewDepositParams(minDeposit, *resp.DepositParams.MaxDepositPeriod) 386 } 387 388 if resp.VotingParams != nil { 389 response.VotingParams = v1beta1.NewVotingParams(*resp.VotingParams.VotingPeriod) 390 } 391 392 if resp.TallyParams != nil { 393 quorum, err := sdkmath.LegacyNewDecFromStr(resp.TallyParams.Quorum) 394 if err != nil { 395 return nil, err 396 } 397 threshold, err := sdkmath.LegacyNewDecFromStr(resp.TallyParams.Threshold) 398 if err != nil { 399 return nil, err 400 } 401 vetoThreshold, err := sdkmath.LegacyNewDecFromStr(resp.TallyParams.VetoThreshold) 402 if err != nil { 403 return nil, err 404 } 405 406 response.TallyParams = v1beta1.NewTallyParams(quorum, threshold, vetoThreshold) 407 } 408 409 return response, nil 410 } 411 412 func (q legacyQueryServer) Deposit(ctx context.Context, req *v1beta1.QueryDepositRequest) (*v1beta1.QueryDepositResponse, error) { 413 resp, err := q.qs.Deposit(ctx, &v1.QueryDepositRequest{ 414 ProposalId: req.ProposalId, 415 Depositor: req.Depositor, 416 }) 417 if err != nil { 418 return nil, err 419 } 420 421 deposit := v3.ConvertToLegacyDeposit(resp.Deposit) 422 return &v1beta1.QueryDepositResponse{Deposit: deposit}, nil 423 } 424 425 func (q legacyQueryServer) Deposits(ctx context.Context, req *v1beta1.QueryDepositsRequest) (*v1beta1.QueryDepositsResponse, error) { 426 resp, err := q.qs.Deposits(ctx, &v1.QueryDepositsRequest{ 427 ProposalId: req.ProposalId, 428 Pagination: req.Pagination, 429 }) 430 if err != nil { 431 return nil, err 432 } 433 deposits := make([]v1beta1.Deposit, len(resp.Deposits)) 434 for idx, deposit := range resp.Deposits { 435 deposits[idx] = v3.ConvertToLegacyDeposit(deposit) 436 } 437 438 return &v1beta1.QueryDepositsResponse{Deposits: deposits, Pagination: resp.Pagination}, nil 439 } 440 441 func (q legacyQueryServer) TallyResult(ctx context.Context, req *v1beta1.QueryTallyResultRequest) (*v1beta1.QueryTallyResultResponse, error) { 442 resp, err := q.qs.TallyResult(ctx, &v1.QueryTallyResultRequest{ 443 ProposalId: req.ProposalId, 444 }) 445 if err != nil { 446 return nil, err 447 } 448 449 tally, err := v3.ConvertToLegacyTallyResult(resp.Tally) 450 if err != nil { 451 return nil, err 452 } 453 454 return &v1beta1.QueryTallyResultResponse{Tally: tally}, nil 455 }