add mre fields to committee type

This commit is contained in:
rhuairahrighairigh 2020-03-27 18:34:03 +00:00
parent 77553ed299
commit 57f4ca7c9a
11 changed files with 183 additions and 125 deletions

View File

@ -14,6 +14,8 @@ import (
"github.com/kava-labs/kava/x/committee" "github.com/kava-labs/kava/x/committee"
) )
func d(s string) sdk.Dec { return sdk.MustNewDecFromStr(s) }
type ModuleTestSuite struct { type ModuleTestSuite struct {
suite.Suite suite.Suite
@ -35,9 +37,11 @@ func (suite *ModuleTestSuite) TestBeginBlock() {
suite.app.InitializeFromGenesisStates() suite.app.InitializeFromGenesisStates()
// TODO replace below with genesis state // TODO replace below with genesis state
normalCom := committee.Committee{ normalCom := committee.Committee{
ID: 12, ID: 12,
Members: suite.addresses[:2], Members: suite.addresses[:2],
Permissions: []committee.Permission{committee.GodPermission{}}, Permissions: []committee.Permission{committee.GodPermission{}},
VoteThreshold: d("0.8"),
MaxProposalDuration: time.Hour * 24 * 7,
} }
suite.keeper.SetCommittee(suite.ctx, normalCom) suite.keeper.SetCommittee(suite.ctx, normalCom)
@ -51,7 +55,7 @@ func (suite *ModuleTestSuite) TestBeginBlock() {
suite.NoError(err) suite.NoError(err)
// Run BeginBlocker // Run BeginBlocker
proposalDurationLaterCtx := suite.ctx.WithBlockTime(suite.ctx.BlockTime().Add(committee.MaxProposalDuration)) proposalDurationLaterCtx := suite.ctx.WithBlockTime(suite.ctx.BlockTime().Add(normalCom.MaxProposalDuration))
suite.NotPanics(func() { suite.NotPanics(func() {
committee.BeginBlocker(proposalDurationLaterCtx, abci.RequestBeginBlock{}, suite.keeper) committee.BeginBlocker(proposalDurationLaterCtx, abci.RequestBeginBlock{}, suite.keeper)
}) })

View File

@ -14,6 +14,7 @@ const (
DefaultNextProposalID = types.DefaultNextProposalID DefaultNextProposalID = types.DefaultNextProposalID
DefaultParamspace = types.DefaultParamspace DefaultParamspace = types.DefaultParamspace
EventTypeSubmitProposal = types.EventTypeSubmitProposal EventTypeSubmitProposal = types.EventTypeSubmitProposal
MaxDescriptionLength = types.MaxDescriptionLength
ModuleName = types.ModuleName ModuleName = types.ModuleName
ProposalTypeCommitteeChange = types.ProposalTypeCommitteeChange ProposalTypeCommitteeChange = types.ProposalTypeCommitteeChange
ProposalTypeCommitteeDelete = types.ProposalTypeCommitteeDelete ProposalTypeCommitteeDelete = types.ProposalTypeCommitteeDelete
@ -53,14 +54,12 @@ var (
Uint64FromBytes = types.Uint64FromBytes Uint64FromBytes = types.Uint64FromBytes
// variable aliases // variable aliases
ProposalHandler = client.ProposalHandler ProposalHandler = client.ProposalHandler
CommitteeKeyPrefix = types.CommitteeKeyPrefix CommitteeKeyPrefix = types.CommitteeKeyPrefix
MaxProposalDuration = types.MaxProposalDuration ModuleCdc = types.ModuleCdc
ModuleCdc = types.ModuleCdc NextProposalIDKey = types.NextProposalIDKey
NextProposalIDKey = types.NextProposalIDKey ProposalKeyPrefix = types.ProposalKeyPrefix
ProposalKeyPrefix = types.ProposalKeyPrefix VoteKeyPrefix = types.VoteKeyPrefix
VoteKeyPrefix = types.VoteKeyPrefix
VoteThreshold = types.VoteThreshold
) )
type ( type (

View File

@ -4,6 +4,7 @@ import (
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"strconv" "strconv"
"time"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@ -189,8 +190,15 @@ func mustGetExampleCommitteeChangeProposal(cdc *codec.Codec) string {
"A description of this proposal.", "A description of this proposal.",
types.NewCommittee( types.NewCommittee(
1, 1,
"The description of this committee.",
[]sdk.AccAddress{sdk.AccAddress(crypto.AddressHash([]byte("exampleAddres")))}, []sdk.AccAddress{sdk.AccAddress(crypto.AddressHash([]byte("exampleAddres")))},
[]types.Permission{}, // TODO permissions []types.Permission{
types.ParamChangePermission{
AllowedParams: types.AllowedParams{{Subspace: "cdp", Key: "CircuitBreaker"}},
},
},
sdk.MustNewDecFromStr("0.8"),
time.Hour*24*7,
), ),
) )
exampleChangeProposalBz, err := cdc.MarshalJSONIndent(exampleChangeProposal, "", " ") exampleChangeProposalBz, err := cdc.MarshalJSONIndent(exampleChangeProposal, "", " ")

View File

@ -2,10 +2,12 @@ package keeper_test
import ( import (
"testing" "testing"
"time"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/gov"
abci "github.com/tendermint/tendermint/abci/types" abci "github.com/tendermint/tendermint/abci/types"
"github.com/kava-labs/kava/app" "github.com/kava-labs/kava/app"
@ -13,6 +15,8 @@ import (
"github.com/kava-labs/kava/x/committee/types" "github.com/kava-labs/kava/x/committee/types"
) )
func d(s string) sdk.Dec { return sdk.MustNewDecFromStr(s) }
type KeeperTestSuite struct { type KeeperTestSuite struct {
suite.Suite suite.Suite
@ -33,8 +37,12 @@ func (suite *KeeperTestSuite) SetupTest() {
func (suite *KeeperTestSuite) TestGetSetDeleteCommittee() { func (suite *KeeperTestSuite) TestGetSetDeleteCommittee() {
// setup test // setup test
com := types.Committee{ com := types.Committee{
ID: 12, ID: 12,
// TODO other fields Description: "This committee is for testing.",
Members: suite.addresses,
Permissions: []types.Permission{types.GodPermission{}},
VoteThreshold: d("0.667"),
MaxProposalDuration: time.Hour * 24 * 7,
} }
// write and read from store // write and read from store
@ -56,8 +64,10 @@ func (suite *KeeperTestSuite) TestGetSetDeleteCommittee() {
func (suite *KeeperTestSuite) TestGetSetProposal() { func (suite *KeeperTestSuite) TestGetSetProposal() {
// test setup // test setup
prop := types.Proposal{ prop := types.Proposal{
ID: 12, ID: 12,
// TODO other fields CommitteeID: 0,
PubProposal: gov.NewTextProposal("A Title", "A description of this proposal."),
Deadline: time.Date(1998, time.January, 1, 0, 0, 0, 0, time.UTC),
} }
// write and read from store // write and read from store
@ -81,7 +91,6 @@ func (suite *KeeperTestSuite) TestGetSetVote() {
vote := types.Vote{ vote := types.Vote{
ProposalID: 12, ProposalID: 12,
Voter: suite.addresses[0], Voter: suite.addresses[0],
// TODO other fields
} }
// write and read from store // write and read from store

View File

@ -30,7 +30,7 @@ func (k Keeper) SubmitProposal(ctx sdk.Context, proposer sdk.AccAddress, committ
} }
// Get a new ID and store the proposal // Get a new ID and store the proposal
deadline := ctx.BlockTime().Add(types.MaxProposalDuration) deadline := ctx.BlockTime().Add(com.MaxProposalDuration)
proposalID, err := k.StoreNewProposal(ctx, pubProposal, committeeID, deadline) proposalID, err := k.StoreNewProposal(ctx, pubProposal, committeeID, deadline)
if err != nil { if err != nil {
return 0, err return 0, err
@ -82,7 +82,7 @@ func (k Keeper) GetProposalResult(ctx sdk.Context, proposalID uint64) (bool, sdk
numVotes := k.TallyVotes(ctx, proposalID) numVotes := k.TallyVotes(ctx, proposalID)
proposalResult := sdk.NewDec(numVotes).GTE(types.VoteThreshold.MulInt64(int64(len(com.Members)))) proposalResult := sdk.NewDec(numVotes).GTE(com.VoteThreshold.MulInt64(int64(len(com.Members))))
return proposalResult, nil return proposalResult, nil
} }

View File

@ -16,15 +16,15 @@ import (
func (suite *KeeperTestSuite) TestSubmitProposal() { func (suite *KeeperTestSuite) TestSubmitProposal() {
normalCom := types.Committee{ normalCom := types.Committee{
ID: 12, ID: 12,
Members: suite.addresses[:2], Description: "This committee is for testing.",
Permissions: []types.Permission{types.GodPermission{}}, Members: suite.addresses[:2],
} Permissions: []types.Permission{types.GodPermission{}},
noPermissionsCom := types.Committee{ VoteThreshold: d("0.667"),
ID: 12, MaxProposalDuration: time.Hour * 24 * 7,
Members: suite.addresses[:2],
Permissions: []types.Permission{},
} }
noPermissionsCom := normalCom
noPermissionsCom.Permissions = []types.Permission{}
testcases := []struct { testcases := []struct {
name string name string
@ -96,7 +96,7 @@ func (suite *KeeperTestSuite) TestSubmitProposal() {
pr, found := keeper.GetProposal(ctx, id) pr, found := keeper.GetProposal(ctx, id)
suite.True(found) suite.True(found)
suite.Equal(tc.committeeID, pr.CommitteeID) suite.Equal(tc.committeeID, pr.CommitteeID)
suite.Equal(ctx.BlockTime().Add(types.MaxProposalDuration), pr.Deadline) suite.Equal(ctx.BlockTime().Add(tc.committee.MaxProposalDuration), pr.Deadline)
} else { } else {
suite.NotNil(err) suite.NotNil(err)
} }
@ -141,7 +141,7 @@ func (suite *KeeperTestSuite) TestAddVote() {
name: "proposal expired", name: "proposal expired",
proposalID: types.DefaultNextProposalID, proposalID: types.DefaultNextProposalID,
voter: normalCom.Members[0], voter: normalCom.Members[0],
voteTime: firstBlockTime.Add(types.MaxProposalDuration), voteTime: firstBlockTime.Add(normalCom.MaxProposalDuration),
expectPass: false, expectPass: false,
}, },
} }
@ -174,6 +174,14 @@ func (suite *KeeperTestSuite) TestAddVote() {
} }
func (suite *KeeperTestSuite) TestGetProposalResult() { func (suite *KeeperTestSuite) TestGetProposalResult() {
normalCom := types.Committee{
ID: 12,
Description: "This committee is for testing.",
Members: suite.addresses[:5],
Permissions: []types.Permission{types.GodPermission{}},
VoteThreshold: d("0.667"),
MaxProposalDuration: time.Hour * 24 * 7,
}
var defaultID uint64 = 1 var defaultID uint64 = 1
firstBlockTime := time.Date(1998, time.January, 1, 1, 0, 0, 0, time.UTC) firstBlockTime := time.Date(1998, time.January, 1, 1, 0, 0, 0, time.UTC)
@ -185,12 +193,8 @@ func (suite *KeeperTestSuite) TestGetProposalResult() {
expectPass bool expectPass bool
}{ }{
{ {
name: "enough votes", name: "enough votes",
committee: types.Committee{ committee: normalCom,
ID: 12,
Members: suite.addresses[:5],
Permissions: []types.Permission{types.GodPermission{}},
},
votes: []types.Vote{ votes: []types.Vote{
{ProposalID: defaultID, Voter: suite.addresses[0]}, {ProposalID: defaultID, Voter: suite.addresses[0]},
{ProposalID: defaultID, Voter: suite.addresses[1]}, {ProposalID: defaultID, Voter: suite.addresses[1]},
@ -201,12 +205,8 @@ func (suite *KeeperTestSuite) TestGetProposalResult() {
expectPass: true, expectPass: true,
}, },
{ {
name: "not enough votes", name: "not enough votes",
committee: types.Committee{ committee: normalCom,
ID: 12,
Members: suite.addresses[:5],
Permissions: []types.Permission{types.GodPermission{}},
},
votes: []types.Vote{ votes: []types.Vote{
{ProposalID: defaultID, Voter: suite.addresses[0]}, {ProposalID: defaultID, Voter: suite.addresses[0]},
}, },

View File

@ -3,6 +3,7 @@ package keeper_test
import ( import (
"strings" "strings"
"testing" "testing"
"time"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
@ -12,6 +13,7 @@ import (
abci "github.com/tendermint/tendermint/abci/types" abci "github.com/tendermint/tendermint/abci/types"
"github.com/kava-labs/kava/app" "github.com/kava-labs/kava/app"
"github.com/kava-labs/kava/x/committee"
"github.com/kava-labs/kava/x/committee/keeper" "github.com/kava-labs/kava/x/committee/keeper"
"github.com/kava-labs/kava/x/committee/types" "github.com/kava-labs/kava/x/committee/types"
) )
@ -20,6 +22,12 @@ const (
custom = "custom" custom = "custom"
) )
var testTime time.Time = time.Date(1998, time.January, 1, 0, 0, 0, 0, time.UTC)
func NewCommitteeGenesisState(cdc *codec.Codec, gs committee.GenesisState) app.GenesisState {
return app.GenesisState{committee.ModuleName: cdc.MustMarshalJSON(gs)}
}
type QuerierTestSuite struct { type QuerierTestSuite struct {
suite.Suite suite.Suite
@ -30,10 +38,9 @@ type QuerierTestSuite struct {
querier sdk.Querier querier sdk.Querier
addresses []sdk.AccAddress addresses []sdk.AccAddress
committees []types.Committee testGenesis types.GenesisState
proposals []types.Proposal votes map[uint64]([]types.Vote)
votes map[uint64]([]types.Vote)
} }
func (suite *QuerierTestSuite) SetupTest() { func (suite *QuerierTestSuite) SetupTest() {
@ -44,37 +51,40 @@ func (suite *QuerierTestSuite) SetupTest() {
suite.querier = keeper.NewQuerier(suite.keeper) suite.querier = keeper.NewQuerier(suite.keeper)
_, suite.addresses = app.GeneratePrivKeyAddressPairs(5) _, suite.addresses = app.GeneratePrivKeyAddressPairs(5)
suite.app.InitializeFromGenesisStates() suite.testGenesis = types.NewGenesisState(
// TODO replace below with genesis state 3,
normalCom := types.Committee{ []types.Committee{
ID: 12, {
Members: suite.addresses[:2], ID: 1,
Permissions: []types.Permission{types.GodPermission{}}, Description: "This committee is for testing.",
} Members: suite.addresses[:3],
suite.keeper.SetCommittee(suite.ctx, normalCom) Permissions: []types.Permission{types.GodPermission{}},
VoteThreshold: d("0.667"),
MaxProposalDuration: time.Hour * 24 * 7,
},
{
ID: 2,
Members: suite.addresses[2:],
Permissions: nil,
VoteThreshold: d("0.667"),
MaxProposalDuration: time.Hour * 24 * 7,
},
},
[]types.Proposal{
{ID: 1, CommitteeID: 1, PubProposal: gov.NewTextProposal("A Title", "A description of this proposal."), Deadline: testTime.Add(7 * 24 * time.Hour)},
{ID: 2, CommitteeID: 1, PubProposal: gov.NewTextProposal("Another Title", "A description of this other proposal."), Deadline: testTime.Add(21 * 24 * time.Hour)},
},
[]types.Vote{
{ProposalID: 1, Voter: suite.addresses[0]},
{ProposalID: 1, Voter: suite.addresses[1]},
{ProposalID: 2, Voter: suite.addresses[2]},
},
)
suite.app.InitializeFromGenesisStates(
NewCommitteeGenesisState(suite.cdc, suite.testGenesis),
)
pprop1 := gov.NewTextProposal("1A Title", "A description of this proposal.") // Collect up votes into a map indexed by proposalID for convenience
id1, err := suite.keeper.SubmitProposal(suite.ctx, normalCom.Members[0], normalCom.ID, pprop1)
suite.NoError(err)
pprop2 := gov.NewTextProposal("2A Title", "A description of this proposal.")
id2, err := suite.keeper.SubmitProposal(suite.ctx, normalCom.Members[0], normalCom.ID, pprop2)
suite.NoError(err)
err = suite.keeper.AddVote(suite.ctx, id1, normalCom.Members[0])
suite.NoError(err)
err = suite.keeper.AddVote(suite.ctx, id1, normalCom.Members[1])
suite.NoError(err)
err = suite.keeper.AddVote(suite.ctx, id2, normalCom.Members[1])
suite.NoError(err)
suite.committees = []types.Committee{}
suite.committees = []types.Committee{normalCom} // TODO
suite.proposals = []types.Proposal{}
suite.keeper.IterateProposals(suite.ctx, func(p types.Proposal) bool {
suite.proposals = append(suite.proposals, p)
return false
})
suite.votes = map[uint64]([]types.Vote){} suite.votes = map[uint64]([]types.Vote){}
suite.keeper.IterateProposals(suite.ctx, func(p types.Proposal) bool { suite.keeper.IterateProposals(suite.ctx, func(p types.Proposal) bool {
suite.keeper.IterateVotes(suite.ctx, p.ID, func(v types.Vote) bool { suite.keeper.IterateVotes(suite.ctx, p.ID, func(v types.Vote) bool {
@ -102,7 +112,7 @@ func (suite *QuerierTestSuite) TestQueryCommittees() {
suite.NoError(suite.cdc.UnmarshalJSON(bz, &committees)) suite.NoError(suite.cdc.UnmarshalJSON(bz, &committees))
// Check // Check
suite.Equal(suite.committees, committees) suite.Equal(suite.testGenesis.Committees, committees)
} }
func (suite *QuerierTestSuite) TestQueryCommittee() { func (suite *QuerierTestSuite) TestQueryCommittee() {
@ -110,7 +120,7 @@ func (suite *QuerierTestSuite) TestQueryCommittee() {
// Set up request query // Set up request query
query := abci.RequestQuery{ query := abci.RequestQuery{
Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryCommittee}, "/"), Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryCommittee}, "/"),
Data: suite.cdc.MustMarshalJSON(types.NewQueryCommitteeParams(suite.committees[0].ID)), Data: suite.cdc.MustMarshalJSON(types.NewQueryCommitteeParams(suite.testGenesis.Committees[0].ID)),
} }
// Execute query and check the []byte result // Execute query and check the []byte result
@ -123,13 +133,13 @@ func (suite *QuerierTestSuite) TestQueryCommittee() {
suite.NoError(suite.cdc.UnmarshalJSON(bz, &committee)) suite.NoError(suite.cdc.UnmarshalJSON(bz, &committee))
// Check // Check
suite.Equal(suite.committees[0], committee) suite.Equal(suite.testGenesis.Committees[0], committee)
} }
func (suite *QuerierTestSuite) TestQueryProposals() { func (suite *QuerierTestSuite) TestQueryProposals() {
ctx := suite.ctx.WithIsCheckTx(false) ctx := suite.ctx.WithIsCheckTx(false)
// Set up request query // Set up request query
comID := suite.proposals[0].CommitteeID comID := suite.testGenesis.Proposals[0].CommitteeID
query := abci.RequestQuery{ query := abci.RequestQuery{
Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryProposals}, "/"), Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryProposals}, "/"),
Data: suite.cdc.MustMarshalJSON(types.NewQueryCommitteeParams(comID)), Data: suite.cdc.MustMarshalJSON(types.NewQueryCommitteeParams(comID)),
@ -146,7 +156,7 @@ func (suite *QuerierTestSuite) TestQueryProposals() {
// Check // Check
expectedProposals := []types.Proposal{} expectedProposals := []types.Proposal{}
for _, p := range suite.proposals { for _, p := range suite.testGenesis.Proposals {
if p.CommitteeID == comID { if p.CommitteeID == comID {
expectedProposals = append(expectedProposals, p) expectedProposals = append(expectedProposals, p)
} }
@ -159,7 +169,7 @@ func (suite *QuerierTestSuite) TestQueryProposal() {
// Set up request query // Set up request query
query := abci.RequestQuery{ query := abci.RequestQuery{
Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryProposal}, "/"), Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryProposal}, "/"),
Data: suite.cdc.MustMarshalJSON(types.NewQueryProposalParams(suite.proposals[0].ID)), Data: suite.cdc.MustMarshalJSON(types.NewQueryProposalParams(suite.testGenesis.Proposals[0].ID)),
} }
// Execute query and check the []byte result // Execute query and check the []byte result
@ -172,13 +182,13 @@ func (suite *QuerierTestSuite) TestQueryProposal() {
suite.NoError(suite.cdc.UnmarshalJSON(bz, &proposal)) suite.NoError(suite.cdc.UnmarshalJSON(bz, &proposal))
// Check // Check
suite.Equal(suite.proposals[0], proposal) suite.Equal(suite.testGenesis.Proposals[0], proposal)
} }
func (suite *QuerierTestSuite) TestQueryVotes() { func (suite *QuerierTestSuite) TestQueryVotes() {
ctx := suite.ctx.WithIsCheckTx(false) ctx := suite.ctx.WithIsCheckTx(false)
// Set up request query // Set up request query
propID := suite.proposals[0].ID propID := suite.testGenesis.Proposals[0].ID
query := abci.RequestQuery{ query := abci.RequestQuery{
Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryVotes}, "/"), Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryVotes}, "/"),
Data: suite.cdc.MustMarshalJSON(types.NewQueryProposalParams(propID)), Data: suite.cdc.MustMarshalJSON(types.NewQueryProposalParams(propID)),
@ -200,7 +210,7 @@ func (suite *QuerierTestSuite) TestQueryVotes() {
func (suite *QuerierTestSuite) TestQueryVote() { func (suite *QuerierTestSuite) TestQueryVote() {
ctx := suite.ctx.WithIsCheckTx(false) // ? ctx := suite.ctx.WithIsCheckTx(false) // ?
// Set up request query // Set up request query
propID := suite.proposals[0].ID propID := suite.testGenesis.Proposals[0].ID
query := abci.RequestQuery{ query := abci.RequestQuery{
Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryVote}, "/"), Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryVote}, "/"),
Data: suite.cdc.MustMarshalJSON(types.NewQueryVoteParams(propID, suite.votes[propID][0].Voter)), Data: suite.cdc.MustMarshalJSON(types.NewQueryVoteParams(propID, suite.votes[propID][0].Voter)),
@ -222,7 +232,7 @@ func (suite *QuerierTestSuite) TestQueryVote() {
func (suite *QuerierTestSuite) TestQueryTally() { func (suite *QuerierTestSuite) TestQueryTally() {
ctx := suite.ctx.WithIsCheckTx(false) // ? ctx := suite.ctx.WithIsCheckTx(false) // ?
// Set up request query // Set up request query
propID := suite.proposals[0].ID propID := suite.testGenesis.Proposals[0].ID
query := abci.RequestQuery{ query := abci.RequestQuery{
Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryTally}, "/"), Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryTally}, "/"),
Data: suite.cdc.MustMarshalJSON(types.NewQueryProposalParams(propID)), Data: suite.cdc.MustMarshalJSON(types.NewQueryProposalParams(propID)),

View File

@ -38,14 +38,19 @@ func (suite *ProposalHandlerTestSuite) SetupTest() {
2, 2,
[]committee.Committee{ []committee.Committee{
{ {
ID: 1, ID: 1,
Members: suite.addresses[:3], Description: "This committee is for testing.",
Permissions: []types.Permission{types.GodPermission{}}, Members: suite.addresses[:3],
Permissions: []types.Permission{types.GodPermission{}},
VoteThreshold: d("0.667"),
MaxProposalDuration: time.Hour * 24 * 7,
}, },
{ {
ID: 2, ID: 2,
Members: suite.addresses[2:], Members: suite.addresses[2:],
Permissions: nil, Permissions: nil,
VoteThreshold: d("0.667"),
MaxProposalDuration: time.Hour * 24 * 7,
}, },
}, },
[]committee.Proposal{ []committee.Proposal{
@ -69,8 +74,10 @@ func (suite *ProposalHandlerTestSuite) TestProposalHandler_ChangeCommittee() {
"A Title", "A Title",
"A proposal description.", "A proposal description.",
committee.Committee{ committee.Committee{
ID: 34, ID: 34,
Members: suite.addresses[:1], Members: suite.addresses[:1],
VoteThreshold: d("1"),
MaxProposalDuration: time.Hour * 24,
}, },
), ),
expectPass: true, expectPass: true,
@ -81,9 +88,11 @@ func (suite *ProposalHandlerTestSuite) TestProposalHandler_ChangeCommittee() {
"A Title", "A Title",
"A proposal description.", "A proposal description.",
committee.Committee{ committee.Committee{
ID: 1, ID: suite.testGenesis.Committees[0].ID,
Members: suite.addresses, Members: suite.addresses, // add new members
Permissions: suite.testGenesis.Committees[0].Permissions, Permissions: suite.testGenesis.Committees[0].Permissions,
VoteThreshold: suite.testGenesis.Committees[0].VoteThreshold,
MaxProposalDuration: suite.testGenesis.Committees[0].MaxProposalDuration,
}, },
), ),
expectPass: true, expectPass: true,
@ -93,9 +102,7 @@ func (suite *ProposalHandlerTestSuite) TestProposalHandler_ChangeCommittee() {
proposal: committee.NewCommitteeChangeProposal( proposal: committee.NewCommitteeChangeProposal(
"A Title That Is Much Too Long And Really Quite Unreasonable Given That It Is Trying To Fullfill The Roll Of An Acceptable Governance Proposal Title That Should Succinctly Communicate The Goal And Contents Of The Proposed Proposal To All Parties Involved", "A Title That Is Much Too Long And Really Quite Unreasonable Given That It Is Trying To Fullfill The Roll Of An Acceptable Governance Proposal Title That Should Succinctly Communicate The Goal And Contents Of The Proposed Proposal To All Parties Involved",
"A proposal description.", "A proposal description.",
committee.Committee{ suite.testGenesis.Committees[0],
ID: 34,
},
), ),
expectPass: false, expectPass: false,
}, },
@ -105,9 +112,11 @@ func (suite *ProposalHandlerTestSuite) TestProposalHandler_ChangeCommittee() {
"A Title", "A Title",
"A proposal description.", "A proposal description.",
committee.Committee{ committee.Committee{
ID: 1, ID: suite.testGenesis.Committees[0].ID,
Members: append(suite.addresses, suite.addresses[0]), // duplicate address Members: append(suite.addresses, suite.addresses[0]), // duplicate address
Permissions: suite.testGenesis.Committees[0].Permissions, Permissions: suite.testGenesis.Committees[0].Permissions,
VoteThreshold: suite.testGenesis.Committees[0].VoteThreshold,
MaxProposalDuration: suite.testGenesis.Committees[0].MaxProposalDuration,
}, },
), ),
expectPass: false, expectPass: false,

View File

@ -11,6 +11,7 @@ import (
"github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/crypto"
) )
func d(s string) sdk.Dec { return sdk.MustNewDecFromStr(s) }
func TestGenesisState_Validate(t *testing.T) { func TestGenesisState_Validate(t *testing.T) {
testTime := time.Date(1998, time.January, 1, 0, 0, 0, 0, time.UTC) testTime := time.Date(1998, time.January, 1, 0, 0, 0, 0, time.UTC)
addresses := []sdk.AccAddress{ addresses := []sdk.AccAddress{
@ -24,14 +25,20 @@ func TestGenesisState_Validate(t *testing.T) {
NextProposalID: 2, NextProposalID: 2,
Committees: []Committee{ Committees: []Committee{
{ {
ID: 1, ID: 1,
Members: addresses[:3], Description: "This committee is for testing.",
Permissions: []Permission{GodPermission{}}, Members: addresses[:3],
Permissions: []Permission{GodPermission{}},
VoteThreshold: d("0.667"),
MaxProposalDuration: time.Hour * 24 * 7,
}, },
{ {
ID: 2, ID: 2,
Members: addresses[2:], Description: "This committee is also for testing.",
Permissions: nil, Members: addresses[2:],
Permissions: nil,
VoteThreshold: d("0.8"),
MaxProposalDuration: time.Hour * 24 * 21,
}, },
}, },
Proposals: []Proposal{ Proposals: []Proposal{

View File

@ -16,6 +16,7 @@ func init() {
} }
// GodPermission allows any governance proposal. It is used mainly for testing. // GodPermission allows any governance proposal. It is used mainly for testing.
// TODO better name?
type GodPermission struct{} type GodPermission struct{}
var _ Permission = GodPermission{} var _ Permission = GodPermission{}

View File

@ -9,29 +9,28 @@ import (
"github.com/cosmos/cosmos-sdk/x/gov" "github.com/cosmos/cosmos-sdk/x/gov"
) )
// TODO move these into params const MaxCommitteeDescriptionLength int = 5000
var (
VoteThreshold sdk.Dec = sdk.MustNewDecFromStr("0.75")
MaxProposalDuration time.Duration = time.Hour * 24 * 7
)
// -------- Committees -------- // -------- Committees --------
// A Committee is a collection of addresses that are allowed to vote and enact any governance proposal that passes their permissions. // A Committee is a collection of addresses that are allowed to vote and enact any governance proposal that passes their permissions.
type Committee struct { type Committee struct {
ID uint64 `json:"id" yaml:"id"` ID uint64 `json:"id" yaml:"id"`
//Description string `json:"description" yaml:"description"` Description string `json:"description" yaml:"description"`
Members []sdk.AccAddress `json:"members" yaml:"members"` Members []sdk.AccAddress `json:"members" yaml:"members"`
Permissions []Permission `json:"permissions" yaml:"permissions"` Permissions []Permission `json:"permissions" yaml:"permissions"`
// VoteThreshold sdk.Dec `json:"vote_threshold" yaml:"vote_threshold"` VoteThreshold sdk.Dec `json:"vote_threshold" yaml:"vote_threshold"`
// MaxProposalDuration time.Duration `json:"max_proposal_duration" yaml:"max_proposal_duration"` MaxProposalDuration time.Duration `json:"max_proposal_duration" yaml:"max_proposal_duration"`
} }
func NewCommittee(id uint64, members []sdk.AccAddress, permissions []Permission) Committee { func NewCommittee(id uint64, description string, members []sdk.AccAddress, permissions []Permission, threshold sdk.Dec, duration time.Duration) Committee {
return Committee{ return Committee{
ID: id, ID: id,
Members: members, Description: description,
Permissions: permissions, Members: members,
Permissions: permissions,
VoteThreshold: threshold,
MaxProposalDuration: duration,
} }
} }
@ -74,6 +73,19 @@ func (c Committee) Validate() error {
if len(c.Members) == 0 { if len(c.Members) == 0 {
return fmt.Errorf("committee %d invalid: cannot have zero members", c.ID) return fmt.Errorf("committee %d invalid: cannot have zero members", c.ID)
} }
if len(c.Description) > MaxCommitteeDescriptionLength {
return fmt.Errorf("invalid description")
}
if c.VoteThreshold.IsNil() || c.VoteThreshold.IsNegative() || c.VoteThreshold.GT(sdk.NewDec(1)) {
return fmt.Errorf("invalid threshold")
}
if c.MaxProposalDuration < 0 {
return fmt.Errorf("invalid time")
}
return nil return nil
} }
@ -118,5 +130,4 @@ func (p Proposal) String() string {
type Vote struct { type Vote struct {
ProposalID uint64 `json:"proposal_id" yaml:"proposal_id"` ProposalID uint64 `json:"proposal_id" yaml:"proposal_id"`
Voter sdk.AccAddress `json:"voter" yaml:"voter"` Voter sdk.AccAddress `json:"voter" yaml:"voter"`
// Option byte // TODO for now don't need more than just a yes as options
} }