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 = &params
   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  }