From 795d330183aa2782465fb1031b1b23590d2f5af0 Mon Sep 17 00:00:00 2001 From: Amaury <1293565+amaurym@users.noreply.github.com> Date: Tue, 13 Dec 2022 10:23:38 +0100 Subject: [PATCH 1/4] fix(gov): Fix v3 votes migrations (#14214) Co-authored-by: Julien Robert Co-authored-by: atheeshp <59333759+atheeshp@users.noreply.github.com> Co-authored-by: Aleksandr Bezobchuk (cherry picked from commit 1bd9cbe9e096a0e456ae83aa9a5e092c1589744c) # Conflicts: # CHANGELOG.md # x/gov/migrations/v3/json_test.go # x/gov/migrations/v3/store.go # x/gov/migrations/v3/store_test.go --- CHANGELOG.md | 5 + x/gov/migrations/v3/json_test.go | 157 ++++++++++++++++++++++++++++++ x/gov/migrations/v3/store.go | 95 ++++++++++++++++++ x/gov/migrations/v3/store_test.go | 95 ++++++++++++++++++ 4 files changed, 352 insertions(+) create mode 100644 x/gov/migrations/v3/json_test.go create mode 100644 x/gov/migrations/v3/store.go create mode 100644 x/gov/migrations/v3/store_test.go diff --git a/CHANGELOG.md b/CHANGELOG.md index ff59d85f2697..41728a66a3e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -52,6 +52,11 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### State Machine Breaking +<<<<<<< HEAD +======= +* (x/gov) [#14214](https://github.com/cosmos/cosmos-sdk/pull/14214) Fix gov v0.46 migration to v1 votes. +* (x/group) [#13742](https://github.com/cosmos/cosmos-sdk/pull/13742) Migrate group policy account from module accounts to base account. +>>>>>>> 1bd9cbe9e (fix(gov): Fix v3 votes migrations (#14214)) * (x/group) [#14071](https://github.com/cosmos/cosmos-sdk/pull/14071) Don't re-tally proposal after voting period end if they have been marked as ACCEPTED or REJECTED. ### API Breaking Changes diff --git a/x/gov/migrations/v3/json_test.go b/x/gov/migrations/v3/json_test.go new file mode 100644 index 000000000000..3801c8d4df85 --- /dev/null +++ b/x/gov/migrations/v3/json_test.go @@ -0,0 +1,157 @@ +package v3_test + +import ( + "encoding/json" + "testing" + "time" + + "github.com/cosmos/gogoproto/proto" + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/client" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" + moduletestutil "github.com/cosmos/cosmos-sdk/types/module/testutil" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/cosmos/cosmos-sdk/x/gov" + v3 "github.com/cosmos/cosmos-sdk/x/gov/migrations/v3" + "github.com/cosmos/cosmos-sdk/x/gov/types" + v1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" + "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" +) + +var voter = sdk.MustAccAddressFromBech32("cosmos1fl48vsnmsdzcv85q5d2q4z5ajdha8yu34mf0eh") + +func TestMigrateJSON(t *testing.T) { + encodingConfig := moduletestutil.MakeTestEncodingConfig(gov.AppModuleBasic{}) + clientCtx := client.Context{}. + WithInterfaceRegistry(encodingConfig.InterfaceRegistry). + WithTxConfig(encodingConfig.TxConfig). + WithCodec(encodingConfig.Codec) + + govGenState := v1beta1.DefaultGenesisState() + 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{ + 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}, + v1beta1.Vote{ProposalId: 2, Voter: voter.String(), Options: v1beta1.NewNonSplitVoteOption(v1beta1.OptionNo)}, + } + + migrated, err := v3.MigrateJSON(govGenState) + require.NoError(t, err) + + // 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().(*v1.MsgExecLegacyContent).Authority, + ) + + 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: + // - Proposals use MsgExecLegacyContent + expected := `{ + "deposit_params": { + "max_deposit_period": "172800s", + "min_deposit": [ + { + "amount": "10000000", + "denom": "stake" + } + ] + }, + "deposits": [], + "params": null, + "proposals": [ + { + "deposit_end_time": "2001-09-09T01:46:40Z", + "final_tally_result": { + "abstain_count": "0", + "no_count": "0", + "no_with_veto_count": "0", + "yes_count": "0" + }, + "id": "1", + "messages": [ + { + "@type": "/cosmos.gov.v1.MsgExecLegacyContent", + "authority": "cosmos10d07y265gmmuvt4z0w9aw880jnsr700j6zn9kn", + "content": { + "@type": "/cosmos.gov.v1beta1.TextProposal", + "description": "my desc", + "title": "my title" + } + } + ], + "metadata": "", + "status": "PROPOSAL_STATUS_DEPOSIT_PERIOD", + "submit_time": "2001-09-09T01:46:40Z", + "total_deposit": [ + { + "amount": "123", + "denom": "stake" + } + ], + "voting_end_time": "2001-09-09T01:46:40Z", + "voting_start_time": "2001-09-09T01:46:40Z" + } + ], + "starting_proposal_id": "1", + "tally_params": { + "quorum": "0.334000000000000000", + "threshold": "0.500000000000000000", + "veto_threshold": "0.334000000000000000" + }, + "votes": [ + { + "metadata": "", + "options": [ + { + "option": "VOTE_OPTION_ABSTAIN", + "weight": "1.000000000000000000" + } + ], + "proposal_id": "1", + "voter": "cosmos1fl48vsnmsdzcv85q5d2q4z5ajdha8yu34mf0eh" + }, + { + "metadata": "", + "options": [ + { + "option": "VOTE_OPTION_NO", + "weight": "1.000000000000000000" + } + ], + "proposal_id": "2", + "voter": "cosmos1fl48vsnmsdzcv85q5d2q4z5ajdha8yu34mf0eh" + } + ], + "voting_params": { + "voting_period": "172800s" + } +}` + + require.Equal(t, expected, string(indentedBz)) +} diff --git a/x/gov/migrations/v3/store.go b/x/gov/migrations/v3/store.go new file mode 100644 index 000000000000..7f3cfe76c66e --- /dev/null +++ b/x/gov/migrations/v3/store.go @@ -0,0 +1,95 @@ +package v3 + +import ( + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/store/prefix" + storetypes "github.com/cosmos/cosmos-sdk/store/types" + sdk "github.com/cosmos/cosmos-sdk/types" + v1 "github.com/cosmos/cosmos-sdk/x/gov/migrations/v1" + govv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" + govv1beta1 "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, v1.ProposalsKeyPrefix) + + iter := propStore.Iterator(nil, nil) + defer iter.Close() + + for ; iter.Valid(); iter.Next() { + var oldProp govv1beta1.Proposal + err := cdc.Unmarshal(iter.Value(), &oldProp) + if err != nil { + return err + } + + newProp, err := convertToNewProposal(oldProp) + if err != nil { + return err + } + bz, err := cdc.Marshal(&newProp) + if err != nil { + return err + } + + // Set new value on store. + propStore.Set(iter.Key(), bz) + } + + return nil +} + +// migrateVotes migrates all v1beta1 weighted votes (with sdk.Dec as weight) +// to v1 weighted votes (with string as weight) +func migrateVotes(store sdk.KVStore, cdc codec.BinaryCodec) error { + votesStore := prefix.NewStore(store, v1.VotesKeyPrefix) + + iter := votesStore.Iterator(nil, nil) + defer iter.Close() + + for ; iter.Valid(); iter.Next() { + var oldVote govv1beta1.Vote + err := cdc.Unmarshal(iter.Value(), &oldVote) + if err != nil { + return err + } + + newVote := govv1.Vote{ + ProposalId: oldVote.ProposalId, + Voter: oldVote.Voter, + } + newOptions := make([]*govv1.WeightedVoteOption, len(oldVote.Options)) + for i, o := range oldVote.Options { + newOptions[i] = &govv1.WeightedVoteOption{ + Option: govv1.VoteOption(o.Option), + Weight: o.Weight.String(), // Convert to decimal string + } + } + newVote.Options = newOptions + bz, err := cdc.Marshal(&newVote) + if err != nil { + return err + } + + // Set new value on store. + votesStore.Set(iter.Key(), bz) + } + + return nil +} + +// MigrateStore performs in-place store migrations from v2 (v0.43) to v3 (v0.46). 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) + + if err := migrateVotes(store, cdc); err != nil { + return err + } + + return migrateProposals(store, cdc) +} diff --git a/x/gov/migrations/v3/store_test.go b/x/gov/migrations/v3/store_test.go new file mode 100644 index 000000000000..95612a718547 --- /dev/null +++ b/x/gov/migrations/v3/store_test.go @@ -0,0 +1,95 @@ +package v3_test + +import ( + "testing" + "time" + + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/testutil" + sdk "github.com/cosmos/cosmos-sdk/types" + moduletestutil "github.com/cosmos/cosmos-sdk/types/module/testutil" + "github.com/cosmos/cosmos-sdk/x/gov" + v1gov "github.com/cosmos/cosmos-sdk/x/gov/migrations/v1" + v3gov "github.com/cosmos/cosmos-sdk/x/gov/migrations/v3" + v1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" + "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" + "github.com/cosmos/cosmos-sdk/x/upgrade" + upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" +) + +func TestMigrateStore(t *testing.T) { + cdc := moduletestutil.MakeTestEncodingConfig(upgrade.AppModuleBasic{}, gov.AppModuleBasic{}).Codec + govKey := sdk.NewKVStoreKey("gov") + ctx := testutil.DefaultContext(govKey, sdk.NewTransientStoreKey("transient_test")) + store := ctx.KVStore(govKey) + + propTime := time.Unix(1e9, 0) + + // 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) + + store.Set(v1gov.ProposalKey(prop1.ProposalId), prop1Bz) + store.Set(v1gov.ProposalKey(prop2.ProposalId), prop2Bz) + + // Vote on prop 1 + options := []v1beta1.WeightedVoteOption{ + {Option: v1beta1.OptionNo, Weight: sdk.MustNewDecFromStr("0.3")}, + {Option: v1beta1.OptionYes, Weight: sdk.MustNewDecFromStr("0.7")}, + } + vote1 := v1beta1.NewVote(1, voter, options) + vote1Bz := cdc.MustMarshal(&vote1) + store.Set(v1gov.VoteKey(1, voter), vote1Bz) + + // Run migrations. + err = v3gov.MigrateStore(ctx, govKey, cdc) + require.NoError(t, err) + + var newProp1 v1.Proposal + err = cdc.Unmarshal(store.Get(v1gov.ProposalKey(prop1.ProposalId)), &newProp1) + require.NoError(t, err) + compareProps(t, prop1, newProp1) + + var newProp2 v1.Proposal + err = cdc.Unmarshal(store.Get(v1gov.ProposalKey(prop2.ProposalId)), &newProp2) + require.NoError(t, err) + compareProps(t, prop2, newProp2) + + var newVote1 v1.Vote + err = cdc.Unmarshal(store.Get(v1gov.VoteKey(prop1.ProposalId, voter)), &newVote1) + require.NoError(t, err) + // Without the votes migration, we would have 300000000000000000 in state, + // because of how sdk.Dec stores itself in state. + require.Equal(t, "0.300000000000000000", newVote1.Options[0].Weight) + require.Equal(t, "0.700000000000000000", newVote1.Options[1].Weight) +} + +func compareProps(t *testing.T, oldProp v1beta1.Proposal, newProp v1.Proposal) { + require.Equal(t, oldProp.ProposalId, newProp.Id) + 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.YesCount) + require.Equal(t, oldProp.FinalTallyResult.No.String(), newProp.FinalTallyResult.NoCount) + require.Equal(t, oldProp.FinalTallyResult.NoWithVeto.String(), newProp.FinalTallyResult.NoWithVetoCount) + require.Equal(t, oldProp.FinalTallyResult.Abstain.String(), newProp.FinalTallyResult.AbstainCount) + + newContent := newProp.Messages[0].GetCachedValue().(*v1.MsgExecLegacyContent).Content.GetCachedValue().(v1beta1.Content) + require.Equal(t, oldProp.Content.GetCachedValue().(v1beta1.Content), newContent) + + // 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 a6f24c37cbc735e378a301b89a4e03176765610c Mon Sep 17 00:00:00 2001 From: Amaury M <1293565+amaurym@users.noreply.github.com> Date: Tue, 13 Dec 2022 10:48:06 +0100 Subject: [PATCH 2/4] Fix v3/v046 --- x/gov/migrations/v046/json_test.go | 2 + x/gov/migrations/v046/store.go | 64 +++++++++++- x/gov/migrations/v046/store_test.go | 17 +++ x/gov/migrations/v3/json_test.go | 157 ---------------------------- x/gov/migrations/v3/store.go | 95 ----------------- x/gov/migrations/v3/store_test.go | 95 ----------------- 6 files changed, 80 insertions(+), 350 deletions(-) delete mode 100644 x/gov/migrations/v3/json_test.go delete mode 100644 x/gov/migrations/v3/store.go delete mode 100644 x/gov/migrations/v3/store_test.go diff --git a/x/gov/migrations/v046/json_test.go b/x/gov/migrations/v046/json_test.go index 518161b9e8b5..bd4330d46063 100644 --- a/x/gov/migrations/v046/json_test.go +++ b/x/gov/migrations/v046/json_test.go @@ -19,6 +19,8 @@ import ( "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" ) +var voter = sdk.MustAccAddressFromBech32("cosmos1fl48vsnmsdzcv85q5d2q4z5ajdha8yu34mf0eh") + func TestMigrateJSON(t *testing.T) { encodingConfig := simapp.MakeTestEncodingConfig() clientCtx := client.Context{}. diff --git a/x/gov/migrations/v046/store.go b/x/gov/migrations/v046/store.go index c0ad36dfb4b7..f0ea72d46d3c 100644 --- a/x/gov/migrations/v046/store.go +++ b/x/gov/migrations/v046/store.go @@ -6,7 +6,8 @@ import ( storetypes "github.com/cosmos/cosmos-sdk/store/types" sdk "github.com/cosmos/cosmos-sdk/types" v042 "github.com/cosmos/cosmos-sdk/x/gov/migrations/v042" - "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" + govv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" + govv1beta1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" ) // migrateProposals migrates all legacy proposals into MsgExecLegacyContent @@ -18,7 +19,7 @@ func migrateProposals(store sdk.KVStore, cdc codec.BinaryCodec) error { defer iter.Close() for ; iter.Valid(); iter.Next() { - var oldProp v1beta1.Proposal + var oldProp govv1beta1.Proposal err := cdc.Unmarshal(iter.Value(), &oldProp) if err != nil { return err @@ -40,12 +41,69 @@ func migrateProposals(store sdk.KVStore, cdc codec.BinaryCodec) error { return nil } -// MigrateStore performs in-place store migrations from v0.43 to v0.46. The +// migrateVotes migrates all v1beta1 weighted votes (with sdk.Dec as weight) +// to v1 weighted votes (with string as weight) +func migrateVotes(store sdk.KVStore, cdc codec.BinaryCodec) error { + votesStore := prefix.NewStore(store, v042.VotesKeyPrefix) + + iter := votesStore.Iterator(nil, nil) + defer iter.Close() + + for ; iter.Valid(); iter.Next() { + var oldVote govv1beta1.Vote + err := cdc.Unmarshal(iter.Value(), &oldVote) + if err != nil { + return err + } + + newVote := govv1.Vote{ + ProposalId: oldVote.ProposalId, + Voter: oldVote.Voter, + } + newOptions := make([]*govv1.WeightedVoteOption, len(oldVote.Options)) + for i, o := range oldVote.Options { + newOptions[i] = &govv1.WeightedVoteOption{ + Option: govv1.VoteOption(o.Option), + Weight: o.Weight.String(), // Convert to decimal string + } + } + newVote.Options = newOptions + bz, err := cdc.Marshal(&newVote) + if err != nil { + return err + } + + // Set new value on store. + votesStore.Set(iter.Key(), bz) + } + + return nil +} + +// MigrateStore performs in-place store migrations from v2 (v0.43) to v3 (v0.46). 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) + if err := migrateVotes(store, cdc); err != nil { + return err + } + return migrateProposals(store, cdc) } + +// Migrate_V046_4_To_V046_5 is a helper function to migrate chains from <=v0.46.6 +// to v0.46.7 ONLY. +// +// IMPORTANT: Please do not use this function if you are upgrading to v0.46 +// from <=v0.45. +// +// This function migrates the store in-place by fixing the gov votes weight to +// be stored as decimals strings. +// +// The store is expected to be the gov store, and not any prefixed substore. +func Migrate_V046_6_To_V046_7(store sdk.KVStore) error { + return nil +} diff --git a/x/gov/migrations/v046/store_test.go b/x/gov/migrations/v046/store_test.go index a9318f9390a9..4d3e50e68de1 100644 --- a/x/gov/migrations/v046/store_test.go +++ b/x/gov/migrations/v046/store_test.go @@ -39,6 +39,15 @@ func TestMigrateStore(t *testing.T) { store.Set(v042gov.ProposalKey(prop1.ProposalId), prop1Bz) store.Set(v042gov.ProposalKey(prop2.ProposalId), prop2Bz) + // Vote on prop 1 + options := []v1beta1.WeightedVoteOption{ + {Option: v1beta1.OptionNo, Weight: sdk.MustNewDecFromStr("0.3")}, + {Option: v1beta1.OptionYes, Weight: sdk.MustNewDecFromStr("0.7")}, + } + vote1 := v1beta1.NewVote(1, voter, options) + vote1Bz := cdc.MustMarshal(&vote1) + store.Set(v042gov.VoteKey(1, voter), vote1Bz) + // Run migrations. err = v046gov.MigrateStore(ctx, govKey, cdc) require.NoError(t, err) @@ -52,6 +61,14 @@ func TestMigrateStore(t *testing.T) { err = cdc.Unmarshal(store.Get(v042gov.ProposalKey(prop2.ProposalId)), &newProp2) require.NoError(t, err) compareProps(t, prop2, newProp2) + + var newVote1 v1.Vote + err = cdc.Unmarshal(store.Get(v042gov.VoteKey(prop1.ProposalId, voter)), &newVote1) + require.NoError(t, err) + // Without the votes migration, we would have 300000000000000000 in state, + // because of how sdk.Dec stores itself in state. + require.Equal(t, "0.300000000000000000", newVote1.Options[0].Weight) + require.Equal(t, "0.700000000000000000", newVote1.Options[1].Weight) } func compareProps(t *testing.T, oldProp v1beta1.Proposal, newProp v1.Proposal) { diff --git a/x/gov/migrations/v3/json_test.go b/x/gov/migrations/v3/json_test.go deleted file mode 100644 index 3801c8d4df85..000000000000 --- a/x/gov/migrations/v3/json_test.go +++ /dev/null @@ -1,157 +0,0 @@ -package v3_test - -import ( - "encoding/json" - "testing" - "time" - - "github.com/cosmos/gogoproto/proto" - "github.com/stretchr/testify/require" - - "github.com/cosmos/cosmos-sdk/client" - codectypes "github.com/cosmos/cosmos-sdk/codec/types" - sdk "github.com/cosmos/cosmos-sdk/types" - moduletestutil "github.com/cosmos/cosmos-sdk/types/module/testutil" - authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" - "github.com/cosmos/cosmos-sdk/x/gov" - v3 "github.com/cosmos/cosmos-sdk/x/gov/migrations/v3" - "github.com/cosmos/cosmos-sdk/x/gov/types" - v1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" - "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" -) - -var voter = sdk.MustAccAddressFromBech32("cosmos1fl48vsnmsdzcv85q5d2q4z5ajdha8yu34mf0eh") - -func TestMigrateJSON(t *testing.T) { - encodingConfig := moduletestutil.MakeTestEncodingConfig(gov.AppModuleBasic{}) - clientCtx := client.Context{}. - WithInterfaceRegistry(encodingConfig.InterfaceRegistry). - WithTxConfig(encodingConfig.TxConfig). - WithCodec(encodingConfig.Codec) - - govGenState := v1beta1.DefaultGenesisState() - 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{ - 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}, - v1beta1.Vote{ProposalId: 2, Voter: voter.String(), Options: v1beta1.NewNonSplitVoteOption(v1beta1.OptionNo)}, - } - - migrated, err := v3.MigrateJSON(govGenState) - require.NoError(t, err) - - // 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().(*v1.MsgExecLegacyContent).Authority, - ) - - 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: - // - Proposals use MsgExecLegacyContent - expected := `{ - "deposit_params": { - "max_deposit_period": "172800s", - "min_deposit": [ - { - "amount": "10000000", - "denom": "stake" - } - ] - }, - "deposits": [], - "params": null, - "proposals": [ - { - "deposit_end_time": "2001-09-09T01:46:40Z", - "final_tally_result": { - "abstain_count": "0", - "no_count": "0", - "no_with_veto_count": "0", - "yes_count": "0" - }, - "id": "1", - "messages": [ - { - "@type": "/cosmos.gov.v1.MsgExecLegacyContent", - "authority": "cosmos10d07y265gmmuvt4z0w9aw880jnsr700j6zn9kn", - "content": { - "@type": "/cosmos.gov.v1beta1.TextProposal", - "description": "my desc", - "title": "my title" - } - } - ], - "metadata": "", - "status": "PROPOSAL_STATUS_DEPOSIT_PERIOD", - "submit_time": "2001-09-09T01:46:40Z", - "total_deposit": [ - { - "amount": "123", - "denom": "stake" - } - ], - "voting_end_time": "2001-09-09T01:46:40Z", - "voting_start_time": "2001-09-09T01:46:40Z" - } - ], - "starting_proposal_id": "1", - "tally_params": { - "quorum": "0.334000000000000000", - "threshold": "0.500000000000000000", - "veto_threshold": "0.334000000000000000" - }, - "votes": [ - { - "metadata": "", - "options": [ - { - "option": "VOTE_OPTION_ABSTAIN", - "weight": "1.000000000000000000" - } - ], - "proposal_id": "1", - "voter": "cosmos1fl48vsnmsdzcv85q5d2q4z5ajdha8yu34mf0eh" - }, - { - "metadata": "", - "options": [ - { - "option": "VOTE_OPTION_NO", - "weight": "1.000000000000000000" - } - ], - "proposal_id": "2", - "voter": "cosmos1fl48vsnmsdzcv85q5d2q4z5ajdha8yu34mf0eh" - } - ], - "voting_params": { - "voting_period": "172800s" - } -}` - - require.Equal(t, expected, string(indentedBz)) -} diff --git a/x/gov/migrations/v3/store.go b/x/gov/migrations/v3/store.go deleted file mode 100644 index 7f3cfe76c66e..000000000000 --- a/x/gov/migrations/v3/store.go +++ /dev/null @@ -1,95 +0,0 @@ -package v3 - -import ( - "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/store/prefix" - storetypes "github.com/cosmos/cosmos-sdk/store/types" - sdk "github.com/cosmos/cosmos-sdk/types" - v1 "github.com/cosmos/cosmos-sdk/x/gov/migrations/v1" - govv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" - govv1beta1 "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, v1.ProposalsKeyPrefix) - - iter := propStore.Iterator(nil, nil) - defer iter.Close() - - for ; iter.Valid(); iter.Next() { - var oldProp govv1beta1.Proposal - err := cdc.Unmarshal(iter.Value(), &oldProp) - if err != nil { - return err - } - - newProp, err := convertToNewProposal(oldProp) - if err != nil { - return err - } - bz, err := cdc.Marshal(&newProp) - if err != nil { - return err - } - - // Set new value on store. - propStore.Set(iter.Key(), bz) - } - - return nil -} - -// migrateVotes migrates all v1beta1 weighted votes (with sdk.Dec as weight) -// to v1 weighted votes (with string as weight) -func migrateVotes(store sdk.KVStore, cdc codec.BinaryCodec) error { - votesStore := prefix.NewStore(store, v1.VotesKeyPrefix) - - iter := votesStore.Iterator(nil, nil) - defer iter.Close() - - for ; iter.Valid(); iter.Next() { - var oldVote govv1beta1.Vote - err := cdc.Unmarshal(iter.Value(), &oldVote) - if err != nil { - return err - } - - newVote := govv1.Vote{ - ProposalId: oldVote.ProposalId, - Voter: oldVote.Voter, - } - newOptions := make([]*govv1.WeightedVoteOption, len(oldVote.Options)) - for i, o := range oldVote.Options { - newOptions[i] = &govv1.WeightedVoteOption{ - Option: govv1.VoteOption(o.Option), - Weight: o.Weight.String(), // Convert to decimal string - } - } - newVote.Options = newOptions - bz, err := cdc.Marshal(&newVote) - if err != nil { - return err - } - - // Set new value on store. - votesStore.Set(iter.Key(), bz) - } - - return nil -} - -// MigrateStore performs in-place store migrations from v2 (v0.43) to v3 (v0.46). 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) - - if err := migrateVotes(store, cdc); err != nil { - return err - } - - return migrateProposals(store, cdc) -} diff --git a/x/gov/migrations/v3/store_test.go b/x/gov/migrations/v3/store_test.go deleted file mode 100644 index 95612a718547..000000000000 --- a/x/gov/migrations/v3/store_test.go +++ /dev/null @@ -1,95 +0,0 @@ -package v3_test - -import ( - "testing" - "time" - - "github.com/stretchr/testify/require" - - "github.com/cosmos/cosmos-sdk/testutil" - sdk "github.com/cosmos/cosmos-sdk/types" - moduletestutil "github.com/cosmos/cosmos-sdk/types/module/testutil" - "github.com/cosmos/cosmos-sdk/x/gov" - v1gov "github.com/cosmos/cosmos-sdk/x/gov/migrations/v1" - v3gov "github.com/cosmos/cosmos-sdk/x/gov/migrations/v3" - v1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" - "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" - "github.com/cosmos/cosmos-sdk/x/upgrade" - upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" -) - -func TestMigrateStore(t *testing.T) { - cdc := moduletestutil.MakeTestEncodingConfig(upgrade.AppModuleBasic{}, gov.AppModuleBasic{}).Codec - govKey := sdk.NewKVStoreKey("gov") - ctx := testutil.DefaultContext(govKey, sdk.NewTransientStoreKey("transient_test")) - store := ctx.KVStore(govKey) - - propTime := time.Unix(1e9, 0) - - // 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) - - store.Set(v1gov.ProposalKey(prop1.ProposalId), prop1Bz) - store.Set(v1gov.ProposalKey(prop2.ProposalId), prop2Bz) - - // Vote on prop 1 - options := []v1beta1.WeightedVoteOption{ - {Option: v1beta1.OptionNo, Weight: sdk.MustNewDecFromStr("0.3")}, - {Option: v1beta1.OptionYes, Weight: sdk.MustNewDecFromStr("0.7")}, - } - vote1 := v1beta1.NewVote(1, voter, options) - vote1Bz := cdc.MustMarshal(&vote1) - store.Set(v1gov.VoteKey(1, voter), vote1Bz) - - // Run migrations. - err = v3gov.MigrateStore(ctx, govKey, cdc) - require.NoError(t, err) - - var newProp1 v1.Proposal - err = cdc.Unmarshal(store.Get(v1gov.ProposalKey(prop1.ProposalId)), &newProp1) - require.NoError(t, err) - compareProps(t, prop1, newProp1) - - var newProp2 v1.Proposal - err = cdc.Unmarshal(store.Get(v1gov.ProposalKey(prop2.ProposalId)), &newProp2) - require.NoError(t, err) - compareProps(t, prop2, newProp2) - - var newVote1 v1.Vote - err = cdc.Unmarshal(store.Get(v1gov.VoteKey(prop1.ProposalId, voter)), &newVote1) - require.NoError(t, err) - // Without the votes migration, we would have 300000000000000000 in state, - // because of how sdk.Dec stores itself in state. - require.Equal(t, "0.300000000000000000", newVote1.Options[0].Weight) - require.Equal(t, "0.700000000000000000", newVote1.Options[1].Weight) -} - -func compareProps(t *testing.T, oldProp v1beta1.Proposal, newProp v1.Proposal) { - require.Equal(t, oldProp.ProposalId, newProp.Id) - 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.YesCount) - require.Equal(t, oldProp.FinalTallyResult.No.String(), newProp.FinalTallyResult.NoCount) - require.Equal(t, oldProp.FinalTallyResult.NoWithVeto.String(), newProp.FinalTallyResult.NoWithVetoCount) - require.Equal(t, oldProp.FinalTallyResult.Abstain.String(), newProp.FinalTallyResult.AbstainCount) - - newContent := newProp.Messages[0].GetCachedValue().(*v1.MsgExecLegacyContent).Content.GetCachedValue().(v1beta1.Content) - require.Equal(t, oldProp.Content.GetCachedValue().(v1beta1.Content), newContent) - - // 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 c7199f63aa759c50939d633bd50bba7682860d57 Mon Sep 17 00:00:00 2001 From: Amaury M <1293565+amaurym@users.noreply.github.com> Date: Tue, 13 Dec 2022 11:00:30 +0100 Subject: [PATCH 3/4] release notes --- CHANGELOG.md | 4 +--- RELEASE_NOTES.md | 4 ++++ go.mod | 3 +-- x/gov/migrations/v046/store.go | 6 +++--- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 41728a66a3e0..e77bb959dfc7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -52,11 +52,9 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### State Machine Breaking -<<<<<<< HEAD -======= * (x/gov) [#14214](https://github.com/cosmos/cosmos-sdk/pull/14214) Fix gov v0.46 migration to v1 votes. + * Also provide a helper function `govv046.Migrate_V0466_To_V0467` for migrating a chain already on v0.46 with versions <=v0.46.6 to the latest v0.46.7 correct state. * (x/group) [#13742](https://github.com/cosmos/cosmos-sdk/pull/13742) Migrate group policy account from module accounts to base account. ->>>>>>> 1bd9cbe9e (fix(gov): Fix v3 votes migrations (#14214)) * (x/group) [#14071](https://github.com/cosmos/cosmos-sdk/pull/14071) Don't re-tally proposal after voting period end if they have been marked as ACCEPTED or REJECTED. ### API Breaking Changes diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 86f39bdf588d..cabe154c0b82 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -6,6 +6,10 @@ Please read the release notes of [v0.46.5](https://github.com/cosmos/cosmos-sdk/ A critical vulnerability has been fixed in the group module. For safety, `v0.46.5` and `v0.46.6` are retracted, even though chains not using the group module are not affected. When using the group module, please upgrade immediately to `v0.46.7`. +An issue has been discovered in the gov module's votes migration. It does not impact proposals and votes tallying, but the gRPC queries on votes are incorrect. This issue is fixed in `v0.46.7`, however: +- if your chain is already on v0.46 using `<= v0.46.6`, a **coordinated upgrade** to v0.46.7 is required. You can use the helper function `govv046.Migrate_V046_6_To_V046_7` for migrating a chain **already on v0.46 with versions <=v0.46.6** to the latest v0.46.7 correct state. +- if your chain is on a previous version <= v0.45, then simply use v0.46.7 when upgrading to v0.46. + **NOTE**: The changes mentioned in `v0.46.3` are no longer required. The following replace directive can be removed from the chains. ```go diff --git a/go.mod b/go.mod index 362581ecb6f3..af7a597cccc2 100644 --- a/go.mod +++ b/go.mod @@ -168,8 +168,7 @@ replace ( ) retract ( - // subject to a bug in the group module, - // these versions are safe to use when not using the group module + // subject to a bug in the group module and gov module migration [v0.46.5, v0.46.6] // subject to the dragonberry vulnerability // and/or the bank coin metadata migration issue diff --git a/x/gov/migrations/v046/store.go b/x/gov/migrations/v046/store.go index f0ea72d46d3c..9fbcf9f3413d 100644 --- a/x/gov/migrations/v046/store.go +++ b/x/gov/migrations/v046/store.go @@ -101,9 +101,9 @@ func MigrateStore(ctx sdk.Context, storeKey storetypes.StoreKey, cdc codec.Binar // from <=v0.45. // // This function migrates the store in-place by fixing the gov votes weight to -// be stored as decimals strings. +// be stored as decimals strings (instead of the sdk.Dec BigInt representation). // // The store is expected to be the gov store, and not any prefixed substore. -func Migrate_V046_6_To_V046_7(store sdk.KVStore) error { - return nil +func Migrate_V046_6_To_V046_7(store sdk.KVStore, cdc codec.BinaryCodec) error { + return migrateVotes(store, cdc) } From 7e8af1dec3015425ee04c47a68e99f2bbf84f4ab Mon Sep 17 00:00:00 2001 From: Julien Robert Date: Tue, 13 Dec 2022 11:10:23 +0100 Subject: [PATCH 4/4] fix changelog --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e77bb959dfc7..25800d8001b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -54,7 +54,6 @@ Ref: https://keepachangelog.com/en/1.0.0/ * (x/gov) [#14214](https://github.com/cosmos/cosmos-sdk/pull/14214) Fix gov v0.46 migration to v1 votes. * Also provide a helper function `govv046.Migrate_V0466_To_V0467` for migrating a chain already on v0.46 with versions <=v0.46.6 to the latest v0.46.7 correct state. -* (x/group) [#13742](https://github.com/cosmos/cosmos-sdk/pull/13742) Migrate group policy account from module accounts to base account. * (x/group) [#14071](https://github.com/cosmos/cosmos-sdk/pull/14071) Don't re-tally proposal after voting period end if they have been marked as ACCEPTED or REJECTED. ### API Breaking Changes