mirror of
https://github.com/0glabs/0g-chain.git
synced 2024-12-23 23:05:18 +00:00
add committee change gov proposals
This commit is contained in:
parent
18dfcd2a3d
commit
fbf67b4527
@ -8,40 +8,45 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
AttributeKeyProposalID = types.AttributeKeyProposalID
|
||||
DefaultNextProposalID = types.DefaultNextProposalID
|
||||
DefaultParamspace = types.DefaultParamspace
|
||||
EventTypeSubmitProposal = types.EventTypeSubmitProposal
|
||||
ModuleName = types.ModuleName
|
||||
QuerierRoute = types.QuerierRoute
|
||||
QueryCommittee = types.QueryCommittee
|
||||
QueryCommittees = types.QueryCommittees
|
||||
QueryProposal = types.QueryProposal
|
||||
QueryProposals = types.QueryProposals
|
||||
QueryTally = types.QueryTally
|
||||
QueryVote = types.QueryVote
|
||||
QueryVotes = types.QueryVotes
|
||||
RouterKey = types.RouterKey
|
||||
StoreKey = types.StoreKey
|
||||
TypeMsgSubmitProposal = types.TypeMsgSubmitProposal
|
||||
TypeMsgVote = types.TypeMsgVote
|
||||
AttributeKeyProposalID = types.AttributeKeyProposalID
|
||||
DefaultCodespace = types.DefaultCodespace
|
||||
DefaultNextProposalID = types.DefaultNextProposalID
|
||||
DefaultParamspace = types.DefaultParamspace
|
||||
EventTypeSubmitProposal = types.EventTypeSubmitProposal
|
||||
ModuleName = types.ModuleName
|
||||
ProposalTypeCommitteeChange = types.ProposalTypeCommitteeChange
|
||||
ProposalTypeCommitteeDelete = types.ProposalTypeCommitteeDelete
|
||||
QuerierRoute = types.QuerierRoute
|
||||
QueryCommittee = types.QueryCommittee
|
||||
QueryCommittees = types.QueryCommittees
|
||||
QueryProposal = types.QueryProposal
|
||||
QueryProposals = types.QueryProposals
|
||||
QueryTally = types.QueryTally
|
||||
QueryVote = types.QueryVote
|
||||
QueryVotes = types.QueryVotes
|
||||
RouterKey = types.RouterKey
|
||||
StoreKey = types.StoreKey
|
||||
TypeMsgSubmitProposal = types.TypeMsgSubmitProposal
|
||||
TypeMsgVote = types.TypeMsgVote
|
||||
)
|
||||
|
||||
var (
|
||||
// function aliases
|
||||
NewKeeper = keeper.NewKeeper
|
||||
NewQuerier = keeper.NewQuerier
|
||||
DefaultGenesisState = types.DefaultGenesisState
|
||||
GetKeyFromID = types.GetKeyFromID
|
||||
GetVoteKey = types.GetVoteKey
|
||||
NewGenesisState = types.NewGenesisState
|
||||
NewMsgSubmitProposal = types.NewMsgSubmitProposal
|
||||
NewMsgVote = types.NewMsgVote
|
||||
NewQueryCommitteeParams = types.NewQueryCommitteeParams
|
||||
NewQueryProposalParams = types.NewQueryProposalParams
|
||||
NewQueryVoteParams = types.NewQueryVoteParams
|
||||
RegisterCodec = types.RegisterCodec
|
||||
Uint64FromBytes = types.Uint64FromBytes
|
||||
NewKeeper = keeper.NewKeeper
|
||||
NewQuerier = keeper.NewQuerier
|
||||
DefaultGenesisState = types.DefaultGenesisState
|
||||
GetKeyFromID = types.GetKeyFromID
|
||||
GetVoteKey = types.GetVoteKey
|
||||
NewCommitteeChangeProposal = types.NewCommitteeChangeProposal
|
||||
NewCommitteeDeleteProposal = types.NewCommitteeDeleteProposal
|
||||
NewGenesisState = types.NewGenesisState
|
||||
NewMsgSubmitProposal = types.NewMsgSubmitProposal
|
||||
NewMsgVote = types.NewMsgVote
|
||||
NewQueryCommitteeParams = types.NewQueryCommitteeParams
|
||||
NewQueryProposalParams = types.NewQueryProposalParams
|
||||
NewQueryVoteParams = types.NewQueryVoteParams
|
||||
RegisterCodec = types.RegisterCodec
|
||||
Uint64FromBytes = types.Uint64FromBytes
|
||||
|
||||
// variable aliases
|
||||
CommitteeKeyPrefix = types.CommitteeKeyPrefix
|
||||
@ -56,10 +61,11 @@ var (
|
||||
type (
|
||||
Keeper = keeper.Keeper
|
||||
Committee = types.Committee
|
||||
CommitteeChangeProposal = types.CommitteeChangeProposal
|
||||
CommitteeDeleteProposal = types.CommitteeDeleteProposal
|
||||
GeneralShutdownPermission = types.GeneralShutdownPermission
|
||||
GenesisState = types.GenesisState
|
||||
GodPermission = types.GodPermission
|
||||
GroupChangeProposal = types.GroupChangeProposal
|
||||
InflationRateChangePermission = types.InflationRateChangePermission
|
||||
MsgSubmitProposal = types.MsgSubmitProposal
|
||||
MsgVote = types.MsgVote
|
||||
|
@ -99,11 +99,7 @@ func (k Keeper) CloseOutProposal(ctx sdk.Context, proposalID uint64) sdk.Error {
|
||||
}
|
||||
if proposalPasses || pr.HasExpiredBy(ctx.BlockTime()) {
|
||||
|
||||
// delete proposal and votes
|
||||
k.DeleteProposal(ctx, proposalID)
|
||||
for _, v := range votes {
|
||||
k.DeleteVote(ctx, v.ProposalID, v.Voter)
|
||||
}
|
||||
k.DeleteProposalAndVotes(ctx, proposalID)
|
||||
return nil
|
||||
}
|
||||
return sdk.ErrInternal("note enough votes to close proposal")
|
||||
@ -132,3 +128,16 @@ func (k Keeper) ValidatePubProposal(ctx sdk.Context, pubProposal types.PubPropos
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (k Keeper) DeleteProposalAndVotes(ctx sdk.Context, proposalID uint64) {
|
||||
var votes []types.Vote
|
||||
k.IterateVotes(ctx, proposalID, func(vote types.Vote) bool {
|
||||
votes = append(votes, vote)
|
||||
return false
|
||||
})
|
||||
|
||||
k.DeleteProposal(ctx, proposalID)
|
||||
for _, v := range votes {
|
||||
k.DeleteVote(ctx, v.ProposalID, v.Voter)
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,66 @@
|
||||
package committee
|
||||
|
||||
// TODO create a GroupChangeProposalHandler, see params or distribution
|
||||
// It will overwrite the Members of Permissions field of a group
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"
|
||||
)
|
||||
|
||||
func NewProposalHandler(k Keeper) govtypes.Handler {
|
||||
return func(ctx sdk.Context, content govtypes.Content) sdk.Error {
|
||||
switch c := content.(type) {
|
||||
case CommitteeChangeProposal:
|
||||
return handleCommitteeChangeProposal(ctx, k, c)
|
||||
case CommitteeDeleteProposal:
|
||||
return handleCommitteeDeleteProposal(ctx, k, c)
|
||||
|
||||
default:
|
||||
errMsg := fmt.Sprintf("unrecognized %s proposal content type: %T", ModuleName, c)
|
||||
return sdk.ErrUnknownRequest(errMsg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func handleCommitteeChangeProposal(ctx sdk.Context, k Keeper, committeeProposal CommitteeChangeProposal) sdk.Error {
|
||||
if err := committeeProposal.ValidateBasic(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Remove all committee's ongoing proposals
|
||||
var proposals []Proposal
|
||||
k.IterateProposals(ctx, func(p Proposal) bool {
|
||||
if p.CommitteeID == committeeProposal.NewCommittee.ID {
|
||||
proposals = append(proposals, p)
|
||||
}
|
||||
return false
|
||||
})
|
||||
for _, p := range proposals { // split loops to avoid updating the db while iterating
|
||||
k.DeleteProposalAndVotes(ctx, p.ID)
|
||||
}
|
||||
|
||||
// update/create the committee
|
||||
k.SetCommittee(ctx, committeeProposal.NewCommittee)
|
||||
return nil
|
||||
}
|
||||
|
||||
func handleCommitteeDeleteProposal(ctx sdk.Context, k Keeper, committeeProposal CommitteeDeleteProposal) sdk.Error {
|
||||
if err := committeeProposal.ValidateBasic(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Remove all committee's ongoing proposals
|
||||
var proposals []Proposal
|
||||
k.IterateProposals(ctx, func(p Proposal) bool {
|
||||
if p.CommitteeID == committeeProposal.CommitteeID {
|
||||
proposals = append(proposals, p)
|
||||
}
|
||||
return false
|
||||
})
|
||||
for _, p := range proposals { // split loops to avoid updating the db while iterating
|
||||
k.DeleteProposalAndVotes(ctx, p.ID)
|
||||
}
|
||||
|
||||
k.DeleteCommittee(ctx, committeeProposal.CommitteeID)
|
||||
return nil
|
||||
}
|
||||
|
247
x/committee/proposal_handler_test.go
Normal file
247
x/committee/proposal_handler_test.go
Normal file
@ -0,0 +1,247 @@
|
||||
package committee_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/gov"
|
||||
"github.com/stretchr/testify/suite"
|
||||
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/types"
|
||||
)
|
||||
|
||||
var testTime time.Time = time.Date(1998, time.January, 1, 0, 0, 0, 0, time.UTC)
|
||||
|
||||
func NewCommitteeGenState(cdc *codec.Codec, gs committee.GenesisState) app.GenesisState {
|
||||
return app.GenesisState{committee.ModuleName: cdc.MustMarshalJSON(gs)}
|
||||
}
|
||||
|
||||
type ProposalHandlerTestSuite struct {
|
||||
suite.Suite
|
||||
|
||||
keeper committee.Keeper
|
||||
app app.TestApp
|
||||
ctx sdk.Context
|
||||
|
||||
addresses []sdk.AccAddress
|
||||
testGenesis committee.GenesisState
|
||||
}
|
||||
|
||||
func (suite *ProposalHandlerTestSuite) SetupTest() {
|
||||
_, suite.addresses = app.GeneratePrivKeyAddressPairs(5)
|
||||
suite.testGenesis = committee.NewGenesisState(
|
||||
2,
|
||||
[]committee.Committee{
|
||||
{
|
||||
ID: 1,
|
||||
Members: suite.addresses[:3],
|
||||
Permissions: []types.Permission{types.GodPermission{}},
|
||||
},
|
||||
{
|
||||
ID: 2,
|
||||
Members: suite.addresses[2:],
|
||||
Permissions: nil,
|
||||
},
|
||||
},
|
||||
[]committee.Proposal{
|
||||
{ID: 1, CommitteeID: 1, PubProposal: gov.NewTextProposal("A Title", "A description of this proposal."), Deadline: testTime.Add(7 * 24 * time.Hour)},
|
||||
},
|
||||
[]committee.Vote{
|
||||
{ProposalID: 1, Voter: suite.addresses[0]},
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func (suite *ProposalHandlerTestSuite) TestProposalHandler_ChangeCommittee() {
|
||||
testCases := []struct {
|
||||
name string
|
||||
proposal committee.CommitteeChangeProposal
|
||||
expectPass bool
|
||||
}{
|
||||
{
|
||||
name: "add new",
|
||||
proposal: committee.NewCommitteeChangeProposal(
|
||||
"A Title",
|
||||
"A proposal description.",
|
||||
committee.Committee{
|
||||
ID: 34,
|
||||
},
|
||||
),
|
||||
expectPass: true,
|
||||
},
|
||||
{
|
||||
name: "update",
|
||||
proposal: committee.NewCommitteeChangeProposal(
|
||||
"A Title",
|
||||
"A proposal description.",
|
||||
committee.Committee{
|
||||
ID: 1,
|
||||
Members: suite.addresses,
|
||||
Permissions: suite.testGenesis.Committees[0].Permissions,
|
||||
},
|
||||
),
|
||||
expectPass: true,
|
||||
},
|
||||
{
|
||||
name: "invalid title",
|
||||
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,
|
||||
},
|
||||
),
|
||||
expectPass: false,
|
||||
},
|
||||
{
|
||||
name: "invalid committee",
|
||||
proposal: committee.NewCommitteeChangeProposal(
|
||||
"A Title",
|
||||
"A proposal description.",
|
||||
committee.Committee{
|
||||
ID: 1,
|
||||
Members: append(suite.addresses, suite.addresses[0]), // duplicate address
|
||||
Permissions: suite.testGenesis.Committees[0].Permissions,
|
||||
},
|
||||
),
|
||||
expectPass: false,
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
suite.Run(tc.name, func() {
|
||||
// Setup
|
||||
suite.app = app.NewTestApp()
|
||||
suite.keeper = suite.app.GetCommitteeKeeper()
|
||||
suite.app = suite.app.InitializeFromGenesisStates(
|
||||
NewCommitteeGenState(suite.app.Codec(), suite.testGenesis),
|
||||
)
|
||||
suite.ctx = suite.app.NewContext(true, abci.Header{Height: 1, Time: testTime})
|
||||
handler := committee.NewProposalHandler(suite.keeper)
|
||||
|
||||
// get proposals and votes for target committee
|
||||
var proposals []committee.Proposal
|
||||
var votes []committee.Vote
|
||||
suite.keeper.IterateProposals(suite.ctx, func(p committee.Proposal) bool {
|
||||
if p.CommitteeID == tc.proposal.NewCommittee.ID {
|
||||
proposals = append(proposals, p)
|
||||
suite.keeper.IterateVotes(suite.ctx, p.ID, func(v committee.Vote) bool {
|
||||
votes = append(votes, v)
|
||||
return false
|
||||
})
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
// Run
|
||||
err := handler(suite.ctx, tc.proposal)
|
||||
|
||||
// Check
|
||||
if tc.expectPass {
|
||||
suite.NoError(err)
|
||||
// check proposal is accurate
|
||||
actualCom, found := suite.keeper.GetCommittee(suite.ctx, tc.proposal.NewCommittee.ID)
|
||||
suite.True(found)
|
||||
suite.Equal(tc.proposal.NewCommittee, actualCom)
|
||||
|
||||
// check proposals and votes for this committee have been removed
|
||||
for _, p := range proposals {
|
||||
_, found := suite.keeper.GetProposal(suite.ctx, p.ID)
|
||||
suite.False(found)
|
||||
}
|
||||
for _, v := range votes {
|
||||
_, found := suite.keeper.GetVote(suite.ctx, v.ProposalID, v.Voter)
|
||||
suite.False(found)
|
||||
}
|
||||
} else {
|
||||
suite.Error(err)
|
||||
suite.Equal(suite.testGenesis, committee.ExportGenesis(suite.ctx, suite.keeper))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *ProposalHandlerTestSuite) TestProposalHandler_DeleteCommittee() {
|
||||
testCases := []struct {
|
||||
name string
|
||||
proposal committee.CommitteeDeleteProposal
|
||||
expectPass bool
|
||||
}{
|
||||
{
|
||||
name: "normal",
|
||||
proposal: committee.NewCommitteeDeleteProposal(
|
||||
"A Title",
|
||||
"A proposal description.",
|
||||
suite.testGenesis.Committees[0].ID,
|
||||
),
|
||||
expectPass: true,
|
||||
},
|
||||
{
|
||||
name: "invalid title",
|
||||
proposal: committee.NewCommitteeDeleteProposal(
|
||||
"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.",
|
||||
suite.testGenesis.Committees[1].ID,
|
||||
),
|
||||
expectPass: false,
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
suite.Run(tc.name, func() {
|
||||
// Setup
|
||||
suite.app = app.NewTestApp()
|
||||
suite.keeper = suite.app.GetCommitteeKeeper()
|
||||
suite.app = suite.app.InitializeFromGenesisStates(
|
||||
NewCommitteeGenState(suite.app.Codec(), suite.testGenesis),
|
||||
)
|
||||
suite.ctx = suite.app.NewContext(true, abci.Header{Height: 1, Time: testTime})
|
||||
handler := committee.NewProposalHandler(suite.keeper)
|
||||
|
||||
// get proposals and votes for target committee
|
||||
var proposals []committee.Proposal
|
||||
var votes []committee.Vote
|
||||
suite.keeper.IterateProposals(suite.ctx, func(p committee.Proposal) bool {
|
||||
if p.CommitteeID == tc.proposal.CommitteeID {
|
||||
proposals = append(proposals, p)
|
||||
suite.keeper.IterateVotes(suite.ctx, p.ID, func(v committee.Vote) bool {
|
||||
votes = append(votes, v)
|
||||
return false
|
||||
})
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
// Run
|
||||
err := handler(suite.ctx, tc.proposal)
|
||||
|
||||
// Check
|
||||
if tc.expectPass {
|
||||
suite.NoError(err)
|
||||
// check proposal is accurate
|
||||
_, found := suite.keeper.GetCommittee(suite.ctx, tc.proposal.CommitteeID)
|
||||
suite.False(found)
|
||||
|
||||
// check proposals and votes for this committee have been removed
|
||||
for _, p := range proposals {
|
||||
_, found := suite.keeper.GetProposal(suite.ctx, p.ID)
|
||||
suite.False(found)
|
||||
}
|
||||
for _, v := range votes {
|
||||
_, found := suite.keeper.GetVote(suite.ctx, v.ProposalID, v.Voter)
|
||||
suite.False(found)
|
||||
}
|
||||
} else {
|
||||
suite.Error(err)
|
||||
suite.Equal(suite.testGenesis, committee.ExportGenesis(suite.ctx, suite.keeper))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestProposalHandlerTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(ProposalHandlerTestSuite))
|
||||
}
|
@ -2,6 +2,9 @@ package types
|
||||
|
||||
import (
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
"github.com/cosmos/cosmos-sdk/x/distribution"
|
||||
"github.com/cosmos/cosmos-sdk/x/gov"
|
||||
"github.com/cosmos/cosmos-sdk/x/params"
|
||||
)
|
||||
|
||||
// ModuleCdc generic sealed codec to be used throughout module
|
||||
@ -9,6 +12,13 @@ var ModuleCdc *codec.Codec
|
||||
|
||||
func init() {
|
||||
cdc := codec.New()
|
||||
cdc.RegisterInterface((*gov.Content)(nil), nil) // registering the Content interface on the ModuleCdc will not conflict with gov.
|
||||
// TODO ideally dist and params would register their proposals on here at their init. However can't change them so:
|
||||
cdc.RegisterConcrete(distribution.CommunityPoolSpendProposal{}, "cosmos-sdk/CommunityPoolSpendProposal", nil)
|
||||
cdc.RegisterConcrete(params.ParameterChangeProposal{}, "cosmos-sdk/ParameterChangeProposal", nil)
|
||||
cdc.RegisterConcrete(gov.TextProposal{}, "cosmos-sdk/TextProposal", nil)
|
||||
cdc.RegisterConcrete(gov.SoftwareUpgradeProposal{}, "cosmos-sdk/SoftwareUpgradeProposal", nil)
|
||||
|
||||
RegisterCodec(cdc)
|
||||
ModuleCdc = cdc.Seal()
|
||||
}
|
||||
@ -16,9 +26,15 @@ func init() {
|
||||
// RegisterCodec registers the necessary types for the module
|
||||
func RegisterCodec(cdc *codec.Codec) {
|
||||
|
||||
// TODO need to register Content interface, however amino panics if you try and register it twice and helpfully doesn't provide a way to query registered types
|
||||
// The app codec needs the gov.Content type registered. This is done by the gov module.
|
||||
// Ideally it would registered here as well in case these modules are ever used separately.
|
||||
// However amino panics if you register the same interface a second time. So leaving it out for now.
|
||||
|
||||
//cdc.RegisterInterface((*gov.Content)(nil), nil)
|
||||
|
||||
cdc.RegisterConcrete(CommitteeChangeProposal{}, "kava/CommitteeChangeProposal", nil)
|
||||
cdc.RegisterConcrete(CommitteeDeleteProposal{}, "kava/CommitteeDeleteProposal", nil)
|
||||
|
||||
cdc.RegisterInterface((*Permission)(nil), nil)
|
||||
cdc.RegisterConcrete(GodPermission{}, "kava/GodPermission", nil)
|
||||
}
|
||||
|
9
x/committee/types/errors.go
Normal file
9
x/committee/types/errors.go
Normal file
@ -0,0 +1,9 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
const (
|
||||
DefaultCodespace sdk.CodespaceType = ModuleName
|
||||
)
|
120
x/committee/types/gov_proposal.go
Normal file
120
x/committee/types/gov_proposal.go
Normal file
@ -0,0 +1,120 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"gopkg.in/yaml.v2"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"
|
||||
)
|
||||
|
||||
const (
|
||||
ProposalTypeCommitteeChange = "CommitteeChange"
|
||||
ProposalTypeCommitteeDelete = "CommitteeDelete"
|
||||
)
|
||||
|
||||
// CommitteeChangeProposal is a gov proposal for creating a new committee or modifying an existing one.
|
||||
type CommitteeChangeProposal struct {
|
||||
Title string
|
||||
Description string
|
||||
NewCommittee Committee
|
||||
}
|
||||
|
||||
var _ govtypes.Content = CommitteeChangeProposal{}
|
||||
|
||||
func init() {
|
||||
govtypes.RegisterProposalType(ProposalTypeCommitteeChange)
|
||||
govtypes.RegisterProposalTypeCodec(CommitteeChangeProposal{}, "kava/CommitteeChangeProposal")
|
||||
// TODO write these
|
||||
//RegisterProposalType(ProposalTypeCommitteeChange)
|
||||
//RegisterProposalTypeCodec(CommitteeChangeProposal{}, "kava/CommitteeChangeProposal")
|
||||
// How will we register distribution and params proposals on this codec?
|
||||
}
|
||||
|
||||
func NewCommitteeChangeProposal(title string, description string, newCommittee Committee) CommitteeChangeProposal {
|
||||
return CommitteeChangeProposal{
|
||||
Title: title,
|
||||
Description: description,
|
||||
NewCommittee: newCommittee,
|
||||
}
|
||||
}
|
||||
|
||||
// GetTitle returns the title of the proposal.
|
||||
func (ccp CommitteeChangeProposal) GetTitle() string { return ccp.Title }
|
||||
|
||||
// GetDescription returns the description of the proposal.
|
||||
func (ccp CommitteeChangeProposal) GetDescription() string { return ccp.Description }
|
||||
|
||||
// GetDescription returns the routing key of the proposal.
|
||||
func (ccp CommitteeChangeProposal) ProposalRoute() string { return RouterKey }
|
||||
|
||||
// ProposalType returns the type of the proposal.
|
||||
func (ccp CommitteeChangeProposal) ProposalType() string { return ProposalTypeCommitteeChange }
|
||||
|
||||
// ValidateBasic runs basic stateless validity checks
|
||||
func (ccp CommitteeChangeProposal) ValidateBasic() sdk.Error {
|
||||
if err := govtypes.ValidateAbstract(DefaultCodespace, ccp); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ccp.NewCommittee.Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// String implements the Stringer interface.
|
||||
func (ccp CommitteeChangeProposal) String() string {
|
||||
bz, _ := yaml.Marshal(ccp) // TODO test
|
||||
return string(bz)
|
||||
}
|
||||
|
||||
// CommitteeDeleteProposal is a gov proposal for removing a committee.
|
||||
type CommitteeDeleteProposal struct {
|
||||
Title string
|
||||
Description string
|
||||
CommitteeID uint64
|
||||
}
|
||||
|
||||
var _ govtypes.Content = CommitteeDeleteProposal{}
|
||||
|
||||
func init() {
|
||||
govtypes.RegisterProposalType(ProposalTypeCommitteeDelete)
|
||||
govtypes.RegisterProposalTypeCodec(CommitteeDeleteProposal{}, "kava/CommitteeDeleteProposal")
|
||||
// TODO write these
|
||||
//RegisterProposalType(ProposalTypeCommitteeDelete)
|
||||
//RegisterProposalTypeCodec(CommitteeDeleteProposal{}, "kava/CommitteeDeleteProposal")
|
||||
// How will we register distribution and params proposals on this codec?
|
||||
}
|
||||
|
||||
func NewCommitteeDeleteProposal(title string, description string, committeeID uint64) CommitteeDeleteProposal {
|
||||
return CommitteeDeleteProposal{
|
||||
Title: title,
|
||||
Description: description,
|
||||
CommitteeID: committeeID,
|
||||
}
|
||||
}
|
||||
|
||||
// GetTitle returns the title of the proposal.
|
||||
func (cdp CommitteeDeleteProposal) GetTitle() string { return cdp.Title }
|
||||
|
||||
// GetDescription returns the description of the proposal.
|
||||
func (cdp CommitteeDeleteProposal) GetDescription() string { return cdp.Description }
|
||||
|
||||
// GetDescription returns the routing key of the proposal.
|
||||
func (cdp CommitteeDeleteProposal) ProposalRoute() string { return RouterKey }
|
||||
|
||||
// ProposalType returns the type of the proposal.
|
||||
func (cdp CommitteeDeleteProposal) ProposalType() string { return ProposalTypeCommitteeDelete }
|
||||
|
||||
// ValidateBasic runs basic stateless validity checks
|
||||
func (cdp CommitteeDeleteProposal) ValidateBasic() sdk.Error {
|
||||
if err := govtypes.ValidateAbstract(DefaultCodespace, cdp); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// String implements the Stringer interface.
|
||||
func (cdp CommitteeDeleteProposal) String() string {
|
||||
bz, _ := yaml.Marshal(cdp) // TODO test
|
||||
return string(bz)
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
// A gov.Proposal to used to add/remove members from a group, or to add/remove permissions.
|
||||
// Normally registered with standard gov. But could also be registed with committee to allow groups to be controlled by other groups.
|
||||
type GroupChangeProposal struct {
|
||||
Members []sdk.AccAddress
|
||||
Permissions []Permission
|
||||
}
|
@ -44,6 +44,20 @@ func (c Committee) HasPermissionsFor(proposal PubProposal) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (c Committee) Validate() sdk.Error {
|
||||
// check for duplicate addresses
|
||||
addressMap := make(map[string]bool, len(c.Members))
|
||||
for _, m := range c.Members {
|
||||
// check there are no duplicate members
|
||||
if _, ok := addressMap[m.String()]; ok {
|
||||
return sdk.ErrInternal(fmt.Sprintf("duplicate member found in committee, %s", m))
|
||||
}
|
||||
addressMap[m.String()] = true
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Permission is anything with a method that validates whether a proposal is allowed by it or not.
|
||||
type Permission interface {
|
||||
Allows(PubProposal) bool
|
||||
|
Loading…
Reference in New Issue
Block a user