From 093f48e0e498c968033a74dcc6ec7b1219ffb91a Mon Sep 17 00:00:00 2001 From: Amaury M <1293565+amaurym@users.noreply.github.com> Date: Wed, 26 Jan 2022 14:47:37 +0100 Subject: [PATCH 01/10] Add proposal migration --- x/gov/keeper/migrations.go | 6 ++ x/gov/migrations/v046/json.go | 32 +++++++ x/gov/migrations/v046/json_test.go | 128 ++++++++++++++++++++++++++++ x/gov/migrations/v046/keys.go | 31 +++++++ x/gov/migrations/v046/store.go | 81 ++++++++++++++++++ x/gov/migrations/v046/store_test.go | 92 ++++++++++++++++++++ x/gov/module.go | 6 +- 7 files changed, 375 insertions(+), 1 deletion(-) create mode 100644 x/gov/migrations/v046/json.go create mode 100644 x/gov/migrations/v046/json_test.go create mode 100644 x/gov/migrations/v046/keys.go create mode 100644 x/gov/migrations/v046/store.go create mode 100644 x/gov/migrations/v046/store_test.go diff --git a/x/gov/keeper/migrations.go b/x/gov/keeper/migrations.go index 290e03cea5c6..9b87a5bcd062 100644 --- a/x/gov/keeper/migrations.go +++ b/x/gov/keeper/migrations.go @@ -2,6 +2,7 @@ package keeper import ( sdk "github.com/cosmos/cosmos-sdk/types" + v046 "github.com/cosmos/cosmos-sdk/x/bank/migrations/v046" v043 "github.com/cosmos/cosmos-sdk/x/gov/migrations/v043" ) @@ -19,3 +20,8 @@ func NewMigrator(keeper Keeper) Migrator { func (m Migrator) Migrate1to2(ctx sdk.Context) error { return v043.MigrateStore(ctx, m.keeper.storeKey, m.keeper.cdc) } + +// Migrate2to3 migrates from version 2 to 3. +func (m Migrator) Migrate2to3(ctx sdk.Context) error { + return v046.MigrateStore(ctx, m.keeper.storeKey, m.keeper.cdc) +} diff --git a/x/gov/migrations/v046/json.go b/x/gov/migrations/v046/json.go new file mode 100644 index 000000000000..f960cfadb389 --- /dev/null +++ b/x/gov/migrations/v046/json.go @@ -0,0 +1,32 @@ +package v046 + +import ( + "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" + "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta2" +) + +// migrateWeightedVotes migrates the ADR-037 weighted votes. +func migrateJSONProposals(oldVotes v1beta1.Votes) v1beta1.Votes { + newVotes := make(v1beta1.Votes, len(oldVotes)) + for i, oldVote := range oldVotes { + newVotes[i] = migrateVote(oldVote) + } + + return newVotes +} + +// MigrateJSON accepts exported v0.40 x/gov genesis state and migrates it to +// v0.43 x/gov genesis state. The migration includes: +// +// - Gov weighted votes. +func MigrateJSON(oldState *v1beta1.GenesisState) *v1beta2.GenesisState { + return &v1beta1.GenesisState{ + StartingProposalId: oldState.StartingProposalId, + Deposits: oldState.Deposits, + Votes: oldState.Votes, + Proposals: oldState.Proposals, + DepositParams: oldState.DepositParams, + VotingParams: oldState.VotingParams, + TallyParams: oldState.TallyParams, + } +} diff --git a/x/gov/migrations/v046/json_test.go b/x/gov/migrations/v046/json_test.go new file mode 100644 index 000000000000..4f08c481059b --- /dev/null +++ b/x/gov/migrations/v046/json_test.go @@ -0,0 +1,128 @@ +package v046_test + +import ( + "encoding/json" + "fmt" + "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" + v043gov "github.com/cosmos/cosmos-sdk/x/gov/migrations/v043" + "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" +) + +func TestMigrateJSON(t *testing.T) { + encodingConfig := simapp.MakeTestEncodingConfig() + clientCtx := client.Context{}. + WithInterfaceRegistry(encodingConfig.InterfaceRegistry). + WithTxConfig(encodingConfig.TxConfig). + WithCodec(encodingConfig.Codec) + + voter, err := sdk.AccAddressFromBech32("cosmos1fl48vsnmsdzcv85q5d2q4z5ajdha8yu34mf0eh") + require.NoError(t, err) + govGenState := &v1beta1.GenesisState{ + Votes: v1beta1.Votes{ + v1beta1.Vote{ProposalId: 1, Voter: voter.String(), Option: v1beta1.OptionAbstain}, + v1beta1.Vote{ProposalId: 2, Voter: voter.String(), Option: v1beta1.OptionEmpty}, + v1beta1.Vote{ProposalId: 3, Voter: voter.String(), Option: v1beta1.OptionNo}, + v1beta1.Vote{ProposalId: 4, Voter: voter.String(), Option: v1beta1.OptionNoWithVeto}, + v1beta1.Vote{ProposalId: 5, Voter: voter.String(), Option: v1beta1.OptionYes}, + }, + } + + migrated := v043gov.MigrateJSON(govGenState) + + bz, err := clientCtx.Codec.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, "", "\t") + require.NoError(t, err) + + // Make sure about: + // - Votes are all ADR-037 weighted votes with weight 1. + expected := `{ + "deposit_params": { + "max_deposit_period": "0s", + "min_deposit": [] + }, + "deposits": [], + "proposals": [], + "starting_proposal_id": "0", + "tally_params": { + "quorum": "0", + "threshold": "0", + "veto_threshold": "0" + }, + "votes": [ + { + "option": "VOTE_OPTION_UNSPECIFIED", + "options": [ + { + "option": "VOTE_OPTION_ABSTAIN", + "weight": "1.000000000000000000" + } + ], + "proposal_id": "1", + "voter": "cosmos1fl48vsnmsdzcv85q5d2q4z5ajdha8yu34mf0eh" + }, + { + "option": "VOTE_OPTION_UNSPECIFIED", + "options": [ + { + "option": "VOTE_OPTION_UNSPECIFIED", + "weight": "1.000000000000000000" + } + ], + "proposal_id": "2", + "voter": "cosmos1fl48vsnmsdzcv85q5d2q4z5ajdha8yu34mf0eh" + }, + { + "option": "VOTE_OPTION_UNSPECIFIED", + "options": [ + { + "option": "VOTE_OPTION_NO", + "weight": "1.000000000000000000" + } + ], + "proposal_id": "3", + "voter": "cosmos1fl48vsnmsdzcv85q5d2q4z5ajdha8yu34mf0eh" + }, + { + "option": "VOTE_OPTION_UNSPECIFIED", + "options": [ + { + "option": "VOTE_OPTION_NO_WITH_VETO", + "weight": "1.000000000000000000" + } + ], + "proposal_id": "4", + "voter": "cosmos1fl48vsnmsdzcv85q5d2q4z5ajdha8yu34mf0eh" + }, + { + "option": "VOTE_OPTION_UNSPECIFIED", + "options": [ + { + "option": "VOTE_OPTION_YES", + "weight": "1.000000000000000000" + } + ], + "proposal_id": "5", + "voter": "cosmos1fl48vsnmsdzcv85q5d2q4z5ajdha8yu34mf0eh" + } + ], + "voting_params": { + "voting_period": "0s" + } +}` + + fmt.Println(string(indentedBz)) + + require.Equal(t, expected, string(indentedBz)) +} diff --git a/x/gov/migrations/v046/keys.go b/x/gov/migrations/v046/keys.go new file mode 100644 index 000000000000..6e3656916f1d --- /dev/null +++ b/x/gov/migrations/v046/keys.go @@ -0,0 +1,31 @@ +package v046 + +const ( + // ModuleName is the name of the module + ModuleName = "gov" +) + +// Keys for governance store +// Items are stored with the following key: values +// +// - 0x00: Proposal +// +// - 0x01: activeProposalID +// +// - 0x02: inactiveProposalID +// +// - 0x03: nextProposalID +// +// - 0x10: Deposit +// +// - 0x20: Voter +var ( + ProposalsKeyPrefix = []byte{0x00} + ActiveProposalQueuePrefix = []byte{0x01} + InactiveProposalQueuePrefix = []byte{0x02} + ProposalIDKey = []byte{0x03} + + DepositsKeyPrefix = []byte{0x10} + + VotesKeyPrefix = []byte{0x20} +) diff --git a/x/gov/migrations/v046/store.go b/x/gov/migrations/v046/store.go new file mode 100644 index 000000000000..642cb0243a49 --- /dev/null +++ b/x/gov/migrations/v046/store.go @@ -0,0 +1,81 @@ +package v046 + +import ( + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/store/prefix" + storetypes "github.com/cosmos/cosmos-sdk/store/types" + sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" + "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta2" +) + +func convertProposal(oldProp v1beta1.Proposal) (v1beta2.Proposal, error) { + msg, err := v1beta2.NewLegacyContent(oldProp.GetContent(), authtypes.NewModuleAddress(ModuleName).String()) + if err != nil { + return v1beta2.Proposal{}, err + } + msgAny, err := codectypes.NewAnyWithValue(msg) + if err != nil { + return v1beta2.Proposal{}, err + } + + return v1beta2.Proposal{ + ProposalId: oldProp.ProposalId, + Messages: []*codectypes.Any{msgAny}, + Status: v1beta2.ProposalStatus(oldProp.Status), + FinalTallyResult: &v1beta2.TallyResult{ + Yes: oldProp.FinalTallyResult.Yes.String(), + No: oldProp.FinalTallyResult.No.String(), + Abstain: oldProp.FinalTallyResult.Abstain.String(), + NoWithVeto: oldProp.FinalTallyResult.NoWithVeto.String(), + }, + SubmitTime: &oldProp.SubmitTime, + DepositEndTime: &oldProp.DepositEndTime, + TotalDeposit: oldProp.TotalDeposit, + VotingStartTime: &oldProp.VotingStartTime, + VotingEndTime: &oldProp.VotingEndTime, + }, nil +} + +// migrateProposals migrates all legacy proposals into MsgExecLegacyContent +// proposals. +func migrateProposals(store sdk.KVStore, cdc codec.BinaryCodec) error { + propStore := prefix.NewStore(store, ProposalsKeyPrefix) + + iter := propStore.Iterator(nil, nil) + defer iter.Close() + + for ; iter.Valid(); iter.Next() { + var oldProp v1beta1.Proposal + err := cdc.Unmarshal(iter.Value(), &oldProp) + if err != nil { + return err + } + + newProp, err := convertProposal(oldProp) + if err != nil { + return err + } + bz, err := cdc.Marshal(&newProp) + if err != nil { + return err + } + + // Set new value on store. + store.Set(iter.Key(), bz) + } + + return nil +} + +// MigrateStore performs in-place store migrations from v0.40 to v0.43. The +// migration includes: +// +// - Migrate proposals to be Msg-based. +func MigrateStore(ctx sdk.Context, storeKey storetypes.StoreKey, cdc codec.BinaryCodec) error { + store := ctx.KVStore(storeKey) + + return migrateProposals(store, cdc) +} diff --git a/x/gov/migrations/v046/store_test.go b/x/gov/migrations/v046/store_test.go new file mode 100644 index 000000000000..827d4e6bd32a --- /dev/null +++ b/x/gov/migrations/v046/store_test.go @@ -0,0 +1,92 @@ +package v046_test + +import ( + "bytes" + "testing" + "time" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/simapp" + "github.com/cosmos/cosmos-sdk/testutil" + "github.com/cosmos/cosmos-sdk/testutil/testdata" + sdk "github.com/cosmos/cosmos-sdk/types" + v040gov "github.com/cosmos/cosmos-sdk/x/gov/migrations/v040" + v043gov "github.com/cosmos/cosmos-sdk/x/gov/migrations/v043" + "github.com/cosmos/cosmos-sdk/x/gov/types" + "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" +) + +func TestMigrateStore(t *testing.T) { + cdc := simapp.MakeTestEncodingConfig().Codec + govKey := sdk.NewKVStoreKey("gov") + ctx := testutil.DefaultContext(govKey, sdk.NewTransientStoreKey("transient_test")) + store := ctx.KVStore(govKey) + + _, _, addr1 := testdata.KeyTestPubAddr() + proposalID := uint64(6) + now := time.Now() + // Use dummy value for keys where we don't test values. + dummyValue := []byte("foo") + // Use real values for votes, as we're testing weighted votes. + oldVote := v1beta1.Vote{ProposalId: 1, Voter: "foobar", Option: v1beta1.OptionNoWithVeto} + oldVoteValue := cdc.MustMarshal(&oldVote) + newVote := v1beta1.Vote{ProposalId: 1, Voter: "foobar", Options: v1beta1.WeightedVoteOptions{{Option: v1beta1.OptionNoWithVeto, Weight: sdk.NewDec(1)}}} + newVoteValue := cdc.MustMarshal(&newVote) + + testCases := []struct { + name string + oldKey, oldValue, newKey, newValue []byte + }{ + { + "ProposalKey", + v040gov.ProposalKey(proposalID), dummyValue, + types.ProposalKey(proposalID), dummyValue, + }, + { + "ActiveProposalQueue", + v040gov.ActiveProposalQueueKey(proposalID, now), dummyValue, + types.ActiveProposalQueueKey(proposalID, now), dummyValue, + }, + { + "InactiveProposalQueue", + v040gov.InactiveProposalQueueKey(proposalID, now), dummyValue, + types.InactiveProposalQueueKey(proposalID, now), dummyValue, + }, + { + "ProposalIDKey", + v040gov.ProposalIDKey, dummyValue, + types.ProposalIDKey, dummyValue, + }, + { + "DepositKey", + v040gov.DepositKey(proposalID, addr1), dummyValue, + types.DepositKey(proposalID, addr1), dummyValue, + }, + { + "VotesKeyPrefix", + v040gov.VoteKey(proposalID, addr1), oldVoteValue, + types.VoteKey(proposalID, addr1), newVoteValue, + }, + } + + // Set all the old keys to the store + for _, tc := range testCases { + store.Set(tc.oldKey, tc.oldValue) + } + + // Run migrations. + err := v043gov.MigrateStore(ctx, govKey, cdc) + require.NoError(t, err) + + // Make sure the new keys are set and old keys are deleted. + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + if !bytes.Equal(tc.oldKey, tc.newKey) { + require.Nil(t, store.Get(tc.oldKey)) + } + require.Equal(t, tc.newValue, store.Get(tc.newKey)) + }) + } +} diff --git a/x/gov/module.go b/x/gov/module.go index 3e2002efe342..31ad9cd7ebd5 100644 --- a/x/gov/module.go +++ b/x/gov/module.go @@ -169,6 +169,10 @@ func (am AppModule) RegisterServices(cfg module.Configurator) { if err != nil { panic(err) } + err = cfg.RegisterMigration(types.ModuleName, 2, m.Migrate2to3) + if err != nil { + panic(err) + } } // InitGenesis performs genesis initialization for the gov module. It returns @@ -188,7 +192,7 @@ func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec) json.Raw } // ConsensusVersion implements AppModule/ConsensusVersion. -func (AppModule) ConsensusVersion() uint64 { return 2 } +func (AppModule) ConsensusVersion() uint64 { return 3 } // BeginBlock performs a no-op. func (AppModule) BeginBlock(_ sdk.Context, _ abci.RequestBeginBlock) {} From eaa94fd25dc8e1003f563e949a360d07a6706362 Mon Sep 17 00:00:00 2001 From: Amaury M <1293565+amaurym@users.noreply.github.com> Date: Thu, 27 Jan 2022 11:32:45 +0100 Subject: [PATCH 02/10] Add conversion functions --- x/gov/migrations/v046/convert.go | 104 +++++++++++++++++++++++++++++++ x/gov/migrations/v046/json.go | 36 +++++------ x/gov/migrations/v046/store.go | 33 +--------- 3 files changed, 122 insertions(+), 51 deletions(-) create mode 100644 x/gov/migrations/v046/convert.go diff --git a/x/gov/migrations/v046/convert.go b/x/gov/migrations/v046/convert.go new file mode 100644 index 000000000000..045bfee396d7 --- /dev/null +++ b/x/gov/migrations/v046/convert.go @@ -0,0 +1,104 @@ +package v046 + +import ( + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" + "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta2" +) + +func convertDeposits(oldDeps v1beta1.Deposits) v1beta2.Deposits { + newDeps := make([]*v1beta2.Deposit, len(oldDeps)) + for i, oldDep := range oldDeps { + newDeps[i] = &v1beta2.Deposit{ + ProposalId: oldDep.ProposalId, + Depositor: oldDep.Depositor, + Amount: oldDep.Amount, + } + } + + return newDeps +} + +func convertVotes(oldVotes v1beta1.Votes) v1beta2.Votes { + newVotes := make([]*v1beta2.Vote, len(oldVotes)) + for i, oldVote := range oldVotes { + // All oldVotes don't have the Option field anymore, as they have been + // migrated in the v043 package. + newWVOs := make([]*v1beta2.WeightedVoteOption, len(oldVote.Options)) + for j, oldWVO := range oldVote.Options { + newWVOs[j] = v1beta2.NewWeightedVoteOption(v1beta2.VoteOption(oldVote.Option), oldWVO.Weight) + } + + newVotes[i] = &v1beta2.Vote{ + ProposalId: oldVote.ProposalId, + Voter: oldVote.Voter, + Options: newWVOs, + } + } + + return newVotes +} + +func convertDepParams(oldDepParams v1beta1.DepositParams) v1beta2.DepositParams { + return v1beta2.DepositParams{ + MinDeposit: oldDepParams.MinDeposit, + MaxDepositPeriod: &oldDepParams.MaxDepositPeriod, + } +} + +func convertVotingParams(oldVoteParams v1beta1.VotingParams) v1beta2.VotingParams { + return v1beta2.VotingParams{ + VotingPeriod: &oldVoteParams.VotingPeriod, + } +} + +func convertTallyParams(oldTallyParams v1beta1.TallyParams) v1beta2.TallyParams { + return v1beta2.TallyParams{ + Quorum: oldTallyParams.Quorum.String(), + Threshold: oldTallyParams.Threshold.String(), + VetoThreshold: oldTallyParams.VetoThreshold.String(), + } +} + +func convertProposal(oldProp v1beta1.Proposal) (v1beta2.Proposal, error) { + msg, err := v1beta2.NewLegacyContent(oldProp.GetContent(), authtypes.NewModuleAddress(ModuleName).String()) + if err != nil { + return v1beta2.Proposal{}, err + } + msgAny, err := codectypes.NewAnyWithValue(msg) + if err != nil { + return v1beta2.Proposal{}, err + } + + return v1beta2.Proposal{ + ProposalId: oldProp.ProposalId, + Messages: []*codectypes.Any{msgAny}, + Status: v1beta2.ProposalStatus(oldProp.Status), + FinalTallyResult: &v1beta2.TallyResult{ + Yes: oldProp.FinalTallyResult.Yes.String(), + No: oldProp.FinalTallyResult.No.String(), + Abstain: oldProp.FinalTallyResult.Abstain.String(), + NoWithVeto: oldProp.FinalTallyResult.NoWithVeto.String(), + }, + SubmitTime: &oldProp.SubmitTime, + DepositEndTime: &oldProp.DepositEndTime, + TotalDeposit: oldProp.TotalDeposit, + VotingStartTime: &oldProp.VotingStartTime, + VotingEndTime: &oldProp.VotingEndTime, + }, nil +} + +func convertProposals(oldProps v1beta1.Proposals) (v1beta2.Proposals, error) { + newProps := make([]*v1beta2.Proposal, len(oldProps)) + for i, oldProp := range oldProps { + p, err := convertProposal(oldProp) + if err != nil { + return nil, err + } + + newProps[i] = &p + } + + return newProps, nil +} diff --git a/x/gov/migrations/v046/json.go b/x/gov/migrations/v046/json.go index f960cfadb389..1ee1b684f57f 100644 --- a/x/gov/migrations/v046/json.go +++ b/x/gov/migrations/v046/json.go @@ -5,28 +5,26 @@ import ( "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta2" ) -// migrateWeightedVotes migrates the ADR-037 weighted votes. -func migrateJSONProposals(oldVotes v1beta1.Votes) v1beta1.Votes { - newVotes := make(v1beta1.Votes, len(oldVotes)) - for i, oldVote := range oldVotes { - newVotes[i] = migrateVote(oldVote) +// MigrateJSON accepts exported v0.43 x/gov genesis state and migrates it to +// v0.46 x/gov genesis state. The migration includes: +// +// - Updating everything to v1beta2. +// - Migrating proposals to be Msg-based. +func MigrateJSON(oldState *v1beta1.GenesisState) *v1beta2.GenesisState { + newProps, err := convertProposals(oldState.Proposals) + if err != nil { + panic(err) } - return newVotes -} + depParams, votingParms, tallyParams := convertDepParams(oldState.DepositParams), convertVotingParams(oldState.VotingParams), convertTallyParams(oldState.TallyParams) -// MigrateJSON accepts exported v0.40 x/gov genesis state and migrates it to -// v0.43 x/gov genesis state. The migration includes: -// -// - Gov weighted votes. -func MigrateJSON(oldState *v1beta1.GenesisState) *v1beta2.GenesisState { - return &v1beta1.GenesisState{ + return &v1beta2.GenesisState{ StartingProposalId: oldState.StartingProposalId, - Deposits: oldState.Deposits, - Votes: oldState.Votes, - Proposals: oldState.Proposals, - DepositParams: oldState.DepositParams, - VotingParams: oldState.VotingParams, - TallyParams: oldState.TallyParams, + Deposits: convertDeposits(oldState.Deposits), + Votes: convertVotes(oldState.Votes), + Proposals: newProps, + DepositParams: &depParams, + VotingParams: &votingParms, + TallyParams: &tallyParams, } } diff --git a/x/gov/migrations/v046/store.go b/x/gov/migrations/v046/store.go index 642cb0243a49..781332b10808 100644 --- a/x/gov/migrations/v046/store.go +++ b/x/gov/migrations/v046/store.go @@ -2,43 +2,12 @@ package v046 import ( "github.com/cosmos/cosmos-sdk/codec" - codectypes "github.com/cosmos/cosmos-sdk/codec/types" "github.com/cosmos/cosmos-sdk/store/prefix" storetypes "github.com/cosmos/cosmos-sdk/store/types" sdk "github.com/cosmos/cosmos-sdk/types" - authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" - "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta2" ) -func convertProposal(oldProp v1beta1.Proposal) (v1beta2.Proposal, error) { - msg, err := v1beta2.NewLegacyContent(oldProp.GetContent(), authtypes.NewModuleAddress(ModuleName).String()) - if err != nil { - return v1beta2.Proposal{}, err - } - msgAny, err := codectypes.NewAnyWithValue(msg) - if err != nil { - return v1beta2.Proposal{}, err - } - - return v1beta2.Proposal{ - ProposalId: oldProp.ProposalId, - Messages: []*codectypes.Any{msgAny}, - Status: v1beta2.ProposalStatus(oldProp.Status), - FinalTallyResult: &v1beta2.TallyResult{ - Yes: oldProp.FinalTallyResult.Yes.String(), - No: oldProp.FinalTallyResult.No.String(), - Abstain: oldProp.FinalTallyResult.Abstain.String(), - NoWithVeto: oldProp.FinalTallyResult.NoWithVeto.String(), - }, - SubmitTime: &oldProp.SubmitTime, - DepositEndTime: &oldProp.DepositEndTime, - TotalDeposit: oldProp.TotalDeposit, - VotingStartTime: &oldProp.VotingStartTime, - VotingEndTime: &oldProp.VotingEndTime, - }, nil -} - // migrateProposals migrates all legacy proposals into MsgExecLegacyContent // proposals. func migrateProposals(store sdk.KVStore, cdc codec.BinaryCodec) error { @@ -70,7 +39,7 @@ func migrateProposals(store sdk.KVStore, cdc codec.BinaryCodec) error { return nil } -// MigrateStore performs in-place store migrations from v0.40 to v0.43. The +// MigrateStore performs in-place store migrations from v0.43 to v0.46. The // migration includes: // // - Migrate proposals to be Msg-based. From 933cf8f7f916c5dc816921258b1625f5946a83bc Mon Sep 17 00:00:00 2001 From: Amaury M <1293565+amaurym@users.noreply.github.com> Date: Mon, 31 Jan 2022 14:21:39 +0100 Subject: [PATCH 03/10] Add JSON migration and tests --- x/genutil/client/cli/migrate.go | 4 +- x/genutil/migrations/v046/migrate.go | 32 ++++++ x/gov/migrations/v043/json_test.go | 3 - x/gov/migrations/v046/json.go | 6 +- x/gov/migrations/v046/json_test.go | 143 +++++++++++++++------------ x/gov/migrations/v046/keys.go | 25 ----- x/gov/migrations/v046/store.go | 3 +- 7 files changed, 118 insertions(+), 98 deletions(-) create mode 100644 x/genutil/migrations/v046/migrate.go diff --git a/x/genutil/client/cli/migrate.go b/x/genutil/client/cli/migrate.go index 3b8d9f5a0872..f27d979ad363 100644 --- a/x/genutil/client/cli/migrate.go +++ b/x/genutil/client/cli/migrate.go @@ -16,6 +16,7 @@ import ( "github.com/cosmos/cosmos-sdk/version" v040 "github.com/cosmos/cosmos-sdk/x/genutil/migrations/v040" v043 "github.com/cosmos/cosmos-sdk/x/genutil/migrations/v043" + v046 "github.com/cosmos/cosmos-sdk/x/genutil/migrations/v046" "github.com/cosmos/cosmos-sdk/x/genutil/types" ) @@ -26,7 +27,8 @@ const flagGenesisTime = "genesis-time" // Ref: https://github.com/cosmos/cosmos-sdk/issues/5041 var migrationMap = types.MigrationMap{ "v0.42": v040.Migrate, // NOTE: v0.40, v0.41 and v0.42 are genesis compatible. - "v0.43": v043.Migrate, + "v0.43": v043.Migrate, // NOTE: v0.43, v0.44 and v0.45 are genesis compatible. + "v0.46": v046.Migrate, } // GetMigrationCallback returns a MigrationCallback for a given version. diff --git a/x/genutil/migrations/v046/migrate.go b/x/genutil/migrations/v046/migrate.go new file mode 100644 index 000000000000..7adaaddc7b68 --- /dev/null +++ b/x/genutil/migrations/v046/migrate.go @@ -0,0 +1,32 @@ +package v046 + +import ( + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/x/genutil/types" + v043gov "github.com/cosmos/cosmos-sdk/x/gov/migrations/v043" + v046gov "github.com/cosmos/cosmos-sdk/x/gov/migrations/v046" + gov "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" +) + +// Migrate migrates exported state from v0.45 to a v0.46 genesis state. +func Migrate(appState types.AppMap, clientCtx client.Context) types.AppMap { + // Migrate x/gov. + if appState[v043gov.ModuleName] != nil { + // unmarshal relative source genesis application state + var oldGovState gov.GenesisState + clientCtx.Codec.MustUnmarshalJSON(appState[v043gov.ModuleName], &oldGovState) + + // delete deprecated x/gov genesis state + delete(appState, v043gov.ModuleName) + + // Migrate relative source genesis application state and marshal it into + // the respective key. + newGovState, err := v046gov.MigrateJSON(&oldGovState) + if err != nil { + panic(err) + } + appState[v046gov.ModuleName] = clientCtx.Codec.MustMarshalJSON(newGovState) + } + + return appState +} diff --git a/x/gov/migrations/v043/json_test.go b/x/gov/migrations/v043/json_test.go index abe85162429e..4202752aada6 100644 --- a/x/gov/migrations/v043/json_test.go +++ b/x/gov/migrations/v043/json_test.go @@ -2,7 +2,6 @@ package v043_test import ( "encoding/json" - "fmt" "testing" "github.com/stretchr/testify/require" @@ -122,7 +121,5 @@ func TestMigrateJSON(t *testing.T) { } }` - fmt.Println(string(indentedBz)) - require.Equal(t, expected, string(indentedBz)) } diff --git a/x/gov/migrations/v046/json.go b/x/gov/migrations/v046/json.go index 1ee1b684f57f..7e97dc2f645d 100644 --- a/x/gov/migrations/v046/json.go +++ b/x/gov/migrations/v046/json.go @@ -10,10 +10,10 @@ import ( // // - Updating everything to v1beta2. // - Migrating proposals to be Msg-based. -func MigrateJSON(oldState *v1beta1.GenesisState) *v1beta2.GenesisState { +func MigrateJSON(oldState *v1beta1.GenesisState) (*v1beta2.GenesisState, error) { newProps, err := convertProposals(oldState.Proposals) if err != nil { - panic(err) + return nil, err } depParams, votingParms, tallyParams := convertDepParams(oldState.DepositParams), convertVotingParams(oldState.VotingParams), convertTallyParams(oldState.TallyParams) @@ -26,5 +26,5 @@ func MigrateJSON(oldState *v1beta1.GenesisState) *v1beta2.GenesisState { DepositParams: &depParams, VotingParams: &votingParms, TallyParams: &tallyParams, - } + }, nil } diff --git a/x/gov/migrations/v046/json_test.go b/x/gov/migrations/v046/json_test.go index 4f08c481059b..adee9f0184ac 100644 --- a/x/gov/migrations/v046/json_test.go +++ b/x/gov/migrations/v046/json_test.go @@ -4,14 +4,20 @@ import ( "encoding/json" "fmt" "testing" + "time" + "github.com/gogo/protobuf/proto" "github.com/stretchr/testify/require" "github.com/cosmos/cosmos-sdk/client" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" "github.com/cosmos/cosmos-sdk/simapp" sdk "github.com/cosmos/cosmos-sdk/types" - v043gov "github.com/cosmos/cosmos-sdk/x/gov/migrations/v043" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + v046 "github.com/cosmos/cosmos-sdk/x/gov/migrations/v046" + "github.com/cosmos/cosmos-sdk/x/gov/types" "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" + "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta2" ) func TestMigrateJSON(t *testing.T) { @@ -23,17 +29,36 @@ func TestMigrateJSON(t *testing.T) { voter, err := sdk.AccAddressFromBech32("cosmos1fl48vsnmsdzcv85q5d2q4z5ajdha8yu34mf0eh") require.NoError(t, err) - govGenState := &v1beta1.GenesisState{ - Votes: v1beta1.Votes{ - v1beta1.Vote{ProposalId: 1, Voter: voter.String(), Option: v1beta1.OptionAbstain}, - v1beta1.Vote{ProposalId: 2, Voter: voter.String(), Option: v1beta1.OptionEmpty}, - v1beta1.Vote{ProposalId: 3, Voter: voter.String(), Option: v1beta1.OptionNo}, - v1beta1.Vote{ProposalId: 4, Voter: voter.String(), Option: v1beta1.OptionNoWithVeto}, - v1beta1.Vote{ProposalId: 5, Voter: voter.String(), Option: v1beta1.OptionYes}, + + govGenState := v1beta1.DefaultGenesisState() + propTime := time.Unix(9999, 0) + contentAny, err := codectypes.NewAnyWithValue(v1beta1.NewTextProposal("my title", "my desc").(proto.Message)) + require.NoError(t, err) + govGenState.Proposals = v1beta1.Proposals{ + v1beta1.Proposal{ + ProposalId: 1, + Content: contentAny, + SubmitTime: propTime, + DepositEndTime: propTime, + VotingStartTime: propTime, + VotingEndTime: propTime, + Status: v1beta1.StatusDepositPeriod, + FinalTallyResult: v1beta1.EmptyTallyResult(), + TotalDeposit: sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(123))), }, } + govGenState.Votes = v1beta1.Votes{ + v1beta1.Vote{ProposalId: 1, Voter: voter.String(), Option: v1beta1.OptionAbstain}, + } + + migrated, err := v046.MigrateJSON(govGenState) + require.NoError(t, err) - migrated := v043gov.MigrateJSON(govGenState) + // Make sure the migrated proposal's Msg signer is the gov acct. + require.Equal(t, + authtypes.NewModuleAddress(types.ModuleName).String(), + migrated.Proposals[0].Messages[0].GetCachedValue().(*v1beta2.MsgExecLegacyContent).Authority, + ) bz, err := clientCtx.Codec.MarshalJSON(migrated) require.NoError(t, err) @@ -46,79 +71,67 @@ func TestMigrateJSON(t *testing.T) { require.NoError(t, err) // Make sure about: - // - Votes are all ADR-037 weighted votes with weight 1. + // - Proposals use MsgExecLegacyContent expected := `{ "deposit_params": { - "max_deposit_period": "0s", - "min_deposit": [] + "max_deposit_period": "172800s", + "min_deposit": [ + { + "amount": "10000000", + "denom": "stake" + } + ] }, "deposits": [], - "proposals": [], - "starting_proposal_id": "0", - "tally_params": { - "quorum": "0", - "threshold": "0", - "veto_threshold": "0" - }, - "votes": [ + "proposals": [ { - "option": "VOTE_OPTION_UNSPECIFIED", - "options": [ + "deposit_end_time": "1970-01-01T02:46:39Z", + "final_tally_result": { + "abstain": "0", + "no": "0", + "no_with_veto": "0", + "yes": "0" + }, + "messages": [ { - "option": "VOTE_OPTION_ABSTAIN", - "weight": "1.000000000000000000" + "@type": "/cosmos.gov.v1beta2.MsgExecLegacyContent", + "authority": "cosmos10d07y265gmmuvt4z0w9aw880jnsr700j6zn9kn", + "content": { + "@type": "/cosmos.gov.v1beta1.TextProposal", + "description": "my desc", + "title": "my title" + } } ], + "metadata": null, "proposal_id": "1", - "voter": "cosmos1fl48vsnmsdzcv85q5d2q4z5ajdha8yu34mf0eh" - }, - { - "option": "VOTE_OPTION_UNSPECIFIED", - "options": [ - { - "option": "VOTE_OPTION_UNSPECIFIED", - "weight": "1.000000000000000000" - } - ], - "proposal_id": "2", - "voter": "cosmos1fl48vsnmsdzcv85q5d2q4z5ajdha8yu34mf0eh" - }, - { - "option": "VOTE_OPTION_UNSPECIFIED", - "options": [ + "status": "PROPOSAL_STATUS_DEPOSIT_PERIOD", + "submit_time": "1970-01-01T02:46:39Z", + "total_deposit": [ { - "option": "VOTE_OPTION_NO", - "weight": "1.000000000000000000" + "amount": "123", + "denom": "stake" } ], - "proposal_id": "3", - "voter": "cosmos1fl48vsnmsdzcv85q5d2q4z5ajdha8yu34mf0eh" - }, - { - "option": "VOTE_OPTION_UNSPECIFIED", - "options": [ - { - "option": "VOTE_OPTION_NO_WITH_VETO", - "weight": "1.000000000000000000" - } - ], - "proposal_id": "4", - "voter": "cosmos1fl48vsnmsdzcv85q5d2q4z5ajdha8yu34mf0eh" - }, + "voting_end_time": "1970-01-01T02:46:39Z", + "voting_start_time": "1970-01-01T02:46:39Z" + } + ], + "starting_proposal_id": "1", + "tally_params": { + "quorum": "0.334000000000000000", + "threshold": "0.500000000000000000", + "veto_threshold": "0.334000000000000000" + }, + "votes": [ { - "option": "VOTE_OPTION_UNSPECIFIED", - "options": [ - { - "option": "VOTE_OPTION_YES", - "weight": "1.000000000000000000" - } - ], - "proposal_id": "5", + "options": [], + "proposal_id": "1", "voter": "cosmos1fl48vsnmsdzcv85q5d2q4z5ajdha8yu34mf0eh" } ], "voting_params": { - "voting_period": "0s" + "voting_period": "172800s" } }` diff --git a/x/gov/migrations/v046/keys.go b/x/gov/migrations/v046/keys.go index 6e3656916f1d..aaf810f3b00d 100644 --- a/x/gov/migrations/v046/keys.go +++ b/x/gov/migrations/v046/keys.go @@ -4,28 +4,3 @@ const ( // ModuleName is the name of the module ModuleName = "gov" ) - -// Keys for governance store -// Items are stored with the following key: values -// -// - 0x00: Proposal -// -// - 0x01: activeProposalID -// -// - 0x02: inactiveProposalID -// -// - 0x03: nextProposalID -// -// - 0x10: Deposit -// -// - 0x20: Voter -var ( - ProposalsKeyPrefix = []byte{0x00} - ActiveProposalQueuePrefix = []byte{0x01} - InactiveProposalQueuePrefix = []byte{0x02} - ProposalIDKey = []byte{0x03} - - DepositsKeyPrefix = []byte{0x10} - - VotesKeyPrefix = []byte{0x20} -) diff --git a/x/gov/migrations/v046/store.go b/x/gov/migrations/v046/store.go index 781332b10808..fa1ccdcff0f3 100644 --- a/x/gov/migrations/v046/store.go +++ b/x/gov/migrations/v046/store.go @@ -5,13 +5,14 @@ import ( "github.com/cosmos/cosmos-sdk/store/prefix" storetypes "github.com/cosmos/cosmos-sdk/store/types" sdk "github.com/cosmos/cosmos-sdk/types" + v040 "github.com/cosmos/cosmos-sdk/x/gov/migrations/v040" "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" ) // migrateProposals migrates all legacy proposals into MsgExecLegacyContent // proposals. func migrateProposals(store sdk.KVStore, cdc codec.BinaryCodec) error { - propStore := prefix.NewStore(store, ProposalsKeyPrefix) + propStore := prefix.NewStore(store, v040.ProposalsKeyPrefix) iter := propStore.Iterator(nil, nil) defer iter.Close() From 341a7773a90ef72788409915fe795adc312e2581 Mon Sep 17 00:00:00 2001 From: Amaury M <1293565+amaurym@users.noreply.github.com> Date: Mon, 31 Jan 2022 15:07:58 +0100 Subject: [PATCH 04/10] Add store tests --- x/gov/migrations/v043/store.go | 1 + x/gov/migrations/v046/json_test.go | 13 ++-- x/gov/migrations/v046/store.go | 2 +- x/gov/migrations/v046/store_test.go | 112 ++++++++++++---------------- 4 files changed, 55 insertions(+), 73 deletions(-) diff --git a/x/gov/migrations/v043/store.go b/x/gov/migrations/v043/store.go index 886279ac93eb..72658f39c268 100644 --- a/x/gov/migrations/v043/store.go +++ b/x/gov/migrations/v043/store.go @@ -75,6 +75,7 @@ func migrateStoreWeightedVotes(store sdk.KVStore, cdc codec.BinaryCodec) error { // migration includes: // // - Change addresses to be length-prefixed. +// - Change all legacy votes to ADR-037 weighted votes. func MigrateStore(ctx sdk.Context, storeKey storetypes.StoreKey, cdc codec.BinaryCodec) error { store := ctx.KVStore(storeKey) migratePrefixProposalAddress(store, types.DepositsKeyPrefix) diff --git a/x/gov/migrations/v046/json_test.go b/x/gov/migrations/v046/json_test.go index adee9f0184ac..228666162abd 100644 --- a/x/gov/migrations/v046/json_test.go +++ b/x/gov/migrations/v046/json_test.go @@ -2,7 +2,6 @@ package v046_test import ( "encoding/json" - "fmt" "testing" "time" @@ -31,7 +30,7 @@ func TestMigrateJSON(t *testing.T) { require.NoError(t, err) govGenState := v1beta1.DefaultGenesisState() - propTime := time.Unix(9999, 0) + propTime := time.Unix(1e9, 0) contentAny, err := codectypes.NewAnyWithValue(v1beta1.NewTextProposal("my title", "my desc").(proto.Message)) require.NoError(t, err) govGenState.Proposals = v1beta1.Proposals{ @@ -85,7 +84,7 @@ func TestMigrateJSON(t *testing.T) { "deposits": [], "proposals": [ { - "deposit_end_time": "1970-01-01T02:46:39Z", + "deposit_end_time": "2001-09-09T01:46:40Z", "final_tally_result": { "abstain": "0", "no": "0", @@ -106,15 +105,15 @@ func TestMigrateJSON(t *testing.T) { "metadata": null, "proposal_id": "1", "status": "PROPOSAL_STATUS_DEPOSIT_PERIOD", - "submit_time": "1970-01-01T02:46:39Z", + "submit_time": "2001-09-09T01:46:40Z", "total_deposit": [ { "amount": "123", "denom": "stake" } ], - "voting_end_time": "1970-01-01T02:46:39Z", - "voting_start_time": "1970-01-01T02:46:39Z" + "voting_end_time": "2001-09-09T01:46:40Z", + "voting_start_time": "2001-09-09T01:46:40Z" } ], "starting_proposal_id": "1", @@ -135,7 +134,5 @@ func TestMigrateJSON(t *testing.T) { } }` - fmt.Println(string(indentedBz)) - require.Equal(t, expected, string(indentedBz)) } diff --git a/x/gov/migrations/v046/store.go b/x/gov/migrations/v046/store.go index fa1ccdcff0f3..565f8596e76e 100644 --- a/x/gov/migrations/v046/store.go +++ b/x/gov/migrations/v046/store.go @@ -34,7 +34,7 @@ func migrateProposals(store sdk.KVStore, cdc codec.BinaryCodec) error { } // Set new value on store. - store.Set(iter.Key(), bz) + propStore.Set(iter.Key(), bz) } return nil diff --git a/x/gov/migrations/v046/store_test.go b/x/gov/migrations/v046/store_test.go index 827d4e6bd32a..d392a9d72d3f 100644 --- a/x/gov/migrations/v046/store_test.go +++ b/x/gov/migrations/v046/store_test.go @@ -1,7 +1,6 @@ package v046_test import ( - "bytes" "testing" "time" @@ -9,12 +8,12 @@ import ( "github.com/cosmos/cosmos-sdk/simapp" "github.com/cosmos/cosmos-sdk/testutil" - "github.com/cosmos/cosmos-sdk/testutil/testdata" sdk "github.com/cosmos/cosmos-sdk/types" v040gov "github.com/cosmos/cosmos-sdk/x/gov/migrations/v040" - v043gov "github.com/cosmos/cosmos-sdk/x/gov/migrations/v043" - "github.com/cosmos/cosmos-sdk/x/gov/types" + v046gov "github.com/cosmos/cosmos-sdk/x/gov/migrations/v046" "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" + "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta2" + upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" ) func TestMigrateStore(t *testing.T) { @@ -23,70 +22,55 @@ func TestMigrateStore(t *testing.T) { ctx := testutil.DefaultContext(govKey, sdk.NewTransientStoreKey("transient_test")) store := ctx.KVStore(govKey) - _, _, addr1 := testdata.KeyTestPubAddr() - proposalID := uint64(6) - now := time.Now() - // Use dummy value for keys where we don't test values. - dummyValue := []byte("foo") - // Use real values for votes, as we're testing weighted votes. - oldVote := v1beta1.Vote{ProposalId: 1, Voter: "foobar", Option: v1beta1.OptionNoWithVeto} - oldVoteValue := cdc.MustMarshal(&oldVote) - newVote := v1beta1.Vote{ProposalId: 1, Voter: "foobar", Options: v1beta1.WeightedVoteOptions{{Option: v1beta1.OptionNoWithVeto, Weight: sdk.NewDec(1)}}} - newVoteValue := cdc.MustMarshal(&newVote) + propTime := time.Unix(1e9, 0) - testCases := []struct { - name string - oldKey, oldValue, newKey, newValue []byte - }{ - { - "ProposalKey", - v040gov.ProposalKey(proposalID), dummyValue, - types.ProposalKey(proposalID), dummyValue, - }, - { - "ActiveProposalQueue", - v040gov.ActiveProposalQueueKey(proposalID, now), dummyValue, - types.ActiveProposalQueueKey(proposalID, now), dummyValue, - }, - { - "InactiveProposalQueue", - v040gov.InactiveProposalQueueKey(proposalID, now), dummyValue, - types.InactiveProposalQueueKey(proposalID, now), dummyValue, - }, - { - "ProposalIDKey", - v040gov.ProposalIDKey, dummyValue, - types.ProposalIDKey, dummyValue, - }, - { - "DepositKey", - v040gov.DepositKey(proposalID, addr1), dummyValue, - types.DepositKey(proposalID, addr1), dummyValue, - }, - { - "VotesKeyPrefix", - v040gov.VoteKey(proposalID, addr1), oldVoteValue, - types.VoteKey(proposalID, addr1), newVoteValue, - }, - } + // Create 2 proposals + prop1, err := v1beta1.NewProposal(v1beta1.NewTextProposal("my title 1", "my desc 1"), 1, propTime, propTime) + require.NoError(t, err) + prop1Bz, err := cdc.Marshal(&prop1) + require.NoError(t, err) + prop2, err := v1beta1.NewProposal(upgradetypes.NewSoftwareUpgradeProposal("my title 2", "my desc 2", upgradetypes.Plan{ + Name: "my plan 2", + }), 2, propTime, propTime) + require.NoError(t, err) + prop2Bz, err := cdc.Marshal(&prop2) + require.NoError(t, err) - // Set all the old keys to the store - for _, tc := range testCases { - store.Set(tc.oldKey, tc.oldValue) - } + store.Set(v040gov.ProposalKey(prop1.ProposalId), prop1Bz) + store.Set(v040gov.ProposalKey(prop2.ProposalId), prop2Bz) // Run migrations. - err := v043gov.MigrateStore(ctx, govKey, cdc) + err = v046gov.MigrateStore(ctx, govKey, cdc) + require.NoError(t, err) + + var newProp1 v1beta2.Proposal + err = cdc.Unmarshal(store.Get(v040gov.ProposalKey(prop1.ProposalId)), &newProp1) require.NoError(t, err) + compareProps(t, prop1, newProp1) + + var newProp2 v1beta2.Proposal + err = cdc.Unmarshal(store.Get(v040gov.ProposalKey(prop2.ProposalId)), &newProp2) + require.NoError(t, err) + compareProps(t, prop2, newProp2) +} + +func compareProps(t *testing.T, oldProp v1beta1.Proposal, newProp v1beta2.Proposal) { + require.Equal(t, oldProp.ProposalId, newProp.ProposalId) + require.Equal(t, oldProp.TotalDeposit.String(), sdk.Coins(newProp.TotalDeposit).String()) + require.Equal(t, oldProp.Status.String(), newProp.Status.String()) + require.Equal(t, oldProp.FinalTallyResult.Yes.String(), newProp.FinalTallyResult.Yes) + require.Equal(t, oldProp.FinalTallyResult.No.String(), newProp.FinalTallyResult.No) + require.Equal(t, oldProp.FinalTallyResult.NoWithVeto.String(), newProp.FinalTallyResult.NoWithVeto) + require.Equal(t, oldProp.FinalTallyResult.Abstain.String(), newProp.FinalTallyResult.Abstain) + + newContent := newProp.Messages[0].GetCachedValue().(*v1beta2.MsgExecLegacyContent).Content.GetCachedValue().(v1beta1.Content) + require.Equal(t, oldProp.Content.GetCachedValue().(v1beta1.Content), newContent) - // Make sure the new keys are set and old keys are deleted. - for _, tc := range testCases { - tc := tc - t.Run(tc.name, func(t *testing.T) { - if !bytes.Equal(tc.oldKey, tc.newKey) { - require.Nil(t, store.Get(tc.oldKey)) - } - require.Equal(t, tc.newValue, store.Get(tc.newKey)) - }) - } + // Compare UNIX times, as a simple Equal gives difference between Local and + // UTC times. + // ref: https://github.com/golang/go/issues/19486#issuecomment-292968278 + require.Equal(t, oldProp.SubmitTime.Unix(), newProp.SubmitTime.Unix()) + require.Equal(t, oldProp.DepositEndTime.Unix(), newProp.DepositEndTime.Unix()) + require.Equal(t, oldProp.VotingStartTime.Unix(), newProp.VotingStartTime.Unix()) + require.Equal(t, oldProp.VotingEndTime.Unix(), newProp.VotingEndTime.Unix()) } From 9ed859f793e1c2d3d2abd0d3f9bf9df0c8cf9eaf Mon Sep 17 00:00:00 2001 From: Amaury M <1293565+amaurym@users.noreply.github.com> Date: Wed, 2 Feb 2022 11:17:02 +0100 Subject: [PATCH 05/10] Move all conversion functions inside one file --- x/gov/migrations/v046/convert.go | 129 +++++++++++++++++++++++++++++-- x/gov/migrations/v046/json.go | 8 +- x/gov/migrations/v046/migrate.go | 119 ---------------------------- 3 files changed, 125 insertions(+), 131 deletions(-) delete mode 100644 x/gov/migrations/v046/migrate.go diff --git a/x/gov/migrations/v046/convert.go b/x/gov/migrations/v046/convert.go index 045bfee396d7..6a129335ebdc 100644 --- a/x/gov/migrations/v046/convert.go +++ b/x/gov/migrations/v046/convert.go @@ -1,13 +1,126 @@ package v046 import ( + "fmt" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/types" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta2" ) -func convertDeposits(oldDeps v1beta1.Deposits) v1beta2.Deposits { +// ConvertToLegacyProposal takes a new proposal and attempts to convert it to the +// legacy proposal format. This conversion is best effort. New proposal types that +// don't have a legacy message will return a "nil" content. +func ConvertToLegacyProposal(proposal v1beta2.Proposal) (v1beta1.Proposal, error) { + var err error + legacyProposal := v1beta1.Proposal{ + ProposalId: proposal.ProposalId, + Status: v1beta1.ProposalStatus(proposal.Status), + TotalDeposit: types.NewCoins(proposal.TotalDeposit...), + } + + legacyProposal.FinalTallyResult, err = ConvertToLegacyTallyResult(proposal.FinalTallyResult) + if err != nil { + return v1beta1.Proposal{}, err + } + + if proposal.VotingStartTime != nil { + legacyProposal.VotingStartTime = *proposal.VotingStartTime + } + + if proposal.VotingEndTime != nil { + legacyProposal.VotingEndTime = *proposal.VotingEndTime + } + + if proposal.SubmitTime != nil { + legacyProposal.SubmitTime = *proposal.SubmitTime + } + + if proposal.DepositEndTime != nil { + legacyProposal.DepositEndTime = *proposal.DepositEndTime + } + + msgs, err := proposal.GetMsgs() + if err != nil { + return v1beta1.Proposal{}, err + } + for _, msg := range msgs { + if legacyMsg, ok := msg.(*v1beta2.MsgExecLegacyContent); ok { + // check that the content struct can be unmarshalled + _, err := v1beta2.LegacyContentFromMessage(legacyMsg) + if err != nil { + return v1beta1.Proposal{}, err + } + legacyProposal.Content = legacyMsg.Content + } + } + return legacyProposal, nil +} + +func ConvertToLegacyTallyResult(tally *v1beta2.TallyResult) (v1beta1.TallyResult, error) { + yes, ok := types.NewIntFromString(tally.Yes) + if !ok { + return v1beta1.TallyResult{}, fmt.Errorf("unable to convert yes tally string (%s) to int", tally.Yes) + } + no, ok := types.NewIntFromString(tally.No) + if !ok { + return v1beta1.TallyResult{}, fmt.Errorf("unable to convert no tally string (%s) to int", tally.No) + } + veto, ok := types.NewIntFromString(tally.NoWithVeto) + if !ok { + return v1beta1.TallyResult{}, fmt.Errorf("unable to convert no with veto tally string (%s) to int", tally.NoWithVeto) + } + abstain, ok := types.NewIntFromString(tally.Abstain) + if !ok { + return v1beta1.TallyResult{}, fmt.Errorf("unable to convert abstain tally string (%s) to int", tally.Abstain) + } + + return v1beta1.TallyResult{ + Yes: yes, + No: no, + NoWithVeto: veto, + Abstain: abstain, + }, nil +} + +func ConvertToLegacyVote(vote v1beta2.Vote) (v1beta1.Vote, error) { + options, err := ConvertToLegacyVoteOptions(vote.Options) + if err != nil { + return v1beta1.Vote{}, err + } + return v1beta1.Vote{ + ProposalId: vote.ProposalId, + Voter: vote.Voter, + Options: options, + }, nil +} + +func ConvertToLegacyVoteOptions(voteOptions []*v1beta2.WeightedVoteOption) ([]v1beta1.WeightedVoteOption, error) { + options := make([]v1beta1.WeightedVoteOption, len(voteOptions)) + for i, option := range voteOptions { + weight, err := types.NewDecFromStr(option.Weight) + if err != nil { + return options, err + } + options[i] = v1beta1.WeightedVoteOption{ + Option: v1beta1.VoteOption(option.Option), + Weight: weight, + } + } + return options, nil +} + +func ConvertToLegacyDeposit(deposit *v1beta2.Deposit) v1beta1.Deposit { + return v1beta1.Deposit{ + ProposalId: deposit.ProposalId, + Depositor: deposit.Depositor, + Amount: types.NewCoins(deposit.Amount...), + } +} + +func convertToNewDeposits(oldDeps v1beta1.Deposits) v1beta2.Deposits { newDeps := make([]*v1beta2.Deposit, len(oldDeps)) for i, oldDep := range oldDeps { newDeps[i] = &v1beta2.Deposit{ @@ -20,7 +133,7 @@ func convertDeposits(oldDeps v1beta1.Deposits) v1beta2.Deposits { return newDeps } -func convertVotes(oldVotes v1beta1.Votes) v1beta2.Votes { +func convertToNewVotes(oldVotes v1beta1.Votes) v1beta2.Votes { newVotes := make([]*v1beta2.Vote, len(oldVotes)) for i, oldVote := range oldVotes { // All oldVotes don't have the Option field anymore, as they have been @@ -40,20 +153,20 @@ func convertVotes(oldVotes v1beta1.Votes) v1beta2.Votes { return newVotes } -func convertDepParams(oldDepParams v1beta1.DepositParams) v1beta2.DepositParams { +func convertToNewDepParams(oldDepParams v1beta1.DepositParams) v1beta2.DepositParams { return v1beta2.DepositParams{ MinDeposit: oldDepParams.MinDeposit, MaxDepositPeriod: &oldDepParams.MaxDepositPeriod, } } -func convertVotingParams(oldVoteParams v1beta1.VotingParams) v1beta2.VotingParams { +func convertToNewVotingParams(oldVoteParams v1beta1.VotingParams) v1beta2.VotingParams { return v1beta2.VotingParams{ VotingPeriod: &oldVoteParams.VotingPeriod, } } -func convertTallyParams(oldTallyParams v1beta1.TallyParams) v1beta2.TallyParams { +func convertToNewTallyParams(oldTallyParams v1beta1.TallyParams) v1beta2.TallyParams { return v1beta2.TallyParams{ Quorum: oldTallyParams.Quorum.String(), Threshold: oldTallyParams.Threshold.String(), @@ -61,7 +174,7 @@ func convertTallyParams(oldTallyParams v1beta1.TallyParams) v1beta2.TallyParams } } -func convertProposal(oldProp v1beta1.Proposal) (v1beta2.Proposal, error) { +func convertToNewProposal(oldProp v1beta1.Proposal) (v1beta2.Proposal, error) { msg, err := v1beta2.NewLegacyContent(oldProp.GetContent(), authtypes.NewModuleAddress(ModuleName).String()) if err != nil { return v1beta2.Proposal{}, err @@ -89,10 +202,10 @@ func convertProposal(oldProp v1beta1.Proposal) (v1beta2.Proposal, error) { }, nil } -func convertProposals(oldProps v1beta1.Proposals) (v1beta2.Proposals, error) { +func convertToNewProposals(oldProps v1beta1.Proposals) (v1beta2.Proposals, error) { newProps := make([]*v1beta2.Proposal, len(oldProps)) for i, oldProp := range oldProps { - p, err := convertProposal(oldProp) + p, err := convertToNewProposal(oldProp) if err != nil { return nil, err } diff --git a/x/gov/migrations/v046/json.go b/x/gov/migrations/v046/json.go index 7e97dc2f645d..e24036bc3888 100644 --- a/x/gov/migrations/v046/json.go +++ b/x/gov/migrations/v046/json.go @@ -11,17 +11,17 @@ import ( // - Updating everything to v1beta2. // - Migrating proposals to be Msg-based. func MigrateJSON(oldState *v1beta1.GenesisState) (*v1beta2.GenesisState, error) { - newProps, err := convertProposals(oldState.Proposals) + newProps, err := convertToNewProposals(oldState.Proposals) if err != nil { return nil, err } - depParams, votingParms, tallyParams := convertDepParams(oldState.DepositParams), convertVotingParams(oldState.VotingParams), convertTallyParams(oldState.TallyParams) + depParams, votingParms, tallyParams := convertToNewDepParams(oldState.DepositParams), convertToNewVotingParams(oldState.VotingParams), convertToNewTallyParams(oldState.TallyParams) return &v1beta2.GenesisState{ StartingProposalId: oldState.StartingProposalId, - Deposits: convertDeposits(oldState.Deposits), - Votes: convertVotes(oldState.Votes), + Deposits: convertToNewDeposits(oldState.Deposits), + Votes: convertToNewVotes(oldState.Votes), Proposals: newProps, DepositParams: &depParams, VotingParams: &votingParms, diff --git a/x/gov/migrations/v046/migrate.go b/x/gov/migrations/v046/migrate.go deleted file mode 100644 index 991e87376338..000000000000 --- a/x/gov/migrations/v046/migrate.go +++ /dev/null @@ -1,119 +0,0 @@ -package v046 - -import ( - "fmt" - - "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" - "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta2" -) - -// ConvertToLegacyProposal takes a new proposal and attempts to convert it to the -// legacy proposal format. This conversion is best effort. New proposal types that -// don't have a legacy message will return a "nil" content. -func ConvertToLegacyProposal(proposal v1beta2.Proposal) (v1beta1.Proposal, error) { - var err error - legacyProposal := v1beta1.Proposal{ - ProposalId: proposal.ProposalId, - Status: v1beta1.ProposalStatus(proposal.Status), - TotalDeposit: types.NewCoins(proposal.TotalDeposit...), - } - - legacyProposal.FinalTallyResult, err = ConvertToLegacyTallyResult(proposal.FinalTallyResult) - if err != nil { - return v1beta1.Proposal{}, err - } - - if proposal.VotingStartTime != nil { - legacyProposal.VotingStartTime = *proposal.VotingStartTime - } - - if proposal.VotingEndTime != nil { - legacyProposal.VotingEndTime = *proposal.VotingEndTime - } - - if proposal.SubmitTime != nil { - legacyProposal.SubmitTime = *proposal.SubmitTime - } - - if proposal.DepositEndTime != nil { - legacyProposal.DepositEndTime = *proposal.DepositEndTime - } - - msgs, err := proposal.GetMsgs() - if err != nil { - return v1beta1.Proposal{}, err - } - for _, msg := range msgs { - if legacyMsg, ok := msg.(*v1beta2.MsgExecLegacyContent); ok { - // check that the content struct can be unmarshalled - _, err := v1beta2.LegacyContentFromMessage(legacyMsg) - if err != nil { - return v1beta1.Proposal{}, err - } - legacyProposal.Content = legacyMsg.Content - } - } - return legacyProposal, nil -} - -func ConvertToLegacyTallyResult(tally *v1beta2.TallyResult) (v1beta1.TallyResult, error) { - yes, ok := types.NewIntFromString(tally.Yes) - if !ok { - return v1beta1.TallyResult{}, fmt.Errorf("unable to convert yes tally string (%s) to int", tally.Yes) - } - no, ok := types.NewIntFromString(tally.No) - if !ok { - return v1beta1.TallyResult{}, fmt.Errorf("unable to convert no tally string (%s) to int", tally.No) - } - veto, ok := types.NewIntFromString(tally.NoWithVeto) - if !ok { - return v1beta1.TallyResult{}, fmt.Errorf("unable to convert no with veto tally string (%s) to int", tally.NoWithVeto) - } - abstain, ok := types.NewIntFromString(tally.Abstain) - if !ok { - return v1beta1.TallyResult{}, fmt.Errorf("unable to convert abstain tally string (%s) to int", tally.Abstain) - } - - return v1beta1.TallyResult{ - Yes: yes, - No: no, - NoWithVeto: veto, - Abstain: abstain, - }, nil -} - -func ConvertToLegacyVote(vote v1beta2.Vote) (v1beta1.Vote, error) { - options, err := ConvertToLegacyVoteOptions(vote.Options) - if err != nil { - return v1beta1.Vote{}, err - } - return v1beta1.Vote{ - ProposalId: vote.ProposalId, - Voter: vote.Voter, - Options: options, - }, nil -} - -func ConvertToLegacyVoteOptions(voteOptions []*v1beta2.WeightedVoteOption) ([]v1beta1.WeightedVoteOption, error) { - options := make([]v1beta1.WeightedVoteOption, len(voteOptions)) - for i, option := range voteOptions { - weight, err := types.NewDecFromStr(option.Weight) - if err != nil { - return options, err - } - options[i] = v1beta1.WeightedVoteOption{ - Option: v1beta1.VoteOption(option.Option), - Weight: weight, - } - } - return options, nil -} - -func ConvertToLegacyDeposit(deposit *v1beta2.Deposit) v1beta1.Deposit { - return v1beta1.Deposit{ - ProposalId: deposit.ProposalId, - Depositor: deposit.Depositor, - Amount: types.NewCoins(deposit.Amount...), - } -} From eab183f1af2a4e197b91b483f1df43657c1b9e53 Mon Sep 17 00:00:00 2001 From: Amaury M <1293565+amaurym@users.noreply.github.com> Date: Wed, 2 Feb 2022 11:17:20 +0100 Subject: [PATCH 06/10] fix build --- x/gov/migrations/v046/store.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x/gov/migrations/v046/store.go b/x/gov/migrations/v046/store.go index 565f8596e76e..20f367fedeb0 100644 --- a/x/gov/migrations/v046/store.go +++ b/x/gov/migrations/v046/store.go @@ -24,7 +24,7 @@ func migrateProposals(store sdk.KVStore, cdc codec.BinaryCodec) error { return err } - newProp, err := convertProposal(oldProp) + newProp, err := convertToNewProposal(oldProp) if err != nil { return err } From eaae9add39f39e7415311a3a5f93daa7b94fb3ca Mon Sep 17 00:00:00 2001 From: Amaury M <1293565+amaurym@users.noreply.github.com> Date: Wed, 2 Feb 2022 11:19:48 +0100 Subject: [PATCH 07/10] Update cl --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 754a308be2be..9569c370ea43 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -60,6 +60,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ * [\#11019](https://github.com/cosmos/cosmos-sdk/pull/11019) Add `MsgCreatePermanentLockedAccount` and CLI method for creating permanent locked account * [\#10947](https://github.com/cosmos/cosmos-sdk/pull/10947) Add `AllowancesByGranter` query to the feegrant module * [\#10407](https://github.com/cosmos/cosmos-sdk/pull/10407) Add validation to `x/upgrade` module's `BeginBlock` to check accidental binary downgrades +* (gov) [\#11036](https://github.com/cosmos/cosmos-sdk/pull/11036) Add in-place migrations for 0.43->0.46. Add a `migrate v0.46` CLI command for v0.43->0.46 JSON genesis migration. ### API Breaking Changes From 2b88cd637f903e1c6090c166107311a33a51a6a6 Mon Sep 17 00:00:00 2001 From: Amaury <1293565+amaurym@users.noreply.github.com> Date: Wed, 2 Feb 2022 11:21:05 +0100 Subject: [PATCH 08/10] Update x/genutil/migrations/v046/migrate.go --- x/genutil/migrations/v046/migrate.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x/genutil/migrations/v046/migrate.go b/x/genutil/migrations/v046/migrate.go index 7adaaddc7b68..10a04fd5bd55 100644 --- a/x/genutil/migrations/v046/migrate.go +++ b/x/genutil/migrations/v046/migrate.go @@ -8,7 +8,7 @@ import ( gov "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" ) -// Migrate migrates exported state from v0.45 to a v0.46 genesis state. +// Migrate migrates exported state from v0.43 to a v0.46 genesis state. func Migrate(appState types.AppMap, clientCtx client.Context) types.AppMap { // Migrate x/gov. if appState[v043gov.ModuleName] != nil { From 89cd20caea39f63b2c8ca231bcb1fb003b3771d9 Mon Sep 17 00:00:00 2001 From: Amaury M <1293565+amaurym@users.noreply.github.com> Date: Wed, 2 Feb 2022 16:22:26 +0100 Subject: [PATCH 09/10] Handle old vote Option field --- x/gov/migrations/v046/convert.go | 24 +++++++++++++++++------- x/gov/migrations/v046/json.go | 6 +++++- x/gov/migrations/v046/json_test.go | 18 +++++++++++++++++- 3 files changed, 39 insertions(+), 9 deletions(-) diff --git a/x/gov/migrations/v046/convert.go b/x/gov/migrations/v046/convert.go index 6a129335ebdc..ceab38d43ba8 100644 --- a/x/gov/migrations/v046/convert.go +++ b/x/gov/migrations/v046/convert.go @@ -133,14 +133,24 @@ func convertToNewDeposits(oldDeps v1beta1.Deposits) v1beta2.Deposits { return newDeps } -func convertToNewVotes(oldVotes v1beta1.Votes) v1beta2.Votes { +func convertToNewVotes(oldVotes v1beta1.Votes) (v1beta2.Votes, error) { newVotes := make([]*v1beta2.Vote, len(oldVotes)) for i, oldVote := range oldVotes { - // All oldVotes don't have the Option field anymore, as they have been - // migrated in the v043 package. - newWVOs := make([]*v1beta2.WeightedVoteOption, len(oldVote.Options)) - for j, oldWVO := range oldVote.Options { - newWVOs[j] = v1beta2.NewWeightedVoteOption(v1beta2.VoteOption(oldVote.Option), oldWVO.Weight) + var newWVOs []*v1beta2.WeightedVoteOption + + // We deprecated Vote.Option in v043. However, it might still be set. + // - if only Options is set, or both Option & Options are set, we read from Options, + // - if Options is not set, and Option is set, we read from Option, + // - if none are set, we throw. + if oldVote.Options != nil { + newWVOs = make([]*v1beta2.WeightedVoteOption, len(oldVote.Options)) + for j, oldWVO := range oldVote.Options { + newWVOs[j] = v1beta2.NewWeightedVoteOption(v1beta2.VoteOption(oldWVO.Option), oldWVO.Weight) + } + } else if oldVote.Option != v1beta1.OptionEmpty { + newWVOs = v1beta2.NewNonSplitVoteOption(v1beta2.VoteOption(oldVote.Option)) + } else { + return nil, fmt.Errorf("vote does not have neither Options nor Option") } newVotes[i] = &v1beta2.Vote{ @@ -150,7 +160,7 @@ func convertToNewVotes(oldVotes v1beta1.Votes) v1beta2.Votes { } } - return newVotes + return newVotes, nil } func convertToNewDepParams(oldDepParams v1beta1.DepositParams) v1beta2.DepositParams { diff --git a/x/gov/migrations/v046/json.go b/x/gov/migrations/v046/json.go index e24036bc3888..7d92cd944e8c 100644 --- a/x/gov/migrations/v046/json.go +++ b/x/gov/migrations/v046/json.go @@ -15,13 +15,17 @@ func MigrateJSON(oldState *v1beta1.GenesisState) (*v1beta2.GenesisState, error) if err != nil { return nil, err } + newVotes, err := convertToNewVotes(oldState.Votes) + if err != nil { + return nil, err + } depParams, votingParms, tallyParams := convertToNewDepParams(oldState.DepositParams), convertToNewVotingParams(oldState.VotingParams), convertToNewTallyParams(oldState.TallyParams) return &v1beta2.GenesisState{ StartingProposalId: oldState.StartingProposalId, Deposits: convertToNewDeposits(oldState.Deposits), - Votes: convertToNewVotes(oldState.Votes), + Votes: newVotes, Proposals: newProps, DepositParams: &depParams, VotingParams: &votingParms, diff --git a/x/gov/migrations/v046/json_test.go b/x/gov/migrations/v046/json_test.go index 228666162abd..655d2409a6c3 100644 --- a/x/gov/migrations/v046/json_test.go +++ b/x/gov/migrations/v046/json_test.go @@ -48,6 +48,7 @@ func TestMigrateJSON(t *testing.T) { } govGenState.Votes = v1beta1.Votes{ v1beta1.Vote{ProposalId: 1, Voter: voter.String(), Option: v1beta1.OptionAbstain}, + v1beta1.Vote{ProposalId: 2, Voter: voter.String(), Options: v1beta1.NewNonSplitVoteOption(v1beta1.OptionNo)}, } migrated, err := v046.MigrateJSON(govGenState) @@ -124,9 +125,24 @@ func TestMigrateJSON(t *testing.T) { }, "votes": [ { - "options": [], + "options": [ + { + "option": "VOTE_OPTION_ABSTAIN", + "weight": "1.000000000000000000" + } + ], "proposal_id": "1", "voter": "cosmos1fl48vsnmsdzcv85q5d2q4z5ajdha8yu34mf0eh" + }, + { + "options": [ + { + "option": "VOTE_OPTION_NO", + "weight": "1.000000000000000000" + } + ], + "proposal_id": "2", + "voter": "cosmos1fl48vsnmsdzcv85q5d2q4z5ajdha8yu34mf0eh" } ], "voting_params": { From 06c2ac69908bdff244d3103ac4a183b26482484d Mon Sep 17 00:00:00 2001 From: atheeshp <59333759+atheeshp@users.noreply.github.com> Date: Thu, 3 Feb 2022 12:16:17 +0530 Subject: [PATCH 10/10] Update x/gov/migrations/v046/convert.go --- x/gov/migrations/v046/convert.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x/gov/migrations/v046/convert.go b/x/gov/migrations/v046/convert.go index ceab38d43ba8..ae1c937808c8 100644 --- a/x/gov/migrations/v046/convert.go +++ b/x/gov/migrations/v046/convert.go @@ -141,7 +141,7 @@ func convertToNewVotes(oldVotes v1beta1.Votes) (v1beta2.Votes, error) { // We deprecated Vote.Option in v043. However, it might still be set. // - if only Options is set, or both Option & Options are set, we read from Options, // - if Options is not set, and Option is set, we read from Option, - // - if none are set, we throw. + // - if none are set, we throw error. if oldVote.Options != nil { newWVOs = make([]*v1beta2.WeightedVoteOption, len(oldVote.Options)) for j, oldWVO := range oldVote.Options {