github.com/cosmos/cosmos-sdk@v0.50.10/x/gov/client/utils/query.go (about) 1 package utils 2 3 import ( 4 "fmt" 5 6 "github.com/cosmos/cosmos-sdk/client" 7 sdk "github.com/cosmos/cosmos-sdk/types" 8 authtx "github.com/cosmos/cosmos-sdk/x/auth/tx" 9 "github.com/cosmos/cosmos-sdk/x/gov/types" 10 v1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" 11 "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" 12 ) 13 14 const ( 15 defaultPage = 1 16 defaultLimit = 30 // should be consistent with https://github.com/cometbft/cometbft/tree/v0.37.x/rpc/core#pagination 17 ) 18 19 // Proposer contains metadata of a governance proposal used for querying a 20 // proposer. 21 type Proposer struct { 22 ProposalID uint64 `json:"proposal_id" yaml:"proposal_id"` 23 Proposer string `json:"proposer" yaml:"proposer"` 24 } 25 26 // NewProposer returns a new Proposer given id and proposer 27 func NewProposer(proposalID uint64, proposer string) Proposer { 28 return Proposer{proposalID, proposer} 29 } 30 31 // String implements the fmt.Stringer interface. 32 func (p Proposer) String() string { 33 return fmt.Sprintf("Proposal with ID %d was proposed by %s", p.ProposalID, p.Proposer) 34 } 35 36 // QueryVotesByTxQuery will query for votes via a direct txs tags query. It 37 // will fetch and build votes directly from the returned txs and returns a JSON 38 // marshaled result or any error that occurred. 39 func QueryVotesByTxQuery(clientCtx client.Context, params v1.QueryProposalVotesParams) ([]byte, error) { 40 var ( 41 votes []*v1.Vote 42 nextTxPage = defaultPage 43 totalLimit = params.Limit * params.Page 44 ) 45 46 // query interrupted either if we collected enough votes or tx indexer run out of relevant txs 47 for len(votes) < totalLimit { 48 // Search for both (legacy) votes and weighted votes. 49 q := fmt.Sprintf("%s.%s='%d'", types.EventTypeProposalVote, types.AttributeKeyProposalID, params.ProposalID) 50 searchResult, err := authtx.QueryTxsByEvents(clientCtx, nextTxPage, defaultLimit, q, "") 51 if err != nil { 52 return nil, err 53 } 54 55 for _, info := range searchResult.Txs { 56 for _, msg := range info.GetTx().GetMsgs() { 57 if voteMsg, ok := msg.(*v1beta1.MsgVote); ok { 58 votes = append(votes, &v1.Vote{ 59 Voter: voteMsg.Voter, 60 ProposalId: params.ProposalID, 61 Options: v1.NewNonSplitVoteOption(v1.VoteOption(voteMsg.Option)), 62 }) 63 } 64 65 if voteMsg, ok := msg.(*v1.MsgVote); ok { 66 votes = append(votes, &v1.Vote{ 67 Voter: voteMsg.Voter, 68 ProposalId: params.ProposalID, 69 Options: v1.NewNonSplitVoteOption(voteMsg.Option), 70 }) 71 } 72 73 if voteWeightedMsg, ok := msg.(*v1beta1.MsgVoteWeighted); ok { 74 votes = append(votes, convertVote(voteWeightedMsg)) 75 } 76 77 if voteWeightedMsg, ok := msg.(*v1.MsgVoteWeighted); ok { 78 votes = append(votes, &v1.Vote{ 79 Voter: voteWeightedMsg.Voter, 80 ProposalId: params.ProposalID, 81 Options: voteWeightedMsg.Options, 82 }) 83 } 84 } 85 } 86 if len(searchResult.Txs) != defaultLimit { 87 break 88 } 89 90 nextTxPage++ 91 } 92 start, end := client.Paginate(len(votes), params.Page, params.Limit, 100) 93 if start < 0 || end < 0 { 94 votes = []*v1.Vote{} 95 } else { 96 votes = votes[start:end] 97 } 98 99 bz, err := clientCtx.LegacyAmino.MarshalJSON(votes) 100 if err != nil { 101 return nil, err 102 } 103 104 return bz, nil 105 } 106 107 // QueryVoteByTxQuery will query for a single vote via a direct txs tags query. 108 func QueryVoteByTxQuery(clientCtx client.Context, params v1.QueryVoteParams) ([]byte, error) { 109 q1 := fmt.Sprintf("%s.%s='%d'", types.EventTypeProposalVote, types.AttributeKeyProposalID, params.ProposalID) 110 q2 := fmt.Sprintf("%s.%s='%s'", sdk.EventTypeMessage, sdk.AttributeKeySender, params.Voter.String()) 111 q3 := fmt.Sprintf("%s.%s='%s'", sdk.EventTypeMessage, sdk.AttributeKeySender, params.Voter) 112 searchResult, err := authtx.QueryTxsByEvents(clientCtx, defaultPage, defaultLimit, fmt.Sprintf("%s AND (%s OR %s)", q1, q2, q3), "") 113 if err != nil { 114 return nil, err 115 } 116 117 for _, info := range searchResult.Txs { 118 for _, msg := range info.GetTx().GetMsgs() { 119 // there should only be a single vote under the given conditions 120 var vote *v1.Vote 121 if voteMsg, ok := msg.(*v1beta1.MsgVote); ok { 122 vote = &v1.Vote{ 123 Voter: voteMsg.Voter, 124 ProposalId: params.ProposalID, 125 Options: v1.NewNonSplitVoteOption(v1.VoteOption(voteMsg.Option)), 126 } 127 } 128 129 if voteMsg, ok := msg.(*v1.MsgVote); ok { 130 vote = &v1.Vote{ 131 Voter: voteMsg.Voter, 132 ProposalId: params.ProposalID, 133 Options: v1.NewNonSplitVoteOption(voteMsg.Option), 134 } 135 } 136 137 if voteWeightedMsg, ok := msg.(*v1beta1.MsgVoteWeighted); ok { 138 vote = convertVote(voteWeightedMsg) 139 } 140 141 if voteWeightedMsg, ok := msg.(*v1.MsgVoteWeighted); ok { 142 vote = &v1.Vote{ 143 Voter: voteWeightedMsg.Voter, 144 ProposalId: params.ProposalID, 145 Options: voteWeightedMsg.Options, 146 } 147 } 148 149 if vote != nil { 150 bz, err := clientCtx.Codec.MarshalJSON(vote) 151 if err != nil { 152 return nil, err 153 } 154 155 return bz, nil 156 } 157 } 158 } 159 160 return nil, fmt.Errorf("address '%s' did not vote on proposalID %d", params.Voter, params.ProposalID) 161 } 162 163 // QueryProposerByTxQuery will query for a proposer of a governance proposal by ID. 164 // Deprecated: Should not be used, as not always accurate. It will be removed in v0.51. 165 func QueryProposerByTxQuery(clientCtx client.Context, proposalID uint64) (Proposer, error) { 166 q := fmt.Sprintf("%s.%s='%d'", types.EventTypeSubmitProposal, types.AttributeKeyProposalID, proposalID) 167 searchResult, err := authtx.QueryTxsByEvents(clientCtx, defaultPage, defaultLimit, q, "") 168 if err != nil { 169 return Proposer{}, err 170 } 171 172 for _, info := range searchResult.Txs { 173 for _, msg := range info.GetTx().GetMsgs() { 174 // there should only be a single proposal under the given conditions 175 if subMsg, ok := msg.(*v1beta1.MsgSubmitProposal); ok { 176 return NewProposer(proposalID, subMsg.Proposer), nil 177 } 178 if subMsg, ok := msg.(*v1.MsgSubmitProposal); ok { 179 return NewProposer(proposalID, subMsg.Proposer), nil 180 } 181 } 182 } 183 184 return Proposer{}, fmt.Errorf("failed to find the proposer for proposalID %d", proposalID) 185 } 186 187 // convertVote converts a MsgVoteWeighted into a *v1.Vote. 188 func convertVote(v *v1beta1.MsgVoteWeighted) *v1.Vote { 189 opts := make([]*v1.WeightedVoteOption, len(v.Options)) 190 for i, o := range v.Options { 191 opts[i] = &v1.WeightedVoteOption{ 192 Option: v1.VoteOption(o.Option), 193 Weight: o.Weight.String(), 194 } 195 } 196 return &v1.Vote{ 197 Voter: v.Voter, 198 ProposalId: v.ProposalId, 199 Options: opts, 200 } 201 }