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 <anil@vitwit.com>
Co-authored-by: Federico Kunze <31522760+fedekunze@users.noreply.github.com>
This commit is contained in:
Amaury 2020-11-25 00:32:32 +01:00 committed by GitHub
parent 12a02b98a5
commit 1b3ce2c1d1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 588 additions and 78 deletions

View File

@ -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 {

View File

@ -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

View File

@ -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 {

View File

@ -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

View File

@ -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

View File

@ -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))

View File

@ -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)
}

View File

@ -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)
}