diff --git a/x/committee/abci_test.go b/x/committee/abci_test.go index 7c64b766..adfa501d 100644 --- a/x/committee/abci_test.go +++ b/x/committee/abci_test.go @@ -14,6 +14,8 @@ import ( "github.com/kava-labs/kava/x/committee" ) +func d(s string) sdk.Dec { return sdk.MustNewDecFromStr(s) } + type ModuleTestSuite struct { suite.Suite @@ -35,9 +37,11 @@ func (suite *ModuleTestSuite) TestBeginBlock() { suite.app.InitializeFromGenesisStates() // TODO replace below with genesis state normalCom := committee.Committee{ - ID: 12, - Members: suite.addresses[:2], - Permissions: []committee.Permission{committee.GodPermission{}}, + ID: 12, + Members: suite.addresses[:2], + Permissions: []committee.Permission{committee.GodPermission{}}, + VoteThreshold: d("0.8"), + MaxProposalDuration: time.Hour * 24 * 7, } suite.keeper.SetCommittee(suite.ctx, normalCom) @@ -51,7 +55,7 @@ func (suite *ModuleTestSuite) TestBeginBlock() { suite.NoError(err) // 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() { committee.BeginBlocker(proposalDurationLaterCtx, abci.RequestBeginBlock{}, suite.keeper) }) diff --git a/x/committee/alias.go b/x/committee/alias.go index 9fe5aae4..1fe2eab5 100644 --- a/x/committee/alias.go +++ b/x/committee/alias.go @@ -14,6 +14,7 @@ const ( DefaultNextProposalID = types.DefaultNextProposalID DefaultParamspace = types.DefaultParamspace EventTypeSubmitProposal = types.EventTypeSubmitProposal + MaxDescriptionLength = types.MaxDescriptionLength ModuleName = types.ModuleName ProposalTypeCommitteeChange = types.ProposalTypeCommitteeChange ProposalTypeCommitteeDelete = types.ProposalTypeCommitteeDelete @@ -53,14 +54,12 @@ var ( Uint64FromBytes = types.Uint64FromBytes // variable aliases - ProposalHandler = client.ProposalHandler - CommitteeKeyPrefix = types.CommitteeKeyPrefix - MaxProposalDuration = types.MaxProposalDuration - ModuleCdc = types.ModuleCdc - NextProposalIDKey = types.NextProposalIDKey - ProposalKeyPrefix = types.ProposalKeyPrefix - VoteKeyPrefix = types.VoteKeyPrefix - VoteThreshold = types.VoteThreshold + ProposalHandler = client.ProposalHandler + CommitteeKeyPrefix = types.CommitteeKeyPrefix + ModuleCdc = types.ModuleCdc + NextProposalIDKey = types.NextProposalIDKey + ProposalKeyPrefix = types.ProposalKeyPrefix + VoteKeyPrefix = types.VoteKeyPrefix ) type ( diff --git a/x/committee/client/cli/tx.go b/x/committee/client/cli/tx.go index ce6bed5b..2261a9c1 100644 --- a/x/committee/client/cli/tx.go +++ b/x/committee/client/cli/tx.go @@ -4,6 +4,7 @@ import ( "fmt" "io/ioutil" "strconv" + "time" "github.com/spf13/cobra" @@ -189,8 +190,15 @@ func mustGetExampleCommitteeChangeProposal(cdc *codec.Codec) string { "A description of this proposal.", types.NewCommittee( 1, + "The description of this committee.", []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, "", " ") diff --git a/x/committee/keeper/keeper_test.go b/x/committee/keeper/keeper_test.go index f57e380a..90f624a7 100644 --- a/x/committee/keeper/keeper_test.go +++ b/x/committee/keeper/keeper_test.go @@ -2,10 +2,12 @@ package keeper_test import ( "testing" + "time" "github.com/stretchr/testify/suite" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/gov" abci "github.com/tendermint/tendermint/abci/types" "github.com/kava-labs/kava/app" @@ -13,6 +15,8 @@ import ( "github.com/kava-labs/kava/x/committee/types" ) +func d(s string) sdk.Dec { return sdk.MustNewDecFromStr(s) } + type KeeperTestSuite struct { suite.Suite @@ -33,8 +37,12 @@ func (suite *KeeperTestSuite) SetupTest() { func (suite *KeeperTestSuite) TestGetSetDeleteCommittee() { // setup test com := types.Committee{ - ID: 12, - // TODO other fields + ID: 12, + 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 @@ -56,8 +64,10 @@ func (suite *KeeperTestSuite) TestGetSetDeleteCommittee() { func (suite *KeeperTestSuite) TestGetSetProposal() { // test setup prop := types.Proposal{ - ID: 12, - // TODO other fields + ID: 12, + 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 @@ -81,7 +91,6 @@ func (suite *KeeperTestSuite) TestGetSetVote() { vote := types.Vote{ ProposalID: 12, Voter: suite.addresses[0], - // TODO other fields } // write and read from store diff --git a/x/committee/keeper/proposal.go b/x/committee/keeper/proposal.go index 6aeefeca..ad0e3ced 100644 --- a/x/committee/keeper/proposal.go +++ b/x/committee/keeper/proposal.go @@ -30,7 +30,7 @@ func (k Keeper) SubmitProposal(ctx sdk.Context, proposer sdk.AccAddress, committ } // 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) if err != nil { return 0, err @@ -82,7 +82,7 @@ func (k Keeper) GetProposalResult(ctx sdk.Context, proposalID uint64) (bool, sdk 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 } diff --git a/x/committee/keeper/proposal_test.go b/x/committee/keeper/proposal_test.go index c61c3c54..6bf59141 100644 --- a/x/committee/keeper/proposal_test.go +++ b/x/committee/keeper/proposal_test.go @@ -16,15 +16,15 @@ import ( func (suite *KeeperTestSuite) TestSubmitProposal() { normalCom := types.Committee{ - ID: 12, - Members: suite.addresses[:2], - Permissions: []types.Permission{types.GodPermission{}}, - } - noPermissionsCom := types.Committee{ - ID: 12, - Members: suite.addresses[:2], - Permissions: []types.Permission{}, + ID: 12, + Description: "This committee is for testing.", + Members: suite.addresses[:2], + Permissions: []types.Permission{types.GodPermission{}}, + VoteThreshold: d("0.667"), + MaxProposalDuration: time.Hour * 24 * 7, } + noPermissionsCom := normalCom + noPermissionsCom.Permissions = []types.Permission{} testcases := []struct { name string @@ -96,7 +96,7 @@ func (suite *KeeperTestSuite) TestSubmitProposal() { pr, found := keeper.GetProposal(ctx, id) suite.True(found) 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 { suite.NotNil(err) } @@ -141,7 +141,7 @@ func (suite *KeeperTestSuite) TestAddVote() { name: "proposal expired", proposalID: types.DefaultNextProposalID, voter: normalCom.Members[0], - voteTime: firstBlockTime.Add(types.MaxProposalDuration), + voteTime: firstBlockTime.Add(normalCom.MaxProposalDuration), expectPass: false, }, } @@ -174,6 +174,14 @@ func (suite *KeeperTestSuite) TestAddVote() { } 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 firstBlockTime := time.Date(1998, time.January, 1, 1, 0, 0, 0, time.UTC) @@ -185,12 +193,8 @@ func (suite *KeeperTestSuite) TestGetProposalResult() { expectPass bool }{ { - name: "enough votes", - committee: types.Committee{ - ID: 12, - Members: suite.addresses[:5], - Permissions: []types.Permission{types.GodPermission{}}, - }, + name: "enough votes", + committee: normalCom, votes: []types.Vote{ {ProposalID: defaultID, Voter: suite.addresses[0]}, {ProposalID: defaultID, Voter: suite.addresses[1]}, @@ -201,12 +205,8 @@ func (suite *KeeperTestSuite) TestGetProposalResult() { expectPass: true, }, { - name: "not enough votes", - committee: types.Committee{ - ID: 12, - Members: suite.addresses[:5], - Permissions: []types.Permission{types.GodPermission{}}, - }, + name: "not enough votes", + committee: normalCom, votes: []types.Vote{ {ProposalID: defaultID, Voter: suite.addresses[0]}, }, diff --git a/x/committee/keeper/querier_test.go b/x/committee/keeper/querier_test.go index 2d532d23..92cfcb2f 100644 --- a/x/committee/keeper/querier_test.go +++ b/x/committee/keeper/querier_test.go @@ -3,6 +3,7 @@ package keeper_test import ( "strings" "testing" + "time" "github.com/stretchr/testify/suite" @@ -12,6 +13,7 @@ import ( abci "github.com/tendermint/tendermint/abci/types" "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/types" ) @@ -20,6 +22,12 @@ const ( 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 { suite.Suite @@ -30,10 +38,9 @@ type QuerierTestSuite struct { querier sdk.Querier - addresses []sdk.AccAddress - committees []types.Committee - proposals []types.Proposal - votes map[uint64]([]types.Vote) + addresses []sdk.AccAddress + testGenesis types.GenesisState + votes map[uint64]([]types.Vote) } func (suite *QuerierTestSuite) SetupTest() { @@ -44,37 +51,40 @@ func (suite *QuerierTestSuite) SetupTest() { suite.querier = keeper.NewQuerier(suite.keeper) _, suite.addresses = app.GeneratePrivKeyAddressPairs(5) - suite.app.InitializeFromGenesisStates() - // TODO replace below with genesis state - normalCom := types.Committee{ - ID: 12, - Members: suite.addresses[:2], - Permissions: []types.Permission{types.GodPermission{}}, - } - suite.keeper.SetCommittee(suite.ctx, normalCom) + suite.testGenesis = types.NewGenesisState( + 3, + []types.Committee{ + { + ID: 1, + Description: "This committee is for testing.", + Members: suite.addresses[:3], + 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.") - 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 - }) + // Collect up votes into a map indexed by proposalID for convenience suite.votes = map[uint64]([]types.Vote){} suite.keeper.IterateProposals(suite.ctx, func(p types.Proposal) 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)) // Check - suite.Equal(suite.committees, committees) + suite.Equal(suite.testGenesis.Committees, committees) } func (suite *QuerierTestSuite) TestQueryCommittee() { @@ -110,7 +120,7 @@ func (suite *QuerierTestSuite) TestQueryCommittee() { // Set up request query query := abci.RequestQuery{ 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 @@ -123,13 +133,13 @@ func (suite *QuerierTestSuite) TestQueryCommittee() { suite.NoError(suite.cdc.UnmarshalJSON(bz, &committee)) // Check - suite.Equal(suite.committees[0], committee) + suite.Equal(suite.testGenesis.Committees[0], committee) } func (suite *QuerierTestSuite) TestQueryProposals() { ctx := suite.ctx.WithIsCheckTx(false) // Set up request query - comID := suite.proposals[0].CommitteeID + comID := suite.testGenesis.Proposals[0].CommitteeID query := abci.RequestQuery{ Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryProposals}, "/"), Data: suite.cdc.MustMarshalJSON(types.NewQueryCommitteeParams(comID)), @@ -146,7 +156,7 @@ func (suite *QuerierTestSuite) TestQueryProposals() { // Check expectedProposals := []types.Proposal{} - for _, p := range suite.proposals { + for _, p := range suite.testGenesis.Proposals { if p.CommitteeID == comID { expectedProposals = append(expectedProposals, p) } @@ -159,7 +169,7 @@ func (suite *QuerierTestSuite) TestQueryProposal() { // Set up request query query := abci.RequestQuery{ 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 @@ -172,13 +182,13 @@ func (suite *QuerierTestSuite) TestQueryProposal() { suite.NoError(suite.cdc.UnmarshalJSON(bz, &proposal)) // Check - suite.Equal(suite.proposals[0], proposal) + suite.Equal(suite.testGenesis.Proposals[0], proposal) } func (suite *QuerierTestSuite) TestQueryVotes() { ctx := suite.ctx.WithIsCheckTx(false) // Set up request query - propID := suite.proposals[0].ID + propID := suite.testGenesis.Proposals[0].ID query := abci.RequestQuery{ Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryVotes}, "/"), Data: suite.cdc.MustMarshalJSON(types.NewQueryProposalParams(propID)), @@ -200,7 +210,7 @@ func (suite *QuerierTestSuite) TestQueryVotes() { func (suite *QuerierTestSuite) TestQueryVote() { ctx := suite.ctx.WithIsCheckTx(false) // ? // Set up request query - propID := suite.proposals[0].ID + propID := suite.testGenesis.Proposals[0].ID query := abci.RequestQuery{ Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryVote}, "/"), Data: suite.cdc.MustMarshalJSON(types.NewQueryVoteParams(propID, suite.votes[propID][0].Voter)), @@ -222,7 +232,7 @@ func (suite *QuerierTestSuite) TestQueryVote() { func (suite *QuerierTestSuite) TestQueryTally() { ctx := suite.ctx.WithIsCheckTx(false) // ? // Set up request query - propID := suite.proposals[0].ID + propID := suite.testGenesis.Proposals[0].ID query := abci.RequestQuery{ Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryTally}, "/"), Data: suite.cdc.MustMarshalJSON(types.NewQueryProposalParams(propID)), diff --git a/x/committee/proposal_handler_test.go b/x/committee/proposal_handler_test.go index c5150462..f9c58263 100644 --- a/x/committee/proposal_handler_test.go +++ b/x/committee/proposal_handler_test.go @@ -38,14 +38,19 @@ func (suite *ProposalHandlerTestSuite) SetupTest() { 2, []committee.Committee{ { - ID: 1, - Members: suite.addresses[:3], - Permissions: []types.Permission{types.GodPermission{}}, + ID: 1, + Description: "This committee is for testing.", + Members: suite.addresses[:3], + Permissions: []types.Permission{types.GodPermission{}}, + VoteThreshold: d("0.667"), + MaxProposalDuration: time.Hour * 24 * 7, }, { - ID: 2, - Members: suite.addresses[2:], - Permissions: nil, + ID: 2, + Members: suite.addresses[2:], + Permissions: nil, + VoteThreshold: d("0.667"), + MaxProposalDuration: time.Hour * 24 * 7, }, }, []committee.Proposal{ @@ -69,8 +74,10 @@ func (suite *ProposalHandlerTestSuite) TestProposalHandler_ChangeCommittee() { "A Title", "A proposal description.", committee.Committee{ - ID: 34, - Members: suite.addresses[:1], + ID: 34, + Members: suite.addresses[:1], + VoteThreshold: d("1"), + MaxProposalDuration: time.Hour * 24, }, ), expectPass: true, @@ -81,9 +88,11 @@ func (suite *ProposalHandlerTestSuite) TestProposalHandler_ChangeCommittee() { "A Title", "A proposal description.", committee.Committee{ - ID: 1, - Members: suite.addresses, - Permissions: suite.testGenesis.Committees[0].Permissions, + ID: suite.testGenesis.Committees[0].ID, + Members: suite.addresses, // add new members + Permissions: suite.testGenesis.Committees[0].Permissions, + VoteThreshold: suite.testGenesis.Committees[0].VoteThreshold, + MaxProposalDuration: suite.testGenesis.Committees[0].MaxProposalDuration, }, ), expectPass: true, @@ -93,9 +102,7 @@ func (suite *ProposalHandlerTestSuite) TestProposalHandler_ChangeCommittee() { 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 proposal description.", - committee.Committee{ - ID: 34, - }, + suite.testGenesis.Committees[0], ), expectPass: false, }, @@ -105,9 +112,11 @@ func (suite *ProposalHandlerTestSuite) TestProposalHandler_ChangeCommittee() { "A Title", "A proposal description.", committee.Committee{ - ID: 1, - Members: append(suite.addresses, suite.addresses[0]), // duplicate address - Permissions: suite.testGenesis.Committees[0].Permissions, + ID: suite.testGenesis.Committees[0].ID, + Members: append(suite.addresses, suite.addresses[0]), // duplicate address + Permissions: suite.testGenesis.Committees[0].Permissions, + VoteThreshold: suite.testGenesis.Committees[0].VoteThreshold, + MaxProposalDuration: suite.testGenesis.Committees[0].MaxProposalDuration, }, ), expectPass: false, diff --git a/x/committee/types/genesis_test.go b/x/committee/types/genesis_test.go index 9b76d4f2..009b5371 100644 --- a/x/committee/types/genesis_test.go +++ b/x/committee/types/genesis_test.go @@ -11,6 +11,7 @@ import ( "github.com/tendermint/tendermint/crypto" ) +func d(s string) sdk.Dec { return sdk.MustNewDecFromStr(s) } func TestGenesisState_Validate(t *testing.T) { testTime := time.Date(1998, time.January, 1, 0, 0, 0, 0, time.UTC) addresses := []sdk.AccAddress{ @@ -24,14 +25,20 @@ func TestGenesisState_Validate(t *testing.T) { NextProposalID: 2, Committees: []Committee{ { - ID: 1, - Members: addresses[:3], - Permissions: []Permission{GodPermission{}}, + ID: 1, + Description: "This committee is for testing.", + Members: addresses[:3], + Permissions: []Permission{GodPermission{}}, + VoteThreshold: d("0.667"), + MaxProposalDuration: time.Hour * 24 * 7, }, { - ID: 2, - Members: addresses[2:], - Permissions: nil, + ID: 2, + Description: "This committee is also for testing.", + Members: addresses[2:], + Permissions: nil, + VoteThreshold: d("0.8"), + MaxProposalDuration: time.Hour * 24 * 21, }, }, Proposals: []Proposal{ diff --git a/x/committee/types/permissions.go b/x/committee/types/permissions.go index 2481587e..f3ae5640 100644 --- a/x/committee/types/permissions.go +++ b/x/committee/types/permissions.go @@ -16,6 +16,7 @@ func init() { } // GodPermission allows any governance proposal. It is used mainly for testing. +// TODO better name? type GodPermission struct{} var _ Permission = GodPermission{} diff --git a/x/committee/types/types.go b/x/committee/types/types.go index ab1c3ede..4665a044 100644 --- a/x/committee/types/types.go +++ b/x/committee/types/types.go @@ -9,29 +9,28 @@ import ( "github.com/cosmos/cosmos-sdk/x/gov" ) -// TODO move these into params -var ( - VoteThreshold sdk.Dec = sdk.MustNewDecFromStr("0.75") - MaxProposalDuration time.Duration = time.Hour * 24 * 7 -) +const MaxCommitteeDescriptionLength int = 5000 // -------- Committees -------- // A Committee is a collection of addresses that are allowed to vote and enact any governance proposal that passes their permissions. type Committee struct { - ID uint64 `json:"id" yaml:"id"` - //Description string `json:"description" yaml:"description"` - Members []sdk.AccAddress `json:"members" yaml:"members"` - Permissions []Permission `json:"permissions" yaml:"permissions"` - // VoteThreshold sdk.Dec `json:"vote_threshold" yaml:"vote_threshold"` - // MaxProposalDuration time.Duration `json:"max_proposal_duration" yaml:"max_proposal_duration"` + ID uint64 `json:"id" yaml:"id"` + Description string `json:"description" yaml:"description"` + Members []sdk.AccAddress `json:"members" yaml:"members"` + Permissions []Permission `json:"permissions" yaml:"permissions"` + VoteThreshold sdk.Dec `json:"vote_threshold" yaml:"vote_threshold"` + 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{ - ID: id, - Members: members, - Permissions: permissions, + ID: id, + Description: description, + Members: members, + Permissions: permissions, + VoteThreshold: threshold, + MaxProposalDuration: duration, } } @@ -74,6 +73,19 @@ func (c Committee) Validate() error { if len(c.Members) == 0 { 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 } @@ -118,5 +130,4 @@ func (p Proposal) String() string { type Vote struct { ProposalID uint64 `json:"proposal_id" yaml:"proposal_id"` Voter sdk.AccAddress `json:"voter" yaml:"voter"` - // Option byte // TODO for now don't need more than just a yes as options }