From 77553ed2999d10fdc3d6e48b1a6f8d33821f1420 Mon Sep 17 00:00:00 2001 From: rhuairahrighairigh Date: Fri, 27 Mar 2020 15:27:45 +0000 Subject: [PATCH] improve permission types --- x/committee/alias.go | 42 ++++++------ x/committee/module.go | 2 +- x/committee/types/codec.go | 31 ++++++--- x/committee/types/gov_proposal.go | 14 +--- x/committee/types/permissions.go | 110 +++++++++++++++++++++--------- x/committee/types/types.go | 5 +- 6 files changed, 130 insertions(+), 74 deletions(-) diff --git a/x/committee/alias.go b/x/committee/alias.go index c33b9b01..9fe5aae4 100644 --- a/x/committee/alias.go +++ b/x/committee/alias.go @@ -38,6 +38,7 @@ var ( DefaultGenesisState = types.DefaultGenesisState GetKeyFromID = types.GetKeyFromID GetVoteKey = types.GetVoteKey + NewCommittee = types.NewCommittee NewCommitteeChangeProposal = types.NewCommitteeChangeProposal NewCommitteeDeleteProposal = types.NewCommitteeDeleteProposal NewGenesisState = types.NewGenesisState @@ -46,7 +47,9 @@ var ( NewQueryCommitteeParams = types.NewQueryCommitteeParams NewQueryProposalParams = types.NewQueryProposalParams NewQueryVoteParams = types.NewQueryVoteParams - RegisterCodec = types.RegisterCodec + RegisterAppCodec = types.RegisterAppCodec + RegisterModuleCodec = types.RegisterModuleCodec + RegisterProposalTypeCodec = types.RegisterProposalTypeCodec Uint64FromBytes = types.Uint64FromBytes // variable aliases @@ -61,22 +64,23 @@ var ( ) type ( - Keeper = keeper.Keeper - Committee = types.Committee - CommitteeChangeProposal = types.CommitteeChangeProposal - CommitteeDeleteProposal = types.CommitteeDeleteProposal - GeneralShutdownPermission = types.GeneralShutdownPermission - GenesisState = types.GenesisState - GodPermission = types.GodPermission - InflationRateChangePermission = types.InflationRateChangePermission - MsgSubmitProposal = types.MsgSubmitProposal - MsgVote = types.MsgVote - Permission = types.Permission - Proposal = types.Proposal - PubProposal = types.PubProposal - QueryCommitteeParams = types.QueryCommitteeParams - QueryProposalParams = types.QueryProposalParams - QueryVoteParams = types.QueryVoteParams - ShutdownCDPDepsitPermission = types.ShutdownCDPDepsitPermission - Vote = types.Vote + Keeper = keeper.Keeper + AllowedParam = types.AllowedParam + AllowedParams = types.AllowedParams + Committee = types.Committee + CommitteeChangeProposal = types.CommitteeChangeProposal + CommitteeDeleteProposal = types.CommitteeDeleteProposal + GenesisState = types.GenesisState + GodPermission = types.GodPermission + MsgSubmitProposal = types.MsgSubmitProposal + MsgVote = types.MsgVote + ParamChangePermission = types.ParamChangePermission + Permission = types.Permission + Proposal = types.Proposal + PubProposal = types.PubProposal + QueryCommitteeParams = types.QueryCommitteeParams + QueryProposalParams = types.QueryProposalParams + QueryVoteParams = types.QueryVoteParams + ShutdownPermission = types.ShutdownPermission + Vote = types.Vote ) diff --git a/x/committee/module.go b/x/committee/module.go index e188049b..12e95b56 100644 --- a/x/committee/module.go +++ b/x/committee/module.go @@ -32,7 +32,7 @@ func (AppModuleBasic) Name() string { // RegisterCodec register module codec func (AppModuleBasic) RegisterCodec(cdc *codec.Codec) { - RegisterCodec(cdc) + RegisterAppCodec(cdc) } // DefaultGenesis default genesis state diff --git a/x/committee/types/codec.go b/x/committee/types/codec.go index 294068df..8da0a9d3 100644 --- a/x/committee/types/codec.go +++ b/x/committee/types/codec.go @@ -12,32 +12,47 @@ var ModuleCdc *codec.Codec func init() { cdc := codec.New() + RegisterModuleCodec(cdc) + ModuleCdc = cdc.Seal() +} + +// TODO decide if not using gov's Content type would be better + +func RegisterModuleCodec(cdc *codec.Codec) { 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: + // 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() + RegisterAppCodec(cdc) } // RegisterCodec registers the necessary types for the module -func RegisterCodec(cdc *codec.Codec) { - +func RegisterAppCodec(cdc *codec.Codec) { + // Proposals // 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.RegisterInterface((*gov.Content)(nil), nil) cdc.RegisterConcrete(CommitteeChangeProposal{}, "kava/CommitteeChangeProposal", nil) cdc.RegisterConcrete(CommitteeDeleteProposal{}, "kava/CommitteeDeleteProposal", nil) + // Permissions cdc.RegisterInterface((*Permission)(nil), nil) cdc.RegisterConcrete(GodPermission{}, "kava/GodPermission", nil) + cdc.RegisterConcrete(ParamChangePermission{}, "kava/ParamChangePermission", nil) + cdc.RegisterConcrete(ShutdownPermission{}, "kava/ShutdownPermission", nil) + // Msgs cdc.RegisterConcrete(MsgSubmitProposal{}, "kava/MsgSubmitProposal", nil) cdc.RegisterConcrete(MsgVote{}, "kava/MsgVote", nil) } + +// RegisterProposalTypeCodec registers an external proposal content type defined +// in another module for the internal ModuleCdc. This allows the MsgSubmitProposal +// to be correctly Amino encoded and decoded. +func RegisterProposalTypeCodec(o interface{}, name string) { + ModuleCdc.RegisterConcrete(o, name, nil) +} diff --git a/x/committee/types/gov_proposal.go b/x/committee/types/gov_proposal.go index f80b5047..b5938ad3 100644 --- a/x/committee/types/gov_proposal.go +++ b/x/committee/types/gov_proposal.go @@ -25,15 +25,6 @@ func init() { // Gov proposals need to be registered on gov's ModuleCdc so MsgSubmitProposal can be encoded. govtypes.RegisterProposalType(ProposalTypeCommitteeChange) govtypes.RegisterProposalTypeCodec(CommitteeChangeProposal{}, "kava/CommitteeChangeProposal") - // Since these proposals include Permissions that needs to be registered as well (including the interface and concrete types) - govtypes.ModuleCdc.RegisterInterface((*Permission)(nil), nil) - govtypes.RegisterProposalTypeCodec(GodPermission{}, "kava/GodPermission") - // TODO register other permissions here - - // 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 { @@ -83,12 +74,9 @@ type CommitteeDeleteProposal struct { var _ govtypes.Content = CommitteeDeleteProposal{} func init() { + // Gov proposals need to be registered on gov's ModuleCdc so MsgSubmitProposal can be encoded. 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 { diff --git a/x/committee/types/permissions.go b/x/committee/types/permissions.go index 807fc1f4..2481587e 100644 --- a/x/committee/types/permissions.go +++ b/x/committee/types/permissions.go @@ -6,64 +6,110 @@ import ( sdtypes "github.com/kava-labs/kava/x/shutdown/types" ) -// EXAMPLE PERMISSIONS ------------------------------ +func init() { + // Gov proposals need to be registered on gov's ModuleCdc. + // But since proposals contain Permissions, those types also need registering. + gov.ModuleCdc.RegisterInterface((*Permission)(nil), nil) + gov.RegisterProposalTypeCodec(GodPermission{}, "kava/GodPermission") + gov.RegisterProposalTypeCodec(ParamChangePermission{}, "kava/ParamChangePermission") + gov.RegisterProposalTypeCodec(ShutdownPermission{}, "kava/ShutdownPermission") +} +// GodPermission allows any governance proposal. It is used mainly for testing. type GodPermission struct{} +var _ Permission = GodPermission{} + func (GodPermission) Allows(gov.Content) bool { return true } -// Allow only changes to inflation_rate -type InflationRateChangePermission struct{} +func (GodPermission) MarshalYAML() (interface{}, error) { + valueToMarshal := struct { + Type string `yaml:"type"` + }{ + Type: "god_permission", + } + return valueToMarshal, nil +} -var _ Permission = InflationRateChangePermission{} +// ParamChangeProposal only allows changes to certain params +type ParamChangePermission struct { + AllowedParams AllowedParams `json:"allowed_params" yaml:"allowed_params"` +} -func (InflationRateChangePermission) Allows(p gov.Content) bool { - pcp, ok := p.(params.ParameterChangeProposal) +var _ Permission = ParamChangePermission{} + +func (perm ParamChangePermission) Allows(p gov.Content) bool { + proposal, ok := p.(params.ParameterChangeProposal) if !ok { return false } - for _, pc := range pcp.Changes { - if pc.Key == "inflation_rate" { + for _, change := range proposal.Changes { + if !perm.AllowedParams.Contains(change) { + return false + } + } + return true +} + +func (perm ParamChangePermission) MarshalYAML() (interface{}, error) { + valueToMarshal := struct { + Type string `yaml:"type"` + AllowedParams AllowedParams `yaml:"allowed_params` + }{ + Type: "param_change_permission", + AllowedParams: perm.AllowedParams, + } + return valueToMarshal, nil +} + +type AllowedParam struct { + Subspace string `json:"subspace" yaml:"subspace"` + Key string `json:"key" yaml:"key"` + Subkey string `json:"subkey,omitempty" yaml:"subkey,omitempty"` +} +type AllowedParams []AllowedParam + +func (allowed AllowedParams) Contains(paramChange params.ParamChange) bool { + for _, p := range allowed { + if paramChange.Subspace == p.Subspace && paramChange.Key == p.Key && paramChange.Subkey == p.Subkey { return true } } return false } -// Allow only shutdown of the CDP Deposit msg -type ShutdownCDPDepsitPermission struct{} - -var _ Permission = ShutdownCDPDepsitPermission{} - -func (ShutdownCDPDepsitPermission) Allows(p gov.Content) bool { - sdp, ok := p.(sdtypes.ShutdownProposal) - if !ok { - return false - } - for _, r := range sdp.MsgRoutes { - if r.Route == "cdp" && r.Msg == "MsgCDPDeposit" { - return true - } - } - return false -} - -// Same as above but the route isn't static -type GeneralShutdownPermission struct { +// ShutdownPermission allows certain message types to be disabled +type ShutdownPermission struct { MsgRoute sdtypes.MsgRoute `json:"msg_route" yaml:"msg_route"` } -var _ Permission = GeneralShutdownPermission{} +var _ Permission = ShutdownPermission{} -func (perm GeneralShutdownPermission) Allows(p gov.Content) bool { - sdp, ok := p.(sdtypes.ShutdownProposal) +func (perm ShutdownPermission) Allows(p gov.Content) bool { + proposal, ok := p.(sdtypes.ShutdownProposal) if !ok { return false } - for _, r := range sdp.MsgRoutes { + for _, r := range proposal.MsgRoutes { if r == perm.MsgRoute { return true } } return false } + +func (perm ShutdownPermission) MarshalYAML() (interface{}, error) { + valueToMarshal := struct { + Type string `yaml:"type"` + MsgRoute sdtypes.MsgRoute `yaml:"msg_route"` + }{ + Type: "shutdown_permission", + MsgRoute: perm.MsgRoute, + } + return valueToMarshal, nil +} + +// TODO add more permissions? +// - limit parameter changes to bew withing small ranges or fixed sets +// - allow community spend proposals +// - allow committee change proposals diff --git a/x/committee/types/types.go b/x/committee/types/types.go index 37c412fa..ab1c3ede 100644 --- a/x/committee/types/types.go +++ b/x/committee/types/types.go @@ -19,9 +19,12 @@ var ( // 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"` // TODO or a name? + 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 {