Merge PR #4665: Refactor x/gov Module Structure (prep for module spec)
This commit is contained in:
parent
83db58ec5e
commit
e4c8bd72b7
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
|
@ -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)))
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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})
|
||||
}
|
||||
}
|
|
@ -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 {
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
|
|
@ -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(),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
})
|
||||
}
|
|
@ -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())
|
||||
}
|
|
@ -1,4 +1,6 @@
|
|||
package gov
|
||||
package keeper
|
||||
|
||||
// DONTCOVER
|
||||
|
||||
import (
|
||||
"fmt"
|
|
@ -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)))
|
||||
}
|
|
@ -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()
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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, ¶ms)
|
||||
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, ¶ms)
|
||||
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, ¶ms)
|
||||
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, ¶ms)
|
||||
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, ¶ms)
|
||||
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, ¶ms)
|
||||
|
||||
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, ¶ms)
|
||||
if err != nil {
|
||||
return nil, sdk.ErrUnknownRequest(sdk.AppendMsgToErr("incorrectly formatted request data", err.Error()))
|
|
@ -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)
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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()))
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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))
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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{}
|
||||
|
|
|
@ -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)
|
||||
}
|
|
@ -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()))
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
|
|
@ -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,
|
|
@ -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
|
||||
}
|
|
@ -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))
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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}
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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),
|
|
@ -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)
|
||||
}
|
|
@ -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))
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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{})
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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{
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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{
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
Loading…
Reference in New Issue