mirror of
				https://github.com/0glabs/0g-chain.git
				synced 2025-11-04 05:17:27 +00:00 
			
		
		
		
	Add upgrade handler and upgrade e2e tests (#1739)
- Add upgrade handler for mainnet, testnet, and e2e test - Set validator minimum commission to 5% - Initialize `x/community` parameters - Add `banktypes.MsgSend` authz grant for `x/kavadist` for gov proposals - Set `x/gov` Quorum param to 20% - Set `x/incentive` earn rewards param for bkava to 600K KAVA per year
This commit is contained in:
		
							parent
							
								
									67a66bebd0
								
							
						
					
					
						commit
						802f1c8112
					
				
							
								
								
									
										264
									
								
								app/upgrades.go
									
									
									
									
									
								
							
							
						
						
									
										264
									
								
								app/upgrades.go
									
									
									
									
									
								
							@ -1,3 +1,265 @@
 | 
			
		||||
package app
 | 
			
		||||
 | 
			
		||||
func (app App) RegisterUpgradeHandlers() {}
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	sdkmath "cosmossdk.io/math"
 | 
			
		||||
	storetypes "github.com/cosmos/cosmos-sdk/store/types"
 | 
			
		||||
	sdk "github.com/cosmos/cosmos-sdk/types"
 | 
			
		||||
	"github.com/cosmos/cosmos-sdk/types/module"
 | 
			
		||||
	"github.com/cosmos/cosmos-sdk/x/authz"
 | 
			
		||||
	banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
 | 
			
		||||
	govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"
 | 
			
		||||
	stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
 | 
			
		||||
	upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types"
 | 
			
		||||
 | 
			
		||||
	communitytypes "github.com/kava-labs/kava/x/community/types"
 | 
			
		||||
	kavadisttypes "github.com/kava-labs/kava/x/kavadist/types"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	UpgradeName_Mainnet = "v0.25.0"
 | 
			
		||||
	UpgradeName_Testnet = "v0.25.0-alpha.0"
 | 
			
		||||
	UpgradeName_E2ETest = "v0.25.0-testing"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	// KAVA to ukava - 6 decimals
 | 
			
		||||
	kavaConversionFactor = sdk.NewInt(1000_000)
 | 
			
		||||
	secondsPerYear       = sdk.NewInt(365 * 24 * 60 * 60)
 | 
			
		||||
 | 
			
		||||
	// 10 Million KAVA per year in staking rewards, inflation disable time 2024-01-01T00:00:00 UTC
 | 
			
		||||
	CommunityParams_Mainnet = communitytypes.NewParams(
 | 
			
		||||
		time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC),
 | 
			
		||||
		// before switchover
 | 
			
		||||
		sdkmath.LegacyZeroDec(),
 | 
			
		||||
		// after switchover - 10M KAVA to ukava per year / seconds per year
 | 
			
		||||
		sdkmath.LegacyNewDec(10_000_000).
 | 
			
		||||
			MulInt(kavaConversionFactor).
 | 
			
		||||
			QuoInt(secondsPerYear),
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	// Testnet -- 15 Trillion KAVA per year in staking rewards, inflation disable time 2023-11-16T00:00:00 UTC
 | 
			
		||||
	CommunityParams_Testnet = communitytypes.NewParams(
 | 
			
		||||
		time.Date(2023, 11, 16, 0, 0, 0, 0, time.UTC),
 | 
			
		||||
		// before switchover
 | 
			
		||||
		sdkmath.LegacyZeroDec(),
 | 
			
		||||
		// after switchover
 | 
			
		||||
		sdkmath.LegacyNewDec(15_000_000).
 | 
			
		||||
			MulInt64(1_000_000). // 15M * 1M = 15T
 | 
			
		||||
			MulInt(kavaConversionFactor).
 | 
			
		||||
			QuoInt(secondsPerYear),
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	CommunityParams_E2E = communitytypes.NewParams(
 | 
			
		||||
		time.Now().Add(10*time.Second).UTC(), // relative time for testing
 | 
			
		||||
		sdkmath.LegacyNewDec(0),              // stakingRewardsPerSecond
 | 
			
		||||
		sdkmath.LegacyNewDec(1000),           // upgradeTimeSetstakingRewardsPerSecond
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	// ValidatorMinimumCommission is the new 5% minimum commission rate for validators
 | 
			
		||||
	ValidatorMinimumCommission = sdk.NewDecWithPrec(5, 2)
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// RegisterUpgradeHandlers registers the upgrade handlers for the app.
 | 
			
		||||
func (app App) RegisterUpgradeHandlers() {
 | 
			
		||||
	app.upgradeKeeper.SetUpgradeHandler(
 | 
			
		||||
		UpgradeName_Mainnet,
 | 
			
		||||
		upgradeHandler(app, UpgradeName_Mainnet, CommunityParams_Mainnet),
 | 
			
		||||
	)
 | 
			
		||||
	app.upgradeKeeper.SetUpgradeHandler(
 | 
			
		||||
		UpgradeName_Testnet,
 | 
			
		||||
		upgradeHandler(app, UpgradeName_Testnet, CommunityParams_Testnet),
 | 
			
		||||
	)
 | 
			
		||||
	app.upgradeKeeper.SetUpgradeHandler(
 | 
			
		||||
		UpgradeName_E2ETest,
 | 
			
		||||
		upgradeHandler(app, UpgradeName_Testnet, CommunityParams_E2E),
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	upgradeInfo, err := app.upgradeKeeper.ReadUpgradeInfoFromDisk()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		panic(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	doUpgrade := upgradeInfo.Name == UpgradeName_Mainnet ||
 | 
			
		||||
		upgradeInfo.Name == UpgradeName_Testnet ||
 | 
			
		||||
		upgradeInfo.Name == UpgradeName_E2ETest
 | 
			
		||||
 | 
			
		||||
	if doUpgrade && !app.upgradeKeeper.IsSkipHeight(upgradeInfo.Height) {
 | 
			
		||||
		storeUpgrades := storetypes.StoreUpgrades{
 | 
			
		||||
			Added: []string{
 | 
			
		||||
				// x/community added store
 | 
			
		||||
				communitytypes.ModuleName,
 | 
			
		||||
			},
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// configure store loader that checks if version == upgradeHeight and applies store upgrades
 | 
			
		||||
		app.SetStoreLoader(upgradetypes.UpgradeStoreLoader(upgradeInfo.Height, &storeUpgrades))
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// upgradeHandler returns an UpgradeHandler for the given upgrade parameters.
 | 
			
		||||
func upgradeHandler(
 | 
			
		||||
	app App,
 | 
			
		||||
	name string,
 | 
			
		||||
	communityParams communitytypes.Params,
 | 
			
		||||
) upgradetypes.UpgradeHandler {
 | 
			
		||||
	return func(
 | 
			
		||||
		ctx sdk.Context,
 | 
			
		||||
		plan upgradetypes.Plan,
 | 
			
		||||
		fromVM module.VersionMap,
 | 
			
		||||
	) (module.VersionMap, error) {
 | 
			
		||||
		app.Logger().Info(fmt.Sprintf("running %s upgrade handler", name))
 | 
			
		||||
 | 
			
		||||
		toVM, err := app.mm.RunMigrations(ctx, app.configurator, fromVM)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return toVM, err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		//
 | 
			
		||||
		// Staking validator minimum commission
 | 
			
		||||
		//
 | 
			
		||||
		UpdateValidatorMinimumCommission(ctx, app)
 | 
			
		||||
 | 
			
		||||
		//
 | 
			
		||||
		// Community Params
 | 
			
		||||
		//
 | 
			
		||||
		app.communityKeeper.SetParams(ctx, communityParams)
 | 
			
		||||
		app.Logger().Info(
 | 
			
		||||
			"initialized x/community params",
 | 
			
		||||
			"UpgradeTimeDisableInflation", communityParams.UpgradeTimeDisableInflation,
 | 
			
		||||
			"StakingRewardsPerSecond", communityParams.StakingRewardsPerSecond,
 | 
			
		||||
			"UpgradeTimeSetStakingRewardsPerSecond", communityParams.UpgradeTimeSetStakingRewardsPerSecond,
 | 
			
		||||
		)
 | 
			
		||||
 | 
			
		||||
		//
 | 
			
		||||
		// Kavadist gov grant
 | 
			
		||||
		//
 | 
			
		||||
		msgGrant, err := authz.NewMsgGrant(
 | 
			
		||||
			app.accountKeeper.GetModuleAddress(kavadisttypes.ModuleName),        // granter
 | 
			
		||||
			app.accountKeeper.GetModuleAddress(govtypes.ModuleName),             // grantee
 | 
			
		||||
			authz.NewGenericAuthorization(sdk.MsgTypeURL(&banktypes.MsgSend{})), // authorization
 | 
			
		||||
			nil, // expiration
 | 
			
		||||
		)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return toVM, err
 | 
			
		||||
		}
 | 
			
		||||
		_, err = app.authzKeeper.Grant(ctx, msgGrant)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return toVM, err
 | 
			
		||||
		}
 | 
			
		||||
		app.Logger().Info("created gov grant for kavadist funds")
 | 
			
		||||
 | 
			
		||||
		//
 | 
			
		||||
		// Gov Quorum
 | 
			
		||||
		//
 | 
			
		||||
		govTallyParams := app.govKeeper.GetTallyParams(ctx)
 | 
			
		||||
		oldQuorum := govTallyParams.Quorum
 | 
			
		||||
		govTallyParams.Quorum = sdkmath.LegacyMustNewDecFromStr("0.2").String()
 | 
			
		||||
		app.govKeeper.SetTallyParams(ctx, govTallyParams)
 | 
			
		||||
		app.Logger().Info(fmt.Sprintf("updated tally quorum from %s to %s", oldQuorum, govTallyParams.Quorum))
 | 
			
		||||
 | 
			
		||||
		//
 | 
			
		||||
		// Incentive Params
 | 
			
		||||
		//
 | 
			
		||||
		UpdateIncentiveParams(ctx, app)
 | 
			
		||||
 | 
			
		||||
		return toVM, nil
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UpdateValidatorMinimumCommission updates the commission rate for all
 | 
			
		||||
// validators to be at least the new min commission rate, and sets the minimum
 | 
			
		||||
// commission rate in the staking params.
 | 
			
		||||
func UpdateValidatorMinimumCommission(
 | 
			
		||||
	ctx sdk.Context,
 | 
			
		||||
	app App,
 | 
			
		||||
) {
 | 
			
		||||
	resultCount := make(map[stakingtypes.BondStatus]int)
 | 
			
		||||
 | 
			
		||||
	// Iterate over *all* validators including inactive
 | 
			
		||||
	app.stakingKeeper.IterateValidators(
 | 
			
		||||
		ctx,
 | 
			
		||||
		func(index int64, validator stakingtypes.ValidatorI) (stop bool) {
 | 
			
		||||
			// Skip if validator commission is already >= 5%
 | 
			
		||||
			if validator.GetCommission().GTE(ValidatorMinimumCommission) {
 | 
			
		||||
				return false
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			val, ok := validator.(stakingtypes.Validator)
 | 
			
		||||
			if !ok {
 | 
			
		||||
				panic("expected stakingtypes.Validator")
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// Set minimum commission rate to 5%, when commission is < 5%
 | 
			
		||||
			val.Commission.Rate = ValidatorMinimumCommission
 | 
			
		||||
			val.Commission.UpdateTime = ctx.BlockTime()
 | 
			
		||||
 | 
			
		||||
			// Update MaxRate if necessary
 | 
			
		||||
			if val.Commission.MaxRate.LT(ValidatorMinimumCommission) {
 | 
			
		||||
				val.Commission.MaxRate = ValidatorMinimumCommission
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if err := app.stakingKeeper.BeforeValidatorModified(ctx, val.GetOperator()); err != nil {
 | 
			
		||||
				panic(fmt.Sprintf("failed to call BeforeValidatorModified: %s", err))
 | 
			
		||||
			}
 | 
			
		||||
			app.stakingKeeper.SetValidator(ctx, val)
 | 
			
		||||
 | 
			
		||||
			// Keep track of counts just for logging purposes
 | 
			
		||||
			switch val.GetStatus() {
 | 
			
		||||
			case stakingtypes.Bonded:
 | 
			
		||||
				resultCount[stakingtypes.Bonded]++
 | 
			
		||||
			case stakingtypes.Unbonded:
 | 
			
		||||
				resultCount[stakingtypes.Unbonded]++
 | 
			
		||||
			case stakingtypes.Unbonding:
 | 
			
		||||
				resultCount[stakingtypes.Unbonding]++
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return false
 | 
			
		||||
		},
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	app.Logger().Info(
 | 
			
		||||
		"updated validator minimum commission rate for all existing validators",
 | 
			
		||||
		stakingtypes.BondStatusBonded, resultCount[stakingtypes.Bonded],
 | 
			
		||||
		stakingtypes.BondStatusUnbonded, resultCount[stakingtypes.Unbonded],
 | 
			
		||||
		stakingtypes.BondStatusUnbonding, resultCount[stakingtypes.Unbonding],
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	stakingParams := app.stakingKeeper.GetParams(ctx)
 | 
			
		||||
	stakingParams.MinCommissionRate = ValidatorMinimumCommission
 | 
			
		||||
	app.stakingKeeper.SetParams(ctx, stakingParams)
 | 
			
		||||
 | 
			
		||||
	app.Logger().Info(
 | 
			
		||||
		"updated x/staking params minimum commission rate",
 | 
			
		||||
		"MinCommissionRate", stakingParams.MinCommissionRate,
 | 
			
		||||
	)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UpdateIncentiveParams modifies the earn rewards period for bkava to be 600K KAVA per year.
 | 
			
		||||
func UpdateIncentiveParams(
 | 
			
		||||
	ctx sdk.Context,
 | 
			
		||||
	app App,
 | 
			
		||||
) {
 | 
			
		||||
	incentiveParams := app.incentiveKeeper.GetParams(ctx)
 | 
			
		||||
 | 
			
		||||
	// bkava annualized rewards: 600K KAVA
 | 
			
		||||
	newAmount := sdkmath.LegacyNewDec(600_000).
 | 
			
		||||
		MulInt(kavaConversionFactor).
 | 
			
		||||
		QuoInt(secondsPerYear).
 | 
			
		||||
		TruncateInt()
 | 
			
		||||
 | 
			
		||||
	for i := range incentiveParams.EarnRewardPeriods {
 | 
			
		||||
		if incentiveParams.EarnRewardPeriods[i].CollateralType != "bkava" {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Update rewards per second via index
 | 
			
		||||
		incentiveParams.EarnRewardPeriods[i].RewardsPerSecond = sdk.NewCoins(
 | 
			
		||||
			sdk.NewCoin("ukava", newAmount),
 | 
			
		||||
		)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	app.incentiveKeeper.SetParams(ctx, incentiveParams)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										241
									
								
								app/upgrades_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										241
									
								
								app/upgrades_test.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,241 @@
 | 
			
		||||
package app_test
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"testing"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	sdkmath "cosmossdk.io/math"
 | 
			
		||||
	sdk "github.com/cosmos/cosmos-sdk/types"
 | 
			
		||||
	stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
 | 
			
		||||
	"github.com/evmos/ethermint/crypto/ethsecp256k1"
 | 
			
		||||
	"github.com/kava-labs/kava/app"
 | 
			
		||||
	incentivetypes "github.com/kava-labs/kava/x/incentive/types"
 | 
			
		||||
	"github.com/stretchr/testify/require"
 | 
			
		||||
	tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
 | 
			
		||||
	tmtime "github.com/tendermint/tendermint/types/time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestUpgradeCommunityParams_Mainnet(t *testing.T) {
 | 
			
		||||
	require.Equal(
 | 
			
		||||
		t,
 | 
			
		||||
		sdkmath.LegacyZeroDec().String(),
 | 
			
		||||
		app.CommunityParams_Mainnet.StakingRewardsPerSecond.String(),
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	require.Equal(
 | 
			
		||||
		t,
 | 
			
		||||
		// Manually confirmed
 | 
			
		||||
		"317097.919837645865043125",
 | 
			
		||||
		app.CommunityParams_Mainnet.UpgradeTimeSetStakingRewardsPerSecond.String(),
 | 
			
		||||
		"mainnet kava per second should be correct",
 | 
			
		||||
	)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestUpgradeCommunityParams_Testnet(t *testing.T) {
 | 
			
		||||
	require.Equal(
 | 
			
		||||
		t,
 | 
			
		||||
		sdkmath.LegacyZeroDec().String(),
 | 
			
		||||
		app.CommunityParams_Testnet.StakingRewardsPerSecond.String(),
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	require.Equal(
 | 
			
		||||
		t,
 | 
			
		||||
		// Manually confirmed
 | 
			
		||||
		"475646879756.468797564687975646",
 | 
			
		||||
		app.CommunityParams_Testnet.UpgradeTimeSetStakingRewardsPerSecond.String(),
 | 
			
		||||
		"testnet kava per second should be correct",
 | 
			
		||||
	)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestUpdateValidatorMinimumCommission(t *testing.T) {
 | 
			
		||||
	tApp := app.NewTestApp()
 | 
			
		||||
	tApp.InitializeFromGenesisStates()
 | 
			
		||||
	ctx := tApp.NewContext(true, tmproto.Header{Height: 1, Time: tmtime.Now()})
 | 
			
		||||
 | 
			
		||||
	sk := tApp.GetStakingKeeper()
 | 
			
		||||
	stakingParams := sk.GetParams(ctx)
 | 
			
		||||
	stakingParams.MinCommissionRate = sdk.ZeroDec()
 | 
			
		||||
	sk.SetParams(ctx, stakingParams)
 | 
			
		||||
 | 
			
		||||
	// Set some validators with varying commission rates
 | 
			
		||||
 | 
			
		||||
	vals := []struct {
 | 
			
		||||
		name              string
 | 
			
		||||
		operatorAddr      sdk.ValAddress
 | 
			
		||||
		consPriv          *ethsecp256k1.PrivKey
 | 
			
		||||
		commissionRateMin sdk.Dec
 | 
			
		||||
		commissionRateMax sdk.Dec
 | 
			
		||||
		shouldBeUpdated   bool
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			name:              "zero commission rate",
 | 
			
		||||
			operatorAddr:      sdk.ValAddress("val0"),
 | 
			
		||||
			consPriv:          generateConsKey(t),
 | 
			
		||||
			commissionRateMin: sdk.ZeroDec(),
 | 
			
		||||
			commissionRateMax: sdk.ZeroDec(),
 | 
			
		||||
			shouldBeUpdated:   true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:              "0.01 commission rate",
 | 
			
		||||
			operatorAddr:      sdk.ValAddress("val1"),
 | 
			
		||||
			consPriv:          generateConsKey(t),
 | 
			
		||||
			commissionRateMin: sdk.MustNewDecFromStr("0.01"),
 | 
			
		||||
			commissionRateMax: sdk.MustNewDecFromStr("0.01"),
 | 
			
		||||
			shouldBeUpdated:   true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:              "0.05 commission rate",
 | 
			
		||||
			operatorAddr:      sdk.ValAddress("val2"),
 | 
			
		||||
			consPriv:          generateConsKey(t),
 | 
			
		||||
			commissionRateMin: sdk.MustNewDecFromStr("0.05"),
 | 
			
		||||
			commissionRateMax: sdk.MustNewDecFromStr("0.05"),
 | 
			
		||||
			shouldBeUpdated:   false,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:              "0.06 commission rate",
 | 
			
		||||
			operatorAddr:      sdk.ValAddress("val3"),
 | 
			
		||||
			consPriv:          generateConsKey(t),
 | 
			
		||||
			commissionRateMin: sdk.MustNewDecFromStr("0.06"),
 | 
			
		||||
			commissionRateMax: sdk.MustNewDecFromStr("0.06"),
 | 
			
		||||
			shouldBeUpdated:   false,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:              "0.5 commission rate",
 | 
			
		||||
			operatorAddr:      sdk.ValAddress("val4"),
 | 
			
		||||
			consPriv:          generateConsKey(t),
 | 
			
		||||
			commissionRateMin: sdk.MustNewDecFromStr("0.5"),
 | 
			
		||||
			commissionRateMax: sdk.MustNewDecFromStr("0.5"),
 | 
			
		||||
			shouldBeUpdated:   false,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, v := range vals {
 | 
			
		||||
		val, err := stakingtypes.NewValidator(
 | 
			
		||||
			v.operatorAddr,
 | 
			
		||||
			v.consPriv.PubKey(),
 | 
			
		||||
			stakingtypes.Description{},
 | 
			
		||||
		)
 | 
			
		||||
		require.NoError(t, err)
 | 
			
		||||
		val.Commission.Rate = v.commissionRateMin
 | 
			
		||||
		val.Commission.MaxRate = v.commissionRateMax
 | 
			
		||||
 | 
			
		||||
		err = sk.SetValidatorByConsAddr(ctx, val)
 | 
			
		||||
		require.NoError(t, err)
 | 
			
		||||
		sk.SetValidator(ctx, val)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	require.NotPanics(
 | 
			
		||||
		t, func() {
 | 
			
		||||
			app.UpdateValidatorMinimumCommission(ctx, tApp.App)
 | 
			
		||||
		},
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	stakingParamsAfter := sk.GetParams(ctx)
 | 
			
		||||
	require.Equal(t, stakingParamsAfter.MinCommissionRate, app.ValidatorMinimumCommission)
 | 
			
		||||
 | 
			
		||||
	// Check that all validators have a commission rate >= 5%
 | 
			
		||||
	for _, val := range vals {
 | 
			
		||||
		t.Run(val.name, func(t *testing.T) {
 | 
			
		||||
			validator, found := sk.GetValidator(ctx, val.operatorAddr)
 | 
			
		||||
			require.True(t, found, "validator should be found")
 | 
			
		||||
 | 
			
		||||
			require.True(
 | 
			
		||||
				t,
 | 
			
		||||
				validator.GetCommission().GTE(app.ValidatorMinimumCommission),
 | 
			
		||||
				"commission rate should be >= 5%",
 | 
			
		||||
			)
 | 
			
		||||
 | 
			
		||||
			require.True(
 | 
			
		||||
				t,
 | 
			
		||||
				validator.Commission.MaxRate.GTE(app.ValidatorMinimumCommission),
 | 
			
		||||
				"commission rate max should be >= 5%, got %s",
 | 
			
		||||
				validator.Commission.MaxRate,
 | 
			
		||||
			)
 | 
			
		||||
 | 
			
		||||
			if val.shouldBeUpdated {
 | 
			
		||||
				require.Equal(
 | 
			
		||||
					t,
 | 
			
		||||
					ctx.BlockTime(),
 | 
			
		||||
					validator.Commission.UpdateTime,
 | 
			
		||||
					"commission update time should be set to block time",
 | 
			
		||||
				)
 | 
			
		||||
			} else {
 | 
			
		||||
				require.Equal(
 | 
			
		||||
					t,
 | 
			
		||||
					time.Unix(0, 0).UTC(),
 | 
			
		||||
					validator.Commission.UpdateTime,
 | 
			
		||||
					"commission update time should not be changed -- default value is 0",
 | 
			
		||||
				)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestUpdateIncentiveParams(t *testing.T) {
 | 
			
		||||
	tApp := app.NewTestApp()
 | 
			
		||||
	tApp.InitializeFromGenesisStates()
 | 
			
		||||
	ctx := tApp.NewContext(true, tmproto.Header{Height: 1, Time: tmtime.Now()})
 | 
			
		||||
 | 
			
		||||
	ik := tApp.GetIncentiveKeeper()
 | 
			
		||||
	params := ik.GetParams(ctx)
 | 
			
		||||
 | 
			
		||||
	startPeriod := time.Date(2021, 10, 26, 15, 0, 0, 0, time.UTC)
 | 
			
		||||
	endPeriod := time.Date(2022, 10, 26, 15, 0, 0, 0, time.UTC)
 | 
			
		||||
 | 
			
		||||
	params.EarnRewardPeriods = incentivetypes.MultiRewardPeriods{
 | 
			
		||||
		incentivetypes.NewMultiRewardPeriod(
 | 
			
		||||
			true,
 | 
			
		||||
			"bkava",
 | 
			
		||||
			startPeriod,
 | 
			
		||||
			endPeriod,
 | 
			
		||||
			sdk.NewCoins(
 | 
			
		||||
				sdk.NewCoin("ukava", sdk.NewInt(159459)),
 | 
			
		||||
			),
 | 
			
		||||
		),
 | 
			
		||||
	}
 | 
			
		||||
	ik.SetParams(ctx, params)
 | 
			
		||||
 | 
			
		||||
	beforeParams := ik.GetParams(ctx)
 | 
			
		||||
	require.Equal(t, params, beforeParams, "initial incentive params should be set")
 | 
			
		||||
 | 
			
		||||
	// -- UPGRADE
 | 
			
		||||
	app.UpdateIncentiveParams(ctx, tApp.App)
 | 
			
		||||
 | 
			
		||||
	// -- After
 | 
			
		||||
	afterParams := ik.GetParams(ctx)
 | 
			
		||||
 | 
			
		||||
	require.Len(
 | 
			
		||||
		t,
 | 
			
		||||
		afterParams.EarnRewardPeriods[0].RewardsPerSecond,
 | 
			
		||||
		1,
 | 
			
		||||
		"bkava earn reward period should only contain 1 coin",
 | 
			
		||||
	)
 | 
			
		||||
	require.Equal(
 | 
			
		||||
		t,
 | 
			
		||||
		// Manual calculation of
 | 
			
		||||
		// 600,000 * 1000,000 / (365 * 24 * 60 * 60)
 | 
			
		||||
		sdk.NewCoin("ukava", sdkmath.NewInt(19025)),
 | 
			
		||||
		afterParams.EarnRewardPeriods[0].RewardsPerSecond[0],
 | 
			
		||||
		"bkava earn reward period should be updated",
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	// Check that other params are not changed
 | 
			
		||||
	afterParams.EarnRewardPeriods[0].RewardsPerSecond[0] = beforeParams.EarnRewardPeriods[0].RewardsPerSecond[0]
 | 
			
		||||
	require.Equal(
 | 
			
		||||
		t,
 | 
			
		||||
		beforeParams,
 | 
			
		||||
		afterParams,
 | 
			
		||||
		"other param values should not be changed",
 | 
			
		||||
	)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func generateConsKey(
 | 
			
		||||
	t *testing.T,
 | 
			
		||||
) *ethsecp256k1.PrivKey {
 | 
			
		||||
	t.Helper()
 | 
			
		||||
 | 
			
		||||
	key, err := ethsecp256k1.GenerateKey()
 | 
			
		||||
	require.NoError(t, err)
 | 
			
		||||
 | 
			
		||||
	return key
 | 
			
		||||
}
 | 
			
		||||
@ -19,14 +19,14 @@ E2E_SKIP_SHUTDOWN=false
 | 
			
		||||
 | 
			
		||||
# The following variables should be defined to run an upgrade.
 | 
			
		||||
# E2E_INCLUDE_AUTOMATED_UPGRADE when true enables the automated upgrade & corresponding tests in the suite.
 | 
			
		||||
E2E_INCLUDE_AUTOMATED_UPGRADE=false
 | 
			
		||||
E2E_INCLUDE_AUTOMATED_UPGRADE=true
 | 
			
		||||
# E2E_KAVA_UPGRADE_NAME is the name of the upgrade that must be in the current local image.
 | 
			
		||||
E2E_KAVA_UPGRADE_NAME=
 | 
			
		||||
E2E_KAVA_UPGRADE_NAME=v0.25.0-testing
 | 
			
		||||
# E2E_KAVA_UPGRADE_HEIGHT is the height at which the upgrade will be applied.
 | 
			
		||||
# If IBC tests are enabled this should be >30. Otherwise, this should be >10.
 | 
			
		||||
E2E_KAVA_UPGRADE_HEIGHT=
 | 
			
		||||
E2E_KAVA_UPGRADE_HEIGHT=35
 | 
			
		||||
# E2E_KAVA_UPGRADE_BASE_IMAGE_TAG is the tag of the docker image the chain should upgrade from.
 | 
			
		||||
E2E_KAVA_UPGRADE_BASE_IMAGE_TAG=
 | 
			
		||||
E2E_KAVA_UPGRADE_BASE_IMAGE_TAG=v0.24.0
 | 
			
		||||
 | 
			
		||||
# E2E_KAVA_ERC20_ADDRESS is the address of a pre-deployed ERC20 token with the following properties:
 | 
			
		||||
# - the E2E_KAVA_FUNDED_ACCOUNT_MNEMONIC has nonzero balance
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										256
									
								
								tests/e2e/e2e_upgrade_community_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										256
									
								
								tests/e2e/e2e_upgrade_community_test.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,256 @@
 | 
			
		||||
package e2e_test
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	sdk "github.com/cosmos/cosmos-sdk/types"
 | 
			
		||||
	distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types"
 | 
			
		||||
	minttypes "github.com/cosmos/cosmos-sdk/x/mint/types"
 | 
			
		||||
 | 
			
		||||
	"github.com/kava-labs/kava/app"
 | 
			
		||||
	"github.com/kava-labs/kava/tests/util"
 | 
			
		||||
	communitytypes "github.com/kava-labs/kava/x/community/types"
 | 
			
		||||
	kavadisttypes "github.com/kava-labs/kava/x/kavadist/types"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func (suite *IntegrationTestSuite) TestUpgradeCommunityParams() {
 | 
			
		||||
	suite.SkipIfUpgradeDisabled()
 | 
			
		||||
 | 
			
		||||
	beforeUpgradeCtx := util.CtxAtHeight(suite.UpgradeHeight - 1)
 | 
			
		||||
	afterUpgradeCtx := util.CtxAtHeight(suite.UpgradeHeight)
 | 
			
		||||
 | 
			
		||||
	// Before params
 | 
			
		||||
	kavaDistParamsBefore, err := suite.Kava.Kavadist.Params(beforeUpgradeCtx, &kavadisttypes.QueryParamsRequest{})
 | 
			
		||||
	suite.NoError(err)
 | 
			
		||||
	mintParamsBefore, err := suite.Kava.Mint.Params(beforeUpgradeCtx, &minttypes.QueryParamsRequest{})
 | 
			
		||||
	suite.NoError(err)
 | 
			
		||||
 | 
			
		||||
	// Before parameters
 | 
			
		||||
	suite.Run("x/community and x/kavadist parameters before upgrade", func() {
 | 
			
		||||
		_, err = suite.Kava.Community.Params(beforeUpgradeCtx, &communitytypes.QueryParamsRequest{})
 | 
			
		||||
		suite.Error(err, "x/community should not have params before upgrade")
 | 
			
		||||
 | 
			
		||||
		suite.Require().True(
 | 
			
		||||
			kavaDistParamsBefore.Params.Active,
 | 
			
		||||
			"x/kavadist should be active before upgrade",
 | 
			
		||||
		)
 | 
			
		||||
 | 
			
		||||
		suite.Require().True(
 | 
			
		||||
			mintParamsBefore.Params.InflationMax.IsPositive(),
 | 
			
		||||
			"x/mint inflation max should be positive before upgrade",
 | 
			
		||||
		)
 | 
			
		||||
		suite.Require().True(
 | 
			
		||||
			mintParamsBefore.Params.InflationMin.IsPositive(),
 | 
			
		||||
			"x/mint inflation min should be positive before upgrade",
 | 
			
		||||
		)
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	// After upgrade, Before switchover - parameters
 | 
			
		||||
	suite.Run("x/kavadist, x/mint, x/community parameters after upgrade, before switchover", func() {
 | 
			
		||||
		kavaDistParamsAfter, err := suite.Kava.Kavadist.Params(afterUpgradeCtx, &kavadisttypes.QueryParamsRequest{})
 | 
			
		||||
		suite.NoError(err)
 | 
			
		||||
		mintParamsAfter, err := suite.Kava.Mint.Params(afterUpgradeCtx, &minttypes.QueryParamsRequest{})
 | 
			
		||||
		suite.NoError(err)
 | 
			
		||||
		communityParamsAfter, err := suite.Kava.Community.Params(afterUpgradeCtx, &communitytypes.QueryParamsRequest{})
 | 
			
		||||
		suite.NoError(err)
 | 
			
		||||
 | 
			
		||||
		suite.Equal(
 | 
			
		||||
			kavaDistParamsBefore.Params,
 | 
			
		||||
			kavaDistParamsAfter.Params,
 | 
			
		||||
			"x/kavadist should be unaffected after upgrade",
 | 
			
		||||
		)
 | 
			
		||||
 | 
			
		||||
		suite.Equal(
 | 
			
		||||
			mintParamsBefore.Params,
 | 
			
		||||
			mintParamsAfter.Params,
 | 
			
		||||
			"x/mint params should be unaffected after upgrade",
 | 
			
		||||
		)
 | 
			
		||||
 | 
			
		||||
		expectedParams := app.CommunityParams_E2E
 | 
			
		||||
		// Make UpgradeTimeDisableInflation match so that we ignore it, because
 | 
			
		||||
		// referencing app.CommunityParams_E2E in this test files is different
 | 
			
		||||
		// from the one set in the upgrade handler. At least check that it is
 | 
			
		||||
		// set to a non-zero value in the assertion below
 | 
			
		||||
		expectedParams.UpgradeTimeDisableInflation = communityParamsAfter.Params.UpgradeTimeDisableInflation
 | 
			
		||||
 | 
			
		||||
		suite.False(
 | 
			
		||||
			communityParamsAfter.Params.UpgradeTimeDisableInflation.IsZero(),
 | 
			
		||||
			"x/community switchover time should be set after upgrade",
 | 
			
		||||
		)
 | 
			
		||||
		suite.Equal(
 | 
			
		||||
			expectedParams,
 | 
			
		||||
			communityParamsAfter.Params,
 | 
			
		||||
			"x/community params should be set to E2E params after upgrade",
 | 
			
		||||
		)
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	suite.Require().Eventually(
 | 
			
		||||
		func() bool {
 | 
			
		||||
			// Get x/community for switchover time
 | 
			
		||||
			params, err := suite.Kava.Community.Params(
 | 
			
		||||
				context.Background(),
 | 
			
		||||
				&communitytypes.QueryParamsRequest{},
 | 
			
		||||
			)
 | 
			
		||||
			suite.Require().NoError(err)
 | 
			
		||||
 | 
			
		||||
			// Check that switchover time is set to zero, e.g. switchover happened
 | 
			
		||||
			return params.Params.UpgradeTimeDisableInflation.Equal(time.Time{})
 | 
			
		||||
		},
 | 
			
		||||
		20*time.Second, 1*time.Second,
 | 
			
		||||
		"switchover should happen and x/community params should be updated",
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	// Fetch exact block when inflation stop event emitted
 | 
			
		||||
	_, switchoverHeight, err := suite.Kava.GetBeginBlockEventsFromQuery(
 | 
			
		||||
		context.Background(),
 | 
			
		||||
		fmt.Sprintf(
 | 
			
		||||
			"%s.%s EXISTS",
 | 
			
		||||
			communitytypes.EventTypeInflationStop,
 | 
			
		||||
			communitytypes.AttributeKeyInflationDisableTime,
 | 
			
		||||
		),
 | 
			
		||||
	)
 | 
			
		||||
	suite.Require().NoError(err)
 | 
			
		||||
	suite.Require().NotZero(switchoverHeight)
 | 
			
		||||
 | 
			
		||||
	beforeSwitchoverCtx := util.CtxAtHeight(switchoverHeight - 1)
 | 
			
		||||
	afterSwitchoverCtx := util.CtxAtHeight(switchoverHeight)
 | 
			
		||||
 | 
			
		||||
	suite.Run("x/kavadist, x/mint, x/community parameters after upgrade, after switchover", func() {
 | 
			
		||||
		kavaDistParamsAfter, err := suite.Kava.Kavadist.Params(
 | 
			
		||||
			afterSwitchoverCtx,
 | 
			
		||||
			&kavadisttypes.QueryParamsRequest{},
 | 
			
		||||
		)
 | 
			
		||||
		suite.NoError(err)
 | 
			
		||||
		mintParamsAfter, err := suite.Kava.Mint.Params(
 | 
			
		||||
			afterSwitchoverCtx,
 | 
			
		||||
			&minttypes.QueryParamsRequest{},
 | 
			
		||||
		)
 | 
			
		||||
		suite.NoError(err)
 | 
			
		||||
		communityParamsAfter, err := suite.Kava.Community.Params(
 | 
			
		||||
			afterSwitchoverCtx,
 | 
			
		||||
			&communitytypes.QueryParamsRequest{},
 | 
			
		||||
		)
 | 
			
		||||
		suite.NoError(err)
 | 
			
		||||
 | 
			
		||||
		suite.False(
 | 
			
		||||
			kavaDistParamsAfter.Params.Active,
 | 
			
		||||
			"x/kavadist should be disabled after upgrade",
 | 
			
		||||
		)
 | 
			
		||||
 | 
			
		||||
		suite.True(
 | 
			
		||||
			mintParamsAfter.Params.InflationMax.IsZero(),
 | 
			
		||||
			"x/mint inflation max should be zero after switchover",
 | 
			
		||||
		)
 | 
			
		||||
		suite.True(
 | 
			
		||||
			mintParamsAfter.Params.InflationMin.IsZero(),
 | 
			
		||||
			"x/mint inflation min should be zero after switchover",
 | 
			
		||||
		)
 | 
			
		||||
 | 
			
		||||
		suite.Equal(
 | 
			
		||||
			time.Time{},
 | 
			
		||||
			communityParamsAfter.Params.UpgradeTimeDisableInflation,
 | 
			
		||||
			"x/community switchover time should be reset",
 | 
			
		||||
		)
 | 
			
		||||
 | 
			
		||||
		suite.Equal(
 | 
			
		||||
			communityParamsAfter.Params.UpgradeTimeSetStakingRewardsPerSecond,
 | 
			
		||||
			communityParamsAfter.Params.StakingRewardsPerSecond,
 | 
			
		||||
			"x/community staking rewards per second should match upgrade time staking rewards per second",
 | 
			
		||||
		)
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	suite.Run("x/kavadist, x/distribution, x/community balances after switchover", func() {
 | 
			
		||||
		// Before balances - community pool fund consolidation
 | 
			
		||||
		kavaDistBalBefore, err := suite.Kava.Kavadist.Balance(
 | 
			
		||||
			beforeSwitchoverCtx,
 | 
			
		||||
			&kavadisttypes.QueryBalanceRequest{},
 | 
			
		||||
		)
 | 
			
		||||
		suite.NoError(err)
 | 
			
		||||
		distrBalBefore, err := suite.Kava.Distribution.CommunityPool(
 | 
			
		||||
			beforeSwitchoverCtx,
 | 
			
		||||
			&distrtypes.QueryCommunityPoolRequest{},
 | 
			
		||||
		)
 | 
			
		||||
		suite.NoError(err)
 | 
			
		||||
		distrBalCoinsBefore, distrDustBefore := distrBalBefore.Pool.TruncateDecimal()
 | 
			
		||||
		beforeCommPoolBalance, err := suite.Kava.Community.Balance(
 | 
			
		||||
			beforeSwitchoverCtx,
 | 
			
		||||
			&communitytypes.QueryBalanceRequest{},
 | 
			
		||||
		)
 | 
			
		||||
		suite.NoError(err)
 | 
			
		||||
 | 
			
		||||
		// After balances
 | 
			
		||||
		kavaDistBalAfter, err := suite.Kava.Kavadist.Balance(
 | 
			
		||||
			afterSwitchoverCtx,
 | 
			
		||||
			&kavadisttypes.QueryBalanceRequest{},
 | 
			
		||||
		)
 | 
			
		||||
		suite.NoError(err)
 | 
			
		||||
		distrBalAfter, err := suite.Kava.Distribution.CommunityPool(
 | 
			
		||||
			afterSwitchoverCtx,
 | 
			
		||||
			&distrtypes.QueryCommunityPoolRequest{},
 | 
			
		||||
		)
 | 
			
		||||
		suite.NoError(err)
 | 
			
		||||
		afterCommPoolBalance, err := suite.Kava.Community.Balance(
 | 
			
		||||
			afterSwitchoverCtx,
 | 
			
		||||
			&communitytypes.QueryBalanceRequest{},
 | 
			
		||||
		)
 | 
			
		||||
		suite.NoError(err)
 | 
			
		||||
 | 
			
		||||
		expectedKavadistBal := sdk.NewCoins(sdk.NewCoin(
 | 
			
		||||
			"ukava",
 | 
			
		||||
			kavaDistBalBefore.Coins.AmountOf("ukava"),
 | 
			
		||||
		))
 | 
			
		||||
		suite.Equal(
 | 
			
		||||
			expectedKavadistBal,
 | 
			
		||||
			kavaDistBalAfter.Coins,
 | 
			
		||||
			"x/kavadist balance should persist the ukava amount and move all other funds",
 | 
			
		||||
		)
 | 
			
		||||
		expectedKavadistTransferred := kavaDistBalBefore.Coins.Sub(expectedKavadistBal...)
 | 
			
		||||
 | 
			
		||||
		// very low ukava balance after (ignoring dust in x/distribution)
 | 
			
		||||
		// a small amount of tx fees can still end up here.
 | 
			
		||||
		// dust should stay in x/distribution, but may not be the same so it's unchecked
 | 
			
		||||
		distrCoinsAfter, distrDustAfter := distrBalAfter.Pool.TruncateDecimal()
 | 
			
		||||
		suite.Empty(distrCoinsAfter, "expected no coins in x/distribution community pool")
 | 
			
		||||
 | 
			
		||||
		// Fetch block results for paid staking rewards in the block
 | 
			
		||||
		blockRes, err := suite.Kava.TmSignClient.BlockResults(
 | 
			
		||||
			context.Background(),
 | 
			
		||||
			&switchoverHeight,
 | 
			
		||||
		)
 | 
			
		||||
		suite.Require().NoError(err)
 | 
			
		||||
 | 
			
		||||
		stakingRewardPaidEvents := util.FilterEventsByType(
 | 
			
		||||
			blockRes.BeginBlockEvents,
 | 
			
		||||
			communitytypes.EventTypeStakingRewardsPaid,
 | 
			
		||||
		)
 | 
			
		||||
		suite.Require().Len(stakingRewardPaidEvents, 1, "there should be only 1 staking reward paid event")
 | 
			
		||||
		stakingRewardAmount := sdk.NewCoins()
 | 
			
		||||
		for _, attr := range stakingRewardPaidEvents[0].Attributes {
 | 
			
		||||
			if string(attr.Key) == communitytypes.AttributeKeyStakingRewardAmount {
 | 
			
		||||
				stakingRewardAmount, err = sdk.ParseCoinsNormalized(string(attr.Value))
 | 
			
		||||
				suite.Require().NoError(err)
 | 
			
		||||
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		expectedCommunityBal := beforeCommPoolBalance.Coins.
 | 
			
		||||
			Add(distrBalCoinsBefore...).
 | 
			
		||||
			Add(expectedKavadistTransferred...).
 | 
			
		||||
			Sub(stakingRewardAmount...) // Remove staking rewards paid in the block
 | 
			
		||||
 | 
			
		||||
		// x/kavadist and x/distribution community pools should be moved to x/community
 | 
			
		||||
		suite.Equal(
 | 
			
		||||
			expectedCommunityBal,
 | 
			
		||||
			afterCommPoolBalance.Coins,
 | 
			
		||||
		)
 | 
			
		||||
 | 
			
		||||
		suite.Equal(
 | 
			
		||||
			distrDustBefore,
 | 
			
		||||
			distrDustAfter,
 | 
			
		||||
			"x/distribution community pool dust should be unchanged",
 | 
			
		||||
		)
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										291
									
								
								tests/e2e/e2e_upgrade_gov_and_authz_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										291
									
								
								tests/e2e/e2e_upgrade_gov_and_authz_test.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,291 @@
 | 
			
		||||
package e2e_test
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	sdk "github.com/cosmos/cosmos-sdk/types"
 | 
			
		||||
	query "github.com/cosmos/cosmos-sdk/types/query"
 | 
			
		||||
	"github.com/cosmos/cosmos-sdk/x/authz"
 | 
			
		||||
	banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
 | 
			
		||||
	govv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1"
 | 
			
		||||
 | 
			
		||||
	"github.com/kava-labs/kava/tests/e2e/testutil"
 | 
			
		||||
	"github.com/kava-labs/kava/tests/util"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	govModuleAcc       = "kava10d07y265gmmuvt4z0w9aw880jnsr700jxh8cq5"
 | 
			
		||||
	communityModuleAcc = "kava17d2wax0zhjrrecvaszuyxdf5wcu5a0p4qlx3t5"
 | 
			
		||||
	kavadistModuleAcc  = "kava1cj7njkw2g9fqx4e768zc75dp9sks8u9znxrf0w"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func (suite *IntegrationTestSuite) TestGovParamChanges() {
 | 
			
		||||
	suite.SkipIfUpgradeDisabled()
 | 
			
		||||
 | 
			
		||||
	beforeUpgradeCtx := util.CtxAtHeight(suite.UpgradeHeight - 1)
 | 
			
		||||
	afterUpgradeCtx := util.CtxAtHeight(suite.UpgradeHeight)
 | 
			
		||||
 | 
			
		||||
	// fetch gov parameters before upgrade
 | 
			
		||||
	govBeforeParams, err := suite.Kava.Gov.Params(beforeUpgradeCtx, &govv1.QueryParamsRequest{ParamsType: "tallying"})
 | 
			
		||||
	suite.Require().NoError(err)
 | 
			
		||||
 | 
			
		||||
	// assert expected gov quorum before upgrade
 | 
			
		||||
	suite.NotEqual(govBeforeParams.TallyParams.Quorum, "0.200000000000000000")
 | 
			
		||||
 | 
			
		||||
	govAfterParams, err := suite.Kava.Gov.Params(afterUpgradeCtx, &govv1.QueryParamsRequest{ParamsType: "tallying"})
 | 
			
		||||
	suite.Require().NoError(err)
 | 
			
		||||
 | 
			
		||||
	// assert expected gov quorum after upgrade
 | 
			
		||||
	suite.Equal(govAfterParams.TallyParams.Quorum, "0.200000000000000000")
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (suite *IntegrationTestSuite) TestAuthzParamChanges() {
 | 
			
		||||
	suite.SkipIfUpgradeDisabled()
 | 
			
		||||
 | 
			
		||||
	beforeUpgradeCtx := util.CtxAtHeight(suite.UpgradeHeight - 1)
 | 
			
		||||
	afterUpgradeCtx := util.CtxAtHeight(suite.UpgradeHeight)
 | 
			
		||||
 | 
			
		||||
	// fetch authz grants before upgrade
 | 
			
		||||
	authzBeforeGrants, err := suite.Kava.Authz.Grants(beforeUpgradeCtx, &authz.QueryGrantsRequest{Granter: kavadistModuleAcc, Grantee: govModuleAcc, Pagination: &query.PageRequest{Limit: 1000, CountTotal: true}})
 | 
			
		||||
	suite.Require().NoError(err)
 | 
			
		||||
	suite.Require().Equal(authzBeforeGrants.Pagination.Total, uint64(len(authzBeforeGrants.Grants)), "expected all grants to have been requested")
 | 
			
		||||
 | 
			
		||||
	// no kavadist -> gov grants
 | 
			
		||||
	suite.Equal(0, len(authzBeforeGrants.Grants))
 | 
			
		||||
 | 
			
		||||
	// fetch authz grants after upgrade
 | 
			
		||||
	authzAfterGrants, err := suite.Kava.Authz.Grants(afterUpgradeCtx, &authz.QueryGrantsRequest{Granter: kavadistModuleAcc, Grantee: govModuleAcc, Pagination: &query.PageRequest{Limit: 1000, CountTotal: true}})
 | 
			
		||||
	suite.Require().NoError(err)
 | 
			
		||||
	suite.Require().Equal(authzAfterGrants.Pagination.Total, uint64(len(authzAfterGrants.Grants)), "expected all grants to have been requested")
 | 
			
		||||
 | 
			
		||||
	// one kavadist -> gov grants
 | 
			
		||||
	suite.Require().Equal(1, len(authzAfterGrants.Grants))
 | 
			
		||||
 | 
			
		||||
	grant := authzAfterGrants.Grants[0]
 | 
			
		||||
 | 
			
		||||
	var authorization authz.Authorization
 | 
			
		||||
	suite.Kava.EncodingConfig.Marshaler.UnpackAny(grant.Authorization, &authorization)
 | 
			
		||||
 | 
			
		||||
	genericAuthorization, ok := authorization.(*authz.GenericAuthorization)
 | 
			
		||||
	suite.Require().True(ok, "expected generic authorization")
 | 
			
		||||
 | 
			
		||||
	// kavadist allows gov to MsgSend it's funds
 | 
			
		||||
	suite.Equal(sdk.MsgTypeURL(&banktypes.MsgSend{}), genericAuthorization.Msg)
 | 
			
		||||
	// no expiration
 | 
			
		||||
	var expectedExpiration *time.Time
 | 
			
		||||
	suite.Equal(expectedExpiration, grant.Expiration)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (suite *IntegrationTestSuite) TestModuleAccountGovTransfers() {
 | 
			
		||||
	suite.SkipIfUpgradeDisabled()
 | 
			
		||||
	suite.SkipIfKvtoolDisabled()
 | 
			
		||||
 | 
			
		||||
	// the module account (authority) that executes the transfers
 | 
			
		||||
	govAcc := sdk.MustAccAddressFromBech32(govModuleAcc)
 | 
			
		||||
 | 
			
		||||
	// module accounts for gov transfer test cases
 | 
			
		||||
	communityAcc := sdk.MustAccAddressFromBech32(communityModuleAcc)
 | 
			
		||||
	kavadistAcc := sdk.MustAccAddressFromBech32(kavadistModuleAcc)
 | 
			
		||||
 | 
			
		||||
	testCases := []struct {
 | 
			
		||||
		name     string
 | 
			
		||||
		sender   sdk.AccAddress
 | 
			
		||||
		receiver sdk.AccAddress
 | 
			
		||||
		amount   sdk.Coin
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			name:     "transfer from community to kavadist for incentive rewards",
 | 
			
		||||
			sender:   communityAcc,
 | 
			
		||||
			receiver: kavadistAcc,
 | 
			
		||||
			amount:   ukava(100e6),
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:     "transfer from kavadist to community",
 | 
			
		||||
			sender:   kavadistAcc,
 | 
			
		||||
			receiver: communityAcc,
 | 
			
		||||
			amount:   ukava(50e6),
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, tc := range testCases {
 | 
			
		||||
		suite.Run(tc.name, func() {
 | 
			
		||||
			// create msg exec for transfer between modules
 | 
			
		||||
			msg := banktypes.NewMsgSend(
 | 
			
		||||
				tc.sender,
 | 
			
		||||
				tc.receiver,
 | 
			
		||||
				sdk.NewCoins(tc.amount),
 | 
			
		||||
			)
 | 
			
		||||
			execMsg := authz.NewMsgExec(govAcc, []sdk.Msg{msg})
 | 
			
		||||
 | 
			
		||||
			// ensure proposal passes
 | 
			
		||||
			passBlock := suite.submitAndPassProposal([]sdk.Msg{&execMsg})
 | 
			
		||||
			transfers := suite.getBankTransferAmountAtBlock(passBlock, tc.sender, tc.receiver)
 | 
			
		||||
 | 
			
		||||
			suite.Require().Containsf(
 | 
			
		||||
				transfers,
 | 
			
		||||
				tc.amount,
 | 
			
		||||
				"expected transfer of %s to be included in bank transfer events: %s",
 | 
			
		||||
				tc.amount,
 | 
			
		||||
				transfers,
 | 
			
		||||
			)
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (suite *IntegrationTestSuite) submitAndPassProposal(msgs []sdk.Msg) int64 {
 | 
			
		||||
	govParamsRes, err := suite.Kava.Gov.Params(context.Background(), &govv1.QueryParamsRequest{
 | 
			
		||||
		ParamsType: govv1.ParamDeposit,
 | 
			
		||||
	})
 | 
			
		||||
	suite.NoError(err)
 | 
			
		||||
 | 
			
		||||
	kavaAcc := suite.Kava.GetAccount(testutil.FundedAccountName)
 | 
			
		||||
 | 
			
		||||
	proposalMsg, err := govv1.NewMsgSubmitProposal(
 | 
			
		||||
		msgs,
 | 
			
		||||
		govParamsRes.DepositParams.MinDeposit,
 | 
			
		||||
		kavaAcc.SdkAddress.String(),
 | 
			
		||||
		"",
 | 
			
		||||
	)
 | 
			
		||||
	suite.NoError(err)
 | 
			
		||||
 | 
			
		||||
	gasLimit := 1e6
 | 
			
		||||
	fee := ukava(1000)
 | 
			
		||||
 | 
			
		||||
	req := util.KavaMsgRequest{
 | 
			
		||||
		Msgs:      []sdk.Msg{proposalMsg},
 | 
			
		||||
		GasLimit:  uint64(gasLimit),
 | 
			
		||||
		FeeAmount: sdk.NewCoins(fee),
 | 
			
		||||
		Memo:      "this is a proposal please accept me",
 | 
			
		||||
	}
 | 
			
		||||
	res := kavaAcc.SignAndBroadcastKavaTx(req)
 | 
			
		||||
	suite.Require().NoError(res.Err)
 | 
			
		||||
 | 
			
		||||
	// Wait for proposal to be submitted
 | 
			
		||||
	txRes, err := util.WaitForSdkTxCommit(suite.Kava.Tx, res.Result.TxHash, 6*time.Second)
 | 
			
		||||
	suite.Require().NoError(err)
 | 
			
		||||
 | 
			
		||||
	var govRes govv1.MsgSubmitProposalResponse
 | 
			
		||||
	suite.decodeTxMsgResponse(txRes, &govRes)
 | 
			
		||||
 | 
			
		||||
	// 2. Vote for proposal from whale account
 | 
			
		||||
	whale := suite.Kava.GetAccount(testutil.FundedAccountName)
 | 
			
		||||
	voteMsg := govv1.NewMsgVote(
 | 
			
		||||
		whale.SdkAddress,
 | 
			
		||||
		govRes.ProposalId,
 | 
			
		||||
		govv1.OptionYes,
 | 
			
		||||
		"",
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	voteReq := util.KavaMsgRequest{
 | 
			
		||||
		Msgs:      []sdk.Msg{voteMsg},
 | 
			
		||||
		GasLimit:  uint64(gasLimit),
 | 
			
		||||
		FeeAmount: sdk.NewCoins(fee),
 | 
			
		||||
		Memo:      "voting",
 | 
			
		||||
	}
 | 
			
		||||
	voteRes := whale.SignAndBroadcastKavaTx(voteReq)
 | 
			
		||||
	suite.Require().NoError(voteRes.Err)
 | 
			
		||||
 | 
			
		||||
	_, err = util.WaitForSdkTxCommit(suite.Kava.Tx, voteRes.Result.TxHash, 6*time.Second)
 | 
			
		||||
	suite.Require().NoError(err)
 | 
			
		||||
 | 
			
		||||
	// 3. Wait until proposal passes
 | 
			
		||||
	suite.Require().Eventually(func() bool {
 | 
			
		||||
		proposalRes, err := suite.Kava.Gov.Proposal(context.Background(), &govv1.QueryProposalRequest{
 | 
			
		||||
			ProposalId: govRes.ProposalId,
 | 
			
		||||
		})
 | 
			
		||||
		suite.NoError(err)
 | 
			
		||||
 | 
			
		||||
		switch status := proposalRes.Proposal.Status; status {
 | 
			
		||||
		case govv1.StatusDepositPeriod, govv1.StatusVotingPeriod:
 | 
			
		||||
			return false
 | 
			
		||||
		case govv1.StatusPassed:
 | 
			
		||||
			return true
 | 
			
		||||
		case govv1.StatusFailed, govv1.StatusRejected:
 | 
			
		||||
			suite.Failf("proposal failed", "proposal failed with status %s", status.String())
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return false
 | 
			
		||||
	}, 60*time.Second, 1*time.Second)
 | 
			
		||||
 | 
			
		||||
	page := 1
 | 
			
		||||
	perPage := 100
 | 
			
		||||
 | 
			
		||||
	// Get the block the proposal was passed in
 | 
			
		||||
	passBlock, err := suite.Kava.TmSignClient.BlockSearch(
 | 
			
		||||
		context.Background(),
 | 
			
		||||
		fmt.Sprintf(
 | 
			
		||||
			"active_proposal.proposal_result = 'proposal_passed' AND active_proposal.proposal_id = %d",
 | 
			
		||||
			govRes.ProposalId,
 | 
			
		||||
		),
 | 
			
		||||
		&page,
 | 
			
		||||
		&perPage,
 | 
			
		||||
		"asc",
 | 
			
		||||
	)
 | 
			
		||||
	suite.Require().NoError(err)
 | 
			
		||||
	suite.Require().Equal(1, len(passBlock.Blocks), "passed proposal should be searchable")
 | 
			
		||||
 | 
			
		||||
	return passBlock.Blocks[len(passBlock.Blocks)-1].Block.Height
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// getBankTransferAmountAtBlock returns the amount of coins transferred between
 | 
			
		||||
// the given accounts in the block at the given height. Note that this returns
 | 
			
		||||
// a slice of sdk.Coin that can contain multiple coins of the SAME denom -- ie. NOT sdk.Coins
 | 
			
		||||
func (suite *IntegrationTestSuite) getBankTransferAmountAtBlock(
 | 
			
		||||
	blockHeight int64,
 | 
			
		||||
	sender sdk.AccAddress,
 | 
			
		||||
	receiver sdk.AccAddress,
 | 
			
		||||
) []sdk.Coin {
 | 
			
		||||
	// Fetch block results for paid staking rewards in the block
 | 
			
		||||
	blockRes, err := suite.Kava.TmSignClient.BlockResults(
 | 
			
		||||
		context.Background(),
 | 
			
		||||
		&blockHeight,
 | 
			
		||||
	)
 | 
			
		||||
	suite.Require().NoError(err)
 | 
			
		||||
 | 
			
		||||
	transferEvents := util.FilterEventsByType(
 | 
			
		||||
		blockRes.EndBlockEvents, // gov proposals applied in EndBlocker
 | 
			
		||||
		banktypes.EventTypeTransfer,
 | 
			
		||||
	)
 | 
			
		||||
	suite.Require().NotEmpty(transferEvents, "there should be at least 1 bank transfer event")
 | 
			
		||||
 | 
			
		||||
	transfers := []sdk.Coin{}
 | 
			
		||||
 | 
			
		||||
event:
 | 
			
		||||
	for _, event := range transferEvents {
 | 
			
		||||
		if event.Type != banktypes.EventTypeTransfer {
 | 
			
		||||
			suite.FailNowf(
 | 
			
		||||
				"unexpected event type %s in block results",
 | 
			
		||||
				event.Type,
 | 
			
		||||
			)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for _, attr := range event.Attributes {
 | 
			
		||||
			suite.T().Logf("event attr: %s = %s", string(attr.Key), string(attr.Value))
 | 
			
		||||
 | 
			
		||||
			if string(attr.Key) == banktypes.AttributeKeyRecipient {
 | 
			
		||||
				if string(attr.Value) != receiver.String() {
 | 
			
		||||
					continue event
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if string(attr.Key) == banktypes.AttributeKeySender {
 | 
			
		||||
				if string(attr.Value) != sender.String() {
 | 
			
		||||
					continue event
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if string(attr.Key) == sdk.AttributeKeyAmount {
 | 
			
		||||
				amount, err := sdk.ParseCoinNormalized(string(attr.Value))
 | 
			
		||||
				suite.Require().NoError(err)
 | 
			
		||||
 | 
			
		||||
				transfers = append(transfers, amount)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return transfers
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										65
									
								
								tests/e2e/e2e_upgrade_incentive_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								tests/e2e/e2e_upgrade_incentive_test.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,65 @@
 | 
			
		||||
package e2e_test
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	sdkmath "cosmossdk.io/math"
 | 
			
		||||
	sdk "github.com/cosmos/cosmos-sdk/types"
 | 
			
		||||
 | 
			
		||||
	"github.com/kava-labs/kava/tests/util"
 | 
			
		||||
	incentivetypes "github.com/kava-labs/kava/x/incentive/types"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func (suite *IntegrationTestSuite) TestUpgradeIncentiveParams() {
 | 
			
		||||
	suite.SkipIfUpgradeDisabled()
 | 
			
		||||
 | 
			
		||||
	beforeUpgradeCtx := util.CtxAtHeight(suite.UpgradeHeight - 1)
 | 
			
		||||
	afterUpgradeCtx := util.CtxAtHeight(suite.UpgradeHeight)
 | 
			
		||||
 | 
			
		||||
	// Before params
 | 
			
		||||
	incentiveParamsBefore, err := suite.Kava.Incentive.Params(
 | 
			
		||||
		beforeUpgradeCtx,
 | 
			
		||||
		&incentivetypes.QueryParamsRequest{},
 | 
			
		||||
	)
 | 
			
		||||
	suite.NoError(err)
 | 
			
		||||
 | 
			
		||||
	incentiveParamsAfter, err := suite.Kava.Incentive.Params(
 | 
			
		||||
		afterUpgradeCtx,
 | 
			
		||||
		&incentivetypes.QueryParamsRequest{},
 | 
			
		||||
	)
 | 
			
		||||
	suite.NoError(err)
 | 
			
		||||
 | 
			
		||||
	suite.Run("x/incentive parameters before upgrade", func() {
 | 
			
		||||
		suite.Require().Len(
 | 
			
		||||
			incentiveParamsBefore.Params.EarnRewardPeriods,
 | 
			
		||||
			1,
 | 
			
		||||
			"x/incentive should have 1 earn reward period before upgrade",
 | 
			
		||||
		)
 | 
			
		||||
 | 
			
		||||
		suite.Require().Equal(
 | 
			
		||||
			sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(159_459))),
 | 
			
		||||
			incentiveParamsBefore.Params.EarnRewardPeriods[0].RewardsPerSecond,
 | 
			
		||||
		)
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	suite.Run("x/incentive parameters after upgrade", func() {
 | 
			
		||||
		suite.Require().Len(
 | 
			
		||||
			incentiveParamsAfter.Params.EarnRewardPeriods,
 | 
			
		||||
			1,
 | 
			
		||||
			"x/incentive should have 1 earn reward period before upgrade",
 | 
			
		||||
		)
 | 
			
		||||
 | 
			
		||||
		suite.Require().Equal(
 | 
			
		||||
			// Manual calculation of
 | 
			
		||||
			// 600,000 * 1000,000 / (365 * 24 * 60 * 60)
 | 
			
		||||
			sdk.NewCoins(sdk.NewCoin("ukava", sdkmath.NewInt(19025))),
 | 
			
		||||
			incentiveParamsAfter.Params.EarnRewardPeriods[0].RewardsPerSecond,
 | 
			
		||||
		)
 | 
			
		||||
 | 
			
		||||
		// No other changes
 | 
			
		||||
		incentiveParamsAfter.Params.EarnRewardPeriods[0].RewardsPerSecond = incentiveParamsBefore.Params.EarnRewardPeriods[0].RewardsPerSecond
 | 
			
		||||
		suite.Require().Equal(
 | 
			
		||||
			incentiveParamsBefore,
 | 
			
		||||
			incentiveParamsAfter,
 | 
			
		||||
			"other param values should not be changed",
 | 
			
		||||
		)
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										474
									
								
								tests/e2e/e2e_upgrade_inflation_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										474
									
								
								tests/e2e/e2e_upgrade_inflation_test.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,474 @@
 | 
			
		||||
package e2e_test
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	sdkmath "cosmossdk.io/math"
 | 
			
		||||
	cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
 | 
			
		||||
	sdk "github.com/cosmos/cosmos-sdk/types"
 | 
			
		||||
	"github.com/cosmos/cosmos-sdk/x/bank/types"
 | 
			
		||||
	distributiontypes "github.com/cosmos/cosmos-sdk/x/distribution/types"
 | 
			
		||||
	minttypes "github.com/cosmos/cosmos-sdk/x/mint/types"
 | 
			
		||||
	stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
 | 
			
		||||
	abci "github.com/tendermint/tendermint/abci/types"
 | 
			
		||||
	coretypes "github.com/tendermint/tendermint/rpc/core/types"
 | 
			
		||||
 | 
			
		||||
	"github.com/kava-labs/kava/tests/util"
 | 
			
		||||
	communitytypes "github.com/kava-labs/kava/x/community/types"
 | 
			
		||||
	kavadisttypes "github.com/kava-labs/kava/x/kavadist/types"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func (suite *IntegrationTestSuite) TestUpgradeInflation_Disable() {
 | 
			
		||||
	suite.SkipIfUpgradeDisabled()
 | 
			
		||||
 | 
			
		||||
	afterUpgradeCtx := util.CtxAtHeight(suite.UpgradeHeight)
 | 
			
		||||
 | 
			
		||||
	// Get x/community for switchover time
 | 
			
		||||
	params, err := suite.Kava.Community.Params(afterUpgradeCtx, &communitytypes.QueryParamsRequest{})
 | 
			
		||||
	suite.Require().NoError(err)
 | 
			
		||||
 | 
			
		||||
	// Sleep until switchover time + 6 seconds for extra block
 | 
			
		||||
	sleepDuration := time.Until(params.Params.UpgradeTimeDisableInflation.Add(6 * time.Second))
 | 
			
		||||
	time.Sleep(sleepDuration)
 | 
			
		||||
 | 
			
		||||
	suite.Require().Eventually(func() bool {
 | 
			
		||||
		communityParams, err := suite.Kava.Community.Params(afterUpgradeCtx, &communitytypes.QueryParamsRequest{})
 | 
			
		||||
		suite.Require().NoError(err)
 | 
			
		||||
 | 
			
		||||
		// After params are set in x/community -- non-zero switchover time
 | 
			
		||||
		return !communityParams.Params.UpgradeTimeDisableInflation.Equal(time.Time{})
 | 
			
		||||
	}, 20*time.Second, 3*time.Second)
 | 
			
		||||
 | 
			
		||||
	// Fetch exact block when inflation stop event emitted
 | 
			
		||||
	// This is run after the switchover, so we don't need to poll
 | 
			
		||||
	_, switchoverHeight, err := suite.Kava.GetBeginBlockEventsFromQuery(
 | 
			
		||||
		context.Background(),
 | 
			
		||||
		fmt.Sprintf(
 | 
			
		||||
			"%s.%s EXISTS",
 | 
			
		||||
			communitytypes.EventTypeInflationStop,
 | 
			
		||||
			communitytypes.AttributeKeyInflationDisableTime,
 | 
			
		||||
		),
 | 
			
		||||
	)
 | 
			
		||||
	suite.Require().NoError(err)
 | 
			
		||||
	suite.Require().NotZero(switchoverHeight)
 | 
			
		||||
 | 
			
		||||
	// 1 block before switchover
 | 
			
		||||
	beforeSwitchoverCtx := util.CtxAtHeight(switchoverHeight - 1)
 | 
			
		||||
	afterSwitchoverCtx := util.CtxAtHeight(switchoverHeight)
 | 
			
		||||
 | 
			
		||||
	suite.Run("x/mint, x/kavadist inflation before switchover", func() {
 | 
			
		||||
		mintParams, err := suite.Kava.Mint.Params(
 | 
			
		||||
			beforeSwitchoverCtx,
 | 
			
		||||
			&minttypes.QueryParamsRequest{},
 | 
			
		||||
		)
 | 
			
		||||
		suite.NoError(err)
 | 
			
		||||
		kavaDistParams, err := suite.Kava.Kavadist.Params(
 | 
			
		||||
			beforeSwitchoverCtx,
 | 
			
		||||
			&kavadisttypes.QueryParamsRequest{},
 | 
			
		||||
		)
 | 
			
		||||
		suite.NoError(err)
 | 
			
		||||
 | 
			
		||||
		// Use .String() to compare Decs since x/mint uses the deprecated one,
 | 
			
		||||
		// mismatch of types but same value.
 | 
			
		||||
		suite.Equal(
 | 
			
		||||
			sdkmath.LegacyMustNewDecFromStr("0.595000000000000000").String(),
 | 
			
		||||
			mintParams.Params.InflationMin.String(),
 | 
			
		||||
			"x/mint inflation min should be 59.5%% before switchover",
 | 
			
		||||
		)
 | 
			
		||||
		suite.Equal(
 | 
			
		||||
			sdkmath.LegacyMustNewDecFromStr("0.595000000000000000").String(),
 | 
			
		||||
			mintParams.Params.InflationMax.String(),
 | 
			
		||||
			"x/mint inflation max should be 59.5%% before switchover",
 | 
			
		||||
		)
 | 
			
		||||
 | 
			
		||||
		suite.True(
 | 
			
		||||
			kavaDistParams.Params.Active,
 | 
			
		||||
			"x/kavadist should be active before switchover",
 | 
			
		||||
		)
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	suite.Run("x/distribution community tax before switchover", func() {
 | 
			
		||||
		distrParams, err := suite.Kava.Distribution.Params(
 | 
			
		||||
			beforeSwitchoverCtx,
 | 
			
		||||
			&distributiontypes.QueryParamsRequest{},
 | 
			
		||||
		)
 | 
			
		||||
		suite.NoError(err)
 | 
			
		||||
 | 
			
		||||
		suite.Equal(
 | 
			
		||||
			sdkmath.LegacyMustNewDecFromStr("0.949500000000000000").String(),
 | 
			
		||||
			distrParams.Params.CommunityTax.String(),
 | 
			
		||||
			"x/distribution community tax should be 94.95%% before switchover",
 | 
			
		||||
		)
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	suite.Run("x/mint, x/kavadist inflation after switchover", func() {
 | 
			
		||||
		mintParams, err := suite.Kava.Mint.Params(
 | 
			
		||||
			afterSwitchoverCtx,
 | 
			
		||||
			&minttypes.QueryParamsRequest{},
 | 
			
		||||
		)
 | 
			
		||||
		suite.NoError(err)
 | 
			
		||||
		kavaDistParams, err := suite.Kava.Kavadist.Params(
 | 
			
		||||
			afterSwitchoverCtx,
 | 
			
		||||
			&kavadisttypes.QueryParamsRequest{},
 | 
			
		||||
		)
 | 
			
		||||
		suite.NoError(err)
 | 
			
		||||
 | 
			
		||||
		suite.Equal(
 | 
			
		||||
			sdkmath.LegacyZeroDec().String(),
 | 
			
		||||
			mintParams.Params.InflationMin.String(),
 | 
			
		||||
			"x/mint inflation min should be 0% after switchover",
 | 
			
		||||
		)
 | 
			
		||||
		suite.Equal(
 | 
			
		||||
			sdkmath.LegacyZeroDec().String(),
 | 
			
		||||
			mintParams.Params.InflationMax.String(),
 | 
			
		||||
			"x/mint inflation max should be 0% after switchover",
 | 
			
		||||
		)
 | 
			
		||||
 | 
			
		||||
		suite.False(
 | 
			
		||||
			kavaDistParams.Params.Active,
 | 
			
		||||
			"x/kavadist should be inactive after switchover",
 | 
			
		||||
		)
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	suite.Run("x/distribution community tax after switchover", func() {
 | 
			
		||||
		distrParams, err := suite.Kava.Distribution.Params(
 | 
			
		||||
			afterSwitchoverCtx,
 | 
			
		||||
			&distributiontypes.QueryParamsRequest{},
 | 
			
		||||
		)
 | 
			
		||||
		suite.NoError(err)
 | 
			
		||||
 | 
			
		||||
		suite.Equal(
 | 
			
		||||
			sdkmath.LegacyZeroDec().String(),
 | 
			
		||||
			distrParams.Params.CommunityTax.String(),
 | 
			
		||||
			"x/distribution community tax should be 0%% before switchover",
 | 
			
		||||
		)
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	// Ensure inflation was still active before switchover
 | 
			
		||||
	suite.Run("positive mint events before switchover", func() {
 | 
			
		||||
		// 1 block before switchover
 | 
			
		||||
		queryHeight := switchoverHeight - 1
 | 
			
		||||
 | 
			
		||||
		block, err := suite.Kava.TmSignClient.BlockResults(
 | 
			
		||||
			context.Background(),
 | 
			
		||||
			&queryHeight,
 | 
			
		||||
		)
 | 
			
		||||
		suite.Require().NoError(err)
 | 
			
		||||
 | 
			
		||||
		// Mint events should only occur in begin block
 | 
			
		||||
		mintEvents := util.FilterEventsByType(block.BeginBlockEvents, minttypes.EventTypeMint)
 | 
			
		||||
 | 
			
		||||
		suite.Require().NotEmpty(mintEvents, "mint events should be emitted")
 | 
			
		||||
 | 
			
		||||
		// Ensure mint amounts are non-zero
 | 
			
		||||
		found := false
 | 
			
		||||
		for _, event := range mintEvents {
 | 
			
		||||
			for _, attribute := range event.Attributes {
 | 
			
		||||
				// Bonded ratio and annual provisions unchecked
 | 
			
		||||
 | 
			
		||||
				if string(attribute.Key) == minttypes.AttributeKeyInflation {
 | 
			
		||||
					suite.Equal(
 | 
			
		||||
						sdkmath.LegacyMustNewDecFromStr("0.595000000000000000").String(),
 | 
			
		||||
						string(attribute.Value),
 | 
			
		||||
						"inflation should be 59.5%% before switchover",
 | 
			
		||||
					)
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				if string(attribute.Key) == sdk.AttributeKeyAmount {
 | 
			
		||||
					found = true
 | 
			
		||||
					// Parse as native go int, not necessary to use sdk.Int
 | 
			
		||||
					value, err := strconv.Atoi(string(attribute.Value))
 | 
			
		||||
					suite.Require().NoError(err)
 | 
			
		||||
 | 
			
		||||
					suite.NotZero(value, "mint amount should be non-zero")
 | 
			
		||||
					suite.Positive(value, "mint amount should be positive")
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		suite.True(found, "mint amount should be found")
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	suite.Run("staking denom supply increases before switchover", func() {
 | 
			
		||||
		queryHeight := switchoverHeight - 2
 | 
			
		||||
 | 
			
		||||
		supply1, err := suite.Kava.Bank.SupplyOf(
 | 
			
		||||
			util.CtxAtHeight(queryHeight),
 | 
			
		||||
			&types.QuerySupplyOfRequest{
 | 
			
		||||
				Denom: suite.Kava.StakingDenom,
 | 
			
		||||
			},
 | 
			
		||||
		)
 | 
			
		||||
		suite.Require().NoError(err)
 | 
			
		||||
 | 
			
		||||
		suite.NotZero(supply1.Amount, "ukava supply should be non-zero")
 | 
			
		||||
 | 
			
		||||
		// Next block
 | 
			
		||||
		queryHeight += 1
 | 
			
		||||
		supply2, err := suite.Kava.Bank.SupplyOf(
 | 
			
		||||
			util.CtxAtHeight(queryHeight),
 | 
			
		||||
			&types.QuerySupplyOfRequest{
 | 
			
		||||
				Denom: suite.Kava.StakingDenom,
 | 
			
		||||
			},
 | 
			
		||||
		)
 | 
			
		||||
		suite.Require().NoError(err)
 | 
			
		||||
 | 
			
		||||
		suite.NotZero(supply2.Amount, "ukava supply should be non-zero")
 | 
			
		||||
 | 
			
		||||
		suite.Truef(
 | 
			
		||||
			supply2.Amount.Amount.GT(supply1.Amount.Amount),
 | 
			
		||||
			"ukava supply before switchover should increase between blocks, %s > %s",
 | 
			
		||||
			supply2.Amount.Amount.String(),
 | 
			
		||||
		)
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	// Check if inflation is ACTUALLY disabled... check if any coins are being
 | 
			
		||||
	// minted in the blocks after switchover
 | 
			
		||||
	suite.Run("no minting after switchover", func() {
 | 
			
		||||
		kavaSupply := sdk.NewCoin(suite.Kava.StakingDenom, sdkmath.ZeroInt())
 | 
			
		||||
 | 
			
		||||
		// Next 5 blocks after switchover, ensure there's actually no more inflation
 | 
			
		||||
		for i := 0; i < 5; i++ {
 | 
			
		||||
			queryHeight := switchoverHeight + int64(i)
 | 
			
		||||
 | 
			
		||||
			suite.Run(
 | 
			
		||||
				fmt.Sprintf("x/mint events with 0 amount @ height=%d", queryHeight),
 | 
			
		||||
				func() {
 | 
			
		||||
					var block *coretypes.ResultBlockResults
 | 
			
		||||
					suite.Require().Eventually(func() bool {
 | 
			
		||||
						// Check begin block events
 | 
			
		||||
						block, err = suite.Kava.TmSignClient.BlockResults(
 | 
			
		||||
							context.Background(),
 | 
			
		||||
							&queryHeight,
 | 
			
		||||
						)
 | 
			
		||||
 | 
			
		||||
						return err == nil
 | 
			
		||||
					}, 20*time.Second, 3*time.Second)
 | 
			
		||||
 | 
			
		||||
					var mintEvents []abci.Event
 | 
			
		||||
 | 
			
		||||
					// Mint events should only occur in begin block, but we just include
 | 
			
		||||
					// everything else just in case anything changes in x/mint
 | 
			
		||||
					mintEventsBegin := util.FilterEventsByType(block.BeginBlockEvents, minttypes.EventTypeMint)
 | 
			
		||||
					mintEventsEnd := util.FilterEventsByType(block.EndBlockEvents, minttypes.EventTypeMint)
 | 
			
		||||
					mintEventsTx := util.FilterTxEventsByType(block.TxsResults, minttypes.EventTypeMint)
 | 
			
		||||
 | 
			
		||||
					mintEvents = append(mintEvents, mintEventsBegin...)
 | 
			
		||||
					mintEvents = append(mintEvents, mintEventsEnd...)
 | 
			
		||||
					mintEvents = append(mintEvents, mintEventsTx...)
 | 
			
		||||
 | 
			
		||||
					suite.Require().NotEmpty(mintEvents, "mint events should still be emitted")
 | 
			
		||||
 | 
			
		||||
					// Ensure mint amounts are 0
 | 
			
		||||
					found := false
 | 
			
		||||
					for _, event := range mintEvents {
 | 
			
		||||
						for _, attribute := range event.Attributes {
 | 
			
		||||
							// Bonded ratio and annual provisions unchecked
 | 
			
		||||
 | 
			
		||||
							if string(attribute.Key) == minttypes.AttributeKeyInflation {
 | 
			
		||||
								suite.Equal(sdkmath.LegacyZeroDec().String(), string(attribute.Value))
 | 
			
		||||
							}
 | 
			
		||||
 | 
			
		||||
							if string(attribute.Key) == sdk.AttributeKeyAmount {
 | 
			
		||||
								found = true
 | 
			
		||||
								suite.Equal(sdkmath.ZeroInt().String(), string(attribute.Value))
 | 
			
		||||
							}
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					suite.True(found, "mint amount should be found")
 | 
			
		||||
				},
 | 
			
		||||
			)
 | 
			
		||||
 | 
			
		||||
			// Run this after the events check, since that one waits for the
 | 
			
		||||
			// new block if necessary
 | 
			
		||||
			suite.Run(
 | 
			
		||||
				fmt.Sprintf("total staking denom supply should not change @ height=%d", queryHeight),
 | 
			
		||||
				func() {
 | 
			
		||||
					supplyRes, err := suite.Kava.Bank.SupplyOf(
 | 
			
		||||
						util.CtxAtHeight(queryHeight),
 | 
			
		||||
						&types.QuerySupplyOfRequest{
 | 
			
		||||
							Denom: suite.Kava.StakingDenom,
 | 
			
		||||
						},
 | 
			
		||||
					)
 | 
			
		||||
					suite.Require().NoError(err)
 | 
			
		||||
 | 
			
		||||
					if kavaSupply.IsZero() {
 | 
			
		||||
						// First iteration, set supply
 | 
			
		||||
						kavaSupply = supplyRes.Amount
 | 
			
		||||
					} else {
 | 
			
		||||
						suite.Require().Equal(
 | 
			
		||||
							kavaSupply,
 | 
			
		||||
							supplyRes.Amount,
 | 
			
		||||
							"ukava supply should not change",
 | 
			
		||||
						)
 | 
			
		||||
					}
 | 
			
		||||
				},
 | 
			
		||||
			)
 | 
			
		||||
		}
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	suite.Run("no staking rewards from x/community before switchover", func() {
 | 
			
		||||
		// 1 block before switchover
 | 
			
		||||
		queryHeight := switchoverHeight - 1
 | 
			
		||||
 | 
			
		||||
		block, err := suite.Kava.TmSignClient.BlockResults(
 | 
			
		||||
			context.Background(),
 | 
			
		||||
			&queryHeight,
 | 
			
		||||
		)
 | 
			
		||||
		suite.Require().NoError(err)
 | 
			
		||||
 | 
			
		||||
		// Events are not emitted if amount is 0
 | 
			
		||||
		stakingRewardEvents := util.FilterEventsByType(block.BeginBlockEvents, communitytypes.EventTypeStakingRewardsPaid)
 | 
			
		||||
		suite.Require().Empty(stakingRewardEvents, "staking reward events should not be emitted")
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	suite.Run("staking rewards pay out from x/community after switchover", func() {
 | 
			
		||||
		for i := 0; i < 5; i++ {
 | 
			
		||||
			// after switchover
 | 
			
		||||
			queryHeight := switchoverHeight + int64(i)
 | 
			
		||||
 | 
			
		||||
			block, err := suite.Kava.TmSignClient.BlockResults(
 | 
			
		||||
				context.Background(),
 | 
			
		||||
				&queryHeight,
 | 
			
		||||
			)
 | 
			
		||||
			suite.Require().NoError(err)
 | 
			
		||||
 | 
			
		||||
			stakingRewardEvents := util.FilterEventsByType(
 | 
			
		||||
				block.BeginBlockEvents,
 | 
			
		||||
				communitytypes.EventTypeStakingRewardsPaid,
 | 
			
		||||
			)
 | 
			
		||||
			suite.Require().NotEmptyf(
 | 
			
		||||
				stakingRewardEvents,
 | 
			
		||||
				"staking reward events should be emitted at height=%d",
 | 
			
		||||
				queryHeight,
 | 
			
		||||
			)
 | 
			
		||||
 | 
			
		||||
			// Ensure amounts are non-zero
 | 
			
		||||
			found := false
 | 
			
		||||
			for _, attr := range stakingRewardEvents[0].Attributes {
 | 
			
		||||
				if string(attr.Key) == communitytypes.AttributeKeyStakingRewardAmount {
 | 
			
		||||
					coins, err := sdk.ParseCoinNormalized(string(attr.Value))
 | 
			
		||||
					suite.Require().NoError(err, "staking reward amount should be parsable coins")
 | 
			
		||||
 | 
			
		||||
					suite.Truef(
 | 
			
		||||
						coins.Amount.IsPositive(),
 | 
			
		||||
						"staking reward amount should be a positive amount at height=%d",
 | 
			
		||||
						queryHeight,
 | 
			
		||||
					)
 | 
			
		||||
					found = true
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			suite.Truef(
 | 
			
		||||
				found,
 | 
			
		||||
				"staking reward amount should be found in events at height=%d",
 | 
			
		||||
				queryHeight,
 | 
			
		||||
			)
 | 
			
		||||
		}
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	// Staking rewards can still be claimed
 | 
			
		||||
	suite.Run("staking rewards claimable after switchover", func() {
 | 
			
		||||
		suite.SkipIfKvtoolDisabled()
 | 
			
		||||
 | 
			
		||||
		// Get the delegator of the only validator
 | 
			
		||||
		validators, err := suite.Kava.Staking.Validators(
 | 
			
		||||
			context.Background(),
 | 
			
		||||
			&stakingtypes.QueryValidatorsRequest{},
 | 
			
		||||
		)
 | 
			
		||||
		suite.Require().NoError(err)
 | 
			
		||||
		suite.Require().Positive(len(validators.Validators), "should only be at least 1 validator")
 | 
			
		||||
 | 
			
		||||
		valAddr, err := sdk.ValAddressFromBech32(validators.Validators[0].OperatorAddress)
 | 
			
		||||
		suite.Require().NoError(err)
 | 
			
		||||
 | 
			
		||||
		accAddr := sdk.AccAddress(valAddr.Bytes())
 | 
			
		||||
 | 
			
		||||
		balBefore, err := suite.Kava.Bank.Balance(
 | 
			
		||||
			context.Background(),
 | 
			
		||||
			&types.QueryBalanceRequest{
 | 
			
		||||
				Address: accAddr.String(),
 | 
			
		||||
				Denom:   suite.Kava.StakingDenom,
 | 
			
		||||
			},
 | 
			
		||||
		)
 | 
			
		||||
		suite.Require().NoError(err)
 | 
			
		||||
		suite.Require().False(balBefore.Balance.IsZero(), "val staking denom balance should be non-zero")
 | 
			
		||||
 | 
			
		||||
		delegationRewards, err := suite.Kava.Distribution.DelegationRewards(
 | 
			
		||||
			context.Background(),
 | 
			
		||||
			&distributiontypes.QueryDelegationRewardsRequest{
 | 
			
		||||
				ValidatorAddress: valAddr.String(),
 | 
			
		||||
				DelegatorAddress: accAddr.String(),
 | 
			
		||||
			},
 | 
			
		||||
		)
 | 
			
		||||
		suite.Require().NoError(err)
 | 
			
		||||
 | 
			
		||||
		suite.False(delegationRewards.Rewards.Empty())
 | 
			
		||||
		suite.True(delegationRewards.Rewards.IsAllPositive(), "queried rewards should be positive")
 | 
			
		||||
 | 
			
		||||
		withdrawRewardsMsg := distributiontypes.NewMsgWithdrawDelegatorReward(
 | 
			
		||||
			accAddr,
 | 
			
		||||
			valAddr,
 | 
			
		||||
		)
 | 
			
		||||
 | 
			
		||||
		// Get the validator private key from kava keyring
 | 
			
		||||
		key, err := suite.Kava.Keyring.(unsafeExporter).ExportPrivateKeyObject(
 | 
			
		||||
			"validator",
 | 
			
		||||
		)
 | 
			
		||||
		suite.Require().NoError(err)
 | 
			
		||||
 | 
			
		||||
		acc := suite.Kava.AddNewSigningAccountFromPrivKey(
 | 
			
		||||
			"validator",
 | 
			
		||||
			key,
 | 
			
		||||
			"",
 | 
			
		||||
			suite.Kava.ChainID,
 | 
			
		||||
		)
 | 
			
		||||
 | 
			
		||||
		gasLimit := int64(2e5)
 | 
			
		||||
		fee := ukava(200)
 | 
			
		||||
		req := util.KavaMsgRequest{
 | 
			
		||||
			Msgs:      []sdk.Msg{withdrawRewardsMsg},
 | 
			
		||||
			GasLimit:  uint64(gasLimit),
 | 
			
		||||
			FeeAmount: sdk.NewCoins(fee),
 | 
			
		||||
			Memo:      "give me my money",
 | 
			
		||||
		}
 | 
			
		||||
		res := acc.SignAndBroadcastKavaTx(req)
 | 
			
		||||
 | 
			
		||||
		_, err = util.WaitForSdkTxCommit(suite.Kava.Tx, res.Result.TxHash, 6*time.Second)
 | 
			
		||||
		suite.Require().NoError(err)
 | 
			
		||||
 | 
			
		||||
		balAfter, err := suite.Kava.Bank.Balance(
 | 
			
		||||
			context.Background(),
 | 
			
		||||
			&types.QueryBalanceRequest{
 | 
			
		||||
				Address: accAddr.String(),
 | 
			
		||||
				Denom:   suite.Kava.StakingDenom,
 | 
			
		||||
			},
 | 
			
		||||
		)
 | 
			
		||||
		suite.Require().NoError(err)
 | 
			
		||||
		suite.Require().False(balAfter.Balance.IsZero(), "val staking denom balance should be non-zero")
 | 
			
		||||
 | 
			
		||||
		balIncrease := balAfter.Balance.
 | 
			
		||||
			Sub(*balBefore.Balance).
 | 
			
		||||
			Add(res.Tx.GetFee()[0]) // Add the fee back to balance to compare actual balances
 | 
			
		||||
 | 
			
		||||
		queriedRewardsCoins, _ := delegationRewards.Rewards.TruncateDecimal()
 | 
			
		||||
 | 
			
		||||
		suite.Require().Truef(
 | 
			
		||||
			queriedRewardsCoins.AmountOf(suite.Kava.StakingDenom).
 | 
			
		||||
				LTE(balIncrease.Amount),
 | 
			
		||||
			"claimed rewards should be >= queried delegation rewards, got claimed %s vs queried %s",
 | 
			
		||||
			balIncrease.Amount.String(),
 | 
			
		||||
			queriedRewardsCoins.AmountOf(suite.Kava.StakingDenom).String(),
 | 
			
		||||
		)
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// unsafeExporter is implemented by key stores that support unsafe export
 | 
			
		||||
// of private keys' material.
 | 
			
		||||
type unsafeExporter interface {
 | 
			
		||||
	// ExportPrivateKeyObject returns a private key in unarmored format.
 | 
			
		||||
	ExportPrivateKeyObject(uid string) (cryptotypes.PrivKey, error)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										103
									
								
								tests/e2e/e2e_upgrade_min_commission_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										103
									
								
								tests/e2e/e2e_upgrade_min_commission_test.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,103 @@
 | 
			
		||||
package e2e_test
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
 | 
			
		||||
	sdkmath "cosmossdk.io/math"
 | 
			
		||||
	"github.com/cosmos/cosmos-sdk/client/grpc/tmservice"
 | 
			
		||||
	sdk "github.com/cosmos/cosmos-sdk/types"
 | 
			
		||||
	"github.com/cosmos/cosmos-sdk/x/staking/types"
 | 
			
		||||
 | 
			
		||||
	"github.com/kava-labs/kava/tests/util"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func (suite *IntegrationTestSuite) TestValMinCommission() {
 | 
			
		||||
	suite.SkipIfUpgradeDisabled()
 | 
			
		||||
 | 
			
		||||
	beforeUpgradeCtx := util.CtxAtHeight(suite.UpgradeHeight - 1)
 | 
			
		||||
	afterUpgradeCtx := util.CtxAtHeight(suite.UpgradeHeight)
 | 
			
		||||
 | 
			
		||||
	suite.Run("before upgrade", func() {
 | 
			
		||||
		// Before params
 | 
			
		||||
		beforeParams, err := suite.Kava.Staking.Params(beforeUpgradeCtx, &types.QueryParamsRequest{})
 | 
			
		||||
		suite.Require().NoError(err)
 | 
			
		||||
 | 
			
		||||
		suite.Require().Equal(
 | 
			
		||||
			sdkmath.LegacyZeroDec().String(),
 | 
			
		||||
			beforeParams.Params.MinCommissionRate.String(),
 | 
			
		||||
			"min commission rate should be 0%% before upgrade",
 | 
			
		||||
		)
 | 
			
		||||
 | 
			
		||||
		// Before validators
 | 
			
		||||
		beforeValidators, err := suite.Kava.Staking.Validators(beforeUpgradeCtx, &types.QueryValidatorsRequest{})
 | 
			
		||||
		suite.Require().NoError(err)
 | 
			
		||||
 | 
			
		||||
		for _, val := range beforeValidators.Validators {
 | 
			
		||||
			// In kvtool gentx, the commission rate is set to 0, with max of 0.01
 | 
			
		||||
			expectedRate := sdkmath.LegacyZeroDec()
 | 
			
		||||
			expectedRateMax := sdkmath.LegacyMustNewDecFromStr("0.01")
 | 
			
		||||
 | 
			
		||||
			suite.Require().Equalf(
 | 
			
		||||
				expectedRate.String(),
 | 
			
		||||
				val.Commission.CommissionRates.Rate.String(),
 | 
			
		||||
				"validator %s should have commission rate of %s before upgrade",
 | 
			
		||||
				val.OperatorAddress,
 | 
			
		||||
				expectedRate,
 | 
			
		||||
			)
 | 
			
		||||
 | 
			
		||||
			suite.Require().Equalf(
 | 
			
		||||
				expectedRateMax.String(),
 | 
			
		||||
				val.Commission.CommissionRates.MaxRate.String(),
 | 
			
		||||
				"validator %s should have max commission rate of %s before upgrade",
 | 
			
		||||
				val.OperatorAddress,
 | 
			
		||||
				expectedRateMax,
 | 
			
		||||
			)
 | 
			
		||||
		}
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	suite.Run("after upgrade", func() {
 | 
			
		||||
		block, err := suite.Kava.Tm.GetBlockByHeight(context.Background(), &tmservice.GetBlockByHeightRequest{
 | 
			
		||||
			Height: suite.UpgradeHeight,
 | 
			
		||||
		})
 | 
			
		||||
		suite.Require().NoError(err)
 | 
			
		||||
 | 
			
		||||
		// After params
 | 
			
		||||
		afterParams, err := suite.Kava.Staking.Params(afterUpgradeCtx, &types.QueryParamsRequest{})
 | 
			
		||||
		suite.Require().NoError(err)
 | 
			
		||||
 | 
			
		||||
		expectedMinRate := sdk.MustNewDecFromStr("0.05")
 | 
			
		||||
 | 
			
		||||
		suite.Require().Equal(
 | 
			
		||||
			expectedMinRate.String(),
 | 
			
		||||
			afterParams.Params.MinCommissionRate.String(),
 | 
			
		||||
			"min commission rate should be 5%% after upgrade",
 | 
			
		||||
		)
 | 
			
		||||
 | 
			
		||||
		// After validators
 | 
			
		||||
		afterValidators, err := suite.Kava.Staking.Validators(afterUpgradeCtx, &types.QueryValidatorsRequest{})
 | 
			
		||||
		suite.Require().NoError(err)
 | 
			
		||||
 | 
			
		||||
		for _, val := range afterValidators.Validators {
 | 
			
		||||
 | 
			
		||||
			suite.Require().Truef(
 | 
			
		||||
				val.Commission.CommissionRates.Rate.GTE(expectedMinRate),
 | 
			
		||||
				"validator %s should have commission rate of at least 5%%",
 | 
			
		||||
				val.OperatorAddress,
 | 
			
		||||
			)
 | 
			
		||||
 | 
			
		||||
			suite.Require().Truef(
 | 
			
		||||
				val.Commission.CommissionRates.MaxRate.GTE(expectedMinRate),
 | 
			
		||||
				"validator %s should have max commission rate of at least 5%%",
 | 
			
		||||
				val.OperatorAddress,
 | 
			
		||||
			)
 | 
			
		||||
 | 
			
		||||
			suite.Require().Truef(
 | 
			
		||||
				val.Commission.UpdateTime.Equal(block.SdkBlock.Header.Time),
 | 
			
		||||
				"validator %s should have commission update time set to block time, expected %s, got %s",
 | 
			
		||||
				val.OperatorAddress,
 | 
			
		||||
				block.SdkBlock.Header.Time,
 | 
			
		||||
				val.Commission.UpdateTime,
 | 
			
		||||
			)
 | 
			
		||||
		}
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
@ -14,6 +14,7 @@ import (
 | 
			
		||||
	sdk "github.com/cosmos/cosmos-sdk/types"
 | 
			
		||||
	txtypes "github.com/cosmos/cosmos-sdk/types/tx"
 | 
			
		||||
	authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
 | 
			
		||||
	authz "github.com/cosmos/cosmos-sdk/x/authz"
 | 
			
		||||
	banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
 | 
			
		||||
	distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types"
 | 
			
		||||
	govv1types "github.com/cosmos/cosmos-sdk/x/gov/types/v1"
 | 
			
		||||
@ -39,6 +40,7 @@ import (
 | 
			
		||||
	communitytypes "github.com/kava-labs/kava/x/community/types"
 | 
			
		||||
	earntypes "github.com/kava-labs/kava/x/earn/types"
 | 
			
		||||
	evmutiltypes "github.com/kava-labs/kava/x/evmutil/types"
 | 
			
		||||
	incentivetypes "github.com/kava-labs/kava/x/incentive/types"
 | 
			
		||||
	kavadisttypes "github.com/kava-labs/kava/x/kavadist/types"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@ -58,11 +60,13 @@ type Chain struct {
 | 
			
		||||
	EncodingConfig kavaparams.EncodingConfig
 | 
			
		||||
 | 
			
		||||
	Auth         authtypes.QueryClient
 | 
			
		||||
	Authz        authz.QueryClient
 | 
			
		||||
	Bank         banktypes.QueryClient
 | 
			
		||||
	Cdp          cdptypes.QueryClient
 | 
			
		||||
	Committee    committeetypes.QueryClient
 | 
			
		||||
	Community    communitytypes.QueryClient
 | 
			
		||||
	Distribution distrtypes.QueryClient
 | 
			
		||||
	Incentive    incentivetypes.QueryClient
 | 
			
		||||
	Kavadist     kavadisttypes.QueryClient
 | 
			
		||||
	Earn         earntypes.QueryClient
 | 
			
		||||
	Evm          evmtypes.QueryClient
 | 
			
		||||
@ -120,11 +124,13 @@ func NewChain(t *testing.T, details *runner.ChainDetails, fundedAccountMnemonic
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	chain.Auth = authtypes.NewQueryClient(grpcConn)
 | 
			
		||||
	chain.Authz = authz.NewQueryClient(grpcConn)
 | 
			
		||||
	chain.Bank = banktypes.NewQueryClient(grpcConn)
 | 
			
		||||
	chain.Cdp = cdptypes.NewQueryClient(grpcConn)
 | 
			
		||||
	chain.Committee = committeetypes.NewQueryClient(grpcConn)
 | 
			
		||||
	chain.Community = communitytypes.NewQueryClient(grpcConn)
 | 
			
		||||
	chain.Distribution = distrtypes.NewQueryClient(grpcConn)
 | 
			
		||||
	chain.Incentive = incentivetypes.NewQueryClient(grpcConn)
 | 
			
		||||
	chain.Kavadist = kavadisttypes.NewQueryClient(grpcConn)
 | 
			
		||||
	chain.Earn = earntypes.NewQueryClient(grpcConn)
 | 
			
		||||
	chain.Evm = evmtypes.NewQueryClient(grpcConn)
 | 
			
		||||
@ -224,6 +230,21 @@ func (chain *Chain) QuerySdkForBalances(addr sdk.AccAddress) sdk.Coins {
 | 
			
		||||
	return res.Balances
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// QuerySdkForBalancesAtHeight gets the balance of a particular address on this Chain, at the specified height.
 | 
			
		||||
func (chain *Chain) QuerySdkForBalancesAtHeight(
 | 
			
		||||
	height int64,
 | 
			
		||||
	addr sdk.AccAddress,
 | 
			
		||||
) sdk.Coins {
 | 
			
		||||
	res, err := chain.Bank.AllBalances(
 | 
			
		||||
		util.CtxAtHeight(height),
 | 
			
		||||
		&banktypes.QueryAllBalancesRequest{
 | 
			
		||||
			Address: addr.String(),
 | 
			
		||||
		},
 | 
			
		||||
	)
 | 
			
		||||
	require.NoError(chain.t, err)
 | 
			
		||||
	return res.Balances
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetModuleBalances returns the balance of a requested module account
 | 
			
		||||
func (chain *Chain) GetModuleBalances(moduleName string) sdk.Coins {
 | 
			
		||||
	addr := authtypes.NewModuleAddress(moduleName)
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user