diff --git a/CHANGELOG.md b/CHANGELOG.md index db7f9556e07a..eaac8cf16c52 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ ## Improvements +* (x/gov) [#13010](https://github.com/cosmos/cosmos-sdk/pull/13010) Partial cherry-pick of this issue for adding proposer migration. * [#14691](https://github.com/cosmos/cosmos-sdk/pull/14691) Change behavior of `sdk.StringifyEvents` to not flatten events attributes by events type. * This change only affects ABCI message logs, and not the actual events. * [#14692](https://github.com/cosmos/cosmos-sdk/pull/14692) Improve RPC queries error message when app is at height 0. @@ -50,6 +51,11 @@ Ref: https://keepachangelog.com/en/1.0.0/ * (baseapp, x/auth/posthandler) [#13940](https://github.com/cosmos/cosmos-sdk/pull/13940) Update `PostHandler` to receive the `runTx` success boolean. + +## API Breaking Changes + +* (x/gov) [#14782](https://github.com/cosmos/cosmos-sdk/pull/14782) Move the `metadata` argument in `govv1.NewProposal` alongside `title` and `summary`. + ## [v0.47.0-rc1](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.47.0-rc1) - 2022-01-09 ### Features diff --git a/UPGRADING.md b/UPGRADING.md index 506a7ac57b1e..505906d22d5c 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -143,6 +143,32 @@ By default, the new `MinInitialDepositRatio` parameter is set to zero during mig feature is disabled. If chains wish to utilize the minimum proposal deposits at time of submission, the migration logic needs to be modified to set the new parameter to the desired value. +##### New Proposal.Proposer field + +The `Proposal` proto has been updated with proposer field. For proposal state migraton developers can call `v4.AddProposerAddressToProposal` in their upgrade handler to update all existing proposal and make them compatible and **this migration is optional**. + +```go +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" + v4 "github.com/cosmos/cosmos-sdk/x/gov/migrations/v4" + upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" +) + +func (app SimApp) RegisterUpgradeHandlers() { + app.UpgradeKeeper.SetUpgradeHandler(UpgradeName, + func(ctx sdk.Context, plan upgradetypes.Plan, fromVM module.VersionMap) (module.VersionMap, error) { + // this migration is optional + // add proposal ids with proposers which are active (deposit or voting period) + proposals := make(map[uint64]string) + proposals[1] = "cosmos1luyncewxk4lm24k6gqy8y5dxkj0klr4tu0lmnj" ... + v4.AddProposerAddressToProposal(ctx, sdk.NewKVStoreKey(v4.ModuleName), app.appCodec, proposals) + return app.ModuleManager.RunMigrations(ctx, app.Configurator(), fromVM) + }) +} + +``` + #### `x/consensus` Introducing a new `x/consensus` module to handle managing Tendermint consensus diff --git a/x/gov/keeper/proposal.go b/x/gov/keeper/proposal.go index 6b260248c844..9a63b98a177c 100644 --- a/x/gov/keeper/proposal.go +++ b/x/gov/keeper/proposal.go @@ -84,7 +84,7 @@ func (keeper Keeper) SubmitProposal(ctx sdk.Context, messages []sdk.Msg, metadat submitTime := ctx.BlockHeader().Time depositPeriod := keeper.GetParams(ctx).MaxDepositPeriod - proposal, err := v1.NewProposal(messages, proposalID, metadata, submitTime, submitTime.Add(*depositPeriod), title, summary, proposer) + proposal, err := v1.NewProposal(messages, proposalID, submitTime, submitTime.Add(*depositPeriod), metadata, title, summary, proposer) if err != nil { return v1.Proposal{}, err } diff --git a/x/gov/keeper/proposal_test.go b/x/gov/keeper/proposal_test.go index d135c954bc26..735258a27ab0 100644 --- a/x/gov/keeper/proposal_test.go +++ b/x/gov/keeper/proposal_test.go @@ -111,7 +111,7 @@ func (suite *KeeperTestSuite) TestGetProposalsFiltered() { for _, s := range status { for i := 0; i < 50; i++ { - p, err := v1.NewProposal(TestProposal, proposalID, "", time.Now(), time.Now(), "title", "summary", sdk.AccAddress("cosmos1ghekyjucln7y67ntx7cf27m9dpuxxemn4c8g4r")) + p, err := v1.NewProposal(TestProposal, proposalID, time.Now(), time.Now(), "", "title", "summary", sdk.AccAddress("cosmos1ghekyjucln7y67ntx7cf27m9dpuxxemn4c8g4r")) suite.Require().NoError(err) p.Status = s diff --git a/x/gov/migrations/v4/store.go b/x/gov/migrations/v4/store.go index eb8ebc5004cd..2f6799896ff6 100644 --- a/x/gov/migrations/v4/store.go +++ b/x/gov/migrations/v4/store.go @@ -1,12 +1,16 @@ package v4 import ( + "fmt" + "sort" + "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" "github.com/cosmos/cosmos-sdk/x/gov/exported" v1 "github.com/cosmos/cosmos-sdk/x/gov/migrations/v1" + "github.com/cosmos/cosmos-sdk/x/gov/types" govv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" ) @@ -75,3 +79,50 @@ func MigrateStore(ctx sdk.Context, storeKey storetypes.StoreKey, legacySubspace return migrateParams(ctx, storeKey, legacySubspace, cdc) } + +// AddProposerAddressToProposal will add proposer to proposal and set to the store. This function is optional. +func AddProposerAddressToProposal(ctx sdk.Context, storeKey storetypes.StoreKey, cdc codec.BinaryCodec, proposals map[uint64]string) error { + proposalIDs := make([]uint64, 0, len(proposals)) + + for proposalID := range proposals { + proposalIDs = append(proposalIDs, proposalID) + } + + // sort the proposalIDs + sort.Slice(proposalIDs, func(i, j int) bool { return proposalIDs[i] < proposalIDs[j] }) + + store := ctx.KVStore(storeKey) + + for _, proposalID := range proposalIDs { + if len(proposals[proposalID]) == 0 { + return fmt.Errorf("found missing proposer for proposal ID: %d", proposalID) + } + + if _, err := sdk.AccAddressFromBech32(proposals[proposalID]); err != nil { + return fmt.Errorf("invalid proposer address : %s", proposals[proposalID]) + } + + bz := store.Get(types.ProposalKey(proposalID)) + var proposal govv1.Proposal + if err := cdc.Unmarshal(bz, &proposal); err != nil { + panic(err) + } + + // Check if proposal is active + if proposal.Status != govv1.ProposalStatus_PROPOSAL_STATUS_VOTING_PERIOD && + proposal.Status != govv1.ProposalStatus_PROPOSAL_STATUS_DEPOSIT_PERIOD { + return fmt.Errorf("invalid proposal : %s, proposal not active", proposals[proposalID]) + } + + proposal.Proposer = proposals[proposalID] + + // set the new proposal with proposer + bz, err := cdc.Marshal(&proposal) + if err != nil { + panic(err) + } + store.Set(types.ProposalKey(proposal.Id), bz) + } + + return nil +} diff --git a/x/gov/migrations/v4/store_test.go b/x/gov/migrations/v4/store_test.go index 92f2601af7a0..8547204b86b2 100644 --- a/x/gov/migrations/v4/store_test.go +++ b/x/gov/migrations/v4/store_test.go @@ -75,13 +75,13 @@ func TestMigrateStore(t *testing.T) { // Create 2 proposals prop1Content, err := v1.NewLegacyContent(v1beta1.NewTextProposal("Test", "description"), authtypes.NewModuleAddress("gov").String()) require.NoError(t, err) - proposal1, err := v1.NewProposal([]sdk.Msg{prop1Content}, 1, "some metadata for the legacy content", propTime, propTime, "Test", "description", sdk.AccAddress("cosmos1ghekyjucln7y67ntx7cf27m9dpuxxemn4c8g4r")) + proposal1, err := v1.NewProposal([]sdk.Msg{prop1Content}, 1, propTime, propTime, "some metadata for the legacy content", "Test", "description", sdk.AccAddress("cosmos1ghekyjucln7y67ntx7cf27m9dpuxxemn4c8g4r")) require.NoError(t, err) prop1Bz, err := cdc.Marshal(&proposal1) require.NoError(t, err) store.Set(v1gov.ProposalKey(proposal1.Id), prop1Bz) - proposal2, err := v1.NewProposal(getTestProposal(), 2, "some metadata for the legacy content", propTime, propTime, "Test", "description", sdk.AccAddress("cosmos1ghekyjucln7y67ntx7cf27m9dpuxxemn4c8g4r")) + proposal2, err := v1.NewProposal(getTestProposal(), 2, propTime, propTime, "some metadata for the legacy content", "Test", "description", sdk.AccAddress("cosmos1ghekyjucln7y67ntx7cf27m9dpuxxemn4c8g4r")) proposal2.Status = v1.StatusVotingPeriod require.NoError(t, err) prop2Bz, err := cdc.Marshal(&proposal2) diff --git a/x/gov/simulation/operations_test.go b/x/gov/simulation/operations_test.go index 809fabde49f2..7192069311ee 100644 --- a/x/gov/simulation/operations_test.go +++ b/x/gov/simulation/operations_test.go @@ -164,7 +164,7 @@ func TestSimulateMsgDeposit(t *testing.T) { submitTime := ctx.BlockHeader().Time depositPeriod := suite.GovKeeper.GetParams(ctx).MaxDepositPeriod - proposal, err := v1.NewProposal([]sdk.Msg{contentMsg}, 1, "", submitTime, submitTime.Add(*depositPeriod), "text proposal", "description", sdk.AccAddress("cosmos1ghekyjucln7y67ntx7cf27m9dpuxxemn4c8g4r")) + proposal, err := v1.NewProposal([]sdk.Msg{contentMsg}, 1, submitTime, submitTime.Add(*depositPeriod), "", "text proposal", "description", sdk.AccAddress("cosmos1ghekyjucln7y67ntx7cf27m9dpuxxemn4c8g4r")) require.NoError(t, err) suite.GovKeeper.SetProposal(ctx, proposal) @@ -211,7 +211,7 @@ func TestSimulateMsgVote(t *testing.T) { submitTime := ctx.BlockHeader().Time depositPeriod := suite.GovKeeper.GetParams(ctx).MaxDepositPeriod - proposal, err := v1.NewProposal([]sdk.Msg{contentMsg}, 1, "", submitTime, submitTime.Add(*depositPeriod), "text proposal", "description", sdk.AccAddress("cosmos1ghekyjucln7y67ntx7cf27m9dpuxxemn4c8g4r")) + proposal, err := v1.NewProposal([]sdk.Msg{contentMsg}, 1, submitTime, submitTime.Add(*depositPeriod), "", "text proposal", "description", sdk.AccAddress("cosmos1ghekyjucln7y67ntx7cf27m9dpuxxemn4c8g4r")) require.NoError(t, err) suite.GovKeeper.ActivateVotingPeriod(ctx, proposal) @@ -255,7 +255,7 @@ func TestSimulateMsgVoteWeighted(t *testing.T) { submitTime := ctx.BlockHeader().Time depositPeriod := suite.GovKeeper.GetParams(ctx).MaxDepositPeriod - proposal, err := v1.NewProposal([]sdk.Msg{contentMsg}, 1, "", submitTime, submitTime.Add(*depositPeriod), "text proposal", "test", sdk.AccAddress("cosmos1ghekyjucln7y67ntx7cf27m9dpuxxemn4c8g4r")) + proposal, err := v1.NewProposal([]sdk.Msg{contentMsg}, 1, submitTime, submitTime.Add(*depositPeriod), "", "text proposal", "test", sdk.AccAddress("cosmos1ghekyjucln7y67ntx7cf27m9dpuxxemn4c8g4r")) require.NoError(t, err) suite.GovKeeper.ActivateVotingPeriod(ctx, proposal) diff --git a/x/gov/types/v1/proposal.go b/x/gov/types/v1/proposal.go index 161c898e2540..a9053a529ad9 100644 --- a/x/gov/types/v1/proposal.go +++ b/x/gov/types/v1/proposal.go @@ -23,7 +23,7 @@ const ( ) // NewProposal creates a new Proposal instance -func NewProposal(messages []sdk.Msg, id uint64, metadata string, submitTime, depositEndTime time.Time, title, summary string, proposer sdk.AccAddress) (Proposal, error) { +func NewProposal(messages []sdk.Msg, id uint64, submitTime, depositEndTime time.Time, metadata, title, summary string, proposer sdk.AccAddress) (Proposal, error) { msgs, err := sdktx.SetMsgs(messages) if err != nil { return Proposal{}, err diff --git a/x/gov/types/v1/proposals_test.go b/x/gov/types/v1/proposals_test.go index baa1fdeba65d..f97d58ac7d31 100644 --- a/x/gov/types/v1/proposals_test.go +++ b/x/gov/types/v1/proposals_test.go @@ -37,7 +37,7 @@ func TestNestedAnys(t *testing.T) { testProposal := v1beta1.NewTextProposal("Proposal", "testing proposal") msgContent, err := v1.NewLegacyContent(testProposal, "cosmos1govacct") require.NoError(t, err) - proposal, err := v1.NewProposal([]sdk.Msg{msgContent}, 1, "", time.Now(), time.Now(), "title", "summary", sdk.AccAddress("cosmos1ghekyjucln7y67ntx7cf27m9dpuxxemn4c8g4r")) + proposal, err := v1.NewProposal([]sdk.Msg{msgContent}, 1, time.Now(), time.Now(), "", "title", "summary", sdk.AccAddress("cosmos1ghekyjucln7y67ntx7cf27m9dpuxxemn4c8g4r")) require.NoError(t, err) require.Equal(t, "TODO Fix panic here", proposal.String())