-
Notifications
You must be signed in to change notification settings - Fork 692
/
gov_vote_ante.go
141 lines (121 loc) · 3.62 KB
/
gov_vote_ante.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
package ante
import (
errorsmod "cosmossdk.io/errors"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/authz"
govv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1"
govv1beta1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1"
stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
gaiaerrors "github.com/cosmos/gaia/v17/types/errors"
)
var (
minStakedTokens = sdk.NewDec(1000000) // 1_000_000 uatom (or 1 atom)
maxDelegationsChecked = 100 // number of delegation to check for the minStakedTokens
)
// SetMinStakedTokens sets the minimum amount of staked tokens required to vote
// Should only be used in testing
func SetMinStakedTokens(tokens sdk.Dec) {
minStakedTokens = tokens
}
type GovVoteDecorator struct {
stakingKeeper *stakingkeeper.Keeper
cdc codec.BinaryCodec
}
func NewGovVoteDecorator(cdc codec.BinaryCodec, stakingKeeper *stakingkeeper.Keeper) GovVoteDecorator {
return GovVoteDecorator{
stakingKeeper: stakingKeeper,
cdc: cdc,
}
}
func (g GovVoteDecorator) AnteHandle(
ctx sdk.Context, tx sdk.Tx,
simulate bool, next sdk.AnteHandler,
) (newCtx sdk.Context, err error) {
// do not run check during simulations
if simulate {
return next(ctx, tx, simulate)
}
msgs := tx.GetMsgs()
if err = g.ValidateVoteMsgs(ctx, msgs); err != nil {
return ctx, err
}
return next(ctx, tx, simulate)
}
// ValidateVoteMsgs checks if a voter has enough stake to vote
func (g GovVoteDecorator) ValidateVoteMsgs(ctx sdk.Context, msgs []sdk.Msg) error {
validMsg := func(m sdk.Msg) error {
var accAddr sdk.AccAddress
var err error
switch msg := m.(type) {
case *govv1beta1.MsgVote:
accAddr, err = sdk.AccAddressFromBech32(msg.Voter)
if err != nil {
return err
}
case *govv1.MsgVote:
accAddr, err = sdk.AccAddressFromBech32(msg.Voter)
if err != nil {
return err
}
default:
// not a vote message - nothing to validate
return nil
}
if minStakedTokens.IsZero() {
return nil
}
enoughStake := false
delegationCount := 0
stakedTokens := sdk.NewDec(0)
g.stakingKeeper.IterateDelegatorDelegations(ctx, accAddr, func(delegation stakingtypes.Delegation) bool {
validatorAddr, err := sdk.ValAddressFromBech32(delegation.ValidatorAddress)
if err != nil {
panic(err) // shouldn't happen
}
validator, found := g.stakingKeeper.GetValidator(ctx, validatorAddr)
if found {
shares := delegation.Shares
tokens := validator.TokensFromSharesTruncated(shares)
stakedTokens = stakedTokens.Add(tokens)
if stakedTokens.GTE(minStakedTokens) {
enoughStake = true
return true // break the iteration
}
}
delegationCount++
// break the iteration if maxDelegationsChecked were already checked
return delegationCount >= maxDelegationsChecked
})
if !enoughStake {
return errorsmod.Wrapf(gaiaerrors.ErrInsufficientStake, "insufficient stake for voting - min required %v", minStakedTokens)
}
return nil
}
validAuthz := func(execMsg *authz.MsgExec) error {
for _, v := range execMsg.Msgs {
var innerMsg sdk.Msg
if err := g.cdc.UnpackAny(v, &innerMsg); err != nil {
return errorsmod.Wrap(gaiaerrors.ErrUnauthorized, "cannot unmarshal authz exec msgs")
}
if err := validMsg(innerMsg); err != nil {
return err
}
}
return nil
}
for _, m := range msgs {
if msg, ok := m.(*authz.MsgExec); ok {
if err := validAuthz(msg); err != nil {
return err
}
continue
}
// validate normal msgs
if err := validMsg(m); err != nil {
return err
}
}
return nil
}