Skip to content

Commit

Permalink
fix: migrate community pool spend (#645)
Browse files Browse the repository at this point in the history
Co-authored-by: nulnut <[email protected]>
  • Loading branch information
zakir-code and nulnut authored Aug 14, 2024
1 parent 95179d8 commit 4edcd2b
Show file tree
Hide file tree
Showing 2 changed files with 172 additions and 10 deletions.
111 changes: 104 additions & 7 deletions app/upgrade_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,17 @@ import (
"testing"
"time"

sdkmath "cosmossdk.io/math"
dbm "github.com/cometbft/cometbft-db"
abci "github.com/cometbft/cometbft/abci/types"
"github.com/cometbft/cometbft/libs/log"
tmproto "github.com/cometbft/cometbft/proto/tendermint/types"
tmtime "github.com/cometbft/cometbft/types/time"
"github.com/cosmos/cosmos-sdk/baseapp"
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
sdk "github.com/cosmos/cosmos-sdk/types"
v1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1"
minttypes "github.com/cosmos/cosmos-sdk/x/mint/types"
upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
Expand All @@ -29,7 +33,7 @@ func Test_UpgradeAndMigrate(t *testing.T) {
fxtypes.SetConfig(true)

home := filepath.Join(os.Getenv("HOME"), "tmp")
chainId := fxtypes.TestnetChainId // The upgrade test is not related to chainId, do not modify it
chainId := fxtypes.MainnetChainId
fxtypes.SetChainId(chainId)

db, err := dbm.NewDB("application", dbm.GoLevelDBBackend, filepath.Join(home, "data"))
Expand All @@ -42,16 +46,15 @@ func Test_UpgradeAndMigrate(t *testing.T) {
myApp.SetStoreLoader(upgradetypes.UpgradeStoreLoader(myApp.LastBlockHeight()+1, nextversion.Upgrade.StoreUpgrades()))
require.NoError(t, myApp.LoadLatestVersion())

ctx := newContext(t, myApp, chainId)

ctx := newContext(t, myApp, chainId, false)
require.NoError(t, myApp.UpgradeKeeper.ScheduleUpgrade(ctx, upgradetypes.Plan{
Name: nextversion.Upgrade.UpgradeName,
Height: ctx.BlockHeight() + 1,
}))

header := ctx.BlockHeader()
header.Height = header.Height + 1
header.Time = time.Now().UTC()

require.NotPanics(t, func() {
myApp.BeginBlock(abci.RequestBeginBlock{
Header: header,
Expand All @@ -62,13 +65,37 @@ func Test_UpgradeAndMigrate(t *testing.T) {
Height: header.Height,
})
})

ctx = newContext(t, myApp, chainId, true)
ingProposalIds := govDepositAndVote(t, ctx, myApp)

header.Time = tmtime.Now().Add(21 * 24 * time.Hour) // exec proposal
require.NotPanics(t, func() {
myApp.BeginBlock(abci.RequestBeginBlock{
Header: header,
})
})
require.NotPanics(t, func() {
myApp.EndBlock(abci.RequestEndBlock{
Height: header.Height,
})
})

checkProposalPassed(t, ctx, myApp, ingProposalIds)
}

func newContext(t *testing.T, myApp *app.App, chainId string) sdk.Context {
ctx := myApp.NewUncachedContext(false, tmproto.Header{
func newContext(t *testing.T, myApp *app.App, chainId string, deliveState bool) sdk.Context {
header := tmproto.Header{
ChainID: chainId,
Height: myApp.LastBlockHeight(),
})
Time: tmtime.Now(),
}
var ctx sdk.Context
if deliveState {
ctx = myApp.NewContext(false, header)
} else {
ctx = myApp.NewUncachedContext(false, header)
}
// set the first validator to proposer
validators := myApp.StakingKeeper.GetAllValidators(ctx)
assert.True(t, len(validators) > 0)
Expand All @@ -77,3 +104,73 @@ func newContext(t *testing.T, myApp *app.App, chainId string) sdk.Context {
ctx = ctx.WithProposer(pubKey.Address().Bytes())
return ctx
}

func govDepositAndVote(t *testing.T, ctx sdk.Context, myApp *app.App) []uint64 {
var ingProposalIds []uint64
amount := sdk.NewCoins(sdk.NewCoin(fxtypes.DefaultDenom, sdkmath.NewIntWithDecimal(1_000_000_000, 18)))
require.NoError(t, myApp.BankKeeper.MintCoins(ctx, minttypes.ModuleName, amount))
accAddr := helpers.GenAccAddress()
require.NoError(t, myApp.BankKeeper.SendCoinsFromModuleToAccount(ctx, minttypes.ModuleName, accAddr, amount))

endTime := tmtime.Now().Add(21 * 24 * time.Hour)
myApp.GovKeeper.IterateInactiveProposalsQueue(ctx, endTime, func(proposal v1.Proposal) (stop bool) {
minDeposit := myApp.GovKeeper.NeedMinDeposit(ctx, proposal)
if sdk.NewCoins(proposal.TotalDeposit...).IsAllGTE(minDeposit) {
return false
}
insufficientCoins := sdk.NewCoins(minDeposit...).Sub(proposal.TotalDeposit...)
deposit, err := myApp.GovKeeper.AddDeposit(ctx, proposal.Id, accAddr, insufficientCoins)
require.NoError(t, err)
require.True(t, deposit)
return false
})

myApp.GovKeeper.IterateActiveProposalsQueue(ctx, endTime, func(proposal v1.Proposal) (stop bool) {
if isUpgradeProposal(t, proposal) {
ctx.Logger().Info("skip upgrade proposal", "id", proposal.Id)
return false
}
ingProposalIds = append(ingProposalIds, proposal.Id)
powerStoreIterator := myApp.StakingKeeper.ValidatorsPowerStoreIterator(ctx)
defer powerStoreIterator.Close()
voteOptions := v1.WeightedVoteOptions{&v1.WeightedVoteOption{Option: v1.VoteOption_VOTE_OPTION_YES, Weight: "1"}}
for ; powerStoreIterator.Valid(); powerStoreIterator.Next() {
err := myApp.GovKeeper.AddVote(ctx, proposal.Id, powerStoreIterator.Value(), voteOptions, "")
require.NoError(t, err)
}
return false
})
return ingProposalIds
}

func isUpgradeProposal(t *testing.T, proposal v1.Proposal) bool {
msgs, err := proposal.GetMsgs()
require.NoError(t, err)

if len(msgs) != 1 {
return false
}
msg := msgs[0]
if _, ok := msg.(*upgradetypes.MsgSoftwareUpgrade); ok {
return true
}
legacyMsg, ok := msg.(*v1.MsgExecLegacyContent)
if !ok {
return false
}
content, err := v1.LegacyContentFromMessage(legacyMsg)
require.NoError(t, err)
if _, ok = content.(*upgradetypes.SoftwareUpgradeProposal); ok { // nolint:staticcheck
return true
}
return false
}

func checkProposalPassed(t *testing.T, ctx sdk.Context, myApp *app.App, ids []uint64) {
for _, id := range ids {
proposal, found := myApp.GovKeeper.GetProposal(ctx, id)
require.True(t, found)
require.Equal(t, proposal.Status, v1.ProposalStatus_PROPOSAL_STATUS_PASSED)
t.Logf("proposal id:%d, status:%d, title:%s, msgType:%s", id, proposal.Status, proposal.Title, proposal.Messages[0].TypeUrl)
}
}
71 changes: 68 additions & 3 deletions app/upgrades/v7/upgrade.go
Original file line number Diff line number Diff line change
@@ -1,23 +1,29 @@
package v7

import (
"time"

"github.com/cosmos/cosmos-sdk/baseapp"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/module"
autytypes "github.com/cosmos/cosmos-sdk/x/auth/types"
sdktx "github.com/cosmos/cosmos-sdk/types/tx"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types"
v1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1"
upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types"

"github.com/functionx/fx-core/v7/app/keepers"
"github.com/functionx/fx-core/v7/contract"
fxtypes "github.com/functionx/fx-core/v7/types"
crosschaintypes "github.com/functionx/fx-core/v7/x/crosschain/types"
fxevmkeeper "github.com/functionx/fx-core/v7/x/evm/keeper"
fxgovkeeper "github.com/functionx/fx-core/v7/x/gov/keeper"
)

func CreateUpgradeHandler(mm *module.Manager, configurator module.Configurator, app *keepers.AppKeepers) upgradetypes.UpgradeHandler {
return func(ctx sdk.Context, plan upgradetypes.Plan, fromVM module.VersionMap) (module.VersionMap, error) {
// Testnet skip
if fxtypes.ChainId() == fxtypes.TestnetChainId {
if ctx.ChainID() == fxtypes.TestnetChainId {
return fromVM, nil
}
// Migrate Tendermint consensus parameters from x/params module to a dedicated x/consensus module.
Expand All @@ -37,11 +43,13 @@ func CreateUpgradeHandler(mm *module.Manager, configurator module.Configurator,

UpdateWFXLogicCode(cacheCtx, app.EvmKeeper)
UpdateFIP20LogicCode(cacheCtx, app.EvmKeeper)
crosschainBridgeCallFrom := autytypes.NewModuleAddress(crosschaintypes.ModuleName)
crosschainBridgeCallFrom := authtypes.NewModuleAddress(crosschaintypes.ModuleName)
if account := app.AccountKeeper.GetAccount(ctx, crosschainBridgeCallFrom); account == nil {
app.AccountKeeper.SetAccount(ctx, app.AccountKeeper.NewAccountWithAddress(ctx, crosschainBridgeCallFrom))
}

MigrateCommunityPoolSpendProposals(cacheCtx, app.GovKeeper)

commit()
ctx.Logger().Info("upgrade complete", "module", "upgrade")
return toVM, nil
Expand All @@ -65,3 +73,60 @@ func UpdateFIP20LogicCode(ctx sdk.Context, keeper *fxevmkeeper.Keeper) {
ctx.Logger().Info("update FIP20 contract", "module", "upgrade", "codeHash", fip20.CodeHash())
}
}

func MigrateCommunityPoolSpendProposals(ctx sdk.Context, keeper *fxgovkeeper.Keeper) {
// migrate inactive CommunityPoolSpendProposal
maxEndTime := time.Hour * 24 * 14
keeper.IterateInactiveProposalsQueue(ctx, ctx.BlockHeader().Time.Add(maxEndTime), func(proposal v1.Proposal) (stop bool) {
ConvertCommunityPoolSpendProposal(ctx, keeper, proposal)
return false
})

// migrate active CommunityPoolSpendProposal
keeper.IterateActiveProposalsQueue(ctx, ctx.BlockHeader().Time.Add(maxEndTime), func(proposal v1.Proposal) (stop bool) {
ConvertCommunityPoolSpendProposal(ctx, keeper, proposal)
return false
})
}

func ConvertCommunityPoolSpendProposal(ctx sdk.Context, keeper *fxgovkeeper.Keeper, proposal v1.Proposal) {
msgs, err := proposal.GetMsgs()
if err != nil {
panic(err)
}
haveSpendMsg := false
newMsgs := make([]sdk.Msg, 0, len(msgs))
for _, msg := range msgs {
legacyMsg, ok := msg.(*v1.MsgExecLegacyContent)
if !ok {
newMsgs = append(newMsgs, msg)
continue
}
content, err := v1.LegacyContentFromMessage(legacyMsg)
if err != nil {
panic(err)
}
cpsp, ok := content.(*distrtypes.CommunityPoolSpendProposal) // nolint:staticcheck
if !ok {
newMsgs = append(newMsgs, msg)
continue
}
haveSpendMsg = true
spendMsg := &distrtypes.MsgCommunityPoolSpend{
Authority: keeper.GetAuthority(),
Recipient: cpsp.Recipient,
Amount: cpsp.Amount,
}
newMsgs = append(newMsgs, spendMsg)
}
if !haveSpendMsg {
return
}
ctx.Logger().Info("migrate community pool spend proposal,", "id", proposal.Id)
anyMsgs, err := sdktx.SetMsgs(newMsgs)
if err != nil {
panic(err)
}
proposal.Messages = anyMsgs
keeper.SetProposal(ctx, proposal)
}

0 comments on commit 4edcd2b

Please sign in to comment.