diff --git a/app/app.go b/app/app.go index 21a36269..6a15fb71 100644 --- a/app/app.go +++ b/app/app.go @@ -1149,6 +1149,8 @@ func (app *MigalooApp) setupUpgradeHandlers() { app.ConsensusParamsKeeper, app.ICAControllerKeeper, app.AccountKeeper, + *app.StakingKeeper, + app.BankKeeper, ), ) diff --git a/app/test_helpers.go b/app/test_helpers.go index bed20f74..fca8b11f 100644 --- a/app/test_helpers.go +++ b/app/test_helpers.go @@ -1,7 +1,18 @@ package app import ( + "cosmossdk.io/math" "encoding/json" + "fmt" + tmproto "github.com/cometbft/cometbft/proto/tendermint/types" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/types/tx/signing" + authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing" + distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" + minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" + slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" + stakinghelper "github.com/cosmos/cosmos-sdk/x/staking/testutil" + upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" "testing" "time" @@ -10,8 +21,9 @@ import ( 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" + tdmtypes "github.com/cometbft/cometbft/proto/tendermint/types" tmtypes "github.com/cometbft/cometbft/types" + "github.com/cosmos/cosmos-sdk/baseapp" codectypes "github.com/cosmos/cosmos-sdk/codec/types" cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" @@ -36,23 +48,26 @@ const ( type KeeperTestHelper struct { suite.Suite - App *MigalooApp - Ctx sdk.Context // ctx is deliver ctx - CheckCtx sdk.Context - QueryHelper *baseapp.QueryServiceTestHelper - TestAccs []sdk.AccAddress + App *MigalooApp + Ctx sdk.Context // ctx is deliver ctx + CheckCtx sdk.Context + QueryHelper *baseapp.QueryServiceTestHelper + TestAccs []sdk.AccAddress + StakingHelper *stakinghelper.Helper } func (s *KeeperTestHelper) Setup(_ *testing.T, chainID string) { - s.App = SetupApp(s.T()) - s.Ctx = s.App.BaseApp.NewContext(false, tmproto.Header{Height: 1, ChainID: chainID, Time: time.Now().UTC()}) - s.CheckCtx = s.App.BaseApp.NewContext(true, tmproto.Header{Height: 1, ChainID: chainID, Time: time.Now().UTC()}) + t := s.T() + s.App = SetupApp(t) + s.Ctx = s.App.BaseApp.NewContext(false, tdmtypes.Header{Height: 1, ChainID: "test-1", Time: time.Now().UTC()}) s.QueryHelper = &baseapp.QueryServiceTestHelper{ GRPCQueryRouter: s.App.GRPCQueryRouter(), Ctx: s.Ctx, } + s.TestAccs = CreateRandomAccounts(3) - s.TestAccs = s.RandomAccountAddresses(3) + s.StakingHelper = stakinghelper.NewHelper(s.Suite.T(), s.Ctx, s.App.StakingKeeper) + s.StakingHelper.Denom = "uwhale" } // DefaultConsensusParams defines the default Tendermint consensus params used @@ -253,3 +268,177 @@ type EmptyBaseAppOptions struct{} func (ao EmptyBaseAppOptions) Get(_ string) interface{} { return nil } + +// CreateTestContext creates a test context. +func (s *KeeperTestHelper) Commit() { + oldHeight := s.Ctx.BlockHeight() + oldHeader := s.Ctx.BlockHeader() + s.App.Commit() + newHeader := tdmtypes.Header{Height: oldHeight + 1, ChainID: "testing", Time: oldHeader.Time.Add(time.Second)} + s.App.BeginBlock(abci.RequestBeginBlock{Header: newHeader}) + s.Ctx = s.App.NewContext(false, newHeader) +} + +// FundModuleAcc funds target modules with specified amount. +func (s *KeeperTestHelper) FundModuleAcc(moduleName string, amounts sdk.Coins) { + err := banktestutil.FundModuleAccount(s.App.BankKeeper, s.Ctx, moduleName, amounts) + s.Require().NoError(err) +} + +func (s *KeeperTestHelper) MintCoins(coins sdk.Coins) { + err := s.App.BankKeeper.MintCoins(s.Ctx, minttypes.ModuleName, coins) + s.Require().NoError(err) +} + +// SetupValidator sets up a validator and returns the ValAddress. +func (s *KeeperTestHelper) SetupValidator(bondStatus stakingtypes.BondStatus) sdk.ValAddress { + valPriv := secp256k1.GenPrivKey() + valPub := valPriv.PubKey() + valAddr := sdk.ValAddress(valPub.Address()) + bondDenom := s.App.StakingKeeper.GetParams(s.Ctx).BondDenom + selfBond := sdk.NewCoins(sdk.Coin{Amount: sdk.NewInt(100), Denom: bondDenom}) + + s.FundAcc(sdk.AccAddress(valAddr), selfBond) + + msg := s.StakingHelper.CreateValidatorMsg(valAddr, valPub, selfBond[0].Amount) + res, err := s.StakingHelper.CreateValidatorWithMsg(s.Ctx, msg) + s.Require().NoError(err) + s.Require().NotNil(res) + + val, found := s.App.StakingKeeper.GetValidator(s.Ctx, valAddr) + s.Require().True(found) + + val = val.UpdateStatus(bondStatus) + s.App.StakingKeeper.SetValidator(s.Ctx, val) + + consAddr, err := val.GetConsAddr() + s.Suite.Require().NoError(err) + + signingInfo := slashingtypes.NewValidatorSigningInfo( + consAddr, + s.Ctx.BlockHeight(), + 0, + time.Unix(0, 0), + false, + 0, + ) + s.App.SlashingKeeper.SetValidatorSigningInfo(s.Ctx, consAddr, signingInfo) + + return valAddr +} + +// BeginNewBlock starts a new block. +func (s *KeeperTestHelper) BeginNewBlock() { + var valAddr []byte + + validators := s.App.StakingKeeper.GetAllValidators(s.Ctx) + if len(validators) >= 1 { + valAddrFancy, err := validators[0].GetConsAddr() + s.Require().NoError(err) + valAddr = valAddrFancy.Bytes() + } else { + valAddrFancy := s.SetupValidator(stakingtypes.Bonded) + validator, _ := s.App.StakingKeeper.GetValidator(s.Ctx, valAddrFancy) + valAddr2, _ := validator.GetConsAddr() + valAddr = valAddr2.Bytes() + } + + s.BeginNewBlockWithProposer(valAddr) +} + +// BeginNewBlockWithProposer begins a new block with a proposer. +func (s *KeeperTestHelper) BeginNewBlockWithProposer(proposer sdk.ValAddress) { + validator, found := s.App.StakingKeeper.GetValidator(s.Ctx, proposer) + s.Assert().True(found) + + valConsAddr, err := validator.GetConsAddr() + s.Require().NoError(err) + + valAddr := valConsAddr.Bytes() + + newBlockTime := s.Ctx.BlockTime().Add(5 * time.Second) + + header := tdmtypes.Header{Height: s.Ctx.BlockHeight() + 1, Time: newBlockTime} + newCtx := s.Ctx.WithBlockTime(newBlockTime).WithBlockHeight(s.Ctx.BlockHeight() + 1) + s.Ctx = newCtx + lastCommitInfo := abci.CommitInfo{ + Votes: []abci.VoteInfo{{ + Validator: abci.Validator{Address: valAddr, Power: 1000}, + SignedLastBlock: true, + }}, + Round: 0, + } + reqBeginBlock := abci.RequestBeginBlock{Header: header, LastCommitInfo: lastCommitInfo} + + fmt.Println("beginning block ", s.Ctx.BlockHeight()) + s.App.BeginBlocker(s.Ctx, reqBeginBlock) +} + +// EndBlock ends the block. +func (s *KeeperTestHelper) EndBlock() { + reqEndBlock := abci.RequestEndBlock{Height: s.Ctx.BlockHeight()} + s.App.EndBlocker(s.Ctx, reqEndBlock) +} + +// AllocateRewardsToValidator allocates reward tokens to a distribution module then allocates rewards to the validator address. +func (s *KeeperTestHelper) AllocateRewardsToValidator(valAddr sdk.ValAddress, rewardAmt math.Int) { + validator, found := s.App.StakingKeeper.GetValidator(s.Ctx, valAddr) + s.Require().True(found) + + // allocate reward tokens to distribution module + coins := sdk.Coins{sdk.NewCoin(config.BaseDenom, rewardAmt)} + err := banktestutil.FundModuleAccount(s.App.BankKeeper, s.Ctx, distrtypes.ModuleName, coins) + s.Require().NoError(err) + + // allocate rewards to validator + s.Ctx = s.Ctx.WithBlockHeight(s.Ctx.BlockHeight() + 1) + decTokens := sdk.DecCoins{{Denom: config.BaseDenom, Amount: sdk.NewDec(20000)}} + s.App.DistrKeeper.AllocateTokensToValidator(s.Ctx, validator, decTokens) +} + +// BuildTx builds a transaction. +func (s *KeeperTestHelper) BuildTx( + txBuilder client.TxBuilder, + msgs []sdk.Msg, + sigV2 signing.SignatureV2, + memo string, txFee sdk.Coins, + gasLimit uint64, +) authsigning.Tx { + err := txBuilder.SetMsgs(msgs[0]) + s.Require().NoError(err) + + err = txBuilder.SetSignatures(sigV2) + s.Require().NoError(err) + + txBuilder.SetMemo(memo) + txBuilder.SetFeeAmount(txFee) + txBuilder.SetGasLimit(gasLimit) + + return txBuilder.GetTx() +} + +func (s *KeeperTestHelper) ConfirmUpgradeSucceeded(upgradeName string, upgradeHeight int64) { + s.Ctx = s.Ctx.WithBlockHeight(upgradeHeight - 1) + plan := upgradetypes.Plan{Name: upgradeName, Height: upgradeHeight} + err := s.App.UpgradeKeeper.ScheduleUpgrade(s.Ctx, plan) + s.Require().NoError(err) + _, exists := s.App.UpgradeKeeper.GetUpgradePlan(s.Ctx) + s.Require().True(exists) + + s.Ctx = s.Ctx.WithBlockHeight(upgradeHeight) + s.Require().NotPanics(func() { + beginBlockRequest := abci.RequestBeginBlock{} + s.App.BeginBlocker(s.Ctx, beginBlockRequest) + }) +} + +// CreateRandomAccounts is a function return a list of randomly generated AccAddresses +func CreateRandomAccounts(numAccts int) []sdk.AccAddress { + testAddrs := make([]sdk.AccAddress, numAccts) + for i := 0; i < numAccts; i++ { + pk := ed25519.GenPrivKey().PubKey() + testAddrs[i] = sdk.AccAddress(pk.Address()) + } + + return testAddrs +} diff --git a/app/upgrades/v4_1_0/constants.go b/app/upgrades/v4_1_0/constants.go index c68f43a0..09b3c5ae 100644 --- a/app/upgrades/v4_1_0/constants.go +++ b/app/upgrades/v4_1_0/constants.go @@ -2,4 +2,8 @@ package v4 // UpgradeName defines the on-chain upgrade name for the Migaloo v3.0.2 upgrade. // this upgrade includes the fix for pfm -const UpgradeName = "v4.1.0" +const ( + UpgradeName = "v4.1.0" + NotionalMultisigVestingAccount = "migaloo1alga5e8vr6ccr9yrg0kgxevpt5xgmgrvqgujs6" + NewNotionalMultisigVestingAccount = "migaloo1tx6f2hetpd9uhveja26kr074496hzk2zzqhsh0" +) diff --git a/app/upgrades/v4_1_0/mainnet_account.go b/app/upgrades/v4_1_0/mainnet_account.go new file mode 100644 index 00000000..eab55497 --- /dev/null +++ b/app/upgrades/v4_1_0/mainnet_account.go @@ -0,0 +1,35 @@ +package v4 + +import ( + "encoding/json" + authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" + bankKeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" + + "cosmossdk.io/math" + + sdk "github.com/cosmos/cosmos-sdk/types" + vestingtypes "github.com/cosmos/cosmos-sdk/x/auth/vesting/types" + banktestutil "github.com/cosmos/cosmos-sdk/x/bank/testutil" +) + +func CreateMainnetVestingAccount(ctx sdk.Context, + bankKeeper bankKeeper.Keeper, + accountKeeper authkeeper.AccountKeeper) (*vestingtypes.ContinuousVestingAccount, math.Int) { + str := `{"@type":"/cosmos.vesting.v1beta1.ContinuousVestingAccount","base_vesting_account":{"base_account":{"address":"migaloo1alga5e8vr6ccr9yrg0kgxevpt5xgmgrvqgujs6","pub_key":{"@type":"/cosmos.crypto.multisig.LegacyAminoPubKey","threshold":4,"public_keys":[{"@type":"/cosmos.crypto.secp256k1.PubKey","key":"AlnzK22KrkylnvTCvZZc8eZnydtQuzCWLjJJSMFUvVHf"},{"@type":"/cosmos.crypto.secp256k1.PubKey","key":"Aiw2Ftg+fnoHDU7M3b0VMRsI0qurXlerW0ahtfzSDZA4"},{"@type":"/cosmos.crypto.secp256k1.PubKey","key":"AvEHv+MVYRVau8FbBcJyG0ql85Tbbn7yhSA0VGmAY4ku"},{"@type":"/cosmos.crypto.secp256k1.PubKey","key":"Az5VHWqi3zMJu1rLGcu2EgNXLLN+al4Dy/lj6UZTzTCl"},{"@type":"/cosmos.crypto.secp256k1.PubKey","key":"Ai4GlSH3uG+joMnAFbQC3jQeHl9FPvVTlRmwIFt7d7TI"},{"@type":"/cosmos.crypto.secp256k1.PubKey","key":"A2kAzH2bZr530jmFq/bRFrT2q8SRqdnfIebba+YIBqI1"}]},"account_number":46,"sequence":27},"original_vesting":[{"denom":"uwhale","amount":"22165200000000"}],"delegated_free":[{"denom":"uwhale","amount":"443382497453"}],"delegated_vesting":[{"denom":"uwhale","amount":"22129422502547"}],"end_time":1770994800},"start_time":1676300400}` + + var acc vestingtypes.ContinuousVestingAccount + if err := json.Unmarshal([]byte(str), &acc); err != nil { + panic(err) + } + + vesting := GetVestingCoin(ctx, &acc) + + err := banktestutil.FundAccount(bankKeeper, ctx, acc.BaseAccount.GetAddress(), + acc.GetOriginalVesting()) + if err != nil { + panic(err) + } + + accountKeeper.SetAccount(ctx, &acc) + return &acc, vesting +} diff --git a/app/upgrades/v4_1_0/upgrades.go b/app/upgrades/v4_1_0/upgrades.go index 9dfc72a6..c2b6d773 100644 --- a/app/upgrades/v4_1_0/upgrades.go +++ b/app/upgrades/v4_1_0/upgrades.go @@ -1,14 +1,23 @@ package v4 import ( + "fmt" + "time" + + "cosmossdk.io/math" + "github.com/White-Whale-Defi-Platform/migaloo-chain/v4/app/params" "github.com/cosmos/cosmos-sdk/baseapp" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + vestingtypes "github.com/cosmos/cosmos-sdk/x/auth/vesting/types" + bankKeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" consensuskeeper "github.com/cosmos/cosmos-sdk/x/consensus/keeper" paramskeeper "github.com/cosmos/cosmos-sdk/x/params/keeper" paramstypes "github.com/cosmos/cosmos-sdk/x/params/types" + stakingKeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" icacontrollerkeeper "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/controller/keeper" icacontrollertypes "github.com/cosmos/ibc-go/v7/modules/apps/27-interchain-accounts/controller/types" @@ -25,7 +34,8 @@ func CreateUpgradeHandler( consensusParamsKeeper consensuskeeper.Keeper, icacontrollerKeeper icacontrollerkeeper.Keeper, accountKeeper authkeeper.AccountKeeper, - + stakingKeeper stakingKeeper.Keeper, + bankKeeper bankKeeper.Keeper, ) upgradetypes.UpgradeHandler { return func(ctx sdk.Context, _plan upgradetypes.Plan, fromVM module.VersionMap) (module.VersionMap, error) { // READ: https://github.com/cosmos/cosmos-sdk/blob/v0.47.4/UPGRADING.md#xconsensus @@ -47,6 +57,159 @@ func CreateUpgradeHandler( moduleAcc.Permissions = []string{authtypes.Burner} accountKeeper.SetModuleAccount(ctx, moduleAcc) + migrateMultisigVesting(ctx, stakingKeeper, bankKeeper, accountKeeper) return mm.RunMigrations(ctx, configurator, fromVM) } } + +// migrateMultisigVesting moves the vested and reward token from the ContinuousVestingAccount -> the new multisig vesting account. +// - Retrieves the old multisig vesting account +// - Instantly finish all redelegations, then unbond all tokens. +// - Transfer all tokens vested and reward tokens to the new multisig vesting (including the previously held balance) +// - Delegates the vesting coins to the top 10 validators +func migrateMultisigVesting(ctx sdk.Context, + stakingKeeper stakingKeeper.Keeper, + bankKeeper bankKeeper.Keeper, + accountKeeper authkeeper.AccountKeeper) { + currentAddr := sdk.MustAccAddressFromBech32(NotionalMultisigVestingAccount) + newAddr := sdk.MustAccAddressFromBech32(NewNotionalMultisigVestingAccount) + + currentAcc := accountKeeper.GetAccount(ctx, currentAddr) + + currentVestingAcc, ok := currentAcc.(*vestingtypes.ContinuousVestingAccount) + if !ok { + // skip if account invalid + fmt.Printf("err currentAcc.(*vestingtypes.ContinuousVestingAccount): %+v", currentAcc) + return + } + // process migrate + processMigrateMultisig(ctx, stakingKeeper, bankKeeper, currentAddr, newAddr, currentVestingAcc) +} + +func processMigrateMultisig(ctx sdk.Context, stakingKeeper stakingKeeper.Keeper, + bankKeeper bankKeeper.Keeper, currentAddr, newAddr sdk.AccAddress, + oldAcc *vestingtypes.ContinuousVestingAccount) { // nolint:gocritic + redelegated, err := completeAllRedelegations(ctx, ctx.BlockTime(), stakingKeeper, currentAddr) + if err != nil { + panic(err) + } + + unbonded, err := unbondAllAndFinish(ctx, ctx.BlockTime(), stakingKeeper, currentAddr) + if err != nil { + panic(err) + } + + fmt.Printf("currentAddr Instant Redelegations: %s\n", redelegated) + fmt.Printf("currentAddr Instant Unbonding: %s\n", unbonded) + + //delegate vesting coin to validator + err = delegateToValidator(ctx, stakingKeeper, currentAddr, oldAcc.GetVestingCoins(ctx.BlockTime())[0].Amount) + if err != nil { + panic(err) + } + + // get vested + reward balance + totalBalance := bankKeeper.GetBalance(ctx, currentAddr, params.BaseDenom) + fmt.Println("total balance before migration ", totalBalance) + balanceCanSend := totalBalance.Amount.Sub(oldAcc.GetVestingCoins(ctx.BlockTime())[0].Amount) + + // keep 100.000.000 uwhate for tx fee + balanceCanSend = balanceCanSend.Sub(math.NewInt(100_000_000)) + fmt.Printf("total balance send to new multisig addr: %s\n", balanceCanSend) + // send vested + reward balance no newAddr + err = bankKeeper.SendCoins(ctx, currentAddr, newAddr, sdk.NewCoins(sdk.NewCoin(params.BaseDenom, balanceCanSend))) + if err != nil { + panic(err) + } + +} +func GetVestingCoin(ctx sdk.Context, acc *vestingtypes.ContinuousVestingAccount) (unvested math.Int) { + vestingCoin := acc.GetVestingCoins(ctx.BlockTime()) + return vestingCoin[0].Amount +} + +func completeAllRedelegations(ctx sdk.Context, now time.Time, + stakingKeeper stakingKeeper.Keeper, + accAddr sdk.AccAddress) (math.Int, error) { + redelegatedAmt := math.ZeroInt() + + for _, activeRedelegation := range stakingKeeper.GetRedelegations(ctx, accAddr, 65535) { + redelegationSrc, _ := sdk.ValAddressFromBech32(activeRedelegation.ValidatorSrcAddress) + redelegationDst, _ := sdk.ValAddressFromBech32(activeRedelegation.ValidatorDstAddress) + + // set all entry completionTime to now so we can complete re-delegation + for i := range activeRedelegation.Entries { + activeRedelegation.Entries[i].CompletionTime = now + redelegatedAmt = redelegatedAmt.Add(math.Int(activeRedelegation.Entries[i].SharesDst)) + } + + stakingKeeper.SetRedelegation(ctx, activeRedelegation) + _, err := stakingKeeper.CompleteRedelegation(ctx, accAddr, redelegationSrc, redelegationDst) + if err != nil { + return redelegatedAmt, err + } + } + + return redelegatedAmt, nil +} + +func unbondAllAndFinish(ctx sdk.Context, now time.Time, + stakingKeeper stakingKeeper.Keeper, + accAddr sdk.AccAddress) (math.Int, error) { + unbondedAmt := math.ZeroInt() + + // Unbond all delegations from the account + for _, delegation := range stakingKeeper.GetAllDelegatorDelegations(ctx, accAddr) { + validatorValAddr := delegation.GetValidatorAddr() + _, found := stakingKeeper.GetValidator(ctx, validatorValAddr) + if !found { + continue + } + + _, err := stakingKeeper.Undelegate(ctx, accAddr, validatorValAddr, delegation.GetShares()) + if err != nil { + return math.ZeroInt(), err + } + } + + // Take all unbonding and complete them. + for _, unbondingDelegation := range stakingKeeper.GetAllUnbondingDelegations(ctx, accAddr) { + validatorStringAddr := unbondingDelegation.ValidatorAddress + validatorValAddr, _ := sdk.ValAddressFromBech32(validatorStringAddr) + + // Complete unbonding delegation + for i := range unbondingDelegation.Entries { + unbondingDelegation.Entries[i].CompletionTime = now + unbondedAmt = unbondedAmt.Add(unbondingDelegation.Entries[i].Balance) + } + + stakingKeeper.SetUnbondingDelegation(ctx, unbondingDelegation) + _, err := stakingKeeper.CompleteUnbonding(ctx, accAddr, validatorValAddr) + if err != nil { + return math.ZeroInt(), err + } + } + + return unbondedAmt, nil +} + +// delegate to top 10 validator +func delegateToValidator(ctx sdk.Context, + stakingKeeper stakingKeeper.Keeper, + accAddr sdk.AccAddress, totalVestingBalance math.Int) error { + + listValidator := stakingKeeper.GetBondedValidatorsByPower(ctx) + totalValidatorDelegate := math.Min(10, len(listValidator)) + balanceDelegate := totalVestingBalance.Quo(totalVestingBalance) + + for i, validator := range listValidator { + if i >= totalValidatorDelegate { + break + } + _, err := stakingKeeper.Delegate(ctx, accAddr, balanceDelegate, stakingtypes.Unbonded, validator, true) + if err != nil { + return err + } + } + return nil +} diff --git a/app/upgrades/v4_1_0/upgrades_test.go b/app/upgrades/v4_1_0/upgrades_test.go index 9ea52f7d..365212ec 100644 --- a/app/upgrades/v4_1_0/upgrades_test.go +++ b/app/upgrades/v4_1_0/upgrades_test.go @@ -1,18 +1,18 @@ package v4_test import ( + "fmt" + "github.com/White-Whale-Defi-Platform/migaloo-chain/v4/app/params" + v4 "github.com/White-Whale-Defi-Platform/migaloo-chain/v4/app/upgrades/v4_1_0" + sdk "github.com/cosmos/cosmos-sdk/types" + vestingtypes "github.com/cosmos/cosmos-sdk/x/auth/vesting/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" "testing" apptesting "github.com/White-Whale-Defi-Platform/migaloo-chain/v4/app" - abci "github.com/cometbft/cometbft/abci/types" - upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" "github.com/stretchr/testify/suite" ) -const ( - v4UpgradeHeight = int64(10) -) - type UpgradeTestSuite struct { apptesting.KeeperTestHelper } @@ -21,25 +21,63 @@ func TestUpgradeTestSuite(t *testing.T) { suite.Run(t, new(UpgradeTestSuite)) } -func (suite *UpgradeTestSuite) TestUpgrade() { - suite.Setup(suite.T(), apptesting.SimAppChainID) - dummyUpgrade(suite) - feeBurnParam := suite.App.FeeBurnKeeper.GetParams(suite.Ctx) - suite.Require().Equal("0", feeBurnParam.GetTxFeeBurnPercent()) -} +// Ensures the test does not error out. +func (s *UpgradeTestSuite) TestUpgrade() { + s.Setup(s.T(), apptesting.SimAppChainID) + // == CREATE MOCK VESTING ACCOUNT == + cVesting, unvested := v4.CreateMainnetVestingAccount(s.Ctx, s.App.BankKeeper, s.App.AccountKeeper) + vestingAddr := cVesting.GetAddress() + fmt.Printf("VestingAddr unvested: %+v\n", unvested) + + accVestingBalance := s.App.BankKeeper.GetAllBalances(s.Ctx, vestingAddr) + fmt.Printf("Acc vesting bal: %s\n", accVestingBalance) + + // create many validators to confirm the unbonding code works + newVal1 := s.SetupValidator(stakingtypes.Bonded) + newVal2 := s.SetupValidator(stakingtypes.Bonded) + newVal3 := s.SetupValidator(stakingtypes.Bonded) + + // Delegate tokens of the vesting multisig account + s.StakingHelper.Delegate(vestingAddr, newVal1, sdk.NewInt(100)) + s.StakingHelper.Delegate(vestingAddr, newVal2, sdk.NewInt(200)) + s.StakingHelper.Delegate(vestingAddr, newVal3, sdk.NewInt(300)) + + // Undelegate part of the tokens from val2 (test instant unbonding on undelegation started before upgrade) + s.StakingHelper.Undelegate(vestingAddr, newVal3, sdk.NewInt(10), true) -func dummyUpgrade(s *UpgradeTestSuite) { - s.Ctx = s.Ctx.WithBlockHeight(v4UpgradeHeight - 1) - plan := upgradetypes.Plan{Name: "v4.1.0", Height: v4UpgradeHeight} - err := s.App.UpgradeKeeper.ScheduleUpgrade(s.Ctx, plan) + // Redelegate part of the tokens from val2 -> val3 (test instant unbonding on redelegations started before upgrade) + _, err := s.App.StakingKeeper.BeginRedelegation(s.Ctx, vestingAddr, newVal2, newVal3, sdk.NewDec(1)) s.Require().NoError(err) - _, exists := s.App.UpgradeKeeper.GetUpgradePlan(s.Ctx) - s.Require().True(exists) - s.Ctx = s.Ctx.WithBlockHeight(v4UpgradeHeight) + // Confirm delegated to 3 validators + s.Require().Equal(3, len(s.App.StakingKeeper.GetAllDelegatorDelegations(s.Ctx, vestingAddr))) + + // == UPGRADE == + upgradeHeight := int64(5) + s.ConfirmUpgradeSucceeded(v4.UpgradeName, upgradeHeight) + + // == VERIFICATION FEEBURN == + feeBurnParam := s.App.FeeBurnKeeper.GetParams(s.Ctx) + s.Require().Equal("0", feeBurnParam.GetTxFeeBurnPercent()) + + // VERIFY MULTISIGN MIGRATION + accAfter := s.App.AccountKeeper.GetAccount(s.Ctx, vestingAddr) + _, ok := accAfter.(*vestingtypes.ContinuousVestingAccount) + s.Require().True(ok) + + s.Require().Equal(1, len(s.App.BankKeeper.GetAllBalances(s.Ctx, vestingAddr))) + s.Require().Equal(4, len(s.App.StakingKeeper.GetAllDelegatorDelegations(s.Ctx, vestingAddr))) + s.Require().Equal(0, len(s.App.StakingKeeper.GetRedelegations(s.Ctx, vestingAddr, 65535))) + + // check old multisign address balance + oldMultisigBalance := s.App.BankKeeper.GetAllBalances(s.Ctx, sdk.MustAccAddressFromBech32(v4.NotionalMultisigVestingAccount)) + fmt.Printf("Old multisign address Upgrade Balance: %s\n", oldMultisigBalance) + s.Require().True(oldMultisigBalance.AmountOf(params.BaseDenom).GTE(unvested)) + + // check new multisign address balance + newBalance := s.App.BankKeeper.GetAllBalances(s.Ctx, sdk.MustAccAddressFromBech32(v4.NewNotionalMultisigVestingAccount)) + vestedBalance := cVesting.GetVestedCoins(s.Ctx.BlockTime()) + fmt.Printf("New multisign Upgrade Balance: %s\n", newBalance) + s.Require().True(newBalance.AmountOf(params.BaseDenom).LTE(vestedBalance.AmountOf(params.BaseDenom))) - s.Require().NotPanics(func() { - beginBlockRequest := abci.RequestBeginBlock{} - s.App.BeginBlocker(s.Ctx, beginBlockRequest) - }) } diff --git a/scripts/run-node.sh b/scripts/run-node.sh index 227395cf..fa431b98 100755 --- a/scripts/run-node.sh +++ b/scripts/run-node.sh @@ -82,5 +82,5 @@ $BINARY collect-gentxs --home $HOME_DIR # Run this to ensure everything worked and that the genesis file is setup correctly $BINARY validate-genesis --home $HOME_DIR -# $BINARY start --home $HOME_DIR +$BINARY start --home $HOME_DIR