From 3c43370d0c78d1903c461d40ec557fe79e3b646d Mon Sep 17 00:00:00 2001 From: Amaury Martiny Date: Tue, 20 Oct 2020 15:19:04 +0200 Subject: [PATCH] Add CommunitySpendProposals in migration (#7607) * Add CommunitySpendProposals in migration * Fix lint * Avoid double registration Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- x/distribution/legacy/v036/types.go | 66 ++++++++++++++ x/genutil/legacy/v036/migrate.go | 1 + x/genutil/legacy/v038/migrate.go | 5 ++ x/genutil/legacy/v039/migrate.go | 6 ++ x/genutil/legacy/v040/migrate.go | 17 ++-- x/gov/legacy/v040/migrate.go | 26 +++++- x/gov/legacy/v040/migrate_test.go | 128 ++++++++++++++++++++++++++++ 7 files changed, 240 insertions(+), 9 deletions(-) create mode 100644 x/gov/legacy/v040/migrate_test.go diff --git a/x/distribution/legacy/v036/types.go b/x/distribution/legacy/v036/types.go index ed96ce770..1c43eb5ac 100644 --- a/x/distribution/legacy/v036/types.go +++ b/x/distribution/legacy/v036/types.go @@ -3,8 +3,14 @@ package v036 import ( + "fmt" + "strings" + + "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" v034distr "github.com/cosmos/cosmos-sdk/x/distribution/legacy/v034" + "github.com/cosmos/cosmos-sdk/x/distribution/types" + v036gov "github.com/cosmos/cosmos-sdk/x/gov/legacy/v036" ) // ---------------------------------------------------------------------------- @@ -13,6 +19,12 @@ import ( const ( ModuleName = "distribution" + + // RouterKey is the message route for distribution + RouterKey = ModuleName + + // ProposalTypeCommunityPoolSpend defines the type for a CommunityPoolSpendProposal + ProposalTypeCommunityPoolSpend = "CommunityPoolSpend" ) type ( @@ -40,6 +52,14 @@ type ( DelegatorStartingInfos []v034distr.DelegatorStartingInfoRecord `json:"delegator_starting_infos"` ValidatorSlashEvents []ValidatorSlashEventRecord `json:"validator_slash_events"` } + + // CommunityPoolSpendProposal spends from the community pool + CommunityPoolSpendProposal struct { + Title string `json:"title" yaml:"title"` + Description string `json:"description" yaml:"description"` + Recipient sdk.AccAddress `json:"recipient" yaml:"recipient"` + Amount sdk.Coins `json:"amount" yaml:"amount"` + } ) func NewGenesisState( @@ -66,3 +86,49 @@ func NewGenesisState( ValidatorSlashEvents: slashes, } } + +var _ v036gov.Content = CommunityPoolSpendProposal{} + +// GetTitle returns the title of a community pool spend proposal. +func (csp CommunityPoolSpendProposal) GetTitle() string { return csp.Title } + +// GetDescription returns the description of a community pool spend proposal. +func (csp CommunityPoolSpendProposal) GetDescription() string { return csp.Description } + +// GetDescription returns the routing key of a community pool spend proposal. +func (csp CommunityPoolSpendProposal) ProposalRoute() string { return RouterKey } + +// ProposalType returns the type of a community pool spend proposal. +func (csp CommunityPoolSpendProposal) ProposalType() string { return ProposalTypeCommunityPoolSpend } + +// ValidateBasic runs basic stateless validity checks +func (csp CommunityPoolSpendProposal) ValidateBasic() error { + err := v036gov.ValidateAbstract(csp) + if err != nil { + return err + } + if !csp.Amount.IsValid() { + return types.ErrInvalidProposalAmount + } + if csp.Recipient.Empty() { + return types.ErrEmptyProposalRecipient + } + + return nil +} + +// String implements the Stringer interface. +func (csp CommunityPoolSpendProposal) String() string { + var b strings.Builder + b.WriteString(fmt.Sprintf(`Community Pool Spend Proposal: + Title: %s + Description: %s + Recipient: %s + Amount: %s +`, csp.Title, csp.Description, csp.Recipient, csp.Amount)) + return b.String() +} + +func RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { + cdc.RegisterConcrete(CommunityPoolSpendProposal{}, "cosmos-sdk/CommunityPoolSpendProposal", nil) +} diff --git a/x/genutil/legacy/v036/migrate.go b/x/genutil/legacy/v036/migrate.go index ea576c41e..8b7136159 100644 --- a/x/genutil/legacy/v036/migrate.go +++ b/x/genutil/legacy/v036/migrate.go @@ -27,6 +27,7 @@ func Migrate(appState types.AppMap, _ client.Context) types.AppMap { v036Codec := codec.NewLegacyAmino() cryptocodec.RegisterCrypto(v036Codec) v036gov.RegisterLegacyAminoCodec(v036Codec) + v036distr.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 267dccda0..cd71f57b1 100644 --- a/x/genutil/legacy/v038/migrate.go +++ b/x/genutil/legacy/v038/migrate.go @@ -10,6 +10,7 @@ import ( v038distr "github.com/cosmos/cosmos-sdk/x/distribution/legacy/v038" 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" v036staking "github.com/cosmos/cosmos-sdk/x/staking/legacy/v036" v038staking "github.com/cosmos/cosmos-sdk/x/staking/legacy/v038" ) @@ -18,10 +19,14 @@ import ( func Migrate(appState types.AppMap, _ client.Context) types.AppMap { v036Codec := codec.NewLegacyAmino() cryptocodec.RegisterCrypto(v036Codec) + v036gov.RegisterLegacyAminoCodec(v036Codec) + v036distr.RegisterLegacyAminoCodec(v036Codec) v038Codec := codec.NewLegacyAmino() cryptocodec.RegisterCrypto(v038Codec) v038auth.RegisterLegacyAminoCodec(v038Codec) + v036gov.RegisterLegacyAminoCodec(v038Codec) + v036distr.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 3b427fd88..bf112c152 100644 --- a/x/genutil/legacy/v039/migrate.go +++ b/x/genutil/legacy/v039/migrate.go @@ -6,7 +6,9 @@ import ( cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" v038auth "github.com/cosmos/cosmos-sdk/x/auth/legacy/v038" v039auth "github.com/cosmos/cosmos-sdk/x/auth/legacy/v039" + 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" ) // Migrate migrates exported state from v0.38 to a v0.39 genesis state. @@ -17,10 +19,14 @@ func Migrate(appState types.AppMap, _ client.Context) types.AppMap { v038Codec := codec.NewLegacyAmino() cryptocodec.RegisterCrypto(v038Codec) v038auth.RegisterLegacyAminoCodec(v038Codec) + v036gov.RegisterLegacyAminoCodec(v038Codec) + v036distr.RegisterLegacyAminoCodec(v038Codec) v039Codec := codec.NewLegacyAmino() cryptocodec.RegisterCrypto(v039Codec) v039auth.RegisterLegacyAminoCodec(v039Codec) + v036gov.RegisterLegacyAminoCodec(v039Codec) + v036distr.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 c07bcdb74..ec7103868 100644 --- a/x/genutil/legacy/v040/migrate.go +++ b/x/genutil/legacy/v040/migrate.go @@ -11,8 +11,9 @@ import ( v040bank "github.com/cosmos/cosmos-sdk/x/bank/legacy/v040" v039crisis "github.com/cosmos/cosmos-sdk/x/crisis/legacy/v039" v040crisis "github.com/cosmos/cosmos-sdk/x/crisis/legacy/v040" - v038distribution "github.com/cosmos/cosmos-sdk/x/distribution/legacy/v038" - v040distribution "github.com/cosmos/cosmos-sdk/x/distribution/legacy/v040" + v036distr "github.com/cosmos/cosmos-sdk/x/distribution/legacy/v036" + v038distr "github.com/cosmos/cosmos-sdk/x/distribution/legacy/v038" + v040distr "github.com/cosmos/cosmos-sdk/x/distribution/legacy/v040" v038evidence "github.com/cosmos/cosmos-sdk/x/evidence/legacy/v038" v040evidence "github.com/cosmos/cosmos-sdk/x/evidence/legacy/v040" v039genutil "github.com/cosmos/cosmos-sdk/x/genutil/legacy/v039" @@ -38,6 +39,8 @@ 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) v040Codec := clientCtx.JSONMarshaler @@ -94,17 +97,17 @@ func Migrate(appState types.AppMap, clientCtx client.Context) types.AppMap { } // Migrate x/distribution. - if appState[v038distribution.ModuleName] != nil { + if appState[v038distr.ModuleName] != nil { // unmarshal relative source genesis application state - var distributionGenState v038distribution.GenesisState - v039Codec.MustUnmarshalJSON(appState[v038distribution.ModuleName], &distributionGenState) + var distributionGenState v038distr.GenesisState + v039Codec.MustUnmarshalJSON(appState[v038distr.ModuleName], &distributionGenState) // delete deprecated x/distribution genesis state - delete(appState, v038distribution.ModuleName) + delete(appState, v038distr.ModuleName) // Migrate relative source genesis application state and marshal it into // the respective key. - appState[v040distribution.ModuleName] = v040Codec.MustMarshalJSON(v040distribution.Migrate(distributionGenState)) + appState[v040distr.ModuleName] = v040Codec.MustMarshalJSON(v040distr.Migrate(distributionGenState)) } // Migrate x/evidence. diff --git a/x/gov/legacy/v040/migrate.go b/x/gov/legacy/v040/migrate.go index 73ee066dd..223eb6626 100644 --- a/x/gov/legacy/v040/migrate.go +++ b/x/gov/legacy/v040/migrate.go @@ -4,6 +4,8 @@ import ( "fmt" 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" @@ -59,10 +61,30 @@ func migrateProposalStatus(oldProposalStatus v034gov.ProposalStatus) v040gov.Pro func migrateContent(oldContent v036gov.Content) *codectypes.Any { switch oldContent := oldContent.(type) { - case *v040gov.TextProposal: + case v036gov.TextProposal: { + protoProposal := &v040gov.TextProposal{ + Title: oldContent.Title, + Description: oldContent.Description, + } // Convert the content into Any. - contentAny, err := codectypes.NewAnyWithValue(oldContent) + contentAny, err := codectypes.NewAnyWithValue(protoProposal) + if err != nil { + panic(err) + } + + return contentAny + } + case v036distr.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) } diff --git a/x/gov/legacy/v040/migrate_test.go b/x/gov/legacy/v040/migrate_test.go new file mode 100644 index 000000000..beefa9de5 --- /dev/null +++ b/x/gov/legacy/v040/migrate_test.go @@ -0,0 +1,128 @@ +package v040_test + +import ( + "encoding/json" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/simapp" + sdk "github.com/cosmos/cosmos-sdk/types" + 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" +) + +func TestMigrate(t *testing.T) { + encodingConfig := simapp.MakeEncodingConfig() + clientCtx := client.Context{}. + WithInterfaceRegistry(encodingConfig.InterfaceRegistry). + WithTxConfig(encodingConfig.TxConfig). + WithLegacyAmino(encodingConfig.Amino). + WithJSONMarshaler(encodingConfig.Marshaler) + + recipient, err := sdk.AccAddressFromBech32("cosmos1fl48vsnmsdzcv85q5d2q4z5ajdha8yu34mf0eh") + require.NoError(t, err) + govGenState := v036gov.GenesisState{ + Proposals: []v036gov.Proposal{ + { + Content: v036gov.TextProposal{ + Title: "foo_test", + Description: "bar_test", + }, + }, + { + Content: v036distr.CommunityPoolSpendProposal{ + Title: "foo_community", + Description: "bar_community", + Recipient: recipient, + Amount: sdk.NewCoins(sdk.NewCoin("footoken", sdk.NewInt(2))), + }, + }, + }, + } + + migrated := v040gov.Migrate(govGenState) + + bz, err := clientCtx.JSONMarshaler.MarshalJSON(migrated) + require.NoError(t, err) + + // Indent the JSON bz correctly. + var jsonObj map[string]interface{} + err = json.Unmarshal(bz, &jsonObj) + require.NoError(t, err) + indentedBz, err := json.MarshalIndent(jsonObj, "", " ") + require.NoError(t, err) + + // Make sure about: + // - TextProposal and CommunityPoolSpendProposal have correct any 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" + } +}` + + require.Equal(t, expected, string(indentedBz)) +}