refactor: staking module using mocks (#12827)
## Description Closes: #12504 --- ### Author Checklist *All items are required. Please add a note to the item if the item is not applicable and please add links to any relevant follow up issues.* I have... - [ ] included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] added `!` to the type prefix if API or client breaking change - [ ] targeted the correct branch (see [PR Targeting](https://github.com/cosmos/cosmos-sdk/blob/main/CONTRIBUTING.md#pr-targeting)) - [ ] provided a link to the relevant issue or specification - [ ] followed the guidelines for [building modules](https://github.com/cosmos/cosmos-sdk/blob/main/docs/building-modules) - [ ] included the necessary unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/main/CONTRIBUTING.md#testing) - [ ] added a changelog entry to `CHANGELOG.md` - [ ] included comments for [documenting Go code](https://blog.golang.org/godoc) - [ ] updated the relevant documentation or specification - [ ] reviewed "Files changed" and left comments if necessary - [ ] confirmed all CI checks have passed ### Reviewers Checklist *All items are required. Please add a note if the item is not applicable and please add your handle next to the items reviewed if you only reviewed selected items.* I have... - [ ] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] confirmed `!` in the type prefix if API or client breaking change - [ ] confirmed all author checklist items have been addressed - [ ] reviewed state machine logic - [ ] reviewed API design and naming - [ ] reviewed documentation is accurate - [ ] reviewed tests and test coverage - [ ] manually tested (if applicable)
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
package keeper_test
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
"github.com/cosmos/cosmos-sdk/simapp"
|
||||
simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
moduletestutil "github.com/cosmos/cosmos-sdk/types/module/testutil"
|
||||
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
|
||||
govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/staking/keeper"
|
||||
"github.com/cosmos/cosmos-sdk/x/staking/teststaking"
|
||||
"github.com/cosmos/cosmos-sdk/x/staking/types"
|
||||
)
|
||||
|
||||
var PKs = simtestutil.CreateTestPubKeys(500)
|
||||
|
||||
func init() {
|
||||
sdk.DefaultPowerReduction = sdk.NewIntFromBigInt(new(big.Int).Exp(big.NewInt(10), big.NewInt(18), nil))
|
||||
}
|
||||
|
||||
// createTestInput Returns a simapp with custom StakingKeeper
|
||||
// to avoid messing with the hooks.
|
||||
func createTestInput(t *testing.T) (*codec.LegacyAmino, *simapp.SimApp, sdk.Context) {
|
||||
app := simapp.Setup(t, false)
|
||||
ctx := app.BaseApp.NewContext(false, tmproto.Header{})
|
||||
|
||||
app.StakingKeeper = keeper.NewKeeper(
|
||||
app.AppCodec(),
|
||||
app.GetKey(types.StoreKey),
|
||||
app.AccountKeeper,
|
||||
app.BankKeeper,
|
||||
authtypes.NewModuleAddress(govtypes.ModuleName).String(),
|
||||
)
|
||||
return app.LegacyAmino(), app, ctx
|
||||
}
|
||||
|
||||
// intended to be used with require/assert: require.True(ValEq(...))
|
||||
func ValEq(t *testing.T, exp, got types.Validator) (*testing.T, bool, string, types.Validator, types.Validator) {
|
||||
return t, exp.MinEqual(&got), "expected:\n%v\ngot:\n%v", exp, got
|
||||
}
|
||||
|
||||
// generateAddresses generates numAddrs of normal AccAddrs and ValAddrs
|
||||
func generateAddresses(app *simapp.SimApp, ctx sdk.Context, numAddrs int) ([]sdk.AccAddress, []sdk.ValAddress) {
|
||||
addrDels := simapp.AddTestAddrsIncremental(app, ctx, numAddrs, sdk.NewInt(10000))
|
||||
addrVals := simtestutil.ConvertAddrsToValAddrs(addrDels)
|
||||
|
||||
return addrDels, addrVals
|
||||
}
|
||||
|
||||
func createValidators(t *testing.T, ctx sdk.Context, app *simapp.SimApp, powers []int64) ([]sdk.AccAddress, []sdk.ValAddress, []types.Validator) {
|
||||
addrs := simapp.AddTestAddrsIncremental(app, ctx, 5, app.StakingKeeper.TokensFromConsensusPower(ctx, 300))
|
||||
valAddrs := simtestutil.ConvertAddrsToValAddrs(addrs)
|
||||
pks := simtestutil.CreateTestPubKeys(5)
|
||||
cdc := moduletestutil.MakeTestEncodingConfig().Codec
|
||||
app.StakingKeeper = keeper.NewKeeper(
|
||||
cdc,
|
||||
app.GetKey(types.StoreKey),
|
||||
app.AccountKeeper,
|
||||
app.BankKeeper,
|
||||
authtypes.NewModuleAddress(govtypes.ModuleName).String(),
|
||||
)
|
||||
|
||||
val1 := teststaking.NewValidator(t, valAddrs[0], pks[0])
|
||||
val2 := teststaking.NewValidator(t, valAddrs[1], pks[1])
|
||||
vals := []types.Validator{val1, val2}
|
||||
|
||||
app.StakingKeeper.SetValidator(ctx, val1)
|
||||
app.StakingKeeper.SetValidator(ctx, val2)
|
||||
app.StakingKeeper.SetValidatorByConsAddr(ctx, val1)
|
||||
app.StakingKeeper.SetValidatorByConsAddr(ctx, val2)
|
||||
app.StakingKeeper.SetNewValidatorByPowerIndex(ctx, val1)
|
||||
app.StakingKeeper.SetNewValidatorByPowerIndex(ctx, val2)
|
||||
|
||||
_, err := app.StakingKeeper.Delegate(ctx, addrs[0], app.StakingKeeper.TokensFromConsensusPower(ctx, powers[0]), types.Unbonded, val1, true)
|
||||
require.NoError(t, err)
|
||||
_, err = app.StakingKeeper.Delegate(ctx, addrs[1], app.StakingKeeper.TokensFromConsensusPower(ctx, powers[1]), types.Unbonded, val2, true)
|
||||
require.NoError(t, err)
|
||||
_, err = app.StakingKeeper.Delegate(ctx, addrs[0], app.StakingKeeper.TokensFromConsensusPower(ctx, powers[2]), types.Unbonded, val2, true)
|
||||
require.NoError(t, err)
|
||||
applyValidatorSetUpdates(t, ctx, app.StakingKeeper, -1)
|
||||
|
||||
return addrs, valAddrs, vals
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
package keeper_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"cosmossdk.io/math"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/simapp"
|
||||
simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/bank/testutil"
|
||||
"github.com/cosmos/cosmos-sdk/x/staking/keeper"
|
||||
"github.com/cosmos/cosmos-sdk/x/staking/teststaking"
|
||||
"github.com/cosmos/cosmos-sdk/x/staking/types"
|
||||
)
|
||||
|
||||
func TestUnbondingDelegationsMaxEntries(t *testing.T) {
|
||||
_, app, ctx := createTestInput(t)
|
||||
|
||||
addrDels := simapp.AddTestAddrsIncremental(app, ctx, 1, sdk.NewInt(10000))
|
||||
addrVals := simtestutil.ConvertAddrsToValAddrs(addrDels)
|
||||
|
||||
startTokens := app.StakingKeeper.TokensFromConsensusPower(ctx, 10)
|
||||
|
||||
bondDenom := app.StakingKeeper.BondDenom(ctx)
|
||||
notBondedPool := app.StakingKeeper.GetNotBondedPool(ctx)
|
||||
|
||||
require.NoError(t, testutil.FundModuleAccount(app.BankKeeper, ctx, notBondedPool.GetName(), sdk.NewCoins(sdk.NewCoin(bondDenom, startTokens))))
|
||||
app.AccountKeeper.SetModuleAccount(ctx, notBondedPool)
|
||||
|
||||
// create a validator and a delegator to that validator
|
||||
validator := teststaking.NewValidator(t, addrVals[0], PKs[0])
|
||||
|
||||
validator, issuedShares := validator.AddTokensFromDel(startTokens)
|
||||
require.Equal(t, startTokens, issuedShares.RoundInt())
|
||||
|
||||
validator = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validator, true)
|
||||
require.True(math.IntEq(t, startTokens, validator.BondedTokens()))
|
||||
require.True(t, validator.IsBonded())
|
||||
|
||||
delegation := types.NewDelegation(addrDels[0], addrVals[0], issuedShares)
|
||||
app.StakingKeeper.SetDelegation(ctx, delegation)
|
||||
|
||||
maxEntries := app.StakingKeeper.MaxEntries(ctx)
|
||||
|
||||
oldBonded := app.BankKeeper.GetBalance(ctx, app.StakingKeeper.GetBondedPool(ctx).GetAddress(), bondDenom).Amount
|
||||
oldNotBonded := app.BankKeeper.GetBalance(ctx, app.StakingKeeper.GetNotBondedPool(ctx).GetAddress(), bondDenom).Amount
|
||||
|
||||
// should all pass
|
||||
var completionTime time.Time
|
||||
for i := int64(0); i < int64(maxEntries); i++ {
|
||||
var err error
|
||||
ctx = ctx.WithBlockHeight(i)
|
||||
completionTime, err = app.StakingKeeper.Undelegate(ctx, addrDels[0], addrVals[0], math.LegacyNewDec(1))
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
newBonded := app.BankKeeper.GetBalance(ctx, app.StakingKeeper.GetBondedPool(ctx).GetAddress(), bondDenom).Amount
|
||||
newNotBonded := app.BankKeeper.GetBalance(ctx, app.StakingKeeper.GetNotBondedPool(ctx).GetAddress(), bondDenom).Amount
|
||||
require.True(math.IntEq(t, newBonded, oldBonded.SubRaw(int64(maxEntries))))
|
||||
require.True(math.IntEq(t, newNotBonded, oldNotBonded.AddRaw(int64(maxEntries))))
|
||||
|
||||
oldBonded = app.BankKeeper.GetBalance(ctx, app.StakingKeeper.GetBondedPool(ctx).GetAddress(), bondDenom).Amount
|
||||
oldNotBonded = app.BankKeeper.GetBalance(ctx, app.StakingKeeper.GetNotBondedPool(ctx).GetAddress(), bondDenom).Amount
|
||||
|
||||
// an additional unbond should fail due to max entries
|
||||
_, err := app.StakingKeeper.Undelegate(ctx, addrDels[0], addrVals[0], math.LegacyNewDec(1))
|
||||
require.Error(t, err)
|
||||
|
||||
newBonded = app.BankKeeper.GetBalance(ctx, app.StakingKeeper.GetBondedPool(ctx).GetAddress(), bondDenom).Amount
|
||||
newNotBonded = app.BankKeeper.GetBalance(ctx, app.StakingKeeper.GetNotBondedPool(ctx).GetAddress(), bondDenom).Amount
|
||||
|
||||
require.True(math.IntEq(t, newBonded, oldBonded))
|
||||
require.True(math.IntEq(t, newNotBonded, oldNotBonded))
|
||||
|
||||
// mature unbonding delegations
|
||||
ctx = ctx.WithBlockTime(completionTime)
|
||||
_, err = app.StakingKeeper.CompleteUnbonding(ctx, addrDels[0], addrVals[0])
|
||||
require.NoError(t, err)
|
||||
|
||||
newBonded = app.BankKeeper.GetBalance(ctx, app.StakingKeeper.GetBondedPool(ctx).GetAddress(), bondDenom).Amount
|
||||
newNotBonded = app.BankKeeper.GetBalance(ctx, app.StakingKeeper.GetNotBondedPool(ctx).GetAddress(), bondDenom).Amount
|
||||
require.True(math.IntEq(t, newBonded, oldBonded))
|
||||
require.True(math.IntEq(t, newNotBonded, oldNotBonded.SubRaw(int64(maxEntries))))
|
||||
|
||||
oldNotBonded = app.BankKeeper.GetBalance(ctx, app.StakingKeeper.GetNotBondedPool(ctx).GetAddress(), bondDenom).Amount
|
||||
|
||||
// unbonding should work again
|
||||
_, err = app.StakingKeeper.Undelegate(ctx, addrDels[0], addrVals[0], math.LegacyNewDec(1))
|
||||
require.NoError(t, err)
|
||||
|
||||
newBonded = app.BankKeeper.GetBalance(ctx, app.StakingKeeper.GetBondedPool(ctx).GetAddress(), bondDenom).Amount
|
||||
newNotBonded = app.BankKeeper.GetBalance(ctx, app.StakingKeeper.GetNotBondedPool(ctx).GetAddress(), bondDenom).Amount
|
||||
require.True(math.IntEq(t, newBonded, oldBonded.SubRaw(1)))
|
||||
require.True(math.IntEq(t, newNotBonded, oldNotBonded.AddRaw(1)))
|
||||
}
|
||||
@@ -0,0 +1,220 @@
|
||||
package keeper_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"cosmossdk.io/math"
|
||||
"github.com/stretchr/testify/require"
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
|
||||
|
||||
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
|
||||
"github.com/cosmos/cosmos-sdk/simapp"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/bank/testutil"
|
||||
"github.com/cosmos/cosmos-sdk/x/staking"
|
||||
"github.com/cosmos/cosmos-sdk/x/staking/types"
|
||||
)
|
||||
|
||||
func bootstrapGenesisTest(t *testing.T, numAddrs int) (*simapp.SimApp, sdk.Context, []sdk.AccAddress) {
|
||||
app := simapp.Setup(t, false)
|
||||
ctx := app.BaseApp.NewContext(false, tmproto.Header{})
|
||||
|
||||
addrDels, _ := generateAddresses(app, ctx, numAddrs)
|
||||
return app, ctx, addrDels
|
||||
}
|
||||
|
||||
func TestInitGenesis(t *testing.T) {
|
||||
app, ctx, addrs := bootstrapGenesisTest(t, 10)
|
||||
|
||||
valTokens := app.StakingKeeper.TokensFromConsensusPower(ctx, 1)
|
||||
|
||||
params := app.StakingKeeper.GetParams(ctx)
|
||||
validators := app.StakingKeeper.GetAllValidators(ctx)
|
||||
require.Len(t, validators, 1)
|
||||
var delegations []types.Delegation
|
||||
|
||||
pk0, err := codectypes.NewAnyWithValue(PKs[0])
|
||||
require.NoError(t, err)
|
||||
|
||||
pk1, err := codectypes.NewAnyWithValue(PKs[1])
|
||||
require.NoError(t, err)
|
||||
|
||||
// initialize the validators
|
||||
bondedVal1 := types.Validator{
|
||||
OperatorAddress: sdk.ValAddress(addrs[0]).String(),
|
||||
ConsensusPubkey: pk0,
|
||||
Status: types.Bonded,
|
||||
Tokens: valTokens,
|
||||
DelegatorShares: sdk.NewDecFromInt(valTokens),
|
||||
Description: types.NewDescription("hoop", "", "", "", ""),
|
||||
}
|
||||
bondedVal2 := types.Validator{
|
||||
OperatorAddress: sdk.ValAddress(addrs[1]).String(),
|
||||
ConsensusPubkey: pk1,
|
||||
Status: types.Bonded,
|
||||
Tokens: valTokens,
|
||||
DelegatorShares: sdk.NewDecFromInt(valTokens),
|
||||
Description: types.NewDescription("bloop", "", "", "", ""),
|
||||
}
|
||||
|
||||
// append new bonded validators to the list
|
||||
validators = append(validators, bondedVal1, bondedVal2)
|
||||
|
||||
// mint coins in the bonded pool representing the validators coins
|
||||
i2 := len(validators) - 1 // -1 to exclude genesis validator
|
||||
require.NoError(t,
|
||||
testutil.FundModuleAccount(
|
||||
app.BankKeeper,
|
||||
ctx,
|
||||
types.BondedPoolName,
|
||||
sdk.NewCoins(
|
||||
sdk.NewCoin(params.BondDenom, valTokens.MulRaw((int64)(i2))),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
genesisDelegations := app.StakingKeeper.GetAllDelegations(ctx)
|
||||
delegations = append(delegations, genesisDelegations...)
|
||||
|
||||
genesisState := types.NewGenesisState(params, validators, delegations)
|
||||
vals := app.StakingKeeper.InitGenesis(ctx, genesisState)
|
||||
|
||||
actualGenesis := app.StakingKeeper.ExportGenesis(ctx)
|
||||
require.Equal(t, genesisState.Params, actualGenesis.Params)
|
||||
require.Equal(t, genesisState.Delegations, actualGenesis.Delegations)
|
||||
require.EqualValues(t, app.StakingKeeper.GetAllValidators(ctx), actualGenesis.Validators)
|
||||
|
||||
// Ensure validators have addresses.
|
||||
vals2, err := staking.WriteValidators(ctx, app.StakingKeeper)
|
||||
require.NoError(t, err)
|
||||
|
||||
for _, val := range vals2 {
|
||||
require.NotEmpty(t, val.Address)
|
||||
}
|
||||
|
||||
// now make sure the validators are bonded and intra-tx counters are correct
|
||||
resVal, found := app.StakingKeeper.GetValidator(ctx, sdk.ValAddress(addrs[0]))
|
||||
require.True(t, found)
|
||||
require.Equal(t, types.Bonded, resVal.Status)
|
||||
|
||||
resVal, found = app.StakingKeeper.GetValidator(ctx, sdk.ValAddress(addrs[1]))
|
||||
require.True(t, found)
|
||||
require.Equal(t, types.Bonded, resVal.Status)
|
||||
|
||||
abcivals := make([]abci.ValidatorUpdate, len(vals))
|
||||
|
||||
validators = validators[1:] // remove genesis validator
|
||||
for i, val := range validators {
|
||||
abcivals[i] = val.ABCIValidatorUpdate(app.StakingKeeper.PowerReduction(ctx))
|
||||
}
|
||||
|
||||
require.Equal(t, abcivals, vals)
|
||||
}
|
||||
|
||||
func TestInitGenesis_PoolsBalanceMismatch(t *testing.T) {
|
||||
app := simapp.Setup(t, false)
|
||||
ctx := app.NewContext(false, tmproto.Header{})
|
||||
|
||||
consPub, err := codectypes.NewAnyWithValue(PKs[0])
|
||||
require.NoError(t, err)
|
||||
|
||||
validator := types.Validator{
|
||||
OperatorAddress: sdk.ValAddress("12345678901234567890").String(),
|
||||
ConsensusPubkey: consPub,
|
||||
Jailed: false,
|
||||
Tokens: sdk.NewInt(10),
|
||||
DelegatorShares: sdk.NewDecFromInt(sdk.NewInt(10)),
|
||||
Description: types.NewDescription("bloop", "", "", "", ""),
|
||||
}
|
||||
|
||||
params := types.Params{
|
||||
UnbondingTime: 10000,
|
||||
MaxValidators: 1,
|
||||
MaxEntries: 10,
|
||||
BondDenom: "stake",
|
||||
}
|
||||
|
||||
require.Panics(t, func() {
|
||||
// setting validator status to bonded so the balance counts towards bonded pool
|
||||
validator.Status = types.Bonded
|
||||
app.StakingKeeper.InitGenesis(ctx, &types.GenesisState{
|
||||
Params: params,
|
||||
Validators: []types.Validator{validator},
|
||||
})
|
||||
},
|
||||
"should panic because bonded pool balance is different from bonded pool coins",
|
||||
)
|
||||
|
||||
require.Panics(t, func() {
|
||||
// setting validator status to unbonded so the balance counts towards not bonded pool
|
||||
validator.Status = types.Unbonded
|
||||
app.StakingKeeper.InitGenesis(ctx, &types.GenesisState{
|
||||
Params: params,
|
||||
Validators: []types.Validator{validator},
|
||||
})
|
||||
},
|
||||
"should panic because not bonded pool balance is different from not bonded pool coins",
|
||||
)
|
||||
}
|
||||
|
||||
func TestInitGenesisLargeValidatorSet(t *testing.T) {
|
||||
size := 200
|
||||
require.True(t, size > 100)
|
||||
|
||||
app, ctx, addrs := bootstrapGenesisTest(t, 200)
|
||||
genesisValidators := app.StakingKeeper.GetAllValidators(ctx)
|
||||
|
||||
params := app.StakingKeeper.GetParams(ctx)
|
||||
delegations := []types.Delegation{}
|
||||
validators := make([]types.Validator, size)
|
||||
|
||||
var err error
|
||||
|
||||
bondedPoolAmt := math.ZeroInt()
|
||||
for i := range validators {
|
||||
validators[i], err = types.NewValidator(
|
||||
sdk.ValAddress(addrs[i]),
|
||||
PKs[i],
|
||||
types.NewDescription(fmt.Sprintf("#%d", i), "", "", "", ""),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
validators[i].Status = types.Bonded
|
||||
|
||||
tokens := app.StakingKeeper.TokensFromConsensusPower(ctx, 1)
|
||||
if i < 100 {
|
||||
tokens = app.StakingKeeper.TokensFromConsensusPower(ctx, 2)
|
||||
}
|
||||
|
||||
validators[i].Tokens = tokens
|
||||
validators[i].DelegatorShares = sdk.NewDecFromInt(tokens)
|
||||
|
||||
// add bonded coins
|
||||
bondedPoolAmt = bondedPoolAmt.Add(tokens)
|
||||
}
|
||||
|
||||
validators = append(validators, genesisValidators...)
|
||||
genesisState := types.NewGenesisState(params, validators, delegations)
|
||||
|
||||
// mint coins in the bonded pool representing the validators coins
|
||||
require.NoError(t,
|
||||
testutil.FundModuleAccount(
|
||||
app.BankKeeper,
|
||||
ctx,
|
||||
types.BondedPoolName,
|
||||
sdk.NewCoins(sdk.NewCoin(params.BondDenom, bondedPoolAmt)),
|
||||
),
|
||||
)
|
||||
|
||||
vals := app.StakingKeeper.InitGenesis(ctx, genesisState)
|
||||
|
||||
abcivals := make([]abci.ValidatorUpdate, 100)
|
||||
for i, val := range validators[:100] {
|
||||
abcivals[i] = val.ABCIValidatorUpdate(app.StakingKeeper.PowerReduction(ctx))
|
||||
}
|
||||
|
||||
// remove genesis validator
|
||||
vals = vals[:100]
|
||||
require.Equal(t, abcivals, vals)
|
||||
}
|
||||
@@ -0,0 +1,740 @@
|
||||
package keeper_test
|
||||
|
||||
import (
|
||||
gocontext "context"
|
||||
"fmt"
|
||||
|
||||
simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/types/query"
|
||||
"github.com/cosmos/cosmos-sdk/x/staking/types"
|
||||
)
|
||||
|
||||
func (suite *IntegrationTestSuite) TestGRPCQueryValidators() {
|
||||
queryClient, vals := suite.queryClient, suite.vals
|
||||
var req *types.QueryValidatorsRequest
|
||||
testCases := []struct {
|
||||
msg string
|
||||
malleate func()
|
||||
expPass bool
|
||||
numVals int
|
||||
hasNext bool
|
||||
}{
|
||||
{
|
||||
"empty request",
|
||||
func() {
|
||||
req = &types.QueryValidatorsRequest{}
|
||||
},
|
||||
true,
|
||||
|
||||
len(vals) + 1, // +1 validator from genesis state
|
||||
false,
|
||||
},
|
||||
{
|
||||
"empty status returns all the validators",
|
||||
func() {
|
||||
req = &types.QueryValidatorsRequest{Status: ""}
|
||||
},
|
||||
true,
|
||||
len(vals) + 1, // +1 validator from genesis state
|
||||
false,
|
||||
},
|
||||
{
|
||||
"invalid request",
|
||||
func() {
|
||||
req = &types.QueryValidatorsRequest{Status: "test"}
|
||||
},
|
||||
false,
|
||||
0,
|
||||
false,
|
||||
},
|
||||
{
|
||||
"valid request",
|
||||
func() {
|
||||
req = &types.QueryValidatorsRequest{
|
||||
Status: types.Bonded.String(),
|
||||
Pagination: &query.PageRequest{Limit: 1, CountTotal: true},
|
||||
}
|
||||
},
|
||||
true,
|
||||
1,
|
||||
true,
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
suite.Run(fmt.Sprintf("Case %s", tc.msg), func() {
|
||||
tc.malleate()
|
||||
valsResp, err := queryClient.Validators(gocontext.Background(), req)
|
||||
if tc.expPass {
|
||||
suite.NoError(err)
|
||||
suite.NotNil(valsResp)
|
||||
suite.Equal(tc.numVals, len(valsResp.Validators))
|
||||
suite.Equal(uint64(len(vals))+1, valsResp.Pagination.Total) // +1 validator from genesis state
|
||||
|
||||
if tc.hasNext {
|
||||
suite.NotNil(valsResp.Pagination.NextKey)
|
||||
} else {
|
||||
suite.Nil(valsResp.Pagination.NextKey)
|
||||
}
|
||||
} else {
|
||||
suite.Require().Error(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *IntegrationTestSuite) TestGRPCQueryDelegatorValidators() {
|
||||
app, ctx, queryClient, addrs := suite.app, suite.ctx, suite.queryClient, suite.addrs
|
||||
params := app.StakingKeeper.GetParams(ctx)
|
||||
delValidators := app.StakingKeeper.GetDelegatorValidators(ctx, addrs[0], params.MaxValidators)
|
||||
var req *types.QueryDelegatorValidatorsRequest
|
||||
testCases := []struct {
|
||||
msg string
|
||||
malleate func()
|
||||
expPass bool
|
||||
}{
|
||||
{
|
||||
"empty request",
|
||||
func() {
|
||||
req = &types.QueryDelegatorValidatorsRequest{}
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"valid request",
|
||||
func() {
|
||||
req = &types.QueryDelegatorValidatorsRequest{
|
||||
DelegatorAddr: addrs[0].String(),
|
||||
Pagination: &query.PageRequest{Limit: 1, CountTotal: true},
|
||||
}
|
||||
},
|
||||
true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
suite.Run(fmt.Sprintf("Case %s", tc.msg), func() {
|
||||
tc.malleate()
|
||||
res, err := queryClient.DelegatorValidators(gocontext.Background(), req)
|
||||
if tc.expPass {
|
||||
suite.NoError(err)
|
||||
suite.Equal(1, len(res.Validators))
|
||||
suite.NotNil(res.Pagination.NextKey)
|
||||
suite.Equal(uint64(len(delValidators)), res.Pagination.Total)
|
||||
} else {
|
||||
suite.Error(err)
|
||||
suite.Nil(res)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *IntegrationTestSuite) TestGRPCQueryDelegatorValidator() {
|
||||
queryClient, addrs, vals := suite.queryClient, suite.addrs, suite.vals
|
||||
addr := addrs[1]
|
||||
addrVal, addrVal1 := vals[0].OperatorAddress, vals[1].OperatorAddress
|
||||
var req *types.QueryDelegatorValidatorRequest
|
||||
testCases := []struct {
|
||||
msg string
|
||||
malleate func()
|
||||
expPass bool
|
||||
}{
|
||||
{
|
||||
"empty request",
|
||||
func() {
|
||||
req = &types.QueryDelegatorValidatorRequest{}
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"invalid delegator, validator pair",
|
||||
func() {
|
||||
req = &types.QueryDelegatorValidatorRequest{
|
||||
DelegatorAddr: addr.String(),
|
||||
ValidatorAddr: addrVal,
|
||||
}
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"valid request",
|
||||
func() {
|
||||
req = &types.QueryDelegatorValidatorRequest{
|
||||
DelegatorAddr: addr.String(),
|
||||
ValidatorAddr: addrVal1,
|
||||
}
|
||||
},
|
||||
true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
suite.Run(fmt.Sprintf("Case %s", tc.msg), func() {
|
||||
tc.malleate()
|
||||
res, err := queryClient.DelegatorValidator(gocontext.Background(), req)
|
||||
if tc.expPass {
|
||||
suite.NoError(err)
|
||||
suite.Equal(addrVal1, res.Validator.OperatorAddress)
|
||||
} else {
|
||||
suite.Error(err)
|
||||
suite.Nil(res)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *IntegrationTestSuite) TestGRPCQueryDelegation() {
|
||||
app, ctx, queryClient, addrs, vals := suite.app, suite.ctx, suite.queryClient, suite.addrs, suite.vals
|
||||
addrAcc, addrAcc1 := addrs[0], addrs[1]
|
||||
addrVal := vals[0].OperatorAddress
|
||||
valAddr, err := sdk.ValAddressFromBech32(addrVal)
|
||||
suite.NoError(err)
|
||||
delegation, found := app.StakingKeeper.GetDelegation(ctx, addrAcc, valAddr)
|
||||
suite.True(found)
|
||||
var req *types.QueryDelegationRequest
|
||||
|
||||
testCases := []struct {
|
||||
msg string
|
||||
malleate func()
|
||||
expPass bool
|
||||
}{
|
||||
{
|
||||
"empty request",
|
||||
func() {
|
||||
req = &types.QueryDelegationRequest{}
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"invalid validator, delegator pair",
|
||||
func() {
|
||||
req = &types.QueryDelegationRequest{
|
||||
DelegatorAddr: addrAcc1.String(),
|
||||
ValidatorAddr: addrVal,
|
||||
}
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"valid request",
|
||||
func() {
|
||||
req = &types.QueryDelegationRequest{DelegatorAddr: addrAcc.String(), ValidatorAddr: addrVal}
|
||||
},
|
||||
true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
suite.Run(fmt.Sprintf("Case %s", tc.msg), func() {
|
||||
tc.malleate()
|
||||
res, err := queryClient.Delegation(gocontext.Background(), req)
|
||||
if tc.expPass {
|
||||
suite.Equal(delegation.ValidatorAddress, res.DelegationResponse.Delegation.ValidatorAddress)
|
||||
suite.Equal(delegation.DelegatorAddress, res.DelegationResponse.Delegation.DelegatorAddress)
|
||||
suite.Equal(sdk.NewCoin(sdk.DefaultBondDenom, delegation.Shares.TruncateInt()), res.DelegationResponse.Balance)
|
||||
} else {
|
||||
suite.Error(err)
|
||||
suite.Nil(res)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *IntegrationTestSuite) TestGRPCQueryDelegatorDelegations() {
|
||||
app, ctx, queryClient, addrs, vals := suite.app, suite.ctx, suite.queryClient, suite.addrs, suite.vals
|
||||
addrAcc := addrs[0]
|
||||
addrVal1 := vals[0].OperatorAddress
|
||||
valAddr, err := sdk.ValAddressFromBech32(addrVal1)
|
||||
suite.NoError(err)
|
||||
delegation, found := app.StakingKeeper.GetDelegation(ctx, addrAcc, valAddr)
|
||||
suite.True(found)
|
||||
var req *types.QueryDelegatorDelegationsRequest
|
||||
|
||||
testCases := []struct {
|
||||
msg string
|
||||
malleate func()
|
||||
onSuccess func(suite *IntegrationTestSuite, response *types.QueryDelegatorDelegationsResponse)
|
||||
expErr bool
|
||||
}{
|
||||
{
|
||||
"empty request",
|
||||
func() {
|
||||
req = &types.QueryDelegatorDelegationsRequest{}
|
||||
},
|
||||
func(suite *IntegrationTestSuite, response *types.QueryDelegatorDelegationsResponse) {},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"valid request with no delegations",
|
||||
func() {
|
||||
req = &types.QueryDelegatorDelegationsRequest{DelegatorAddr: addrs[4].String()}
|
||||
},
|
||||
func(suite *IntegrationTestSuite, response *types.QueryDelegatorDelegationsResponse) {
|
||||
suite.Equal(uint64(0), response.Pagination.Total)
|
||||
suite.Len(response.DelegationResponses, 0)
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"valid request",
|
||||
func() {
|
||||
req = &types.QueryDelegatorDelegationsRequest{
|
||||
DelegatorAddr: addrAcc.String(),
|
||||
Pagination: &query.PageRequest{Limit: 1, CountTotal: true},
|
||||
}
|
||||
},
|
||||
func(suite *IntegrationTestSuite, response *types.QueryDelegatorDelegationsResponse) {
|
||||
suite.Equal(uint64(2), response.Pagination.Total)
|
||||
suite.Len(response.DelegationResponses, 1)
|
||||
suite.Equal(sdk.NewCoin(sdk.DefaultBondDenom, delegation.Shares.TruncateInt()), response.DelegationResponses[0].Balance)
|
||||
},
|
||||
false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
suite.Run(fmt.Sprintf("Case %s", tc.msg), func() {
|
||||
tc.malleate()
|
||||
res, err := queryClient.DelegatorDelegations(gocontext.Background(), req)
|
||||
if tc.expErr {
|
||||
suite.Error(err)
|
||||
} else {
|
||||
suite.NoError(err)
|
||||
tc.onSuccess(suite, res)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *IntegrationTestSuite) TestGRPCQueryValidatorDelegations() {
|
||||
app, ctx, queryClient, addrs, vals := suite.app, suite.ctx, suite.queryClient, suite.addrs, suite.vals
|
||||
addrAcc := addrs[0]
|
||||
addrVal1 := vals[1].OperatorAddress
|
||||
valAddrs := simtestutil.ConvertAddrsToValAddrs(addrs)
|
||||
addrVal2 := valAddrs[4]
|
||||
valAddr, err := sdk.ValAddressFromBech32(addrVal1)
|
||||
suite.NoError(err)
|
||||
delegation, found := app.StakingKeeper.GetDelegation(ctx, addrAcc, valAddr)
|
||||
suite.True(found)
|
||||
|
||||
var req *types.QueryValidatorDelegationsRequest
|
||||
testCases := []struct {
|
||||
msg string
|
||||
malleate func()
|
||||
expPass bool
|
||||
expErr bool
|
||||
}{
|
||||
{
|
||||
"empty request",
|
||||
func() {
|
||||
req = &types.QueryValidatorDelegationsRequest{}
|
||||
},
|
||||
false,
|
||||
true,
|
||||
},
|
||||
{
|
||||
"invalid validator delegator pair",
|
||||
func() {
|
||||
req = &types.QueryValidatorDelegationsRequest{ValidatorAddr: addrVal2.String()}
|
||||
},
|
||||
false,
|
||||
false,
|
||||
},
|
||||
{
|
||||
"valid request",
|
||||
func() {
|
||||
req = &types.QueryValidatorDelegationsRequest{
|
||||
ValidatorAddr: addrVal1,
|
||||
Pagination: &query.PageRequest{Limit: 1, CountTotal: true},
|
||||
}
|
||||
},
|
||||
true,
|
||||
false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
suite.Run(fmt.Sprintf("Case %s", tc.msg), func() {
|
||||
tc.malleate()
|
||||
res, err := queryClient.ValidatorDelegations(gocontext.Background(), req)
|
||||
if tc.expPass && !tc.expErr {
|
||||
suite.NoError(err)
|
||||
suite.Len(res.DelegationResponses, 1)
|
||||
suite.NotNil(res.Pagination.NextKey)
|
||||
suite.Equal(uint64(2), res.Pagination.Total)
|
||||
suite.Equal(addrVal1, res.DelegationResponses[0].Delegation.ValidatorAddress)
|
||||
suite.Equal(sdk.NewCoin(sdk.DefaultBondDenom, delegation.Shares.TruncateInt()), res.DelegationResponses[0].Balance)
|
||||
} else if !tc.expPass && !tc.expErr {
|
||||
suite.NoError(err)
|
||||
suite.Nil(res.DelegationResponses)
|
||||
} else {
|
||||
suite.Error(err)
|
||||
suite.Nil(res)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *IntegrationTestSuite) TestGRPCQueryUnbondingDelegation() {
|
||||
app, ctx, queryClient, addrs, vals := suite.app, suite.ctx, suite.queryClient, suite.addrs, suite.vals
|
||||
addrAcc2 := addrs[1]
|
||||
addrVal2 := vals[1].OperatorAddress
|
||||
|
||||
unbondingTokens := app.StakingKeeper.TokensFromConsensusPower(ctx, 2)
|
||||
valAddr, err1 := sdk.ValAddressFromBech32(addrVal2)
|
||||
suite.NoError(err1)
|
||||
_, err := app.StakingKeeper.Undelegate(ctx, addrAcc2, valAddr, sdk.NewDecFromInt(unbondingTokens))
|
||||
suite.NoError(err)
|
||||
|
||||
unbond, found := app.StakingKeeper.GetUnbondingDelegation(ctx, addrAcc2, valAddr)
|
||||
suite.True(found)
|
||||
var req *types.QueryUnbondingDelegationRequest
|
||||
testCases := []struct {
|
||||
msg string
|
||||
malleate func()
|
||||
expPass bool
|
||||
}{
|
||||
{
|
||||
"empty request",
|
||||
func() {
|
||||
req = &types.QueryUnbondingDelegationRequest{}
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"invalid request",
|
||||
func() {
|
||||
req = &types.QueryUnbondingDelegationRequest{}
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"valid request",
|
||||
func() {
|
||||
req = &types.QueryUnbondingDelegationRequest{
|
||||
DelegatorAddr: addrAcc2.String(), ValidatorAddr: addrVal2,
|
||||
}
|
||||
},
|
||||
true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
suite.Run(fmt.Sprintf("Case %s", tc.msg), func() {
|
||||
tc.malleate()
|
||||
res, err := queryClient.UnbondingDelegation(gocontext.Background(), req)
|
||||
if tc.expPass {
|
||||
suite.NotNil(res)
|
||||
suite.Equal(unbond, res.Unbond)
|
||||
} else {
|
||||
suite.Error(err)
|
||||
suite.Nil(res)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *IntegrationTestSuite) TestGRPCQueryDelegatorUnbondingDelegations() {
|
||||
app, ctx, queryClient, addrs, vals := suite.app, suite.ctx, suite.queryClient, suite.addrs, suite.vals
|
||||
addrAcc, addrAcc1 := addrs[0], addrs[1]
|
||||
addrVal, addrVal2 := vals[0].OperatorAddress, vals[1].OperatorAddress
|
||||
|
||||
unbondingTokens := app.StakingKeeper.TokensFromConsensusPower(ctx, 2)
|
||||
valAddr1, err1 := sdk.ValAddressFromBech32(addrVal)
|
||||
suite.NoError(err1)
|
||||
_, err := app.StakingKeeper.Undelegate(ctx, addrAcc, valAddr1, sdk.NewDecFromInt(unbondingTokens))
|
||||
suite.NoError(err)
|
||||
valAddr2, err1 := sdk.ValAddressFromBech32(addrVal2)
|
||||
suite.NoError(err1)
|
||||
_, err = app.StakingKeeper.Undelegate(ctx, addrAcc, valAddr2, sdk.NewDecFromInt(unbondingTokens))
|
||||
suite.NoError(err)
|
||||
|
||||
unbond, found := app.StakingKeeper.GetUnbondingDelegation(ctx, addrAcc, valAddr1)
|
||||
suite.True(found)
|
||||
var req *types.QueryDelegatorUnbondingDelegationsRequest
|
||||
testCases := []struct {
|
||||
msg string
|
||||
malleate func()
|
||||
expPass bool
|
||||
expErr bool
|
||||
}{
|
||||
{
|
||||
"empty request",
|
||||
func() {
|
||||
req = &types.QueryDelegatorUnbondingDelegationsRequest{}
|
||||
},
|
||||
false,
|
||||
true,
|
||||
},
|
||||
{
|
||||
"invalid request",
|
||||
func() {
|
||||
req = &types.QueryDelegatorUnbondingDelegationsRequest{DelegatorAddr: addrAcc1.String()}
|
||||
},
|
||||
false,
|
||||
false,
|
||||
},
|
||||
{
|
||||
"valid request",
|
||||
func() {
|
||||
req = &types.QueryDelegatorUnbondingDelegationsRequest{
|
||||
DelegatorAddr: addrAcc.String(),
|
||||
Pagination: &query.PageRequest{Limit: 1, CountTotal: true},
|
||||
}
|
||||
},
|
||||
true,
|
||||
false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
suite.Run(fmt.Sprintf("Case %s", tc.msg), func() {
|
||||
tc.malleate()
|
||||
res, err := queryClient.DelegatorUnbondingDelegations(gocontext.Background(), req)
|
||||
if tc.expPass && !tc.expErr {
|
||||
suite.NoError(err)
|
||||
suite.NotNil(res.Pagination.NextKey)
|
||||
suite.Equal(uint64(2), res.Pagination.Total)
|
||||
suite.Len(res.UnbondingResponses, 1)
|
||||
suite.Equal(unbond, res.UnbondingResponses[0])
|
||||
} else if !tc.expPass && !tc.expErr {
|
||||
suite.NoError(err)
|
||||
suite.Nil(res.UnbondingResponses)
|
||||
} else {
|
||||
suite.Error(err)
|
||||
suite.Nil(res)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *IntegrationTestSuite) TestGRPCQueryPoolParameters() {
|
||||
app, ctx, queryClient := suite.app, suite.ctx, suite.queryClient
|
||||
bondDenom := sdk.DefaultBondDenom
|
||||
|
||||
// Query pool
|
||||
res, err := queryClient.Pool(gocontext.Background(), &types.QueryPoolRequest{})
|
||||
suite.NoError(err)
|
||||
bondedPool := app.StakingKeeper.GetBondedPool(ctx)
|
||||
notBondedPool := app.StakingKeeper.GetNotBondedPool(ctx)
|
||||
suite.Equal(app.BankKeeper.GetBalance(ctx, notBondedPool.GetAddress(), bondDenom).Amount, res.Pool.NotBondedTokens)
|
||||
suite.Equal(app.BankKeeper.GetBalance(ctx, bondedPool.GetAddress(), bondDenom).Amount, res.Pool.BondedTokens)
|
||||
|
||||
// Query Params
|
||||
resp, err := queryClient.Params(gocontext.Background(), &types.QueryParamsRequest{})
|
||||
suite.NoError(err)
|
||||
suite.Equal(app.StakingKeeper.GetParams(ctx), resp.Params)
|
||||
}
|
||||
|
||||
func (suite *IntegrationTestSuite) TestGRPCQueryHistoricalInfo() {
|
||||
app, ctx, queryClient := suite.app, suite.ctx, suite.queryClient
|
||||
|
||||
hi, found := app.StakingKeeper.GetHistoricalInfo(ctx, 5)
|
||||
suite.True(found)
|
||||
|
||||
var req *types.QueryHistoricalInfoRequest
|
||||
testCases := []struct {
|
||||
msg string
|
||||
malleate func()
|
||||
expPass bool
|
||||
}{
|
||||
{
|
||||
"empty request",
|
||||
func() {
|
||||
req = &types.QueryHistoricalInfoRequest{}
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"invalid request with negative height",
|
||||
func() {
|
||||
req = &types.QueryHistoricalInfoRequest{Height: -1}
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"valid request with old height",
|
||||
func() {
|
||||
req = &types.QueryHistoricalInfoRequest{Height: 4}
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"valid request with current height",
|
||||
func() {
|
||||
req = &types.QueryHistoricalInfoRequest{Height: 5}
|
||||
},
|
||||
true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
suite.Run(fmt.Sprintf("Case %s", tc.msg), func() {
|
||||
tc.malleate()
|
||||
res, err := queryClient.HistoricalInfo(gocontext.Background(), req)
|
||||
if tc.expPass {
|
||||
suite.NoError(err)
|
||||
suite.NotNil(res)
|
||||
suite.True(hi.Equal(res.Hist))
|
||||
} else {
|
||||
suite.Error(err)
|
||||
suite.Nil(res)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *IntegrationTestSuite) TestGRPCQueryRedelegations() {
|
||||
app, ctx, queryClient, addrs, vals := suite.app, suite.ctx, suite.queryClient, suite.addrs, suite.vals
|
||||
|
||||
addrAcc, addrAcc1 := addrs[0], addrs[1]
|
||||
valAddrs := simtestutil.ConvertAddrsToValAddrs(addrs)
|
||||
val1, val2, val3, val4 := vals[0], vals[1], valAddrs[3], valAddrs[4]
|
||||
delAmount := app.StakingKeeper.TokensFromConsensusPower(ctx, 1)
|
||||
_, err := app.StakingKeeper.Delegate(ctx, addrAcc1, delAmount, types.Unbonded, val1, true)
|
||||
suite.NoError(err)
|
||||
applyValidatorSetUpdates(suite.T(), ctx, app.StakingKeeper, -1)
|
||||
|
||||
rdAmount := app.StakingKeeper.TokensFromConsensusPower(ctx, 1)
|
||||
_, err = app.StakingKeeper.BeginRedelegation(ctx, addrAcc1, val1.GetOperator(), val2.GetOperator(), sdk.NewDecFromInt(rdAmount))
|
||||
suite.NoError(err)
|
||||
applyValidatorSetUpdates(suite.T(), ctx, app.StakingKeeper, -1)
|
||||
|
||||
redel, found := app.StakingKeeper.GetRedelegation(ctx, addrAcc1, val1.GetOperator(), val2.GetOperator())
|
||||
suite.True(found)
|
||||
|
||||
var req *types.QueryRedelegationsRequest
|
||||
testCases := []struct {
|
||||
msg string
|
||||
malleate func()
|
||||
expPass bool
|
||||
expErr bool
|
||||
}{
|
||||
{
|
||||
"request redelegations for non existent addr",
|
||||
func() {
|
||||
req = &types.QueryRedelegationsRequest{DelegatorAddr: addrAcc.String()}
|
||||
},
|
||||
false,
|
||||
false,
|
||||
},
|
||||
{
|
||||
"request redelegations with non existent pairs",
|
||||
func() {
|
||||
req = &types.QueryRedelegationsRequest{
|
||||
DelegatorAddr: addrAcc.String(), SrcValidatorAddr: val3.String(),
|
||||
DstValidatorAddr: val4.String(),
|
||||
}
|
||||
},
|
||||
false,
|
||||
true,
|
||||
},
|
||||
{
|
||||
"request redelegations with delegatoraddr, sourceValAddr, destValAddr",
|
||||
func() {
|
||||
req = &types.QueryRedelegationsRequest{
|
||||
DelegatorAddr: addrAcc1.String(), SrcValidatorAddr: val1.OperatorAddress,
|
||||
DstValidatorAddr: val2.OperatorAddress, Pagination: &query.PageRequest{},
|
||||
}
|
||||
},
|
||||
true,
|
||||
false,
|
||||
},
|
||||
{
|
||||
"request redelegations with delegatoraddr and sourceValAddr",
|
||||
func() {
|
||||
req = &types.QueryRedelegationsRequest{
|
||||
DelegatorAddr: addrAcc1.String(), SrcValidatorAddr: val1.OperatorAddress,
|
||||
Pagination: &query.PageRequest{},
|
||||
}
|
||||
},
|
||||
true,
|
||||
false,
|
||||
},
|
||||
{
|
||||
"query redelegations with sourceValAddr only",
|
||||
func() {
|
||||
req = &types.QueryRedelegationsRequest{
|
||||
SrcValidatorAddr: val1.GetOperator().String(),
|
||||
Pagination: &query.PageRequest{Limit: 1, CountTotal: true},
|
||||
}
|
||||
},
|
||||
true,
|
||||
false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
suite.Run(fmt.Sprintf("Case %s", tc.msg), func() {
|
||||
tc.malleate()
|
||||
res, err := queryClient.Redelegations(gocontext.Background(), req)
|
||||
if tc.expPass && !tc.expErr {
|
||||
suite.NoError(err)
|
||||
suite.Len(res.RedelegationResponses, len(redel.Entries))
|
||||
suite.Equal(redel.DelegatorAddress, res.RedelegationResponses[0].Redelegation.DelegatorAddress)
|
||||
suite.Equal(redel.ValidatorSrcAddress, res.RedelegationResponses[0].Redelegation.ValidatorSrcAddress)
|
||||
suite.Equal(redel.ValidatorDstAddress, res.RedelegationResponses[0].Redelegation.ValidatorDstAddress)
|
||||
suite.Len(redel.Entries, len(res.RedelegationResponses[0].Entries))
|
||||
} else if !tc.expPass && !tc.expErr {
|
||||
suite.NoError(err)
|
||||
suite.Nil(res.RedelegationResponses)
|
||||
} else {
|
||||
suite.Error(err)
|
||||
suite.Nil(res)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *IntegrationTestSuite) TestGRPCQueryValidatorUnbondingDelegations() {
|
||||
app, ctx, queryClient, addrs, vals := suite.app, suite.ctx, suite.queryClient, suite.addrs, suite.vals
|
||||
addrAcc1, _ := addrs[0], addrs[1]
|
||||
val1 := vals[0]
|
||||
|
||||
// undelegate
|
||||
undelAmount := app.StakingKeeper.TokensFromConsensusPower(ctx, 2)
|
||||
_, err := app.StakingKeeper.Undelegate(ctx, addrAcc1, val1.GetOperator(), sdk.NewDecFromInt(undelAmount))
|
||||
suite.NoError(err)
|
||||
applyValidatorSetUpdates(suite.T(), ctx, app.StakingKeeper, -1)
|
||||
|
||||
var req *types.QueryValidatorUnbondingDelegationsRequest
|
||||
testCases := []struct {
|
||||
msg string
|
||||
malleate func()
|
||||
expPass bool
|
||||
}{
|
||||
{
|
||||
"empty request",
|
||||
func() {
|
||||
req = &types.QueryValidatorUnbondingDelegationsRequest{}
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"valid request",
|
||||
func() {
|
||||
req = &types.QueryValidatorUnbondingDelegationsRequest{
|
||||
ValidatorAddr: val1.GetOperator().String(),
|
||||
Pagination: &query.PageRequest{Limit: 1, CountTotal: true},
|
||||
}
|
||||
},
|
||||
true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
suite.Run(fmt.Sprintf("Case %s", tc.msg), func() {
|
||||
tc.malleate()
|
||||
res, err := queryClient.ValidatorUnbondingDelegations(gocontext.Background(), req)
|
||||
if tc.expPass {
|
||||
suite.NoError(err)
|
||||
suite.Equal(uint64(1), res.Pagination.Total)
|
||||
suite.Equal(1, len(res.UnbondingResponses))
|
||||
suite.Equal(res.UnbondingResponses[0].ValidatorAddress, val1.OperatorAddress)
|
||||
} else {
|
||||
suite.Error(err)
|
||||
suite.Nil(res)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package keeper_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/stretchr/testify/suite"
|
||||
|
||||
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/baseapp"
|
||||
"github.com/cosmos/cosmos-sdk/simapp"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/staking/keeper"
|
||||
"github.com/cosmos/cosmos-sdk/x/staking/types"
|
||||
)
|
||||
|
||||
type IntegrationTestSuite struct {
|
||||
suite.Suite
|
||||
|
||||
app *simapp.SimApp
|
||||
ctx sdk.Context
|
||||
addrs []sdk.AccAddress
|
||||
vals []types.Validator
|
||||
queryClient types.QueryClient
|
||||
msgServer types.MsgServer
|
||||
}
|
||||
|
||||
func (suite *IntegrationTestSuite) SetupTest() {
|
||||
app := simapp.Setup(suite.T(), false)
|
||||
ctx := app.BaseApp.NewContext(false, tmproto.Header{})
|
||||
|
||||
querier := keeper.Querier{Keeper: app.StakingKeeper}
|
||||
|
||||
queryHelper := baseapp.NewQueryServerTestHelper(ctx, app.InterfaceRegistry())
|
||||
types.RegisterQueryServer(queryHelper, querier)
|
||||
queryClient := types.NewQueryClient(queryHelper)
|
||||
|
||||
suite.msgServer = keeper.NewMsgServerImpl(app.StakingKeeper)
|
||||
|
||||
addrs, _, validators := createValidators(suite.T(), ctx, app, []int64{9, 8, 7})
|
||||
header := tmproto.Header{
|
||||
ChainID: "HelloChain",
|
||||
Height: 5,
|
||||
}
|
||||
|
||||
// sort a copy of the validators, so that original validators does not
|
||||
// have its order changed
|
||||
sortedVals := make([]types.Validator, len(validators))
|
||||
copy(sortedVals, validators)
|
||||
hi := types.NewHistoricalInfo(header, sortedVals, app.StakingKeeper.PowerReduction(ctx))
|
||||
app.StakingKeeper.SetHistoricalInfo(ctx, 5, &hi)
|
||||
|
||||
suite.app, suite.ctx, suite.queryClient, suite.addrs, suite.vals = app, ctx, queryClient, addrs, validators
|
||||
}
|
||||
|
||||
func TestParams(t *testing.T) {
|
||||
app := simapp.Setup(t, false)
|
||||
ctx := app.BaseApp.NewContext(false, tmproto.Header{})
|
||||
|
||||
expParams := types.DefaultParams()
|
||||
|
||||
// check that the empty keeper loads the default
|
||||
resParams := app.StakingKeeper.GetParams(ctx)
|
||||
require.True(t, expParams.Equal(resParams))
|
||||
|
||||
// modify a params, save, and retrieve
|
||||
expParams.MaxValidators = 777
|
||||
app.StakingKeeper.SetParams(ctx, expParams)
|
||||
resParams = app.StakingKeeper.GetParams(ctx)
|
||||
require.True(t, expParams.Equal(resParams))
|
||||
}
|
||||
|
||||
func TestIntegrationTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(IntegrationTestSuite))
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
package keeper_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/simapp"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/bank/testutil"
|
||||
"github.com/cosmos/cosmos-sdk/x/staking/keeper"
|
||||
"github.com/cosmos/cosmos-sdk/x/staking/types"
|
||||
"github.com/stretchr/testify/require"
|
||||
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
|
||||
)
|
||||
|
||||
func TestCancelUnbondingDelegation(t *testing.T) {
|
||||
// setup the app
|
||||
app := simapp.Setup(t, false)
|
||||
ctx := app.BaseApp.NewContext(false, tmproto.Header{})
|
||||
msgServer := keeper.NewMsgServerImpl(app.StakingKeeper)
|
||||
bondDenom := app.StakingKeeper.BondDenom(ctx)
|
||||
|
||||
// set the not bonded pool module account
|
||||
notBondedPool := app.StakingKeeper.GetNotBondedPool(ctx)
|
||||
startTokens := app.StakingKeeper.TokensFromConsensusPower(ctx, 5)
|
||||
|
||||
require.NoError(t, testutil.FundModuleAccount(app.BankKeeper, ctx, notBondedPool.GetName(), sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), startTokens))))
|
||||
app.AccountKeeper.SetModuleAccount(ctx, notBondedPool)
|
||||
|
||||
moduleBalance := app.BankKeeper.GetBalance(ctx, notBondedPool.GetAddress(), app.StakingKeeper.BondDenom(ctx))
|
||||
require.Equal(t, sdk.NewInt64Coin(bondDenom, startTokens.Int64()), moduleBalance)
|
||||
|
||||
// accounts
|
||||
delAddrs := simapp.AddTestAddrsIncremental(app, ctx, 2, sdk.NewInt(10000))
|
||||
validators := app.StakingKeeper.GetValidators(ctx, 10)
|
||||
require.Equal(t, len(validators), 1)
|
||||
|
||||
validatorAddr, err := sdk.ValAddressFromBech32(validators[0].OperatorAddress)
|
||||
require.NoError(t, err)
|
||||
delegatorAddr := delAddrs[0]
|
||||
|
||||
// setting the ubd entry
|
||||
unbondingAmount := sdk.NewInt64Coin(app.StakingKeeper.BondDenom(ctx), 5)
|
||||
ubd := types.NewUnbondingDelegation(
|
||||
delegatorAddr, validatorAddr, 10,
|
||||
ctx.BlockTime().Add(time.Minute*10),
|
||||
unbondingAmount.Amount,
|
||||
)
|
||||
|
||||
// set and retrieve a record
|
||||
app.StakingKeeper.SetUnbondingDelegation(ctx, ubd)
|
||||
resUnbond, found := app.StakingKeeper.GetUnbondingDelegation(ctx, delegatorAddr, validatorAddr)
|
||||
require.True(t, found)
|
||||
require.Equal(t, ubd, resUnbond)
|
||||
|
||||
testCases := []struct {
|
||||
Name string
|
||||
ExceptErr bool
|
||||
req types.MsgCancelUnbondingDelegation
|
||||
}{
|
||||
{
|
||||
Name: "invalid height",
|
||||
ExceptErr: true,
|
||||
req: types.MsgCancelUnbondingDelegation{
|
||||
DelegatorAddress: resUnbond.DelegatorAddress,
|
||||
ValidatorAddress: resUnbond.ValidatorAddress,
|
||||
Amount: sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), sdk.NewInt(4)),
|
||||
CreationHeight: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "invalid coin",
|
||||
ExceptErr: true,
|
||||
req: types.MsgCancelUnbondingDelegation{
|
||||
DelegatorAddress: resUnbond.DelegatorAddress,
|
||||
ValidatorAddress: resUnbond.ValidatorAddress,
|
||||
Amount: sdk.NewCoin("dump_coin", sdk.NewInt(4)),
|
||||
CreationHeight: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "validator not exists",
|
||||
ExceptErr: true,
|
||||
req: types.MsgCancelUnbondingDelegation{
|
||||
DelegatorAddress: resUnbond.DelegatorAddress,
|
||||
ValidatorAddress: sdk.ValAddress(sdk.AccAddress("asdsad")).String(),
|
||||
Amount: unbondingAmount,
|
||||
CreationHeight: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "invalid delegator address",
|
||||
ExceptErr: true,
|
||||
req: types.MsgCancelUnbondingDelegation{
|
||||
DelegatorAddress: "invalid_delegator_addrtess",
|
||||
ValidatorAddress: resUnbond.ValidatorAddress,
|
||||
Amount: unbondingAmount,
|
||||
CreationHeight: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "invalid amount",
|
||||
ExceptErr: true,
|
||||
req: types.MsgCancelUnbondingDelegation{
|
||||
DelegatorAddress: resUnbond.DelegatorAddress,
|
||||
ValidatorAddress: resUnbond.ValidatorAddress,
|
||||
Amount: unbondingAmount.Add(sdk.NewInt64Coin(bondDenom, 10)),
|
||||
CreationHeight: 10,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "success",
|
||||
ExceptErr: false,
|
||||
req: types.MsgCancelUnbondingDelegation{
|
||||
DelegatorAddress: resUnbond.DelegatorAddress,
|
||||
ValidatorAddress: resUnbond.ValidatorAddress,
|
||||
Amount: unbondingAmount.Sub(sdk.NewInt64Coin(bondDenom, 1)),
|
||||
CreationHeight: 10,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "success",
|
||||
ExceptErr: false,
|
||||
req: types.MsgCancelUnbondingDelegation{
|
||||
DelegatorAddress: resUnbond.DelegatorAddress,
|
||||
ValidatorAddress: resUnbond.ValidatorAddress,
|
||||
Amount: unbondingAmount.Sub(unbondingAmount.Sub(sdk.NewInt64Coin(bondDenom, 1))),
|
||||
CreationHeight: 10,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.Name, func(t *testing.T) {
|
||||
_, err := msgServer.CancelUnbondingDelegation(ctx, &testCase.req)
|
||||
if testCase.ExceptErr {
|
||||
require.Error(t, err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
balanceForNotBondedPool := app.BankKeeper.GetBalance(ctx, sdk.AccAddress(notBondedPool.GetAddress()), bondDenom)
|
||||
require.Equal(t, balanceForNotBondedPool, moduleBalance.Sub(testCase.req.Amount))
|
||||
moduleBalance = moduleBalance.Sub(testCase.req.Amount)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,585 @@
|
||||
package keeper_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"cosmossdk.io/math"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/simapp"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/bank/testutil"
|
||||
"github.com/cosmos/cosmos-sdk/x/staking/keeper"
|
||||
"github.com/cosmos/cosmos-sdk/x/staking/teststaking"
|
||||
"github.com/cosmos/cosmos-sdk/x/staking/types"
|
||||
)
|
||||
|
||||
// bootstrapSlashTest creates 3 validators and bootstrap the app.
|
||||
func bootstrapSlashTest(t *testing.T, power int64) (*simapp.SimApp, sdk.Context, []sdk.AccAddress, []sdk.ValAddress) {
|
||||
_, app, ctx := createTestInput(t)
|
||||
|
||||
addrDels, addrVals := generateAddresses(app, ctx, 100)
|
||||
|
||||
amt := app.StakingKeeper.TokensFromConsensusPower(ctx, power)
|
||||
totalSupply := sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), amt.MulRaw(int64(len(addrDels)))))
|
||||
|
||||
notBondedPool := app.StakingKeeper.GetNotBondedPool(ctx)
|
||||
require.NoError(t, testutil.FundModuleAccount(app.BankKeeper, ctx, notBondedPool.GetName(), totalSupply))
|
||||
|
||||
app.AccountKeeper.SetModuleAccount(ctx, notBondedPool)
|
||||
|
||||
numVals := int64(3)
|
||||
bondedCoins := sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), amt.MulRaw(numVals)))
|
||||
bondedPool := app.StakingKeeper.GetBondedPool(ctx)
|
||||
|
||||
// set bonded pool balance
|
||||
app.AccountKeeper.SetModuleAccount(ctx, bondedPool)
|
||||
require.NoError(t, testutil.FundModuleAccount(app.BankKeeper, ctx, bondedPool.GetName(), bondedCoins))
|
||||
|
||||
for i := int64(0); i < numVals; i++ {
|
||||
validator := teststaking.NewValidator(t, addrVals[i], PKs[i])
|
||||
validator, _ = validator.AddTokensFromDel(amt)
|
||||
validator = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validator, true)
|
||||
app.StakingKeeper.SetValidatorByConsAddr(ctx, validator)
|
||||
}
|
||||
|
||||
return app, ctx, addrDels, addrVals
|
||||
}
|
||||
|
||||
// tests slashUnbondingDelegation
|
||||
func TestSlashUnbondingDelegation(t *testing.T) {
|
||||
app, ctx, addrDels, addrVals := bootstrapSlashTest(t, 10)
|
||||
|
||||
fraction := sdk.NewDecWithPrec(5, 1)
|
||||
|
||||
// set an unbonding delegation with expiration timestamp (beyond which the
|
||||
// unbonding delegation shouldn't be slashed)
|
||||
ubd := types.NewUnbondingDelegation(addrDels[0], addrVals[0], 0,
|
||||
time.Unix(5, 0), sdk.NewInt(10))
|
||||
|
||||
app.StakingKeeper.SetUnbondingDelegation(ctx, ubd)
|
||||
|
||||
// unbonding started prior to the infraction height, stakw didn't contribute
|
||||
slashAmount := app.StakingKeeper.SlashUnbondingDelegation(ctx, ubd, 1, fraction)
|
||||
require.True(t, slashAmount.Equal(sdk.NewInt(0)))
|
||||
|
||||
// after the expiration time, no longer eligible for slashing
|
||||
ctx = ctx.WithBlockHeader(tmproto.Header{Time: time.Unix(10, 0)})
|
||||
app.StakingKeeper.SetUnbondingDelegation(ctx, ubd)
|
||||
slashAmount = app.StakingKeeper.SlashUnbondingDelegation(ctx, ubd, 0, fraction)
|
||||
require.True(t, slashAmount.Equal(sdk.NewInt(0)))
|
||||
|
||||
// test valid slash, before expiration timestamp and to which stake contributed
|
||||
notBondedPool := app.StakingKeeper.GetNotBondedPool(ctx)
|
||||
oldUnbondedPoolBalances := app.BankKeeper.GetAllBalances(ctx, notBondedPool.GetAddress())
|
||||
ctx = ctx.WithBlockHeader(tmproto.Header{Time: time.Unix(0, 0)})
|
||||
app.StakingKeeper.SetUnbondingDelegation(ctx, ubd)
|
||||
slashAmount = app.StakingKeeper.SlashUnbondingDelegation(ctx, ubd, 0, fraction)
|
||||
require.True(t, slashAmount.Equal(sdk.NewInt(5)))
|
||||
ubd, found := app.StakingKeeper.GetUnbondingDelegation(ctx, addrDels[0], addrVals[0])
|
||||
require.True(t, found)
|
||||
require.Len(t, ubd.Entries, 1)
|
||||
|
||||
// initial balance unchanged
|
||||
require.Equal(t, sdk.NewInt(10), ubd.Entries[0].InitialBalance)
|
||||
|
||||
// balance decreased
|
||||
require.Equal(t, sdk.NewInt(5), ubd.Entries[0].Balance)
|
||||
newUnbondedPoolBalances := app.BankKeeper.GetAllBalances(ctx, notBondedPool.GetAddress())
|
||||
diffTokens := oldUnbondedPoolBalances.Sub(newUnbondedPoolBalances...)
|
||||
require.True(t, diffTokens.AmountOf(app.StakingKeeper.BondDenom(ctx)).Equal(sdk.NewInt(5)))
|
||||
}
|
||||
|
||||
// tests slashRedelegation
|
||||
func TestSlashRedelegation(t *testing.T) {
|
||||
app, ctx, addrDels, addrVals := bootstrapSlashTest(t, 10)
|
||||
fraction := sdk.NewDecWithPrec(5, 1)
|
||||
|
||||
// add bonded tokens to pool for (re)delegations
|
||||
startCoins := sdk.NewCoins(sdk.NewInt64Coin(app.StakingKeeper.BondDenom(ctx), 15))
|
||||
bondedPool := app.StakingKeeper.GetBondedPool(ctx)
|
||||
balances := app.BankKeeper.GetAllBalances(ctx, bondedPool.GetAddress())
|
||||
|
||||
require.NoError(t, testutil.FundModuleAccount(app.BankKeeper, ctx, bondedPool.GetName(), startCoins))
|
||||
app.AccountKeeper.SetModuleAccount(ctx, bondedPool)
|
||||
|
||||
// set a redelegation with an expiration timestamp beyond which the
|
||||
// redelegation shouldn't be slashed
|
||||
rd := types.NewRedelegation(addrDels[0], addrVals[0], addrVals[1], 0,
|
||||
time.Unix(5, 0), sdk.NewInt(10), math.LegacyNewDec(10))
|
||||
|
||||
app.StakingKeeper.SetRedelegation(ctx, rd)
|
||||
|
||||
// set the associated delegation
|
||||
del := types.NewDelegation(addrDels[0], addrVals[1], math.LegacyNewDec(10))
|
||||
app.StakingKeeper.SetDelegation(ctx, del)
|
||||
|
||||
// started redelegating prior to the current height, stake didn't contribute to infraction
|
||||
validator, found := app.StakingKeeper.GetValidator(ctx, addrVals[1])
|
||||
require.True(t, found)
|
||||
slashAmount := app.StakingKeeper.SlashRedelegation(ctx, validator, rd, 1, fraction)
|
||||
require.True(t, slashAmount.Equal(sdk.NewInt(0)))
|
||||
|
||||
// after the expiration time, no longer eligible for slashing
|
||||
ctx = ctx.WithBlockHeader(tmproto.Header{Time: time.Unix(10, 0)})
|
||||
app.StakingKeeper.SetRedelegation(ctx, rd)
|
||||
validator, found = app.StakingKeeper.GetValidator(ctx, addrVals[1])
|
||||
require.True(t, found)
|
||||
slashAmount = app.StakingKeeper.SlashRedelegation(ctx, validator, rd, 0, fraction)
|
||||
require.True(t, slashAmount.Equal(sdk.NewInt(0)))
|
||||
|
||||
balances = app.BankKeeper.GetAllBalances(ctx, bondedPool.GetAddress())
|
||||
|
||||
// test valid slash, before expiration timestamp and to which stake contributed
|
||||
ctx = ctx.WithBlockHeader(tmproto.Header{Time: time.Unix(0, 0)})
|
||||
app.StakingKeeper.SetRedelegation(ctx, rd)
|
||||
validator, found = app.StakingKeeper.GetValidator(ctx, addrVals[1])
|
||||
require.True(t, found)
|
||||
slashAmount = app.StakingKeeper.SlashRedelegation(ctx, validator, rd, 0, fraction)
|
||||
require.True(t, slashAmount.Equal(sdk.NewInt(5)))
|
||||
rd, found = app.StakingKeeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1])
|
||||
require.True(t, found)
|
||||
require.Len(t, rd.Entries, 1)
|
||||
|
||||
// end block
|
||||
applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 1)
|
||||
|
||||
// initialbalance unchanged
|
||||
require.Equal(t, sdk.NewInt(10), rd.Entries[0].InitialBalance)
|
||||
|
||||
// shares decreased
|
||||
del, found = app.StakingKeeper.GetDelegation(ctx, addrDels[0], addrVals[1])
|
||||
require.True(t, found)
|
||||
require.Equal(t, int64(5), del.Shares.RoundInt64())
|
||||
|
||||
// pool bonded tokens should decrease
|
||||
burnedCoins := sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), slashAmount))
|
||||
require.Equal(t, balances.Sub(burnedCoins...), app.BankKeeper.GetAllBalances(ctx, bondedPool.GetAddress()))
|
||||
}
|
||||
|
||||
// test slash at a negative height
|
||||
// this just represents pre-genesis and should have the same effect as slashing at height 0
|
||||
func TestSlashAtNegativeHeight(t *testing.T) {
|
||||
app, ctx, _, _ := bootstrapSlashTest(t, 10)
|
||||
consAddr := sdk.ConsAddress(PKs[0].Address())
|
||||
fraction := sdk.NewDecWithPrec(5, 1)
|
||||
|
||||
bondedPool := app.StakingKeeper.GetBondedPool(ctx)
|
||||
oldBondedPoolBalances := app.BankKeeper.GetAllBalances(ctx, bondedPool.GetAddress())
|
||||
|
||||
validator, found := app.StakingKeeper.GetValidatorByConsAddr(ctx, consAddr)
|
||||
require.True(t, found)
|
||||
app.StakingKeeper.Slash(ctx, consAddr, -2, 10, fraction)
|
||||
|
||||
// read updated state
|
||||
validator, found = app.StakingKeeper.GetValidatorByConsAddr(ctx, consAddr)
|
||||
require.True(t, found)
|
||||
|
||||
// end block
|
||||
applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 1)
|
||||
|
||||
validator, found = app.StakingKeeper.GetValidator(ctx, validator.GetOperator())
|
||||
require.True(t, found)
|
||||
// power decreased
|
||||
require.Equal(t, int64(5), validator.GetConsensusPower(app.StakingKeeper.PowerReduction(ctx)))
|
||||
|
||||
// pool bonded shares decreased
|
||||
newBondedPoolBalances := app.BankKeeper.GetAllBalances(ctx, bondedPool.GetAddress())
|
||||
diffTokens := oldBondedPoolBalances.Sub(newBondedPoolBalances...).AmountOf(app.StakingKeeper.BondDenom(ctx))
|
||||
require.Equal(t, app.StakingKeeper.TokensFromConsensusPower(ctx, 5).String(), diffTokens.String())
|
||||
}
|
||||
|
||||
// tests Slash at the current height
|
||||
func TestSlashValidatorAtCurrentHeight(t *testing.T) {
|
||||
app, ctx, _, _ := bootstrapSlashTest(t, 10)
|
||||
consAddr := sdk.ConsAddress(PKs[0].Address())
|
||||
fraction := sdk.NewDecWithPrec(5, 1)
|
||||
|
||||
bondedPool := app.StakingKeeper.GetBondedPool(ctx)
|
||||
oldBondedPoolBalances := app.BankKeeper.GetAllBalances(ctx, bondedPool.GetAddress())
|
||||
|
||||
validator, found := app.StakingKeeper.GetValidatorByConsAddr(ctx, consAddr)
|
||||
require.True(t, found)
|
||||
app.StakingKeeper.Slash(ctx, consAddr, ctx.BlockHeight(), 10, fraction)
|
||||
|
||||
// read updated state
|
||||
validator, found = app.StakingKeeper.GetValidatorByConsAddr(ctx, consAddr)
|
||||
require.True(t, found)
|
||||
|
||||
// end block
|
||||
applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 1)
|
||||
|
||||
validator, found = app.StakingKeeper.GetValidator(ctx, validator.GetOperator())
|
||||
assert.True(t, found)
|
||||
// power decreased
|
||||
require.Equal(t, int64(5), validator.GetConsensusPower(app.StakingKeeper.PowerReduction(ctx)))
|
||||
|
||||
// pool bonded shares decreased
|
||||
newBondedPoolBalances := app.BankKeeper.GetAllBalances(ctx, bondedPool.GetAddress())
|
||||
diffTokens := oldBondedPoolBalances.Sub(newBondedPoolBalances...).AmountOf(app.StakingKeeper.BondDenom(ctx))
|
||||
require.Equal(t, app.StakingKeeper.TokensFromConsensusPower(ctx, 5).String(), diffTokens.String())
|
||||
}
|
||||
|
||||
// tests Slash at a previous height with an unbonding delegation
|
||||
func TestSlashWithUnbondingDelegation(t *testing.T) {
|
||||
app, ctx, addrDels, addrVals := bootstrapSlashTest(t, 10)
|
||||
|
||||
consAddr := sdk.ConsAddress(PKs[0].Address())
|
||||
fraction := sdk.NewDecWithPrec(5, 1)
|
||||
|
||||
// set an unbonding delegation with expiration timestamp beyond which the
|
||||
// unbonding delegation shouldn't be slashed
|
||||
ubdTokens := app.StakingKeeper.TokensFromConsensusPower(ctx, 4)
|
||||
ubd := types.NewUnbondingDelegation(addrDels[0], addrVals[0], 11, time.Unix(0, 0), ubdTokens)
|
||||
app.StakingKeeper.SetUnbondingDelegation(ctx, ubd)
|
||||
|
||||
// slash validator for the first time
|
||||
ctx = ctx.WithBlockHeight(12)
|
||||
bondedPool := app.StakingKeeper.GetBondedPool(ctx)
|
||||
oldBondedPoolBalances := app.BankKeeper.GetAllBalances(ctx, bondedPool.GetAddress())
|
||||
|
||||
validator, found := app.StakingKeeper.GetValidatorByConsAddr(ctx, consAddr)
|
||||
require.True(t, found)
|
||||
app.StakingKeeper.Slash(ctx, consAddr, 10, 10, fraction)
|
||||
|
||||
// end block
|
||||
applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 1)
|
||||
|
||||
// read updating unbonding delegation
|
||||
ubd, found = app.StakingKeeper.GetUnbondingDelegation(ctx, addrDels[0], addrVals[0])
|
||||
require.True(t, found)
|
||||
require.Len(t, ubd.Entries, 1)
|
||||
|
||||
// balance decreased
|
||||
require.Equal(t, app.StakingKeeper.TokensFromConsensusPower(ctx, 2), ubd.Entries[0].Balance)
|
||||
|
||||
// bonded tokens burned
|
||||
newBondedPoolBalances := app.BankKeeper.GetAllBalances(ctx, bondedPool.GetAddress())
|
||||
diffTokens := oldBondedPoolBalances.Sub(newBondedPoolBalances...).AmountOf(app.StakingKeeper.BondDenom(ctx))
|
||||
require.Equal(t, app.StakingKeeper.TokensFromConsensusPower(ctx, 3), diffTokens)
|
||||
|
||||
// read updated validator
|
||||
validator, found = app.StakingKeeper.GetValidatorByConsAddr(ctx, consAddr)
|
||||
require.True(t, found)
|
||||
|
||||
// power decreased by 3 - 6 stake originally bonded at the time of infraction
|
||||
// was still bonded at the time of discovery and was slashed by half, 4 stake
|
||||
// bonded at the time of discovery hadn't been bonded at the time of infraction
|
||||
// and wasn't slashed
|
||||
require.Equal(t, int64(7), validator.GetConsensusPower(app.StakingKeeper.PowerReduction(ctx)))
|
||||
|
||||
// slash validator again
|
||||
ctx = ctx.WithBlockHeight(13)
|
||||
app.StakingKeeper.Slash(ctx, consAddr, 9, 10, fraction)
|
||||
|
||||
ubd, found = app.StakingKeeper.GetUnbondingDelegation(ctx, addrDels[0], addrVals[0])
|
||||
require.True(t, found)
|
||||
require.Len(t, ubd.Entries, 1)
|
||||
|
||||
// balance decreased again
|
||||
require.Equal(t, sdk.NewInt(0), ubd.Entries[0].Balance)
|
||||
|
||||
// bonded tokens burned again
|
||||
newBondedPoolBalances = app.BankKeeper.GetAllBalances(ctx, bondedPool.GetAddress())
|
||||
diffTokens = oldBondedPoolBalances.Sub(newBondedPoolBalances...).AmountOf(app.StakingKeeper.BondDenom(ctx))
|
||||
require.Equal(t, app.StakingKeeper.TokensFromConsensusPower(ctx, 6), diffTokens)
|
||||
|
||||
// read updated validator
|
||||
validator, found = app.StakingKeeper.GetValidatorByConsAddr(ctx, consAddr)
|
||||
require.True(t, found)
|
||||
|
||||
// power decreased by 3 again
|
||||
require.Equal(t, int64(4), validator.GetConsensusPower(app.StakingKeeper.PowerReduction(ctx)))
|
||||
|
||||
// slash validator again
|
||||
// all originally bonded stake has been slashed, so this will have no effect
|
||||
// on the unbonding delegation, but it will slash stake bonded since the infraction
|
||||
// this may not be the desirable behaviour, ref https://github.com/cosmos/cosmos-sdk/issues/1440
|
||||
ctx = ctx.WithBlockHeight(13)
|
||||
app.StakingKeeper.Slash(ctx, consAddr, 9, 10, fraction)
|
||||
|
||||
ubd, found = app.StakingKeeper.GetUnbondingDelegation(ctx, addrDels[0], addrVals[0])
|
||||
require.True(t, found)
|
||||
require.Len(t, ubd.Entries, 1)
|
||||
|
||||
// balance unchanged
|
||||
require.Equal(t, sdk.NewInt(0), ubd.Entries[0].Balance)
|
||||
|
||||
// bonded tokens burned again
|
||||
newBondedPoolBalances = app.BankKeeper.GetAllBalances(ctx, bondedPool.GetAddress())
|
||||
diffTokens = oldBondedPoolBalances.Sub(newBondedPoolBalances...).AmountOf(app.StakingKeeper.BondDenom(ctx))
|
||||
require.Equal(t, app.StakingKeeper.TokensFromConsensusPower(ctx, 9), diffTokens)
|
||||
|
||||
// read updated validator
|
||||
validator, found = app.StakingKeeper.GetValidatorByConsAddr(ctx, consAddr)
|
||||
require.True(t, found)
|
||||
|
||||
// power decreased by 3 again
|
||||
require.Equal(t, int64(1), validator.GetConsensusPower(app.StakingKeeper.PowerReduction(ctx)))
|
||||
|
||||
// slash validator again
|
||||
// all originally bonded stake has been slashed, so this will have no effect
|
||||
// on the unbonding delegation, but it will slash stake bonded since the infraction
|
||||
// this may not be the desirable behaviour, ref https://github.com/cosmos/cosmos-sdk/issues/1440
|
||||
ctx = ctx.WithBlockHeight(13)
|
||||
app.StakingKeeper.Slash(ctx, consAddr, 9, 10, fraction)
|
||||
|
||||
ubd, found = app.StakingKeeper.GetUnbondingDelegation(ctx, addrDels[0], addrVals[0])
|
||||
require.True(t, found)
|
||||
require.Len(t, ubd.Entries, 1)
|
||||
|
||||
// balance unchanged
|
||||
require.Equal(t, sdk.NewInt(0), ubd.Entries[0].Balance)
|
||||
|
||||
// just 1 bonded token burned again since that's all the validator now has
|
||||
newBondedPoolBalances = app.BankKeeper.GetAllBalances(ctx, bondedPool.GetAddress())
|
||||
diffTokens = oldBondedPoolBalances.Sub(newBondedPoolBalances...).AmountOf(app.StakingKeeper.BondDenom(ctx))
|
||||
require.Equal(t, app.StakingKeeper.TokensFromConsensusPower(ctx, 10), diffTokens)
|
||||
|
||||
// apply TM updates
|
||||
applyValidatorSetUpdates(t, ctx, app.StakingKeeper, -1)
|
||||
|
||||
// read updated validator
|
||||
// power decreased by 1 again, validator is out of stake
|
||||
// validator should be in unbonding period
|
||||
validator, _ = app.StakingKeeper.GetValidatorByConsAddr(ctx, consAddr)
|
||||
require.Equal(t, validator.GetStatus(), types.Unbonding)
|
||||
}
|
||||
|
||||
// tests Slash at a previous height with a redelegation
|
||||
func TestSlashWithRedelegation(t *testing.T) {
|
||||
app, ctx, addrDels, addrVals := bootstrapSlashTest(t, 10)
|
||||
consAddr := sdk.ConsAddress(PKs[0].Address())
|
||||
fraction := sdk.NewDecWithPrec(5, 1)
|
||||
bondDenom := app.StakingKeeper.BondDenom(ctx)
|
||||
|
||||
// set a redelegation
|
||||
rdTokens := app.StakingKeeper.TokensFromConsensusPower(ctx, 6)
|
||||
rd := types.NewRedelegation(addrDels[0], addrVals[0], addrVals[1], 11, time.Unix(0, 0), rdTokens, sdk.NewDecFromInt(rdTokens))
|
||||
app.StakingKeeper.SetRedelegation(ctx, rd)
|
||||
|
||||
// set the associated delegation
|
||||
del := types.NewDelegation(addrDels[0], addrVals[1], sdk.NewDecFromInt(rdTokens))
|
||||
app.StakingKeeper.SetDelegation(ctx, del)
|
||||
|
||||
// update bonded tokens
|
||||
bondedPool := app.StakingKeeper.GetBondedPool(ctx)
|
||||
notBondedPool := app.StakingKeeper.GetNotBondedPool(ctx)
|
||||
rdCoins := sdk.NewCoins(sdk.NewCoin(bondDenom, rdTokens.MulRaw(2)))
|
||||
|
||||
require.NoError(t, testutil.FundModuleAccount(app.BankKeeper, ctx, bondedPool.GetName(), rdCoins))
|
||||
|
||||
app.AccountKeeper.SetModuleAccount(ctx, bondedPool)
|
||||
|
||||
oldBonded := app.BankKeeper.GetBalance(ctx, bondedPool.GetAddress(), bondDenom).Amount
|
||||
oldNotBonded := app.BankKeeper.GetBalance(ctx, notBondedPool.GetAddress(), bondDenom).Amount
|
||||
|
||||
// slash validator
|
||||
ctx = ctx.WithBlockHeight(12)
|
||||
validator, found := app.StakingKeeper.GetValidatorByConsAddr(ctx, consAddr)
|
||||
require.True(t, found)
|
||||
|
||||
require.NotPanics(t, func() { app.StakingKeeper.Slash(ctx, consAddr, 10, 10, fraction) })
|
||||
burnAmount := sdk.NewDecFromInt(app.StakingKeeper.TokensFromConsensusPower(ctx, 10)).Mul(fraction).TruncateInt()
|
||||
|
||||
bondedPool = app.StakingKeeper.GetBondedPool(ctx)
|
||||
notBondedPool = app.StakingKeeper.GetNotBondedPool(ctx)
|
||||
|
||||
// burn bonded tokens from only from delegations
|
||||
bondedPoolBalance := app.BankKeeper.GetBalance(ctx, bondedPool.GetAddress(), bondDenom).Amount
|
||||
require.True(math.IntEq(t, oldBonded.Sub(burnAmount), bondedPoolBalance))
|
||||
|
||||
notBondedPoolBalance := app.BankKeeper.GetBalance(ctx, notBondedPool.GetAddress(), bondDenom).Amount
|
||||
require.True(math.IntEq(t, oldNotBonded, notBondedPoolBalance))
|
||||
oldBonded = app.BankKeeper.GetBalance(ctx, bondedPool.GetAddress(), bondDenom).Amount
|
||||
|
||||
// read updating redelegation
|
||||
rd, found = app.StakingKeeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1])
|
||||
require.True(t, found)
|
||||
require.Len(t, rd.Entries, 1)
|
||||
// read updated validator
|
||||
validator, found = app.StakingKeeper.GetValidatorByConsAddr(ctx, consAddr)
|
||||
require.True(t, found)
|
||||
// power decreased by 2 - 4 stake originally bonded at the time of infraction
|
||||
// was still bonded at the time of discovery and was slashed by half, 4 stake
|
||||
// bonded at the time of discovery hadn't been bonded at the time of infraction
|
||||
// and wasn't slashed
|
||||
require.Equal(t, int64(8), validator.GetConsensusPower(app.StakingKeeper.PowerReduction(ctx)))
|
||||
|
||||
// slash the validator again
|
||||
validator, found = app.StakingKeeper.GetValidatorByConsAddr(ctx, consAddr)
|
||||
require.True(t, found)
|
||||
|
||||
require.NotPanics(t, func() { app.StakingKeeper.Slash(ctx, consAddr, 10, 10, math.LegacyOneDec()) })
|
||||
burnAmount = app.StakingKeeper.TokensFromConsensusPower(ctx, 7)
|
||||
|
||||
// read updated pool
|
||||
bondedPool = app.StakingKeeper.GetBondedPool(ctx)
|
||||
notBondedPool = app.StakingKeeper.GetNotBondedPool(ctx)
|
||||
|
||||
// seven bonded tokens burned
|
||||
bondedPoolBalance = app.BankKeeper.GetBalance(ctx, bondedPool.GetAddress(), bondDenom).Amount
|
||||
require.True(math.IntEq(t, oldBonded.Sub(burnAmount), bondedPoolBalance))
|
||||
require.True(math.IntEq(t, oldNotBonded, notBondedPoolBalance))
|
||||
|
||||
bondedPoolBalance = app.BankKeeper.GetBalance(ctx, bondedPool.GetAddress(), bondDenom).Amount
|
||||
require.True(math.IntEq(t, oldBonded.Sub(burnAmount), bondedPoolBalance))
|
||||
|
||||
notBondedPoolBalance = app.BankKeeper.GetBalance(ctx, notBondedPool.GetAddress(), bondDenom).Amount
|
||||
require.True(math.IntEq(t, oldNotBonded, notBondedPoolBalance))
|
||||
oldBonded = app.BankKeeper.GetBalance(ctx, bondedPool.GetAddress(), bondDenom).Amount
|
||||
|
||||
// read updating redelegation
|
||||
rd, found = app.StakingKeeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1])
|
||||
require.True(t, found)
|
||||
require.Len(t, rd.Entries, 1)
|
||||
// read updated validator
|
||||
validator, found = app.StakingKeeper.GetValidatorByConsAddr(ctx, consAddr)
|
||||
require.True(t, found)
|
||||
// power decreased by 4
|
||||
require.Equal(t, int64(4), validator.GetConsensusPower(app.StakingKeeper.PowerReduction(ctx)))
|
||||
|
||||
// slash the validator again, by 100%
|
||||
ctx = ctx.WithBlockHeight(12)
|
||||
validator, found = app.StakingKeeper.GetValidatorByConsAddr(ctx, consAddr)
|
||||
require.True(t, found)
|
||||
|
||||
require.NotPanics(t, func() { app.StakingKeeper.Slash(ctx, consAddr, 10, 10, math.LegacyOneDec()) })
|
||||
|
||||
burnAmount = sdk.NewDecFromInt(app.StakingKeeper.TokensFromConsensusPower(ctx, 10)).Mul(math.LegacyOneDec()).TruncateInt()
|
||||
burnAmount = burnAmount.Sub(math.LegacyOneDec().MulInt(rdTokens).TruncateInt())
|
||||
|
||||
// read updated pool
|
||||
bondedPool = app.StakingKeeper.GetBondedPool(ctx)
|
||||
notBondedPool = app.StakingKeeper.GetNotBondedPool(ctx)
|
||||
|
||||
bondedPoolBalance = app.BankKeeper.GetBalance(ctx, bondedPool.GetAddress(), bondDenom).Amount
|
||||
require.True(math.IntEq(t, oldBonded.Sub(burnAmount), bondedPoolBalance))
|
||||
notBondedPoolBalance = app.BankKeeper.GetBalance(ctx, notBondedPool.GetAddress(), bondDenom).Amount
|
||||
require.True(math.IntEq(t, oldNotBonded, notBondedPoolBalance))
|
||||
oldBonded = app.BankKeeper.GetBalance(ctx, bondedPool.GetAddress(), bondDenom).Amount
|
||||
|
||||
// read updating redelegation
|
||||
rd, found = app.StakingKeeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1])
|
||||
require.True(t, found)
|
||||
require.Len(t, rd.Entries, 1)
|
||||
// apply TM updates
|
||||
applyValidatorSetUpdates(t, ctx, app.StakingKeeper, -1)
|
||||
// read updated validator
|
||||
// validator decreased to zero power, should be in unbonding period
|
||||
validator, _ = app.StakingKeeper.GetValidatorByConsAddr(ctx, consAddr)
|
||||
require.Equal(t, validator.GetStatus(), types.Unbonding)
|
||||
|
||||
// slash the validator again, by 100%
|
||||
// no stake remains to be slashed
|
||||
ctx = ctx.WithBlockHeight(12)
|
||||
// validator still in unbonding period
|
||||
validator, _ = app.StakingKeeper.GetValidatorByConsAddr(ctx, consAddr)
|
||||
require.Equal(t, validator.GetStatus(), types.Unbonding)
|
||||
|
||||
require.NotPanics(t, func() { app.StakingKeeper.Slash(ctx, consAddr, 10, 10, math.LegacyOneDec()) })
|
||||
|
||||
// read updated pool
|
||||
bondedPool = app.StakingKeeper.GetBondedPool(ctx)
|
||||
notBondedPool = app.StakingKeeper.GetNotBondedPool(ctx)
|
||||
|
||||
bondedPoolBalance = app.BankKeeper.GetBalance(ctx, bondedPool.GetAddress(), bondDenom).Amount
|
||||
require.True(math.IntEq(t, oldBonded, bondedPoolBalance))
|
||||
notBondedPoolBalance = app.BankKeeper.GetBalance(ctx, notBondedPool.GetAddress(), bondDenom).Amount
|
||||
require.True(math.IntEq(t, oldNotBonded, notBondedPoolBalance))
|
||||
|
||||
// read updating redelegation
|
||||
rd, found = app.StakingKeeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1])
|
||||
require.True(t, found)
|
||||
require.Len(t, rd.Entries, 1)
|
||||
// read updated validator
|
||||
// power still zero, still in unbonding period
|
||||
validator, _ = app.StakingKeeper.GetValidatorByConsAddr(ctx, consAddr)
|
||||
require.Equal(t, validator.GetStatus(), types.Unbonding)
|
||||
}
|
||||
|
||||
// tests Slash at a previous height with both an unbonding delegation and a redelegation
|
||||
func TestSlashBoth(t *testing.T) {
|
||||
app, ctx, addrDels, addrVals := bootstrapSlashTest(t, 10)
|
||||
fraction := sdk.NewDecWithPrec(5, 1)
|
||||
bondDenom := app.StakingKeeper.BondDenom(ctx)
|
||||
|
||||
// set a redelegation with expiration timestamp beyond which the
|
||||
// redelegation shouldn't be slashed
|
||||
rdATokens := app.StakingKeeper.TokensFromConsensusPower(ctx, 6)
|
||||
rdA := types.NewRedelegation(addrDels[0], addrVals[0], addrVals[1], 11, time.Unix(0, 0), rdATokens, sdk.NewDecFromInt(rdATokens))
|
||||
app.StakingKeeper.SetRedelegation(ctx, rdA)
|
||||
|
||||
// set the associated delegation
|
||||
delA := types.NewDelegation(addrDels[0], addrVals[1], sdk.NewDecFromInt(rdATokens))
|
||||
app.StakingKeeper.SetDelegation(ctx, delA)
|
||||
|
||||
// set an unbonding delegation with expiration timestamp (beyond which the
|
||||
// unbonding delegation shouldn't be slashed)
|
||||
ubdATokens := app.StakingKeeper.TokensFromConsensusPower(ctx, 4)
|
||||
ubdA := types.NewUnbondingDelegation(addrDels[0], addrVals[0], 11,
|
||||
time.Unix(0, 0), ubdATokens)
|
||||
app.StakingKeeper.SetUnbondingDelegation(ctx, ubdA)
|
||||
|
||||
bondedCoins := sdk.NewCoins(sdk.NewCoin(bondDenom, rdATokens.MulRaw(2)))
|
||||
notBondedCoins := sdk.NewCoins(sdk.NewCoin(bondDenom, ubdATokens))
|
||||
|
||||
// update bonded tokens
|
||||
bondedPool := app.StakingKeeper.GetBondedPool(ctx)
|
||||
notBondedPool := app.StakingKeeper.GetNotBondedPool(ctx)
|
||||
|
||||
require.NoError(t, testutil.FundModuleAccount(app.BankKeeper, ctx, bondedPool.GetName(), bondedCoins))
|
||||
require.NoError(t, testutil.FundModuleAccount(app.BankKeeper, ctx, notBondedPool.GetName(), notBondedCoins))
|
||||
|
||||
app.AccountKeeper.SetModuleAccount(ctx, bondedPool)
|
||||
app.AccountKeeper.SetModuleAccount(ctx, notBondedPool)
|
||||
|
||||
oldBonded := app.BankKeeper.GetBalance(ctx, bondedPool.GetAddress(), bondDenom).Amount
|
||||
oldNotBonded := app.BankKeeper.GetBalance(ctx, notBondedPool.GetAddress(), bondDenom).Amount
|
||||
// slash validator
|
||||
ctx = ctx.WithBlockHeight(12)
|
||||
validator, found := app.StakingKeeper.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(PKs[0]))
|
||||
require.True(t, found)
|
||||
consAddr0 := sdk.ConsAddress(PKs[0].Address())
|
||||
app.StakingKeeper.Slash(ctx, consAddr0, 10, 10, fraction)
|
||||
|
||||
burnedNotBondedAmount := fraction.MulInt(ubdATokens).TruncateInt()
|
||||
burnedBondAmount := sdk.NewDecFromInt(app.StakingKeeper.TokensFromConsensusPower(ctx, 10)).Mul(fraction).TruncateInt()
|
||||
burnedBondAmount = burnedBondAmount.Sub(burnedNotBondedAmount)
|
||||
|
||||
// read updated pool
|
||||
bondedPool = app.StakingKeeper.GetBondedPool(ctx)
|
||||
notBondedPool = app.StakingKeeper.GetNotBondedPool(ctx)
|
||||
|
||||
bondedPoolBalance := app.BankKeeper.GetBalance(ctx, bondedPool.GetAddress(), bondDenom).Amount
|
||||
require.True(math.IntEq(t, oldBonded.Sub(burnedBondAmount), bondedPoolBalance))
|
||||
|
||||
notBondedPoolBalance := app.BankKeeper.GetBalance(ctx, notBondedPool.GetAddress(), bondDenom).Amount
|
||||
require.True(math.IntEq(t, oldNotBonded.Sub(burnedNotBondedAmount), notBondedPoolBalance))
|
||||
|
||||
// read updating redelegation
|
||||
rdA, found = app.StakingKeeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1])
|
||||
require.True(t, found)
|
||||
require.Len(t, rdA.Entries, 1)
|
||||
// read updated validator
|
||||
validator, found = app.StakingKeeper.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(PKs[0]))
|
||||
require.True(t, found)
|
||||
// power not decreased, all stake was bonded since
|
||||
require.Equal(t, int64(10), validator.GetConsensusPower(app.StakingKeeper.PowerReduction(ctx)))
|
||||
}
|
||||
|
||||
func TestSlashAmount(t *testing.T) {
|
||||
app, ctx, _, _ := bootstrapSlashTest(t, 10)
|
||||
consAddr := sdk.ConsAddress(PKs[0].Address())
|
||||
fraction := sdk.NewDecWithPrec(5, 1)
|
||||
burnedCoins := app.StakingKeeper.Slash(ctx, consAddr, ctx.BlockHeight(), 10, fraction)
|
||||
require.True(t, burnedCoins.GT(math.ZeroInt()))
|
||||
|
||||
// test the case where the validator was not found, which should return no coins
|
||||
_, addrVals := generateAddresses(app, ctx, 100)
|
||||
noBurned := app.StakingKeeper.Slash(ctx, sdk.ConsAddress(addrVals[0]), ctx.BlockHeight(), 10, fraction)
|
||||
require.True(t, sdk.NewInt(0).Equal(noBurned))
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package keeper_test
|
||||
|
||||
import "testing"
|
||||
|
||||
func BenchmarkGetValidator(b *testing.B) {
|
||||
// 900 is the max number we are allowed to use in order to avoid simtestutil.CreateTestPubKeys
|
||||
// panic: encoding/hex: odd length hex string
|
||||
powersNumber := 900
|
||||
|
||||
var totalPower int64 = 0
|
||||
powers := make([]int64, powersNumber)
|
||||
for i := range powers {
|
||||
powers[i] = int64(i)
|
||||
totalPower += int64(i)
|
||||
}
|
||||
|
||||
app, ctx, _, valAddrs, vals := initValidators(b, totalPower, len(powers), powers)
|
||||
|
||||
for _, validator := range vals {
|
||||
app.StakingKeeper.SetValidator(ctx, validator)
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
for n := 0; n < b.N; n++ {
|
||||
for _, addr := range valAddrs {
|
||||
_, _ = app.StakingKeeper.GetValidator(ctx, addr)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,834 @@
|
||||
package keeper_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"cosmossdk.io/math"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
|
||||
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
|
||||
"github.com/cosmos/cosmos-sdk/simapp"
|
||||
simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/bank/testutil"
|
||||
"github.com/cosmos/cosmos-sdk/x/staking"
|
||||
"github.com/cosmos/cosmos-sdk/x/staking/keeper"
|
||||
"github.com/cosmos/cosmos-sdk/x/staking/teststaking"
|
||||
"github.com/cosmos/cosmos-sdk/x/staking/types"
|
||||
)
|
||||
|
||||
func newMonikerValidator(t testing.TB, operator sdk.ValAddress, pubKey cryptotypes.PubKey, moniker string) types.Validator {
|
||||
v, err := types.NewValidator(operator, pubKey, types.Description{Moniker: moniker})
|
||||
require.NoError(t, err)
|
||||
return v
|
||||
}
|
||||
|
||||
func bootstrapValidatorTest(t testing.TB, power int64, numAddrs int) (*simapp.SimApp, sdk.Context, []sdk.AccAddress, []sdk.ValAddress) {
|
||||
_, app, ctx := createTestInput(&testing.T{})
|
||||
|
||||
addrDels, addrVals := generateAddresses(app, ctx, numAddrs)
|
||||
|
||||
amt := app.StakingKeeper.TokensFromConsensusPower(ctx, power)
|
||||
totalSupply := sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), amt.MulRaw(int64(len(addrDels)))))
|
||||
|
||||
notBondedPool := app.StakingKeeper.GetNotBondedPool(ctx)
|
||||
// set bonded pool supply
|
||||
app.AccountKeeper.SetModuleAccount(ctx, notBondedPool)
|
||||
|
||||
require.NoError(t, testutil.FundModuleAccount(app.BankKeeper, ctx, notBondedPool.GetName(), totalSupply))
|
||||
|
||||
// unbond genesis validator delegations
|
||||
delegations := app.StakingKeeper.GetAllDelegations(ctx)
|
||||
require.Len(t, delegations, 1)
|
||||
delegation := delegations[0]
|
||||
|
||||
_, err := app.StakingKeeper.Undelegate(ctx, delegation.GetDelegatorAddr(), delegation.GetValidatorAddr(), delegation.Shares)
|
||||
require.NoError(t, err)
|
||||
|
||||
// end block to unbond genesis validator
|
||||
staking.EndBlocker(ctx, app.StakingKeeper)
|
||||
|
||||
return app, ctx, addrDels, addrVals
|
||||
}
|
||||
|
||||
func initValidators(t testing.TB, power int64, numAddrs int, powers []int64) (*simapp.SimApp, sdk.Context, []sdk.AccAddress, []sdk.ValAddress, []types.Validator) {
|
||||
app, ctx, addrs, valAddrs := bootstrapValidatorTest(t, power, numAddrs)
|
||||
pks := simtestutil.CreateTestPubKeys(numAddrs)
|
||||
|
||||
vs := make([]types.Validator, len(powers))
|
||||
for i, power := range powers {
|
||||
vs[i] = teststaking.NewValidator(t, sdk.ValAddress(addrs[i]), pks[i])
|
||||
tokens := app.StakingKeeper.TokensFromConsensusPower(ctx, power)
|
||||
vs[i], _ = vs[i].AddTokensFromDel(tokens)
|
||||
}
|
||||
return app, ctx, addrs, valAddrs, vs
|
||||
}
|
||||
|
||||
func TestUpdateBondedValidatorsDecreaseCliff(t *testing.T) {
|
||||
numVals := 10
|
||||
maxVals := 5
|
||||
|
||||
// create context, keeper, and pool for tests
|
||||
app, ctx, _, valAddrs := bootstrapValidatorTest(t, 0, 100)
|
||||
|
||||
bondedPool := app.StakingKeeper.GetBondedPool(ctx)
|
||||
notBondedPool := app.StakingKeeper.GetNotBondedPool(ctx)
|
||||
|
||||
// create keeper parameters
|
||||
params := app.StakingKeeper.GetParams(ctx)
|
||||
params.MaxValidators = uint32(maxVals)
|
||||
app.StakingKeeper.SetParams(ctx, params)
|
||||
|
||||
// create a random pool
|
||||
require.NoError(t, testutil.FundModuleAccount(app.BankKeeper, ctx, bondedPool.GetName(), sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), app.StakingKeeper.TokensFromConsensusPower(ctx, 1234)))))
|
||||
require.NoError(t, testutil.FundModuleAccount(app.BankKeeper, ctx, notBondedPool.GetName(), sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), app.StakingKeeper.TokensFromConsensusPower(ctx, 10000)))))
|
||||
|
||||
app.AccountKeeper.SetModuleAccount(ctx, bondedPool)
|
||||
app.AccountKeeper.SetModuleAccount(ctx, notBondedPool)
|
||||
|
||||
validators := make([]types.Validator, numVals)
|
||||
for i := 0; i < len(validators); i++ {
|
||||
moniker := fmt.Sprintf("val#%d", int64(i))
|
||||
val := newMonikerValidator(t, valAddrs[i], PKs[i], moniker)
|
||||
delTokens := app.StakingKeeper.TokensFromConsensusPower(ctx, int64((i+1)*10))
|
||||
val, _ = val.AddTokensFromDel(delTokens)
|
||||
|
||||
val = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, val, true)
|
||||
validators[i] = val
|
||||
}
|
||||
|
||||
nextCliffVal := validators[numVals-maxVals+1]
|
||||
|
||||
// remove enough tokens to kick out the validator below the current cliff
|
||||
// validator and next in line cliff validator
|
||||
app.StakingKeeper.DeleteValidatorByPowerIndex(ctx, nextCliffVal)
|
||||
shares := app.StakingKeeper.TokensFromConsensusPower(ctx, 21)
|
||||
nextCliffVal, _ = nextCliffVal.RemoveDelShares(sdk.NewDecFromInt(shares))
|
||||
nextCliffVal = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, nextCliffVal, true)
|
||||
|
||||
expectedValStatus := map[int]types.BondStatus{
|
||||
9: types.Bonded, 8: types.Bonded, 7: types.Bonded, 5: types.Bonded, 4: types.Bonded,
|
||||
0: types.Unbonding, 1: types.Unbonding, 2: types.Unbonding, 3: types.Unbonding, 6: types.Unbonding,
|
||||
}
|
||||
|
||||
// require all the validators have their respective statuses
|
||||
for valIdx, status := range expectedValStatus {
|
||||
valAddr := validators[valIdx].OperatorAddress
|
||||
addr, err := sdk.ValAddressFromBech32(valAddr)
|
||||
assert.NoError(t, err)
|
||||
val, _ := app.StakingKeeper.GetValidator(ctx, addr)
|
||||
|
||||
assert.Equal(
|
||||
t, status, val.GetStatus(),
|
||||
fmt.Sprintf("expected validator at index %v to have status: %s", valIdx, status),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSlashToZeroPowerRemoved(t *testing.T) {
|
||||
// initialize setup
|
||||
app, ctx, _, addrVals := bootstrapValidatorTest(t, 100, 20)
|
||||
|
||||
// add a validator
|
||||
validator := teststaking.NewValidator(t, addrVals[0], PKs[0])
|
||||
valTokens := app.StakingKeeper.TokensFromConsensusPower(ctx, 100)
|
||||
|
||||
bondedPool := app.StakingKeeper.GetBondedPool(ctx)
|
||||
|
||||
require.NoError(t, testutil.FundModuleAccount(app.BankKeeper, ctx, bondedPool.GetName(), sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), valTokens))))
|
||||
|
||||
app.AccountKeeper.SetModuleAccount(ctx, bondedPool)
|
||||
|
||||
validator, _ = validator.AddTokensFromDel(valTokens)
|
||||
require.Equal(t, types.Unbonded, validator.Status)
|
||||
require.Equal(t, valTokens, validator.Tokens)
|
||||
app.StakingKeeper.SetValidatorByConsAddr(ctx, validator)
|
||||
validator = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validator, true)
|
||||
require.Equal(t, valTokens, validator.Tokens, "\nvalidator %v\npool %v", validator, valTokens)
|
||||
|
||||
// slash the validator by 100%
|
||||
app.StakingKeeper.Slash(ctx, sdk.ConsAddress(PKs[0].Address()), 0, 100, math.LegacyOneDec())
|
||||
// apply TM updates
|
||||
applyValidatorSetUpdates(t, ctx, app.StakingKeeper, -1)
|
||||
// validator should be unbonding
|
||||
validator, _ = app.StakingKeeper.GetValidator(ctx, addrVals[0])
|
||||
require.Equal(t, validator.GetStatus(), types.Unbonding)
|
||||
}
|
||||
|
||||
// test how the validators are sorted, tests GetBondedValidatorsByPower
|
||||
func TestGetValidatorSortingUnmixed(t *testing.T) {
|
||||
app, ctx, addrs, _ := bootstrapValidatorTest(t, 1000, 20)
|
||||
|
||||
// initialize some validators into the state
|
||||
amts := []math.Int{
|
||||
sdk.NewIntFromUint64(0),
|
||||
app.StakingKeeper.PowerReduction(ctx).MulRaw(100),
|
||||
app.StakingKeeper.PowerReduction(ctx),
|
||||
app.StakingKeeper.PowerReduction(ctx).MulRaw(400),
|
||||
app.StakingKeeper.PowerReduction(ctx).MulRaw(200),
|
||||
}
|
||||
n := len(amts)
|
||||
var validators [5]types.Validator
|
||||
for i, amt := range amts {
|
||||
validators[i] = teststaking.NewValidator(t, sdk.ValAddress(addrs[i]), PKs[i])
|
||||
validators[i].Status = types.Bonded
|
||||
validators[i].Tokens = amt
|
||||
validators[i].DelegatorShares = sdk.NewDecFromInt(amt)
|
||||
keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[i], true)
|
||||
}
|
||||
|
||||
// first make sure everything made it in to the gotValidator group
|
||||
resValidators := app.StakingKeeper.GetBondedValidatorsByPower(ctx)
|
||||
assert.Equal(t, n, len(resValidators))
|
||||
assert.Equal(t, sdk.NewInt(400).Mul(app.StakingKeeper.PowerReduction(ctx)), resValidators[0].BondedTokens(), "%v", resValidators)
|
||||
assert.Equal(t, sdk.NewInt(200).Mul(app.StakingKeeper.PowerReduction(ctx)), resValidators[1].BondedTokens(), "%v", resValidators)
|
||||
assert.Equal(t, sdk.NewInt(100).Mul(app.StakingKeeper.PowerReduction(ctx)), resValidators[2].BondedTokens(), "%v", resValidators)
|
||||
assert.Equal(t, sdk.NewInt(1).Mul(app.StakingKeeper.PowerReduction(ctx)), resValidators[3].BondedTokens(), "%v", resValidators)
|
||||
assert.Equal(t, sdk.NewInt(0), resValidators[4].BondedTokens(), "%v", resValidators)
|
||||
assert.Equal(t, validators[3].OperatorAddress, resValidators[0].OperatorAddress, "%v", resValidators)
|
||||
assert.Equal(t, validators[4].OperatorAddress, resValidators[1].OperatorAddress, "%v", resValidators)
|
||||
assert.Equal(t, validators[1].OperatorAddress, resValidators[2].OperatorAddress, "%v", resValidators)
|
||||
assert.Equal(t, validators[2].OperatorAddress, resValidators[3].OperatorAddress, "%v", resValidators)
|
||||
assert.Equal(t, validators[0].OperatorAddress, resValidators[4].OperatorAddress, "%v", resValidators)
|
||||
|
||||
// test a basic increase in voting power
|
||||
validators[3].Tokens = sdk.NewInt(500).Mul(app.StakingKeeper.PowerReduction(ctx))
|
||||
keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[3], true)
|
||||
resValidators = app.StakingKeeper.GetBondedValidatorsByPower(ctx)
|
||||
require.Equal(t, len(resValidators), n)
|
||||
assert.True(ValEq(t, validators[3], resValidators[0]))
|
||||
|
||||
// test a decrease in voting power
|
||||
validators[3].Tokens = sdk.NewInt(300).Mul(app.StakingKeeper.PowerReduction(ctx))
|
||||
keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[3], true)
|
||||
resValidators = app.StakingKeeper.GetBondedValidatorsByPower(ctx)
|
||||
require.Equal(t, len(resValidators), n)
|
||||
assert.True(ValEq(t, validators[3], resValidators[0]))
|
||||
assert.True(ValEq(t, validators[4], resValidators[1]))
|
||||
|
||||
// test equal voting power, different age
|
||||
validators[3].Tokens = sdk.NewInt(200).Mul(app.StakingKeeper.PowerReduction(ctx))
|
||||
ctx = ctx.WithBlockHeight(10)
|
||||
keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[3], true)
|
||||
resValidators = app.StakingKeeper.GetBondedValidatorsByPower(ctx)
|
||||
require.Equal(t, len(resValidators), n)
|
||||
assert.True(ValEq(t, validators[3], resValidators[0]))
|
||||
assert.True(ValEq(t, validators[4], resValidators[1]))
|
||||
|
||||
// no change in voting power - no change in sort
|
||||
ctx = ctx.WithBlockHeight(20)
|
||||
keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[4], true)
|
||||
resValidators = app.StakingKeeper.GetBondedValidatorsByPower(ctx)
|
||||
require.Equal(t, len(resValidators), n)
|
||||
assert.True(ValEq(t, validators[3], resValidators[0]))
|
||||
assert.True(ValEq(t, validators[4], resValidators[1]))
|
||||
|
||||
// change in voting power of both validators, both still in v-set, no age change
|
||||
validators[3].Tokens = sdk.NewInt(300).Mul(app.StakingKeeper.PowerReduction(ctx))
|
||||
validators[4].Tokens = sdk.NewInt(300).Mul(app.StakingKeeper.PowerReduction(ctx))
|
||||
keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[3], true)
|
||||
resValidators = app.StakingKeeper.GetBondedValidatorsByPower(ctx)
|
||||
require.Equal(t, len(resValidators), n)
|
||||
ctx = ctx.WithBlockHeight(30)
|
||||
keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[4], true)
|
||||
resValidators = app.StakingKeeper.GetBondedValidatorsByPower(ctx)
|
||||
require.Equal(t, len(resValidators), n, "%v", resValidators)
|
||||
assert.True(ValEq(t, validators[3], resValidators[0]))
|
||||
assert.True(ValEq(t, validators[4], resValidators[1]))
|
||||
}
|
||||
|
||||
func TestGetValidatorSortingMixed(t *testing.T) {
|
||||
app, ctx, addrs, _ := bootstrapValidatorTest(t, 1000, 20)
|
||||
bondedPool := app.StakingKeeper.GetBondedPool(ctx)
|
||||
notBondedPool := app.StakingKeeper.GetNotBondedPool(ctx)
|
||||
|
||||
require.NoError(t, testutil.FundModuleAccount(app.BankKeeper, ctx, bondedPool.GetName(), sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), app.StakingKeeper.TokensFromConsensusPower(ctx, 501)))))
|
||||
require.NoError(t, testutil.FundModuleAccount(app.BankKeeper, ctx, notBondedPool.GetName(), sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), app.StakingKeeper.TokensFromConsensusPower(ctx, 0)))))
|
||||
|
||||
app.AccountKeeper.SetModuleAccount(ctx, notBondedPool)
|
||||
app.AccountKeeper.SetModuleAccount(ctx, bondedPool)
|
||||
|
||||
// now 2 max resValidators
|
||||
params := app.StakingKeeper.GetParams(ctx)
|
||||
params.MaxValidators = 2
|
||||
app.StakingKeeper.SetParams(ctx, params)
|
||||
|
||||
// initialize some validators into the state
|
||||
amts := []math.Int{
|
||||
sdk.NewIntFromUint64(0),
|
||||
app.StakingKeeper.PowerReduction(ctx).MulRaw(100),
|
||||
app.StakingKeeper.PowerReduction(ctx),
|
||||
app.StakingKeeper.PowerReduction(ctx).MulRaw(400),
|
||||
app.StakingKeeper.PowerReduction(ctx).MulRaw(200),
|
||||
}
|
||||
|
||||
var validators [5]types.Validator
|
||||
for i, amt := range amts {
|
||||
validators[i] = teststaking.NewValidator(t, sdk.ValAddress(addrs[i]), PKs[i])
|
||||
validators[i].DelegatorShares = sdk.NewDecFromInt(amt)
|
||||
validators[i].Status = types.Bonded
|
||||
validators[i].Tokens = amt
|
||||
keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[i], true)
|
||||
}
|
||||
|
||||
val0, found := app.StakingKeeper.GetValidator(ctx, sdk.ValAddress(addrs[0]))
|
||||
require.True(t, found)
|
||||
val1, found := app.StakingKeeper.GetValidator(ctx, sdk.ValAddress(addrs[1]))
|
||||
require.True(t, found)
|
||||
val2, found := app.StakingKeeper.GetValidator(ctx, sdk.ValAddress(addrs[2]))
|
||||
require.True(t, found)
|
||||
val3, found := app.StakingKeeper.GetValidator(ctx, sdk.ValAddress(addrs[3]))
|
||||
require.True(t, found)
|
||||
val4, found := app.StakingKeeper.GetValidator(ctx, sdk.ValAddress(addrs[4]))
|
||||
require.True(t, found)
|
||||
require.Equal(t, types.Bonded, val0.Status)
|
||||
require.Equal(t, types.Unbonding, val1.Status)
|
||||
require.Equal(t, types.Unbonding, val2.Status)
|
||||
require.Equal(t, types.Bonded, val3.Status)
|
||||
require.Equal(t, types.Bonded, val4.Status)
|
||||
|
||||
// first make sure everything made it in to the gotValidator group
|
||||
resValidators := app.StakingKeeper.GetBondedValidatorsByPower(ctx)
|
||||
// The validators returned should match the max validators
|
||||
assert.Equal(t, 2, len(resValidators))
|
||||
assert.Equal(t, sdk.NewInt(400).Mul(app.StakingKeeper.PowerReduction(ctx)), resValidators[0].BondedTokens(), "%v", resValidators)
|
||||
assert.Equal(t, sdk.NewInt(200).Mul(app.StakingKeeper.PowerReduction(ctx)), resValidators[1].BondedTokens(), "%v", resValidators)
|
||||
assert.Equal(t, validators[3].OperatorAddress, resValidators[0].OperatorAddress, "%v", resValidators)
|
||||
assert.Equal(t, validators[4].OperatorAddress, resValidators[1].OperatorAddress, "%v", resValidators)
|
||||
}
|
||||
|
||||
// TODO separate out into multiple tests
|
||||
func TestGetValidatorsEdgeCases(t *testing.T) {
|
||||
app, ctx, addrs, _ := bootstrapValidatorTest(t, 1000, 20)
|
||||
|
||||
// set max validators to 2
|
||||
params := app.StakingKeeper.GetParams(ctx)
|
||||
nMax := uint32(2)
|
||||
params.MaxValidators = nMax
|
||||
app.StakingKeeper.SetParams(ctx, params)
|
||||
|
||||
// initialize some validators into the state
|
||||
powers := []int64{0, 100, 400, 400}
|
||||
var validators [4]types.Validator
|
||||
for i, power := range powers {
|
||||
moniker := fmt.Sprintf("val#%d", int64(i))
|
||||
validators[i] = newMonikerValidator(t, sdk.ValAddress(addrs[i]), PKs[i], moniker)
|
||||
|
||||
tokens := app.StakingKeeper.TokensFromConsensusPower(ctx, power)
|
||||
validators[i], _ = validators[i].AddTokensFromDel(tokens)
|
||||
|
||||
notBondedPool := app.StakingKeeper.GetNotBondedPool(ctx)
|
||||
require.NoError(t, testutil.FundModuleAccount(app.BankKeeper, ctx, notBondedPool.GetName(), sdk.NewCoins(sdk.NewCoin(params.BondDenom, tokens))))
|
||||
app.AccountKeeper.SetModuleAccount(ctx, notBondedPool)
|
||||
validators[i] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[i], true)
|
||||
}
|
||||
|
||||
// ensure that the first two bonded validators are the largest validators
|
||||
resValidators := app.StakingKeeper.GetBondedValidatorsByPower(ctx)
|
||||
require.Equal(t, nMax, uint32(len(resValidators)))
|
||||
assert.True(ValEq(t, validators[2], resValidators[0]))
|
||||
assert.True(ValEq(t, validators[3], resValidators[1]))
|
||||
|
||||
// delegate 500 tokens to validator 0
|
||||
app.StakingKeeper.DeleteValidatorByPowerIndex(ctx, validators[0])
|
||||
delTokens := app.StakingKeeper.TokensFromConsensusPower(ctx, 500)
|
||||
validators[0], _ = validators[0].AddTokensFromDel(delTokens)
|
||||
notBondedPool := app.StakingKeeper.GetNotBondedPool(ctx)
|
||||
|
||||
newTokens := sdk.NewCoins()
|
||||
|
||||
require.NoError(t, testutil.FundModuleAccount(app.BankKeeper, ctx, notBondedPool.GetName(), newTokens))
|
||||
app.AccountKeeper.SetModuleAccount(ctx, notBondedPool)
|
||||
|
||||
// test that the two largest validators are
|
||||
// a) validator 0 with 500 tokens
|
||||
// b) validator 2 with 400 tokens (delegated before validator 3)
|
||||
validators[0] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[0], true)
|
||||
resValidators = app.StakingKeeper.GetBondedValidatorsByPower(ctx)
|
||||
require.Equal(t, nMax, uint32(len(resValidators)))
|
||||
assert.True(ValEq(t, validators[0], resValidators[0]))
|
||||
assert.True(ValEq(t, validators[2], resValidators[1]))
|
||||
|
||||
// A validator which leaves the bonded validator set due to a decrease in voting power,
|
||||
// then increases to the original voting power, does not get its spot back in the
|
||||
// case of a tie.
|
||||
//
|
||||
// Order of operations for this test:
|
||||
// - validator 3 enter validator set with 1 new token
|
||||
// - validator 3 removed validator set by removing 201 tokens (validator 2 enters)
|
||||
// - validator 3 adds 200 tokens (equal to validator 2 now) and does not get its spot back
|
||||
|
||||
// validator 3 enters bonded validator set
|
||||
ctx = ctx.WithBlockHeight(40)
|
||||
|
||||
var found bool
|
||||
validators[3], found = app.StakingKeeper.GetValidator(ctx, validators[3].GetOperator())
|
||||
assert.True(t, found)
|
||||
app.StakingKeeper.DeleteValidatorByPowerIndex(ctx, validators[3])
|
||||
validators[3], _ = validators[3].AddTokensFromDel(app.StakingKeeper.TokensFromConsensusPower(ctx, 1))
|
||||
|
||||
notBondedPool = app.StakingKeeper.GetNotBondedPool(ctx)
|
||||
newTokens = sdk.NewCoins(sdk.NewCoin(params.BondDenom, app.StakingKeeper.TokensFromConsensusPower(ctx, 1)))
|
||||
require.NoError(t, testutil.FundModuleAccount(app.BankKeeper, ctx, notBondedPool.GetName(), newTokens))
|
||||
app.AccountKeeper.SetModuleAccount(ctx, notBondedPool)
|
||||
|
||||
validators[3] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[3], true)
|
||||
resValidators = app.StakingKeeper.GetBondedValidatorsByPower(ctx)
|
||||
require.Equal(t, nMax, uint32(len(resValidators)))
|
||||
assert.True(ValEq(t, validators[0], resValidators[0]))
|
||||
assert.True(ValEq(t, validators[3], resValidators[1]))
|
||||
|
||||
// validator 3 kicked out temporarily
|
||||
app.StakingKeeper.DeleteValidatorByPowerIndex(ctx, validators[3])
|
||||
rmTokens := validators[3].TokensFromShares(math.LegacyNewDec(201)).TruncateInt()
|
||||
validators[3], _ = validators[3].RemoveDelShares(math.LegacyNewDec(201))
|
||||
|
||||
bondedPool := app.StakingKeeper.GetBondedPool(ctx)
|
||||
require.NoError(t, testutil.FundModuleAccount(app.BankKeeper, ctx, bondedPool.GetName(), sdk.NewCoins(sdk.NewCoin(params.BondDenom, rmTokens))))
|
||||
app.AccountKeeper.SetModuleAccount(ctx, bondedPool)
|
||||
|
||||
validators[3] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[3], true)
|
||||
resValidators = app.StakingKeeper.GetBondedValidatorsByPower(ctx)
|
||||
require.Equal(t, nMax, uint32(len(resValidators)))
|
||||
assert.True(ValEq(t, validators[0], resValidators[0]))
|
||||
assert.True(ValEq(t, validators[2], resValidators[1]))
|
||||
|
||||
// validator 3 does not get spot back
|
||||
app.StakingKeeper.DeleteValidatorByPowerIndex(ctx, validators[3])
|
||||
validators[3], _ = validators[3].AddTokensFromDel(sdk.NewInt(200))
|
||||
|
||||
notBondedPool = app.StakingKeeper.GetNotBondedPool(ctx)
|
||||
require.NoError(t, testutil.FundModuleAccount(app.BankKeeper, ctx, notBondedPool.GetName(), sdk.NewCoins(sdk.NewCoin(params.BondDenom, sdk.NewInt(200)))))
|
||||
app.AccountKeeper.SetModuleAccount(ctx, notBondedPool)
|
||||
|
||||
validators[3] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[3], true)
|
||||
resValidators = app.StakingKeeper.GetBondedValidatorsByPower(ctx)
|
||||
require.Equal(t, nMax, uint32(len(resValidators)))
|
||||
assert.True(ValEq(t, validators[0], resValidators[0]))
|
||||
assert.True(ValEq(t, validators[2], resValidators[1]))
|
||||
_, exists := app.StakingKeeper.GetValidator(ctx, validators[3].GetOperator())
|
||||
require.True(t, exists)
|
||||
}
|
||||
|
||||
func TestValidatorBondHeight(t *testing.T) {
|
||||
app, ctx, addrs, _ := bootstrapValidatorTest(t, 1000, 20)
|
||||
|
||||
// now 2 max resValidators
|
||||
params := app.StakingKeeper.GetParams(ctx)
|
||||
params.MaxValidators = 2
|
||||
app.StakingKeeper.SetParams(ctx, params)
|
||||
|
||||
// initialize some validators into the state
|
||||
var validators [3]types.Validator
|
||||
validators[0] = teststaking.NewValidator(t, sdk.ValAddress(PKs[0].Address().Bytes()), PKs[0])
|
||||
validators[1] = teststaking.NewValidator(t, sdk.ValAddress(addrs[1]), PKs[1])
|
||||
validators[2] = teststaking.NewValidator(t, sdk.ValAddress(addrs[2]), PKs[2])
|
||||
|
||||
tokens0 := app.StakingKeeper.TokensFromConsensusPower(ctx, 200)
|
||||
tokens1 := app.StakingKeeper.TokensFromConsensusPower(ctx, 100)
|
||||
tokens2 := app.StakingKeeper.TokensFromConsensusPower(ctx, 100)
|
||||
validators[0], _ = validators[0].AddTokensFromDel(tokens0)
|
||||
validators[1], _ = validators[1].AddTokensFromDel(tokens1)
|
||||
validators[2], _ = validators[2].AddTokensFromDel(tokens2)
|
||||
|
||||
validators[0] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[0], true)
|
||||
|
||||
////////////////////////////////////////
|
||||
// If two validators both increase to the same voting power in the same block,
|
||||
// the one with the first transaction should become bonded
|
||||
validators[1] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[1], true)
|
||||
validators[2] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[2], true)
|
||||
|
||||
resValidators := app.StakingKeeper.GetBondedValidatorsByPower(ctx)
|
||||
require.Equal(t, uint32(len(resValidators)), params.MaxValidators)
|
||||
|
||||
assert.True(ValEq(t, validators[0], resValidators[0]))
|
||||
assert.True(ValEq(t, validators[1], resValidators[1]))
|
||||
app.StakingKeeper.DeleteValidatorByPowerIndex(ctx, validators[1])
|
||||
app.StakingKeeper.DeleteValidatorByPowerIndex(ctx, validators[2])
|
||||
delTokens := app.StakingKeeper.TokensFromConsensusPower(ctx, 50)
|
||||
validators[1], _ = validators[1].AddTokensFromDel(delTokens)
|
||||
validators[2], _ = validators[2].AddTokensFromDel(delTokens)
|
||||
validators[2] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[2], true)
|
||||
resValidators = app.StakingKeeper.GetBondedValidatorsByPower(ctx)
|
||||
require.Equal(t, params.MaxValidators, uint32(len(resValidators)))
|
||||
validators[1] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[1], true)
|
||||
assert.True(ValEq(t, validators[0], resValidators[0]))
|
||||
assert.True(ValEq(t, validators[2], resValidators[1]))
|
||||
}
|
||||
|
||||
func TestFullValidatorSetPowerChange(t *testing.T) {
|
||||
app, ctx, addrs, _ := bootstrapValidatorTest(t, 1000, 20)
|
||||
params := app.StakingKeeper.GetParams(ctx)
|
||||
max := 2
|
||||
params.MaxValidators = uint32(2)
|
||||
app.StakingKeeper.SetParams(ctx, params)
|
||||
|
||||
// initialize some validators into the state
|
||||
powers := []int64{0, 100, 400, 400, 200}
|
||||
var validators [5]types.Validator
|
||||
for i, power := range powers {
|
||||
validators[i] = teststaking.NewValidator(t, sdk.ValAddress(addrs[i]), PKs[i])
|
||||
tokens := app.StakingKeeper.TokensFromConsensusPower(ctx, power)
|
||||
validators[i], _ = validators[i].AddTokensFromDel(tokens)
|
||||
keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[i], true)
|
||||
}
|
||||
for i := range powers {
|
||||
var found bool
|
||||
validators[i], found = app.StakingKeeper.GetValidator(ctx, validators[i].GetOperator())
|
||||
require.True(t, found)
|
||||
}
|
||||
assert.Equal(t, types.Unbonded, validators[0].Status)
|
||||
assert.Equal(t, types.Unbonding, validators[1].Status)
|
||||
assert.Equal(t, types.Bonded, validators[2].Status)
|
||||
assert.Equal(t, types.Bonded, validators[3].Status)
|
||||
assert.Equal(t, types.Unbonded, validators[4].Status)
|
||||
resValidators := app.StakingKeeper.GetBondedValidatorsByPower(ctx)
|
||||
assert.Equal(t, max, len(resValidators))
|
||||
assert.True(ValEq(t, validators[2], resValidators[0])) // in the order of txs
|
||||
assert.True(ValEq(t, validators[3], resValidators[1]))
|
||||
|
||||
// test a swap in voting power
|
||||
|
||||
tokens := app.StakingKeeper.TokensFromConsensusPower(ctx, 600)
|
||||
validators[0], _ = validators[0].AddTokensFromDel(tokens)
|
||||
validators[0] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[0], true)
|
||||
resValidators = app.StakingKeeper.GetBondedValidatorsByPower(ctx)
|
||||
assert.Equal(t, max, len(resValidators))
|
||||
assert.True(ValEq(t, validators[0], resValidators[0]))
|
||||
assert.True(ValEq(t, validators[2], resValidators[1]))
|
||||
}
|
||||
|
||||
func TestApplyAndReturnValidatorSetUpdatesAllNone(t *testing.T) {
|
||||
app, ctx, _, _ := bootstrapValidatorTest(t, 1000, 20)
|
||||
|
||||
powers := []int64{10, 20}
|
||||
var validators [2]types.Validator
|
||||
for i, power := range powers {
|
||||
valPubKey := PKs[i+1]
|
||||
valAddr := sdk.ValAddress(valPubKey.Address().Bytes())
|
||||
|
||||
validators[i] = teststaking.NewValidator(t, valAddr, valPubKey)
|
||||
tokens := app.StakingKeeper.TokensFromConsensusPower(ctx, power)
|
||||
validators[i], _ = validators[i].AddTokensFromDel(tokens)
|
||||
}
|
||||
|
||||
// test from nothing to something
|
||||
// tendermintUpdate set: {} -> {c1, c3}
|
||||
applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 0)
|
||||
app.StakingKeeper.SetValidator(ctx, validators[0])
|
||||
app.StakingKeeper.SetValidatorByPowerIndex(ctx, validators[0])
|
||||
app.StakingKeeper.SetValidator(ctx, validators[1])
|
||||
app.StakingKeeper.SetValidatorByPowerIndex(ctx, validators[1])
|
||||
|
||||
updates := applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 2)
|
||||
validators[0], _ = app.StakingKeeper.GetValidator(ctx, validators[0].GetOperator())
|
||||
validators[1], _ = app.StakingKeeper.GetValidator(ctx, validators[1].GetOperator())
|
||||
assert.Equal(t, validators[0].ABCIValidatorUpdate(app.StakingKeeper.PowerReduction(ctx)), updates[1])
|
||||
assert.Equal(t, validators[1].ABCIValidatorUpdate(app.StakingKeeper.PowerReduction(ctx)), updates[0])
|
||||
}
|
||||
|
||||
func TestApplyAndReturnValidatorSetUpdatesIdentical(t *testing.T) {
|
||||
app, ctx, addrs, _ := bootstrapValidatorTest(t, 1000, 20)
|
||||
|
||||
powers := []int64{10, 20}
|
||||
var validators [2]types.Validator
|
||||
for i, power := range powers {
|
||||
validators[i] = teststaking.NewValidator(t, sdk.ValAddress(addrs[i]), PKs[i])
|
||||
|
||||
tokens := app.StakingKeeper.TokensFromConsensusPower(ctx, power)
|
||||
validators[i], _ = validators[i].AddTokensFromDel(tokens)
|
||||
|
||||
}
|
||||
validators[0] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[0], false)
|
||||
validators[1] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[1], false)
|
||||
applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 2)
|
||||
|
||||
// test identical,
|
||||
// tendermintUpdate set: {} -> {}
|
||||
validators[0] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[0], false)
|
||||
validators[1] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[1], false)
|
||||
applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 0)
|
||||
}
|
||||
|
||||
func TestApplyAndReturnValidatorSetUpdatesSingleValueChange(t *testing.T) {
|
||||
app, ctx, addrs, _ := bootstrapValidatorTest(t, 1000, 20)
|
||||
|
||||
powers := []int64{10, 20}
|
||||
var validators [2]types.Validator
|
||||
for i, power := range powers {
|
||||
validators[i] = teststaking.NewValidator(t, sdk.ValAddress(addrs[i]), PKs[i])
|
||||
|
||||
tokens := app.StakingKeeper.TokensFromConsensusPower(ctx, power)
|
||||
validators[i], _ = validators[i].AddTokensFromDel(tokens)
|
||||
|
||||
}
|
||||
validators[0] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[0], false)
|
||||
validators[1] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[1], false)
|
||||
applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 2)
|
||||
|
||||
// test single value change
|
||||
// tendermintUpdate set: {} -> {c1'}
|
||||
validators[0].Status = types.Bonded
|
||||
validators[0].Tokens = app.StakingKeeper.TokensFromConsensusPower(ctx, 600)
|
||||
validators[0] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[0], false)
|
||||
|
||||
updates := applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 1)
|
||||
require.Equal(t, validators[0].ABCIValidatorUpdate(app.StakingKeeper.PowerReduction(ctx)), updates[0])
|
||||
}
|
||||
|
||||
func TestApplyAndReturnValidatorSetUpdatesMultipleValueChange(t *testing.T) {
|
||||
powers := []int64{10, 20}
|
||||
// TODO: use it in other places
|
||||
app, ctx, _, _, validators := initValidators(t, 1000, 20, powers)
|
||||
|
||||
validators[0] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[0], false)
|
||||
validators[1] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[1], false)
|
||||
applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 2)
|
||||
|
||||
// test multiple value change
|
||||
// tendermintUpdate set: {c1, c3} -> {c1', c3'}
|
||||
delTokens1 := app.StakingKeeper.TokensFromConsensusPower(ctx, 190)
|
||||
delTokens2 := app.StakingKeeper.TokensFromConsensusPower(ctx, 80)
|
||||
validators[0], _ = validators[0].AddTokensFromDel(delTokens1)
|
||||
validators[1], _ = validators[1].AddTokensFromDel(delTokens2)
|
||||
validators[0] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[0], false)
|
||||
validators[1] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[1], false)
|
||||
|
||||
updates := applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 2)
|
||||
require.Equal(t, validators[0].ABCIValidatorUpdate(app.StakingKeeper.PowerReduction(ctx)), updates[0])
|
||||
require.Equal(t, validators[1].ABCIValidatorUpdate(app.StakingKeeper.PowerReduction(ctx)), updates[1])
|
||||
}
|
||||
|
||||
func TestApplyAndReturnValidatorSetUpdatesInserted(t *testing.T) {
|
||||
powers := []int64{10, 20, 5, 15, 25}
|
||||
app, ctx, _, _, validators := initValidators(t, 1000, 20, powers)
|
||||
|
||||
validators[0] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[0], false)
|
||||
validators[1] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[1], false)
|
||||
applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 2)
|
||||
|
||||
// test validtor added at the beginning
|
||||
// tendermintUpdate set: {} -> {c0}
|
||||
app.StakingKeeper.SetValidator(ctx, validators[2])
|
||||
app.StakingKeeper.SetValidatorByPowerIndex(ctx, validators[2])
|
||||
updates := applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 1)
|
||||
validators[2], _ = app.StakingKeeper.GetValidator(ctx, validators[2].GetOperator())
|
||||
require.Equal(t, validators[2].ABCIValidatorUpdate(app.StakingKeeper.PowerReduction(ctx)), updates[0])
|
||||
|
||||
// test validtor added at the beginning
|
||||
// tendermintUpdate set: {} -> {c0}
|
||||
app.StakingKeeper.SetValidator(ctx, validators[3])
|
||||
app.StakingKeeper.SetValidatorByPowerIndex(ctx, validators[3])
|
||||
updates = applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 1)
|
||||
validators[3], _ = app.StakingKeeper.GetValidator(ctx, validators[3].GetOperator())
|
||||
require.Equal(t, validators[3].ABCIValidatorUpdate(app.StakingKeeper.PowerReduction(ctx)), updates[0])
|
||||
|
||||
// test validtor added at the end
|
||||
// tendermintUpdate set: {} -> {c0}
|
||||
app.StakingKeeper.SetValidator(ctx, validators[4])
|
||||
app.StakingKeeper.SetValidatorByPowerIndex(ctx, validators[4])
|
||||
updates = applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 1)
|
||||
validators[4], _ = app.StakingKeeper.GetValidator(ctx, validators[4].GetOperator())
|
||||
require.Equal(t, validators[4].ABCIValidatorUpdate(app.StakingKeeper.PowerReduction(ctx)), updates[0])
|
||||
}
|
||||
|
||||
func TestApplyAndReturnValidatorSetUpdatesWithCliffValidator(t *testing.T) {
|
||||
app, ctx, addrs, _ := bootstrapValidatorTest(t, 1000, 20)
|
||||
params := types.DefaultParams()
|
||||
params.MaxValidators = 2
|
||||
app.StakingKeeper.SetParams(ctx, params)
|
||||
|
||||
powers := []int64{10, 20, 5}
|
||||
var validators [5]types.Validator
|
||||
for i, power := range powers {
|
||||
validators[i] = teststaking.NewValidator(t, sdk.ValAddress(addrs[i]), PKs[i])
|
||||
tokens := app.StakingKeeper.TokensFromConsensusPower(ctx, power)
|
||||
validators[i], _ = validators[i].AddTokensFromDel(tokens)
|
||||
}
|
||||
validators[0] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[0], false)
|
||||
validators[1] = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[1], false)
|
||||
applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 2)
|
||||
|
||||
// test validator added at the end but not inserted in the valset
|
||||
// tendermintUpdate set: {} -> {}
|
||||
keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validators[2], false)
|
||||
applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 0)
|
||||
|
||||
// test validator change its power and become a gotValidator (pushing out an existing)
|
||||
// tendermintUpdate set: {} -> {c0, c4}
|
||||
applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 0)
|
||||
|
||||
tokens := app.StakingKeeper.TokensFromConsensusPower(ctx, 10)
|
||||
validators[2], _ = validators[2].AddTokensFromDel(tokens)
|
||||
app.StakingKeeper.SetValidator(ctx, validators[2])
|
||||
app.StakingKeeper.SetValidatorByPowerIndex(ctx, validators[2])
|
||||
updates := applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 2)
|
||||
validators[2], _ = app.StakingKeeper.GetValidator(ctx, validators[2].GetOperator())
|
||||
require.Equal(t, validators[0].ABCIValidatorUpdateZero(), updates[1])
|
||||
require.Equal(t, validators[2].ABCIValidatorUpdate(app.StakingKeeper.PowerReduction(ctx)), updates[0])
|
||||
}
|
||||
|
||||
func TestApplyAndReturnValidatorSetUpdatesNewValidator(t *testing.T) {
|
||||
app, ctx, _, _ := bootstrapValidatorTest(t, 1000, 20)
|
||||
params := app.StakingKeeper.GetParams(ctx)
|
||||
params.MaxValidators = uint32(3)
|
||||
|
||||
app.StakingKeeper.SetParams(ctx, params)
|
||||
|
||||
powers := []int64{100, 100}
|
||||
var validators [2]types.Validator
|
||||
|
||||
// initialize some validators into the state
|
||||
for i, power := range powers {
|
||||
valPubKey := PKs[i+1]
|
||||
valAddr := sdk.ValAddress(valPubKey.Address().Bytes())
|
||||
|
||||
validators[i] = teststaking.NewValidator(t, valAddr, valPubKey)
|
||||
tokens := app.StakingKeeper.TokensFromConsensusPower(ctx, power)
|
||||
validators[i], _ = validators[i].AddTokensFromDel(tokens)
|
||||
|
||||
app.StakingKeeper.SetValidator(ctx, validators[i])
|
||||
app.StakingKeeper.SetValidatorByPowerIndex(ctx, validators[i])
|
||||
}
|
||||
|
||||
// verify initial Tendermint updates are correct
|
||||
updates := applyValidatorSetUpdates(t, ctx, app.StakingKeeper, len(validators))
|
||||
validators[0], _ = app.StakingKeeper.GetValidator(ctx, validators[0].GetOperator())
|
||||
validators[1], _ = app.StakingKeeper.GetValidator(ctx, validators[1].GetOperator())
|
||||
require.Equal(t, validators[0].ABCIValidatorUpdate(app.StakingKeeper.PowerReduction(ctx)), updates[0])
|
||||
require.Equal(t, validators[1].ABCIValidatorUpdate(app.StakingKeeper.PowerReduction(ctx)), updates[1])
|
||||
|
||||
applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 0)
|
||||
|
||||
// update initial validator set
|
||||
for i, power := range powers {
|
||||
|
||||
app.StakingKeeper.DeleteValidatorByPowerIndex(ctx, validators[i])
|
||||
tokens := app.StakingKeeper.TokensFromConsensusPower(ctx, power)
|
||||
validators[i], _ = validators[i].AddTokensFromDel(tokens)
|
||||
|
||||
app.StakingKeeper.SetValidator(ctx, validators[i])
|
||||
app.StakingKeeper.SetValidatorByPowerIndex(ctx, validators[i])
|
||||
}
|
||||
|
||||
// add a new validator that goes from zero power, to non-zero power, back to
|
||||
// zero power
|
||||
valPubKey := PKs[len(validators)+1]
|
||||
valAddr := sdk.ValAddress(valPubKey.Address().Bytes())
|
||||
amt := sdk.NewInt(100)
|
||||
|
||||
validator := teststaking.NewValidator(t, valAddr, valPubKey)
|
||||
validator, _ = validator.AddTokensFromDel(amt)
|
||||
|
||||
app.StakingKeeper.SetValidator(ctx, validator)
|
||||
|
||||
validator, _ = validator.RemoveDelShares(sdk.NewDecFromInt(amt))
|
||||
app.StakingKeeper.SetValidator(ctx, validator)
|
||||
app.StakingKeeper.SetValidatorByPowerIndex(ctx, validator)
|
||||
|
||||
// add a new validator that increases in power
|
||||
valPubKey = PKs[len(validators)+2]
|
||||
valAddr = sdk.ValAddress(valPubKey.Address().Bytes())
|
||||
|
||||
validator = teststaking.NewValidator(t, valAddr, valPubKey)
|
||||
tokens := app.StakingKeeper.TokensFromConsensusPower(ctx, 500)
|
||||
validator, _ = validator.AddTokensFromDel(tokens)
|
||||
app.StakingKeeper.SetValidator(ctx, validator)
|
||||
app.StakingKeeper.SetValidatorByPowerIndex(ctx, validator)
|
||||
|
||||
// verify initial Tendermint updates are correct
|
||||
updates = applyValidatorSetUpdates(t, ctx, app.StakingKeeper, len(validators)+1)
|
||||
validator, _ = app.StakingKeeper.GetValidator(ctx, validator.GetOperator())
|
||||
validators[0], _ = app.StakingKeeper.GetValidator(ctx, validators[0].GetOperator())
|
||||
validators[1], _ = app.StakingKeeper.GetValidator(ctx, validators[1].GetOperator())
|
||||
require.Equal(t, validator.ABCIValidatorUpdate(app.StakingKeeper.PowerReduction(ctx)), updates[0])
|
||||
require.Equal(t, validators[0].ABCIValidatorUpdate(app.StakingKeeper.PowerReduction(ctx)), updates[1])
|
||||
require.Equal(t, validators[1].ABCIValidatorUpdate(app.StakingKeeper.PowerReduction(ctx)), updates[2])
|
||||
}
|
||||
|
||||
func TestApplyAndReturnValidatorSetUpdatesBondTransition(t *testing.T) {
|
||||
app, ctx, _, _ := bootstrapValidatorTest(t, 1000, 20)
|
||||
params := app.StakingKeeper.GetParams(ctx)
|
||||
params.MaxValidators = uint32(2)
|
||||
|
||||
app.StakingKeeper.SetParams(ctx, params)
|
||||
|
||||
powers := []int64{100, 200, 300}
|
||||
var validators [3]types.Validator
|
||||
|
||||
// initialize some validators into the state
|
||||
for i, power := range powers {
|
||||
moniker := fmt.Sprintf("%d", i)
|
||||
valPubKey := PKs[i+1]
|
||||
valAddr := sdk.ValAddress(valPubKey.Address().Bytes())
|
||||
|
||||
validators[i] = newMonikerValidator(t, valAddr, valPubKey, moniker)
|
||||
tokens := app.StakingKeeper.TokensFromConsensusPower(ctx, power)
|
||||
validators[i], _ = validators[i].AddTokensFromDel(tokens)
|
||||
app.StakingKeeper.SetValidator(ctx, validators[i])
|
||||
app.StakingKeeper.SetValidatorByPowerIndex(ctx, validators[i])
|
||||
}
|
||||
|
||||
// verify initial Tendermint updates are correct
|
||||
updates := applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 2)
|
||||
validators[2], _ = app.StakingKeeper.GetValidator(ctx, validators[2].GetOperator())
|
||||
validators[1], _ = app.StakingKeeper.GetValidator(ctx, validators[1].GetOperator())
|
||||
require.Equal(t, validators[2].ABCIValidatorUpdate(app.StakingKeeper.PowerReduction(ctx)), updates[0])
|
||||
require.Equal(t, validators[1].ABCIValidatorUpdate(app.StakingKeeper.PowerReduction(ctx)), updates[1])
|
||||
|
||||
applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 0)
|
||||
|
||||
// delegate to validator with lowest power but not enough to bond
|
||||
ctx = ctx.WithBlockHeight(1)
|
||||
|
||||
var found bool
|
||||
validators[0], found = app.StakingKeeper.GetValidator(ctx, validators[0].GetOperator())
|
||||
require.True(t, found)
|
||||
|
||||
app.StakingKeeper.DeleteValidatorByPowerIndex(ctx, validators[0])
|
||||
tokens := app.StakingKeeper.TokensFromConsensusPower(ctx, 1)
|
||||
validators[0], _ = validators[0].AddTokensFromDel(tokens)
|
||||
app.StakingKeeper.SetValidator(ctx, validators[0])
|
||||
app.StakingKeeper.SetValidatorByPowerIndex(ctx, validators[0])
|
||||
|
||||
// verify initial Tendermint updates are correct
|
||||
applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 0)
|
||||
|
||||
// create a series of events that will bond and unbond the validator with
|
||||
// lowest power in a single block context (height)
|
||||
ctx = ctx.WithBlockHeight(2)
|
||||
|
||||
validators[1], found = app.StakingKeeper.GetValidator(ctx, validators[1].GetOperator())
|
||||
require.True(t, found)
|
||||
|
||||
app.StakingKeeper.DeleteValidatorByPowerIndex(ctx, validators[0])
|
||||
validators[0], _ = validators[0].RemoveDelShares(validators[0].DelegatorShares)
|
||||
app.StakingKeeper.SetValidator(ctx, validators[0])
|
||||
app.StakingKeeper.SetValidatorByPowerIndex(ctx, validators[0])
|
||||
applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 0)
|
||||
|
||||
app.StakingKeeper.DeleteValidatorByPowerIndex(ctx, validators[1])
|
||||
tokens = app.StakingKeeper.TokensFromConsensusPower(ctx, 250)
|
||||
validators[1], _ = validators[1].AddTokensFromDel(tokens)
|
||||
app.StakingKeeper.SetValidator(ctx, validators[1])
|
||||
app.StakingKeeper.SetValidatorByPowerIndex(ctx, validators[1])
|
||||
|
||||
// verify initial Tendermint updates are correct
|
||||
updates = applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 1)
|
||||
require.Equal(t, validators[1].ABCIValidatorUpdate(app.StakingKeeper.PowerReduction(ctx)), updates[0])
|
||||
|
||||
applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 0)
|
||||
}
|
||||
|
||||
func applyValidatorSetUpdates(t *testing.T, ctx sdk.Context, k *keeper.Keeper, expectedUpdatesLen int) []abci.ValidatorUpdate {
|
||||
updates, err := k.ApplyAndReturnValidatorSetUpdates(ctx)
|
||||
require.NoError(t, err)
|
||||
if expectedUpdatesLen >= 0 {
|
||||
require.Equal(t, expectedUpdatesLen, len(updates), "%v", updates)
|
||||
}
|
||||
return updates
|
||||
}
|
||||
Reference in New Issue
Block a user