package staking_test import ( "strings" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" abci "github.com/tendermint/tendermint/abci/types" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" tmtypes "github.com/tendermint/tendermint/types" "github.com/golang/protobuf/proto" cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" "github.com/cosmos/cosmos-sdk/simapp" "github.com/cosmos/cosmos-sdk/testutil/testdata" sdk "github.com/cosmos/cosmos-sdk/types" "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 bootstrapHandlerGenesisTest(t *testing.T, power int64, numAddrs int, accAmount sdk.Int) (*simapp.SimApp, sdk.Context, []sdk.AccAddress, []sdk.ValAddress) { _, app, ctx := getBaseSimappWithCustomKeeper() addrDels, addrVals := generateAddresses(app, ctx, numAddrs, accAmount) 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 non bonded pool balance app.AccountKeeper.SetModuleAccount(ctx, notBondedPool) require.NoError(t, simapp.FundModuleAccount(app.BankKeeper, ctx, notBondedPool.GetName(), totalSupply)) return app, ctx, addrDels, addrVals } func TestValidatorByPowerIndex(t *testing.T) { initPower := int64(1000000) app, ctx, _, valAddrs := bootstrapHandlerGenesisTest(t, initPower, 10, sdk.TokensFromConsensusPower(initPower, sdk.DefaultPowerReduction)) validatorAddr, validatorAddr3 := valAddrs[0], valAddrs[1] tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) // create validator initBond := tstaking.CreateValidatorWithValPower(validatorAddr, PKs[0], initPower, true) // must end-block updates, err := app.StakingKeeper.ApplyAndReturnValidatorSetUpdates(ctx) require.NoError(t, err) require.Equal(t, 1, len(updates)) // verify the self-delegation exists bond, found := app.StakingKeeper.GetDelegation(ctx, sdk.AccAddress(validatorAddr), validatorAddr) require.True(t, found) gotBond := bond.Shares.RoundInt() require.Equal(t, initBond, gotBond) // verify that the by power index exists validator, found := app.StakingKeeper.GetValidator(ctx, validatorAddr) require.True(t, found) power := types.GetValidatorsByPowerIndexKey(validator, app.StakingKeeper.PowerReduction(ctx)) require.True(t, keeper.ValidatorByPowerIndexExists(ctx, app.StakingKeeper, power)) // create a second validator keep it bonded tstaking.CreateValidatorWithValPower(validatorAddr3, PKs[2], initPower, true) // must end-block updates, err = app.StakingKeeper.ApplyAndReturnValidatorSetUpdates(ctx) require.NoError(t, err) require.Equal(t, 1, len(updates)) // slash and jail the first validator consAddr0 := sdk.ConsAddress(PKs[0].Address()) app.StakingKeeper.Slash(ctx, consAddr0, 0, initPower, sdk.NewDecWithPrec(5, 1)) app.StakingKeeper.Jail(ctx, consAddr0) app.StakingKeeper.ApplyAndReturnValidatorSetUpdates(ctx) validator, found = app.StakingKeeper.GetValidator(ctx, validatorAddr) require.True(t, found) require.Equal(t, types.Unbonding, validator.Status) // ensure is unbonding require.Equal(t, initBond.QuoRaw(2), validator.Tokens) // ensure tokens slashed app.StakingKeeper.Unjail(ctx, consAddr0) // the old power record should have been deleted as the power changed require.False(t, keeper.ValidatorByPowerIndexExists(ctx, app.StakingKeeper, power)) // but the new power record should have been created validator, found = app.StakingKeeper.GetValidator(ctx, validatorAddr) require.True(t, found) power2 := types.GetValidatorsByPowerIndexKey(validator, app.StakingKeeper.PowerReduction(ctx)) require.True(t, keeper.ValidatorByPowerIndexExists(ctx, app.StakingKeeper, power2)) // now the new record power index should be the same as the original record power3 := types.GetValidatorsByPowerIndexKey(validator, app.StakingKeeper.PowerReduction(ctx)) require.Equal(t, power2, power3) // unbond self-delegation totalBond := validator.TokensFromShares(bond.GetShares()).TruncateInt() res := tstaking.Undelegate(sdk.AccAddress(validatorAddr), validatorAddr, totalBond, true) var resData types.MsgUndelegateResponse err = proto.Unmarshal(res.Data, &resData) require.NoError(t, err) ctx = ctx.WithBlockTime(resData.CompletionTime) staking.EndBlocker(ctx, app.StakingKeeper) staking.EndBlocker(ctx, app.StakingKeeper) // verify that by power key nolonger exists _, found = app.StakingKeeper.GetValidator(ctx, validatorAddr) require.False(t, found) require.False(t, keeper.ValidatorByPowerIndexExists(ctx, app.StakingKeeper, power3)) } func TestDuplicatesMsgCreateValidator(t *testing.T) { initPower := int64(1000000) app, ctx, _, valAddrs := bootstrapHandlerGenesisTest(t, initPower, 10, sdk.TokensFromConsensusPower(initPower, sdk.DefaultPowerReduction)) addr1, addr2 := valAddrs[0], valAddrs[1] pk1, pk2 := PKs[0], PKs[1] tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) valTokens := tstaking.CreateValidatorWithValPower(addr1, pk1, 10, true) app.StakingKeeper.ApplyAndReturnValidatorSetUpdates(ctx) validator := tstaking.CheckValidator(addr1, types.Bonded, false) assert.Equal(t, addr1.String(), validator.OperatorAddress) consKey, err := validator.TmConsPublicKey() require.NoError(t, err) tmPk1, err := cryptocodec.ToTmProtoPublicKey(pk1) require.NoError(t, err) assert.Equal(t, tmPk1, consKey) assert.Equal(t, valTokens, validator.BondedTokens()) assert.Equal(t, valTokens.ToDec(), validator.DelegatorShares) assert.Equal(t, types.Description{}, validator.Description) // two validators can't have the same operator address tstaking.CreateValidator(addr1, pk2, valTokens, false) // two validators can't have the same pubkey tstaking.CreateValidator(addr2, pk1, valTokens, false) // must have different pubkey and operator tstaking.CreateValidator(addr2, pk2, valTokens, true) // must end-block updates, err := app.StakingKeeper.ApplyAndReturnValidatorSetUpdates(ctx) require.NoError(t, err) require.Equal(t, 1, len(updates)) validator = tstaking.CheckValidator(addr2, types.Bonded, false) assert.Equal(t, addr2.String(), validator.OperatorAddress) consPk, err := validator.TmConsPublicKey() require.NoError(t, err) tmPk2, err := cryptocodec.ToTmProtoPublicKey(pk2) require.NoError(t, err) assert.Equal(t, tmPk2, consPk) assert.True(sdk.IntEq(t, valTokens, validator.Tokens)) assert.True(sdk.DecEq(t, valTokens.ToDec(), validator.DelegatorShares)) assert.Equal(t, types.Description{}, validator.Description) } func TestInvalidPubKeyTypeMsgCreateValidator(t *testing.T) { initPower := int64(1000) app, ctx, _, valAddrs := bootstrapHandlerGenesisTest(t, initPower, 1, sdk.TokensFromConsensusPower(initPower, sdk.DefaultPowerReduction)) ctx = ctx.WithConsensusParams(&abci.ConsensusParams{ Validator: &tmproto.ValidatorParams{PubKeyTypes: []string{tmtypes.ABCIPubKeyTypeEd25519}}, }) addr := valAddrs[0] invalidPk := secp256k1.GenPrivKey().PubKey() tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) // invalid pukKey type should not be allowed tstaking.CreateValidator(addr, invalidPk, sdk.NewInt(10), false) } func TestBothPubKeyTypesMsgCreateValidator(t *testing.T) { app, ctx, _, valAddrs := bootstrapHandlerGenesisTest(t, 1000, 2, sdk.NewInt(1000)) ctx = ctx.WithConsensusParams(&abci.ConsensusParams{ Validator: &tmproto.ValidatorParams{PubKeyTypes: []string{tmtypes.ABCIPubKeyTypeEd25519, tmtypes.ABCIPubKeyTypeSecp256k1}}, }) tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) testCases := []struct { name string addr sdk.ValAddress pk cryptotypes.PubKey }{ { "can create a validator with ed25519 pubkey", valAddrs[0], ed25519.GenPrivKey().PubKey(), }, { "can create a validator with secp256k1 pubkey", valAddrs[1], secp256k1.GenPrivKey().PubKey(), }, } for _, tc := range testCases { t.Run(tc.name, func(*testing.T) { tstaking.CreateValidator(tc.addr, tc.pk, sdk.NewInt(10), true) }) } } func TestLegacyValidatorDelegations(t *testing.T) { initPower := int64(1000) app, ctx, delAddrs, valAddrs := bootstrapHandlerGenesisTest(t, initPower, 2, sdk.TokensFromConsensusPower(initPower, sdk.DefaultPowerReduction)) tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) valAddr := valAddrs[0] valConsPubKey, valConsAddr := PKs[0], sdk.ConsAddress(PKs[0].Address()) delAddr := delAddrs[1] // create validator bondAmount := tstaking.CreateValidatorWithValPower(valAddr, valConsPubKey, 10, true) // must end-block updates, err := app.StakingKeeper.ApplyAndReturnValidatorSetUpdates(ctx) require.NoError(t, err) require.Equal(t, 1, len(updates)) // verify the validator exists and has the correct attributes validator := tstaking.CheckValidator(valAddr, types.Bonded, false) require.Equal(t, bondAmount, validator.DelegatorShares.RoundInt()) require.Equal(t, bondAmount, validator.BondedTokens()) // delegate tokens to the validator tstaking.Delegate(delAddr, valAddr, bondAmount) // verify validator bonded shares validator = tstaking.CheckValidator(valAddr, types.Bonded, false) require.Equal(t, bondAmount.MulRaw(2), validator.DelegatorShares.RoundInt()) require.Equal(t, bondAmount.MulRaw(2), validator.BondedTokens()) // unbond validator total self-delegations (which should jail the validator) res := tstaking.Undelegate(sdk.AccAddress(valAddr), valAddr, bondAmount, true) var resData types.MsgUndelegateResponse err = proto.Unmarshal(res.Data, &resData) require.NoError(t, err) ctx = ctx.WithBlockTime(resData.CompletionTime) tstaking.Ctx = ctx staking.EndBlocker(ctx, app.StakingKeeper) // verify the validator record still exists, is jailed, and has correct tokens validator = tstaking.CheckValidator(valAddr, -1, true) require.Equal(t, bondAmount, validator.Tokens) // verify delegation still exists bond, found := app.StakingKeeper.GetDelegation(ctx, delAddr, valAddr) require.True(t, found) require.Equal(t, bondAmount, bond.Shares.RoundInt()) require.Equal(t, bondAmount, validator.DelegatorShares.RoundInt()) // verify the validator can still self-delegate tstaking.Delegate(sdk.AccAddress(valAddr), valAddr, bondAmount) // verify validator bonded shares validator, found = app.StakingKeeper.GetValidator(ctx, valAddr) require.True(t, found) require.Equal(t, bondAmount.MulRaw(2), validator.DelegatorShares.RoundInt()) require.Equal(t, bondAmount.MulRaw(2), validator.Tokens) // unjail the validator now that is has non-zero self-delegated shares app.StakingKeeper.Unjail(ctx, valConsAddr) // verify the validator can now accept delegations tstaking.Delegate(delAddr, valAddr, bondAmount) // verify validator bonded shares validator, found = app.StakingKeeper.GetValidator(ctx, valAddr) require.True(t, found) require.Equal(t, bondAmount.MulRaw(3), validator.DelegatorShares.RoundInt()) require.Equal(t, bondAmount.MulRaw(3), validator.Tokens) // verify new delegation bond, found = app.StakingKeeper.GetDelegation(ctx, delAddr, valAddr) require.True(t, found) require.Equal(t, bondAmount.MulRaw(2), bond.Shares.RoundInt()) require.Equal(t, bondAmount.MulRaw(3), validator.DelegatorShares.RoundInt()) } func TestIncrementsMsgDelegate(t *testing.T) { initPower := int64(1000) initBond := sdk.TokensFromConsensusPower(initPower, sdk.DefaultPowerReduction) app, ctx, delAddrs, valAddrs := bootstrapHandlerGenesisTest(t, initPower, 2, sdk.TokensFromConsensusPower(initPower, sdk.DefaultPowerReduction)) params := app.StakingKeeper.GetParams(ctx) validatorAddr, delegatorAddr := valAddrs[0], delAddrs[1] tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) // first create validator bondAmount := tstaking.CreateValidatorWithValPower(validatorAddr, PKs[0], 10, true) // apply TM updates app.StakingKeeper.ApplyAndReturnValidatorSetUpdates(ctx) validator := tstaking.CheckValidator(validatorAddr, types.Bonded, false) require.Equal(t, bondAmount, validator.DelegatorShares.RoundInt()) require.Equal(t, bondAmount, validator.BondedTokens(), "validator: %v", validator) tstaking.CheckDelegator(delegatorAddr, validatorAddr, false) bond, found := app.StakingKeeper.GetDelegation(ctx, sdk.AccAddress(validatorAddr), validatorAddr) require.True(t, found) require.Equal(t, bondAmount, bond.Shares.RoundInt()) bondedTokens := app.StakingKeeper.TotalBondedTokens(ctx) require.Equal(t, bondAmount, bondedTokens) for i := int64(0); i < 5; i++ { ctx = ctx.WithBlockHeight(i) tstaking.Ctx = ctx tstaking.Delegate(delegatorAddr, validatorAddr, bondAmount) //Check that the accounts and the bond account have the appropriate values validator, found := app.StakingKeeper.GetValidator(ctx, validatorAddr) require.True(t, found) bond, found := app.StakingKeeper.GetDelegation(ctx, delegatorAddr, validatorAddr) require.True(t, found) expBond := bondAmount.MulRaw(i + 1) expDelegatorShares := bondAmount.MulRaw(i + 2) // (1 self delegation) expDelegatorAcc := initBond.Sub(expBond) gotBond := bond.Shares.RoundInt() gotDelegatorShares := validator.DelegatorShares.RoundInt() gotDelegatorAcc := app.BankKeeper.GetBalance(ctx, delegatorAddr, params.BondDenom).Amount require.Equal(t, expBond, gotBond, "i: %v\nexpBond: %v\ngotBond: %v\nvalidator: %v\nbond: %v\n", i, expBond, gotBond, validator, bond) require.Equal(t, expDelegatorShares, gotDelegatorShares, "i: %v\nexpDelegatorShares: %v\ngotDelegatorShares: %v\nvalidator: %v\nbond: %v\n", i, expDelegatorShares, gotDelegatorShares, validator, bond) require.Equal(t, expDelegatorAcc, gotDelegatorAcc, "i: %v\nexpDelegatorAcc: %v\ngotDelegatorAcc: %v\nvalidator: %v\nbond: %v\n", i, expDelegatorAcc, gotDelegatorAcc, validator, bond) } } func TestEditValidatorDecreaseMinSelfDelegation(t *testing.T) { initPower := int64(100) initBond := sdk.TokensFromConsensusPower(100, sdk.DefaultPowerReduction) app, ctx, _, valAddrs := bootstrapHandlerGenesisTest(t, initPower, 1, sdk.TokensFromConsensusPower(initPower, sdk.DefaultPowerReduction)) validatorAddr := valAddrs[0] tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) // create validator msgCreateValidator := tstaking.CreateValidatorMsg(validatorAddr, PKs[0], initBond) msgCreateValidator.MinSelfDelegation = sdk.NewInt(2) tstaking.Handle(msgCreateValidator, true) // must end-block updates, err := app.StakingKeeper.ApplyAndReturnValidatorSetUpdates(ctx) require.NoError(t, err) require.Equal(t, 1, len(updates)) // verify the self-delegation exists bond, found := app.StakingKeeper.GetDelegation(ctx, sdk.AccAddress(validatorAddr), validatorAddr) require.True(t, found) gotBond := bond.Shares.RoundInt() require.Equal(t, initBond, gotBond, "initBond: %v\ngotBond: %v\nbond: %v\n", initBond, gotBond, bond) newMinSelfDelegation := sdk.OneInt() msgEditValidator := types.NewMsgEditValidator(validatorAddr, types.Description{}, nil, &newMinSelfDelegation) tstaking.Handle(msgEditValidator, false) } func TestEditValidatorIncreaseMinSelfDelegationBeyondCurrentBond(t *testing.T) { initPower := int64(100) initBond := sdk.TokensFromConsensusPower(100, sdk.DefaultPowerReduction) app, ctx, _, valAddrs := bootstrapHandlerGenesisTest(t, initPower, 2, sdk.TokensFromConsensusPower(initPower, sdk.DefaultPowerReduction)) validatorAddr := valAddrs[0] tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) // create validator msgCreateValidator := tstaking.CreateValidatorMsg(validatorAddr, PKs[0], initBond) msgCreateValidator.MinSelfDelegation = sdk.NewInt(2) tstaking.Handle(msgCreateValidator, true) // must end-block updates, err := app.StakingKeeper.ApplyAndReturnValidatorSetUpdates(ctx) require.NoError(t, err) require.Equal(t, 1, len(updates)) // verify the self-delegation exists bond, found := app.StakingKeeper.GetDelegation(ctx, sdk.AccAddress(validatorAddr), validatorAddr) require.True(t, found) gotBond := bond.Shares.RoundInt() require.Equal(t, initBond, gotBond, "initBond: %v\ngotBond: %v\nbond: %v\n", initBond, gotBond, bond) newMinSelfDelegation := initBond.Add(sdk.OneInt()) msgEditValidator := types.NewMsgEditValidator(validatorAddr, types.Description{}, nil, &newMinSelfDelegation) tstaking.Handle(msgEditValidator, false) } func TestIncrementsMsgUnbond(t *testing.T) { initPower := int64(1000) app, ctx, delAddrs, valAddrs := bootstrapHandlerGenesisTest(t, initPower, 2, sdk.TokensFromConsensusPower(initPower, sdk.DefaultPowerReduction)) tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) params := app.StakingKeeper.GetParams(ctx) denom := params.BondDenom // create validator, delegate validatorAddr, delegatorAddr := valAddrs[0], delAddrs[1] initBond := tstaking.CreateValidatorWithValPower(validatorAddr, PKs[0], initPower, true) // initial balance amt1 := app.BankKeeper.GetBalance(ctx, delegatorAddr, denom).Amount tstaking.Delegate(delegatorAddr, validatorAddr, initBond) // balance should have been subtracted after delegation amt2 := app.BankKeeper.GetBalance(ctx, delegatorAddr, denom).Amount require.True(sdk.IntEq(t, amt1.Sub(initBond), amt2)) // apply TM updates app.StakingKeeper.ApplyAndReturnValidatorSetUpdates(ctx) validator, found := app.StakingKeeper.GetValidator(ctx, validatorAddr) require.True(t, found) require.Equal(t, initBond.MulRaw(2), validator.DelegatorShares.RoundInt()) require.Equal(t, initBond.MulRaw(2), validator.BondedTokens()) // just send the same msgUnbond multiple times // TODO use decimals here unbondAmt := sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(10)) msgUndelegate := types.NewMsgUndelegate(delegatorAddr, validatorAddr, unbondAmt) numUnbonds := int64(5) for i := int64(0); i < numUnbonds; i++ { res := tstaking.Handle(msgUndelegate, true) var resData types.MsgUndelegateResponse err := proto.Unmarshal(res.Data, &resData) require.NoError(t, err) ctx = ctx.WithBlockTime(resData.CompletionTime) tstaking.Ctx = ctx staking.EndBlocker(ctx, app.StakingKeeper) // check that the accounts and the bond account have the appropriate values validator, found = app.StakingKeeper.GetValidator(ctx, validatorAddr) require.True(t, found) bond, found := app.StakingKeeper.GetDelegation(ctx, delegatorAddr, validatorAddr) require.True(t, found) expBond := initBond.Sub(unbondAmt.Amount.Mul(sdk.NewInt(i + 1))) expDelegatorShares := initBond.MulRaw(2).Sub(unbondAmt.Amount.Mul(sdk.NewInt(i + 1))) expDelegatorAcc := initBond.Sub(expBond) gotBond := bond.Shares.RoundInt() gotDelegatorShares := validator.DelegatorShares.RoundInt() gotDelegatorAcc := app.BankKeeper.GetBalance(ctx, delegatorAddr, params.BondDenom).Amount require.Equal(t, expBond, gotBond, "i: %v\nexpBond: %v\ngotBond: %v\nvalidator: %v\nbond: %v\n", i, expBond, gotBond, validator, bond) require.Equal(t, expDelegatorShares, gotDelegatorShares, "i: %v\nexpDelegatorShares: %v\ngotDelegatorShares: %v\nvalidator: %v\nbond: %v\n", i, expDelegatorShares, gotDelegatorShares, validator, bond) require.Equal(t, expDelegatorAcc, gotDelegatorAcc, "i: %v\nexpDelegatorAcc: %v\ngotDelegatorAcc: %v\nvalidator: %v\nbond: %v\n", i, expDelegatorAcc, gotDelegatorAcc, validator, bond) } // these are more than we have bonded now errorCases := []sdk.Int{ //1<<64 - 1, // more than int64 power //1<<63 + 1, // more than int64 power app.StakingKeeper.TokensFromConsensusPower(ctx, 1<<63-1), app.StakingKeeper.TokensFromConsensusPower(ctx, 1<<31), initBond, } for _, c := range errorCases { tstaking.Undelegate(delegatorAddr, validatorAddr, c, false) } // should be able to unbond remaining leftBonded := initBond.Sub(unbondAmt.Amount.Mul(sdk.NewInt(numUnbonds))) tstaking.Undelegate(delegatorAddr, validatorAddr, leftBonded, true) } func TestMultipleMsgCreateValidator(t *testing.T) { initPower := int64(1000) initTokens := sdk.TokensFromConsensusPower(initPower, sdk.DefaultPowerReduction) app, ctx, delAddrs, valAddrs := bootstrapHandlerGenesisTest(t, initPower, 3, sdk.TokensFromConsensusPower(initPower, sdk.DefaultPowerReduction)) params := app.StakingKeeper.GetParams(ctx) blockTime := time.Now().UTC() ctx = ctx.WithBlockTime(blockTime) tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) validatorAddrs := []sdk.ValAddress{ valAddrs[0], valAddrs[1], valAddrs[2], } delegatorAddrs := []sdk.AccAddress{ delAddrs[0], delAddrs[1], delAddrs[2], } // bond them all amt := app.StakingKeeper.TokensFromConsensusPower(ctx, 10) for i, validatorAddr := range validatorAddrs { tstaking.CreateValidator(validatorAddr, PKs[i], amt, true) // verify that the account is bonded validators := app.StakingKeeper.GetValidators(ctx, 100) require.Equal(t, (i + 1), len(validators)) val := validators[i] balanceExpd := initTokens.Sub(amt) balanceGot := app.BankKeeper.GetBalance(ctx, delegatorAddrs[i], params.BondDenom).Amount require.Equal(t, i+1, len(validators), "expected %d validators got %d, validators: %v", i+1, len(validators), validators) require.Equal(t, amt, val.DelegatorShares.RoundInt(), "expected %d shares, got %d", amt, val.DelegatorShares) require.Equal(t, balanceExpd, balanceGot, "expected account to have %d, got %d", balanceExpd, balanceGot) } staking.EndBlocker(ctx, app.StakingKeeper) // unbond them all by removing delegation for i, validatorAddr := range validatorAddrs { _, found := app.StakingKeeper.GetValidator(ctx, validatorAddr) require.True(t, found) res := tstaking.Undelegate(delegatorAddrs[i], validatorAddr, amt, true) var resData types.MsgUndelegateResponse err := proto.Unmarshal(res.Data, &resData) require.NoError(t, err) // adds validator into unbonding queue staking.EndBlocker(ctx, app.StakingKeeper) // removes validator from queue and set staking.EndBlocker(ctx.WithBlockTime(blockTime.Add(params.UnbondingTime)), app.StakingKeeper) // Check that the validator is deleted from state validators := app.StakingKeeper.GetValidators(ctx, 100) require.Equal(t, len(validatorAddrs)-(i+1), len(validators), "expected %d validators got %d", len(validatorAddrs)-(i+1), len(validators)) _, found = app.StakingKeeper.GetValidator(ctx, validatorAddr) require.False(t, found) gotBalance := app.BankKeeper.GetBalance(ctx, delegatorAddrs[i], params.BondDenom).Amount require.Equal(t, initTokens, gotBalance, "expected account to have %d, got %d", initTokens, gotBalance) } } func TestMultipleMsgDelegate(t *testing.T) { initPower := int64(1000) app, ctx, delAddrs, valAddrs := bootstrapHandlerGenesisTest(t, initPower, 50, sdk.TokensFromConsensusPower(initPower, sdk.DefaultPowerReduction)) validatorAddr, delegatorAddrs := valAddrs[0], delAddrs[1:] tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) var amount int64 = 10 // first make a validator tstaking.CreateValidator(validatorAddr, PKs[0], sdk.NewInt(amount), true) // delegate multiple parties for _, delegatorAddr := range delegatorAddrs { tstaking.Delegate(delegatorAddr, validatorAddr, sdk.NewInt(10)) tstaking.CheckDelegator(delegatorAddr, validatorAddr, true) } // unbond them all for _, delegatorAddr := range delegatorAddrs { res := tstaking.Undelegate(delegatorAddr, validatorAddr, sdk.NewInt(amount), true) var resData types.MsgUndelegateResponse err := proto.Unmarshal(res.Data, &resData) require.NoError(t, err) ctx = ctx.WithBlockTime(resData.CompletionTime) staking.EndBlocker(ctx, app.StakingKeeper) tstaking.Ctx = ctx // check that the account is unbonded _, found := app.StakingKeeper.GetDelegation(ctx, delegatorAddr, validatorAddr) require.False(t, found) } } func TestJailValidator(t *testing.T) { initPower := int64(1000) app, ctx, delAddrs, valAddrs := bootstrapHandlerGenesisTest(t, initPower, 2, sdk.TokensFromConsensusPower(initPower, sdk.DefaultPowerReduction)) validatorAddr, delegatorAddr := valAddrs[0], delAddrs[1] tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) var amt int64 = 10 // create the validator and delegate tstaking.CreateValidator(validatorAddr, PKs[0], sdk.NewInt(amt), true) tstaking.Delegate(delegatorAddr, validatorAddr, sdk.NewInt(amt)) // unbond the validators bond portion unamt := sdk.NewInt(amt) res := tstaking.Undelegate(sdk.AccAddress(validatorAddr), validatorAddr, unamt, true) var resData types.MsgUndelegateResponse err := proto.Unmarshal(res.Data, &resData) require.NoError(t, err) ctx = ctx.WithBlockTime(resData.CompletionTime) staking.EndBlocker(ctx, app.StakingKeeper) tstaking.Ctx = ctx tstaking.CheckValidator(validatorAddr, -1, true) // test that the delegator can still withdraw their bonds tstaking.Undelegate(delegatorAddr, validatorAddr, unamt, true) err = proto.Unmarshal(res.Data, &resData) require.NoError(t, err) ctx = ctx.WithBlockTime(resData.CompletionTime) staking.EndBlocker(ctx, app.StakingKeeper) tstaking.Ctx = ctx // verify that the pubkey can now be reused tstaking.CreateValidator(validatorAddr, PKs[0], sdk.NewInt(amt), true) } func TestValidatorQueue(t *testing.T) { initPower := int64(1000) app, ctx, delAddrs, valAddrs := bootstrapHandlerGenesisTest(t, initPower, 2, sdk.TokensFromConsensusPower(initPower, sdk.DefaultPowerReduction)) validatorAddr, delegatorAddr := valAddrs[0], delAddrs[1] tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) // set the unbonding time params := app.StakingKeeper.GetParams(ctx) params.UnbondingTime = 7 * time.Second app.StakingKeeper.SetParams(ctx, params) // create the validator and make a bond amt := tstaking.CreateValidatorWithValPower(validatorAddr, PKs[0], 10, true) tstaking.Delegate(delegatorAddr, validatorAddr, amt) staking.EndBlocker(ctx, app.StakingKeeper) // unbond the all self-delegation to put validator in unbonding state res := tstaking.Undelegate(sdk.AccAddress(validatorAddr), validatorAddr, amt, true) var resData types.MsgUndelegateResponse err := proto.Unmarshal(res.Data, &resData) require.NoError(t, err) finishTime := resData.CompletionTime ctx = tstaking.TurnBlock(finishTime) origHeader := ctx.BlockHeader() validator, found := app.StakingKeeper.GetValidator(ctx, validatorAddr) require.True(t, found) require.True(t, validator.IsUnbonding(), "%v", validator) // should still be unbonding at time 6 seconds later ctx = tstaking.TurnBlock(origHeader.Time.Add(time.Second * 6)) validator, found = app.StakingKeeper.GetValidator(ctx, validatorAddr) require.True(t, found) require.True(t, validator.IsUnbonding(), "%v", validator) // should be in unbonded state at time 7 seconds later ctx = tstaking.TurnBlock(origHeader.Time.Add(time.Second * 7)) validator, found = app.StakingKeeper.GetValidator(ctx, validatorAddr) require.True(t, found) require.True(t, validator.IsUnbonded(), "%v", validator) } func TestUnbondingPeriod(t *testing.T) { initPower := int64(1000) app, ctx, _, valAddrs := bootstrapHandlerGenesisTest(t, initPower, 1, sdk.TokensFromConsensusPower(initPower, sdk.DefaultPowerReduction)) validatorAddr := valAddrs[0] tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) // set the unbonding time params := app.StakingKeeper.GetParams(ctx) params.UnbondingTime = 7 * time.Second app.StakingKeeper.SetParams(ctx, params) // create the validator amt := tstaking.CreateValidatorWithValPower(validatorAddr, PKs[0], 10, true) staking.EndBlocker(ctx, app.StakingKeeper) // begin unbonding tstaking.Undelegate(sdk.AccAddress(validatorAddr), validatorAddr, amt, true) origHeader := ctx.BlockHeader() _, found := app.StakingKeeper.GetUnbondingDelegation(ctx, sdk.AccAddress(validatorAddr), validatorAddr) require.True(t, found, "should not have unbonded") // cannot complete unbonding at same time staking.EndBlocker(ctx, app.StakingKeeper) _, found = app.StakingKeeper.GetUnbondingDelegation(ctx, sdk.AccAddress(validatorAddr), validatorAddr) require.True(t, found, "should not have unbonded") // cannot complete unbonding at time 6 seconds later ctx = tstaking.TurnBlock(origHeader.Time.Add(time.Second * 6)) _, found = app.StakingKeeper.GetUnbondingDelegation(ctx, sdk.AccAddress(validatorAddr), validatorAddr) require.True(t, found, "should not have unbonded") // can complete unbonding at time 7 seconds later ctx = tstaking.TurnBlock(origHeader.Time.Add(time.Second * 7)) _, found = app.StakingKeeper.GetUnbondingDelegation(ctx, sdk.AccAddress(validatorAddr), validatorAddr) require.False(t, found, "should have unbonded") } func TestUnbondingFromUnbondingValidator(t *testing.T) { initPower := int64(1000) app, ctx, delAddrs, valAddrs := bootstrapHandlerGenesisTest(t, initPower, 2, sdk.TokensFromConsensusPower(initPower, sdk.DefaultPowerReduction)) validatorAddr, delegatorAddr := valAddrs[0], delAddrs[1] tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) // create the validator and delegate tstaking.CreateValidator(validatorAddr, PKs[0], sdk.NewInt(10), true) tstaking.Delegate(delegatorAddr, validatorAddr, sdk.NewInt(10)) // unbond the validators bond portion unbondAmt := sdk.NewInt(10) res := tstaking.Undelegate(sdk.AccAddress(validatorAddr), validatorAddr, unbondAmt, true) // change the ctx to Block Time one second before the validator would have unbonded var resData types.MsgUndelegateResponse err := proto.Unmarshal(res.Data, &resData) require.NoError(t, err) ctx = ctx.WithBlockTime(resData.CompletionTime.Add(time.Second * -1)) // unbond the delegator from the validator res = tstaking.Undelegate(delegatorAddr, validatorAddr, unbondAmt, true) ctx = tstaking.TurnBlockTimeDiff(app.StakingKeeper.UnbondingTime(ctx)) tstaking.Ctx = ctx // Check to make sure that the unbonding delegation is no longer in state // (meaning it was deleted in the above EndBlocker) _, found := app.StakingKeeper.GetUnbondingDelegation(ctx, delegatorAddr, validatorAddr) require.False(t, found, "should be removed from state") } func TestRedelegationPeriod(t *testing.T) { initPower := int64(1000) app, ctx, _, valAddrs := bootstrapHandlerGenesisTest(t, initPower, 2, sdk.TokensFromConsensusPower(initPower, sdk.DefaultPowerReduction)) validatorAddr, validatorAddr2 := valAddrs[0], valAddrs[1] denom := app.StakingKeeper.GetParams(ctx).BondDenom tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) // set the unbonding time params := app.StakingKeeper.GetParams(ctx) params.UnbondingTime = 7 * time.Second app.StakingKeeper.SetParams(ctx, params) // initial balance amt1 := app.BankKeeper.GetBalance(ctx, sdk.AccAddress(validatorAddr), denom).Amount // create the validators tstaking.CreateValidator(validatorAddr, PKs[0], sdk.NewInt(10), true) // balance should have been subtracted after creation amt2 := app.BankKeeper.GetBalance(ctx, sdk.AccAddress(validatorAddr), denom).Amount require.Equal(t, amt1.Sub(sdk.NewInt(10)), amt2, "expected coins to be subtracted") tstaking.CreateValidator(validatorAddr2, PKs[1], sdk.NewInt(10), true) bal1 := app.BankKeeper.GetAllBalances(ctx, sdk.AccAddress(validatorAddr)) // begin redelegate redAmt := sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(10)) msgBeginRedelegate := types.NewMsgBeginRedelegate(sdk.AccAddress(validatorAddr), validatorAddr, validatorAddr2, redAmt) tstaking.Handle(msgBeginRedelegate, true) // origin account should not lose tokens as with a regular delegation bal2 := app.BankKeeper.GetAllBalances(ctx, sdk.AccAddress(validatorAddr)) require.Equal(t, bal1, bal2) origHeader := ctx.BlockHeader() // cannot complete redelegation at same time staking.EndBlocker(ctx, app.StakingKeeper) _, found := app.StakingKeeper.GetRedelegation(ctx, sdk.AccAddress(validatorAddr), validatorAddr, validatorAddr2) require.True(t, found, "should not have unbonded") // cannot complete redelegation at time 6 seconds later ctx = tstaking.TurnBlock(origHeader.Time.Add(time.Second * 6)) _, found = app.StakingKeeper.GetRedelegation(ctx, sdk.AccAddress(validatorAddr), validatorAddr, validatorAddr2) require.True(t, found, "should not have unbonded") // can complete redelegation at time 7 seconds later ctx = tstaking.TurnBlock(origHeader.Time.Add(time.Second * 7)) _, found = app.StakingKeeper.GetRedelegation(ctx, sdk.AccAddress(validatorAddr), validatorAddr, validatorAddr2) require.False(t, found, "should have unbonded") } func TestTransitiveRedelegation(t *testing.T) { initPower := int64(1000) app, ctx, _, valAddrs := bootstrapHandlerGenesisTest(t, initPower, 3, sdk.TokensFromConsensusPower(initPower, sdk.DefaultPowerReduction)) val1, val2, val3 := valAddrs[0], valAddrs[1], valAddrs[2] blockTime := time.Now().UTC() ctx = ctx.WithBlockTime(blockTime) tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) // create the validators tstaking.CreateValidator(val1, PKs[0], sdk.NewInt(10), true) tstaking.CreateValidator(val2, PKs[1], sdk.NewInt(10), true) tstaking.CreateValidator(val3, PKs[2], sdk.NewInt(10), true) // begin redelegate redAmt := sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(10)) msgBeginRedelegate := types.NewMsgBeginRedelegate(sdk.AccAddress(val1), val1, val2, redAmt) tstaking.Handle(msgBeginRedelegate, true) // cannot redelegation to next validator while first delegation exists msgBeginRedelegate = types.NewMsgBeginRedelegate(sdk.AccAddress(val1), val2, val3, redAmt) tstaking.Handle(msgBeginRedelegate, false) params := app.StakingKeeper.GetParams(ctx) ctx = ctx.WithBlockTime(blockTime.Add(params.UnbondingTime)) tstaking.Ctx = ctx // complete first redelegation staking.EndBlocker(ctx, app.StakingKeeper) // now should be able to redelegate from the second validator to the third tstaking.Handle(msgBeginRedelegate, true) } func TestMultipleRedelegationAtSameTime(t *testing.T) { initPower := int64(1000) app, ctx, _, valAddrs := bootstrapHandlerGenesisTest(t, initPower, 2, sdk.TokensFromConsensusPower(initPower, sdk.DefaultPowerReduction)) valAddr := valAddrs[0] valAddr2 := valAddrs[1] tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) // set the unbonding time params := app.StakingKeeper.GetParams(ctx) params.UnbondingTime = 1 * time.Second app.StakingKeeper.SetParams(ctx, params) // create the validators valTokens := tstaking.CreateValidatorWithValPower(valAddr, PKs[0], 10, true) tstaking.CreateValidator(valAddr2, PKs[1], valTokens, true) // end block to bond them staking.EndBlocker(ctx, app.StakingKeeper) // begin a redelegate selfDelAddr := sdk.AccAddress(valAddr) // (the validator is it's own delegator) redAmt := sdk.NewCoin(sdk.DefaultBondDenom, valTokens.QuoRaw(2)) msgBeginRedelegate := types.NewMsgBeginRedelegate(selfDelAddr, valAddr, valAddr2, redAmt) tstaking.Handle(msgBeginRedelegate, true) // there should only be one entry in the redelegation object rd, found := app.StakingKeeper.GetRedelegation(ctx, selfDelAddr, valAddr, valAddr2) require.True(t, found) require.Len(t, rd.Entries, 1) // start a second redelegation at this same time as the first tstaking.Handle(msgBeginRedelegate, true) // now there should be two entries rd, found = app.StakingKeeper.GetRedelegation(ctx, selfDelAddr, valAddr, valAddr2) require.True(t, found) require.Len(t, rd.Entries, 2) // move forward in time, should complete both redelegations ctx = tstaking.TurnBlockTimeDiff(1 * time.Second) rd, found = app.StakingKeeper.GetRedelegation(ctx, selfDelAddr, valAddr, valAddr2) require.False(t, found) } func TestMultipleRedelegationAtUniqueTimes(t *testing.T) { initPower := int64(1000) app, ctx, _, valAddrs := bootstrapHandlerGenesisTest(t, initPower, 2, sdk.TokensFromConsensusPower(initPower, sdk.DefaultPowerReduction)) valAddr := valAddrs[0] valAddr2 := valAddrs[1] tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) // set the unbonding time params := app.StakingKeeper.GetParams(ctx) params.UnbondingTime = 10 * time.Second app.StakingKeeper.SetParams(ctx, params) // create the validators valTokens := tstaking.CreateValidatorWithValPower(valAddr, PKs[0], 10, true) tstaking.CreateValidator(valAddr2, PKs[1], valTokens, true) // end block to bond them staking.EndBlocker(ctx, app.StakingKeeper) // begin a redelegate selfDelAddr := sdk.AccAddress(valAddr) // (the validator is it's own delegator) redAmt := sdk.NewCoin(sdk.DefaultBondDenom, valTokens.QuoRaw(2)) msgBeginRedelegate := types.NewMsgBeginRedelegate(selfDelAddr, valAddr, valAddr2, redAmt) tstaking.Handle(msgBeginRedelegate, true) // move forward in time and start a second redelegation ctx = ctx.WithBlockTime(ctx.BlockHeader().Time.Add(5 * time.Second)) tstaking.Ctx = ctx tstaking.Handle(msgBeginRedelegate, true) // now there should be two entries rd, found := app.StakingKeeper.GetRedelegation(ctx, selfDelAddr, valAddr, valAddr2) require.True(t, found) require.Len(t, rd.Entries, 2) // move forward in time, should complete the first redelegation, but not the second ctx = tstaking.TurnBlockTimeDiff(5 * time.Second) rd, found = app.StakingKeeper.GetRedelegation(ctx, selfDelAddr, valAddr, valAddr2) require.True(t, found) require.Len(t, rd.Entries, 1) // move forward in time, should complete the second redelegation ctx = tstaking.TurnBlockTimeDiff(5 * time.Second) rd, found = app.StakingKeeper.GetRedelegation(ctx, selfDelAddr, valAddr, valAddr2) require.False(t, found) } func TestMultipleUnbondingDelegationAtSameTime(t *testing.T) { initPower := int64(1000) app, ctx, _, valAddrs := bootstrapHandlerGenesisTest(t, initPower, 1, sdk.TokensFromConsensusPower(initPower, sdk.DefaultPowerReduction)) valAddr := valAddrs[0] tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) // set the unbonding time params := app.StakingKeeper.GetParams(ctx) params.UnbondingTime = 1 * time.Second app.StakingKeeper.SetParams(ctx, params) // create the validators valTokens := tstaking.CreateValidatorWithValPower(valAddr, PKs[0], 10, true) // end block to bond staking.EndBlocker(ctx, app.StakingKeeper) // begin an unbonding delegation selfDelAddr := sdk.AccAddress(valAddr) // (the validator is it's own delegator) tstaking.Undelegate(selfDelAddr, valAddr, valTokens.QuoRaw(2), true) // there should only be one entry in the ubd object ubd, found := app.StakingKeeper.GetUnbondingDelegation(ctx, selfDelAddr, valAddr) require.True(t, found) require.Len(t, ubd.Entries, 1) // start a second ubd at this same time as the first tstaking.Undelegate(selfDelAddr, valAddr, valTokens.QuoRaw(2), true) // now there should be two entries ubd, found = app.StakingKeeper.GetUnbondingDelegation(ctx, selfDelAddr, valAddr) require.True(t, found) require.Len(t, ubd.Entries, 2) // move forwaubd in time, should complete both ubds ctx = tstaking.TurnBlockTimeDiff(1 * time.Second) ubd, found = app.StakingKeeper.GetUnbondingDelegation(ctx, selfDelAddr, valAddr) require.False(t, found) } func TestMultipleUnbondingDelegationAtUniqueTimes(t *testing.T) { initPower := int64(1000) app, ctx, _, valAddrs := bootstrapHandlerGenesisTest(t, initPower, 1, sdk.TokensFromConsensusPower(initPower, sdk.DefaultPowerReduction)) valAddr := valAddrs[0] tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) // set the unbonding time params := app.StakingKeeper.GetParams(ctx) params.UnbondingTime = 10 * time.Second app.StakingKeeper.SetParams(ctx, params) // create the validator valTokens := tstaking.CreateValidatorWithValPower(valAddr, PKs[0], 10, true) // end block to bond staking.EndBlocker(ctx, app.StakingKeeper) // begin an unbonding delegation selfDelAddr := sdk.AccAddress(valAddr) // (the validator is it's own delegator) tstaking.Undelegate(selfDelAddr, valAddr, valTokens.QuoRaw(2), true) // there should only be one entry in the ubd object ubd, found := app.StakingKeeper.GetUnbondingDelegation(ctx, selfDelAddr, valAddr) require.True(t, found) require.Len(t, ubd.Entries, 1) // move forwaubd in time and start a second redelegation ctx = ctx.WithBlockTime(ctx.BlockHeader().Time.Add(5 * time.Second)) tstaking.Ctx = ctx tstaking.Undelegate(selfDelAddr, valAddr, valTokens.QuoRaw(2), true) // now there should be two entries ubd, found = app.StakingKeeper.GetUnbondingDelegation(ctx, selfDelAddr, valAddr) require.True(t, found) require.Len(t, ubd.Entries, 2) // move forwaubd in time, should complete the first redelegation, but not the second ctx = tstaking.TurnBlockTimeDiff(5 * time.Second) ubd, found = app.StakingKeeper.GetUnbondingDelegation(ctx, selfDelAddr, valAddr) require.True(t, found) require.Len(t, ubd.Entries, 1) // move forwaubd in time, should complete the second redelegation ctx = tstaking.TurnBlockTimeDiff(5 * time.Second) ubd, found = app.StakingKeeper.GetUnbondingDelegation(ctx, selfDelAddr, valAddr) require.False(t, found) } func TestUnbondingWhenExcessValidators(t *testing.T) { initPower := int64(1000) app, ctx, _, valAddrs := bootstrapHandlerGenesisTest(t, initPower, 3, sdk.TokensFromConsensusPower(initPower, sdk.DefaultPowerReduction)) val1 := valAddrs[0] val2 := valAddrs[1] val3 := valAddrs[2] tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) // set the unbonding time params := app.StakingKeeper.GetParams(ctx) params.MaxValidators = 2 app.StakingKeeper.SetParams(ctx, params) // add three validators tstaking.CreateValidatorWithValPower(val1, PKs[0], 50, true) // apply TM updates app.StakingKeeper.ApplyAndReturnValidatorSetUpdates(ctx) require.Equal(t, 1, len(app.StakingKeeper.GetLastValidators(ctx))) valTokens2 := tstaking.CreateValidatorWithValPower(val2, PKs[1], 30, true) app.StakingKeeper.ApplyAndReturnValidatorSetUpdates(ctx) require.Equal(t, 2, len(app.StakingKeeper.GetLastValidators(ctx))) tstaking.CreateValidatorWithValPower(val3, PKs[2], 10, true) app.StakingKeeper.ApplyAndReturnValidatorSetUpdates(ctx) require.Equal(t, 2, len(app.StakingKeeper.GetLastValidators(ctx))) // unbond the validator-2 tstaking.Undelegate(sdk.AccAddress(val2), val2, valTokens2, true) // apply TM updates app.StakingKeeper.ApplyAndReturnValidatorSetUpdates(ctx) // because there are extra validators waiting to get in, the queued // validator (aka. validator-1) should make it into the bonded group, thus // the total number of validators should stay the same vals := app.StakingKeeper.GetLastValidators(ctx) require.Equal(t, 2, len(vals), "vals %v", vals) tstaking.CheckValidator(val1, types.Bonded, false) } func TestBondUnbondRedelegateSlashTwice(t *testing.T) { initPower := int64(1000) app, ctx, delAddrs, valAddrs := bootstrapHandlerGenesisTest(t, initPower, 3, sdk.TokensFromConsensusPower(initPower, sdk.DefaultPowerReduction)) valA, valB, del := valAddrs[0], valAddrs[1], delAddrs[2] consAddr0 := sdk.ConsAddress(PKs[0].Address()) tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) valTokens := tstaking.CreateValidatorWithValPower(valA, PKs[0], 10, true) tstaking.CreateValidator(valB, PKs[1], valTokens, true) // delegate 10 stake tstaking.Delegate(del, valA, valTokens) // apply Tendermint updates updates, err := app.StakingKeeper.ApplyAndReturnValidatorSetUpdates(ctx) require.NoError(t, err) require.Equal(t, 2, len(updates)) // a block passes ctx = ctx.WithBlockHeight(1) tstaking.Ctx = ctx // begin unbonding 4 stake unbondAmt := app.StakingKeeper.TokensFromConsensusPower(ctx, 4) tstaking.Undelegate(del, valA, unbondAmt, true) // begin redelegate 6 stake redAmt := sdk.NewCoin(sdk.DefaultBondDenom, app.StakingKeeper.TokensFromConsensusPower(ctx, 6)) msgBeginRedelegate := types.NewMsgBeginRedelegate(del, valA, valB, redAmt) tstaking.Handle(msgBeginRedelegate, true) // destination delegation should have 6 shares delegation, found := app.StakingKeeper.GetDelegation(ctx, del, valB) require.True(t, found) require.Equal(t, sdk.NewDecFromInt(redAmt.Amount), delegation.Shares) // must apply validator updates updates, err = app.StakingKeeper.ApplyAndReturnValidatorSetUpdates(ctx) require.NoError(t, err) require.Equal(t, 2, len(updates)) // slash the validator by half app.StakingKeeper.Slash(ctx, consAddr0, 0, 20, sdk.NewDecWithPrec(5, 1)) // unbonding delegation should have been slashed by half ubd, found := app.StakingKeeper.GetUnbondingDelegation(ctx, del, valA) require.True(t, found) require.Len(t, ubd.Entries, 1) require.Equal(t, unbondAmt.QuoRaw(2), ubd.Entries[0].Balance) // redelegation should have been slashed by half redelegation, found := app.StakingKeeper.GetRedelegation(ctx, del, valA, valB) require.True(t, found) require.Len(t, redelegation.Entries, 1) // destination delegation should have been slashed by half delegation, found = app.StakingKeeper.GetDelegation(ctx, del, valB) require.True(t, found) require.Equal(t, sdk.NewDecFromInt(redAmt.Amount.QuoRaw(2)), delegation.Shares) // validator power should have been reduced by half validator, found := app.StakingKeeper.GetValidator(ctx, valA) require.True(t, found) require.Equal(t, valTokens.QuoRaw(2), validator.GetBondedTokens()) // slash the validator for an infraction committed after the unbonding and redelegation begin ctx = ctx.WithBlockHeight(3) app.StakingKeeper.Slash(ctx, consAddr0, 2, 10, sdk.NewDecWithPrec(5, 1)) tstaking.Ctx = ctx // unbonding delegation should be unchanged ubd, found = app.StakingKeeper.GetUnbondingDelegation(ctx, del, valA) require.True(t, found) require.Len(t, ubd.Entries, 1) require.Equal(t, unbondAmt.QuoRaw(2), ubd.Entries[0].Balance) // redelegation should be unchanged redelegation, found = app.StakingKeeper.GetRedelegation(ctx, del, valA, valB) require.True(t, found) require.Len(t, redelegation.Entries, 1) // destination delegation should be unchanged delegation, found = app.StakingKeeper.GetDelegation(ctx, del, valB) require.True(t, found) require.Equal(t, sdk.NewDecFromInt(redAmt.Amount.QuoRaw(2)), delegation.Shares) // end blocker staking.EndBlocker(ctx, app.StakingKeeper) // validator power should have been reduced to zero // validator should be in unbonding state validator, _ = app.StakingKeeper.GetValidator(ctx, valA) require.Equal(t, validator.GetStatus(), types.Unbonding) } func TestInvalidMsg(t *testing.T) { k := keeper.Keeper{} h := staking.NewHandler(k) res, err := h(sdk.NewContext(nil, tmproto.Header{}, false, nil), testdata.NewTestMsg()) require.Error(t, err) require.Nil(t, res) require.True(t, strings.Contains(err.Error(), "unrecognized staking message type")) } func TestInvalidCoinDenom(t *testing.T) { initPower := int64(1000) app, ctx, delAddrs, valAddrs := bootstrapHandlerGenesisTest(t, initPower, 3, sdk.TokensFromConsensusPower(initPower, sdk.DefaultPowerReduction)) valA, valB, delAddr := valAddrs[0], valAddrs[1], delAddrs[2] tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) valTokens := app.StakingKeeper.TokensFromConsensusPower(ctx, 100) invalidCoin := sdk.NewCoin("churros", valTokens) validCoin := sdk.NewCoin(sdk.DefaultBondDenom, valTokens) oneCoin := sdk.NewCoin(sdk.DefaultBondDenom, sdk.OneInt()) commission := types.NewCommissionRates(sdk.OneDec(), sdk.OneDec(), sdk.ZeroDec()) msgCreate, err := types.NewMsgCreateValidator(valA, PKs[0], invalidCoin, types.Description{}, commission, sdk.OneInt()) require.NoError(t, err) tstaking.Handle(msgCreate, false) msgCreate, err = types.NewMsgCreateValidator(valA, PKs[0], validCoin, types.Description{}, commission, sdk.OneInt()) require.NoError(t, err) tstaking.Handle(msgCreate, true) msgCreate, err = types.NewMsgCreateValidator(valB, PKs[1], validCoin, types.Description{}, commission, sdk.OneInt()) require.NoError(t, err) tstaking.Handle(msgCreate, true) msgDelegate := types.NewMsgDelegate(delAddr, valA, invalidCoin) tstaking.Handle(msgDelegate, false) msgDelegate = types.NewMsgDelegate(delAddr, valA, validCoin) tstaking.Handle(msgDelegate, true) msgUndelegate := types.NewMsgUndelegate(delAddr, valA, invalidCoin) tstaking.Handle(msgUndelegate, false) msgUndelegate = types.NewMsgUndelegate(delAddr, valA, oneCoin) tstaking.Handle(msgUndelegate, true) msgRedelegate := types.NewMsgBeginRedelegate(delAddr, valA, valB, invalidCoin) tstaking.Handle(msgRedelegate, false) msgRedelegate = types.NewMsgBeginRedelegate(delAddr, valA, valB, oneCoin) tstaking.Handle(msgRedelegate, true) }