-
Notifications
You must be signed in to change notification settings - Fork 0
/
query.go
160 lines (129 loc) · 4.96 KB
/
query.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
package common
import (
"context"
"fmt"
"strings"
"time"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
authtx "github.com/cosmos/cosmos-sdk/x/auth/tx"
"github.com/furya-official/mage/x/committee/types"
)
// Note: QueryProposer is copied in from the gov module
const (
defaultPage = 1
defaultLimit = 30 // should be consistent with tendermint/tendermint/rpc/core/pipe.go:19
)
// Proposer contains metadata of a governance proposal used for querying a proposer.
type Proposer struct {
ProposalID uint64 `json:"proposal_id" yaml:"proposal_id"`
Proposer string `json:"proposer" yaml:"proposer"`
}
// NewProposer returns a new Proposer given id and proposer
func NewProposer(proposalID uint64, proposer string) Proposer {
return Proposer{proposalID, proposer}
}
func (p Proposer) String() string {
return fmt.Sprintf("Proposal with ID %d was proposed by %s", p.ProposalID, p.Proposer)
}
// QueryProposer will query for a proposer of a governance proposal by ID.
func QueryProposer(cliCtx client.Context, proposalID uint64) (Proposer, error) {
events := []string{
fmt.Sprintf("%s.%s='%s'", sdk.EventTypeMessage, sdk.AttributeKeyAction, types.TypeMsgSubmitProposal),
fmt.Sprintf("%s.%s='%s'", types.EventTypeProposalSubmit, types.AttributeKeyProposalID, []byte(fmt.Sprintf("%d", proposalID))),
}
// NOTE: SearchTxs is used to facilitate the txs query which does not currently
// support configurable pagination.
searchResult, err := authtx.QueryTxsByEvents(cliCtx, events, defaultPage, defaultLimit, "")
if err != nil {
return Proposer{}, err
}
for _, info := range searchResult.Txs {
for _, msg := range info.GetTx().GetMsgs() {
// there should only be a single proposal under the given conditions
if subMsg, ok := msg.(*types.MsgSubmitProposal); ok {
return NewProposer(proposalID, subMsg.Proposer), nil
}
}
}
return Proposer{}, fmt.Errorf("failed to find the proposer for proposalID %d", proposalID)
}
// QueryProposalByID returns a proposal from state if present or fallbacks to searching old blocks
func QueryProposalByID(cliCtx client.Context, cdc *codec.LegacyAmino, queryRoute string, proposalID uint64) (*types.Proposal, int64, error) {
bz, err := cdc.MarshalJSON(types.NewQueryProposalParams(proposalID))
if err != nil {
return nil, 0, err
}
res, height, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", queryRoute, types.QueryProposal), bz)
if err == nil {
var proposal types.Proposal
cdc.MustUnmarshalJSON(res, &proposal)
return &proposal, height, nil
}
// NOTE: !errors.Is(err, types.ErrUnknownProposal) does not work here
if err != nil && !strings.Contains(err.Error(), "proposal not found") {
return nil, 0, err
}
res, height, err = cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", queryRoute, types.QueryNextProposalID), nil)
if err != nil {
return nil, 0, err
}
var nextProposalID uint64
cdc.MustUnmarshalJSON(res, &nextProposalID)
if proposalID >= nextProposalID {
return nil, 0, sdkerrors.Wrapf(types.ErrUnknownProposal, "%d", proposalID)
}
events := []string{
fmt.Sprintf("%s.%s='%s'", sdk.EventTypeMessage, sdk.AttributeKeyAction, types.TypeMsgSubmitProposal),
fmt.Sprintf("%s.%s='%s'", types.EventTypeProposalSubmit, types.AttributeKeyProposalID, []byte(fmt.Sprintf("%d", proposalID))),
}
searchResult, err := authtx.QueryTxsByEvents(cliCtx, events, defaultPage, defaultLimit, "")
if err != nil {
return nil, 0, err
}
for _, info := range searchResult.Txs {
for _, msg := range info.GetTx().GetMsgs() {
if subMsg, ok := msg.(*types.MsgSubmitProposal); ok {
deadline, err := calculateDeadline(cliCtx, cdc, queryRoute, subMsg.CommitteeID, info.Height)
if err != nil {
return nil, 0, err
}
proposal, err := types.NewProposal(subMsg.GetPubProposal(), proposalID, subMsg.CommitteeID, deadline)
if err != nil {
return nil, 0, err
}
return &proposal, height, nil
}
}
}
return nil, 0, sdkerrors.Wrapf(types.ErrUnknownProposal, "%d", proposalID)
}
// calculateDeadline returns the proposal deadline for a committee and block height
func calculateDeadline(cliCtx client.Context, cdc *codec.LegacyAmino, queryRoute string, committeeID uint64, blockHeight int64) (time.Time, error) {
var deadline time.Time
bz, err := cdc.MarshalJSON(types.NewQueryCommitteeParams(committeeID))
if err != nil {
return deadline, err
}
res, _, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/%s", queryRoute, types.QueryCommittee), bz)
if err != nil {
return deadline, err
}
var committee types.Committee
err = cdc.UnmarshalJSON(res, &committee)
if err != nil {
return deadline, err
}
node, err := cliCtx.GetNode()
if err != nil {
return deadline, err
}
resultBlock, err := node.Block(context.Background(), &blockHeight)
if err != nil {
return deadline, err
}
deadline = resultBlock.Block.Header.Time.Add(committee.GetProposalDuration())
return deadline, nil
}