Skip to content

Commit

Permalink
add upgrade test for v020
Browse files Browse the repository at this point in the history
  • Loading branch information
hard-nett committed Dec 22, 2024
1 parent c7dbf2a commit 0eb2b8a
Show file tree
Hide file tree
Showing 3 changed files with 109 additions and 155 deletions.
24 changes: 12 additions & 12 deletions app/test_helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ func Setup(t *testing.T) *BitsongApp {
return app
}

// Setup node for testing simulation
func setup(t *testing.T, withGenesis bool, opts ...wasmkeeper.Option) (*BitsongApp, GenesisState) {
db := dbm.NewMemDB()
nodeHome := t.TempDir()
Expand All @@ -110,7 +111,7 @@ func setup(t *testing.T, withGenesis bool, opts ...wasmkeeper.Option) (*BitsongA
appOptions := make(simtestutil.AppOptionsMap, 0)
appOptions[flags.FlagHome] = nodeHome // ensure unique folder

app := NewBitsongApp(log.NewNopLogger(), db, nil, true, EmptyAppOptions{}, opts, bam.SetChainID("testing"), bam.SetSnapshot(snapshotStore, snapshottypes.SnapshotOptions{KeepRecent: 2}))
app := NewBitsongApp(log.NewNopLogger(), db, nil, true, appOptions, opts, bam.SetChainID("testing"), bam.SetSnapshot(snapshotStore, snapshottypes.SnapshotOptions{KeepRecent: 2}))
if withGenesis {
return app, NewDefaultGenesisState()
}
Expand All @@ -123,6 +124,7 @@ func SetupWithGenesisAccounts(t *testing.T, valSet *tmtypes.ValidatorSet, genAcc
t.Helper()

btsgApp, genesisState := setup(t, true)

genesisState = GenesisStateWithValSet(t, btsgApp, genesisState, valSet, genAccs, balances...)

stateBytes, err := json.MarshalIndent(genesisState, "", " ")
Expand Down Expand Up @@ -231,19 +233,17 @@ func GenesisStateWithValSet(t *testing.T,
bondAmt := sdk.DefaultPowerReduction
initValPowers := []abci.ValidatorUpdate{}

for i, val := range valSet.Validators {
if i == 0 {

}
for _, val := range valSet.Validators {
pk, _ := cryptocodec.FromTmPubKeyInterface(val.PubKey)
pkAny, _ := codectypes.NewAnyWithValue(pk)
validator := stakingtypes.Validator{
OperatorAddress: sdk.ValAddress(val.Address).String(),
ConsensusPubkey: pkAny,
Jailed: false,
Status: stakingtypes.Bonded,
Tokens: bondAmt,
DelegatorShares: math.LegacyOneDec().MulTruncate(math.LegacyOneDec().Sub(math.LegacyNewDecWithPrec(1, 3))), // 1 % slash
OperatorAddress: sdk.ValAddress(val.Address).String(),
ConsensusPubkey: pkAny,
Jailed: false,
Status: stakingtypes.Bonded,
Tokens: bondAmt,
DelegatorShares: math.LegacyOneDec(),
// DelegatorShares: math.LegacyOneDec().MulTruncate(math.LegacyOneDec().Sub(math.LegacyNewDecWithPrec(1, 3))), // 1 % slash
Description: stakingtypes.Description{},
UnbondingHeight: int64(0),
UnbondingTime: time.Unix(0, 0).UTC(),
Expand Down Expand Up @@ -284,7 +284,7 @@ func GenesisStateWithValSet(t *testing.T,

for range delegations {
// add delegated tokens to total supply
totalSupply = totalSupply.Add(sdk.NewCoin(sdk.DefaultBondDenom, bondAmt))
totalSupply = totalSupply.Add(sdk.NewCoin(appparams.DefaultBondDenom, bondAmt))
}

// add bonded amount to bonded pool module account
Expand Down
14 changes: 7 additions & 7 deletions app/testing/test_suite.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,23 @@ import (
bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper"
minttypes "github.com/cosmos/cosmos-sdk/x/mint/types"
stakinghelper "github.com/cosmos/cosmos-sdk/x/staking/testutil"

"github.com/stretchr/testify/suite"
)

type KeeperTestHelper struct {
suite.Suite

App *app.BitsongApp
Ctx sdk.Context
QueryHelper *baseapp.QueryServiceTestHelper
TestAccs []sdk.AccAddress
App *app.BitsongApp // Mock bitsong application
Ctx sdk.Context // simulated context
QueryHelper *baseapp.QueryServiceTestHelper // GRPC query simulator
TestAccs []sdk.AccAddress // Test accounts

StakingHelper *stakinghelper.Helper
StakingHelper *stakinghelper.Helper // Useful staking helpers
}

func (s *KeeperTestHelper) Setup() {
t := s.T()
s.App = app.Setup(t)
s.App = app.Setup(s.T())
s.Ctx = s.App.BaseApp.NewContext(false, tmproto.Header{Height: 1, ChainID: "testing", Time: time.Now().UTC()})
s.QueryHelper = &baseapp.QueryServiceTestHelper{
GRPCQueryRouter: s.App.GRPCQueryRouter(),
Expand Down
226 changes: 90 additions & 136 deletions app/upgrades/v020/upgrade_test.go
Original file line number Diff line number Diff line change
@@ -1,166 +1,120 @@
package v020_test

import (
"fmt"
"testing"
"time"

"cosmossdk.io/math"
"github.com/bitsongofficial/go-bitsong/app"
"github.com/bitsongofficial/go-bitsong/app/helpers"
"github.com/bitsongofficial/go-bitsong/app/keepers"
appparams "github.com/bitsongofficial/go-bitsong/app/params"
apptesting "github.com/bitsongofficial/go-bitsong/app/testing"
abci "github.com/cometbft/cometbft/abci/types"

abcitypes "github.com/cometbft/cometbft/abci/types"
tmtypes "github.com/cometbft/cometbft/types"
"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
sdk "github.com/cosmos/cosmos-sdk/types"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
"github.com/cosmos/cosmos-sdk/x/staking/types"
upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
)

const dummyUpgradeHeight = 5

type UpgradeTestSuite struct {
apptesting.KeeperTestHelper
}

func TestUpgradeSuite(t *testing.T) {
suite.Run(t, new(UpgradeTestSuite))

}

func (s *UpgradeTestSuite) SetupTest() {
V020Setup(s.T())
}

const dummyUpgradeHeight = 5

func (s *UpgradeTestSuite) dummyUpgrade() {
s.Ctx = s.Ctx.WithBlockHeight(dummyUpgradeHeight - 1)
plan := upgradetypes.Plan{Name: "v20", Height: dummyUpgradeHeight}
err := s.App.AppKeepers.UpgradeKeeper.ScheduleUpgrade(s.Ctx, plan)
s.Require().NoError(err)
_, exists := s.App.AppKeepers.UpgradeKeeper.GetUpgradePlan(s.Ctx)
s.Require().True(exists)

s.Ctx = s.Ctx.WithBlockHeight(dummyUpgradeHeight)
s.Require().NotPanics(func() {
beginBlockRequest := abcitypes.RequestBeginBlock{}
s.App.BeginBlocker(s.Ctx, beginBlockRequest)
})
s.Setup()
}

// Setup initializes a new BitsongApp
func V020Setup(t *testing.T) *app.BitsongApp {
t.Helper()

privVal := helpers.NewPV()
pubKey, err := privVal.GetPubKey()
require.NoError(t, err)

// create validator set with single validator
validator := tmtypes.NewValidator(pubKey, 1)
valSet := tmtypes.NewValidatorSet([]*tmtypes.Validator{validator})

// generate genesis account
senderPrivKey := secp256k1.GenPrivKey()
acc := authtypes.NewBaseAccount(senderPrivKey.PubKey().Address().Bytes(), senderPrivKey.PubKey(), 0, 0)
balance := banktypes.Balance{
Address: acc.GetAddress().String(),
Coins: sdk.NewCoins(sdk.NewCoin(appparams.DefaultBondDenom, sdk.NewInt(100000000000000))),
}

app := app.SetupWithGenesisAccounts(t, valSet, []authtypes.GenesisAccount{acc}, balance)
return app
func TestUpgradeTestSuite(t *testing.T) {
suite.Run(t, new(UpgradeTestSuite))
}

func (s *UpgradeTestSuite) TestUpgrade() {

// corrupt state to match mainnet
s.setupCorruptedState()

// upgrade software
s.dummyUpgrade()
s.App.BeginBlocker(s.Ctx, abci.RequestBeginBlock{})
s.Ctx = s.Ctx.WithBlockTime(s.Ctx.BlockTime().Add(time.Hour * 24))

// ensure post upgrade reward query works
s.ensurePostUpgradeDistributionWorks()
}

func (s *UpgradeTestSuite) ensurePostUpgradeDistributionWorks() {

}

// setupCorruptedState aligns the testing environment with the mainnet state.
// By running this method, it will modify the delegations tokens from shares,
// reflecting a slashing event that
func (s *UpgradeTestSuite) setupCorruptedState() {
// infractionHeight := int64(3)

power := int64(1000000)
slashFactor := sdk.NewDecWithPrec(1, 2) //1 %
staking := s.App.AppKeepers.StakingKeeper

// Amount of slashing = slash slashFactor * power at time of infraction
amount := staking.TokensFromConsensusPower(s.Ctx, power)
slashAmountDec := sdk.NewDecFromInt(amount).Mul(slashFactor)
slashAmount := slashAmountDec.TruncateInt()

// mimic slashing event on staking power, but not update slashing event to distribution module
validator, _ := staking.GetValidatorByConsAddr(s.Ctx, sdk.ConsAddress{})

operatorAddress := validator.GetOperator()

// call the before-modification hook
if err := staking.Hooks().BeforeValidatorModified(s.Ctx, operatorAddress); err != nil {
staking.Logger(s.Ctx).Error("failed to call before validator modified hook", "error", err)
}
remainingSlashAmount := slashAmount

// cannot decrease balance below zero
tokensToBurn := sdk.MinInt(remainingSlashAmount, validator.Tokens)
tokensToBurn = sdk.MaxInt(tokensToBurn, math.ZeroInt()) // defensive.

// we need to calculate the *effective* slash fraction for distribution
if validator.Tokens.IsPositive() {
effectiveFraction := sdk.NewDecFromInt(tokensToBurn).QuoRoundUp(sdk.NewDecFromInt(validator.Tokens))
// possible if power has changed
if effectiveFraction.GT(math.LegacyOneDec()) {
effectiveFraction = math.LegacyOneDec()
}
// call the before-slashed hook. Omitted to simulate v0.18.0 error bitsong is experiencing
// if err := staking.Hooks().BeforeValidatorSlashed(s.Ctx, operatorAddress, effectiveFraction); err != nil {
// staking.Logger(s.Ctx).Error("failed to call before validator slashed hook", "error", err)
// }
validator = staking.RemoveValidatorTokens(s.Ctx, validator, tokensToBurn)

if err := s.burnBondedTokens(s.App.AppKeepers, s.Ctx, tokensToBurn); err != nil {
panic(err)
upgradeSetup := func() {
validators := s.App.AppKeepers.StakingKeeper.GetAllValidators(s.Ctx)
for _, val := range validators {
// update the tokens staked to validator due to slashing event
// mimic slashing event on staking power, but not update slashing event to distribution module
val.Tokens = math.LegacyNewDecFromInt(val.Tokens).MulTruncate(math.LegacyOneDec().Sub(math.LegacyNewDecWithPrec(1, 3))).RoundInt() // 1 % slash

dels := s.App.AppKeepers.StakingKeeper.GetAllDelegations(s.Ctx)
// fmt.Println("Delegations:", dels)
for _, del := range dels {
endingPeriod := s.App.AppKeepers.DistrKeeper.IncrementValidatorPeriod(s.Ctx, val)
// assert v018 bug is present prior to upgrade
assertPanic(s.T(), func() {
s.App.AppKeepers.DistrKeeper.CalculateDelegationRewards(s.Ctx, val, del, endingPeriod)
})
}
}
}

// Deduct from validator's bonded tokens and update the validator.
// Burn the slashed tokens from the pool account and decrease the total supply.

}

func (s *UpgradeTestSuite) ensurePreUpgradeDistributionPanics() {
// calculate rewards from distribution
testCases := []struct {
name string
pre_upgrade func()
upgrade func()
post_upgrade func()
}{
{
"Test that the upgrade succeeds",
func() {
upgradeSetup()
},
func() {
s.Ctx = s.Ctx.WithBlockHeight(dummyUpgradeHeight - 1)
plan := upgradetypes.Plan{Name: "v020", Height: dummyUpgradeHeight}
err := s.App.AppKeepers.UpgradeKeeper.ScheduleUpgrade(s.Ctx, plan)
s.Require().NoError(err)
_, exists := s.App.AppKeepers.UpgradeKeeper.GetUpgradePlan(s.Ctx)
s.Require().True(exists)

s.Ctx = s.Ctx.WithBlockHeight(dummyUpgradeHeight)
s.Require().NotPanics(func() {
beginBlockRequest := abcitypes.RequestBeginBlock{}
s.App.BeginBlocker(s.Ctx, beginBlockRequest)
})
},
func() {
// assert rewards can be calculated
validators := s.App.AppKeepers.StakingKeeper.GetAllValidators(s.Ctx)
for _, val := range validators {
dels := s.App.AppKeepers.StakingKeeper.GetAllDelegations(s.Ctx)
fmt.Println("Delegations:", dels)
for _, del := range dels {
// confirm delegators can query, withdraw and stake
// require all rewards to have been claimed for this delegator
// confirm delegators claimed tokens was accurate
endingPeriod := s.App.AppKeepers.DistrKeeper.IncrementValidatorPeriod(s.Ctx, val)
rewards := s.App.AppKeepers.DistrKeeper.CalculateDelegationRewards(s.Ctx, val, del, endingPeriod)
fmt.Println("rewards:", rewards)
s.Ctx = s.Ctx.WithBlockHeight(dummyUpgradeHeight + 10)
s.StakingHelper.Delegate(del.GetDelegatorAddr(), del.GetValidatorAddr(), math.NewInt(1000000))
s.Ctx = s.Ctx.WithBlockHeight(dummyUpgradeHeight + 10)
withdraw, err := s.App.AppKeepers.DistrKeeper.WithdrawDelegationRewards(s.Ctx, del.GetDelegatorAddr(), del.GetValidatorAddr())
s.Require().NoError(err)
fmt.Println("withdraw:", withdraw)
}
}

},
},
}

// expect difference due to unregistered slashing event from validator
}
for _, tc := range testCases {
s.Run(fmt.Sprintf("Case %s", tc.name), func() {
s.SetupTest() // reset

// burnBondedTokens removes coins from the bonded pool module account
func (s *UpgradeTestSuite) burnBondedTokens(k keepers.AppKeepers, ctx sdk.Context, amt math.Int) error {
if !amt.IsPositive() {
// skip as no coins need to be burned
return nil
tc.pre_upgrade()
tc.upgrade()
tc.post_upgrade()
})
}
}

coins := sdk.NewCoins(sdk.NewCoin(k.StakingKeeper.BondDenom(ctx), amt))
return k.BankKeeper.BurnCoins(ctx, types.BondedPoolName, coins)
func assertPanic(t *testing.T, f func()) {
defer func() {
if r := recover(); r != nil {
return
}
t.Errorf("Expected panic did not occur")
t.FailNow()
}()
f()
}

0 comments on commit 0eb2b8a

Please sign in to comment.