Merge PR #4665: Refactor x/gov Module Structure (prep for module spec)

This commit is contained in:
Federico Kunze 2019-08-08 21:51:18 +02:00 committed by Alexander Bezobchuk
parent 83db58ec5e
commit e4c8bd72b7
56 changed files with 2757 additions and 2633 deletions

View File

@ -0,0 +1,3 @@
#4665 Refactored `x/gov` module structure and dev-UX:
- Prepare for module spec integration
- Update gov keys to use big endian encoding instead of little endian

View File

@ -136,7 +136,7 @@ func NewSimApp(
mintSubspace := app.paramsKeeper.Subspace(mint.DefaultParamspace)
distrSubspace := app.paramsKeeper.Subspace(distr.DefaultParamspace)
slashingSubspace := app.paramsKeeper.Subspace(slashing.DefaultParamspace)
govSubspace := app.paramsKeeper.Subspace(gov.DefaultParamspace)
govSubspace := app.paramsKeeper.Subspace(gov.DefaultParamspace).WithKeyTable(gov.ParamKeyTable())
crisisSubspace := app.paramsKeeper.Subspace(crisis.DefaultParamspace)
// add keepers
@ -157,7 +157,7 @@ func NewSimApp(
govRouter.AddRoute(gov.RouterKey, gov.ProposalHandler).
AddRoute(params.RouterKey, params.NewParamChangeProposalHandler(app.paramsKeeper)).
AddRoute(distr.RouterKey, distr.NewCommunityPoolSpendProposalHandler(app.distrKeeper))
app.govKeeper = gov.NewKeeper(app.cdc, keys[gov.StoreKey], app.paramsKeeper, govSubspace,
app.govKeeper = gov.NewKeeper(app.cdc, keys[gov.StoreKey], govSubspace,
app.supplyKeeper, &stakingKeeper, gov.DefaultCodespace, govRouter)
// register the staking hooks

View File

@ -121,10 +121,10 @@ func CreateTestInputAdvanced(t *testing.T, isCheckTx bool, initPower int64,
bondPool := supply.NewEmptyModuleAccount(staking.BondedPoolName, supply.Burner, supply.Staking)
blacklistedAddrs := make(map[string]bool)
blacklistedAddrs[feeCollectorAcc.String()] = true
blacklistedAddrs[notBondedPool.String()] = true
blacklistedAddrs[bondPool.String()] = true
blacklistedAddrs[distrAcc.String()] = true
blacklistedAddrs[feeCollectorAcc.GetAddress().String()] = true
blacklistedAddrs[notBondedPool.GetAddress().String()] = true
blacklistedAddrs[bondPool.GetAddress().String()] = true
blacklistedAddrs[distrAcc.GetAddress().String()] = true
cdc := MakeTestCodec()
pk := params.NewKeeper(cdc, keyParams, tkeyParams, params.DefaultCodespace)

View File

@ -39,7 +39,7 @@ func EndBlocker(ctx sdk.Context, keeper Keeper) {
keeper.IterateActiveProposalsQueue(ctx, ctx.BlockHeader().Time, func(proposal Proposal) bool {
var tagValue, logMsg string
passes, burnDeposits, tallyResults := tally(ctx, keeper, proposal)
passes, burnDeposits, tallyResults := keeper.Tally(ctx, proposal)
if burnDeposits {
keeper.DeleteDeposits(ctx, proposal.ProposalID)
@ -48,7 +48,7 @@ func EndBlocker(ctx sdk.Context, keeper Keeper) {
}
if passes {
handler := keeper.router.GetRoute(proposal.ProposalRoute())
handler := keeper.Router().GetRoute(proposal.ProposalRoute())
cacheCtx, writeCache := ctx.CacheContext()
// The proposal handler may execute state mutating logic depending

View File

@ -9,11 +9,12 @@ import (
abci "github.com/tendermint/tendermint/abci/types"
sdk "github.com/cosmos/cosmos-sdk/types"
keep "github.com/cosmos/cosmos-sdk/x/gov/keeper"
"github.com/cosmos/cosmos-sdk/x/staking"
)
func TestTickExpiredDepositPeriod(t *testing.T) {
input := getMockApp(t, 10, GenesisState{}, nil)
input := getMockApp(t, 10, GenesisState{}, nil, ProposalHandler)
header := abci.Header{Height: input.mApp.LastBlockHeight() + 1}
input.mApp.BeginBlock(abci.RequestBeginBlock{Header: header})
@ -62,7 +63,7 @@ func TestTickExpiredDepositPeriod(t *testing.T) {
}
func TestTickMultipleExpiredDepositPeriod(t *testing.T) {
input := getMockApp(t, 10, GenesisState{}, nil)
input := getMockApp(t, 10, GenesisState{}, nil, ProposalHandler)
header := abci.Header{Height: input.mApp.LastBlockHeight() + 1}
input.mApp.BeginBlock(abci.RequestBeginBlock{Header: header})
@ -130,7 +131,7 @@ func TestTickMultipleExpiredDepositPeriod(t *testing.T) {
}
func TestTickPassedDepositPeriod(t *testing.T) {
input := getMockApp(t, 10, GenesisState{}, nil)
input := getMockApp(t, 10, GenesisState{}, nil, ProposalHandler)
header := abci.Header{Height: input.mApp.LastBlockHeight() + 1}
input.mApp.BeginBlock(abci.RequestBeginBlock{Header: header})
@ -153,8 +154,7 @@ func TestTickPassedDepositPeriod(t *testing.T) {
res := govHandler(ctx, newProposalMsg)
require.True(t, res.IsOK())
var proposalID uint64
input.keeper.cdc.MustUnmarshalBinaryLengthPrefixed(res.Data, &proposalID)
proposalID := GetProposalIDFromBytes(res.Data)
inactiveQueue = input.keeper.InactiveProposalQueueIterator(ctx, ctx.BlockHeader().Time)
require.False(t, inactiveQueue.Valid())
@ -178,7 +178,7 @@ func TestTickPassedDepositPeriod(t *testing.T) {
}
func TestTickPassedVotingPeriod(t *testing.T) {
input := getMockApp(t, 10, GenesisState{}, nil)
input := getMockApp(t, 10, GenesisState{}, nil, ProposalHandler)
SortAddresses(input.addrs)
header := abci.Header{Height: input.mApp.LastBlockHeight() + 1}
@ -195,12 +195,11 @@ func TestTickPassedVotingPeriod(t *testing.T) {
activeQueue.Close()
proposalCoins := sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, sdk.TokensFromConsensusPower(5))}
newProposalMsg := NewMsgSubmitProposal(testProposal(), proposalCoins, input.addrs[0])
newProposalMsg := NewMsgSubmitProposal(keep.TestProposal, proposalCoins, input.addrs[0])
res := govHandler(ctx, newProposalMsg)
require.True(t, res.IsOK())
var proposalID uint64
input.keeper.cdc.MustUnmarshalBinaryLengthPrefixed(res.Data, &proposalID)
proposalID := GetProposalIDFromBytes(res.Data)
newHeader := ctx.BlockHeader()
newHeader.Time = ctx.BlockHeader().Time.Add(time.Duration(1) * time.Second)
@ -221,15 +220,11 @@ func TestTickPassedVotingPeriod(t *testing.T) {
activeQueue = input.keeper.ActiveProposalQueueIterator(ctx, ctx.BlockHeader().Time)
require.True(t, activeQueue.Valid())
var activeProposalID uint64
require.NoError(t, input.keeper.cdc.UnmarshalBinaryLengthPrefixed(activeQueue.Value(), &activeProposalID))
activeProposalID := GetProposalIDFromBytes(activeQueue.Value())
proposal, ok := input.keeper.GetProposal(ctx, activeProposalID)
require.True(t, ok)
require.Equal(t, StatusVotingPeriod, proposal.Status)
depositsIterator := input.keeper.GetDepositsIterator(ctx, proposalID)
require.True(t, depositsIterator.Valid())
depositsIterator.Close()
activeQueue.Close()
EndBlocker(ctx, input.keeper)
@ -240,7 +235,7 @@ func TestTickPassedVotingPeriod(t *testing.T) {
}
func TestProposalPassedEndblocker(t *testing.T) {
input := getMockApp(t, 1, GenesisState{}, nil)
input := getMockApp(t, 1, GenesisState{}, nil, ProposalHandler)
SortAddresses(input.addrs)
handler := NewHandler(input.keeper)
@ -259,7 +254,7 @@ func TestProposalPassedEndblocker(t *testing.T) {
require.NotNil(t, macc)
initialModuleAccCoins := macc.GetCoins()
proposal, err := input.keeper.SubmitProposal(ctx, testProposal())
proposal, err := input.keeper.SubmitProposal(ctx, keep.TestProposal)
require.NoError(t, err)
proposalCoins := sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, sdk.TokensFromConsensusPower(10))}
@ -289,11 +284,9 @@ func TestProposalPassedEndblocker(t *testing.T) {
}
func TestEndBlockerProposalHandlerFailed(t *testing.T) {
input := getMockApp(t, 1, GenesisState{}, nil)
SortAddresses(input.addrs)
// hijack the router to one that will fail in a proposal's handler
input.keeper.router = NewRouter().AddRoute(RouterKey, badProposalHandler)
input := getMockApp(t, 1, GenesisState{}, nil, badProposalHandler)
SortAddresses(input.addrs)
handler := NewHandler(input.keeper)
stakingHandler := staking.NewHandler(input.sk)
@ -310,7 +303,7 @@ func TestEndBlockerProposalHandlerFailed(t *testing.T) {
// Create a proposal where the handler will pass for the test proposal
// because the value of contextKeyBadProposal is true.
ctx = ctx.WithValue(contextKeyBadProposal, true)
proposal, err := input.keeper.SubmitProposal(ctx, testProposal())
proposal, err := input.keeper.SubmitProposal(ctx, keep.TestProposal)
require.NoError(t, err)
proposalCoins := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.TokensFromConsensusPower(10)))

View File

@ -1,10 +1,12 @@
// nolint
// autogenerated code using github.com/rigelrozanski/multitool
// aliases generated for the following subdirectories:
// ALIASGEN: github.com/cosmos/cosmos-sdk/x/gov/keeper
// ALIASGEN: github.com/cosmos/cosmos-sdk/x/gov/types
package gov
import (
"github.com/cosmos/cosmos-sdk/x/gov/keeper"
"github.com/cosmos/cosmos-sdk/x/gov/types"
)
@ -23,6 +25,7 @@ const (
CodeInvalidGenesis = types.CodeInvalidGenesis
CodeInvalidProposalStatus = types.CodeInvalidProposalStatus
CodeProposalHandlerNotExists = types.CodeProposalHandlerNotExists
DefaultPeriod = types.DefaultPeriod
ModuleName = types.ModuleName
StoreKey = types.StoreKey
RouterKey = types.RouterKey
@ -59,6 +62,11 @@ const (
var (
// functions aliases
RegisterInvariants = keeper.RegisterInvariants
AllInvariants = keeper.AllInvariants
ModuleAccountInvariant = keeper.ModuleAccountInvariant
NewKeeper = keeper.NewKeeper
NewQuerier = keeper.NewQuerier
RegisterCodec = types.RegisterCodec
RegisterProposalTypeCodec = types.RegisterProposalTypeCodec
ValidateAbstract = types.ValidateAbstract
@ -66,13 +74,16 @@ var (
ErrUnknownProposal = types.ErrUnknownProposal
ErrInactiveProposal = types.ErrInactiveProposal
ErrAlreadyActiveProposal = types.ErrAlreadyActiveProposal
ErrAlreadyFinishedProposal = types.ErrAlreadyFinishedProposal
ErrAddressNotStaked = types.ErrAddressNotStaked
ErrInvalidProposalContent = types.ErrInvalidProposalContent
ErrInvalidProposalType = types.ErrInvalidProposalType
ErrInvalidVote = types.ErrInvalidVote
ErrInvalidGenesis = types.ErrInvalidGenesis
ErrNoProposalHandlerExists = types.ErrNoProposalHandlerExists
NewGenesisState = types.NewGenesisState
DefaultGenesisState = types.DefaultGenesisState
ValidateGenesis = types.ValidateGenesis
GetProposalIDBytes = types.GetProposalIDBytes
GetProposalIDFromBytes = types.GetProposalIDFromBytes
ProposalKey = types.ProposalKey
ActiveProposalByTimeKey = types.ActiveProposalByTimeKey
ActiveProposalQueueKey = types.ActiveProposalQueueKey
@ -96,11 +107,9 @@ var (
NewVotingParams = types.NewVotingParams
NewParams = types.NewParams
NewProposal = types.NewProposal
NewRouter = types.NewRouter
ProposalStatusFromString = types.ProposalStatusFromString
ValidProposalStatus = types.ValidProposalStatus
NewTallyResult = types.NewTallyResult
NewTallyResultFromMap = types.NewTallyResultFromMap
EmptyTallyResult = types.EmptyTallyResult
NewTextProposal = types.NewTextProposal
NewSoftwareUpgradeProposal = types.NewSoftwareUpgradeProposal
RegisterProposalType = types.RegisterProposalType
@ -111,6 +120,10 @@ var (
NewQueryDepositParams = types.NewQueryDepositParams
NewQueryVoteParams = types.NewQueryVoteParams
NewQueryProposalsParams = types.NewQueryProposalsParams
NewValidatorGovInfo = types.NewValidatorGovInfo
NewTallyResult = types.NewTallyResult
NewTallyResultFromMap = types.NewTallyResultFromMap
EmptyTallyResult = types.EmptyTallyResult
NewVote = types.NewVote
VoteOptionFromString = types.VoteOptionFromString
ValidVoteOption = types.ValidVoteOption
@ -129,10 +142,12 @@ var (
)
type (
Keeper = keeper.Keeper
Content = types.Content
Handler = types.Handler
Deposit = types.Deposit
Deposits = types.Deposits
GenesisState = types.GenesisState
MsgSubmitProposal = types.MsgSubmitProposal
MsgDeposit = types.MsgDeposit
MsgVote = types.MsgVote
@ -144,13 +159,14 @@ type (
Proposals = types.Proposals
ProposalQueue = types.ProposalQueue
ProposalStatus = types.ProposalStatus
TallyResult = types.TallyResult
TextProposal = types.TextProposal
SoftwareUpgradeProposal = types.SoftwareUpgradeProposal
QueryProposalParams = types.QueryProposalParams
QueryDepositParams = types.QueryDepositParams
QueryVoteParams = types.QueryVoteParams
QueryProposalsParams = types.QueryProposalsParams
ValidatorGovInfo = types.ValidatorGovInfo
TallyResult = types.TallyResult
Vote = types.Vote
Votes = types.Votes
VoteOption = types.VoteOption

499
x/gov/client/rest/query.go Normal file
View File

@ -0,0 +1,499 @@
package rest
import (
"errors"
"fmt"
"net/http"
"github.com/gorilla/mux"
"github.com/cosmos/cosmos-sdk/client/context"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/rest"
gcutils "github.com/cosmos/cosmos-sdk/x/gov/client/utils"
"github.com/cosmos/cosmos-sdk/x/gov/types"
)
func registerQueryRoutes(cliCtx context.CLIContext, r *mux.Router) {
r.HandleFunc(fmt.Sprintf("/gov/parameters/{%s}", RestParamsType), queryParamsHandlerFn(cliCtx)).Methods("GET")
r.HandleFunc("/gov/proposals", queryProposalsWithParameterFn(cliCtx)).Methods("GET")
r.HandleFunc(fmt.Sprintf("/gov/proposals/{%s}", RestProposalID), queryProposalHandlerFn(cliCtx)).Methods("GET")
r.HandleFunc(fmt.Sprintf("/gov/proposals/{%s}/proposer", RestProposalID), queryProposerHandlerFn(cliCtx)).Methods("GET")
r.HandleFunc(fmt.Sprintf("/gov/proposals/{%s}/deposits", RestProposalID), queryDepositsHandlerFn(cliCtx)).Methods("GET")
r.HandleFunc(fmt.Sprintf("/gov/proposals/{%s}/deposits/{%s}", RestProposalID, RestDepositor), queryDepositHandlerFn(cliCtx)).Methods("GET")
r.HandleFunc(fmt.Sprintf("/gov/proposals/{%s}/tally", RestProposalID), queryTallyOnProposalHandlerFn(cliCtx)).Methods("GET")
r.HandleFunc(fmt.Sprintf("/gov/proposals/{%s}/votes", RestProposalID), queryVotesOnProposalHandlerFn(cliCtx)).Methods("GET")
r.HandleFunc(fmt.Sprintf("/gov/proposals/{%s}/votes/{%s}", RestProposalID, RestVoter), queryVoteHandlerFn(cliCtx)).Methods("GET")
}
func queryParamsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
paramType := vars[RestParamsType]
cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r)
if !ok {
return
}
res, height, err := cliCtx.QueryWithData(fmt.Sprintf("custom/gov/%s/%s", types.QueryParams, paramType), nil)
if err != nil {
rest.WriteErrorResponse(w, http.StatusNotFound, err.Error())
return
}
cliCtx = cliCtx.WithHeight(height)
rest.PostProcessResponse(w, cliCtx, res)
}
}
func queryProposalHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
strProposalID := vars[RestProposalID]
if len(strProposalID) == 0 {
err := errors.New("proposalId required but not specified")
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
proposalID, ok := rest.ParseUint64OrReturnBadRequest(w, strProposalID)
if !ok {
return
}
cliCtx, ok = rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r)
if !ok {
return
}
params := types.NewQueryProposalParams(proposalID)
bz, err := cliCtx.Codec.MarshalJSON(params)
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
res, height, err := cliCtx.QueryWithData("custom/gov/proposal", bz)
if err != nil {
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
cliCtx = cliCtx.WithHeight(height)
rest.PostProcessResponse(w, cliCtx, res)
}
}
func queryDepositsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
strProposalID := vars[RestProposalID]
proposalID, ok := rest.ParseUint64OrReturnBadRequest(w, strProposalID)
if !ok {
return
}
cliCtx, ok = rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r)
if !ok {
return
}
params := types.NewQueryProposalParams(proposalID)
bz, err := cliCtx.Codec.MarshalJSON(params)
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
res, _, err := cliCtx.QueryWithData("custom/gov/proposal", bz)
if err != nil {
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
var proposal types.Proposal
if err := cliCtx.Codec.UnmarshalJSON(res, &proposal); err != nil {
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
// For inactive proposals we must query the txs directly to get the deposits
// as they're no longer in state.
propStatus := proposal.Status
if !(propStatus == types.StatusVotingPeriod || propStatus == types.StatusDepositPeriod) {
res, err = gcutils.QueryDepositsByTxQuery(cliCtx, params)
} else {
res, _, err = cliCtx.QueryWithData("custom/gov/deposits", bz)
}
if err != nil {
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
rest.PostProcessResponse(w, cliCtx, res)
}
}
func queryProposerHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
strProposalID := vars[RestProposalID]
proposalID, ok := rest.ParseUint64OrReturnBadRequest(w, strProposalID)
if !ok {
return
}
cliCtx, ok = rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r)
if !ok {
return
}
res, err := gcutils.QueryProposerByTxQuery(cliCtx, proposalID)
if err != nil {
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
rest.PostProcessResponse(w, cliCtx, res)
}
}
func queryDepositHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
strProposalID := vars[RestProposalID]
bechDepositorAddr := vars[RestDepositor]
if len(strProposalID) == 0 {
err := errors.New("proposalId required but not specified")
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
proposalID, ok := rest.ParseUint64OrReturnBadRequest(w, strProposalID)
if !ok {
return
}
if len(bechDepositorAddr) == 0 {
err := errors.New("depositor address required but not specified")
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
depositorAddr, err := sdk.AccAddressFromBech32(bechDepositorAddr)
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
cliCtx, ok = rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r)
if !ok {
return
}
params := types.NewQueryDepositParams(proposalID, depositorAddr)
bz, err := cliCtx.Codec.MarshalJSON(params)
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
res, _, err := cliCtx.QueryWithData("custom/gov/deposit", bz)
if err != nil {
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
var deposit types.Deposit
if err := cliCtx.Codec.UnmarshalJSON(res, &deposit); err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
// For an empty deposit, either the proposal does not exist or is inactive in
// which case the deposit would be removed from state and should be queried
// for directly via a txs query.
if deposit.Empty() {
bz, err := cliCtx.Codec.MarshalJSON(types.NewQueryProposalParams(proposalID))
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
res, _, err = cliCtx.QueryWithData("custom/gov/proposal", bz)
if err != nil || len(res) == 0 {
err := fmt.Errorf("proposalID %d does not exist", proposalID)
rest.WriteErrorResponse(w, http.StatusNotFound, err.Error())
return
}
res, err = gcutils.QueryDepositByTxQuery(cliCtx, params)
if err != nil {
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
}
rest.PostProcessResponse(w, cliCtx, res)
}
}
func queryVoteHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
strProposalID := vars[RestProposalID]
bechVoterAddr := vars[RestVoter]
if len(strProposalID) == 0 {
err := errors.New("proposalId required but not specified")
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
proposalID, ok := rest.ParseUint64OrReturnBadRequest(w, strProposalID)
if !ok {
return
}
if len(bechVoterAddr) == 0 {
err := errors.New("voter address required but not specified")
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
voterAddr, err := sdk.AccAddressFromBech32(bechVoterAddr)
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
cliCtx, ok = rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r)
if !ok {
return
}
params := types.NewQueryVoteParams(proposalID, voterAddr)
bz, err := cliCtx.Codec.MarshalJSON(params)
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
res, _, err := cliCtx.QueryWithData("custom/gov/vote", bz)
if err != nil {
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
var vote types.Vote
if err := cliCtx.Codec.UnmarshalJSON(res, &vote); err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
// For an empty vote, either the proposal does not exist or is inactive in
// which case the vote would be removed from state and should be queried for
// directly via a txs query.
if vote.Empty() {
bz, err := cliCtx.Codec.MarshalJSON(types.NewQueryProposalParams(proposalID))
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
res, _, err = cliCtx.QueryWithData("custom/gov/proposal", bz)
if err != nil || len(res) == 0 {
err := fmt.Errorf("proposalID %d does not exist", proposalID)
rest.WriteErrorResponse(w, http.StatusNotFound, err.Error())
return
}
res, err = gcutils.QueryVoteByTxQuery(cliCtx, params)
if err != nil {
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
}
rest.PostProcessResponse(w, cliCtx, res)
}
}
// todo: Split this functionality into helper functions to remove the above
func queryVotesOnProposalHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
strProposalID := vars[RestProposalID]
if len(strProposalID) == 0 {
err := errors.New("proposalId required but not specified")
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
proposalID, ok := rest.ParseUint64OrReturnBadRequest(w, strProposalID)
if !ok {
return
}
cliCtx, ok = rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r)
if !ok {
return
}
params := types.NewQueryProposalParams(proposalID)
bz, err := cliCtx.Codec.MarshalJSON(params)
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
res, _, err := cliCtx.QueryWithData("custom/gov/proposal", bz)
if err != nil {
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
var proposal types.Proposal
if err := cliCtx.Codec.UnmarshalJSON(res, &proposal); err != nil {
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
// For inactive proposals we must query the txs directly to get the votes
// as they're no longer in state.
propStatus := proposal.Status
if !(propStatus == types.StatusVotingPeriod || propStatus == types.StatusDepositPeriod) {
res, err = gcutils.QueryVotesByTxQuery(cliCtx, params)
} else {
res, _, err = cliCtx.QueryWithData("custom/gov/votes", bz)
}
if err != nil {
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
rest.PostProcessResponse(w, cliCtx, res)
}
}
// todo: Split this functionality into helper functions to remove the above
func queryProposalsWithParameterFn(cliCtx context.CLIContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
bechVoterAddr := r.URL.Query().Get(RestVoter)
bechDepositorAddr := r.URL.Query().Get(RestDepositor)
strProposalStatus := r.URL.Query().Get(RestProposalStatus)
strNumLimit := r.URL.Query().Get(RestNumLimit)
params := types.QueryProposalsParams{}
if len(bechVoterAddr) != 0 {
voterAddr, err := sdk.AccAddressFromBech32(bechVoterAddr)
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
params.Voter = voterAddr
}
if len(bechDepositorAddr) != 0 {
depositorAddr, err := sdk.AccAddressFromBech32(bechDepositorAddr)
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
params.Depositor = depositorAddr
}
if len(strProposalStatus) != 0 {
proposalStatus, err := types.ProposalStatusFromString(gcutils.NormalizeProposalStatus(strProposalStatus))
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
params.ProposalStatus = proposalStatus
}
if len(strNumLimit) != 0 {
numLimit, ok := rest.ParseUint64OrReturnBadRequest(w, strNumLimit)
if !ok {
return
}
params.Limit = numLimit
}
cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r)
if !ok {
return
}
bz, err := cliCtx.Codec.MarshalJSON(params)
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
res, height, err := cliCtx.QueryWithData("custom/gov/proposals", bz)
if err != nil {
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
cliCtx = cliCtx.WithHeight(height)
rest.PostProcessResponse(w, cliCtx, res)
}
}
// todo: Split this functionality into helper functions to remove the above
func queryTallyOnProposalHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
strProposalID := vars[RestProposalID]
if len(strProposalID) == 0 {
err := errors.New("proposalId required but not specified")
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
proposalID, ok := rest.ParseUint64OrReturnBadRequest(w, strProposalID)
if !ok {
return
}
cliCtx, ok = rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r)
if !ok {
return
}
params := types.NewQueryProposalParams(proposalID)
bz, err := cliCtx.Codec.MarshalJSON(params)
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
res, height, err := cliCtx.QueryWithData("custom/gov/tally", bz)
if err != nil {
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
cliCtx = cliCtx.WithHeight(height)
rest.PostProcessResponse(w, cliCtx, res)
}
}

View File

@ -1,8 +1,6 @@
package rest
import (
"errors"
"fmt"
"net/http"
"github.com/gorilla/mux"
@ -10,9 +8,6 @@ import (
"github.com/cosmos/cosmos-sdk/client/context"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/rest"
"github.com/cosmos/cosmos-sdk/x/auth/client/utils"
gcutils "github.com/cosmos/cosmos-sdk/x/gov/client/utils"
"github.com/cosmos/cosmos-sdk/x/gov/types"
)
// REST Variable names
@ -35,31 +30,8 @@ type ProposalRESTHandler struct {
// RegisterRoutes - Central function to define routes that get registered by the main application
func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router, phs []ProposalRESTHandler) {
propSubRtr := r.PathPrefix("/gov/proposals").Subrouter()
for _, ph := range phs {
propSubRtr.HandleFunc(fmt.Sprintf("/%s", ph.SubRoute), ph.Handler).Methods("POST")
}
r.HandleFunc("/gov/proposals", postProposalHandlerFn(cliCtx)).Methods("POST")
r.HandleFunc(fmt.Sprintf("/gov/proposals/{%s}/deposits", RestProposalID), depositHandlerFn(cliCtx)).Methods("POST")
r.HandleFunc(fmt.Sprintf("/gov/proposals/{%s}/votes", RestProposalID), voteHandlerFn(cliCtx)).Methods("POST")
r.HandleFunc(
fmt.Sprintf("/gov/parameters/{%s}", RestParamsType),
queryParamsHandlerFn(cliCtx),
).Methods("GET")
r.HandleFunc("/gov/proposals", queryProposalsWithParameterFn(cliCtx)).Methods("GET")
r.HandleFunc(fmt.Sprintf("/gov/proposals/{%s}", RestProposalID), queryProposalHandlerFn(cliCtx)).Methods("GET")
r.HandleFunc(
fmt.Sprintf("/gov/proposals/{%s}/proposer", RestProposalID),
queryProposerHandlerFn(cliCtx),
).Methods("GET")
r.HandleFunc(fmt.Sprintf("/gov/proposals/{%s}/deposits", RestProposalID), queryDepositsHandlerFn(cliCtx)).Methods("GET")
r.HandleFunc(fmt.Sprintf("/gov/proposals/{%s}/deposits/{%s}", RestProposalID, RestDepositor), queryDepositHandlerFn(cliCtx)).Methods("GET")
r.HandleFunc(fmt.Sprintf("/gov/proposals/{%s}/tally", RestProposalID), queryTallyOnProposalHandlerFn(cliCtx)).Methods("GET")
r.HandleFunc(fmt.Sprintf("/gov/proposals/{%s}/votes", RestProposalID), queryVotesOnProposalHandlerFn(cliCtx)).Methods("GET")
r.HandleFunc(fmt.Sprintf("/gov/proposals/{%s}/votes/{%s}", RestProposalID, RestVoter), queryVoteHandlerFn(cliCtx)).Methods("GET")
registerQueryRoutes(cliCtx, r)
registerTxRoutes(cliCtx, r, phs)
}
// PostProposalReq defines the properties of a proposal request's body.
@ -85,578 +57,3 @@ type VoteReq struct {
Voter sdk.AccAddress `json:"voter" yaml:"voter"` // address of the voter
Option string `json:"option" yaml:"option"` // option from OptionSet chosen by the voter
}
func postProposalHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req PostProposalReq
if !rest.ReadRESTReq(w, r, cliCtx.Codec, &req) {
return
}
req.BaseReq = req.BaseReq.Sanitize()
if !req.BaseReq.ValidateBasic(w) {
return
}
proposalType := gcutils.NormalizeProposalType(req.ProposalType)
content := types.ContentFromProposalType(req.Title, req.Description, proposalType)
msg := types.NewMsgSubmitProposal(content, req.InitialDeposit, req.Proposer)
if err := msg.ValidateBasic(); err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
utils.WriteGenerateStdTxResponse(w, cliCtx, req.BaseReq, []sdk.Msg{msg})
}
}
func depositHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
strProposalID := vars[RestProposalID]
if len(strProposalID) == 0 {
err := errors.New("proposalId required but not specified")
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
proposalID, ok := rest.ParseUint64OrReturnBadRequest(w, strProposalID)
if !ok {
return
}
var req DepositReq
if !rest.ReadRESTReq(w, r, cliCtx.Codec, &req) {
return
}
req.BaseReq = req.BaseReq.Sanitize()
if !req.BaseReq.ValidateBasic(w) {
return
}
// create the message
msg := types.NewMsgDeposit(req.Depositor, proposalID, req.Amount)
if err := msg.ValidateBasic(); err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
utils.WriteGenerateStdTxResponse(w, cliCtx, req.BaseReq, []sdk.Msg{msg})
}
}
func voteHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
strProposalID := vars[RestProposalID]
if len(strProposalID) == 0 {
err := errors.New("proposalId required but not specified")
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
proposalID, ok := rest.ParseUint64OrReturnBadRequest(w, strProposalID)
if !ok {
return
}
var req VoteReq
if !rest.ReadRESTReq(w, r, cliCtx.Codec, &req) {
return
}
req.BaseReq = req.BaseReq.Sanitize()
if !req.BaseReq.ValidateBasic(w) {
return
}
voteOption, err := types.VoteOptionFromString(gcutils.NormalizeVoteOption(req.Option))
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
// create the message
msg := types.NewMsgVote(req.Voter, proposalID, voteOption)
if err := msg.ValidateBasic(); err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
utils.WriteGenerateStdTxResponse(w, cliCtx, req.BaseReq, []sdk.Msg{msg})
}
}
func queryParamsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
paramType := vars[RestParamsType]
cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r)
if !ok {
return
}
res, height, err := cliCtx.QueryWithData(fmt.Sprintf("custom/gov/%s/%s", types.QueryParams, paramType), nil)
if err != nil {
rest.WriteErrorResponse(w, http.StatusNotFound, err.Error())
return
}
cliCtx = cliCtx.WithHeight(height)
rest.PostProcessResponse(w, cliCtx, res)
}
}
func queryProposalHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
strProposalID := vars[RestProposalID]
if len(strProposalID) == 0 {
err := errors.New("proposalId required but not specified")
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
proposalID, ok := rest.ParseUint64OrReturnBadRequest(w, strProposalID)
if !ok {
return
}
cliCtx, ok = rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r)
if !ok {
return
}
params := types.NewQueryProposalParams(proposalID)
bz, err := cliCtx.Codec.MarshalJSON(params)
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
res, height, err := cliCtx.QueryWithData("custom/gov/proposal", bz)
if err != nil {
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
cliCtx = cliCtx.WithHeight(height)
rest.PostProcessResponse(w, cliCtx, res)
}
}
func queryDepositsHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
strProposalID := vars[RestProposalID]
proposalID, ok := rest.ParseUint64OrReturnBadRequest(w, strProposalID)
if !ok {
return
}
cliCtx, ok = rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r)
if !ok {
return
}
params := types.NewQueryProposalParams(proposalID)
bz, err := cliCtx.Codec.MarshalJSON(params)
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
res, _, err := cliCtx.QueryWithData("custom/gov/proposal", bz)
if err != nil {
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
var proposal types.Proposal
if err := cliCtx.Codec.UnmarshalJSON(res, &proposal); err != nil {
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
// For inactive proposals we must query the txs directly to get the deposits
// as they're no longer in state.
propStatus := proposal.Status
if !(propStatus == types.StatusVotingPeriod || propStatus == types.StatusDepositPeriod) {
res, err = gcutils.QueryDepositsByTxQuery(cliCtx, params)
} else {
res, _, err = cliCtx.QueryWithData("custom/gov/deposits", bz)
}
if err != nil {
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
rest.PostProcessResponse(w, cliCtx, res)
}
}
func queryProposerHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
strProposalID := vars[RestProposalID]
proposalID, ok := rest.ParseUint64OrReturnBadRequest(w, strProposalID)
if !ok {
return
}
cliCtx, ok = rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r)
if !ok {
return
}
res, err := gcutils.QueryProposerByTxQuery(cliCtx, proposalID)
if err != nil {
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
rest.PostProcessResponse(w, cliCtx, res)
}
}
func queryDepositHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
strProposalID := vars[RestProposalID]
bechDepositorAddr := vars[RestDepositor]
if len(strProposalID) == 0 {
err := errors.New("proposalId required but not specified")
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
proposalID, ok := rest.ParseUint64OrReturnBadRequest(w, strProposalID)
if !ok {
return
}
if len(bechDepositorAddr) == 0 {
err := errors.New("depositor address required but not specified")
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
depositorAddr, err := sdk.AccAddressFromBech32(bechDepositorAddr)
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
cliCtx, ok = rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r)
if !ok {
return
}
params := types.NewQueryDepositParams(proposalID, depositorAddr)
bz, err := cliCtx.Codec.MarshalJSON(params)
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
res, _, err := cliCtx.QueryWithData("custom/gov/deposit", bz)
if err != nil {
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
var deposit types.Deposit
if err := cliCtx.Codec.UnmarshalJSON(res, &deposit); err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
// For an empty deposit, either the proposal does not exist or is inactive in
// which case the deposit would be removed from state and should be queried
// for directly via a txs query.
if deposit.Empty() {
bz, err := cliCtx.Codec.MarshalJSON(types.NewQueryProposalParams(proposalID))
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
res, _, err = cliCtx.QueryWithData("custom/gov/proposal", bz)
if err != nil || len(res) == 0 {
err := fmt.Errorf("proposalID %d does not exist", proposalID)
rest.WriteErrorResponse(w, http.StatusNotFound, err.Error())
return
}
res, err = gcutils.QueryDepositByTxQuery(cliCtx, params)
if err != nil {
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
}
rest.PostProcessResponse(w, cliCtx, res)
}
}
func queryVoteHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
strProposalID := vars[RestProposalID]
bechVoterAddr := vars[RestVoter]
if len(strProposalID) == 0 {
err := errors.New("proposalId required but not specified")
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
proposalID, ok := rest.ParseUint64OrReturnBadRequest(w, strProposalID)
if !ok {
return
}
if len(bechVoterAddr) == 0 {
err := errors.New("voter address required but not specified")
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
voterAddr, err := sdk.AccAddressFromBech32(bechVoterAddr)
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
cliCtx, ok = rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r)
if !ok {
return
}
params := types.NewQueryVoteParams(proposalID, voterAddr)
bz, err := cliCtx.Codec.MarshalJSON(params)
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
res, _, err := cliCtx.QueryWithData("custom/gov/vote", bz)
if err != nil {
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
var vote types.Vote
if err := cliCtx.Codec.UnmarshalJSON(res, &vote); err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
// For an empty vote, either the proposal does not exist or is inactive in
// which case the vote would be removed from state and should be queried for
// directly via a txs query.
if vote.Empty() {
bz, err := cliCtx.Codec.MarshalJSON(types.NewQueryProposalParams(proposalID))
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
res, _, err = cliCtx.QueryWithData("custom/gov/proposal", bz)
if err != nil || len(res) == 0 {
err := fmt.Errorf("proposalID %d does not exist", proposalID)
rest.WriteErrorResponse(w, http.StatusNotFound, err.Error())
return
}
res, err = gcutils.QueryVoteByTxQuery(cliCtx, params)
if err != nil {
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
}
rest.PostProcessResponse(w, cliCtx, res)
}
}
// todo: Split this functionality into helper functions to remove the above
func queryVotesOnProposalHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
strProposalID := vars[RestProposalID]
if len(strProposalID) == 0 {
err := errors.New("proposalId required but not specified")
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
proposalID, ok := rest.ParseUint64OrReturnBadRequest(w, strProposalID)
if !ok {
return
}
cliCtx, ok = rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r)
if !ok {
return
}
params := types.NewQueryProposalParams(proposalID)
bz, err := cliCtx.Codec.MarshalJSON(params)
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
res, _, err := cliCtx.QueryWithData("custom/gov/proposal", bz)
if err != nil {
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
var proposal types.Proposal
if err := cliCtx.Codec.UnmarshalJSON(res, &proposal); err != nil {
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
// For inactive proposals we must query the txs directly to get the votes
// as they're no longer in state.
propStatus := proposal.Status
if !(propStatus == types.StatusVotingPeriod || propStatus == types.StatusDepositPeriod) {
res, err = gcutils.QueryVotesByTxQuery(cliCtx, params)
} else {
res, _, err = cliCtx.QueryWithData("custom/gov/votes", bz)
}
if err != nil {
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
rest.PostProcessResponse(w, cliCtx, res)
}
}
// todo: Split this functionality into helper functions to remove the above
func queryProposalsWithParameterFn(cliCtx context.CLIContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
bechVoterAddr := r.URL.Query().Get(RestVoter)
bechDepositorAddr := r.URL.Query().Get(RestDepositor)
strProposalStatus := r.URL.Query().Get(RestProposalStatus)
strNumLimit := r.URL.Query().Get(RestNumLimit)
params := types.QueryProposalsParams{}
if len(bechVoterAddr) != 0 {
voterAddr, err := sdk.AccAddressFromBech32(bechVoterAddr)
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
params.Voter = voterAddr
}
if len(bechDepositorAddr) != 0 {
depositorAddr, err := sdk.AccAddressFromBech32(bechDepositorAddr)
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
params.Depositor = depositorAddr
}
if len(strProposalStatus) != 0 {
proposalStatus, err := types.ProposalStatusFromString(gcutils.NormalizeProposalStatus(strProposalStatus))
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
params.ProposalStatus = proposalStatus
}
if len(strNumLimit) != 0 {
numLimit, ok := rest.ParseUint64OrReturnBadRequest(w, strNumLimit)
if !ok {
return
}
params.Limit = numLimit
}
cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r)
if !ok {
return
}
bz, err := cliCtx.Codec.MarshalJSON(params)
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
res, height, err := cliCtx.QueryWithData("custom/gov/proposals", bz)
if err != nil {
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
cliCtx = cliCtx.WithHeight(height)
rest.PostProcessResponse(w, cliCtx, res)
}
}
// todo: Split this functionality into helper functions to remove the above
func queryTallyOnProposalHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
strProposalID := vars[RestProposalID]
if len(strProposalID) == 0 {
err := errors.New("proposalId required but not specified")
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
proposalID, ok := rest.ParseUint64OrReturnBadRequest(w, strProposalID)
if !ok {
return
}
cliCtx, ok = rest.ParseQueryHeightOrReturnBadRequest(w, cliCtx, r)
if !ok {
return
}
params := types.NewQueryProposalParams(proposalID)
bz, err := cliCtx.Codec.MarshalJSON(params)
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
res, height, err := cliCtx.QueryWithData("custom/gov/tally", bz)
if err != nil {
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
cliCtx = cliCtx.WithHeight(height)
rest.PostProcessResponse(w, cliCtx, res)
}
}

129
x/gov/client/rest/tx.go Normal file
View File

@ -0,0 +1,129 @@
package rest
import (
"fmt"
"net/http"
"github.com/gorilla/mux"
"github.com/cosmos/cosmos-sdk/client/context"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/rest"
"github.com/cosmos/cosmos-sdk/x/auth/client/utils"
gcutils "github.com/cosmos/cosmos-sdk/x/gov/client/utils"
"github.com/cosmos/cosmos-sdk/x/gov/types"
)
func registerTxRoutes(cliCtx context.CLIContext, r *mux.Router, phs []ProposalRESTHandler) {
propSubRtr := r.PathPrefix("/gov/proposals").Subrouter()
for _, ph := range phs {
propSubRtr.HandleFunc(fmt.Sprintf("/%s", ph.SubRoute), ph.Handler).Methods("POST")
}
r.HandleFunc("/gov/proposals", postProposalHandlerFn(cliCtx)).Methods("POST")
r.HandleFunc(fmt.Sprintf("/gov/proposals/{%s}/deposits", RestProposalID), depositHandlerFn(cliCtx)).Methods("POST")
r.HandleFunc(fmt.Sprintf("/gov/proposals/{%s}/votes", RestProposalID), voteHandlerFn(cliCtx)).Methods("POST")
}
func postProposalHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req PostProposalReq
if !rest.ReadRESTReq(w, r, cliCtx.Codec, &req) {
return
}
req.BaseReq = req.BaseReq.Sanitize()
if !req.BaseReq.ValidateBasic(w) {
return
}
proposalType := gcutils.NormalizeProposalType(req.ProposalType)
content := types.ContentFromProposalType(req.Title, req.Description, proposalType)
msg := types.NewMsgSubmitProposal(content, req.InitialDeposit, req.Proposer)
if err := msg.ValidateBasic(); err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
utils.WriteGenerateStdTxResponse(w, cliCtx, req.BaseReq, []sdk.Msg{msg})
}
}
func depositHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
strProposalID := vars[RestProposalID]
if len(strProposalID) == 0 {
rest.WriteErrorResponse(w, http.StatusBadRequest, "proposalId required but not specified")
return
}
proposalID, ok := rest.ParseUint64OrReturnBadRequest(w, strProposalID)
if !ok {
return
}
var req DepositReq
if !rest.ReadRESTReq(w, r, cliCtx.Codec, &req) {
return
}
req.BaseReq = req.BaseReq.Sanitize()
if !req.BaseReq.ValidateBasic(w) {
return
}
// create the message
msg := types.NewMsgDeposit(req.Depositor, proposalID, req.Amount)
if err := msg.ValidateBasic(); err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
utils.WriteGenerateStdTxResponse(w, cliCtx, req.BaseReq, []sdk.Msg{msg})
}
}
func voteHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
strProposalID := vars[RestProposalID]
if len(strProposalID) == 0 {
rest.WriteErrorResponse(w, http.StatusBadRequest, "proposalId required but not specified")
return
}
proposalID, ok := rest.ParseUint64OrReturnBadRequest(w, strProposalID)
if !ok {
return
}
var req VoteReq
if !rest.ReadRESTReq(w, r, cliCtx.Codec, &req) {
return
}
req.BaseReq = req.BaseReq.Sanitize()
if !req.BaseReq.ValidateBasic(w) {
return
}
voteOption, err := types.VoteOptionFromString(gcutils.NormalizeVoteOption(req.Option))
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
// create the message
msg := types.NewMsgVote(req.Voter, proposalID, voteOption)
if err := msg.ValidateBasic(); err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
utils.WriteGenerateStdTxResponse(w, cliCtx, req.BaseReq, []sdk.Msg{msg})
}
}

View File

@ -1,102 +1,19 @@
package gov
import (
"bytes"
"fmt"
"time"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/gov/types"
)
const (
// Default period for deposits & voting
DefaultPeriod time.Duration = 86400 * 2 * time.Second // 2 days
)
// GenesisState - all staking state that must be provided at genesis
type GenesisState struct {
StartingProposalID uint64 `json:"starting_proposal_id" yaml:"starting_proposal_id"`
Deposits Deposits `json:"deposits" yaml:"deposits"`
Votes Votes `json:"votes" yaml:"votes"`
Proposals []Proposal `json:"proposals" yaml:"proposals"`
DepositParams DepositParams `json:"deposit_params" yaml:"deposit_params"`
VotingParams VotingParams `json:"voting_params" yaml:"voting_params"`
TallyParams TallyParams `json:"tally_params" yaml:"tally_params"`
}
// NewGenesisState creates a new genesis state for the governance module
func NewGenesisState(startingProposalID uint64, dp DepositParams, vp VotingParams, tp TallyParams) GenesisState {
return GenesisState{
StartingProposalID: startingProposalID,
DepositParams: dp,
VotingParams: vp,
TallyParams: tp,
}
}
// get raw genesis raw message for testing
func DefaultGenesisState() GenesisState {
minDepositTokens := sdk.TokensFromConsensusPower(10)
return GenesisState{
StartingProposalID: 1,
DepositParams: DepositParams{
MinDeposit: sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, minDepositTokens)},
MaxDepositPeriod: DefaultPeriod,
},
VotingParams: VotingParams{
VotingPeriod: DefaultPeriod,
},
TallyParams: TallyParams{
Quorum: sdk.NewDecWithPrec(334, 3),
Threshold: sdk.NewDecWithPrec(5, 1),
Veto: sdk.NewDecWithPrec(334, 3),
},
}
}
// Checks whether 2 GenesisState structs are equivalent.
func (data GenesisState) Equal(data2 GenesisState) bool {
b1 := types.ModuleCdc.MustMarshalBinaryBare(data)
b2 := types.ModuleCdc.MustMarshalBinaryBare(data2)
return bytes.Equal(b1, b2)
}
// Returns if a GenesisState is empty or has data in it
func (data GenesisState) IsEmpty() bool {
emptyGenState := GenesisState{}
return data.Equal(emptyGenState)
}
// ValidateGenesis checks if parameters are within valid ranges
func ValidateGenesis(data GenesisState) error {
threshold := data.TallyParams.Threshold
if threshold.IsNegative() || threshold.GT(sdk.OneDec()) {
return fmt.Errorf("Governance vote threshold should be positive and less or equal to one, is %s",
threshold.String())
}
veto := data.TallyParams.Veto
if veto.IsNegative() || veto.GT(sdk.OneDec()) {
return fmt.Errorf("Governance vote veto threshold should be positive and less or equal to one, is %s",
veto.String())
}
if !data.DepositParams.MinDeposit.IsValid() {
return fmt.Errorf("Governance deposit amount must be a valid sdk.Coins amount, is %s",
data.DepositParams.MinDeposit.String())
}
return nil
}
// InitGenesis - store genesis parameters
func InitGenesis(ctx sdk.Context, k Keeper, supplyKeeper SupplyKeeper, data GenesisState) {
func InitGenesis(ctx sdk.Context, k Keeper, supplyKeeper types.SupplyKeeper, data GenesisState) {
k.setProposalID(ctx, data.StartingProposalID)
k.setDepositParams(ctx, data.DepositParams)
k.setVotingParams(ctx, data.VotingParams)
k.setTallyParams(ctx, data.TallyParams)
k.SetProposalID(ctx, data.StartingProposalID)
k.SetDepositParams(ctx, data.DepositParams)
k.SetVotingParams(ctx, data.VotingParams)
k.SetTallyParams(ctx, data.TallyParams)
// check if the deposits pool account exists
moduleAcc := k.GetGovernanceAccount(ctx)
@ -106,12 +23,12 @@ func InitGenesis(ctx sdk.Context, k Keeper, supplyKeeper SupplyKeeper, data Gene
var totalDeposits sdk.Coins
for _, deposit := range data.Deposits {
k.setDeposit(ctx, deposit.ProposalID, deposit.Depositor, deposit)
k.SetDeposit(ctx, deposit)
totalDeposits = totalDeposits.Add(deposit.Amount)
}
for _, vote := range data.Votes {
k.setVote(ctx, vote.ProposalID, vote.Voter, vote)
k.SetVote(ctx, vote)
}
for _, proposal := range data.Proposals {

View File

@ -3,71 +3,15 @@ package gov
import (
"testing"
keep "github.com/cosmos/cosmos-sdk/x/gov/keeper"
"github.com/stretchr/testify/require"
abci "github.com/tendermint/tendermint/abci/types"
)
func TestEqualProposalID(t *testing.T) {
state1 := GenesisState{}
state2 := GenesisState{}
require.Equal(t, state1, state2)
// Proposals
state1.StartingProposalID = 1
require.NotEqual(t, state1, state2)
require.False(t, state1.Equal(state2))
state2.StartingProposalID = 1
require.Equal(t, state1, state2)
require.True(t, state1.Equal(state2))
}
func TestEqualProposals(t *testing.T) {
// Generate mock app and keepers
input := getMockApp(t, 2, GenesisState{}, nil)
SortAddresses(input.addrs)
header := abci.Header{Height: input.mApp.LastBlockHeight() + 1}
input.mApp.BeginBlock(abci.RequestBeginBlock{Header: header})
ctx := input.mApp.BaseApp.NewContext(false, abci.Header{})
// Submit two proposals
proposal := testProposal()
proposal1, err := input.keeper.SubmitProposal(ctx, proposal)
require.NoError(t, err)
proposal2, err := input.keeper.SubmitProposal(ctx, proposal)
require.NoError(t, err)
// They are similar but their IDs should be different
require.NotEqual(t, proposal1, proposal2)
require.False(t, ProposalEqual(proposal1, proposal2))
// Now create two genesis blocks
state1 := GenesisState{Proposals: []Proposal{proposal1}}
state2 := GenesisState{Proposals: []Proposal{proposal2}}
require.NotEqual(t, state1, state2)
require.False(t, state1.Equal(state2))
// Now make proposals identical by setting both IDs to 55
proposal1.ProposalID = 55
proposal2.ProposalID = 55
require.Equal(t, proposal1, proposal1)
require.True(t, ProposalEqual(proposal1, proposal2))
// Reassign proposals into state
state1.Proposals[0] = proposal1
state2.Proposals[0] = proposal2
// State should be identical now..
require.Equal(t, state1, state2)
require.True(t, state1.Equal(state2))
}
func TestImportExportQueues(t *testing.T) {
// Generate mock app and keepers
input := getMockApp(t, 2, GenesisState{}, nil)
input := getMockApp(t, 2, GenesisState{}, nil, ProposalHandler)
SortAddresses(input.addrs)
header := abci.Header{Height: input.mApp.LastBlockHeight() + 1}
@ -76,7 +20,7 @@ func TestImportExportQueues(t *testing.T) {
ctx := input.mApp.BaseApp.NewContext(false, abci.Header{})
// Create two proposals, put the second into the voting period
proposal := testProposal()
proposal := keep.TestProposal
proposal1, err := input.keeper.SubmitProposal(ctx, proposal)
require.NoError(t, err)
proposalID1 := proposal1.ProposalID
@ -100,7 +44,7 @@ func TestImportExportQueues(t *testing.T) {
// Export the state and import it into a new Mock App
genState := ExportGenesis(ctx, input.keeper)
input2 := getMockApp(t, 2, genState, genAccs)
input2 := getMockApp(t, 2, genState, genAccs, ProposalHandler)
header = abci.Header{Height: input.mApp.LastBlockHeight() + 1}
input2.mApp.BeginBlock(abci.RequestBeginBlock{Header: header})
@ -129,3 +73,45 @@ func TestImportExportQueues(t *testing.T) {
require.True(t, ok)
require.True(t, proposal2.Status == StatusRejected)
}
func TestEqualProposals(t *testing.T) {
// Generate mock app and keepers
input := getMockApp(t, 2, GenesisState{}, nil, ProposalHandler)
SortAddresses(input.addrs)
header := abci.Header{Height: input.mApp.LastBlockHeight() + 1}
input.mApp.BeginBlock(abci.RequestBeginBlock{Header: header})
ctx := input.mApp.BaseApp.NewContext(false, abci.Header{})
// Submit two proposals
proposal := keep.TestProposal
proposal1, err := input.keeper.SubmitProposal(ctx, proposal)
require.NoError(t, err)
proposal2, err := input.keeper.SubmitProposal(ctx, proposal)
require.NoError(t, err)
// They are similar but their IDs should be different
require.NotEqual(t, proposal1, proposal2)
require.False(t, keep.ProposalEqual(proposal1, proposal2))
// Now create two genesis blocks
state1 := GenesisState{Proposals: []Proposal{proposal1}}
state2 := GenesisState{Proposals: []Proposal{proposal2}}
require.NotEqual(t, state1, state2)
require.False(t, state1.Equal(state2))
// Now make proposals identical by setting both IDs to 55
proposal1.ProposalID = 55
proposal2.ProposalID = 55
require.Equal(t, proposal1, proposal1)
require.True(t, keep.ProposalEqual(proposal1, proposal2))
// Reassign proposals into state
state1.Proposals[0] = proposal1
state2.Proposals[0] = proposal2
// State should be identical now..
require.Equal(t, state1, state2)
require.True(t, state1.Equal(state2))
}

View File

@ -7,7 +7,7 @@ import (
"github.com/cosmos/cosmos-sdk/x/gov/types"
)
// Handle all "gov" type messages.
// NewHandler creates an sdk.Handler for all the gov type messages
func NewHandler(keeper Keeper) sdk.Handler {
return func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
ctx = ctx.WithEventManager(sdk.NewEventManager())
@ -58,7 +58,7 @@ func handleMsgSubmitProposal(ctx sdk.Context, keeper Keeper, msg MsgSubmitPropos
}
return sdk.Result{
Data: keeper.cdc.MustMarshalBinaryLengthPrefixed(proposal.ProposalID),
Data: GetProposalIDBytes(proposal.ProposalID),
Events: ctx.EventManager().Events(),
}
}

View File

@ -1,4 +1,4 @@
package gov
package keeper
import (
"fmt"
@ -8,7 +8,7 @@ import (
)
// GetDeposit gets the deposit of a specific depositor on a specific proposal
func (keeper Keeper) GetDeposit(ctx sdk.Context, proposalID uint64, depositorAddr sdk.AccAddress) (deposit Deposit, found bool) {
func (keeper Keeper) GetDeposit(ctx sdk.Context, proposalID uint64, depositorAddr sdk.AccAddress) (deposit types.Deposit, found bool) {
store := ctx.KVStore(keeper.storeKey)
bz := store.Get(types.DepositKey(proposalID, depositorAddr))
if bz == nil {
@ -19,10 +19,76 @@ func (keeper Keeper) GetDeposit(ctx sdk.Context, proposalID uint64, depositorAdd
return deposit, true
}
func (keeper Keeper) setDeposit(ctx sdk.Context, proposalID uint64, depositorAddr sdk.AccAddress, deposit Deposit) {
// SetDeposit sets a Deposit to the gov store
func (keeper Keeper) SetDeposit(ctx sdk.Context, deposit types.Deposit) {
store := ctx.KVStore(keeper.storeKey)
bz := keeper.cdc.MustMarshalBinaryLengthPrefixed(deposit)
store.Set(types.DepositKey(proposalID, depositorAddr), bz)
store.Set(types.DepositKey(deposit.ProposalID, deposit.Depositor), bz)
}
// GetAllDeposits returns all the deposits from the store
func (keeper Keeper) GetAllDeposits(ctx sdk.Context) (deposits types.Deposits) {
keeper.IterateAllDeposits(ctx, func(deposit types.Deposit) bool {
deposits = append(deposits, deposit)
return false
})
return
}
// GetDeposits returns all the deposits from a proposal
func (keeper Keeper) GetDeposits(ctx sdk.Context, proposalID uint64) (deposits types.Deposits) {
keeper.IterateDeposits(ctx, proposalID, func(deposit types.Deposit) bool {
deposits = append(deposits, deposit)
return false
})
return
}
// DeleteDeposits deletes all the deposits on a specific proposal without refunding them
func (keeper Keeper) DeleteDeposits(ctx sdk.Context, proposalID uint64) {
store := ctx.KVStore(keeper.storeKey)
keeper.IterateDeposits(ctx, proposalID, func(deposit types.Deposit) bool {
err := keeper.supplyKeeper.BurnCoins(ctx, types.ModuleName, deposit.Amount)
if err != nil {
panic(err)
}
store.Delete(types.DepositKey(proposalID, deposit.Depositor))
return false
})
}
// IterateAllDeposits iterates over the all the stored deposits and performs a callback function
func (keeper Keeper) IterateAllDeposits(ctx sdk.Context, cb func(deposit types.Deposit) (stop bool)) {
store := ctx.KVStore(keeper.storeKey)
iterator := sdk.KVStorePrefixIterator(store, types.DepositsKeyPrefix)
defer iterator.Close()
for ; iterator.Valid(); iterator.Next() {
var deposit types.Deposit
keeper.cdc.MustUnmarshalBinaryLengthPrefixed(iterator.Value(), &deposit)
if cb(deposit) {
break
}
}
}
// IterateDeposits iterates over the all the proposals deposits and performs a callback function
func (keeper Keeper) IterateDeposits(ctx sdk.Context, proposalID uint64, cb func(deposit types.Deposit) (stop bool)) {
store := ctx.KVStore(keeper.storeKey)
iterator := sdk.KVStorePrefixIterator(store, types.DepositsKey(proposalID))
defer iterator.Close()
for ; iterator.Valid(); iterator.Next() {
var deposit types.Deposit
keeper.cdc.MustUnmarshalBinaryLengthPrefixed(iterator.Value(), &deposit)
if cb(deposit) {
break
}
}
}
// AddDeposit adds or updates a deposit of a specific depositor on a specific proposal
@ -31,12 +97,12 @@ func (keeper Keeper) AddDeposit(ctx sdk.Context, proposalID uint64, depositorAdd
// Checks to see if proposal exists
proposal, ok := keeper.GetProposal(ctx, proposalID)
if !ok {
return ErrUnknownProposal(keeper.codespace, proposalID), false
return types.ErrUnknownProposal(keeper.codespace, proposalID), false
}
// Check if proposal is still depositable
if (proposal.Status != StatusDepositPeriod) && (proposal.Status != StatusVotingPeriod) {
return ErrAlreadyFinishedProposal(keeper.codespace, proposalID), false
if (proposal.Status != types.StatusDepositPeriod) && (proposal.Status != types.StatusVotingPeriod) {
return types.ErrInactiveProposal(keeper.codespace, proposalID), false
}
// update the governance module's account coins pool
@ -51,7 +117,7 @@ func (keeper Keeper) AddDeposit(ctx sdk.Context, proposalID uint64, depositorAdd
// Check if deposit has provided sufficient total funds to transition the proposal into the voting period
activatedVotingPeriod := false
if proposal.Status == StatusDepositPeriod && proposal.TotalDeposit.IsAllGTE(keeper.GetDepositParams(ctx).MinDeposit) {
if proposal.Status == types.StatusDepositPeriod && proposal.TotalDeposit.IsAllGTE(keeper.GetDepositParams(ctx).MinDeposit) {
keeper.activateVotingPeriod(ctx, proposal)
activatedVotingPeriod = true
}
@ -61,7 +127,7 @@ func (keeper Keeper) AddDeposit(ctx sdk.Context, proposalID uint64, depositorAdd
if found {
deposit.Amount = deposit.Amount.Add(depositAmount)
} else {
deposit = NewDeposit(proposalID, depositorAddr, depositAmount)
deposit = types.NewDeposit(proposalID, depositorAddr, depositAmount)
}
ctx.EventManager().EmitEvent(
@ -72,34 +138,10 @@ func (keeper Keeper) AddDeposit(ctx sdk.Context, proposalID uint64, depositorAdd
),
)
keeper.setDeposit(ctx, proposalID, depositorAddr, deposit)
keeper.SetDeposit(ctx, deposit)
return nil, activatedVotingPeriod
}
// GetAllDeposits returns all the deposits from the store
func (keeper Keeper) GetAllDeposits(ctx sdk.Context) (deposits Deposits) {
keeper.IterateAllDeposits(ctx, func(deposit Deposit) bool {
deposits = append(deposits, deposit)
return false
})
return
}
// GetDeposits returns all the deposits from a proposal
func (keeper Keeper) GetDeposits(ctx sdk.Context, proposalID uint64) (deposits Deposits) {
keeper.IterateDeposits(ctx, proposalID, func(deposit Deposit) bool {
deposits = append(deposits, deposit)
return false
})
return
}
// GetDepositsIterator gets all the deposits on a specific proposal as an sdk.Iterator
func (keeper Keeper) GetDepositsIterator(ctx sdk.Context, proposalID uint64) sdk.Iterator {
store := ctx.KVStore(keeper.storeKey)
return sdk.KVStorePrefixIterator(store, types.DepositsKey(proposalID))
}
// RefundDeposits refunds and deletes all the deposits on a specific proposal
func (keeper Keeper) RefundDeposits(ctx sdk.Context, proposalID uint64) {
store := ctx.KVStore(keeper.storeKey)
@ -110,22 +152,7 @@ func (keeper Keeper) RefundDeposits(ctx sdk.Context, proposalID uint64) {
panic(err)
}
store.Delete(DepositKey(proposalID, deposit.Depositor))
return false
})
}
// DeleteDeposits deletes all the deposits on a specific proposal without refunding them
func (keeper Keeper) DeleteDeposits(ctx sdk.Context, proposalID uint64) {
store := ctx.KVStore(keeper.storeKey)
keeper.IterateDeposits(ctx, proposalID, func(deposit types.Deposit) bool {
err := keeper.supplyKeeper.BurnCoins(ctx, types.ModuleName, deposit.Amount)
if err != nil {
panic(err)
}
store.Delete(DepositKey(proposalID, deposit.Depositor))
store.Delete(types.DepositKey(proposalID, deposit.Depositor))
return false
})
}

View File

@ -0,0 +1,98 @@
package keeper
import (
"testing"
"time"
"github.com/stretchr/testify/require"
sdk "github.com/cosmos/cosmos-sdk/types"
)
func TestDeposits(t *testing.T) {
ctx, ak, keeper, _, _ := createTestInput(t, false, 100)
tp := TestProposal
proposal, err := keeper.SubmitProposal(ctx, tp)
require.NoError(t, err)
proposalID := proposal.ProposalID
fourStake := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.TokensFromConsensusPower(4)))
fiveStake := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.TokensFromConsensusPower(5)))
addr0Initial := ak.GetAccount(ctx, TestAddrs[0]).GetCoins()
addr1Initial := ak.GetAccount(ctx, TestAddrs[1]).GetCoins()
require.True(t, proposal.TotalDeposit.IsEqual(sdk.NewCoins()))
// Check no deposits at beginning
deposit, found := keeper.GetDeposit(ctx, proposalID, TestAddrs[1])
require.False(t, found)
proposal, ok := keeper.GetProposal(ctx, proposalID)
require.True(t, ok)
require.True(t, proposal.VotingStartTime.Equal(time.Time{}))
// Check first deposit
err, votingStarted := keeper.AddDeposit(ctx, proposalID, TestAddrs[0], fourStake)
require.NoError(t, err)
require.False(t, votingStarted)
deposit, found = keeper.GetDeposit(ctx, proposalID, TestAddrs[0])
require.True(t, found)
require.Equal(t, fourStake, deposit.Amount)
require.Equal(t, TestAddrs[0], deposit.Depositor)
proposal, ok = keeper.GetProposal(ctx, proposalID)
require.True(t, ok)
require.Equal(t, fourStake, proposal.TotalDeposit)
require.Equal(t, addr0Initial.Sub(fourStake), ak.GetAccount(ctx, TestAddrs[0]).GetCoins())
// Check a second deposit from same address
err, votingStarted = keeper.AddDeposit(ctx, proposalID, TestAddrs[0], fiveStake)
require.NoError(t, err)
require.False(t, votingStarted)
deposit, found = keeper.GetDeposit(ctx, proposalID, TestAddrs[0])
require.True(t, found)
require.Equal(t, fourStake.Add(fiveStake), deposit.Amount)
require.Equal(t, TestAddrs[0], deposit.Depositor)
proposal, ok = keeper.GetProposal(ctx, proposalID)
require.True(t, ok)
require.Equal(t, fourStake.Add(fiveStake), proposal.TotalDeposit)
require.Equal(t, addr0Initial.Sub(fourStake).Sub(fiveStake), ak.GetAccount(ctx, TestAddrs[0]).GetCoins())
// Check third deposit from a new address
err, votingStarted = keeper.AddDeposit(ctx, proposalID, TestAddrs[1], fourStake)
require.NoError(t, err)
require.True(t, votingStarted)
deposit, found = keeper.GetDeposit(ctx, proposalID, TestAddrs[1])
require.True(t, found)
require.Equal(t, TestAddrs[1], deposit.Depositor)
require.Equal(t, fourStake, deposit.Amount)
proposal, ok = keeper.GetProposal(ctx, proposalID)
require.True(t, ok)
require.Equal(t, fourStake.Add(fiveStake).Add(fourStake), proposal.TotalDeposit)
require.Equal(t, addr1Initial.Sub(fourStake), ak.GetAccount(ctx, TestAddrs[1]).GetCoins())
// Check that proposal moved to voting period
proposal, ok = keeper.GetProposal(ctx, proposalID)
require.True(t, ok)
require.True(t, proposal.VotingStartTime.Equal(ctx.BlockHeader().Time))
// Test deposit iterator
// NOTE order of deposits is determined by the addresses
deposits := keeper.GetAllDeposits(ctx)
require.Len(t, deposits, 2)
require.Equal(t, deposits, keeper.GetDeposits(ctx, proposalID))
require.Equal(t, TestAddrs[0], deposits[0].Depositor)
require.Equal(t, fourStake.Add(fiveStake), deposits[0].Amount)
require.Equal(t, TestAddrs[1], deposits[1].Depositor)
require.Equal(t, fourStake, deposits[1].Amount)
// Test Refund Deposits
deposit, found = keeper.GetDeposit(ctx, proposalID, TestAddrs[1])
require.True(t, found)
require.Equal(t, fourStake, deposit.Amount)
keeper.RefundDeposits(ctx, proposalID)
deposit, found = keeper.GetDeposit(ctx, proposalID, TestAddrs[1])
require.False(t, found)
require.Equal(t, addr0Initial, ak.GetAccount(ctx, TestAddrs[0]).GetCoins())
require.Equal(t, addr1Initial, ak.GetAccount(ctx, TestAddrs[1]).GetCoins())
}

View File

@ -1,4 +1,6 @@
package gov
package keeper
// DONTCOVER
import (
"fmt"

View File

@ -1,4 +1,4 @@
package gov
package keeper
import (
"fmt"
@ -7,25 +7,21 @@ import (
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/gov/types"
"github.com/cosmos/cosmos-sdk/x/params"
"github.com/cosmos/cosmos-sdk/x/supply/exported"
"github.com/tendermint/tendermint/libs/log"
)
// Governance Keeper
// Keeper defines the governance module Keeper
type Keeper struct {
// The reference to the Param Keeper to get and set Global Params
paramsKeeper params.Keeper
// The reference to the Paramstore to get and set gov specific params
paramSpace params.Subspace
paramSpace types.ParamSubspace
// The SupplyKeeper to reduce the supply of the network
supplyKeeper SupplyKeeper
supplyKeeper types.SupplyKeeper
// The reference to the DelegationSet and ValidatorSet to get information about validators and delegators
sk StakingKeeper
sk types.StakingKeeper
// The (unexposed) keys used to access the stores from the Context.
storeKey sdk.StoreKey
@ -37,7 +33,7 @@ type Keeper struct {
codespace sdk.CodespaceType
// Proposal router
router Router
router types.Router
}
// NewKeeper returns a governance keeper. It handles:
@ -45,9 +41,11 @@ type Keeper struct {
// - depositing funds into proposals, and activating upon sufficient funds being deposited
// - users voting on proposals, with weight proportional to stake in the system
// - and tallying the result of the vote.
//
// CONTRACT: the parameter Subspace must have the param key table already initialized
func NewKeeper(
cdc *codec.Codec, key sdk.StoreKey, paramsKeeper params.Keeper, paramSpace params.Subspace,
supplyKeeper SupplyKeeper, sk StakingKeeper, codespace sdk.CodespaceType, rtr Router,
cdc *codec.Codec, key sdk.StoreKey, paramSpace types.ParamSubspace,
supplyKeeper types.SupplyKeeper, sk types.StakingKeeper, codespace sdk.CodespaceType, rtr types.Router,
) Keeper {
// ensure governance module account is set
@ -62,8 +60,7 @@ func NewKeeper(
return Keeper{
storeKey: key,
paramsKeeper: paramsKeeper,
paramSpace: paramSpace.WithKeyTable(ParamKeyTable()),
paramSpace: paramSpace,
supplyKeeper: supplyKeeper,
sk: sk,
cdc: cdc,
@ -77,52 +74,22 @@ func (keeper Keeper) Logger(ctx sdk.Context) log.Logger {
return ctx.Logger().With("module", fmt.Sprintf("x/%s", types.ModuleName))
}
// Router returns the gov Keeper's Router
func (keeper Keeper) Router() types.Router {
return keeper.router
}
// GetGovernanceAccount returns the governance ModuleAccount
func (keeper Keeper) GetGovernanceAccount(ctx sdk.Context) exported.ModuleAccountI {
return keeper.supplyKeeper.GetModuleAccount(ctx, types.ModuleName)
}
// Params
// Returns the current DepositParams from the global param store
func (keeper Keeper) GetDepositParams(ctx sdk.Context) DepositParams {
var depositParams DepositParams
keeper.paramSpace.Get(ctx, ParamStoreKeyDepositParams, &depositParams)
return depositParams
}
// Returns the current VotingParams from the global param store
func (keeper Keeper) GetVotingParams(ctx sdk.Context) VotingParams {
var votingParams VotingParams
keeper.paramSpace.Get(ctx, ParamStoreKeyVotingParams, &votingParams)
return votingParams
}
// Returns the current TallyParam from the global param store
func (keeper Keeper) GetTallyParams(ctx sdk.Context) TallyParams {
var tallyParams TallyParams
keeper.paramSpace.Get(ctx, ParamStoreKeyTallyParams, &tallyParams)
return tallyParams
}
func (keeper Keeper) setDepositParams(ctx sdk.Context, depositParams DepositParams) {
keeper.paramSpace.Set(ctx, ParamStoreKeyDepositParams, &depositParams)
}
func (keeper Keeper) setVotingParams(ctx sdk.Context, votingParams VotingParams) {
keeper.paramSpace.Set(ctx, ParamStoreKeyVotingParams, &votingParams)
}
func (keeper Keeper) setTallyParams(ctx sdk.Context, tallyParams TallyParams) {
keeper.paramSpace.Set(ctx, ParamStoreKeyTallyParams, &tallyParams)
}
// ProposalQueues
// InsertActiveProposalQueue inserts a ProposalID into the active proposal queue at endTime
func (keeper Keeper) InsertActiveProposalQueue(ctx sdk.Context, proposalID uint64, endTime time.Time) {
store := ctx.KVStore(keeper.storeKey)
bz := keeper.cdc.MustMarshalBinaryLengthPrefixed(proposalID)
bz := types.GetProposalIDBytes(proposalID)
store.Set(types.ActiveProposalQueueKey(proposalID, endTime), bz)
}
@ -135,7 +102,7 @@ func (keeper Keeper) RemoveFromActiveProposalQueue(ctx sdk.Context, proposalID u
// InsertInactiveProposalQueue Inserts a ProposalID into the inactive proposal queue at endTime
func (keeper Keeper) InsertInactiveProposalQueue(ctx sdk.Context, proposalID uint64, endTime time.Time) {
store := ctx.KVStore(keeper.storeKey)
bz := keeper.cdc.MustMarshalBinaryLengthPrefixed(proposalID)
bz := types.GetProposalIDBytes(proposalID)
store.Set(types.InactiveProposalQueueKey(proposalID, endTime), bz)
}
@ -147,22 +114,6 @@ func (keeper Keeper) RemoveFromInactiveProposalQueue(ctx sdk.Context, proposalID
// Iterators
// IterateProposals iterates over the all the proposals and performs a callback function
func (keeper Keeper) IterateProposals(ctx sdk.Context, cb func(proposal types.Proposal) (stop bool)) {
store := ctx.KVStore(keeper.storeKey)
iterator := sdk.KVStorePrefixIterator(store, types.ProposalsKeyPrefix)
defer iterator.Close()
for ; iterator.Valid(); iterator.Next() {
var proposal types.Proposal
keeper.cdc.MustUnmarshalBinaryLengthPrefixed(iterator.Value(), &proposal)
if cb(proposal) {
break
}
}
}
// IterateActiveProposalsQueue iterates over the proposals in the active proposal queue
// and performs a callback function
func (keeper Keeper) IterateActiveProposalsQueue(ctx sdk.Context, endTime time.Time, cb func(proposal types.Proposal) (stop bool)) {
@ -201,76 +152,14 @@ func (keeper Keeper) IterateInactiveProposalsQueue(ctx sdk.Context, endTime time
}
}
// IterateAllDeposits iterates over the all the stored deposits and performs a callback function
func (keeper Keeper) IterateAllDeposits(ctx sdk.Context, cb func(deposit types.Deposit) (stop bool)) {
store := ctx.KVStore(keeper.storeKey)
iterator := sdk.KVStorePrefixIterator(store, types.DepositsKeyPrefix)
defer iterator.Close()
for ; iterator.Valid(); iterator.Next() {
var deposit types.Deposit
keeper.cdc.MustUnmarshalBinaryLengthPrefixed(iterator.Value(), &deposit)
if cb(deposit) {
break
}
}
}
// IterateDeposits iterates over the all the proposals deposits and performs a callback function
func (keeper Keeper) IterateDeposits(ctx sdk.Context, proposalID uint64, cb func(deposit types.Deposit) (stop bool)) {
iterator := keeper.GetDepositsIterator(ctx, proposalID)
defer iterator.Close()
for ; iterator.Valid(); iterator.Next() {
var deposit types.Deposit
keeper.cdc.MustUnmarshalBinaryLengthPrefixed(iterator.Value(), &deposit)
if cb(deposit) {
break
}
}
}
// IterateAllVotes iterates over the all the stored votes and performs a callback function
func (keeper Keeper) IterateAllVotes(ctx sdk.Context, cb func(vote types.Vote) (stop bool)) {
store := ctx.KVStore(keeper.storeKey)
iterator := sdk.KVStorePrefixIterator(store, types.VotesKeyPrefix)
defer iterator.Close()
for ; iterator.Valid(); iterator.Next() {
var vote types.Vote
keeper.cdc.MustUnmarshalBinaryLengthPrefixed(iterator.Value(), &vote)
if cb(vote) {
break
}
}
}
// IterateVotes iterates over the all the proposals votes and performs a callback function
func (keeper Keeper) IterateVotes(ctx sdk.Context, proposalID uint64, cb func(vote types.Vote) (stop bool)) {
iterator := keeper.GetVotesIterator(ctx, proposalID)
defer iterator.Close()
for ; iterator.Valid(); iterator.Next() {
var vote types.Vote
keeper.cdc.MustUnmarshalBinaryLengthPrefixed(iterator.Value(), &vote)
if cb(vote) {
break
}
}
}
// ActiveProposalQueueIterator returns an sdk.Iterator for all the proposals in the Active Queue that expire by endTime
func (keeper Keeper) ActiveProposalQueueIterator(ctx sdk.Context, endTime time.Time) sdk.Iterator {
store := ctx.KVStore(keeper.storeKey)
return store.Iterator(ActiveProposalQueuePrefix, sdk.PrefixEndBytes(types.ActiveProposalByTimeKey(endTime)))
return store.Iterator(types.ActiveProposalQueuePrefix, sdk.PrefixEndBytes(types.ActiveProposalByTimeKey(endTime)))
}
// InactiveProposalQueueIterator returns an sdk.Iterator for all the proposals in the Inactive Queue that expire by endTime
func (keeper Keeper) InactiveProposalQueueIterator(ctx sdk.Context, endTime time.Time) sdk.Iterator {
store := ctx.KVStore(keeper.storeKey)
return store.Iterator(InactiveProposalQueuePrefix, sdk.PrefixEndBytes(types.InactiveProposalByTimeKey(endTime)))
return store.Iterator(types.InactiveProposalQueuePrefix, sdk.PrefixEndBytes(types.InactiveProposalByTimeKey(endTime)))
}

View File

@ -0,0 +1,50 @@
package keeper
import (
"testing"
"github.com/cosmos/cosmos-sdk/x/gov/types"
"github.com/stretchr/testify/require"
)
func TestIncrementProposalNumber(t *testing.T) {
ctx, _, keeper, _, _ := createTestInput(t, false, 100)
tp := TestProposal
keeper.SubmitProposal(ctx, tp)
keeper.SubmitProposal(ctx, tp)
keeper.SubmitProposal(ctx, tp)
keeper.SubmitProposal(ctx, tp)
keeper.SubmitProposal(ctx, tp)
proposal6, err := keeper.SubmitProposal(ctx, tp)
require.NoError(t, err)
require.Equal(t, uint64(6), proposal6.ProposalID)
}
func TestProposalQueues(t *testing.T) {
ctx, _, keeper, _, _ := createTestInput(t, false, 100)
// create test proposals
tp := TestProposal
proposal, err := keeper.SubmitProposal(ctx, tp)
require.NoError(t, err)
inactiveIterator := keeper.InactiveProposalQueueIterator(ctx, proposal.DepositEndTime)
require.True(t, inactiveIterator.Valid())
proposalID := types.GetProposalIDFromBytes(inactiveIterator.Value())
require.Equal(t, proposalID, proposal.ProposalID)
inactiveIterator.Close()
keeper.activateVotingPeriod(ctx, proposal)
proposal, ok := keeper.GetProposal(ctx, proposal.ProposalID)
require.True(t, ok)
activeIterator := keeper.ActiveProposalQueueIterator(ctx, proposal.VotingEndTime)
require.True(t, activeIterator.Valid())
keeper.cdc.UnmarshalBinaryLengthPrefixed(activeIterator.Value(), &proposalID)
require.Equal(t, proposalID, proposal.ProposalID)
activeIterator.Close()
}

42
x/gov/keeper/params.go Normal file
View File

@ -0,0 +1,42 @@
package keeper
import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/gov/types"
)
// GetDepositParams returns the current DepositParams from the global param store
func (keeper Keeper) GetDepositParams(ctx sdk.Context) types.DepositParams {
var depositParams types.DepositParams
keeper.paramSpace.Get(ctx, types.ParamStoreKeyDepositParams, &depositParams)
return depositParams
}
// GetVotingParams returns the current VotingParams from the global param store
func (keeper Keeper) GetVotingParams(ctx sdk.Context) types.VotingParams {
var votingParams types.VotingParams
keeper.paramSpace.Get(ctx, types.ParamStoreKeyVotingParams, &votingParams)
return votingParams
}
// GetTallyParams returns the current TallyParam from the global param store
func (keeper Keeper) GetTallyParams(ctx sdk.Context) types.TallyParams {
var tallyParams types.TallyParams
keeper.paramSpace.Get(ctx, types.ParamStoreKeyTallyParams, &tallyParams)
return tallyParams
}
// SetDepositParams sets DepositParams to the global param store
func (keeper Keeper) SetDepositParams(ctx sdk.Context, depositParams types.DepositParams) {
keeper.paramSpace.Set(ctx, types.ParamStoreKeyDepositParams, &depositParams)
}
// SetVotingParams sets VotingParams to the global param store
func (keeper Keeper) SetVotingParams(ctx sdk.Context, votingParams types.VotingParams) {
keeper.paramSpace.Set(ctx, types.ParamStoreKeyVotingParams, &votingParams)
}
// SetTallyParams sets TallyParams to the global param store
func (keeper Keeper) SetTallyParams(ctx sdk.Context, tallyParams types.TallyParams) {
keeper.paramSpace.Set(ctx, types.ParamStoreKeyTallyParams, &tallyParams)
}

View File

@ -1,4 +1,4 @@
package gov
package keeper
import (
"fmt"
@ -8,9 +8,9 @@ import (
)
// SubmitProposal create new proposal given a content
func (keeper Keeper) SubmitProposal(ctx sdk.Context, content Content) (Proposal, sdk.Error) {
func (keeper Keeper) SubmitProposal(ctx sdk.Context, content types.Content) (types.Proposal, sdk.Error) {
if !keeper.router.HasRoute(content.ProposalRoute()) {
return Proposal{}, ErrNoProposalHandlerExists(keeper.codespace, content)
return types.Proposal{}, types.ErrNoProposalHandlerExists(keeper.codespace, content)
}
// Execute the proposal content in a cache-wrapped context to validate the
@ -19,22 +19,22 @@ func (keeper Keeper) SubmitProposal(ctx sdk.Context, content Content) (Proposal,
cacheCtx, _ := ctx.CacheContext()
handler := keeper.router.GetRoute(content.ProposalRoute())
if err := handler(cacheCtx, content); err != nil {
return Proposal{}, ErrInvalidProposalContent(keeper.codespace, err.Result().Log)
return types.Proposal{}, types.ErrInvalidProposalContent(keeper.codespace, err.Result().Log)
}
proposalID, err := keeper.GetProposalID(ctx)
if err != nil {
return Proposal{}, err
return types.Proposal{}, err
}
submitTime := ctx.BlockHeader().Time
depositPeriod := keeper.GetDepositParams(ctx).MaxDepositPeriod
proposal := NewProposal(content, proposalID, submitTime, submitTime.Add(depositPeriod))
proposal := types.NewProposal(content, proposalID, submitTime, submitTime.Add(depositPeriod))
keeper.SetProposal(ctx, proposal)
keeper.InsertInactiveProposalQueue(ctx, proposalID, proposal.DepositEndTime)
keeper.setProposalID(ctx, proposalID+1)
keeper.SetProposalID(ctx, proposalID+1)
ctx.EventManager().EmitEvent(
sdk.NewEvent(
@ -46,10 +46,10 @@ func (keeper Keeper) SubmitProposal(ctx sdk.Context, content Content) (Proposal,
return proposal, nil
}
// GetProposal get Proposal from store by ProposalID
func (keeper Keeper) GetProposal(ctx sdk.Context, proposalID uint64) (proposal Proposal, ok bool) {
// GetProposal get proposal from store by ProposalID
func (keeper Keeper) GetProposal(ctx sdk.Context, proposalID uint64) (proposal types.Proposal, ok bool) {
store := ctx.KVStore(keeper.storeKey)
bz := store.Get(ProposalKey(proposalID))
bz := store.Get(types.ProposalKey(proposalID))
if bz == nil {
return
}
@ -58,10 +58,10 @@ func (keeper Keeper) GetProposal(ctx sdk.Context, proposalID uint64) (proposal P
}
// SetProposal set a proposal to store
func (keeper Keeper) SetProposal(ctx sdk.Context, proposal Proposal) {
func (keeper Keeper) SetProposal(ctx sdk.Context, proposal types.Proposal) {
store := ctx.KVStore(keeper.storeKey)
bz := keeper.cdc.MustMarshalBinaryLengthPrefixed(proposal)
store.Set(ProposalKey(proposal.ProposalID), bz)
store.Set(types.ProposalKey(proposal.ProposalID), bz)
}
// DeleteProposal deletes a proposal from store
@ -73,11 +73,27 @@ func (keeper Keeper) DeleteProposal(ctx sdk.Context, proposalID uint64) {
}
keeper.RemoveFromInactiveProposalQueue(ctx, proposalID, proposal.DepositEndTime)
keeper.RemoveFromActiveProposalQueue(ctx, proposalID, proposal.VotingEndTime)
store.Delete(ProposalKey(proposalID))
store.Delete(types.ProposalKey(proposalID))
}
// IterateProposals iterates over the all the proposals and performs a callback function
func (keeper Keeper) IterateProposals(ctx sdk.Context, cb func(proposal types.Proposal) (stop bool)) {
store := ctx.KVStore(keeper.storeKey)
iterator := sdk.KVStorePrefixIterator(store, types.ProposalsKeyPrefix)
defer iterator.Close()
for ; iterator.Valid(); iterator.Next() {
var proposal types.Proposal
keeper.cdc.MustUnmarshalBinaryLengthPrefixed(iterator.Value(), &proposal)
if cb(proposal) {
break
}
}
}
// GetProposals returns all the proposals from store
func (keeper Keeper) GetProposals(ctx sdk.Context) (proposals Proposals) {
func (keeper Keeper) GetProposals(ctx sdk.Context) (proposals types.Proposals) {
keeper.IterateProposals(ctx, func(proposal types.Proposal) bool {
proposals = append(proposals, proposal)
return false
@ -90,14 +106,14 @@ func (keeper Keeper) GetProposals(ctx sdk.Context) (proposals Proposals) {
// depositorAddr will filter proposals by whether or not that address has deposited to them
// status will filter proposals by status
// numLatest will fetch a specified number of the most recent proposals, or 0 for all proposals
func (keeper Keeper) GetProposalsFiltered(ctx sdk.Context, voterAddr sdk.AccAddress, depositorAddr sdk.AccAddress, status ProposalStatus, numLatest uint64) []Proposal {
func (keeper Keeper) GetProposalsFiltered(ctx sdk.Context, voterAddr sdk.AccAddress, depositorAddr sdk.AccAddress, status types.ProposalStatus, numLatest uint64) []types.Proposal {
maxProposalID, err := keeper.GetProposalID(ctx)
if err != nil {
return []Proposal{}
return []types.Proposal{}
}
matchingProposals := []Proposal{}
matchingProposals := []types.Proposal{}
if numLatest == 0 {
numLatest = maxProposalID
@ -123,7 +139,7 @@ func (keeper Keeper) GetProposalsFiltered(ctx sdk.Context, voterAddr sdk.AccAddr
continue
}
if ValidProposalStatus(status) && proposal.Status != status {
if types.ValidProposalStatus(status) && proposal.Status != status {
continue
}
@ -135,26 +151,25 @@ func (keeper Keeper) GetProposalsFiltered(ctx sdk.Context, voterAddr sdk.AccAddr
// GetProposalID gets the highest proposal ID
func (keeper Keeper) GetProposalID(ctx sdk.Context) (proposalID uint64, err sdk.Error) {
store := ctx.KVStore(keeper.storeKey)
bz := store.Get(ProposalIDKey)
bz := store.Get(types.ProposalIDKey)
if bz == nil {
return 0, ErrInvalidGenesis(keeper.codespace, "initial proposal ID hasn't been set")
return 0, types.ErrInvalidGenesis(keeper.codespace, "initial proposal ID hasn't been set")
}
keeper.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &proposalID)
proposalID = types.GetProposalIDFromBytes(bz)
return proposalID, nil
}
// Set the proposal ID
func (keeper Keeper) setProposalID(ctx sdk.Context, proposalID uint64) {
// SetProposalID sets the new proposal ID to the store
func (keeper Keeper) SetProposalID(ctx sdk.Context, proposalID uint64) {
store := ctx.KVStore(keeper.storeKey)
bz := keeper.cdc.MustMarshalBinaryLengthPrefixed(proposalID)
store.Set(ProposalIDKey, bz)
store.Set(types.ProposalIDKey, types.GetProposalIDBytes(proposalID))
}
func (keeper Keeper) activateVotingPeriod(ctx sdk.Context, proposal Proposal) {
func (keeper Keeper) activateVotingPeriod(ctx sdk.Context, proposal types.Proposal) {
proposal.VotingStartTime = ctx.BlockHeader().Time
votingPeriod := keeper.GetVotingParams(ctx).VotingPeriod
proposal.VotingEndTime = proposal.VotingStartTime.Add(votingPeriod)
proposal.Status = StatusVotingPeriod
proposal.Status = types.StatusVotingPeriod
keeper.SetProposal(ctx, proposal)
keeper.RemoveFromInactiveProposalQueue(ctx, proposal.ProposalID, proposal.DepositEndTime)

View File

@ -0,0 +1,122 @@
package keeper
import (
"strings"
"testing"
"time"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/gov/types"
"github.com/stretchr/testify/require"
)
func TestGetSetProposal(t *testing.T) {
ctx, _, keeper, _, _ := createTestInput(t, false, 100)
tp := TestProposal
proposal, err := keeper.SubmitProposal(ctx, tp)
require.NoError(t, err)
proposalID := proposal.ProposalID
keeper.SetProposal(ctx, proposal)
gotProposal, ok := keeper.GetProposal(ctx, proposalID)
require.True(t, ok)
require.True(t, ProposalEqual(proposal, gotProposal))
}
func TestActivateVotingPeriod(t *testing.T) {
ctx, _, keeper, _, _ := createTestInput(t, false, 100)
tp := TestProposal
proposal, err := keeper.SubmitProposal(ctx, tp)
require.NoError(t, err)
require.True(t, proposal.VotingStartTime.Equal(time.Time{}))
keeper.activateVotingPeriod(ctx, proposal)
require.True(t, proposal.VotingStartTime.Equal(ctx.BlockHeader().Time))
proposal, ok := keeper.GetProposal(ctx, proposal.ProposalID)
require.True(t, ok)
activeIterator := keeper.ActiveProposalQueueIterator(ctx, proposal.VotingEndTime)
require.True(t, activeIterator.Valid())
proposalID := types.GetProposalIDFromBytes(activeIterator.Value())
require.Equal(t, proposalID, proposal.ProposalID)
activeIterator.Close()
}
type validProposal struct{}
func (validProposal) GetTitle() string { return "title" }
func (validProposal) GetDescription() string { return "description" }
func (validProposal) ProposalRoute() string { return types.RouterKey }
func (validProposal) ProposalType() string { return types.ProposalTypeText }
func (validProposal) String() string { return "" }
func (validProposal) ValidateBasic() sdk.Error { return nil }
type invalidProposalTitle1 struct{ validProposal }
func (invalidProposalTitle1) GetTitle() string { return "" }
type invalidProposalTitle2 struct{ validProposal }
func (invalidProposalTitle2) GetTitle() string { return strings.Repeat("1234567890", 100) }
type invalidProposalDesc1 struct{ validProposal }
func (invalidProposalDesc1) GetDescription() string { return "" }
type invalidProposalDesc2 struct{ validProposal }
func (invalidProposalDesc2) GetDescription() string { return strings.Repeat("1234567890", 1000) }
type invalidProposalRoute struct{ validProposal }
func (invalidProposalRoute) ProposalRoute() string { return "nonexistingroute" }
type invalidProposalValidation struct{ validProposal }
func (invalidProposalValidation) ValidateBasic() sdk.Error {
return sdk.NewError(sdk.CodespaceUndefined, sdk.CodeInternal, "")
}
func registerTestCodec(cdc *codec.Codec) {
cdc.RegisterConcrete(validProposal{}, "test/validproposal", nil)
cdc.RegisterConcrete(invalidProposalTitle1{}, "test/invalidproposalt1", nil)
cdc.RegisterConcrete(invalidProposalTitle2{}, "test/invalidproposalt2", nil)
cdc.RegisterConcrete(invalidProposalDesc1{}, "test/invalidproposald1", nil)
cdc.RegisterConcrete(invalidProposalDesc2{}, "test/invalidproposald2", nil)
cdc.RegisterConcrete(invalidProposalRoute{}, "test/invalidproposalr", nil)
cdc.RegisterConcrete(invalidProposalValidation{}, "test/invalidproposalv", nil)
}
func TestSubmitProposal(t *testing.T) {
ctx, _, keeper, _, _ := createTestInput(t, false, 100)
registerTestCodec(keeper.cdc)
testCases := []struct {
content types.Content
expectedErr sdk.Error
}{
{validProposal{}, nil},
// Keeper does not check the validity of title and description, no error
{invalidProposalTitle1{}, nil},
{invalidProposalTitle2{}, nil},
{invalidProposalDesc1{}, nil},
{invalidProposalDesc2{}, nil},
// error only when invalid route
{invalidProposalRoute{}, types.ErrNoProposalHandlerExists(types.DefaultCodespace, invalidProposalRoute{})},
// Keeper does not call ValidateBasic, msg.ValidateBasic does
{invalidProposalValidation{}, nil},
}
for _, tc := range testCases {
_, err := keeper.SubmitProposal(ctx, tc.content)
require.Equal(t, tc.expectedErr, err, "unexpected type of error: %s", err)
}
}

View File

@ -1,4 +1,4 @@
package gov
package keeper
import (
"fmt"
@ -10,24 +10,25 @@ import (
"github.com/cosmos/cosmos-sdk/x/gov/types"
)
// NewQuerier creates a new gov Querier instance
func NewQuerier(keeper Keeper) sdk.Querier {
return func(ctx sdk.Context, path []string, req abci.RequestQuery) ([]byte, sdk.Error) {
switch path[0] {
case types.QueryParams:
return queryParams(ctx, path[1:], req, keeper)
case QueryProposals:
case types.QueryProposals:
return queryProposals(ctx, path[1:], req, keeper)
case QueryProposal:
case types.QueryProposal:
return queryProposal(ctx, path[1:], req, keeper)
case QueryDeposits:
case types.QueryDeposits:
return queryDeposits(ctx, path[1:], req, keeper)
case QueryDeposit:
case types.QueryDeposit:
return queryDeposit(ctx, path[1:], req, keeper)
case QueryVotes:
case types.QueryVotes:
return queryVotes(ctx, path[1:], req, keeper)
case QueryVote:
case types.QueryVote:
return queryVote(ctx, path[1:], req, keeper)
case QueryTally:
case types.QueryTally:
return queryTally(ctx, path[1:], req, keeper)
default:
return nil, sdk.ErrUnknownRequest("unknown gov query endpoint")
@ -37,19 +38,19 @@ func NewQuerier(keeper Keeper) sdk.Querier {
func queryParams(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) ([]byte, sdk.Error) {
switch path[0] {
case ParamDeposit:
case types.ParamDeposit:
bz, err := codec.MarshalJSONIndent(keeper.cdc, keeper.GetDepositParams(ctx))
if err != nil {
return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error()))
}
return bz, nil
case ParamVoting:
case types.ParamVoting:
bz, err := codec.MarshalJSONIndent(keeper.cdc, keeper.GetVotingParams(ctx))
if err != nil {
return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error()))
}
return bz, nil
case ParamTallying:
case types.ParamTallying:
bz, err := codec.MarshalJSONIndent(keeper.cdc, keeper.GetTallyParams(ctx))
if err != nil {
return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error()))
@ -62,7 +63,7 @@ func queryParams(ctx sdk.Context, path []string, req abci.RequestQuery, keeper K
// nolint: unparam
func queryProposal(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) ([]byte, sdk.Error) {
var params QueryProposalParams
var params types.QueryProposalParams
err := keeper.cdc.UnmarshalJSON(req.Data, &params)
if err != nil {
return nil, sdk.ErrUnknownRequest(sdk.AppendMsgToErr("incorrectly formatted request data", err.Error()))
@ -70,7 +71,7 @@ func queryProposal(ctx sdk.Context, path []string, req abci.RequestQuery, keeper
proposal, ok := keeper.GetProposal(ctx, params.ProposalID)
if !ok {
return nil, ErrUnknownProposal(DefaultCodespace, params.ProposalID)
return nil, types.ErrUnknownProposal(types.DefaultCodespace, params.ProposalID)
}
bz, err := codec.MarshalJSONIndent(keeper.cdc, proposal)
@ -82,7 +83,7 @@ func queryProposal(ctx sdk.Context, path []string, req abci.RequestQuery, keeper
// nolint: unparam
func queryDeposit(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) ([]byte, sdk.Error) {
var params QueryDepositParams
var params types.QueryDepositParams
err := keeper.cdc.UnmarshalJSON(req.Data, &params)
if err != nil {
return nil, sdk.ErrUnknownRequest(sdk.AppendMsgToErr("incorrectly formatted request data", err.Error()))
@ -98,7 +99,7 @@ func queryDeposit(ctx sdk.Context, path []string, req abci.RequestQuery, keeper
// nolint: unparam
func queryVote(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) ([]byte, sdk.Error) {
var params QueryVoteParams
var params types.QueryVoteParams
err := keeper.cdc.UnmarshalJSON(req.Data, &params)
if err != nil {
return nil, sdk.ErrUnknownRequest(sdk.AppendMsgToErr("incorrectly formatted request data", err.Error()))
@ -114,7 +115,7 @@ func queryVote(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Kee
// nolint: unparam
func queryDeposits(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) ([]byte, sdk.Error) {
var params QueryProposalParams
var params types.QueryProposalParams
err := keeper.cdc.UnmarshalJSON(req.Data, &params)
if err != nil {
return nil, sdk.ErrUnknownRequest(sdk.AppendMsgToErr("incorrectly formatted request data", err.Error()))
@ -131,7 +132,7 @@ func queryDeposits(ctx sdk.Context, path []string, req abci.RequestQuery, keeper
// nolint: unparam
func queryTally(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) ([]byte, sdk.Error) {
var params QueryProposalParams
var params types.QueryProposalParams
err := keeper.cdc.UnmarshalJSON(req.Data, &params)
if err != nil {
return nil, sdk.ErrUnknownRequest(sdk.AppendMsgToErr("incorrectly formatted request data", err.Error()))
@ -141,18 +142,18 @@ func queryTally(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Ke
proposal, ok := keeper.GetProposal(ctx, proposalID)
if !ok {
return nil, ErrUnknownProposal(DefaultCodespace, proposalID)
return nil, types.ErrUnknownProposal(types.DefaultCodespace, proposalID)
}
var tallyResult TallyResult
var tallyResult types.TallyResult
if proposal.Status == StatusDepositPeriod {
tallyResult = EmptyTallyResult()
} else if proposal.Status == StatusPassed || proposal.Status == StatusRejected {
if proposal.Status == types.StatusDepositPeriod {
tallyResult = types.EmptyTallyResult()
} else if proposal.Status == types.StatusPassed || proposal.Status == types.StatusRejected {
tallyResult = proposal.FinalTallyResult
} else {
// proposal is in voting period
_, _, tallyResult = tally(ctx, keeper, proposal)
_, _, tallyResult = keeper.Tally(ctx, proposal)
}
bz, err := codec.MarshalJSONIndent(keeper.cdc, tallyResult)
@ -164,7 +165,7 @@ func queryTally(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Ke
// nolint: unparam
func queryVotes(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) ([]byte, sdk.Error) {
var params QueryProposalParams
var params types.QueryProposalParams
err := keeper.cdc.UnmarshalJSON(req.Data, &params)
if err != nil {
@ -182,7 +183,7 @@ func queryVotes(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Ke
// nolint: unparam
func queryProposals(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) ([]byte, sdk.Error) {
var params QueryProposalsParams
var params types.QueryProposalsParams
err := keeper.cdc.UnmarshalJSON(req.Data, &params)
if err != nil {
return nil, sdk.ErrUnknownRequest(sdk.AppendMsgToErr("incorrectly formatted request data", err.Error()))

View File

@ -0,0 +1,315 @@
package keeper
import (
"strings"
"testing"
"github.com/stretchr/testify/require"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/gov/types"
)
const custom = "custom"
func getQueriedParams(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier) (types.DepositParams, types.VotingParams, types.TallyParams) {
query := abci.RequestQuery{
Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryParams, types.ParamDeposit}, "/"),
Data: []byte{},
}
bz, err := querier(ctx, []string{types.QueryParams, types.ParamDeposit}, query)
require.NoError(t, err)
require.NotNil(t, bz)
var depositParams types.DepositParams
require.NoError(t, cdc.UnmarshalJSON(bz, &depositParams))
query = abci.RequestQuery{
Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryParams, types.ParamVoting}, "/"),
Data: []byte{},
}
bz, err = querier(ctx, []string{types.QueryParams, types.ParamVoting}, query)
require.NoError(t, err)
require.NotNil(t, bz)
var votingParams types.VotingParams
require.NoError(t, cdc.UnmarshalJSON(bz, &votingParams))
query = abci.RequestQuery{
Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryParams, types.ParamTallying}, "/"),
Data: []byte{},
}
bz, err = querier(ctx, []string{types.QueryParams, types.ParamTallying}, query)
require.NoError(t, err)
require.NotNil(t, bz)
var tallyParams types.TallyParams
require.NoError(t, cdc.UnmarshalJSON(bz, &tallyParams))
return depositParams, votingParams, tallyParams
}
func getQueriedProposal(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier, proposalID uint64) types.Proposal {
query := abci.RequestQuery{
Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryProposal}, "/"),
Data: cdc.MustMarshalJSON(types.NewQueryProposalParams(proposalID)),
}
bz, err := querier(ctx, []string{types.QueryProposal}, query)
require.NoError(t, err)
require.NotNil(t, bz)
var proposal types.Proposal
require.NoError(t, cdc.UnmarshalJSON(bz, proposal))
return proposal
}
func getQueriedProposals(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier, depositor, voter sdk.AccAddress, status types.ProposalStatus, limit uint64) []types.Proposal {
query := abci.RequestQuery{
Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryProposals}, "/"),
Data: cdc.MustMarshalJSON(types.NewQueryProposalsParams(status, limit, voter, depositor)),
}
bz, err := querier(ctx, []string{types.QueryProposals}, query)
require.NoError(t, err)
require.NotNil(t, bz)
var proposals types.Proposals
require.NoError(t, cdc.UnmarshalJSON(bz, &proposals))
return proposals
}
func getQueriedDeposit(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier, proposalID uint64, depositor sdk.AccAddress) types.Deposit {
query := abci.RequestQuery{
Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryDeposit}, "/"),
Data: cdc.MustMarshalJSON(types.NewQueryDepositParams(proposalID, depositor)),
}
bz, err := querier(ctx, []string{types.QueryDeposit}, query)
require.NoError(t, err)
require.NotNil(t, bz)
var deposit types.Deposit
require.NoError(t, cdc.UnmarshalJSON(bz, &deposit))
return deposit
}
func getQueriedDeposits(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier, proposalID uint64) []types.Deposit {
query := abci.RequestQuery{
Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryDeposits}, "/"),
Data: cdc.MustMarshalJSON(types.NewQueryProposalParams(proposalID)),
}
bz, err := querier(ctx, []string{types.QueryDeposits}, query)
require.NoError(t, err)
require.NotNil(t, bz)
var deposits []types.Deposit
require.NoError(t, cdc.UnmarshalJSON(bz, &deposits))
return deposits
}
func getQueriedVote(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier, proposalID uint64, voter sdk.AccAddress) types.Vote {
query := abci.RequestQuery{
Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryVote}, "/"),
Data: cdc.MustMarshalJSON(types.NewQueryVoteParams(proposalID, voter)),
}
bz, err := querier(ctx, []string{types.QueryVote}, query)
require.NoError(t, err)
require.NotNil(t, bz)
var vote types.Vote
require.NoError(t, cdc.UnmarshalJSON(bz, &vote))
return vote
}
func getQueriedVotes(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier, proposalID uint64) []types.Vote {
query := abci.RequestQuery{
Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryVote}, "/"),
Data: cdc.MustMarshalJSON(types.NewQueryProposalParams(proposalID)),
}
bz, err := querier(ctx, []string{types.QueryVotes}, query)
require.NoError(t, err)
require.NotNil(t, bz)
var votes []types.Vote
require.NoError(t, cdc.UnmarshalJSON(bz, &votes))
return votes
}
func getQueriedTally(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier, proposalID uint64) types.TallyResult {
query := abci.RequestQuery{
Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryTally}, "/"),
Data: cdc.MustMarshalJSON(types.NewQueryProposalParams(proposalID)),
}
bz, err := querier(ctx, []string{types.QueryTally}, query)
require.NoError(t, err)
require.NotNil(t, bz)
var tally types.TallyResult
require.NoError(t, cdc.UnmarshalJSON(bz, &tally))
return tally
}
func TestQueries(t *testing.T) {
ctx, _, keeper, _, _ := createTestInput(t, false, 1000)
querier := NewQuerier(keeper)
oneCoins := sdk.NewCoins(sdk.NewInt64Coin(sdk.DefaultBondDenom, 1))
consCoins := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.TokensFromConsensusPower(10)))
tp := TestProposal
depositParams, _, _ := getQueriedParams(t, ctx, keeper.cdc, querier)
// TestAddrs[0] proposes (and deposits) proposals #1 and #2
proposal1, err := keeper.SubmitProposal(ctx, tp)
require.NoError(t, err)
deposit1 := types.NewDeposit(proposal1.ProposalID, TestAddrs[0], oneCoins)
err, _ = keeper.AddDeposit(ctx, deposit1.ProposalID, deposit1.Depositor, deposit1.Amount)
require.NoError(t, err)
proposal1.TotalDeposit = proposal1.TotalDeposit.Add(deposit1.Amount)
proposal2, err := keeper.SubmitProposal(ctx, tp)
require.NoError(t, err)
deposit2 := types.NewDeposit(proposal2.ProposalID, TestAddrs[0], consCoins)
err, _ = keeper.AddDeposit(ctx, deposit2.ProposalID, deposit2.Depositor, deposit2.Amount)
require.NoError(t, err)
proposal2.TotalDeposit = proposal2.TotalDeposit.Add(deposit2.Amount)
// TestAddrs[1] proposes (and deposits) on proposal #3
proposal3, err := keeper.SubmitProposal(ctx, tp)
require.NoError(t, err)
deposit3 := types.NewDeposit(proposal3.ProposalID, TestAddrs[1], oneCoins)
err, _ = keeper.AddDeposit(ctx, deposit3.ProposalID, deposit3.Depositor, deposit3.Amount)
require.NoError(t, err)
proposal3.TotalDeposit = proposal3.TotalDeposit.Add(deposit3.Amount)
// TestAddrs[1] deposits on proposals #2 & #3
deposit4 := types.NewDeposit(proposal2.ProposalID, TestAddrs[1], depositParams.MinDeposit)
err, _ = keeper.AddDeposit(ctx, deposit4.ProposalID, deposit4.Depositor, deposit4.Amount)
require.NoError(t, err)
proposal2.TotalDeposit = proposal2.TotalDeposit.Add(deposit4.Amount)
proposal2.Status = types.StatusVotingPeriod
proposal2.VotingEndTime = proposal2.VotingEndTime.Add(types.DefaultPeriod)
deposit5 := types.NewDeposit(proposal3.ProposalID, TestAddrs[1], depositParams.MinDeposit)
err, _ = keeper.AddDeposit(ctx, deposit5.ProposalID, deposit5.Depositor, deposit5.Amount)
require.NoError(t, err)
proposal3.TotalDeposit = proposal3.TotalDeposit.Add(deposit5.Amount)
proposal3.Status = types.StatusVotingPeriod
proposal3.VotingEndTime = proposal3.VotingEndTime.Add(types.DefaultPeriod)
// total deposit of TestAddrs[1] on proposal #3 is worth the proposal deposit + individual deposit
deposit5.Amount = deposit5.Amount.Add(deposit3.Amount)
// check deposits on proposal1 match individual deposits
deposits := getQueriedDeposits(t, ctx, keeper.cdc, querier, proposal1.ProposalID)
require.Len(t, deposits, 1)
require.Equal(t, deposit1, deposits[0])
deposit := getQueriedDeposit(t, ctx, keeper.cdc, querier, proposal1.ProposalID, TestAddrs[0])
require.Equal(t, deposit1, deposit)
// check deposits on proposal2 match individual deposits
deposits = getQueriedDeposits(t, ctx, keeper.cdc, querier, proposal2.ProposalID)
require.Len(t, deposits, 2)
// NOTE order of deposits is determined by the addresses
require.Equal(t, deposit2, deposits[0])
require.Equal(t, deposit4, deposits[1])
deposit = getQueriedDeposit(t, ctx, keeper.cdc, querier, proposal2.ProposalID, TestAddrs[0])
require.Equal(t, deposit2, deposits[0])
deposit = getQueriedDeposit(t, ctx, keeper.cdc, querier, proposal2.ProposalID, TestAddrs[1])
require.Equal(t, deposit4, deposits[1])
// check deposits on proposal3 match individual deposits
deposits = getQueriedDeposits(t, ctx, keeper.cdc, querier, proposal3.ProposalID)
require.Len(t, deposits, 1)
require.Equal(t, deposit5, deposits[0])
deposit = getQueriedDeposit(t, ctx, keeper.cdc, querier, proposal3.ProposalID, TestAddrs[1])
require.Equal(t, deposit5, deposit)
// Only proposal #1 should be in types.Deposit Period
proposals := getQueriedProposals(t, ctx, keeper.cdc, querier, nil, nil, types.StatusDepositPeriod, 0)
require.Len(t, proposals, 1)
require.Equal(t, proposal1, proposals[0])
// Only proposals #2 and #3 should be in Voting Period
proposals = getQueriedProposals(t, ctx, keeper.cdc, querier, nil, nil, types.StatusVotingPeriod, 0)
require.Len(t, proposals, 2)
require.Equal(t, proposal2, proposals[0])
require.Equal(t, proposal3, proposals[1])
// Addrs[0] votes on proposals #2 & #3
vote1 := types.NewVote(proposal2.ProposalID, TestAddrs[0], types.OptionYes)
vote2 := types.NewVote(proposal3.ProposalID, TestAddrs[0], types.OptionYes)
keeper.SetVote(ctx, vote1)
keeper.SetVote(ctx, vote2)
// Addrs[1] votes on proposal #3
vote3 := types.NewVote(proposal3.ProposalID, TestAddrs[1], types.OptionYes)
keeper.SetVote(ctx, vote3)
// Test query voted by TestAddrs[0]
proposals = getQueriedProposals(t, ctx, keeper.cdc, querier, nil, TestAddrs[0], types.StatusNil, 0)
require.Equal(t, proposal2, proposals[0])
require.Equal(t, proposal3, proposals[1])
// Test query votes on types.Proposal 2
votes := getQueriedVotes(t, ctx, keeper.cdc, querier, proposal2.ProposalID)
require.Len(t, votes, 1)
require.Equal(t, vote1, votes[0])
vote := getQueriedVote(t, ctx, keeper.cdc, querier, proposal2.ProposalID, TestAddrs[0])
require.Equal(t, vote1, vote)
// Test query votes on types.Proposal 3
votes = getQueriedVotes(t, ctx, keeper.cdc, querier, proposal3.ProposalID)
require.Len(t, votes, 2)
require.Equal(t, vote2, votes[0])
require.Equal(t, vote3, votes[1])
// Test query all proposals
proposals = getQueriedProposals(t, ctx, keeper.cdc, querier, nil, nil, types.StatusNil, 0)
require.Equal(t, proposal1, proposals[0])
require.Equal(t, proposal2, proposals[1])
require.Equal(t, proposal3, proposals[2])
// Test query voted by TestAddrs[1]
proposals = getQueriedProposals(t, ctx, keeper.cdc, querier, nil, TestAddrs[1], types.StatusNil, 0)
require.Equal(t, proposal3.ProposalID, proposals[0].ProposalID)
// Test query deposited by TestAddrs[0]
proposals = getQueriedProposals(t, ctx, keeper.cdc, querier, TestAddrs[0], nil, types.StatusNil, 0)
require.Equal(t, proposal1.ProposalID, proposals[0].ProposalID)
// Test query deposited by addr2
proposals = getQueriedProposals(t, ctx, keeper.cdc, querier, TestAddrs[1], nil, types.StatusNil, 0)
require.Equal(t, proposal2.ProposalID, proposals[0].ProposalID)
require.Equal(t, proposal3.ProposalID, proposals[1].ProposalID)
// Test query voted AND deposited by addr1
proposals = getQueriedProposals(t, ctx, keeper.cdc, querier, TestAddrs[0], TestAddrs[0], types.StatusNil, 0)
require.Equal(t, proposal2.ProposalID, proposals[0].ProposalID)
}

View File

@ -1,4 +1,4 @@
package gov
package keeper
import (
sdk "github.com/cosmos/cosmos-sdk/types"
@ -6,46 +6,28 @@ import (
"github.com/cosmos/cosmos-sdk/x/staking/exported"
)
// validatorGovInfo used for tallying
type validatorGovInfo struct {
Address sdk.ValAddress // address of the validator operator
BondedTokens sdk.Int // Power of a Validator
DelegatorShares sdk.Dec // Total outstanding delegator shares
DelegatorDeductions sdk.Dec // Delegator deductions from validator's delegators voting independently
Vote VoteOption // Vote of the validator
}
func newValidatorGovInfo(address sdk.ValAddress, bondedTokens sdk.Int, delegatorShares,
delegatorDeductions sdk.Dec, vote VoteOption) validatorGovInfo {
return validatorGovInfo{
Address: address,
BondedTokens: bondedTokens,
DelegatorShares: delegatorShares,
DelegatorDeductions: delegatorDeductions,
Vote: vote,
}
}
// TODO: Break into several smaller functions for clarity
func tally(ctx sdk.Context, keeper Keeper, proposal Proposal) (passes bool, burnDeposits bool, tallyResults TallyResult) {
results := make(map[VoteOption]sdk.Dec)
results[OptionYes] = sdk.ZeroDec()
results[OptionAbstain] = sdk.ZeroDec()
results[OptionNo] = sdk.ZeroDec()
results[OptionNoWithVeto] = sdk.ZeroDec()
// Tally iterates over the votes and updates the tally of a proposal based on the voting power of the
// voters
func (keeper Keeper) Tally(ctx sdk.Context, proposal types.Proposal) (passes bool, burnDeposits bool, tallyResults types.TallyResult) {
results := make(map[types.VoteOption]sdk.Dec)
results[types.OptionYes] = sdk.ZeroDec()
results[types.OptionAbstain] = sdk.ZeroDec()
results[types.OptionNo] = sdk.ZeroDec()
results[types.OptionNoWithVeto] = sdk.ZeroDec()
totalVotingPower := sdk.ZeroDec()
currValidators := make(map[string]validatorGovInfo)
currValidators := make(map[string]types.ValidatorGovInfo)
// fetch all the bonded validators, insert them into currValidators
keeper.sk.IterateBondedValidatorsByPower(ctx, func(index int64, validator exported.ValidatorI) (stop bool) {
currValidators[validator.GetOperator().String()] = newValidatorGovInfo(
currValidators[validator.GetOperator().String()] = types.NewValidatorGovInfo(
validator.GetOperator(),
validator.GetBondedTokens(),
validator.GetDelegatorShares(),
sdk.ZeroDec(),
OptionEmpty,
types.OptionEmpty,
)
return false
@ -84,7 +66,7 @@ func tally(ctx sdk.Context, keeper Keeper, proposal Proposal) (passes bool, burn
// iterate over the validators again to tally their voting power
for _, val := range currValidators {
if val.Vote == OptionEmpty {
if val.Vote == types.OptionEmpty {
continue
}
@ -97,7 +79,7 @@ func tally(ctx sdk.Context, keeper Keeper, proposal Proposal) (passes bool, burn
}
tallyParams := keeper.GetTallyParams(ctx)
tallyResults = NewTallyResultFromMap(results)
tallyResults = types.NewTallyResultFromMap(results)
// TODO: Upgrade the spec to cover all of these cases & remove pseudocode.
// If there is no staked coins, the proposal fails
@ -112,17 +94,17 @@ func tally(ctx sdk.Context, keeper Keeper, proposal Proposal) (passes bool, burn
}
// If no one votes (everyone abstains), proposal fails
if totalVotingPower.Sub(results[OptionAbstain]).Equal(sdk.ZeroDec()) {
if totalVotingPower.Sub(results[types.OptionAbstain]).Equal(sdk.ZeroDec()) {
return false, false, tallyResults
}
// If more than 1/3 of voters veto, proposal fails
if results[OptionNoWithVeto].Quo(totalVotingPower).GT(tallyParams.Veto) {
if results[types.OptionNoWithVeto].Quo(totalVotingPower).GT(tallyParams.Veto) {
return false, true, tallyResults
}
// If more than 1/2 of non-abstaining voters vote Yes, proposal passes
if results[OptionYes].Quo(totalVotingPower.Sub(results[OptionAbstain])).GT(tallyParams.Threshold) {
if results[types.OptionYes].Quo(totalVotingPower.Sub(results[types.OptionAbstain])).GT(tallyParams.Threshold) {
return true, false, tallyResults
}

398
x/gov/keeper/tally_test.go Normal file
View File

@ -0,0 +1,398 @@
package keeper
import (
"testing"
"github.com/stretchr/testify/require"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/gov/types"
"github.com/cosmos/cosmos-sdk/x/staking"
)
func TestTallyNoOneVotes(t *testing.T) {
ctx, _, keeper, sk, _ := createTestInput(t, false, 100)
createValidators(ctx, sk, []int64{5, 5, 5})
tp := TestProposal
proposal, err := keeper.SubmitProposal(ctx, tp)
require.NoError(t, err)
proposalID := proposal.ProposalID
proposal.Status = types.StatusVotingPeriod
keeper.SetProposal(ctx, proposal)
proposal, ok := keeper.GetProposal(ctx, proposalID)
require.True(t, ok)
passes, burnDeposits, tallyResults := keeper.Tally(ctx, proposal)
require.False(t, passes)
require.True(t, burnDeposits)
require.True(t, tallyResults.Equals(types.EmptyTallyResult()))
}
func TestTallyNoQuorum(t *testing.T) {
ctx, _, keeper, sk, _ := createTestInput(t, false, 100)
createValidators(ctx, sk, []int64{2, 5, 0})
tp := TestProposal
proposal, err := keeper.SubmitProposal(ctx, tp)
require.NoError(t, err)
proposalID := proposal.ProposalID
proposal.Status = types.StatusVotingPeriod
keeper.SetProposal(ctx, proposal)
err = keeper.AddVote(ctx, proposalID, TestAddrs[0], types.OptionYes)
require.Nil(t, err)
proposal, ok := keeper.GetProposal(ctx, proposalID)
require.True(t, ok)
passes, burnDeposits, _ := keeper.Tally(ctx, proposal)
require.False(t, passes)
require.True(t, burnDeposits)
}
func TestTallyOnlyValidatorsAllYes(t *testing.T) {
ctx, _, keeper, sk, _ := createTestInput(t, false, 100)
createValidators(ctx, sk, []int64{5, 5, 5})
tp := TestProposal
proposal, err := keeper.SubmitProposal(ctx, tp)
require.NoError(t, err)
proposalID := proposal.ProposalID
proposal.Status = types.StatusVotingPeriod
keeper.SetProposal(ctx, proposal)
require.NoError(t, keeper.AddVote(ctx, proposalID, valAccAddr1, types.OptionYes))
require.NoError(t, keeper.AddVote(ctx, proposalID, valAccAddr2, types.OptionYes))
require.NoError(t, keeper.AddVote(ctx, proposalID, valAccAddr3, types.OptionYes))
proposal, ok := keeper.GetProposal(ctx, proposalID)
require.True(t, ok)
passes, burnDeposits, tallyResults := keeper.Tally(ctx, proposal)
require.True(t, passes)
require.False(t, burnDeposits)
require.False(t, tallyResults.Equals(types.EmptyTallyResult()))
}
func TestTallyOnlyValidators51No(t *testing.T) {
ctx, _, keeper, sk, _ := createTestInput(t, false, 100)
createValidators(ctx, sk, []int64{5, 6, 0})
tp := TestProposal
proposal, err := keeper.SubmitProposal(ctx, tp)
require.NoError(t, err)
proposalID := proposal.ProposalID
proposal.Status = types.StatusVotingPeriod
keeper.SetProposal(ctx, proposal)
require.NoError(t, keeper.AddVote(ctx, proposalID, valAccAddr1, types.OptionYes))
require.NoError(t, keeper.AddVote(ctx, proposalID, valAccAddr2, types.OptionNo))
proposal, ok := keeper.GetProposal(ctx, proposalID)
require.True(t, ok)
passes, burnDeposits, _ := keeper.Tally(ctx, proposal)
require.False(t, passes)
require.False(t, burnDeposits)
}
func TestTallyOnlyValidators51Yes(t *testing.T) {
ctx, _, keeper, sk, _ := createTestInput(t, false, 100)
createValidators(ctx, sk, []int64{5, 6, 0})
tp := TestProposal
proposal, err := keeper.SubmitProposal(ctx, tp)
require.NoError(t, err)
proposalID := proposal.ProposalID
proposal.Status = types.StatusVotingPeriod
keeper.SetProposal(ctx, proposal)
require.NoError(t, keeper.AddVote(ctx, proposalID, valAccAddr1, types.OptionNo))
require.NoError(t, keeper.AddVote(ctx, proposalID, valAccAddr2, types.OptionYes))
proposal, ok := keeper.GetProposal(ctx, proposalID)
require.True(t, ok)
passes, burnDeposits, tallyResults := keeper.Tally(ctx, proposal)
require.True(t, passes)
require.False(t, burnDeposits)
require.False(t, tallyResults.Equals(types.EmptyTallyResult()))
}
func TestTallyOnlyValidatorsVetoed(t *testing.T) {
ctx, _, keeper, sk, _ := createTestInput(t, false, 100)
createValidators(ctx, sk, []int64{6, 6, 7})
tp := TestProposal
proposal, err := keeper.SubmitProposal(ctx, tp)
require.NoError(t, err)
proposalID := proposal.ProposalID
proposal.Status = types.StatusVotingPeriod
keeper.SetProposal(ctx, proposal)
require.NoError(t, keeper.AddVote(ctx, proposalID, valAccAddr1, types.OptionYes))
require.NoError(t, keeper.AddVote(ctx, proposalID, valAccAddr2, types.OptionYes))
require.NoError(t, keeper.AddVote(ctx, proposalID, valAccAddr3, types.OptionNoWithVeto))
proposal, ok := keeper.GetProposal(ctx, proposalID)
require.True(t, ok)
passes, burnDeposits, tallyResults := keeper.Tally(ctx, proposal)
require.False(t, passes)
require.True(t, burnDeposits)
require.False(t, tallyResults.Equals(types.EmptyTallyResult()))
}
func TestTallyOnlyValidatorsAbstainPasses(t *testing.T) {
ctx, _, keeper, sk, _ := createTestInput(t, false, 100)
createValidators(ctx, sk, []int64{6, 6, 7})
tp := TestProposal
proposal, err := keeper.SubmitProposal(ctx, tp)
require.NoError(t, err)
proposalID := proposal.ProposalID
proposal.Status = types.StatusVotingPeriod
keeper.SetProposal(ctx, proposal)
require.NoError(t, keeper.AddVote(ctx, proposalID, valAccAddr1, types.OptionAbstain))
require.NoError(t, keeper.AddVote(ctx, proposalID, valAccAddr2, types.OptionNo))
require.NoError(t, keeper.AddVote(ctx, proposalID, valAccAddr3, types.OptionYes))
proposal, ok := keeper.GetProposal(ctx, proposalID)
require.True(t, ok)
passes, burnDeposits, tallyResults := keeper.Tally(ctx, proposal)
require.True(t, passes)
require.False(t, burnDeposits)
require.False(t, tallyResults.Equals(types.EmptyTallyResult()))
}
func TestTallyOnlyValidatorsAbstainFails(t *testing.T) {
ctx, _, keeper, sk, _ := createTestInput(t, false, 100)
createValidators(ctx, sk, []int64{6, 6, 7})
tp := TestProposal
proposal, err := keeper.SubmitProposal(ctx, tp)
require.NoError(t, err)
proposalID := proposal.ProposalID
proposal.Status = types.StatusVotingPeriod
keeper.SetProposal(ctx, proposal)
require.NoError(t, keeper.AddVote(ctx, proposalID, valAccAddr1, types.OptionAbstain))
require.NoError(t, keeper.AddVote(ctx, proposalID, valAccAddr2, types.OptionYes))
require.NoError(t, keeper.AddVote(ctx, proposalID, valAccAddr3, types.OptionNo))
proposal, ok := keeper.GetProposal(ctx, proposalID)
require.True(t, ok)
passes, burnDeposits, tallyResults := keeper.Tally(ctx, proposal)
require.False(t, passes)
require.False(t, burnDeposits)
require.False(t, tallyResults.Equals(types.EmptyTallyResult()))
}
func TestTallyOnlyValidatorsNonVoter(t *testing.T) {
ctx, _, keeper, sk, _ := createTestInput(t, false, 100)
createValidators(ctx, sk, []int64{5, 6, 7})
tp := TestProposal
proposal, err := keeper.SubmitProposal(ctx, tp)
require.NoError(t, err)
proposalID := proposal.ProposalID
proposal.Status = types.StatusVotingPeriod
keeper.SetProposal(ctx, proposal)
require.NoError(t, keeper.AddVote(ctx, proposalID, valAccAddr1, types.OptionYes))
require.NoError(t, keeper.AddVote(ctx, proposalID, valAccAddr2, types.OptionNo))
proposal, ok := keeper.GetProposal(ctx, proposalID)
require.True(t, ok)
passes, burnDeposits, tallyResults := keeper.Tally(ctx, proposal)
require.False(t, passes)
require.False(t, burnDeposits)
require.False(t, tallyResults.Equals(types.EmptyTallyResult()))
}
func TestTallyDelgatorOverride(t *testing.T) {
ctx, _, keeper, sk, _ := createTestInput(t, false, 100)
createValidators(ctx, sk, []int64{5, 6, 7})
delTokens := sdk.TokensFromConsensusPower(30)
val1, found := sk.GetValidator(ctx, valOpAddr1)
require.True(t, found)
_, err := sk.Delegate(ctx, TestAddrs[0], delTokens, sdk.Unbonded, val1, true)
require.NoError(t, err)
_ = staking.EndBlocker(ctx, sk)
tp := TestProposal
proposal, err := keeper.SubmitProposal(ctx, tp)
require.NoError(t, err)
proposalID := proposal.ProposalID
proposal.Status = types.StatusVotingPeriod
keeper.SetProposal(ctx, proposal)
require.NoError(t, keeper.AddVote(ctx, proposalID, valAccAddr1, types.OptionYes))
require.NoError(t, keeper.AddVote(ctx, proposalID, valAccAddr2, types.OptionYes))
require.NoError(t, keeper.AddVote(ctx, proposalID, valAccAddr3, types.OptionYes))
require.NoError(t, keeper.AddVote(ctx, proposalID, TestAddrs[0], types.OptionNo))
proposal, ok := keeper.GetProposal(ctx, proposalID)
require.True(t, ok)
passes, burnDeposits, tallyResults := keeper.Tally(ctx, proposal)
require.False(t, passes)
require.False(t, burnDeposits)
require.False(t, tallyResults.Equals(types.EmptyTallyResult()))
}
func TestTallyDelgatorInherit(t *testing.T) {
ctx, _, keeper, sk, _ := createTestInput(t, false, 100)
createValidators(ctx, sk, []int64{5, 6, 7})
delTokens := sdk.TokensFromConsensusPower(30)
val3, found := sk.GetValidator(ctx, valOpAddr3)
require.True(t, found)
_, err := sk.Delegate(ctx, TestAddrs[0], delTokens, sdk.Unbonded, val3, true)
require.NoError(t, err)
_ = staking.EndBlocker(ctx, sk)
tp := TestProposal
proposal, err := keeper.SubmitProposal(ctx, tp)
require.NoError(t, err)
proposalID := proposal.ProposalID
proposal.Status = types.StatusVotingPeriod
keeper.SetProposal(ctx, proposal)
require.NoError(t, keeper.AddVote(ctx, proposalID, valAccAddr1, types.OptionNo))
require.NoError(t, keeper.AddVote(ctx, proposalID, valAccAddr2, types.OptionNo))
require.NoError(t, keeper.AddVote(ctx, proposalID, valAccAddr3, types.OptionYes))
proposal, ok := keeper.GetProposal(ctx, proposalID)
require.True(t, ok)
passes, burnDeposits, tallyResults := keeper.Tally(ctx, proposal)
require.True(t, passes)
require.False(t, burnDeposits)
require.False(t, tallyResults.Equals(types.EmptyTallyResult()))
}
func TestTallyDelgatorMultipleOverride(t *testing.T) {
ctx, _, keeper, sk, _ := createTestInput(t, false, 100)
createValidators(ctx, sk, []int64{5, 6, 7})
delTokens := sdk.TokensFromConsensusPower(10)
val1, found := sk.GetValidator(ctx, valOpAddr1)
require.True(t, found)
val2, found := sk.GetValidator(ctx, valOpAddr2)
require.True(t, found)
_, err := sk.Delegate(ctx, TestAddrs[0], delTokens, sdk.Unbonded, val1, true)
require.NoError(t, err)
_, err = sk.Delegate(ctx, TestAddrs[0], delTokens, sdk.Unbonded, val2, true)
require.NoError(t, err)
_ = staking.EndBlocker(ctx, sk)
tp := TestProposal
proposal, err := keeper.SubmitProposal(ctx, tp)
require.NoError(t, err)
proposalID := proposal.ProposalID
proposal.Status = types.StatusVotingPeriod
keeper.SetProposal(ctx, proposal)
require.NoError(t, keeper.AddVote(ctx, proposalID, valAccAddr1, types.OptionYes))
require.NoError(t, keeper.AddVote(ctx, proposalID, valAccAddr2, types.OptionYes))
require.NoError(t, keeper.AddVote(ctx, proposalID, valAccAddr3, types.OptionYes))
require.NoError(t, keeper.AddVote(ctx, proposalID, TestAddrs[0], types.OptionNo))
proposal, ok := keeper.GetProposal(ctx, proposalID)
require.True(t, ok)
passes, burnDeposits, tallyResults := keeper.Tally(ctx, proposal)
require.False(t, passes)
require.False(t, burnDeposits)
require.False(t, tallyResults.Equals(types.EmptyTallyResult()))
}
func TestTallyDelgatorMultipleInherit(t *testing.T) {
ctx, _, keeper, sk, _ := createTestInput(t, false, 100)
createValidators(ctx, sk, []int64{25, 6, 7})
delTokens := sdk.TokensFromConsensusPower(10)
val2, found := sk.GetValidator(ctx, valOpAddr2)
require.True(t, found)
val3, found := sk.GetValidator(ctx, valOpAddr3)
require.True(t, found)
_, err := sk.Delegate(ctx, TestAddrs[0], delTokens, sdk.Unbonded, val2, true)
require.NoError(t, err)
_, err = sk.Delegate(ctx, TestAddrs[0], delTokens, sdk.Unbonded, val3, true)
require.NoError(t, err)
_ = staking.EndBlocker(ctx, sk)
tp := TestProposal
proposal, err := keeper.SubmitProposal(ctx, tp)
require.NoError(t, err)
proposalID := proposal.ProposalID
proposal.Status = types.StatusVotingPeriod
keeper.SetProposal(ctx, proposal)
require.NoError(t, keeper.AddVote(ctx, proposalID, valAccAddr1, types.OptionYes))
require.NoError(t, keeper.AddVote(ctx, proposalID, valAccAddr2, types.OptionNo))
require.NoError(t, keeper.AddVote(ctx, proposalID, valAccAddr3, types.OptionNo))
proposal, ok := keeper.GetProposal(ctx, proposalID)
require.True(t, ok)
passes, burnDeposits, tallyResults := keeper.Tally(ctx, proposal)
require.False(t, passes)
require.False(t, burnDeposits)
require.False(t, tallyResults.Equals(types.EmptyTallyResult()))
}
func TestTallyJailedValidator(t *testing.T) {
ctx, _, keeper, sk, _ := createTestInput(t, false, 100)
createValidators(ctx, sk, []int64{25, 6, 7})
delTokens := sdk.TokensFromConsensusPower(10)
val2, found := sk.GetValidator(ctx, valOpAddr2)
require.True(t, found)
val3, found := sk.GetValidator(ctx, valOpAddr3)
require.True(t, found)
_, err := sk.Delegate(ctx, TestAddrs[0], delTokens, sdk.Unbonded, val2, true)
require.NoError(t, err)
_, err = sk.Delegate(ctx, TestAddrs[0], delTokens, sdk.Unbonded, val3, true)
require.NoError(t, err)
_ = staking.EndBlocker(ctx, sk)
sk.Jail(ctx, sdk.ConsAddress(val2.ConsPubKey.Address()))
tp := TestProposal
proposal, err := keeper.SubmitProposal(ctx, tp)
require.NoError(t, err)
proposalID := proposal.ProposalID
proposal.Status = types.StatusVotingPeriod
keeper.SetProposal(ctx, proposal)
require.NoError(t, keeper.AddVote(ctx, proposalID, valAccAddr1, types.OptionYes))
require.NoError(t, keeper.AddVote(ctx, proposalID, valAccAddr2, types.OptionNo))
require.NoError(t, keeper.AddVote(ctx, proposalID, valAccAddr3, types.OptionNo))
proposal, ok := keeper.GetProposal(ctx, proposalID)
require.True(t, ok)
passes, burnDeposits, tallyResults := keeper.Tally(ctx, proposal)
require.True(t, passes)
require.False(t, burnDeposits)
require.False(t, tallyResults.Equals(types.EmptyTallyResult()))
}

206
x/gov/keeper/test_common.go Normal file
View File

@ -0,0 +1,206 @@
// nolint
package keeper // noalias
// DONTCOVER
import (
"bytes"
"encoding/hex"
"testing"
"github.com/stretchr/testify/require"
"github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/crypto/ed25519"
abci "github.com/tendermint/tendermint/abci/types"
dbm "github.com/tendermint/tm-db"
"github.com/tendermint/tendermint/libs/log"
tmtypes "github.com/tendermint/tendermint/types"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/store"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/bank"
"github.com/cosmos/cosmos-sdk/x/gov/types"
"github.com/cosmos/cosmos-sdk/x/params"
"github.com/cosmos/cosmos-sdk/x/staking"
"github.com/cosmos/cosmos-sdk/x/supply"
)
// dummy addresses used for testing
var (
delPk1 = newPubKey("0B485CFC0EECC619440448436F8FC9DF40566F2369E72400281454CB552AFB51")
delPk2 = newPubKey("0B485CFC0EECC619440448436F8FC9DF40566F2369E72400281454CB552AFB50")
delPk3 = newPubKey("0B485CFC0EECC619440448436F8FC9DF40566F2369E72400281454CB552AFB52")
delAddr1 = sdk.AccAddress(delPk1.Address())
delAddr2 = sdk.AccAddress(delPk2.Address())
delAddr3 = sdk.AccAddress(delPk3.Address())
valOpPk1 = newPubKey("0B485CFC0EECC619440448436F8FC9DF40566F2369E72400281454CB552AFB53")
valOpPk2 = newPubKey("0B485CFC0EECC619440448436F8FC9DF40566F2369E72400281454CB552AFB54")
valOpPk3 = newPubKey("0B485CFC0EECC619440448436F8FC9DF40566F2369E72400281454CB552AFB55")
valOpAddr1 = sdk.ValAddress(valOpPk1.Address())
valOpAddr2 = sdk.ValAddress(valOpPk2.Address())
valOpAddr3 = sdk.ValAddress(valOpPk3.Address())
valAccAddr1 = sdk.AccAddress(valOpPk1.Address())
valAccAddr2 = sdk.AccAddress(valOpPk2.Address())
valAccAddr3 = sdk.AccAddress(valOpPk3.Address())
TestAddrs = []sdk.AccAddress{
delAddr1, delAddr2, delAddr3,
valAccAddr1, valAccAddr2, valAccAddr3,
}
emptyDelAddr sdk.AccAddress
emptyValAddr sdk.ValAddress
emptyPubkey crypto.PubKey
)
// TODO: remove dependency with staking
var (
TestProposal = types.NewTextProposal("Test", "description")
TestDescription = staking.NewDescription("T", "E", "S", "T")
TestCommissionRates = staking.NewCommissionRates(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec())
)
// TODO move to common testing framework
func newPubKey(pk string) (res crypto.PubKey) {
pkBytes, err := hex.DecodeString(pk)
if err != nil {
panic(err)
}
var pkEd ed25519.PubKeyEd25519
copy(pkEd[:], pkBytes[:])
return pkEd
}
func makeTestCodec() *codec.Codec {
var cdc = codec.New()
auth.RegisterCodec(cdc)
types.RegisterCodec(cdc)
supply.RegisterCodec(cdc)
staking.RegisterCodec(cdc)
sdk.RegisterCodec(cdc)
codec.RegisterCrypto(cdc)
return cdc
}
func createTestInput(t *testing.T, isCheckTx bool, initPower int64) (sdk.Context, auth.AccountKeeper, Keeper, staking.Keeper, types.SupplyKeeper) {
initTokens := sdk.TokensFromConsensusPower(initPower)
keyAcc := sdk.NewKVStoreKey(auth.StoreKey)
keyGov := sdk.NewKVStoreKey(types.StoreKey)
keyStaking := sdk.NewKVStoreKey(staking.StoreKey)
tkeyStaking := sdk.NewTransientStoreKey(staking.TStoreKey)
keySupply := sdk.NewKVStoreKey(supply.StoreKey)
keyParams := sdk.NewKVStoreKey(params.StoreKey)
tkeyParams := sdk.NewTransientStoreKey(params.TStoreKey)
db := dbm.NewMemDB()
ms := store.NewCommitMultiStore(db)
ms.MountStoreWithDB(keyAcc, sdk.StoreTypeIAVL, db)
ms.MountStoreWithDB(keySupply, sdk.StoreTypeIAVL, db)
ms.MountStoreWithDB(keyGov, sdk.StoreTypeIAVL, db)
ms.MountStoreWithDB(keyStaking, sdk.StoreTypeIAVL, db)
ms.MountStoreWithDB(tkeyStaking, sdk.StoreTypeTransient, db)
ms.MountStoreWithDB(keyParams, sdk.StoreTypeIAVL, db)
ms.MountStoreWithDB(tkeyParams, sdk.StoreTypeTransient, db)
require.Nil(t, ms.LoadLatestVersion())
ctx := sdk.NewContext(ms, abci.Header{ChainID: "gov-chain"}, isCheckTx, log.NewNopLogger())
ctx = ctx.WithConsensusParams(
&abci.ConsensusParams{
Validator: &abci.ValidatorParams{
PubKeyTypes: []string{tmtypes.ABCIPubKeyTypeEd25519},
},
},
)
cdc := makeTestCodec()
maccPerms := map[string][]string{
auth.FeeCollectorName: nil,
types.ModuleName: nil,
staking.NotBondedPoolName: {supply.Burner, supply.Staking},
staking.BondedPoolName: {supply.Burner, supply.Staking},
}
// create module accounts
feeCollectorAcc := supply.NewEmptyModuleAccount(auth.FeeCollectorName)
govAcc := supply.NewEmptyModuleAccount(types.ModuleName, supply.Burner)
notBondedPool := supply.NewEmptyModuleAccount(staking.NotBondedPoolName, supply.Burner, supply.Staking)
bondPool := supply.NewEmptyModuleAccount(staking.BondedPoolName, supply.Burner, supply.Staking)
blacklistedAddrs := make(map[string]bool)
blacklistedAddrs[feeCollectorAcc.GetAddress().String()] = true
blacklistedAddrs[govAcc.GetAddress().String()] = true
blacklistedAddrs[notBondedPool.GetAddress().String()] = true
blacklistedAddrs[bondPool.GetAddress().String()] = true
pk := params.NewKeeper(cdc, keyParams, tkeyParams, params.DefaultCodespace)
accountKeeper := auth.NewAccountKeeper(cdc, keyAcc, pk.Subspace(auth.DefaultParamspace), auth.ProtoBaseAccount)
bankKeeper := bank.NewBaseKeeper(accountKeeper, pk.Subspace(bank.DefaultParamspace), bank.DefaultCodespace, blacklistedAddrs)
supplyKeeper := supply.NewKeeper(cdc, keySupply, accountKeeper, bankKeeper, maccPerms)
sk := staking.NewKeeper(cdc, keyStaking, tkeyStaking, supplyKeeper, pk.Subspace(staking.DefaultParamspace), staking.DefaultCodespace)
sk.SetParams(ctx, staking.DefaultParams())
rtr := types.NewRouter().
AddRoute(types.RouterKey, types.ProposalHandler)
keeper := NewKeeper(cdc, keyGov, pk.Subspace(types.DefaultParamspace).WithKeyTable(types.ParamKeyTable()),
supplyKeeper, sk, types.DefaultCodespace, rtr)
keeper.SetProposalID(ctx, types.DefaultStartingProposalID)
keeper.SetDepositParams(ctx, types.DefaultDepositParams())
keeper.SetVotingParams(ctx, types.DefaultVotingParams())
keeper.SetTallyParams(ctx, types.DefaultTallyParams())
initCoins := sdk.NewCoins(sdk.NewCoin(sk.BondDenom(ctx), initTokens))
totalSupply := sdk.NewCoins(sdk.NewCoin(sk.BondDenom(ctx), initTokens.MulRaw(int64(len(TestAddrs)))))
supplyKeeper.SetSupply(ctx, supply.NewSupply(totalSupply))
for _, addr := range TestAddrs {
_, err := bankKeeper.AddCoins(ctx, addr, initCoins)
require.Nil(t, err)
}
keeper.supplyKeeper.SetModuleAccount(ctx, feeCollectorAcc)
keeper.supplyKeeper.SetModuleAccount(ctx, govAcc)
keeper.supplyKeeper.SetModuleAccount(ctx, bondPool)
keeper.supplyKeeper.SetModuleAccount(ctx, notBondedPool)
return ctx, accountKeeper, keeper, sk, supplyKeeper
}
// ProposalEqual checks if two proposals are equal (note: slow, for tests only)
func ProposalEqual(proposalA types.Proposal, proposalB types.Proposal) bool {
return bytes.Equal(types.ModuleCdc.MustMarshalBinaryBare(proposalA),
types.ModuleCdc.MustMarshalBinaryBare(proposalB))
}
func createValidators(ctx sdk.Context, sk staking.Keeper, powers []int64) {
val1 := staking.NewValidator(valOpAddr1, valOpPk1, staking.Description{})
val2 := staking.NewValidator(valOpAddr2, valOpPk2, staking.Description{})
val3 := staking.NewValidator(valOpAddr3, valOpPk3, staking.Description{})
sk.SetValidator(ctx, val1)
sk.SetValidator(ctx, val2)
sk.SetValidator(ctx, val3)
sk.SetValidatorByConsAddr(ctx, val1)
sk.SetValidatorByConsAddr(ctx, val2)
sk.SetValidatorByConsAddr(ctx, val3)
sk.SetNewValidatorByPowerIndex(ctx, val1)
sk.SetNewValidatorByPowerIndex(ctx, val2)
sk.SetNewValidatorByPowerIndex(ctx, val3)
_, _ = sk.Delegate(ctx, valAccAddr1, sdk.TokensFromConsensusPower(powers[0]), sdk.Unbonded, val1, true)
_, _ = sk.Delegate(ctx, valAccAddr2, sdk.TokensFromConsensusPower(powers[1]), sdk.Unbonded, val2, true)
_, _ = sk.Delegate(ctx, valAccAddr3, sdk.TokensFromConsensusPower(powers[2]), sdk.Unbonded, val3, true)
_ = staking.EndBlocker(ctx, sk)
}

111
x/gov/keeper/vote.go Normal file
View File

@ -0,0 +1,111 @@
package keeper
import (
"fmt"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/gov/types"
)
// AddVote adds a vote on a specific proposal
func (keeper Keeper) AddVote(ctx sdk.Context, proposalID uint64, voterAddr sdk.AccAddress, option types.VoteOption) sdk.Error {
proposal, ok := keeper.GetProposal(ctx, proposalID)
if !ok {
return types.ErrUnknownProposal(keeper.codespace, proposalID)
}
if proposal.Status != types.StatusVotingPeriod {
return types.ErrInactiveProposal(keeper.codespace, proposalID)
}
if !types.ValidVoteOption(option) {
return types.ErrInvalidVote(keeper.codespace, option)
}
vote := types.NewVote(proposalID, voterAddr, option)
keeper.SetVote(ctx, vote)
ctx.EventManager().EmitEvent(
sdk.NewEvent(
types.EventTypeProposalVote,
sdk.NewAttribute(types.AttributeKeyOption, option.String()),
sdk.NewAttribute(types.AttributeKeyProposalID, fmt.Sprintf("%d", proposalID)),
),
)
return nil
}
// GetAllVotes returns all the votes from the store
func (keeper Keeper) GetAllVotes(ctx sdk.Context) (votes types.Votes) {
keeper.IterateAllVotes(ctx, func(vote types.Vote) bool {
votes = append(votes, vote)
return false
})
return
}
// GetVotes returns all the votes from a proposal
func (keeper Keeper) GetVotes(ctx sdk.Context, proposalID uint64) (votes types.Votes) {
keeper.IterateVotes(ctx, proposalID, func(vote types.Vote) bool {
votes = append(votes, vote)
return false
})
return
}
// GetVote gets the vote from an address on a specific proposal
func (keeper Keeper) GetVote(ctx sdk.Context, proposalID uint64, voterAddr sdk.AccAddress) (vote types.Vote, found bool) {
store := ctx.KVStore(keeper.storeKey)
bz := store.Get(types.VoteKey(proposalID, voterAddr))
if bz == nil {
return vote, false
}
keeper.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &vote)
return vote, true
}
// SetVote sets a Vote to the gov store
func (keeper Keeper) SetVote(ctx sdk.Context, vote types.Vote) {
store := ctx.KVStore(keeper.storeKey)
bz := keeper.cdc.MustMarshalBinaryLengthPrefixed(vote)
store.Set(types.VoteKey(vote.ProposalID, vote.Voter), bz)
}
// IterateAllVotes iterates over the all the stored votes and performs a callback function
func (keeper Keeper) IterateAllVotes(ctx sdk.Context, cb func(vote types.Vote) (stop bool)) {
store := ctx.KVStore(keeper.storeKey)
iterator := sdk.KVStorePrefixIterator(store, types.VotesKeyPrefix)
defer iterator.Close()
for ; iterator.Valid(); iterator.Next() {
var vote types.Vote
keeper.cdc.MustUnmarshalBinaryLengthPrefixed(iterator.Value(), &vote)
if cb(vote) {
break
}
}
}
// IterateVotes iterates over the all the proposals votes and performs a callback function
func (keeper Keeper) IterateVotes(ctx sdk.Context, proposalID uint64, cb func(vote types.Vote) (stop bool)) {
store := ctx.KVStore(keeper.storeKey)
iterator := sdk.KVStorePrefixIterator(store, types.VotesKey(proposalID))
defer iterator.Close()
for ; iterator.Valid(); iterator.Next() {
var vote types.Vote
keeper.cdc.MustUnmarshalBinaryLengthPrefixed(iterator.Value(), &vote)
if cb(vote) {
break
}
}
}
// deleteVote deletes a vote from a given proposalID and voter from the store
func (keeper Keeper) deleteVote(ctx sdk.Context, proposalID uint64, voterAddr sdk.AccAddress) {
store := ctx.KVStore(keeper.storeKey)
store.Delete(types.VoteKey(proposalID, voterAddr))
}

65
x/gov/keeper/vote_test.go Normal file
View File

@ -0,0 +1,65 @@
package keeper
import (
"testing"
"github.com/stretchr/testify/require"
"github.com/cosmos/cosmos-sdk/x/gov/types"
)
func TestVotes(t *testing.T) {
ctx, _, keeper, _, _ := createTestInput(t, false, 100)
tp := TestProposal
proposal, err := keeper.SubmitProposal(ctx, tp)
require.NoError(t, err)
proposalID := proposal.ProposalID
var invalidOption types.VoteOption
invalidOption = 0x10
require.Error(t, keeper.AddVote(ctx, proposalID, TestAddrs[0], types.OptionYes), "proposal not on voting period")
require.Error(t, keeper.AddVote(ctx, 10, TestAddrs[0], types.OptionYes), "invalid proposal ID")
proposal.Status = types.StatusVotingPeriod
keeper.SetProposal(ctx, proposal)
require.Error(t, keeper.AddVote(ctx, proposalID, TestAddrs[0], invalidOption), "invalid option")
// Test first vote
require.NoError(t, keeper.AddVote(ctx, proposalID, TestAddrs[0], types.OptionAbstain))
vote, found := keeper.GetVote(ctx, proposalID, TestAddrs[0])
require.True(t, found)
require.Equal(t, TestAddrs[0], vote.Voter)
require.Equal(t, proposalID, vote.ProposalID)
require.Equal(t, types.OptionAbstain, vote.Option)
// Test change of vote
require.NoError(t, keeper.AddVote(ctx, proposalID, TestAddrs[0], types.OptionYes))
vote, found = keeper.GetVote(ctx, proposalID, TestAddrs[0])
require.True(t, found)
require.Equal(t, TestAddrs[0], vote.Voter)
require.Equal(t, proposalID, vote.ProposalID)
require.Equal(t, types.OptionYes, vote.Option)
// Test second vote
require.NoError(t, keeper.AddVote(ctx, proposalID, TestAddrs[1], types.OptionNoWithVeto))
vote, found = keeper.GetVote(ctx, proposalID, TestAddrs[1])
require.True(t, found)
require.Equal(t, TestAddrs[1], vote.Voter)
require.Equal(t, proposalID, vote.ProposalID)
require.Equal(t, types.OptionNoWithVeto, vote.Option)
// Test vote iterator
// NOTE order of deposits is determined by the addresses
votes := keeper.GetAllVotes(ctx)
require.Len(t, votes, 2)
require.Equal(t, votes, keeper.GetVotes(ctx, proposalID))
require.Equal(t, TestAddrs[0], votes[0].Voter)
require.Equal(t, proposalID, votes[0].ProposalID)
require.Equal(t, types.OptionYes, votes[0].Option)
require.Equal(t, TestAddrs[1], votes[1].Voter)
require.Equal(t, proposalID, votes[1].ProposalID)
require.Equal(t, types.OptionNoWithVeto, votes[1].Option)
}

View File

@ -1,356 +0,0 @@
package gov
import (
"strings"
"testing"
"time"
"github.com/stretchr/testify/require"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
)
func TestGetSetProposal(t *testing.T) {
input := getMockApp(t, 0, GenesisState{}, nil)
header := abci.Header{Height: input.mApp.LastBlockHeight() + 1}
input.mApp.BeginBlock(abci.RequestBeginBlock{Header: header})
ctx := input.mApp.BaseApp.NewContext(false, abci.Header{})
tp := testProposal()
proposal, err := input.keeper.SubmitProposal(ctx, tp)
require.NoError(t, err)
proposalID := proposal.ProposalID
input.keeper.SetProposal(ctx, proposal)
gotProposal, ok := input.keeper.GetProposal(ctx, proposalID)
require.True(t, ok)
require.True(t, ProposalEqual(proposal, gotProposal))
}
func TestIncrementProposalNumber(t *testing.T) {
input := getMockApp(t, 0, GenesisState{}, nil)
header := abci.Header{Height: input.mApp.LastBlockHeight() + 1}
input.mApp.BeginBlock(abci.RequestBeginBlock{Header: header})
ctx := input.mApp.BaseApp.NewContext(false, abci.Header{})
tp := testProposal()
input.keeper.SubmitProposal(ctx, tp)
input.keeper.SubmitProposal(ctx, tp)
input.keeper.SubmitProposal(ctx, tp)
input.keeper.SubmitProposal(ctx, tp)
input.keeper.SubmitProposal(ctx, tp)
proposal6, err := input.keeper.SubmitProposal(ctx, tp)
require.NoError(t, err)
require.Equal(t, uint64(6), proposal6.ProposalID)
}
func TestActivateVotingPeriod(t *testing.T) {
input := getMockApp(t, 0, GenesisState{}, nil)
header := abci.Header{Height: input.mApp.LastBlockHeight() + 1}
input.mApp.BeginBlock(abci.RequestBeginBlock{Header: header})
ctx := input.mApp.BaseApp.NewContext(false, abci.Header{})
tp := testProposal()
proposal, err := input.keeper.SubmitProposal(ctx, tp)
require.NoError(t, err)
require.True(t, proposal.VotingStartTime.Equal(time.Time{}))
input.keeper.activateVotingPeriod(ctx, proposal)
require.True(t, proposal.VotingStartTime.Equal(ctx.BlockHeader().Time))
proposal, ok := input.keeper.GetProposal(ctx, proposal.ProposalID)
require.True(t, ok)
activeIterator := input.keeper.ActiveProposalQueueIterator(ctx, proposal.VotingEndTime)
require.True(t, activeIterator.Valid())
var proposalID uint64
input.keeper.cdc.UnmarshalBinaryLengthPrefixed(activeIterator.Value(), &proposalID)
require.Equal(t, proposalID, proposal.ProposalID)
activeIterator.Close()
}
func TestDeposits(t *testing.T) {
input := getMockApp(t, 2, GenesisState{}, nil)
SortAddresses(input.addrs)
header := abci.Header{Height: input.mApp.LastBlockHeight() + 1}
input.mApp.BeginBlock(abci.RequestBeginBlock{Header: header})
ctx := input.mApp.BaseApp.NewContext(false, abci.Header{})
tp := testProposal()
proposal, err := input.keeper.SubmitProposal(ctx, tp)
require.NoError(t, err)
proposalID := proposal.ProposalID
fourStake := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.TokensFromConsensusPower(4)))
fiveStake := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.TokensFromConsensusPower(5)))
addr0Initial := input.mApp.AccountKeeper.GetAccount(ctx, input.addrs[0]).GetCoins()
addr1Initial := input.mApp.AccountKeeper.GetAccount(ctx, input.addrs[1]).GetCoins()
expTokens := sdk.TokensFromConsensusPower(42)
require.Equal(t, sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, expTokens)), addr0Initial)
require.True(t, proposal.TotalDeposit.IsEqual(sdk.NewCoins()))
// Check no deposits at beginning
deposit, found := input.keeper.GetDeposit(ctx, proposalID, input.addrs[1])
require.False(t, found)
proposal, ok := input.keeper.GetProposal(ctx, proposalID)
require.True(t, ok)
require.True(t, proposal.VotingStartTime.Equal(time.Time{}))
// Check first deposit
err, votingStarted := input.keeper.AddDeposit(ctx, proposalID, input.addrs[0], fourStake)
require.Nil(t, err)
require.False(t, votingStarted)
deposit, found = input.keeper.GetDeposit(ctx, proposalID, input.addrs[0])
require.True(t, found)
require.Equal(t, fourStake, deposit.Amount)
require.Equal(t, input.addrs[0], deposit.Depositor)
proposal, ok = input.keeper.GetProposal(ctx, proposalID)
require.True(t, ok)
require.Equal(t, fourStake, proposal.TotalDeposit)
require.Equal(t, addr0Initial.Sub(fourStake), input.mApp.AccountKeeper.GetAccount(ctx, input.addrs[0]).GetCoins())
// Check a second deposit from same address
err, votingStarted = input.keeper.AddDeposit(ctx, proposalID, input.addrs[0], fiveStake)
require.Nil(t, err)
require.False(t, votingStarted)
deposit, found = input.keeper.GetDeposit(ctx, proposalID, input.addrs[0])
require.True(t, found)
require.Equal(t, fourStake.Add(fiveStake), deposit.Amount)
require.Equal(t, input.addrs[0], deposit.Depositor)
proposal, ok = input.keeper.GetProposal(ctx, proposalID)
require.True(t, ok)
require.Equal(t, fourStake.Add(fiveStake), proposal.TotalDeposit)
require.Equal(t, addr0Initial.Sub(fourStake).Sub(fiveStake), input.mApp.AccountKeeper.GetAccount(ctx, input.addrs[0]).GetCoins())
// Check third deposit from a new address
err, votingStarted = input.keeper.AddDeposit(ctx, proposalID, input.addrs[1], fourStake)
require.Nil(t, err)
require.True(t, votingStarted)
deposit, found = input.keeper.GetDeposit(ctx, proposalID, input.addrs[1])
require.True(t, found)
require.Equal(t, input.addrs[1], deposit.Depositor)
require.Equal(t, fourStake, deposit.Amount)
proposal, ok = input.keeper.GetProposal(ctx, proposalID)
require.True(t, ok)
require.Equal(t, fourStake.Add(fiveStake).Add(fourStake), proposal.TotalDeposit)
require.Equal(t, addr1Initial.Sub(fourStake), input.mApp.AccountKeeper.GetAccount(ctx, input.addrs[1]).GetCoins())
// Check that proposal moved to voting period
proposal, ok = input.keeper.GetProposal(ctx, proposalID)
require.True(t, ok)
require.True(t, proposal.VotingStartTime.Equal(ctx.BlockHeader().Time))
// Test deposit iterator
depositsIterator := input.keeper.GetDepositsIterator(ctx, proposalID)
require.True(t, depositsIterator.Valid())
input.keeper.cdc.MustUnmarshalBinaryLengthPrefixed(depositsIterator.Value(), &deposit)
require.Equal(t, input.addrs[0], deposit.Depositor)
require.Equal(t, fourStake.Add(fiveStake), deposit.Amount)
depositsIterator.Next()
input.keeper.cdc.MustUnmarshalBinaryLengthPrefixed(depositsIterator.Value(), &deposit)
require.Equal(t, input.addrs[1], deposit.Depositor)
require.Equal(t, fourStake, deposit.Amount)
depositsIterator.Next()
require.False(t, depositsIterator.Valid())
depositsIterator.Close()
// Test Refund Deposits
deposit, found = input.keeper.GetDeposit(ctx, proposalID, input.addrs[1])
require.True(t, found)
require.Equal(t, fourStake, deposit.Amount)
input.keeper.RefundDeposits(ctx, proposalID)
deposit, found = input.keeper.GetDeposit(ctx, proposalID, input.addrs[1])
require.False(t, found)
require.Equal(t, addr0Initial, input.mApp.AccountKeeper.GetAccount(ctx, input.addrs[0]).GetCoins())
require.Equal(t, addr1Initial, input.mApp.AccountKeeper.GetAccount(ctx, input.addrs[1]).GetCoins())
}
func TestVotes(t *testing.T) {
input := getMockApp(t, 2, GenesisState{}, nil)
SortAddresses(input.addrs)
header := abci.Header{Height: input.mApp.LastBlockHeight() + 1}
input.mApp.BeginBlock(abci.RequestBeginBlock{Header: header})
ctx := input.mApp.BaseApp.NewContext(false, abci.Header{})
tp := testProposal()
proposal, err := input.keeper.SubmitProposal(ctx, tp)
require.NoError(t, err)
proposalID := proposal.ProposalID
proposal.Status = StatusVotingPeriod
input.keeper.SetProposal(ctx, proposal)
// Test first vote
input.keeper.AddVote(ctx, proposalID, input.addrs[0], OptionAbstain)
vote, found := input.keeper.GetVote(ctx, proposalID, input.addrs[0])
require.True(t, found)
require.Equal(t, input.addrs[0], vote.Voter)
require.Equal(t, proposalID, vote.ProposalID)
require.Equal(t, OptionAbstain, vote.Option)
// Test change of vote
input.keeper.AddVote(ctx, proposalID, input.addrs[0], OptionYes)
vote, found = input.keeper.GetVote(ctx, proposalID, input.addrs[0])
require.True(t, found)
require.Equal(t, input.addrs[0], vote.Voter)
require.Equal(t, proposalID, vote.ProposalID)
require.Equal(t, OptionYes, vote.Option)
// Test second vote
input.keeper.AddVote(ctx, proposalID, input.addrs[1], OptionNoWithVeto)
vote, found = input.keeper.GetVote(ctx, proposalID, input.addrs[1])
require.True(t, found)
require.Equal(t, input.addrs[1], vote.Voter)
require.Equal(t, proposalID, vote.ProposalID)
require.Equal(t, OptionNoWithVeto, vote.Option)
// Test vote iterator
votesIterator := input.keeper.GetVotesIterator(ctx, proposalID)
require.True(t, votesIterator.Valid())
input.keeper.cdc.MustUnmarshalBinaryLengthPrefixed(votesIterator.Value(), &vote)
require.True(t, votesIterator.Valid())
require.Equal(t, input.addrs[0], vote.Voter)
require.Equal(t, proposalID, vote.ProposalID)
require.Equal(t, OptionYes, vote.Option)
votesIterator.Next()
require.True(t, votesIterator.Valid())
input.keeper.cdc.MustUnmarshalBinaryLengthPrefixed(votesIterator.Value(), &vote)
require.True(t, votesIterator.Valid())
require.Equal(t, input.addrs[1], vote.Voter)
require.Equal(t, proposalID, vote.ProposalID)
require.Equal(t, OptionNoWithVeto, vote.Option)
votesIterator.Next()
require.False(t, votesIterator.Valid())
votesIterator.Close()
}
func TestProposalQueues(t *testing.T) {
input := getMockApp(t, 0, GenesisState{}, nil)
header := abci.Header{Height: input.mApp.LastBlockHeight() + 1}
input.mApp.BeginBlock(abci.RequestBeginBlock{Header: header})
ctx := input.mApp.BaseApp.NewContext(false, abci.Header{})
input.mApp.InitChainer(ctx, abci.RequestInitChain{})
// create test proposals
tp := testProposal()
proposal, err := input.keeper.SubmitProposal(ctx, tp)
require.NoError(t, err)
inactiveIterator := input.keeper.InactiveProposalQueueIterator(ctx, proposal.DepositEndTime)
require.True(t, inactiveIterator.Valid())
var proposalID uint64
input.keeper.cdc.UnmarshalBinaryLengthPrefixed(inactiveIterator.Value(), &proposalID)
require.Equal(t, proposalID, proposal.ProposalID)
inactiveIterator.Close()
input.keeper.activateVotingPeriod(ctx, proposal)
proposal, ok := input.keeper.GetProposal(ctx, proposal.ProposalID)
require.True(t, ok)
activeIterator := input.keeper.ActiveProposalQueueIterator(ctx, proposal.VotingEndTime)
require.True(t, activeIterator.Valid())
input.keeper.cdc.UnmarshalBinaryLengthPrefixed(activeIterator.Value(), &proposalID)
require.Equal(t, proposalID, proposal.ProposalID)
activeIterator.Close()
}
type validProposal struct{}
func (validProposal) GetTitle() string { return "title" }
func (validProposal) GetDescription() string { return "description" }
func (validProposal) ProposalRoute() string { return RouterKey }
func (validProposal) ProposalType() string { return ProposalTypeText }
func (validProposal) String() string { return "" }
func (validProposal) ValidateBasic() sdk.Error { return nil }
type invalidProposalTitle1 struct{ validProposal }
func (invalidProposalTitle1) GetTitle() string { return "" }
type invalidProposalTitle2 struct{ validProposal }
func (invalidProposalTitle2) GetTitle() string { return strings.Repeat("1234567890", 100) }
type invalidProposalDesc1 struct{ validProposal }
func (invalidProposalDesc1) GetDescription() string { return "" }
type invalidProposalDesc2 struct{ validProposal }
func (invalidProposalDesc2) GetDescription() string { return strings.Repeat("1234567890", 1000) }
type invalidProposalRoute struct{ validProposal }
func (invalidProposalRoute) ProposalRoute() string { return "nonexistingroute" }
type invalidProposalValidation struct{ validProposal }
func (invalidProposalValidation) ValidateBasic() sdk.Error {
return sdk.NewError(sdk.CodespaceUndefined, sdk.CodeInternal, "")
}
func registerTestCodec(cdc *codec.Codec) {
cdc.RegisterConcrete(validProposal{}, "test/validproposal", nil)
cdc.RegisterConcrete(invalidProposalTitle1{}, "test/invalidproposalt1", nil)
cdc.RegisterConcrete(invalidProposalTitle2{}, "test/invalidproposalt2", nil)
cdc.RegisterConcrete(invalidProposalDesc1{}, "test/invalidproposald1", nil)
cdc.RegisterConcrete(invalidProposalDesc2{}, "test/invalidproposald2", nil)
cdc.RegisterConcrete(invalidProposalRoute{}, "test/invalidproposalr", nil)
cdc.RegisterConcrete(invalidProposalValidation{}, "test/invalidproposalv", nil)
}
func TestSubmitProposal(t *testing.T) {
input := getMockApp(t, 0, GenesisState{}, nil)
registerTestCodec(input.keeper.cdc)
header := abci.Header{Height: input.mApp.LastBlockHeight() + 1}
input.mApp.BeginBlock(abci.RequestBeginBlock{Header: header})
ctx := input.mApp.BaseApp.NewContext(false, abci.Header{})
input.mApp.InitChainer(ctx, abci.RequestInitChain{})
testCases := []struct {
content Content
expectedErr sdk.Error
}{
{validProposal{}, nil},
// Keeper does not check the validity of title and description, no error
{invalidProposalTitle1{}, nil},
{invalidProposalTitle2{}, nil},
{invalidProposalDesc1{}, nil},
{invalidProposalDesc2{}, nil},
// error only when invalid route
{invalidProposalRoute{}, ErrNoProposalHandlerExists(DefaultCodespace, invalidProposalRoute{})},
// Keeper does not call ValidateBasic, msg.ValidateBasic does
{invalidProposalValidation{}, nil},
}
for _, tc := range testCases {
_, err := input.keeper.SubmitProposal(ctx, tc.content)
require.Equal(t, tc.expectedErr, err, "unexpected type of error: %s", err)
}
}

View File

@ -1,5 +1,7 @@
package gov
// DONTCOVER
import (
"encoding/json"
@ -23,7 +25,7 @@ var (
_ module.AppModuleBasic = AppModuleBasic{}
)
// app module basics object
// AppModuleBasic - app module basics object
type AppModuleBasic struct {
proposalHandlers []client.ProposalHandler // proposal handlers which live in governance cli and rest
}
@ -37,32 +39,32 @@ func NewAppModuleBasic(proposalHandlers ...client.ProposalHandler) AppModuleBasi
var _ module.AppModuleBasic = AppModuleBasic{}
// module name
// Name - module name
func (AppModuleBasic) Name() string {
return types.ModuleName
}
// register module codec
// RegisterCodec -register module codec
func (AppModuleBasic) RegisterCodec(cdc *codec.Codec) {
RegisterCodec(cdc)
}
// default genesis state
// DefaultGenesis - default genesis state
func (AppModuleBasic) DefaultGenesis() json.RawMessage {
return types.ModuleCdc.MustMarshalJSON(DefaultGenesisState())
return ModuleCdc.MustMarshalJSON(DefaultGenesisState())
}
// module validate genesis
// ValidateGenesis - module validate genesis
func (AppModuleBasic) ValidateGenesis(bz json.RawMessage) error {
var data GenesisState
err := types.ModuleCdc.UnmarshalJSON(bz, &data)
err := ModuleCdc.UnmarshalJSON(bz, &data)
if err != nil {
return err
}
return ValidateGenesis(data)
}
// register rest routes
// RegisterRESTRoutes - register rest routes
func (a AppModuleBasic) RegisterRESTRoutes(ctx context.CLIContext, rtr *mux.Router) {
var proposalRESTHandlers []rest.ProposalRESTHandler
for _, proposalHandler := range a.proposalHandlers {
@ -72,7 +74,7 @@ func (a AppModuleBasic) RegisterRESTRoutes(ctx context.CLIContext, rtr *mux.Rout
rest.RegisterRoutes(ctx, rtr, proposalRESTHandlers)
}
// get the root tx command of this module
// GetTxCmd gets the root tx command of this module
func (a AppModuleBasic) GetTxCmd(cdc *codec.Codec) *cobra.Command {
var proposalCLIHandlers []*cobra.Command
@ -83,21 +85,22 @@ func (a AppModuleBasic) GetTxCmd(cdc *codec.Codec) *cobra.Command {
return cli.GetTxCmd(StoreKey, cdc, proposalCLIHandlers)
}
// get the root query command of this module
// GetQueryCmd gets the root query command of this module
func (AppModuleBasic) GetQueryCmd(cdc *codec.Codec) *cobra.Command {
return cli.GetQueryCmd(StoreKey, cdc)
}
//___________________________
// app module
// AppModule - app module object
type AppModule struct {
AppModuleBasic
keeper Keeper
supplyKeeper SupplyKeeper
supplyKeeper types.SupplyKeeper
}
// NewAppModule creates a new AppModule object
func NewAppModule(keeper Keeper, supplyKeeper SupplyKeeper) AppModule {
func NewAppModule(keeper Keeper, supplyKeeper types.SupplyKeeper) AppModule {
return AppModule{
AppModuleBasic: AppModuleBasic{},
keeper: keeper,
@ -105,54 +108,54 @@ func NewAppModule(keeper Keeper, supplyKeeper SupplyKeeper) AppModule {
}
}
// module name
// Name - module name
func (AppModule) Name() string {
return types.ModuleName
return ModuleName
}
// register invariants
// RegisterInvariants registers module invariants
func (am AppModule) RegisterInvariants(ir sdk.InvariantRegistry) {
RegisterInvariants(ir, am.keeper)
}
// module message route name
// Route - module message route name
func (AppModule) Route() string {
return RouterKey
}
// module handler
// NewHandler - module handler
func (am AppModule) NewHandler() sdk.Handler {
return NewHandler(am.keeper)
}
// module querier route name
// QuerierRoute - module querier route name
func (AppModule) QuerierRoute() string {
return QuerierRoute
}
// module querier
// NewQuerierHandler - module querier
func (am AppModule) NewQuerierHandler() sdk.Querier {
return NewQuerier(am.keeper)
}
// module init-genesis
// InitGenesis - module init-genesis
func (am AppModule) InitGenesis(ctx sdk.Context, data json.RawMessage) []abci.ValidatorUpdate {
var genesisState GenesisState
types.ModuleCdc.MustUnmarshalJSON(data, &genesisState)
ModuleCdc.MustUnmarshalJSON(data, &genesisState)
InitGenesis(ctx, am.keeper, am.supplyKeeper, genesisState)
return []abci.ValidatorUpdate{}
}
// module export genesis
// ExportGenesis - module export genesis
func (am AppModule) ExportGenesis(ctx sdk.Context) json.RawMessage {
gs := ExportGenesis(ctx, am.keeper)
return types.ModuleCdc.MustMarshalJSON(gs)
return ModuleCdc.MustMarshalJSON(gs)
}
// module begin-block
// BeginBlock - module begin-block
func (AppModule) BeginBlock(_ sdk.Context, _ abci.RequestBeginBlock) {}
// module end-block
// EndBlock - module end-block
func (am AppModule) EndBlock(ctx sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate {
EndBlocker(ctx, am.keeper)
return []abci.ValidatorUpdate{}

View File

@ -1,302 +0,0 @@
package gov
import (
"strings"
"testing"
"github.com/stretchr/testify/require"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/gov/types"
)
const custom = "custom"
func getQueriedParams(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier) (DepositParams, VotingParams, TallyParams) {
query := abci.RequestQuery{
Path: strings.Join([]string{custom, QuerierRoute, QueryParams, ParamDeposit}, "/"),
Data: []byte{},
}
bz, err := querier(ctx, []string{QueryParams, ParamDeposit}, query)
require.Nil(t, err)
require.NotNil(t, bz)
var depositParams DepositParams
err2 := cdc.UnmarshalJSON(bz, &depositParams)
require.Nil(t, err2)
query = abci.RequestQuery{
Path: strings.Join([]string{custom, QuerierRoute, QueryParams, ParamVoting}, "/"),
Data: []byte{},
}
bz, err = querier(ctx, []string{QueryParams, ParamVoting}, query)
require.Nil(t, err)
require.NotNil(t, bz)
var votingParams VotingParams
err2 = cdc.UnmarshalJSON(bz, &votingParams)
require.Nil(t, err2)
query = abci.RequestQuery{
Path: strings.Join([]string{custom, QuerierRoute, QueryParams, ParamTallying}, "/"),
Data: []byte{},
}
bz, err = querier(ctx, []string{QueryParams, ParamTallying}, query)
require.Nil(t, err)
require.NotNil(t, bz)
var tallyParams TallyParams
err2 = cdc.UnmarshalJSON(bz, &tallyParams)
require.Nil(t, err2)
return depositParams, votingParams, tallyParams
}
func getQueriedProposal(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier, proposalID uint64) Proposal {
query := abci.RequestQuery{
Path: strings.Join([]string{custom, QuerierRoute, QueryProposal}, "/"),
Data: cdc.MustMarshalJSON(NewQueryProposalParams(proposalID)),
}
bz, err := querier(ctx, []string{QueryProposal}, query)
require.Nil(t, err)
require.NotNil(t, bz)
var proposal Proposal
err2 := cdc.UnmarshalJSON(bz, proposal)
require.Nil(t, err2)
return proposal
}
func getQueriedProposals(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier, depositor, voter sdk.AccAddress, status ProposalStatus, limit uint64) []Proposal {
query := abci.RequestQuery{
Path: strings.Join([]string{custom, QuerierRoute, QueryProposals}, "/"),
Data: cdc.MustMarshalJSON(NewQueryProposalsParams(status, limit, voter, depositor)),
}
bz, err := querier(ctx, []string{QueryProposals}, query)
require.Nil(t, err)
require.NotNil(t, bz)
var proposals Proposals
err2 := cdc.UnmarshalJSON(bz, &proposals)
require.Nil(t, err2)
return proposals
}
func getQueriedDeposit(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier, proposalID uint64, depositor sdk.AccAddress) Deposit {
query := abci.RequestQuery{
Path: strings.Join([]string{custom, QuerierRoute, QueryDeposit}, "/"),
Data: cdc.MustMarshalJSON(NewQueryDepositParams(proposalID, depositor)),
}
bz, err := querier(ctx, []string{QueryDeposit}, query)
require.Nil(t, err)
require.NotNil(t, bz)
var deposit Deposit
err2 := cdc.UnmarshalJSON(bz, &deposit)
require.Nil(t, err2)
return deposit
}
func getQueriedDeposits(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier, proposalID uint64) []Deposit {
query := abci.RequestQuery{
Path: strings.Join([]string{custom, QuerierRoute, QueryDeposits}, "/"),
Data: cdc.MustMarshalJSON(NewQueryProposalParams(proposalID)),
}
bz, err := querier(ctx, []string{QueryDeposits}, query)
require.Nil(t, err)
require.NotNil(t, bz)
var deposits []Deposit
err2 := cdc.UnmarshalJSON(bz, &deposits)
require.Nil(t, err2)
return deposits
}
func getQueriedVote(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier, proposalID uint64, voter sdk.AccAddress) Vote {
query := abci.RequestQuery{
Path: strings.Join([]string{custom, QuerierRoute, QueryVote}, "/"),
Data: cdc.MustMarshalJSON(NewQueryVoteParams(proposalID, voter)),
}
bz, err := querier(ctx, []string{QueryVote}, query)
require.Nil(t, err)
require.NotNil(t, bz)
var vote Vote
err2 := cdc.UnmarshalJSON(bz, &vote)
require.Nil(t, err2)
return vote
}
func getQueriedVotes(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier, proposalID uint64) []Vote {
query := abci.RequestQuery{
Path: strings.Join([]string{custom, QuerierRoute, QueryVote}, "/"),
Data: cdc.MustMarshalJSON(NewQueryProposalParams(proposalID)),
}
bz, err := querier(ctx, []string{QueryVotes}, query)
require.Nil(t, err)
require.NotNil(t, bz)
var votes []Vote
err2 := cdc.UnmarshalJSON(bz, &votes)
require.Nil(t, err2)
return votes
}
func getQueriedTally(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier, proposalID uint64) TallyResult {
query := abci.RequestQuery{
Path: strings.Join([]string{custom, QuerierRoute, QueryTally}, "/"),
Data: cdc.MustMarshalJSON(NewQueryProposalParams(proposalID)),
}
bz, err := querier(ctx, []string{QueryTally}, query)
require.Nil(t, err)
require.NotNil(t, bz)
var tally TallyResult
err2 := cdc.UnmarshalJSON(bz, &tally)
require.Nil(t, err2)
return tally
}
func TestQueryParams(t *testing.T) {
cdc := codec.New()
input := getMockApp(t, 1000, GenesisState{}, nil)
querier := NewQuerier(input.keeper)
header := abci.Header{Height: input.mApp.LastBlockHeight() + 1}
input.mApp.BeginBlock(abci.RequestBeginBlock{Header: header})
ctx := input.mApp.NewContext(false, abci.Header{})
getQueriedParams(t, ctx, cdc, querier)
}
func TestQueries(t *testing.T) {
cdc := codec.New()
input := getMockApp(t, 1000, GenesisState{}, nil)
querier := NewQuerier(input.keeper)
handler := NewHandler(input.keeper)
types.RegisterCodec(cdc)
header := abci.Header{Height: input.mApp.LastBlockHeight() + 1}
input.mApp.BeginBlock(abci.RequestBeginBlock{Header: header})
ctx := input.mApp.NewContext(false, abci.Header{})
depositParams, _, _ := getQueriedParams(t, ctx, cdc, querier)
// input.addrs[0] proposes (and deposits) proposals #1 and #2
res := handler(ctx, NewMsgSubmitProposal(testProposal(), sdk.Coins{sdk.NewInt64Coin(sdk.DefaultBondDenom, 1)}, input.addrs[0]))
var proposalID1 uint64
require.True(t, res.IsOK())
cdc.MustUnmarshalBinaryLengthPrefixed(res.Data, &proposalID1)
res = handler(ctx, NewMsgSubmitProposal(testProposal(), sdk.Coins{sdk.NewInt64Coin(sdk.DefaultBondDenom, 10000000)}, input.addrs[0]))
var proposalID2 uint64
require.True(t, res.IsOK())
cdc.MustUnmarshalBinaryLengthPrefixed(res.Data, &proposalID2)
// input.addrs[1] proposes (and deposits) proposals #3
res = handler(ctx, NewMsgSubmitProposal(testProposal(), sdk.Coins{sdk.NewInt64Coin(sdk.DefaultBondDenom, 1)}, input.addrs[1]))
var proposalID3 uint64
require.True(t, res.IsOK())
cdc.MustUnmarshalBinaryLengthPrefixed(res.Data, &proposalID3)
// input.addrs[1] deposits on proposals #2 & #3
res = handler(ctx, NewMsgDeposit(input.addrs[1], proposalID2, depositParams.MinDeposit))
res = handler(ctx, NewMsgDeposit(input.addrs[1], proposalID3, depositParams.MinDeposit))
// check deposits on proposal1 match individual deposits
deposits := getQueriedDeposits(t, ctx, cdc, querier, proposalID1)
require.Len(t, deposits, 1)
deposit := getQueriedDeposit(t, ctx, cdc, querier, proposalID1, input.addrs[0])
require.Equal(t, deposit, deposits[0])
// check deposits on proposal2 match individual deposits
deposits = getQueriedDeposits(t, ctx, cdc, querier, proposalID2)
require.Len(t, deposits, 2)
deposit = getQueriedDeposit(t, ctx, cdc, querier, proposalID2, input.addrs[0])
require.True(t, deposit.Equals(deposits[0]))
deposit = getQueriedDeposit(t, ctx, cdc, querier, proposalID2, input.addrs[1])
require.True(t, deposit.Equals(deposits[1]))
// check deposits on proposal3 match individual deposits
deposits = getQueriedDeposits(t, ctx, cdc, querier, proposalID3)
require.Len(t, deposits, 1)
deposit = getQueriedDeposit(t, ctx, cdc, querier, proposalID3, input.addrs[1])
require.Equal(t, deposit, deposits[0])
// Only proposal #1 should be in Deposit Period
proposals := getQueriedProposals(t, ctx, cdc, querier, nil, nil, StatusDepositPeriod, 0)
require.Len(t, proposals, 1)
require.Equal(t, proposalID1, proposals[0].ProposalID)
// Only proposals #2 and #3 should be in Voting Period
proposals = getQueriedProposals(t, ctx, cdc, querier, nil, nil, StatusVotingPeriod, 0)
require.Len(t, proposals, 2)
require.Equal(t, proposalID2, proposals[0].ProposalID)
require.Equal(t, proposalID3, proposals[1].ProposalID)
// Addrs[0] votes on proposals #2 & #3
require.True(t, handler(ctx, NewMsgVote(input.addrs[0], proposalID2, OptionYes)).IsOK())
require.True(t, handler(ctx, NewMsgVote(input.addrs[0], proposalID3, OptionYes)).IsOK())
// Addrs[1] votes on proposal #3
handler(ctx, NewMsgVote(input.addrs[1], proposalID3, OptionYes))
// Test query voted by input.addrs[0]
proposals = getQueriedProposals(t, ctx, cdc, querier, nil, input.addrs[0], StatusNil, 0)
require.Equal(t, proposalID2, (proposals[0]).ProposalID)
require.Equal(t, proposalID3, (proposals[1]).ProposalID)
// Test query votes on Proposal 2
votes := getQueriedVotes(t, ctx, cdc, querier, proposalID2)
require.Len(t, votes, 1)
require.Equal(t, input.addrs[0], votes[0].Voter)
vote := getQueriedVote(t, ctx, cdc, querier, proposalID2, input.addrs[0])
require.Equal(t, vote, votes[0])
// Test query votes on Proposal 3
votes = getQueriedVotes(t, ctx, cdc, querier, proposalID3)
require.Len(t, votes, 2)
require.True(t, input.addrs[0].String() == votes[0].Voter.String())
require.True(t, input.addrs[1].String() == votes[1].Voter.String())
// Test proposals queries with filters
// Test query all proposals
proposals = getQueriedProposals(t, ctx, cdc, querier, nil, nil, StatusNil, 0)
require.Equal(t, proposalID1, (proposals[0]).ProposalID)
require.Equal(t, proposalID2, (proposals[1]).ProposalID)
require.Equal(t, proposalID3, (proposals[2]).ProposalID)
// Test query voted by input.addrs[1]
proposals = getQueriedProposals(t, ctx, cdc, querier, nil, input.addrs[1], StatusNil, 0)
require.Equal(t, proposalID3, (proposals[0]).ProposalID)
// Test query deposited by input.addrs[0]
proposals = getQueriedProposals(t, ctx, cdc, querier, input.addrs[0], nil, StatusNil, 0)
require.Equal(t, proposalID1, (proposals[0]).ProposalID)
// Test query deposited by addr2
proposals = getQueriedProposals(t, ctx, cdc, querier, input.addrs[1], nil, StatusNil, 0)
require.Equal(t, proposalID2, (proposals[0]).ProposalID)
require.Equal(t, proposalID3, (proposals[1]).ProposalID)
// Test query voted AND deposited by addr1
proposals = getQueriedProposals(t, ctx, cdc, querier, input.addrs[0], input.addrs[0], StatusNil, 0)
require.Equal(t, proposalID2, (proposals[0]).ProposalID)
}

View File

@ -1,604 +0,0 @@
package gov
import (
"testing"
"github.com/stretchr/testify/require"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/crypto/ed25519"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/staking"
)
func TestTallyNoOneVotes(t *testing.T) {
input := getMockApp(t, 10, GenesisState{}, nil)
header := abci.Header{Height: input.mApp.LastBlockHeight() + 1}
input.mApp.BeginBlock(abci.RequestBeginBlock{Header: header})
ctx := input.mApp.BaseApp.NewContext(false, abci.Header{})
stakingHandler := staking.NewHandler(input.sk)
valAddrs := make([]sdk.ValAddress, len(input.addrs[:2]))
for i, addr := range input.addrs[:2] {
valAddrs[i] = sdk.ValAddress(addr)
}
createValidators(t, stakingHandler, ctx, valAddrs, []int64{5, 5})
staking.EndBlocker(ctx, input.sk)
tp := testProposal()
proposal, err := input.keeper.SubmitProposal(ctx, tp)
require.NoError(t, err)
proposalID := proposal.ProposalID
proposal.Status = StatusVotingPeriod
input.keeper.SetProposal(ctx, proposal)
proposal, ok := input.keeper.GetProposal(ctx, proposalID)
require.True(t, ok)
passes, burnDeposits, tallyResults := tally(ctx, input.keeper, proposal)
require.False(t, passes)
require.True(t, burnDeposits)
require.True(t, tallyResults.Equals(EmptyTallyResult()))
}
func TestTallyNoQuorum(t *testing.T) {
input := getMockApp(t, 10, GenesisState{}, nil)
header := abci.Header{Height: input.mApp.LastBlockHeight() + 1}
input.mApp.BeginBlock(abci.RequestBeginBlock{Header: header})
ctx := input.mApp.BaseApp.NewContext(false, abci.Header{})
stakingHandler := staking.NewHandler(input.sk)
valAddrs := make([]sdk.ValAddress, len(input.addrs[:2]))
for i, addr := range input.addrs[:2] {
valAddrs[i] = sdk.ValAddress(addr)
}
createValidators(t, stakingHandler, ctx, valAddrs, []int64{2, 5})
staking.EndBlocker(ctx, input.sk)
tp := testProposal()
proposal, err := input.keeper.SubmitProposal(ctx, tp)
require.NoError(t, err)
proposalID := proposal.ProposalID
proposal.Status = StatusVotingPeriod
input.keeper.SetProposal(ctx, proposal)
err = input.keeper.AddVote(ctx, proposalID, input.addrs[0], OptionYes)
require.Nil(t, err)
proposal, ok := input.keeper.GetProposal(ctx, proposalID)
require.True(t, ok)
passes, burnDeposits, _ := tally(ctx, input.keeper, proposal)
require.False(t, passes)
require.True(t, burnDeposits)
}
func TestTallyOnlyValidatorsAllYes(t *testing.T) {
input := getMockApp(t, 10, GenesisState{}, nil)
header := abci.Header{Height: input.mApp.LastBlockHeight() + 1}
input.mApp.BeginBlock(abci.RequestBeginBlock{Header: header})
ctx := input.mApp.BaseApp.NewContext(false, abci.Header{})
stakingHandler := staking.NewHandler(input.sk)
valAddrs := make([]sdk.ValAddress, len(input.addrs[:2]))
for i, addr := range input.addrs[:2] {
valAddrs[i] = sdk.ValAddress(addr)
}
createValidators(t, stakingHandler, ctx, valAddrs, []int64{5, 5})
staking.EndBlocker(ctx, input.sk)
tp := testProposal()
proposal, err := input.keeper.SubmitProposal(ctx, tp)
require.NoError(t, err)
proposalID := proposal.ProposalID
proposal.Status = StatusVotingPeriod
input.keeper.SetProposal(ctx, proposal)
err = input.keeper.AddVote(ctx, proposalID, input.addrs[0], OptionYes)
require.Nil(t, err)
err = input.keeper.AddVote(ctx, proposalID, input.addrs[1], OptionYes)
require.Nil(t, err)
proposal, ok := input.keeper.GetProposal(ctx, proposalID)
require.True(t, ok)
passes, burnDeposits, tallyResults := tally(ctx, input.keeper, proposal)
require.True(t, passes)
require.False(t, burnDeposits)
require.False(t, tallyResults.Equals(EmptyTallyResult()))
}
func TestTallyOnlyValidators51No(t *testing.T) {
input := getMockApp(t, 10, GenesisState{}, nil)
header := abci.Header{Height: input.mApp.LastBlockHeight() + 1}
input.mApp.BeginBlock(abci.RequestBeginBlock{Header: header})
ctx := input.mApp.BaseApp.NewContext(false, abci.Header{})
stakingHandler := staking.NewHandler(input.sk)
valAddrs := make([]sdk.ValAddress, len(input.addrs[:2]))
for i, addr := range input.addrs[:2] {
valAddrs[i] = sdk.ValAddress(addr)
}
createValidators(t, stakingHandler, ctx, valAddrs, []int64{5, 6})
staking.EndBlocker(ctx, input.sk)
tp := testProposal()
proposal, err := input.keeper.SubmitProposal(ctx, tp)
require.NoError(t, err)
proposalID := proposal.ProposalID
proposal.Status = StatusVotingPeriod
input.keeper.SetProposal(ctx, proposal)
err = input.keeper.AddVote(ctx, proposalID, input.addrs[0], OptionYes)
require.Nil(t, err)
err = input.keeper.AddVote(ctx, proposalID, input.addrs[1], OptionNo)
require.Nil(t, err)
proposal, ok := input.keeper.GetProposal(ctx, proposalID)
require.True(t, ok)
passes, burnDeposits, _ := tally(ctx, input.keeper, proposal)
require.False(t, passes)
require.False(t, burnDeposits)
}
func TestTallyOnlyValidators51Yes(t *testing.T) {
input := getMockApp(t, 10, GenesisState{}, nil)
header := abci.Header{Height: input.mApp.LastBlockHeight() + 1}
input.mApp.BeginBlock(abci.RequestBeginBlock{Header: header})
ctx := input.mApp.BaseApp.NewContext(false, abci.Header{})
stakingHandler := staking.NewHandler(input.sk)
valAddrs := make([]sdk.ValAddress, len(input.addrs[:3]))
for i, addr := range input.addrs[:3] {
valAddrs[i] = sdk.ValAddress(addr)
}
createValidators(t, stakingHandler, ctx, valAddrs, []int64{6, 6, 7})
staking.EndBlocker(ctx, input.sk)
tp := testProposal()
proposal, err := input.keeper.SubmitProposal(ctx, tp)
require.NoError(t, err)
proposalID := proposal.ProposalID
proposal.Status = StatusVotingPeriod
input.keeper.SetProposal(ctx, proposal)
err = input.keeper.AddVote(ctx, proposalID, input.addrs[0], OptionYes)
require.Nil(t, err)
err = input.keeper.AddVote(ctx, proposalID, input.addrs[1], OptionYes)
require.Nil(t, err)
err = input.keeper.AddVote(ctx, proposalID, input.addrs[2], OptionNo)
require.Nil(t, err)
proposal, ok := input.keeper.GetProposal(ctx, proposalID)
require.True(t, ok)
passes, burnDeposits, tallyResults := tally(ctx, input.keeper, proposal)
require.True(t, passes)
require.False(t, burnDeposits)
require.False(t, tallyResults.Equals(EmptyTallyResult()))
}
func TestTallyOnlyValidatorsVetoed(t *testing.T) {
input := getMockApp(t, 10, GenesisState{}, nil)
header := abci.Header{Height: input.mApp.LastBlockHeight() + 1}
input.mApp.BeginBlock(abci.RequestBeginBlock{Header: header})
ctx := input.mApp.BaseApp.NewContext(false, abci.Header{})
stakingHandler := staking.NewHandler(input.sk)
valAddrs := make([]sdk.ValAddress, len(input.addrs[:3]))
for i, addr := range input.addrs[:3] {
valAddrs[i] = sdk.ValAddress(addr)
}
createValidators(t, stakingHandler, ctx, valAddrs, []int64{6, 6, 7})
staking.EndBlocker(ctx, input.sk)
tp := testProposal()
proposal, err := input.keeper.SubmitProposal(ctx, tp)
require.NoError(t, err)
proposalID := proposal.ProposalID
proposal.Status = StatusVotingPeriod
input.keeper.SetProposal(ctx, proposal)
err = input.keeper.AddVote(ctx, proposalID, input.addrs[0], OptionYes)
require.Nil(t, err)
err = input.keeper.AddVote(ctx, proposalID, input.addrs[1], OptionYes)
require.Nil(t, err)
err = input.keeper.AddVote(ctx, proposalID, input.addrs[2], OptionNoWithVeto)
require.Nil(t, err)
proposal, ok := input.keeper.GetProposal(ctx, proposalID)
require.True(t, ok)
passes, burnDeposits, tallyResults := tally(ctx, input.keeper, proposal)
require.False(t, passes)
require.True(t, burnDeposits)
require.False(t, tallyResults.Equals(EmptyTallyResult()))
}
func TestTallyOnlyValidatorsAbstainPasses(t *testing.T) {
input := getMockApp(t, 10, GenesisState{}, nil)
header := abci.Header{Height: input.mApp.LastBlockHeight() + 1}
input.mApp.BeginBlock(abci.RequestBeginBlock{Header: header})
ctx := input.mApp.BaseApp.NewContext(false, abci.Header{})
stakingHandler := staking.NewHandler(input.sk)
valAddrs := make([]sdk.ValAddress, len(input.addrs[:3]))
for i, addr := range input.addrs[:3] {
valAddrs[i] = sdk.ValAddress(addr)
}
createValidators(t, stakingHandler, ctx, valAddrs, []int64{6, 6, 7})
staking.EndBlocker(ctx, input.sk)
tp := testProposal()
proposal, err := input.keeper.SubmitProposal(ctx, tp)
require.NoError(t, err)
proposalID := proposal.ProposalID
proposal.Status = StatusVotingPeriod
input.keeper.SetProposal(ctx, proposal)
err = input.keeper.AddVote(ctx, proposalID, input.addrs[0], OptionAbstain)
require.Nil(t, err)
err = input.keeper.AddVote(ctx, proposalID, input.addrs[1], OptionNo)
require.Nil(t, err)
err = input.keeper.AddVote(ctx, proposalID, input.addrs[2], OptionYes)
require.Nil(t, err)
proposal, ok := input.keeper.GetProposal(ctx, proposalID)
require.True(t, ok)
passes, burnDeposits, tallyResults := tally(ctx, input.keeper, proposal)
require.True(t, passes)
require.False(t, burnDeposits)
require.False(t, tallyResults.Equals(EmptyTallyResult()))
}
func TestTallyOnlyValidatorsAbstainFails(t *testing.T) {
input := getMockApp(t, 10, GenesisState{}, nil)
header := abci.Header{Height: input.mApp.LastBlockHeight() + 1}
input.mApp.BeginBlock(abci.RequestBeginBlock{Header: header})
ctx := input.mApp.BaseApp.NewContext(false, abci.Header{})
stakingHandler := staking.NewHandler(input.sk)
valAddrs := make([]sdk.ValAddress, len(input.addrs[:3]))
for i, addr := range input.addrs[:3] {
valAddrs[i] = sdk.ValAddress(addr)
}
createValidators(t, stakingHandler, ctx, valAddrs, []int64{6, 6, 7})
staking.EndBlocker(ctx, input.sk)
tp := testProposal()
proposal, err := input.keeper.SubmitProposal(ctx, tp)
require.NoError(t, err)
proposalID := proposal.ProposalID
proposal.Status = StatusVotingPeriod
input.keeper.SetProposal(ctx, proposal)
err = input.keeper.AddVote(ctx, proposalID, input.addrs[0], OptionAbstain)
require.Nil(t, err)
err = input.keeper.AddVote(ctx, proposalID, input.addrs[1], OptionYes)
require.Nil(t, err)
err = input.keeper.AddVote(ctx, proposalID, input.addrs[2], OptionNo)
require.Nil(t, err)
proposal, ok := input.keeper.GetProposal(ctx, proposalID)
require.True(t, ok)
passes, burnDeposits, tallyResults := tally(ctx, input.keeper, proposal)
require.False(t, passes)
require.False(t, burnDeposits)
require.False(t, tallyResults.Equals(EmptyTallyResult()))
}
func TestTallyOnlyValidatorsNonVoter(t *testing.T) {
input := getMockApp(t, 10, GenesisState{}, nil)
header := abci.Header{Height: input.mApp.LastBlockHeight() + 1}
input.mApp.BeginBlock(abci.RequestBeginBlock{Header: header})
ctx := input.mApp.BaseApp.NewContext(false, abci.Header{})
stakingHandler := staking.NewHandler(input.sk)
valAddrs := make([]sdk.ValAddress, len(input.addrs[:3]))
for i, addr := range input.addrs[:3] {
valAddrs[i] = sdk.ValAddress(addr)
}
createValidators(t, stakingHandler, ctx, valAddrs, []int64{6, 6, 7})
staking.EndBlocker(ctx, input.sk)
tp := testProposal()
proposal, err := input.keeper.SubmitProposal(ctx, tp)
require.NoError(t, err)
proposalID := proposal.ProposalID
proposal.Status = StatusVotingPeriod
input.keeper.SetProposal(ctx, proposal)
err = input.keeper.AddVote(ctx, proposalID, input.addrs[1], OptionYes)
require.Nil(t, err)
err = input.keeper.AddVote(ctx, proposalID, input.addrs[2], OptionNo)
require.Nil(t, err)
proposal, ok := input.keeper.GetProposal(ctx, proposalID)
require.True(t, ok)
passes, burnDeposits, tallyResults := tally(ctx, input.keeper, proposal)
require.False(t, passes)
require.False(t, burnDeposits)
require.False(t, tallyResults.Equals(EmptyTallyResult()))
}
func TestTallyDelgatorOverride(t *testing.T) {
input := getMockApp(t, 10, GenesisState{}, nil)
header := abci.Header{Height: input.mApp.LastBlockHeight() + 1}
input.mApp.BeginBlock(abci.RequestBeginBlock{Header: header})
ctx := input.mApp.BaseApp.NewContext(false, abci.Header{})
stakingHandler := staking.NewHandler(input.sk)
valAddrs := make([]sdk.ValAddress, len(input.addrs[:3]))
for i, addr := range input.addrs[:3] {
valAddrs[i] = sdk.ValAddress(addr)
}
createValidators(t, stakingHandler, ctx, valAddrs, []int64{5, 6, 7})
staking.EndBlocker(ctx, input.sk)
delTokens := sdk.TokensFromConsensusPower(30)
delegator1Msg := staking.NewMsgDelegate(input.addrs[3], sdk.ValAddress(input.addrs[2]), sdk.NewCoin(sdk.DefaultBondDenom, delTokens))
stakingHandler(ctx, delegator1Msg)
tp := testProposal()
proposal, err := input.keeper.SubmitProposal(ctx, tp)
require.NoError(t, err)
proposalID := proposal.ProposalID
proposal.Status = StatusVotingPeriod
input.keeper.SetProposal(ctx, proposal)
err = input.keeper.AddVote(ctx, proposalID, input.addrs[0], OptionYes)
require.Nil(t, err)
err = input.keeper.AddVote(ctx, proposalID, input.addrs[1], OptionYes)
require.Nil(t, err)
err = input.keeper.AddVote(ctx, proposalID, input.addrs[2], OptionYes)
require.Nil(t, err)
err = input.keeper.AddVote(ctx, proposalID, input.addrs[3], OptionNo)
require.Nil(t, err)
proposal, ok := input.keeper.GetProposal(ctx, proposalID)
require.True(t, ok)
passes, burnDeposits, tallyResults := tally(ctx, input.keeper, proposal)
require.False(t, passes)
require.False(t, burnDeposits)
require.False(t, tallyResults.Equals(EmptyTallyResult()))
}
func TestTallyDelgatorInherit(t *testing.T) {
input := getMockApp(t, 10, GenesisState{}, nil)
header := abci.Header{Height: input.mApp.LastBlockHeight() + 1}
input.mApp.BeginBlock(abci.RequestBeginBlock{Header: header})
ctx := input.mApp.BaseApp.NewContext(false, abci.Header{})
stakingHandler := staking.NewHandler(input.sk)
valAddrs := make([]sdk.ValAddress, len(input.addrs[:3]))
for i, addr := range input.addrs[:3] {
valAddrs[i] = sdk.ValAddress(addr)
}
createValidators(t, stakingHandler, ctx, valAddrs, []int64{5, 6, 7})
staking.EndBlocker(ctx, input.sk)
delTokens := sdk.TokensFromConsensusPower(30)
delegator1Msg := staking.NewMsgDelegate(input.addrs[3], sdk.ValAddress(input.addrs[2]), sdk.NewCoin(sdk.DefaultBondDenom, delTokens))
stakingHandler(ctx, delegator1Msg)
tp := testProposal()
proposal, err := input.keeper.SubmitProposal(ctx, tp)
require.NoError(t, err)
proposalID := proposal.ProposalID
proposal.Status = StatusVotingPeriod
input.keeper.SetProposal(ctx, proposal)
err = input.keeper.AddVote(ctx, proposalID, input.addrs[0], OptionNo)
require.Nil(t, err)
err = input.keeper.AddVote(ctx, proposalID, input.addrs[1], OptionNo)
require.Nil(t, err)
err = input.keeper.AddVote(ctx, proposalID, input.addrs[2], OptionYes)
require.Nil(t, err)
proposal, ok := input.keeper.GetProposal(ctx, proposalID)
require.True(t, ok)
passes, burnDeposits, tallyResults := tally(ctx, input.keeper, proposal)
require.True(t, passes)
require.False(t, burnDeposits)
require.False(t, tallyResults.Equals(EmptyTallyResult()))
}
func TestTallyDelgatorMultipleOverride(t *testing.T) {
input := getMockApp(t, 10, GenesisState{}, nil)
header := abci.Header{Height: input.mApp.LastBlockHeight() + 1}
input.mApp.BeginBlock(abci.RequestBeginBlock{Header: header})
ctx := input.mApp.BaseApp.NewContext(false, abci.Header{})
stakingHandler := staking.NewHandler(input.sk)
valAddrs := make([]sdk.ValAddress, len(input.addrs[:3]))
for i, addr := range input.addrs[:3] {
valAddrs[i] = sdk.ValAddress(addr)
}
createValidators(t, stakingHandler, ctx, valAddrs, []int64{5, 6, 7})
staking.EndBlocker(ctx, input.sk)
delTokens := sdk.TokensFromConsensusPower(10)
delegator1Msg := staking.NewMsgDelegate(input.addrs[3], sdk.ValAddress(input.addrs[2]), sdk.NewCoin(sdk.DefaultBondDenom, delTokens))
stakingHandler(ctx, delegator1Msg)
delegator1Msg2 := staking.NewMsgDelegate(input.addrs[3], sdk.ValAddress(input.addrs[1]), sdk.NewCoin(sdk.DefaultBondDenom, delTokens))
stakingHandler(ctx, delegator1Msg2)
tp := testProposal()
proposal, err := input.keeper.SubmitProposal(ctx, tp)
require.NoError(t, err)
proposalID := proposal.ProposalID
proposal.Status = StatusVotingPeriod
input.keeper.SetProposal(ctx, proposal)
err = input.keeper.AddVote(ctx, proposalID, input.addrs[0], OptionYes)
require.Nil(t, err)
err = input.keeper.AddVote(ctx, proposalID, input.addrs[1], OptionYes)
require.Nil(t, err)
err = input.keeper.AddVote(ctx, proposalID, input.addrs[2], OptionYes)
require.Nil(t, err)
err = input.keeper.AddVote(ctx, proposalID, input.addrs[3], OptionNo)
require.Nil(t, err)
proposal, ok := input.keeper.GetProposal(ctx, proposalID)
require.True(t, ok)
passes, burnDeposits, tallyResults := tally(ctx, input.keeper, proposal)
require.False(t, passes)
require.False(t, burnDeposits)
require.False(t, tallyResults.Equals(EmptyTallyResult()))
}
func TestTallyDelgatorMultipleInherit(t *testing.T) {
input := getMockApp(t, 10, GenesisState{}, nil)
header := abci.Header{Height: input.mApp.LastBlockHeight() + 1}
input.mApp.BeginBlock(abci.RequestBeginBlock{Header: header})
ctx := input.mApp.BaseApp.NewContext(false, abci.Header{})
stakingHandler := staking.NewHandler(input.sk)
valTokens1 := sdk.TokensFromConsensusPower(25)
val1CreateMsg := staking.NewMsgCreateValidator(
sdk.ValAddress(input.addrs[0]), ed25519.GenPrivKey().PubKey(), sdk.NewCoin(sdk.DefaultBondDenom, valTokens1), testDescription, testCommissionRates, sdk.OneInt(),
)
stakingHandler(ctx, val1CreateMsg)
valTokens2 := sdk.TokensFromConsensusPower(6)
val2CreateMsg := staking.NewMsgCreateValidator(
sdk.ValAddress(input.addrs[1]), ed25519.GenPrivKey().PubKey(), sdk.NewCoin(sdk.DefaultBondDenom, valTokens2), testDescription, testCommissionRates, sdk.OneInt(),
)
stakingHandler(ctx, val2CreateMsg)
valTokens3 := sdk.TokensFromConsensusPower(7)
val3CreateMsg := staking.NewMsgCreateValidator(
sdk.ValAddress(input.addrs[2]), ed25519.GenPrivKey().PubKey(), sdk.NewCoin(sdk.DefaultBondDenom, valTokens3), testDescription, testCommissionRates, sdk.OneInt(),
)
stakingHandler(ctx, val3CreateMsg)
delTokens := sdk.TokensFromConsensusPower(10)
delegator1Msg := staking.NewMsgDelegate(input.addrs[3], sdk.ValAddress(input.addrs[2]), sdk.NewCoin(sdk.DefaultBondDenom, delTokens))
stakingHandler(ctx, delegator1Msg)
delegator1Msg2 := staking.NewMsgDelegate(input.addrs[3], sdk.ValAddress(input.addrs[1]), sdk.NewCoin(sdk.DefaultBondDenom, delTokens))
stakingHandler(ctx, delegator1Msg2)
staking.EndBlocker(ctx, input.sk)
tp := testProposal()
proposal, err := input.keeper.SubmitProposal(ctx, tp)
require.NoError(t, err)
proposalID := proposal.ProposalID
proposal.Status = StatusVotingPeriod
input.keeper.SetProposal(ctx, proposal)
err = input.keeper.AddVote(ctx, proposalID, input.addrs[0], OptionYes)
require.Nil(t, err)
err = input.keeper.AddVote(ctx, proposalID, input.addrs[1], OptionNo)
require.Nil(t, err)
err = input.keeper.AddVote(ctx, proposalID, input.addrs[2], OptionNo)
require.Nil(t, err)
proposal, ok := input.keeper.GetProposal(ctx, proposalID)
require.True(t, ok)
passes, burnDeposits, tallyResults := tally(ctx, input.keeper, proposal)
require.False(t, passes)
require.False(t, burnDeposits)
require.False(t, tallyResults.Equals(EmptyTallyResult()))
}
func TestTallyJailedValidator(t *testing.T) {
input := getMockApp(t, 10, GenesisState{}, nil)
header := abci.Header{Height: input.mApp.LastBlockHeight() + 1}
input.mApp.BeginBlock(abci.RequestBeginBlock{Header: header})
ctx := input.mApp.BaseApp.NewContext(false, abci.Header{})
stakingHandler := staking.NewHandler(input.sk)
valAddrs := make([]sdk.ValAddress, len(input.addrs[:3]))
for i, addr := range input.addrs[:3] {
valAddrs[i] = sdk.ValAddress(addr)
}
createValidators(t, stakingHandler, ctx, valAddrs, []int64{25, 6, 7})
staking.EndBlocker(ctx, input.sk)
delTokens := sdk.TokensFromConsensusPower(10)
delegator1Msg := staking.NewMsgDelegate(input.addrs[3], sdk.ValAddress(input.addrs[2]), sdk.NewCoin(sdk.DefaultBondDenom, delTokens))
stakingHandler(ctx, delegator1Msg)
delegator1Msg2 := staking.NewMsgDelegate(input.addrs[3], sdk.ValAddress(input.addrs[1]), sdk.NewCoin(sdk.DefaultBondDenom, delTokens))
stakingHandler(ctx, delegator1Msg2)
val2, found := input.sk.GetValidator(ctx, sdk.ValAddress(input.addrs[1]))
require.True(t, found)
input.sk.Jail(ctx, sdk.ConsAddress(val2.ConsPubKey.Address()))
staking.EndBlocker(ctx, input.sk)
tp := testProposal()
proposal, err := input.keeper.SubmitProposal(ctx, tp)
require.NoError(t, err)
proposalID := proposal.ProposalID
proposal.Status = StatusVotingPeriod
input.keeper.SetProposal(ctx, proposal)
err = input.keeper.AddVote(ctx, proposalID, input.addrs[0], OptionYes)
require.Nil(t, err)
err = input.keeper.AddVote(ctx, proposalID, input.addrs[1], OptionNo)
require.Nil(t, err)
err = input.keeper.AddVote(ctx, proposalID, input.addrs[2], OptionNo)
require.Nil(t, err)
proposal, ok := input.keeper.GetProposal(ctx, proposalID)
require.True(t, ok)
passes, burnDeposits, tallyResults := tally(ctx, input.keeper, proposal)
require.True(t, passes)
require.False(t, burnDeposits)
require.False(t, tallyResults.Equals(EmptyTallyResult()))
}

View File

@ -1,4 +1,5 @@
// nolint:deadcode unused
// nolint
// DONTCOVER
package gov
import (
@ -17,6 +18,7 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/bank"
keep "github.com/cosmos/cosmos-sdk/x/gov/keeper"
"github.com/cosmos/cosmos-sdk/x/gov/types"
"github.com/cosmos/cosmos-sdk/x/mock"
"github.com/cosmos/cosmos-sdk/x/staking"
@ -33,15 +35,16 @@ var (
type testInput struct {
mApp *mock.App
keeper Keeper
router Router
keeper keep.Keeper
router types.Router
sk staking.Keeper
addrs []sdk.AccAddress
pubKeys []crypto.PubKey
privKeys []crypto.PrivKey
}
func getMockApp(t *testing.T, numGenAccs int, genState GenesisState, genAccs []auth.Account) testInput {
func getMockApp(t *testing.T, numGenAccs int, genState types.GenesisState, genAccs []auth.Account,
handler func(ctx sdk.Context, c types.Content) sdk.Error) testInput {
mApp := mock.NewApp()
staking.RegisterCodec(mApp.Cdc)
@ -50,7 +53,7 @@ func getMockApp(t *testing.T, numGenAccs int, genState GenesisState, genAccs []a
keyStaking := sdk.NewKVStoreKey(staking.StoreKey)
tKeyStaking := sdk.NewTransientStoreKey(staking.TStoreKey)
keyGov := sdk.NewKVStoreKey(StoreKey)
keyGov := sdk.NewKVStoreKey(types.StoreKey)
keySupply := sdk.NewKVStoreKey(supply.StoreKey)
govAcc := supply.NewEmptyModuleAccount(types.ModuleName, supply.Burner)
@ -58,14 +61,14 @@ func getMockApp(t *testing.T, numGenAccs int, genState GenesisState, genAccs []a
bondPool := supply.NewEmptyModuleAccount(staking.BondedPoolName, supply.Burner, supply.Staking)
blacklistedAddrs := make(map[string]bool)
blacklistedAddrs[govAcc.String()] = true
blacklistedAddrs[notBondedPool.String()] = true
blacklistedAddrs[bondPool.String()] = true
blacklistedAddrs[govAcc.GetAddress().String()] = true
blacklistedAddrs[notBondedPool.GetAddress().String()] = true
blacklistedAddrs[bondPool.GetAddress().String()] = true
pk := mApp.ParamsKeeper
rtr := NewRouter().
AddRoute(RouterKey, ProposalHandler)
rtr := types.NewRouter().
AddRoute(types.RouterKey, handler)
bk := bank.NewBaseKeeper(mApp.AccountKeeper, mApp.ParamsKeeper.Subspace(bank.DefaultParamspace), bank.DefaultCodespace, blacklistedAddrs)
@ -77,10 +80,11 @@ func getMockApp(t *testing.T, numGenAccs int, genState GenesisState, genAccs []a
supplyKeeper := supply.NewKeeper(mApp.Cdc, keySupply, mApp.AccountKeeper, bk, maccPerms)
sk := staking.NewKeeper(mApp.Cdc, keyStaking, tKeyStaking, supplyKeeper, pk.Subspace(staking.DefaultParamspace), staking.DefaultCodespace)
keeper := NewKeeper(mApp.Cdc, keyGov, pk, pk.Subspace(DefaultParamspace), supplyKeeper, sk, DefaultCodespace, rtr)
keeper := keep.NewKeeper(mApp.Cdc, keyGov, pk.Subspace(DefaultParamspace).WithKeyTable(ParamKeyTable()),
supplyKeeper, sk, types.DefaultCodespace, rtr)
mApp.Router().AddRoute(RouterKey, NewHandler(keeper))
mApp.QueryRouter().AddRoute(QuerierRoute, NewQuerier(keeper))
mApp.Router().AddRoute(types.RouterKey, NewHandler(keeper))
mApp.QueryRouter().AddRoute(types.QuerierRoute, keep.NewQuerier(keeper))
mApp.SetEndBlocker(getEndBlocker(keeper))
mApp.SetInitChainer(getInitChainer(mApp, keeper, sk, supplyKeeper, genAccs, genState,
@ -129,7 +133,7 @@ func getInitChainer(mapp *mock.App, keeper Keeper, stakingKeeper staking.Keeper,
validators := staking.InitGenesis(ctx, stakingKeeper, mapp.AccountKeeper, supplyKeeper, stakingGenesis)
if genState.IsEmpty() {
InitGenesis(ctx, keeper, supplyKeeper, DefaultGenesisState())
InitGenesis(ctx, keeper, supplyKeeper, types.DefaultGenesisState())
} else {
InitGenesis(ctx, keeper, supplyKeeper, genState)
}
@ -139,7 +143,7 @@ func getInitChainer(mapp *mock.App, keeper Keeper, stakingKeeper staking.Keeper,
}
}
// Sorts Addresses
// SortAddresses - Sorts Addresses
func SortAddresses(addrs []sdk.AccAddress) {
var byteAddrs [][]byte
for _, addr := range addrs {
@ -175,25 +179,21 @@ func (b sortByteArrays) Swap(i, j int) {
b[j], b[i] = b[i], b[j]
}
// Public
// SortByteArrays - sorts the provided byte array
func SortByteArrays(src [][]byte) [][]byte {
sorted := sortByteArrays(src)
sort.Sort(sorted)
return sorted
}
func testProposal() Content {
return NewTextProposal("Test", "description")
}
const contextKeyBadProposal = "contextKeyBadProposal"
// badProposalHandler implements a governance proposal handler that is identical
// to the actual handler except this fails if the context doesn't contain a value
// for the key contextKeyBadProposal or if the value is false.
func badProposalHandler(ctx sdk.Context, c Content) sdk.Error {
func badProposalHandler(ctx sdk.Context, c types.Content) sdk.Error {
switch c.ProposalType() {
case ProposalTypeText, ProposalTypeSoftwareUpgrade:
case types.ProposalTypeText, types.ProposalTypeSoftwareUpgrade:
v := ctx.Value(contextKeyBadProposal)
if v == nil || !v.(bool) {
@ -203,26 +203,17 @@ func badProposalHandler(ctx sdk.Context, c Content) sdk.Error {
return nil
default:
errMsg := fmt.Sprintf("unrecognized gov proposal type: %s", c.ProposalType())
return sdk.ErrUnknownRequest(errMsg)
msg := fmt.Sprintf("unrecognized gov proposal type: %s", c.ProposalType())
return sdk.ErrUnknownRequest(msg)
}
}
// checks if two proposals are equal (note: slow, for tests only)
func ProposalEqual(proposalA Proposal, proposalB Proposal) bool {
return bytes.Equal(types.ModuleCdc.MustMarshalBinaryBare(proposalA),
types.ModuleCdc.MustMarshalBinaryBare(proposalB))
}
var (
pubkeys = []crypto.PubKey{
ed25519.GenPrivKey().PubKey(),
ed25519.GenPrivKey().PubKey(),
ed25519.GenPrivKey().PubKey(),
}
testDescription = staking.NewDescription("T", "E", "S", "T")
testCommissionRates = staking.NewCommissionRates(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec())
)
func createValidators(t *testing.T, stakingHandler sdk.Handler, ctx sdk.Context, addrs []sdk.ValAddress, powerAmt []int64) {
@ -233,7 +224,7 @@ func createValidators(t *testing.T, stakingHandler sdk.Handler, ctx sdk.Context,
valTokens := sdk.TokensFromConsensusPower(powerAmt[i])
valCreateMsg := staking.NewMsgCreateValidator(
addrs[i], pubkeys[i], sdk.NewCoin(sdk.DefaultBondDenom, valTokens),
testDescription, testCommissionRates, sdk.OneInt(),
keep.TestDescription, keep.TestCommissionRates, sdk.OneInt(),
)
res := stakingHandler(ctx, valCreateMsg)

View File

@ -6,7 +6,7 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
)
// Deposit
// Deposit defines an amount deposited by an account address to an active proposal
type Deposit struct {
ProposalID uint64 `json:"proposal_id" yaml:"proposal_id"` // proposalID of the proposal
Depositor sdk.AccAddress `json:"depositor" yaml:"depositor"` // Address of the depositor

View File

@ -1,14 +1,16 @@
//nolint
package types
// DONTCOVER
import (
"fmt"
sdk "github.com/cosmos/cosmos-sdk/types"
)
// Codes for governance errors
const (
DefaultCodespace sdk.CodespaceType = "gov"
DefaultCodespace sdk.CodespaceType = ModuleName
CodeUnknownProposal sdk.CodeType = 1
CodeInactiveProposal sdk.CodeType = 2
@ -23,42 +25,42 @@ const (
CodeProposalHandlerNotExists sdk.CodeType = 11
)
// ErrUnknownProposal error for unknown proposals
func ErrUnknownProposal(codespace sdk.CodespaceType, proposalID uint64) sdk.Error {
return sdk.NewError(codespace, CodeUnknownProposal, fmt.Sprintf("unknown proposal with id %d", proposalID))
}
// ErrInactiveProposal error for inactive (i.e finalized) proposals
func ErrInactiveProposal(codespace sdk.CodespaceType, proposalID uint64) sdk.Error {
return sdk.NewError(codespace, CodeInactiveProposal, fmt.Sprintf("inactive proposal with id %d", proposalID))
}
// ErrAlreadyActiveProposal error for proposals that are already active
func ErrAlreadyActiveProposal(codespace sdk.CodespaceType, proposalID uint64) sdk.Error {
return sdk.NewError(codespace, CodeAlreadyActiveProposal, fmt.Sprintf("proposal %d has been already active", proposalID))
}
func ErrAlreadyFinishedProposal(codespace sdk.CodespaceType, proposalID uint64) sdk.Error {
return sdk.NewError(codespace, CodeAlreadyFinishedProposal, fmt.Sprintf("proposal %d has already passed its voting period", proposalID))
}
func ErrAddressNotStaked(codespace sdk.CodespaceType, address sdk.AccAddress) sdk.Error {
return sdk.NewError(codespace, CodeAddressNotStaked, fmt.Sprintf("address %s is not staked and is thus ineligible to vote", address))
}
// ErrInvalidProposalContent error for invalid proposal title or description
func ErrInvalidProposalContent(cs sdk.CodespaceType, msg string) sdk.Error {
return sdk.NewError(cs, CodeInvalidContent, fmt.Sprintf("invalid proposal content: %s", msg))
}
// ErrInvalidProposalType error for non registered proposal types
func ErrInvalidProposalType(codespace sdk.CodespaceType, proposalType string) sdk.Error {
return sdk.NewError(codespace, CodeInvalidProposalType, fmt.Sprintf("proposal type '%s' is not valid", proposalType))
}
// ErrInvalidVote error for an invalid vote option
func ErrInvalidVote(codespace sdk.CodespaceType, voteOption VoteOption) sdk.Error {
return sdk.NewError(codespace, CodeInvalidVote, fmt.Sprintf("'%v' is not a valid voting option", voteOption.String()))
}
// ErrInvalidGenesis error for an invalid governance GenesisState
func ErrInvalidGenesis(codespace sdk.CodespaceType, msg string) sdk.Error {
return sdk.NewError(codespace, CodeInvalidVote, msg)
}
// ErrNoProposalHandlerExists error when proposal handler is not defined
func ErrNoProposalHandlerExists(codespace sdk.CodespaceType, content interface{}) sdk.Error {
return sdk.NewError(codespace, CodeProposalHandlerNotExists, fmt.Sprintf("'%T' does not have a corresponding handler", content))
}

View File

@ -1,4 +1,4 @@
package gov
package types
import (
sdk "github.com/cosmos/cosmos-sdk/types"
@ -6,7 +6,13 @@ import (
supplyexported "github.com/cosmos/cosmos-sdk/x/supply/exported"
)
// SupplyKeeper defines the supply Keeper for module accounts
// ParamSubspace defines the expected Subspace interface for parameters (noalias)
type ParamSubspace interface {
Get(ctx sdk.Context, key []byte, ptr interface{})
Set(ctx sdk.Context, key []byte, param interface{})
}
// SupplyKeeper defines the expected supply keeper for module accounts (noalias)
type SupplyKeeper interface {
GetModuleAddress(name string) sdk.AccAddress
GetModuleAccount(ctx sdk.Context, name string) supplyexported.ModuleAccountI
@ -19,7 +25,7 @@ type SupplyKeeper interface {
BurnCoins(ctx sdk.Context, name string, amt sdk.Coins) sdk.Error
}
// StakingKeeper expected staking keeper (Validator and Delegator sets)
// StakingKeeper expected staking keeper (Validator and Delegator sets) (noalias)
type StakingKeeper interface {
// iterate through bonded validators by operator address, execute func for each validator
IterateBondedValidatorsByPower(sdk.Context,

73
x/gov/types/genesis.go Normal file
View File

@ -0,0 +1,73 @@
package types
import (
"bytes"
"fmt"
sdk "github.com/cosmos/cosmos-sdk/types"
)
// GenesisState - all staking state that must be provided at genesis
type GenesisState struct {
StartingProposalID uint64 `json:"starting_proposal_id" yaml:"starting_proposal_id"`
Deposits Deposits `json:"deposits" yaml:"deposits"`
Votes Votes `json:"votes" yaml:"votes"`
Proposals Proposals `json:"proposals" yaml:"proposals"`
DepositParams DepositParams `json:"deposit_params" yaml:"deposit_params"`
VotingParams VotingParams `json:"voting_params" yaml:"voting_params"`
TallyParams TallyParams `json:"tally_params" yaml:"tally_params"`
}
// NewGenesisState creates a new genesis state for the governance module
func NewGenesisState(startingProposalID uint64, dp DepositParams, vp VotingParams, tp TallyParams) GenesisState {
return GenesisState{
StartingProposalID: startingProposalID,
DepositParams: dp,
VotingParams: vp,
TallyParams: tp,
}
}
// DefaultGenesisState defines the default governance genesis state
func DefaultGenesisState() GenesisState {
return NewGenesisState(
DefaultStartingProposalID,
DefaultDepositParams(),
DefaultVotingParams(),
DefaultTallyParams(),
)
}
// Equal checks whether two gov GenesisState structs are equivalent
func (data GenesisState) Equal(data2 GenesisState) bool {
b1 := ModuleCdc.MustMarshalBinaryBare(data)
b2 := ModuleCdc.MustMarshalBinaryBare(data2)
return bytes.Equal(b1, b2)
}
// IsEmpty returns true if a GenesisState is empty
func (data GenesisState) IsEmpty() bool {
return data.Equal(GenesisState{})
}
// ValidateGenesis checks if parameters are within valid ranges
func ValidateGenesis(data GenesisState) error {
threshold := data.TallyParams.Threshold
if threshold.IsNegative() || threshold.GT(sdk.OneDec()) {
return fmt.Errorf("Governance vote threshold should be positive and less or equal to one, is %s",
threshold.String())
}
veto := data.TallyParams.Veto
if veto.IsNegative() || veto.GT(sdk.OneDec()) {
return fmt.Errorf("Governance vote veto threshold should be positive and less or equal to one, is %s",
veto.String())
}
if !data.DepositParams.MinDeposit.IsValid() {
return fmt.Errorf("Governance deposit amount must be a valid sdk.Coins amount, is %s",
data.DepositParams.MinDeposit.String())
}
return nil
}

View File

@ -0,0 +1,22 @@
package types
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestEqualProposalID(t *testing.T) {
state1 := GenesisState{}
state2 := GenesisState{}
require.Equal(t, state1, state2)
// Proposals
state1.StartingProposalID = 1
require.NotEqual(t, state1, state2)
require.False(t, state1.Equal(state2))
state2.StartingProposalID = 1
require.Equal(t, state1, state2)
require.True(t, state1.Equal(state2))
}

View File

@ -52,11 +52,21 @@ var (
var lenTime = len(sdk.FormatTimeBytes(time.Now()))
// GetProposalIDBytes returns the byte representation of the proposalID
func GetProposalIDBytes(proposalID uint64) (proposalIDBz []byte) {
proposalIDBz = make([]byte, 8)
binary.BigEndian.PutUint64(proposalIDBz, proposalID)
return
}
// GetProposalIDFromBytes returns proposalID in uint64 format from a byte array
func GetProposalIDFromBytes(bz []byte) (proposalID uint64) {
return binary.BigEndian.Uint64(bz)
}
// ProposalKey gets a specific proposal from the store
func ProposalKey(proposalID uint64) []byte {
bz := make([]byte, 8)
binary.LittleEndian.PutUint64(bz, proposalID)
return append(ProposalsKeyPrefix, bz...)
return append(ProposalsKeyPrefix, GetProposalIDBytes(proposalID)...)
}
// ActiveProposalByTimeKey gets the active proposal queue key by endTime
@ -66,10 +76,7 @@ func ActiveProposalByTimeKey(endTime time.Time) []byte {
// ActiveProposalQueueKey returns the key for a proposalID in the activeProposalQueue
func ActiveProposalQueueKey(proposalID uint64, endTime time.Time) []byte {
bz := make([]byte, 8)
binary.LittleEndian.PutUint64(bz, proposalID)
return append(ActiveProposalByTimeKey(endTime), bz...)
return append(ActiveProposalByTimeKey(endTime), GetProposalIDBytes(proposalID)...)
}
// InactiveProposalByTimeKey gets the inactive proposal queue key by endTime
@ -79,17 +86,12 @@ func InactiveProposalByTimeKey(endTime time.Time) []byte {
// InactiveProposalQueueKey returns the key for a proposalID in the inactiveProposalQueue
func InactiveProposalQueueKey(proposalID uint64, endTime time.Time) []byte {
bz := make([]byte, 8)
binary.LittleEndian.PutUint64(bz, proposalID)
return append(InactiveProposalByTimeKey(endTime), bz...)
return append(InactiveProposalByTimeKey(endTime), GetProposalIDBytes(proposalID)...)
}
// DepositsKey gets the first part of the deposits key based on the proposalID
func DepositsKey(proposalID uint64) []byte {
bz := make([]byte, 8)
binary.LittleEndian.PutUint64(bz, proposalID)
return append(DepositsKeyPrefix, bz...)
return append(DepositsKeyPrefix, GetProposalIDBytes(proposalID)...)
}
// DepositKey key of a specific deposit from the store
@ -99,9 +101,7 @@ func DepositKey(proposalID uint64, depositorAddr sdk.AccAddress) []byte {
// VotesKey gets the first part of the votes key based on the proposalID
func VotesKey(proposalID uint64) []byte {
bz := make([]byte, 8)
binary.LittleEndian.PutUint64(bz, proposalID)
return append(VotesKeyPrefix, bz...)
return append(VotesKeyPrefix, GetProposalIDBytes(proposalID)...)
}
// VoteKey key of a specific vote from the store
@ -117,7 +117,7 @@ func SplitProposalKey(key []byte) (proposalID uint64) {
panic(fmt.Sprintf("unexpected key length (%d ≠ 8)", len(key[1:])))
}
return binary.LittleEndian.Uint64(key[1:])
return GetProposalIDFromBytes(key[1:])
}
// SplitActiveProposalQueueKey split the active proposal key and returns the proposal id and endTime
@ -151,7 +151,8 @@ func splitKeyWithTime(key []byte) (proposalID uint64, endTime time.Time) {
if err != nil {
panic(err)
}
proposalID = binary.LittleEndian.Uint64(key[1+lenTime:])
proposalID = GetProposalIDFromBytes(key[1+lenTime:])
return
}
@ -160,7 +161,7 @@ func splitKeyWithAddress(key []byte) (proposalID uint64, addr sdk.AccAddress) {
panic(fmt.Sprintf("unexpected key length (%d ≠ %d)", len(key), 8+sdk.AddrLen))
}
proposalID = binary.LittleEndian.Uint64(key[1:9])
proposalID = GetProposalIDFromBytes(key[1:9])
addr = sdk.AccAddress(key[9:])
return
}

View File

@ -15,22 +15,26 @@ const (
var _, _, _ sdk.Msg = MsgSubmitProposal{}, MsgDeposit{}, MsgVote{}
// MsgSubmitProposal
// MsgSubmitProposal defines a message to create a governance proposal with a
// given content and initial deposit
type MsgSubmitProposal struct {
Content Content `json:"content" yaml:"content"`
InitialDeposit sdk.Coins `json:"initial_deposit" yaml:"initial_deposit"` // Initial deposit paid by sender. Must be strictly positive
Proposer sdk.AccAddress `json:"proposer" yaml:"proposer"` // Address of the proposer
}
// NewMsgSubmitProposal creates a new MsgSubmitProposal instance
func NewMsgSubmitProposal(content Content, initialDeposit sdk.Coins, proposer sdk.AccAddress) MsgSubmitProposal {
return MsgSubmitProposal{content, initialDeposit, proposer}
}
//nolint
// Route implements Msg
func (msg MsgSubmitProposal) Route() string { return RouterKey }
func (msg MsgSubmitProposal) Type() string { return TypeMsgSubmitProposal }
// Implements Msg.
// Type implements Msg
func (msg MsgSubmitProposal) Type() string { return TypeMsgSubmitProposal }
// ValidateBasic implements Msg
func (msg MsgSubmitProposal) ValidateBasic() sdk.Error {
if msg.Content == nil {
return ErrInvalidProposalContent(DefaultCodespace, "missing content")
@ -57,6 +61,7 @@ func (msg MsgSubmitProposal) ValidateBasic() sdk.Error {
return msg.Content.ValidateBasic()
}
// String implements the Stringer interface
func (msg MsgSubmitProposal) String() string {
return fmt.Sprintf(`Submit Proposal Message:
Content: %s
@ -64,34 +69,36 @@ func (msg MsgSubmitProposal) String() string {
`, msg.Content.String(), msg.InitialDeposit)
}
// Implements Msg.
// GetSignBytes implements Msg
func (msg MsgSubmitProposal) GetSignBytes() []byte {
bz := ModuleCdc.MustMarshalJSON(msg)
return sdk.MustSortJSON(bz)
}
// Implements Msg.
// GetSigners implements Msg
func (msg MsgSubmitProposal) GetSigners() []sdk.AccAddress {
return []sdk.AccAddress{msg.Proposer}
}
// MsgDeposit
// MsgDeposit defines a message to submit a deposit to an existing proposal
type MsgDeposit struct {
ProposalID uint64 `json:"proposal_id" yaml:"proposal_id"` // ID of the proposal
Depositor sdk.AccAddress `json:"depositor" yaml:"depositor"` // Address of the depositor
Amount sdk.Coins `json:"amount" yaml:"amount"` // Coins to add to the proposal's deposit
}
// NewMsgDeposit creates a new MsgDeposit instance
func NewMsgDeposit(depositor sdk.AccAddress, proposalID uint64, amount sdk.Coins) MsgDeposit {
return MsgDeposit{proposalID, depositor, amount}
}
// Implements Msg.
// nolint
// Route implements Msg
func (msg MsgDeposit) Route() string { return RouterKey }
func (msg MsgDeposit) Type() string { return TypeMsgDeposit }
// Implements Msg.
// Type implements Msg
func (msg MsgDeposit) Type() string { return TypeMsgDeposit }
// ValidateBasic implements Msg
func (msg MsgDeposit) ValidateBasic() sdk.Error {
if msg.Depositor.Empty() {
return sdk.ErrInvalidAddress(msg.Depositor.String())
@ -106,6 +113,7 @@ func (msg MsgDeposit) ValidateBasic() sdk.Error {
return nil
}
// String implements the Stringer interface
func (msg MsgDeposit) String() string {
return fmt.Sprintf(`Deposit Message:
Depositer: %s
@ -114,34 +122,36 @@ func (msg MsgDeposit) String() string {
`, msg.Depositor, msg.ProposalID, msg.Amount)
}
// Implements Msg.
// GetSignBytes implements Msg
func (msg MsgDeposit) GetSignBytes() []byte {
bz := ModuleCdc.MustMarshalJSON(msg)
return sdk.MustSortJSON(bz)
}
// Implements Msg.
// GetSigners implements Msg
func (msg MsgDeposit) GetSigners() []sdk.AccAddress {
return []sdk.AccAddress{msg.Depositor}
}
// MsgVote
// MsgVote defines a message to cast a vote
type MsgVote struct {
ProposalID uint64 `json:"proposal_id" yaml:"proposal_id"` // ID of the proposal
Voter sdk.AccAddress `json:"voter" yaml:"voter"` // address of the voter
Option VoteOption `json:"option" yaml:"option"` // option from OptionSet chosen by the voter
}
// NewMsgVote creates a message to cast a vote on an active proposal
func NewMsgVote(voter sdk.AccAddress, proposalID uint64, option VoteOption) MsgVote {
return MsgVote{proposalID, voter, option}
}
// Implements Msg.
// nolint
// Route implements Msg
func (msg MsgVote) Route() string { return RouterKey }
func (msg MsgVote) Type() string { return TypeMsgVote }
// Implements Msg.
// Type implements Msg
func (msg MsgVote) Type() string { return TypeMsgVote }
// ValidateBasic implements Msg
func (msg MsgVote) ValidateBasic() sdk.Error {
if msg.Voter.Empty() {
return sdk.ErrInvalidAddress(msg.Voter.String())
@ -153,6 +163,7 @@ func (msg MsgVote) ValidateBasic() sdk.Error {
return nil
}
// String implements the Stringer interface
func (msg MsgVote) String() string {
return fmt.Sprintf(`Vote Message:
Proposal ID: %d
@ -160,13 +171,13 @@ func (msg MsgVote) String() string {
`, msg.ProposalID, msg.Option)
}
// Implements Msg.
// GetSignBytes implements Msg
func (msg MsgVote) GetSignBytes() []byte {
bz := ModuleCdc.MustMarshalJSON(msg)
return sdk.MustSortJSON(bz)
}
// Implements Msg.
// GetSigners implements Msg
func (msg MsgVote) GetSigners() []sdk.AccAddress {
return []sdk.AccAddress{msg.Voter}
}

View File

@ -8,6 +8,19 @@ import (
params "github.com/cosmos/cosmos-sdk/x/params/subspace"
)
// Default period for deposits & voting
const (
DefaultPeriod time.Duration = time.Hour * 24 * 2 // 2 days
)
// Default governance params
var (
DefaultMinDepositTokens = sdk.TokensFromConsensusPower(10)
DefaultQuorum = sdk.NewDecWithPrec(334, 3)
DefaultThreshold = sdk.NewDecWithPrec(5, 1)
DefaultVeto = sdk.NewDecWithPrec(334, 3)
)
// Parameter store key
var (
ParamStoreKeyDepositParams = []byte("depositparams")
@ -15,7 +28,7 @@ var (
ParamStoreKeyTallyParams = []byte("tallyparams")
)
// Key declaration for parameters
// ParamKeyTable - Key declaration for parameters
func ParamKeyTable() params.KeyTable {
return params.NewKeyTable(
ParamStoreKeyDepositParams, DepositParams{},
@ -24,7 +37,7 @@ func ParamKeyTable() params.KeyTable {
)
}
// Param around deposits for governance
// DepositParams defines the params around deposits for governance
type DepositParams struct {
MinDeposit sdk.Coins `json:"min_deposit,omitempty" yaml:"min_deposit,omitempty"` // Minimum deposit for a proposal to enter voting period.
MaxDepositPeriod time.Duration `json:"max_deposit_period,omitempty" yaml:"max_deposit_period,omitempty"` // Maximum period for Atom holders to deposit on a proposal. Initial value: 2 months
@ -38,18 +51,27 @@ func NewDepositParams(minDeposit sdk.Coins, maxDepositPeriod time.Duration) Depo
}
}
// DefaultDepositParams default parameters for deposits
func DefaultDepositParams() DepositParams {
return NewDepositParams(
sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, DefaultMinDepositTokens)),
DefaultPeriod,
)
}
// String implements stringer insterface
func (dp DepositParams) String() string {
return fmt.Sprintf(`Deposit Params:
Min Deposit: %s
Max Deposit Period: %s`, dp.MinDeposit, dp.MaxDepositPeriod)
}
// Checks equality of DepositParams
// Equal checks equality of DepositParams
func (dp DepositParams) Equal(dp2 DepositParams) bool {
return dp.MinDeposit.IsEqual(dp2.MinDeposit) && dp.MaxDepositPeriod == dp2.MaxDepositPeriod
}
// Param around Tallying votes in governance
// TallyParams defines the params around Tallying votes in governance
type TallyParams struct {
Quorum sdk.Dec `json:"quorum,omitempty" yaml:"quorum,omitempty"` // Minimum percentage of total stake needed to vote for a result to be considered valid
Threshold sdk.Dec `json:"threshold,omitempty" yaml:"threshold,omitempty"` // Minimum proportion of Yes votes for proposal to pass. Initial value: 0.5
@ -65,6 +87,12 @@ func NewTallyParams(quorum, threshold, veto sdk.Dec) TallyParams {
}
}
// DefaultTallyParams default parameters for tallying
func DefaultTallyParams() TallyParams {
return NewTallyParams(DefaultQuorum, DefaultThreshold, DefaultVeto)
}
// String implements stringer insterface
func (tp TallyParams) String() string {
return fmt.Sprintf(`Tally Params:
Quorum: %s
@ -73,7 +101,7 @@ func (tp TallyParams) String() string {
tp.Quorum, tp.Threshold, tp.Veto)
}
// Param around Voting in governance
// VotingParams defines the params around Voting in governance
type VotingParams struct {
VotingPeriod time.Duration `json:"voting_period,omitempty" yaml:"voting_period,omitempty"` // Length of the voting period.
}
@ -85,6 +113,12 @@ func NewVotingParams(votingPeriod time.Duration) VotingParams {
}
}
// DefaultVotingParams default parameters for voting
func DefaultVotingParams() VotingParams {
return NewVotingParams(DefaultPeriod)
}
// String implements stringer interface
func (vp VotingParams) String() string {
return fmt.Sprintf(`Voting Params:
Voting Period: %s`, vp.VotingPeriod)
@ -102,6 +136,7 @@ func (gp Params) String() string {
gp.TallyParams.String() + "\n" + gp.DepositParams.String()
}
// NewParams creates a new gov Params instance
func NewParams(vp VotingParams, tp TallyParams, dp DepositParams) Params {
return Params{
VotingParams: vp,
@ -109,3 +144,8 @@ func NewParams(vp VotingParams, tp TallyParams, dp DepositParams) Params {
TallyParams: tp,
}
}
// DefaultParams default governance params
func DefaultParams() Params {
return NewParams(DefaultVotingParams(), DefaultTallyParams(), DefaultDepositParams())
}

View File

@ -9,6 +9,9 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
)
// DefaultStartingProposalID is 1
const DefaultStartingProposalID uint64 = 1
// Proposal defines a struct used by the governance module to allow for voting
// on network changes.
type Proposal struct {
@ -26,6 +29,7 @@ type Proposal struct {
VotingEndTime time.Time `json:"voting_end_time" yaml:"voting_end_time"` // Time that the VotingPeriod for this proposal will end and votes will be tallied
}
// NewProposal creates a new Proposal instance
func NewProposal(content Content, id uint64, submitTime, depositEndTime time.Time) Proposal {
return Proposal{
Content: content,
@ -38,7 +42,7 @@ func NewProposal(content Content, id uint64, submitTime, depositEndTime time.Tim
}
}
// nolint
// String implements stringer interface
func (p Proposal) String() string {
return fmt.Sprintf(`Proposal %d:
Title: %s
@ -59,7 +63,7 @@ func (p Proposal) String() string {
// Proposals is an array of proposal
type Proposals []Proposal
// nolint
// String implements stringer interface
func (p Proposals) String() string {
out := "ID - (Status) [Type] Title\n"
for _, prop := range p {
@ -71,14 +75,14 @@ func (p Proposals) String() string {
}
type (
// ProposalQueue
// ProposalQueue defines a queue for proposal ids
ProposalQueue []uint64
// ProposalStatus is a type alias that represents a proposal status as a byte
ProposalStatus byte
)
//nolint
// Valid Proposal statuses
const (
StatusNil ProposalStatus = 0x00
StatusDepositPeriod ProposalStatus = 0x01
@ -88,7 +92,7 @@ const (
StatusFailed ProposalStatus = 0x05
)
// ProposalStatusToString turns a string into a ProposalStatus
// ProposalStatusFromString turns a string into a ProposalStatus
func ProposalStatusFromString(str string) (ProposalStatus, error) {
switch str {
case "DepositPeriod":
@ -138,12 +142,12 @@ func (status *ProposalStatus) Unmarshal(data []byte) error {
return nil
}
// Marshals to JSON using string
// MarshalJSON Marshals to JSON using string representation of the status
func (status ProposalStatus) MarshalJSON() ([]byte, error) {
return json.Marshal(status.String())
}
// Unmarshals from JSON assuming Bech32 encoding
// UnmarshalJSON Unmarshals from JSON assuming Bech32 encoding
func (status *ProposalStatus) UnmarshalJSON(data []byte) error {
var s string
err := json.Unmarshal(data, &s)
@ -195,84 +199,43 @@ func (status ProposalStatus) Format(s fmt.State, verb rune) {
}
}
// Tally Results
type TallyResult struct {
Yes sdk.Int `json:"yes" yaml:"yes"`
Abstain sdk.Int `json:"abstain" yaml:"abstain"`
No sdk.Int `json:"no" yaml:"no"`
NoWithVeto sdk.Int `json:"no_with_veto" yaml:"no_with_veto"`
}
func NewTallyResult(yes, abstain, no, noWithVeto sdk.Int) TallyResult {
return TallyResult{
Yes: yes,
Abstain: abstain,
No: no,
NoWithVeto: noWithVeto,
}
}
func NewTallyResultFromMap(results map[VoteOption]sdk.Dec) TallyResult {
return TallyResult{
Yes: results[OptionYes].TruncateInt(),
Abstain: results[OptionAbstain].TruncateInt(),
No: results[OptionNo].TruncateInt(),
NoWithVeto: results[OptionNoWithVeto].TruncateInt(),
}
}
// EmptyTallyResult returns an empty TallyResult.
func EmptyTallyResult() TallyResult {
return TallyResult{
Yes: sdk.ZeroInt(),
Abstain: sdk.ZeroInt(),
No: sdk.ZeroInt(),
NoWithVeto: sdk.ZeroInt(),
}
}
// Equals returns if two proposals are equal.
func (tr TallyResult) Equals(comp TallyResult) bool {
return tr.Yes.Equal(comp.Yes) &&
tr.Abstain.Equal(comp.Abstain) &&
tr.No.Equal(comp.No) &&
tr.NoWithVeto.Equal(comp.NoWithVeto)
}
func (tr TallyResult) String() string {
return fmt.Sprintf(`Tally Result:
Yes: %s
Abstain: %s
No: %s
NoWithVeto: %s`, tr.Yes, tr.Abstain, tr.No, tr.NoWithVeto)
}
// Proposal types
const (
ProposalTypeText string = "Text"
ProposalTypeSoftwareUpgrade string = "SoftwareUpgrade"
)
// Text Proposal
// TextProposal defines a standard text proposal whose changes need to be
// manually updated in case of approval
type TextProposal struct {
Title string `json:"title" yaml:"title"`
Description string `json:"description" yaml:"description"`
}
// NewTextProposal creates a text proposal Content
func NewTextProposal(title, description string) Content {
return TextProposal{title, description}
}
// Implements Proposal Interface
// Implements Content Interface
var _ Content = TextProposal{}
// nolint
func (tp TextProposal) GetTitle() string { return tp.Title }
func (tp TextProposal) GetDescription() string { return tp.Description }
func (tp TextProposal) ProposalRoute() string { return RouterKey }
func (tp TextProposal) ProposalType() string { return ProposalTypeText }
// GetTitle returns the proposal title
func (tp TextProposal) GetTitle() string { return tp.Title }
// GetDescription returns the proposal description
func (tp TextProposal) GetDescription() string { return tp.Description }
// ProposalRoute returns the proposal router key
func (tp TextProposal) ProposalRoute() string { return RouterKey }
// ProposalType is "Text"
func (tp TextProposal) ProposalType() string { return ProposalTypeText }
// ValidateBasic validates the content's title and description of the proposal
func (tp TextProposal) ValidateBasic() sdk.Error { return ValidateAbstract(DefaultCodespace, tp) }
// String implements Stringer interface
func (tp TextProposal) String() string {
return fmt.Sprintf(`Text Proposal:
Title: %s
@ -280,7 +243,9 @@ func (tp TextProposal) String() string {
`, tp.Title, tp.Description)
}
// Software Upgrade Proposals
// SoftwareUpgradeProposal defines a proposal for upgrading the network nodes
// without the need of manually halting at a given height
//
// TODO: We have to add fields for SUP specific arguments e.g. commit hash,
// upgrade date, etc.
type SoftwareUpgradeProposal struct {
@ -288,22 +253,32 @@ type SoftwareUpgradeProposal struct {
Description string `json:"description" yaml:"description"`
}
// NewSoftwareUpgradeProposal creates a software upgrade proposal Content
func NewSoftwareUpgradeProposal(title, description string) Content {
return SoftwareUpgradeProposal{title, description}
}
// Implements Proposal Interface
// Implements Content Interface
var _ Content = SoftwareUpgradeProposal{}
// nolint
func (sup SoftwareUpgradeProposal) GetTitle() string { return sup.Title }
// GetTitle returns the proposal title
func (sup SoftwareUpgradeProposal) GetTitle() string { return sup.Title }
// GetDescription returns the proposal description
func (sup SoftwareUpgradeProposal) GetDescription() string { return sup.Description }
func (sup SoftwareUpgradeProposal) ProposalRoute() string { return RouterKey }
func (sup SoftwareUpgradeProposal) ProposalType() string { return ProposalTypeSoftwareUpgrade }
// ProposalRoute returns the proposal router key
func (sup SoftwareUpgradeProposal) ProposalRoute() string { return RouterKey }
// ProposalType is "SoftwareUpgrade"
func (sup SoftwareUpgradeProposal) ProposalType() string { return ProposalTypeSoftwareUpgrade }
// ValidateBasic validates the content's title and description of the proposal
func (sup SoftwareUpgradeProposal) ValidateBasic() sdk.Error {
return ValidateAbstract(DefaultCodespace, sup)
}
// String implements Stringer interface
func (sup SoftwareUpgradeProposal) String() string {
return fmt.Sprintf(`Software Upgrade Proposal:
Title: %s

View File

@ -4,6 +4,8 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
)
// DONTCOVER
// query endpoints supported by the governance Querier
const (
QueryParams = "params"
@ -20,7 +22,7 @@ const (
ParamTallying = "tallying"
)
// Params for queries:
// QueryProposalParams Params for queries:
// - 'custom/gov/proposal'
// - 'custom/gov/deposits'
// - 'custom/gov/tally'
@ -29,20 +31,20 @@ type QueryProposalParams struct {
ProposalID uint64
}
// creates a new instance of QueryProposalParams
// NewQueryProposalParams creates a new instance of QueryProposalParams
func NewQueryProposalParams(proposalID uint64) QueryProposalParams {
return QueryProposalParams{
ProposalID: proposalID,
}
}
// Params for query 'custom/gov/deposit'
// QueryDepositParams params for query 'custom/gov/deposit'
type QueryDepositParams struct {
ProposalID uint64
Depositor sdk.AccAddress
}
// creates a new instance of QueryDepositParams
// NewQueryDepositParams creates a new instance of QueryDepositParams
func NewQueryDepositParams(proposalID uint64, depositor sdk.AccAddress) QueryDepositParams {
return QueryDepositParams{
ProposalID: proposalID,
@ -50,13 +52,13 @@ func NewQueryDepositParams(proposalID uint64, depositor sdk.AccAddress) QueryDep
}
}
// Params for query 'custom/gov/vote'
// QueryVoteParams Params for query 'custom/gov/vote'
type QueryVoteParams struct {
ProposalID uint64
Voter sdk.AccAddress
}
// creates a new instance of QueryVoteParams
// NewQueryVoteParams creates a new instance of QueryVoteParams
func NewQueryVoteParams(proposalID uint64, voter sdk.AccAddress) QueryVoteParams {
return QueryVoteParams{
ProposalID: proposalID,
@ -64,7 +66,7 @@ func NewQueryVoteParams(proposalID uint64, voter sdk.AccAddress) QueryVoteParams
}
}
// Params for query 'custom/gov/proposals'
// QueryProposalsParams Params for query 'custom/gov/proposals'
type QueryProposalsParams struct {
Voter sdk.AccAddress
Depositor sdk.AccAddress
@ -72,7 +74,7 @@ type QueryProposalsParams struct {
Limit uint64
}
// creates a new instance of QueryProposalsParams
// NewQueryProposalsParams creates a new instance of QueryProposalsParams
func NewQueryProposalsParams(status ProposalStatus, limit uint64, voter, depositor sdk.AccAddress) QueryProposalsParams {
return QueryProposalsParams{
Voter: voter,

View File

@ -1,4 +1,4 @@
package gov
package types
import (
"fmt"
@ -26,6 +26,7 @@ type router struct {
sealed bool
}
// NewRouter creates a new Router interface instance
func NewRouter() Router {
return &router{
routes: make(map[string]Handler),

78
x/gov/types/tally.go Normal file
View File

@ -0,0 +1,78 @@
package types
import (
"fmt"
sdk "github.com/cosmos/cosmos-sdk/types"
)
// ValidatorGovInfo used for tallying
type ValidatorGovInfo struct {
Address sdk.ValAddress // address of the validator operator
BondedTokens sdk.Int // Power of a Validator
DelegatorShares sdk.Dec // Total outstanding delegator shares
DelegatorDeductions sdk.Dec // Delegator deductions from validator's delegators voting independently
Vote VoteOption // Vote of the validator
}
// NewValidatorGovInfo creates a ValidatorGovInfo instance
func NewValidatorGovInfo(address sdk.ValAddress, bondedTokens sdk.Int, delegatorShares,
delegatorDeductions sdk.Dec, vote VoteOption) ValidatorGovInfo {
return ValidatorGovInfo{
Address: address,
BondedTokens: bondedTokens,
DelegatorShares: delegatorShares,
DelegatorDeductions: delegatorDeductions,
Vote: vote,
}
}
// TallyResult defines a standard tally for a proposal
type TallyResult struct {
Yes sdk.Int `json:"yes" yaml:"yes"`
Abstain sdk.Int `json:"abstain" yaml:"abstain"`
No sdk.Int `json:"no" yaml:"no"`
NoWithVeto sdk.Int `json:"no_with_veto" yaml:"no_with_veto"`
}
// NewTallyResult creates a new TallyResult instance
func NewTallyResult(yes, abstain, no, noWithVeto sdk.Int) TallyResult {
return TallyResult{
Yes: yes,
Abstain: abstain,
No: no,
NoWithVeto: noWithVeto,
}
}
// NewTallyResultFromMap creates a new TallyResult instance from a Option -> Dec map
func NewTallyResultFromMap(results map[VoteOption]sdk.Dec) TallyResult {
return NewTallyResult(
results[OptionYes].TruncateInt(),
results[OptionAbstain].TruncateInt(),
results[OptionNo].TruncateInt(),
results[OptionNoWithVeto].TruncateInt(),
)
}
// EmptyTallyResult returns an empty TallyResult.
func EmptyTallyResult() TallyResult {
return NewTallyResult(sdk.ZeroInt(), sdk.ZeroInt(), sdk.ZeroInt(), sdk.ZeroInt())
}
// Equals returns if two proposals are equal.
func (tr TallyResult) Equals(comp TallyResult) bool {
return tr.Yes.Equal(comp.Yes) &&
tr.Abstain.Equal(comp.Abstain) &&
tr.No.Equal(comp.No) &&
tr.NoWithVeto.Equal(comp.NoWithVeto)
}
// String implements stringer interface
func (tr TallyResult) String() string {
return fmt.Sprintf(`Tally Result:
Yes: %s
Abstain: %s
No: %s
NoWithVeto: %s`, tr.Yes, tr.Abstain, tr.No, tr.NoWithVeto)
}

View File

@ -1,83 +0,0 @@
package gov
import (
"fmt"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/gov/types"
)
// AddVote Adds a vote on a specific proposal
func (keeper Keeper) AddVote(ctx sdk.Context, proposalID uint64, voterAddr sdk.AccAddress, option VoteOption) sdk.Error {
proposal, ok := keeper.GetProposal(ctx, proposalID)
if !ok {
return ErrUnknownProposal(keeper.codespace, proposalID)
}
if proposal.Status != StatusVotingPeriod {
return ErrInactiveProposal(keeper.codespace, proposalID)
}
if !ValidVoteOption(option) {
return ErrInvalidVote(keeper.codespace, option)
}
vote := NewVote(proposalID, voterAddr, option)
keeper.setVote(ctx, proposalID, voterAddr, vote)
ctx.EventManager().EmitEvent(
sdk.NewEvent(
types.EventTypeProposalVote,
sdk.NewAttribute(types.AttributeKeyOption, option.String()),
sdk.NewAttribute(types.AttributeKeyProposalID, fmt.Sprintf("%d", proposalID)),
),
)
return nil
}
// GetAllVotes returns all the votes from the store
func (keeper Keeper) GetAllVotes(ctx sdk.Context) (votes Votes) {
keeper.IterateAllVotes(ctx, func(vote Vote) bool {
votes = append(votes, vote)
return false
})
return
}
// GetVotes returns all the votes from a proposal
func (keeper Keeper) GetVotes(ctx sdk.Context, proposalID uint64) (votes Votes) {
keeper.IterateVotes(ctx, proposalID, func(vote Vote) bool {
votes = append(votes, vote)
return false
})
return
}
// GetVote gets the vote from an address on a specific proposal
func (keeper Keeper) GetVote(ctx sdk.Context, proposalID uint64, voterAddr sdk.AccAddress) (vote Vote, found bool) {
store := ctx.KVStore(keeper.storeKey)
bz := store.Get(types.VoteKey(proposalID, voterAddr))
if bz == nil {
return vote, false
}
keeper.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &vote)
return vote, true
}
func (keeper Keeper) setVote(ctx sdk.Context, proposalID uint64, voterAddr sdk.AccAddress, vote Vote) {
store := ctx.KVStore(keeper.storeKey)
bz := keeper.cdc.MustMarshalBinaryLengthPrefixed(vote)
store.Set(types.VoteKey(proposalID, voterAddr), bz)
}
// GetVotesIterator gets all the votes on a specific proposal as an sdk.Iterator
func (keeper Keeper) GetVotesIterator(ctx sdk.Context, proposalID uint64) sdk.Iterator {
store := ctx.KVStore(keeper.storeKey)
return sdk.KVStorePrefixIterator(store, types.VotesKey(proposalID))
}
func (keeper Keeper) deleteVote(ctx sdk.Context, proposalID uint64, voterAddr sdk.AccAddress) {
store := ctx.KVStore(keeper.storeKey)
store.Delete(types.VoteKey(proposalID, voterAddr))
}

View File

@ -60,10 +60,10 @@ func newTestInput(t *testing.T) testInput {
minterAcc := supply.NewEmptyModuleAccount(types.ModuleName, supply.Minter)
blacklistedAddrs := make(map[string]bool)
blacklistedAddrs[feeCollectorAcc.String()] = true
blacklistedAddrs[notBondedPool.String()] = true
blacklistedAddrs[bondPool.String()] = true
blacklistedAddrs[minterAcc.String()] = true
blacklistedAddrs[feeCollectorAcc.GetAddress().String()] = true
blacklistedAddrs[notBondedPool.GetAddress().String()] = true
blacklistedAddrs[bondPool.GetAddress().String()] = true
blacklistedAddrs[minterAcc.GetAddress().String()] = true
paramsKeeper := params.NewKeeper(types.ModuleCdc, keyParams, tkeyParams, params.DefaultCodespace)
accountKeeper := auth.NewAccountKeeper(types.ModuleCdc, keyAcc, paramsKeeper.Subspace(auth.DefaultParamspace), auth.ProtoBaseAccount)

View File

@ -12,7 +12,7 @@ import (
"github.com/cosmos/cosmos-sdk/version"
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/auth/client/utils"
"github.com/cosmos/cosmos-sdk/x/gov"
govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"
paramscutils "github.com/cosmos/cosmos-sdk/x/params/client/utils"
"github.com/cosmos/cosmos-sdk/x/params/types"
)
@ -75,7 +75,7 @@ Where proposal.json contains:
from := cliCtx.GetFromAddress()
content := types.NewParameterChangeProposal(proposal.Title, proposal.Description, proposal.Changes.ToParamChanges())
msg := gov.NewMsgSubmitProposal(content, proposal.Deposit, from)
msg := govtypes.NewMsgSubmitProposal(content, proposal.Deposit, from)
if err := msg.ValidateBasic(); err != nil {
return err
}

View File

@ -7,7 +7,7 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/rest"
"github.com/cosmos/cosmos-sdk/x/auth/client/utils"
"github.com/cosmos/cosmos-sdk/x/gov"
govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"
govrest "github.com/cosmos/cosmos-sdk/x/gov/client/rest"
"github.com/cosmos/cosmos-sdk/x/params"
paramscutils "github.com/cosmos/cosmos-sdk/x/params/client/utils"
@ -36,7 +36,7 @@ func postProposalHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
content := params.NewParameterChangeProposal(req.Title, req.Description, req.Changes.ToParamChanges())
msg := gov.NewMsgSubmitProposal(content, req.Deposit, req.Proposer)
msg := govtypes.NewMsgSubmitProposal(content, req.Deposit, req.Proposer)
if err := msg.ValidateBasic(); err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return

View File

@ -7,6 +7,7 @@ import (
govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"
)
// NewParamChangeProposalHandler creates a new governance Handler for a ParamChangeProposal
func NewParamChangeProposalHandler(k Keeper) govtypes.Handler {
return func(ctx sdk.Context, content govtypes.Content) sdk.Error {
switch c := content.(type) {

View File

@ -8,7 +8,7 @@ import (
"github.com/cosmos/cosmos-sdk/baseapp"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/gov"
govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"
"github.com/cosmos/cosmos-sdk/x/params"
"github.com/cosmos/cosmos-sdk/x/simulation"
)
@ -158,7 +158,7 @@ var paramChangePool = []simParamChange{
// SimulateParamChangeProposalContent returns random parameter change content.
// It will generate a ParameterChangeProposal object with anywhere between 1 and
// 3 parameter changes all of which have random, but valid values.
func SimulateParamChangeProposalContent(r *rand.Rand, _ *baseapp.BaseApp, _ sdk.Context, _ []simulation.Account) gov.Content {
func SimulateParamChangeProposalContent(r *rand.Rand, _ *baseapp.BaseApp, _ sdk.Context, _ []simulation.Account) govtypes.Content {
numChanges := simulation.RandIntBetween(r, 1, len(paramChangePool)/2)
paramChanges := make([]params.ParamChange, numChanges, numChanges)
paramChangesKeys := make(map[string]struct{})

View File

@ -1,7 +1,7 @@
package types
const (
// ModuleKey defines the name of the module
// ModuleName defines the name of the module
ModuleName = "params"
// RouterKey defines the routing key for a ParameterChangeProposal

View File

@ -39,7 +39,7 @@ func (pcp ParameterChangeProposal) GetTitle() string { return pcp.Title }
// GetDescription returns the description of a parameter change proposal.
func (pcp ParameterChangeProposal) GetDescription() string { return pcp.Description }
// GetDescription returns the routing key of a parameter change proposal.
// ProposalRoute returns the routing key of a parameter change proposal.
func (pcp ParameterChangeProposal) ProposalRoute() string { return RouterKey }
// ProposalType returns the type of a parameter change proposal.
@ -103,7 +103,7 @@ func (pc ParamChange) String() string {
`, pc.Subspace, pc.Key, pc.Subkey, pc.Value)
}
// ValidateChange performs basic validation checks over a set of ParamChange. It
// ValidateChanges performs basic validation checks over a set of ParamChange. It
// returns an error if any ParamChange is invalid.
func ValidateChanges(changes []ParamChange) sdk.Error {
if len(changes) == 0 {

View File

@ -43,9 +43,9 @@ func getMockApp(t *testing.T) (*mock.App, staking.Keeper, Keeper) {
bondPool := supply.NewEmptyModuleAccount(types.BondedPoolName, supply.Burner, supply.Staking)
blacklistedAddrs := make(map[string]bool)
blacklistedAddrs[feeCollector.String()] = true
blacklistedAddrs[notBondedPool.String()] = true
blacklistedAddrs[bondPool.String()] = true
blacklistedAddrs[feeCollector.GetAddress().String()] = true
blacklistedAddrs[notBondedPool.GetAddress().String()] = true
blacklistedAddrs[bondPool.GetAddress().String()] = true
bankKeeper := bank.NewBaseKeeper(mapp.AccountKeeper, mapp.ParamsKeeper.Subspace(bank.DefaultParamspace), bank.DefaultCodespace, blacklistedAddrs)
maccPerms := map[string][]string{

View File

@ -86,9 +86,9 @@ func CreateTestInput(t *testing.T, defaults types.Params) (sdk.Context, bank.Kee
bondPool := supply.NewEmptyModuleAccount(staking.BondedPoolName, supply.Burner, supply.Staking)
blacklistedAddrs := make(map[string]bool)
blacklistedAddrs[feeCollectorAcc.String()] = true
blacklistedAddrs[notBondedPool.String()] = true
blacklistedAddrs[bondPool.String()] = true
blacklistedAddrs[feeCollectorAcc.GetAddress().String()] = true
blacklistedAddrs[notBondedPool.GetAddress().String()] = true
blacklistedAddrs[bondPool.GetAddress().String()] = true
paramsKeeper := params.NewKeeper(cdc, keyParams, tkeyParams, params.DefaultCodespace)
accountKeeper := auth.NewAccountKeeper(cdc, keyAcc, paramsKeeper.Subspace(auth.DefaultParamspace), auth.ProtoBaseAccount)

View File

@ -31,9 +31,9 @@ func getMockApp(t *testing.T) (*mock.App, Keeper) {
bondPool := supply.NewEmptyModuleAccount(types.BondedPoolName, supply.Burner, supply.Staking)
blacklistedAddrs := make(map[string]bool)
blacklistedAddrs[feeCollector.String()] = true
blacklistedAddrs[notBondedPool.String()] = true
blacklistedAddrs[bondPool.String()] = true
blacklistedAddrs[feeCollector.GetAddress().String()] = true
blacklistedAddrs[notBondedPool.GetAddress().String()] = true
blacklistedAddrs[bondPool.GetAddress().String()] = true
bankKeeper := bank.NewBaseKeeper(mApp.AccountKeeper, mApp.ParamsKeeper.Subspace(bank.DefaultParamspace), bank.DefaultCodespace, blacklistedAddrs)
maccPerms := map[string][]string{

View File

@ -112,9 +112,9 @@ func CreateTestInput(t *testing.T, isCheckTx bool, initPower int64) (sdk.Context
bondPool := supply.NewEmptyModuleAccount(types.BondedPoolName, supply.Burner, supply.Staking)
blacklistedAddrs := make(map[string]bool)
blacklistedAddrs[feeCollectorAcc.String()] = true
blacklistedAddrs[notBondedPool.String()] = true
blacklistedAddrs[bondPool.String()] = true
blacklistedAddrs[feeCollectorAcc.GetAddress().String()] = true
blacklistedAddrs[notBondedPool.GetAddress().String()] = true
blacklistedAddrs[bondPool.GetAddress().String()] = true
pk := params.NewKeeper(cdc, keyParams, tkeyParams, params.DefaultCodespace)