From 1b3ce2c1d128866ef33eca34850b0343703df12e Mon Sep 17 00:00:00 2001 From: Amaury Date: Wed, 25 Nov 2020 00:32:32 +0100 Subject: [PATCH] CLI `migrate` command for x/{upgrade,params} proposals (#7875) * Add v038 upgrade types * Add migrate x/upgrade * remove nolint * Add ParameterChangeProposal * Revert change * Don't register crypto twice Co-authored-by: Anil Kumar Kammari Co-authored-by: Federico Kunze <31522760+fedekunze@users.noreply.github.com> --- x/genutil/legacy/v036/migrate.go | 2 + x/genutil/legacy/v038/migrate.go | 5 + x/genutil/legacy/v039/migrate.go | 6 + x/genutil/legacy/v040/migrate.go | 6 +- x/gov/legacy/v040/migrate.go | 62 +++++++- x/gov/legacy/v040/migrate_test.go | 247 ++++++++++++++++++++++-------- x/params/legacy/v036/types.go | 172 +++++++++++++++++++++ x/upgrade/legacy/v038/types.go | 166 ++++++++++++++++++++ 8 files changed, 588 insertions(+), 78 deletions(-) create mode 100644 x/params/legacy/v036/types.go create mode 100644 x/upgrade/legacy/v038/types.go diff --git a/x/genutil/legacy/v036/migrate.go b/x/genutil/legacy/v036/migrate.go index 8b7136159..204eebc23 100644 --- a/x/genutil/legacy/v036/migrate.go +++ b/x/genutil/legacy/v036/migrate.go @@ -14,6 +14,7 @@ import ( "github.com/cosmos/cosmos-sdk/x/genutil/types" v034gov "github.com/cosmos/cosmos-sdk/x/gov/legacy/v034" v036gov "github.com/cosmos/cosmos-sdk/x/gov/legacy/v036" + v036params "github.com/cosmos/cosmos-sdk/x/params/legacy/v036" v034staking "github.com/cosmos/cosmos-sdk/x/staking/legacy/v034" v036staking "github.com/cosmos/cosmos-sdk/x/staking/legacy/v036" ) @@ -28,6 +29,7 @@ func Migrate(appState types.AppMap, _ client.Context) types.AppMap { cryptocodec.RegisterCrypto(v036Codec) v036gov.RegisterLegacyAminoCodec(v036Codec) v036distr.RegisterLegacyAminoCodec(v036Codec) + v036params.RegisterLegacyAminoCodec(v036Codec) // migrate genesis accounts state if appState[v034genAccounts.ModuleName] != nil { diff --git a/x/genutil/legacy/v038/migrate.go b/x/genutil/legacy/v038/migrate.go index 374092298..d0ca4c418 100644 --- a/x/genutil/legacy/v038/migrate.go +++ b/x/genutil/legacy/v038/migrate.go @@ -10,8 +10,10 @@ import ( v036genaccounts "github.com/cosmos/cosmos-sdk/x/genaccounts/legacy/v036" "github.com/cosmos/cosmos-sdk/x/genutil/types" v036gov "github.com/cosmos/cosmos-sdk/x/gov/legacy/v036" + v036params "github.com/cosmos/cosmos-sdk/x/params/legacy/v036" v036staking "github.com/cosmos/cosmos-sdk/x/staking/legacy/v036" v038staking "github.com/cosmos/cosmos-sdk/x/staking/legacy/v038" + v038upgrade "github.com/cosmos/cosmos-sdk/x/upgrade/legacy/v038" ) // Migrate migrates exported state from v0.36/v0.37 to a v0.38 genesis state. @@ -19,11 +21,14 @@ func Migrate(appState types.AppMap, _ client.Context) types.AppMap { v036Codec := codec.NewLegacyAmino() v036gov.RegisterLegacyAminoCodec(v036Codec) v036distr.RegisterLegacyAminoCodec(v036Codec) + v036params.RegisterLegacyAminoCodec(v036Codec) v038Codec := codec.NewLegacyAmino() v038auth.RegisterLegacyAminoCodec(v038Codec) v036gov.RegisterLegacyAminoCodec(v038Codec) v036distr.RegisterLegacyAminoCodec(v038Codec) + v036params.RegisterLegacyAminoCodec(v038Codec) + v038upgrade.RegisterLegacyAminoCodec(v038Codec) if appState[v036genaccounts.ModuleName] != nil { // unmarshal relative source genesis application state diff --git a/x/genutil/legacy/v039/migrate.go b/x/genutil/legacy/v039/migrate.go index 97442e938..99c7fce77 100644 --- a/x/genutil/legacy/v039/migrate.go +++ b/x/genutil/legacy/v039/migrate.go @@ -8,6 +8,8 @@ import ( v036distr "github.com/cosmos/cosmos-sdk/x/distribution/legacy/v036" "github.com/cosmos/cosmos-sdk/x/genutil/types" v036gov "github.com/cosmos/cosmos-sdk/x/gov/legacy/v036" + v036params "github.com/cosmos/cosmos-sdk/x/params/legacy/v036" + v038upgrade "github.com/cosmos/cosmos-sdk/x/upgrade/legacy/v038" ) // Migrate migrates exported state from v0.38 to a v0.39 genesis state. @@ -19,11 +21,15 @@ func Migrate(appState types.AppMap, _ client.Context) types.AppMap { v038auth.RegisterLegacyAminoCodec(v038Codec) v036gov.RegisterLegacyAminoCodec(v038Codec) v036distr.RegisterLegacyAminoCodec(v038Codec) + v036params.RegisterLegacyAminoCodec(v038Codec) + v038upgrade.RegisterLegacyAminoCodec(v038Codec) v039Codec := codec.NewLegacyAmino() v039auth.RegisterLegacyAminoCodec(v039Codec) v036gov.RegisterLegacyAminoCodec(v039Codec) v036distr.RegisterLegacyAminoCodec(v039Codec) + v036params.RegisterLegacyAminoCodec(v039Codec) + v038upgrade.RegisterLegacyAminoCodec(v039Codec) // migrate x/auth state (JSON serialization only) if appState[v038auth.ModuleName] != nil { diff --git a/x/genutil/legacy/v040/migrate.go b/x/genutil/legacy/v040/migrate.go index ec7103868..eb5d10690 100644 --- a/x/genutil/legacy/v040/migrate.go +++ b/x/genutil/legacy/v040/migrate.go @@ -3,7 +3,6 @@ package v040 import ( "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/codec" - cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" v039auth "github.com/cosmos/cosmos-sdk/x/auth/legacy/v039" v040auth "github.com/cosmos/cosmos-sdk/x/auth/legacy/v040" v036supply "github.com/cosmos/cosmos-sdk/x/bank/legacy/v036" @@ -22,10 +21,12 @@ import ( v040gov "github.com/cosmos/cosmos-sdk/x/gov/legacy/v040" v039mint "github.com/cosmos/cosmos-sdk/x/mint/legacy/v039" v040mint "github.com/cosmos/cosmos-sdk/x/mint/legacy/v040" + v036params "github.com/cosmos/cosmos-sdk/x/params/legacy/v036" v039slashing "github.com/cosmos/cosmos-sdk/x/slashing/legacy/v039" v040slashing "github.com/cosmos/cosmos-sdk/x/slashing/legacy/v040" v038staking "github.com/cosmos/cosmos-sdk/x/staking/legacy/v038" v040staking "github.com/cosmos/cosmos-sdk/x/staking/legacy/v040" + v038upgrade "github.com/cosmos/cosmos-sdk/x/upgrade/legacy/v038" ) func migrateGenutil(oldGenState v039genutil.GenesisState) *types.GenesisState { @@ -37,10 +38,11 @@ func migrateGenutil(oldGenState v039genutil.GenesisState) *types.GenesisState { // Migrate migrates exported state from v0.39 to a v0.40 genesis state. func Migrate(appState types.AppMap, clientCtx client.Context) types.AppMap { v039Codec := codec.NewLegacyAmino() - cryptocodec.RegisterCrypto(v039Codec) v039auth.RegisterLegacyAminoCodec(v039Codec) v036gov.RegisterLegacyAminoCodec(v039Codec) v036distr.RegisterLegacyAminoCodec(v039Codec) + v036params.RegisterLegacyAminoCodec(v039Codec) + v038upgrade.RegisterLegacyAminoCodec(v039Codec) v040Codec := clientCtx.JSONMarshaler diff --git a/x/gov/legacy/v040/migrate.go b/x/gov/legacy/v040/migrate.go index 223eb6626..2accb7428 100644 --- a/x/gov/legacy/v040/migrate.go +++ b/x/gov/legacy/v040/migrate.go @@ -3,12 +3,18 @@ package v040 import ( "fmt" + proto "github.com/gogo/protobuf/proto" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" v036distr "github.com/cosmos/cosmos-sdk/x/distribution/legacy/v036" v040distr "github.com/cosmos/cosmos-sdk/x/distribution/types" v034gov "github.com/cosmos/cosmos-sdk/x/gov/legacy/v034" v036gov "github.com/cosmos/cosmos-sdk/x/gov/legacy/v036" v040gov "github.com/cosmos/cosmos-sdk/x/gov/types" + v036params "github.com/cosmos/cosmos-sdk/x/params/legacy/v036" + v040params "github.com/cosmos/cosmos-sdk/x/params/types/proposal" + v038upgrade "github.com/cosmos/cosmos-sdk/x/upgrade/legacy/v038" + v040upgrade "github.com/cosmos/cosmos-sdk/x/upgrade/types" ) func migrateVoteOption(oldVoteOption v034gov.VoteOption) v040gov.VoteOption { @@ -60,10 +66,12 @@ func migrateProposalStatus(oldProposalStatus v034gov.ProposalStatus) v040gov.Pro } func migrateContent(oldContent v036gov.Content) *codectypes.Any { + var protoProposal proto.Message + switch oldContent := oldContent.(type) { case v036gov.TextProposal: { - protoProposal := &v040gov.TextProposal{ + protoProposal = &v040gov.TextProposal{ Title: oldContent.Title, Description: oldContent.Description, } @@ -77,23 +85,61 @@ func migrateContent(oldContent v036gov.Content) *codectypes.Any { } case v036distr.CommunityPoolSpendProposal: { - protoProposal := &v040distr.CommunityPoolSpendProposal{ + protoProposal = &v040distr.CommunityPoolSpendProposal{ Title: oldContent.Title, Description: oldContent.Description, Recipient: oldContent.Recipient.String(), Amount: oldContent.Amount, } - // Convert the content into Any. - contentAny, err := codectypes.NewAnyWithValue(protoProposal) - if err != nil { - panic(err) + } + case v038upgrade.CancelSoftwareUpgradeProposal: + { + protoProposal = &v040upgrade.CancelSoftwareUpgradeProposal{ + Description: oldContent.Description, + Title: oldContent.Title, + } + } + case v038upgrade.SoftwareUpgradeProposal: + { + protoProposal = &v040upgrade.SoftwareUpgradeProposal{ + Description: oldContent.Description, + Title: oldContent.Title, + Plan: v040upgrade.Plan{ + Name: oldContent.Plan.Name, + Time: oldContent.Plan.Time, + Height: oldContent.Plan.Height, + Info: oldContent.Plan.Info, + }, + } + } + case v036params.ParameterChangeProposal: + { + newChanges := make([]v040params.ParamChange, len(oldContent.Changes)) + for i, oldChange := range oldContent.Changes { + newChanges[i] = v040params.ParamChange{ + Subspace: oldChange.Subspace, + Key: oldChange.Key, + Value: oldChange.Value, + } } - return contentAny + protoProposal = &v040params.ParameterChangeProposal{ + Description: oldContent.Description, + Title: oldContent.Title, + Changes: newChanges, + } } default: - panic(fmt.Errorf("'%T' is not a valid proposal content type", oldContent)) + panic(fmt.Errorf("%T is not a valid proposal content type", oldContent)) } + + // Convert the content into Any. + contentAny, err := codectypes.NewAnyWithValue(protoProposal) + if err != nil { + panic(err) + } + + return contentAny } // Migrate accepts exported v0.36 x/gov genesis state and migrates it to diff --git a/x/gov/legacy/v040/migrate_test.go b/x/gov/legacy/v040/migrate_test.go index 84c6a554a..049f5b885 100644 --- a/x/gov/legacy/v040/migrate_test.go +++ b/x/gov/legacy/v040/migrate_test.go @@ -12,6 +12,8 @@ import ( v036distr "github.com/cosmos/cosmos-sdk/x/distribution/legacy/v036" v036gov "github.com/cosmos/cosmos-sdk/x/gov/legacy/v036" v040gov "github.com/cosmos/cosmos-sdk/x/gov/legacy/v040" + v036params "github.com/cosmos/cosmos-sdk/x/params/legacy/v036" + v038upgrade "github.com/cosmos/cosmos-sdk/x/upgrade/legacy/v038" ) func TestMigrate(t *testing.T) { @@ -28,8 +30,8 @@ func TestMigrate(t *testing.T) { Proposals: []v036gov.Proposal{ { Content: v036gov.TextProposal{ - Title: "foo_test", - Description: "bar_test", + Title: "foo_text", + Description: "bar_text", }, }, { @@ -40,6 +42,37 @@ func TestMigrate(t *testing.T) { Amount: sdk.NewCoins(sdk.NewCoin("footoken", sdk.NewInt(2))), }, }, + { + Content: v038upgrade.CancelSoftwareUpgradeProposal{ + Title: "foo_cancel_upgrade", + Description: "bar_cancel_upgrade", + }, + }, + { + Content: v038upgrade.SoftwareUpgradeProposal{ + Title: "foo_software_upgrade", + Description: "bar_software_upgrade", + Plan: v038upgrade.Plan{ + Name: "foo_upgrade_name", + Height: 123, + Info: "foo_upgrade_info", + }, + }, + }, + { + Content: v036params.ParameterChangeProposal{ + Title: "foo_param_change", + Description: "bar_param_change", + Changes: []v036params.ParamChange{ + { + Subspace: "foo_param_change_subspace", + Key: "foo_param_change_key", + Subkey: "foo_param_change_subkey", + Value: "foo_param_change_value", + }, + }, + }, + }, }, } @@ -52,76 +85,154 @@ func TestMigrate(t *testing.T) { var jsonObj map[string]interface{} err = json.Unmarshal(bz, &jsonObj) require.NoError(t, err) - indentedBz, err := json.MarshalIndent(jsonObj, "", " ") + indentedBz, err := json.MarshalIndent(jsonObj, "", "\t") require.NoError(t, err) // Make sure about: - // - TextProposal and CommunityPoolSpendProposal have correct any JSON. + // - TextProposal has correct JSON. + // - CommunityPoolSpendProposal has correct JSON. + // - CancelSoftwareUpgradeProposal has correct JSON. + // - SoftwareUpgradeProposal has correct JSON. + // - ParameterChangeProposal has correct JSON. expected := `{ - "deposit_params": { - "max_deposit_period": "0s", - "min_deposit": [] - }, - "deposits": [], - "proposals": [ - { - "content": { - "@type": "/cosmos.gov.v1beta1.TextProposal", - "description": "bar_test", - "title": "foo_test" - }, - "deposit_end_time": "0001-01-01T00:00:00Z", - "final_tally_result": { - "abstain": "0", - "no": "0", - "no_with_veto": "0", - "yes": "0" - }, - "proposal_id": "0", - "status": "PROPOSAL_STATUS_UNSPECIFIED", - "submit_time": "0001-01-01T00:00:00Z", - "total_deposit": [], - "voting_end_time": "0001-01-01T00:00:00Z", - "voting_start_time": "0001-01-01T00:00:00Z" - }, - { - "content": { - "@type": "/cosmos.distribution.v1beta1.CommunityPoolSpendProposal", - "amount": [ - { - "amount": "2", - "denom": "footoken" - } - ], - "description": "bar_community", - "recipient": "cosmos1fl48vsnmsdzcv85q5d2q4z5ajdha8yu34mf0eh", - "title": "foo_community" - }, - "deposit_end_time": "0001-01-01T00:00:00Z", - "final_tally_result": { - "abstain": "0", - "no": "0", - "no_with_veto": "0", - "yes": "0" - }, - "proposal_id": "0", - "status": "PROPOSAL_STATUS_UNSPECIFIED", - "submit_time": "0001-01-01T00:00:00Z", - "total_deposit": [], - "voting_end_time": "0001-01-01T00:00:00Z", - "voting_start_time": "0001-01-01T00:00:00Z" - } - ], - "starting_proposal_id": "0", - "tally_params": { - "quorum": "0", - "threshold": "0", - "veto_threshold": "0" - }, - "votes": [], - "voting_params": { - "voting_period": "0s" - } + "deposit_params": { + "max_deposit_period": "0s", + "min_deposit": [] + }, + "deposits": [], + "proposals": [ + { + "content": { + "@type": "/cosmos.gov.v1beta1.TextProposal", + "description": "bar_text", + "title": "foo_text" + }, + "deposit_end_time": "0001-01-01T00:00:00Z", + "final_tally_result": { + "abstain": "0", + "no": "0", + "no_with_veto": "0", + "yes": "0" + }, + "proposal_id": "0", + "status": "PROPOSAL_STATUS_UNSPECIFIED", + "submit_time": "0001-01-01T00:00:00Z", + "total_deposit": [], + "voting_end_time": "0001-01-01T00:00:00Z", + "voting_start_time": "0001-01-01T00:00:00Z" + }, + { + "content": { + "@type": "/cosmos.distribution.v1beta1.CommunityPoolSpendProposal", + "amount": [ + { + "amount": "2", + "denom": "footoken" + } + ], + "description": "bar_community", + "recipient": "cosmos1fl48vsnmsdzcv85q5d2q4z5ajdha8yu34mf0eh", + "title": "foo_community" + }, + "deposit_end_time": "0001-01-01T00:00:00Z", + "final_tally_result": { + "abstain": "0", + "no": "0", + "no_with_veto": "0", + "yes": "0" + }, + "proposal_id": "0", + "status": "PROPOSAL_STATUS_UNSPECIFIED", + "submit_time": "0001-01-01T00:00:00Z", + "total_deposit": [], + "voting_end_time": "0001-01-01T00:00:00Z", + "voting_start_time": "0001-01-01T00:00:00Z" + }, + { + "content": { + "@type": "/cosmos.upgrade.v1beta1.CancelSoftwareUpgradeProposal", + "description": "bar_cancel_upgrade", + "title": "foo_cancel_upgrade" + }, + "deposit_end_time": "0001-01-01T00:00:00Z", + "final_tally_result": { + "abstain": "0", + "no": "0", + "no_with_veto": "0", + "yes": "0" + }, + "proposal_id": "0", + "status": "PROPOSAL_STATUS_UNSPECIFIED", + "submit_time": "0001-01-01T00:00:00Z", + "total_deposit": [], + "voting_end_time": "0001-01-01T00:00:00Z", + "voting_start_time": "0001-01-01T00:00:00Z" + }, + { + "content": { + "@type": "/cosmos.upgrade.v1beta1.SoftwareUpgradeProposal", + "description": "bar_software_upgrade", + "plan": { + "height": "123", + "info": "foo_upgrade_info", + "name": "foo_upgrade_name", + "time": "0001-01-01T00:00:00Z", + "upgraded_client_state": null + }, + "title": "foo_software_upgrade" + }, + "deposit_end_time": "0001-01-01T00:00:00Z", + "final_tally_result": { + "abstain": "0", + "no": "0", + "no_with_veto": "0", + "yes": "0" + }, + "proposal_id": "0", + "status": "PROPOSAL_STATUS_UNSPECIFIED", + "submit_time": "0001-01-01T00:00:00Z", + "total_deposit": [], + "voting_end_time": "0001-01-01T00:00:00Z", + "voting_start_time": "0001-01-01T00:00:00Z" + }, + { + "content": { + "@type": "/cosmos.params.v1beta1.ParameterChangeProposal", + "changes": [ + { + "key": "foo_param_change_key", + "subspace": "foo_param_change_subspace", + "value": "foo_param_change_value" + } + ], + "description": "bar_param_change", + "title": "foo_param_change" + }, + "deposit_end_time": "0001-01-01T00:00:00Z", + "final_tally_result": { + "abstain": "0", + "no": "0", + "no_with_veto": "0", + "yes": "0" + }, + "proposal_id": "0", + "status": "PROPOSAL_STATUS_UNSPECIFIED", + "submit_time": "0001-01-01T00:00:00Z", + "total_deposit": [], + "voting_end_time": "0001-01-01T00:00:00Z", + "voting_start_time": "0001-01-01T00:00:00Z" + } + ], + "starting_proposal_id": "0", + "tally_params": { + "quorum": "0", + "threshold": "0", + "veto_threshold": "0" + }, + "votes": [], + "voting_params": { + "voting_period": "0s" + } }` require.Equal(t, expected, string(indentedBz)) diff --git a/x/params/legacy/v036/types.go b/x/params/legacy/v036/types.go new file mode 100644 index 000000000..66cc9e7d8 --- /dev/null +++ b/x/params/legacy/v036/types.go @@ -0,0 +1,172 @@ +package v036 + +import ( + "fmt" + "strings" + + "github.com/cosmos/cosmos-sdk/codec" + v036gov "github.com/cosmos/cosmos-sdk/x/gov/legacy/v036" +) + +const ( + // ModuleName defines the name of the module + ModuleName = "params" + + // RouterKey defines the routing key for a ParameterChangeProposal + RouterKey = "params" +) + +const ( + // ProposalTypeChange defines the type for a ParameterChangeProposal + ProposalTypeChange = "ParameterChange" +) + +// Param module codespace constants +const ( + DefaultCodespace = "params" + + CodeUnknownSubspace = 1 + CodeSettingParameter = 2 + CodeEmptyData = 3 +) + +// Assert ParameterChangeProposal implements v036gov.Content at compile-time +var _ v036gov.Content = ParameterChangeProposal{} + +// ParameterChangeProposal defines a proposal which contains multiple parameter +// changes. +type ParameterChangeProposal struct { + Title string `json:"title" yaml:"title"` + Description string `json:"description" yaml:"description"` + Changes []ParamChange `json:"changes" yaml:"changes"` +} + +func NewParameterChangeProposal(title, description string, changes []ParamChange) ParameterChangeProposal { + return ParameterChangeProposal{title, description, changes} +} + +// GetTitle returns the title of a parameter change proposal. +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. +func (pcp ParameterChangeProposal) ProposalRoute() string { return RouterKey } + +// ProposalType returns the type of a parameter change proposal. +func (pcp ParameterChangeProposal) ProposalType() string { return ProposalTypeChange } + +// ValidateBasic validates the parameter change proposal +func (pcp ParameterChangeProposal) ValidateBasic() error { + err := v036gov.ValidateAbstract(pcp) + if err != nil { + return err + } + + return ValidateChanges(pcp.Changes) +} + +// String implements the Stringer interface. +func (pcp ParameterChangeProposal) String() string { + var b strings.Builder + + b.WriteString(fmt.Sprintf(`Parameter Change Proposal: + Title: %s + Description: %s + Changes: +`, pcp.Title, pcp.Description)) + + for _, pc := range pcp.Changes { + b.WriteString(fmt.Sprintf(` Param Change: + Subspace: %s + Key: %s + Subkey: %X + Value: %X +`, pc.Subspace, pc.Key, pc.Subkey, pc.Value)) + } + + return b.String() +} + +// ParamChange defines a parameter change. +type ParamChange struct { + Subspace string `json:"subspace" yaml:"subspace"` + Key string `json:"key" yaml:"key"` + Subkey string `json:"subkey,omitempty" yaml:"subkey,omitempty"` + Value string `json:"value" yaml:"value"` +} + +func NewParamChange(subspace, key, value string) ParamChange { + return ParamChange{subspace, key, "", value} +} + +func NewParamChangeWithSubkey(subspace, key, subkey, value string) ParamChange { + return ParamChange{subspace, key, subkey, value} +} + +// String implements the Stringer interface. +func (pc ParamChange) String() string { + return fmt.Sprintf(`Param Change: + Subspace: %s + Key: %s + Subkey: %X + Value: %X +`, pc.Subspace, pc.Key, pc.Subkey, pc.Value) +} + +// ValidateChange performs basic validation checks over a set of ParamChange. It +// returns an error if any ParamChange is invalid. +func ValidateChanges(changes []ParamChange) error { + if len(changes) == 0 { + return ErrEmptyChanges(DefaultCodespace) + } + + for _, pc := range changes { + if len(pc.Subspace) == 0 { + return ErrEmptySubspace(DefaultCodespace) + } + if len(pc.Key) == 0 { + return ErrEmptyKey(DefaultCodespace) + } + if len(pc.Value) == 0 { + return ErrEmptyValue(DefaultCodespace) + } + } + + return nil +} + +// ErrUnknownSubspace returns an unknown subspace error. +func ErrUnknownSubspace(codespace string, space string) error { + return fmt.Errorf("unknown subspace %s", space) +} + +// ErrSettingParameter returns an error for failing to set a parameter. +func ErrSettingParameter(codespace string, key, subkey, value, msg string) error { + return fmt.Errorf("error setting parameter %s on %s (%s): %s", value, key, subkey, msg) +} + +// ErrEmptyChanges returns an error for empty parameter changes. +func ErrEmptyChanges(codespace string) error { + return fmt.Errorf("submitted parameter changes are empty") +} + +// ErrEmptySubspace returns an error for an empty subspace. +func ErrEmptySubspace(codespace string) error { + return fmt.Errorf("parameter subspace is empty") +} + +// ErrEmptyKey returns an error for when an empty key is given. +func ErrEmptyKey(codespace string) error { + return fmt.Errorf("parameter key is empty") +} + +// ErrEmptyValue returns an error for when an empty key is given. +func ErrEmptyValue(codespace string) error { + return fmt.Errorf("parameter value is empty") +} + +func RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { + cdc.RegisterConcrete(ParameterChangeProposal{}, "cosmos-sdk/ParameterChangeProposal", nil) +} diff --git a/x/upgrade/legacy/v038/types.go b/x/upgrade/legacy/v038/types.go new file mode 100644 index 000000000..fb06a6dc5 --- /dev/null +++ b/x/upgrade/legacy/v038/types.go @@ -0,0 +1,166 @@ +package v038 + +import ( + "fmt" + "strings" + "time" + + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + v036gov "github.com/cosmos/cosmos-sdk/x/gov/legacy/v036" +) + +const ( + // ModuleName is the name of this module + ModuleName = "upgrade" + + // RouterKey is used to route governance proposals + RouterKey = ModuleName + + // StoreKey is the prefix under which we store this module's data + StoreKey = ModuleName + + // QuerierKey is used to handle abci_query requests + QuerierKey = ModuleName +) + +// Plan specifies information about a planned upgrade and when it should occur +type Plan struct { + // Sets the name for the upgrade. This name will be used by the upgraded version of the software to apply any + // special "on-upgrade" commands during the first BeginBlock method after the upgrade is applied. It is also used + // to detect whether a software version can handle a given upgrade. If no upgrade handler with this name has been + // set in the software, it will be assumed that the software is out-of-date when the upgrade Time or Height + // is reached and the software will exit. + Name string `json:"name,omitempty"` + + // The time after which the upgrade must be performed. + // Leave set to its zero value to use a pre-defined Height instead. + Time time.Time `json:"time,omitempty"` + + // The height at which the upgrade must be performed. + // Only used if Time is not set. + Height int64 `json:"height,omitempty"` + + // Any application specific upgrade info to be included on-chain + // such as a git commit that validators could automatically upgrade to + Info string `json:"info,omitempty"` +} + +func (p Plan) String() string { + due := p.DueAt() + dueUp := strings.ToUpper(due[0:1]) + due[1:] + return fmt.Sprintf(`Upgrade Plan + Name: %s + %s + Info: %s`, p.Name, dueUp, p.Info) +} + +// ValidateBasic does basic validation of a Plan +func (p Plan) ValidateBasic() error { + if len(p.Name) == 0 { + return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "name cannot be empty") + } + if p.Height < 0 { + return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "height cannot be negative") + } + if p.Time.IsZero() && p.Height == 0 { + return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "must set either time or height") + } + if !p.Time.IsZero() && p.Height != 0 { + return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "cannot set both time and height") + } + + return nil +} + +// ShouldExecute returns true if the Plan is ready to execute given the current context +func (p Plan) ShouldExecute(ctx sdk.Context) bool { + if !p.Time.IsZero() { + return !ctx.BlockTime().Before(p.Time) + } + if p.Height > 0 { + return p.Height <= ctx.BlockHeight() + } + return false +} + +// DueAt is a string representation of when this plan is due to be executed +func (p Plan) DueAt() string { + if !p.Time.IsZero() { + return fmt.Sprintf("time: %s", p.Time.UTC().Format(time.RFC3339)) + } + return fmt.Sprintf("height: %d", p.Height) +} + +const ( + ProposalTypeSoftwareUpgrade string = "SoftwareUpgrade" + ProposalTypeCancelSoftwareUpgrade string = "CancelSoftwareUpgrade" +) + +// Software Upgrade Proposals +type SoftwareUpgradeProposal struct { + Title string `json:"title" yaml:"title"` + Description string `json:"description" yaml:"description"` + Plan Plan `json:"plan" yaml:"plan"` +} + +func NewSoftwareUpgradeProposal(title, description string, plan Plan) v036gov.Content { + return SoftwareUpgradeProposal{title, description, plan} +} + +// Implements Proposal Interface +var _ v036gov.Content = SoftwareUpgradeProposal{} + +func (sup SoftwareUpgradeProposal) GetTitle() string { return sup.Title } +func (sup SoftwareUpgradeProposal) GetDescription() string { return sup.Description } +func (sup SoftwareUpgradeProposal) ProposalRoute() string { return RouterKey } +func (sup SoftwareUpgradeProposal) ProposalType() string { return ProposalTypeSoftwareUpgrade } +func (sup SoftwareUpgradeProposal) ValidateBasic() error { + if err := sup.Plan.ValidateBasic(); err != nil { + return err + } + return v036gov.ValidateAbstract(sup) +} + +func (sup SoftwareUpgradeProposal) String() string { + return fmt.Sprintf(`Software Upgrade Proposal: + Title: %s + Description: %s +`, sup.Title, sup.Description) +} + +// Cancel Software Upgrade Proposals +type CancelSoftwareUpgradeProposal struct { + Title string `json:"title" yaml:"title"` + Description string `json:"description" yaml:"description"` +} + +func NewCancelSoftwareUpgradeProposal(title, description string) v036gov.Content { + return CancelSoftwareUpgradeProposal{title, description} +} + +// Implements Proposal Interface +var _ v036gov.Content = CancelSoftwareUpgradeProposal{} + +func (sup CancelSoftwareUpgradeProposal) GetTitle() string { return sup.Title } +func (sup CancelSoftwareUpgradeProposal) GetDescription() string { return sup.Description } +func (sup CancelSoftwareUpgradeProposal) ProposalRoute() string { return RouterKey } +func (sup CancelSoftwareUpgradeProposal) ProposalType() string { + return ProposalTypeCancelSoftwareUpgrade +} +func (sup CancelSoftwareUpgradeProposal) ValidateBasic() error { + return v036gov.ValidateAbstract(sup) +} + +func (sup CancelSoftwareUpgradeProposal) String() string { + return fmt.Sprintf(`Cancel Software Upgrade Proposal: + Title: %s + Description: %s +`, sup.Title, sup.Description) +} + +func RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { + cdc.RegisterConcrete(SoftwareUpgradeProposal{}, "cosmos-sdk/SoftwareUpgradeProposal", nil) + cdc.RegisterConcrete(CancelSoftwareUpgradeProposal{}, "cosmos-sdk/CancelSoftwareUpgradeProposal", nil) +}