feat: Add x/gov v043->v046 migrations (#11036)

## Description

Closes: #10869

depends on: 
- [x] #11029 



---

### Author Checklist

*All items are required. Please add a note to the item if the item is not applicable and
please add links to any relevant follow up issues.*

I have...

- [ ] included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title
- [ ] added `!` to the type prefix if API or client breaking change
- [ ] targeted the correct branch (see [PR Targeting](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#pr-targeting))
- [ ] provided a link to the relevant issue or specification
- [ ] followed the guidelines for [building modules](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules)
- [ ] included the necessary unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#testing)
- [ ] added a changelog entry to `CHANGELOG.md`
- [ ] included comments for [documenting Go code](https://blog.golang.org/godoc)
- [ ] updated the relevant documentation or specification
- [ ] reviewed "Files changed" and left comments if necessary
- [ ] confirmed all CI checks have passed

### Reviewers Checklist

*All items are required. Please add a note if the item is not applicable and please add
your handle next to the items reviewed if you only reviewed selected items.*

I have...

- [ ] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title
- [ ] confirmed `!` in the type prefix if API or client breaking change
- [ ] confirmed all author checklist items have been addressed 
- [ ] reviewed state machine logic
- [ ] reviewed API design and naming
- [ ] reviewed documentation is accurate
- [ ] reviewed tests and test coverage
- [ ] manually tested (if applicable)
This commit is contained in:
Amaury 2022-02-03 11:52:33 +01:00 committed by GitHub
parent 63a248ef03
commit dbc19b3c54
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 477 additions and 5 deletions

View File

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

View File

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

View File

@ -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.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 {
// 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
}

View File

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

View File

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

View File

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

View File

@ -3,7 +3,9 @@ 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"
)
@ -117,3 +119,109 @@ func ConvertToLegacyDeposit(deposit *v1beta2.Deposit) v1beta1.Deposit {
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{
ProposalId: oldDep.ProposalId,
Depositor: oldDep.Depositor,
Amount: oldDep.Amount,
}
}
return newDeps
}
func convertToNewVotes(oldVotes v1beta1.Votes) (v1beta2.Votes, error) {
newVotes := make([]*v1beta2.Vote, len(oldVotes))
for i, oldVote := range oldVotes {
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 error.
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{
ProposalId: oldVote.ProposalId,
Voter: oldVote.Voter,
Options: newWVOs,
}
}
return newVotes, nil
}
func convertToNewDepParams(oldDepParams v1beta1.DepositParams) v1beta2.DepositParams {
return v1beta2.DepositParams{
MinDeposit: oldDepParams.MinDeposit,
MaxDepositPeriod: &oldDepParams.MaxDepositPeriod,
}
}
func convertToNewVotingParams(oldVoteParams v1beta1.VotingParams) v1beta2.VotingParams {
return v1beta2.VotingParams{
VotingPeriod: &oldVoteParams.VotingPeriod,
}
}
func convertToNewTallyParams(oldTallyParams v1beta1.TallyParams) v1beta2.TallyParams {
return v1beta2.TallyParams{
Quorum: oldTallyParams.Quorum.String(),
Threshold: oldTallyParams.Threshold.String(),
VetoThreshold: oldTallyParams.VetoThreshold.String(),
}
}
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
}
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 convertToNewProposals(oldProps v1beta1.Proposals) (v1beta2.Proposals, error) {
newProps := make([]*v1beta2.Proposal, len(oldProps))
for i, oldProp := range oldProps {
p, err := convertToNewProposal(oldProp)
if err != nil {
return nil, err
}
newProps[i] = &p
}
return newProps, nil
}

View File

@ -0,0 +1,34 @@
package v046
import (
"github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1"
"github.com/cosmos/cosmos-sdk/x/gov/types/v1beta2"
)
// 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, error) {
newProps, err := convertToNewProposals(oldState.Proposals)
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: newVotes,
Proposals: newProps,
DepositParams: &depParams,
VotingParams: &votingParms,
TallyParams: &tallyParams,
}, nil
}

View File

@ -0,0 +1,154 @@
package v046_test
import (
"encoding/json"
"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"
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) {
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.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 := v046.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().(*v1beta2.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": [],
"proposals": [
{
"deposit_end_time": "2001-09-09T01:46:40Z",
"final_tally_result": {
"abstain": "0",
"no": "0",
"no_with_veto": "0",
"yes": "0"
},
"messages": [
{
"@type": "/cosmos.gov.v1beta2.MsgExecLegacyContent",
"authority": "cosmos10d07y265gmmuvt4z0w9aw880jnsr700j6zn9kn",
"content": {
"@type": "/cosmos.gov.v1beta1.TextProposal",
"description": "my desc",
"title": "my title"
}
}
],
"metadata": null,
"proposal_id": "1",
"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": [
{
"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": {
"voting_period": "172800s"
}
}`
require.Equal(t, expected, string(indentedBz))
}

View File

@ -0,0 +1,6 @@
package v046
const (
// ModuleName is the name of the module
ModuleName = "gov"
)

View File

@ -0,0 +1,51 @@
package v046
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"
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, v040.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 := 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
}
// MigrateStore performs in-place store migrations from v0.43 to 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)
return migrateProposals(store, cdc)
}

View File

@ -0,0 +1,76 @@
package v046_test
import (
"testing"
"time"
"github.com/stretchr/testify/require"
"github.com/cosmos/cosmos-sdk/simapp"
"github.com/cosmos/cosmos-sdk/testutil"
sdk "github.com/cosmos/cosmos-sdk/types"
v040gov "github.com/cosmos/cosmos-sdk/x/gov/migrations/v040"
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) {
cdc := simapp.MakeTestEncodingConfig().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(v040gov.ProposalKey(prop1.ProposalId), prop1Bz)
store.Set(v040gov.ProposalKey(prop2.ProposalId), prop2Bz)
// Run migrations.
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)
// 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())
}

View File

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