Merge PR #2394: Split up UpdateValidator into distinct state transitions applied only in EndBlock

This commit is contained in:
Rigel 2018-10-03 12:37:06 -04:00 committed by Christopher Goes
parent a7fabbea16
commit 324bdaf55d
24 changed files with 855 additions and 1123 deletions

16
Gopkg.lock generated
View File

@ -239,12 +239,12 @@
version = "v1.0.0"
[[projects]]
digest = "1:645110e089152bd0f4a011a2648fbb0e4df5977be73ca605781157ac297f50c4"
digest = "1:e32dfc6abff6a3633ef4d9a1022fd707c8ef26f1e1e8f855dc58dc415ce7c8f3"
name = "github.com/mitchellh/mapstructure"
packages = ["."]
pruneopts = "UT"
revision = "fa473d140ef3c6adf42d6b391fe76707f1f243c8"
version = "v1.0.0"
revision = "fe40af7a9c397fa3ddba203c38a5042c5d0475ad"
version = "v1.1.1"
[[projects]]
digest = "1:95741de3af260a92cc5c7f3f3061e85273f5a81b5db20d4bd68da74bd521675e"
@ -536,7 +536,7 @@
"salsa20/salsa",
]
pruneopts = "UT"
revision = "0e37d006457bf46f9e6692014ba72ef82c33022c"
revision = "e3636079e1a4c1f337f212cc5cd2aca108f6c900"
[[projects]]
digest = "1:d36f55a999540d29b6ea3c2ea29d71c76b1d9853fdcd3e5c5cb4836f2ba118f1"
@ -556,14 +556,14 @@
[[projects]]
branch = "master"
digest = "1:68023dc297a659d5eb2dafd62eda811456b338c5b3ec3c27da79e8a47d3f456a"
digest = "1:8bc8ecef1d63576cfab4d08b44a1f255dd67e5b019b7a44837d62380f266a91c"
name = "golang.org/x/sys"
packages = [
"cpu",
"unix",
]
pruneopts = "UT"
revision = "2f1df4e56cdeb503a08d8577e6f1a7eb12efab82"
revision = "e4b3c5e9061176387e7cea65e4dc5853801f3fb7"
[[projects]]
digest = "1:a2ab62866c75542dd18d2b069fec854577a20211d7c0ea6ae746072a1dccdd18"
@ -590,11 +590,11 @@
[[projects]]
branch = "master"
digest = "1:56b0bca90b7e5d1facf5fbdacba23e4e0ce069d25381b8e2f70ef1e7ebfb9c1a"
digest = "1:1e6b0176e8c5dd8ff551af65c76f8b73a99bcf4d812cedff1b91711b7df4804c"
name = "google.golang.org/genproto"
packages = ["googleapis/rpc/status"]
pruneopts = "UT"
revision = "0e822944c569bf5c9afd034adaa56208bd2906ac"
revision = "c7e5094acea1ca1b899e2259d80a6b0f882f81f8"
[[projects]]
digest = "1:2dab32a43451e320e49608ff4542fdfc653c95dcc35d0065ec9c6c3dd540ed74"

View File

@ -64,6 +64,7 @@ BREAKING CHANGES
* [x/auth] \#2377 auth.StdSignMsg -> txbuilder.StdSignMsg
* [x/staking] \#2244 staking now holds a consensus-address-index instead of a consensus-pubkey-index
* [x/staking] \#2236 more distribution hooks for distribution
* [x/stake] \#2394 Split up UpdateValidator into distinct state transitions applied only in EndBlock
* Tendermint

View File

@ -45,6 +45,7 @@ func TestTallyNoOneVotes(t *testing.T) {
}
createValidators(t, stakeHandler, ctx, valAddrs, []int64{5, 5})
stake.EndBlocker(ctx, sk)
proposal := keeper.NewTextProposal(ctx, "Test", "description", ProposalTypeText)
proposalID := proposal.GetProposalID()
@ -69,6 +70,7 @@ func TestTallyOnlyValidatorsAllYes(t *testing.T) {
}
createValidators(t, stakeHandler, ctx, valAddrs, []int64{5, 5})
stake.EndBlocker(ctx, sk)
proposal := keeper.NewTextProposal(ctx, "Test", "description", ProposalTypeText)
proposalID := proposal.GetProposalID()
@ -98,6 +100,7 @@ func TestTallyOnlyValidators51No(t *testing.T) {
}
createValidators(t, stakeHandler, ctx, valAddrs, []int64{5, 6})
stake.EndBlocker(ctx, sk)
proposal := keeper.NewTextProposal(ctx, "Test", "description", ProposalTypeText)
proposalID := proposal.GetProposalID()
@ -126,6 +129,7 @@ func TestTallyOnlyValidators51Yes(t *testing.T) {
}
createValidators(t, stakeHandler, ctx, valAddrs, []int64{6, 6, 7})
stake.EndBlocker(ctx, sk)
proposal := keeper.NewTextProposal(ctx, "Test", "description", ProposalTypeText)
proposalID := proposal.GetProposalID()
@ -157,6 +161,7 @@ func TestTallyOnlyValidatorsVetoed(t *testing.T) {
}
createValidators(t, stakeHandler, ctx, valAddrs, []int64{6, 6, 7})
stake.EndBlocker(ctx, sk)
proposal := keeper.NewTextProposal(ctx, "Test", "description", ProposalTypeText)
proposalID := proposal.GetProposalID()
@ -188,6 +193,7 @@ func TestTallyOnlyValidatorsAbstainPasses(t *testing.T) {
}
createValidators(t, stakeHandler, ctx, valAddrs, []int64{6, 6, 7})
stake.EndBlocker(ctx, sk)
proposal := keeper.NewTextProposal(ctx, "Test", "description", ProposalTypeText)
proposalID := proposal.GetProposalID()
@ -219,6 +225,7 @@ func TestTallyOnlyValidatorsAbstainFails(t *testing.T) {
}
createValidators(t, stakeHandler, ctx, valAddrs, []int64{6, 6, 7})
stake.EndBlocker(ctx, sk)
proposal := keeper.NewTextProposal(ctx, "Test", "description", ProposalTypeText)
proposalID := proposal.GetProposalID()
@ -250,6 +257,7 @@ func TestTallyOnlyValidatorsNonVoter(t *testing.T) {
}
createValidators(t, stakeHandler, ctx, valAddrs, []int64{6, 6, 7})
stake.EndBlocker(ctx, sk)
proposal := keeper.NewTextProposal(ctx, "Test", "description", ProposalTypeText)
proposalID := proposal.GetProposalID()
@ -279,6 +287,7 @@ func TestTallyDelgatorOverride(t *testing.T) {
}
createValidators(t, stakeHandler, ctx, valAddrs, []int64{5, 6, 7})
stake.EndBlocker(ctx, sk)
delegator1Msg := stake.NewMsgDelegate(addrs[3], sdk.ValAddress(addrs[2]), sdk.NewInt64Coin("steak", 30))
stakeHandler(ctx, delegator1Msg)
@ -315,6 +324,7 @@ func TestTallyDelgatorInherit(t *testing.T) {
}
createValidators(t, stakeHandler, ctx, valAddrs, []int64{5, 6, 7})
stake.EndBlocker(ctx, sk)
delegator1Msg := stake.NewMsgDelegate(addrs[3], sdk.ValAddress(addrs[2]), sdk.NewInt64Coin("steak", 30))
stakeHandler(ctx, delegator1Msg)
@ -349,6 +359,7 @@ func TestTallyDelgatorMultipleOverride(t *testing.T) {
}
createValidators(t, stakeHandler, ctx, valAddrs, []int64{5, 6, 7})
stake.EndBlocker(ctx, sk)
delegator1Msg := stake.NewMsgDelegate(addrs[3], sdk.ValAddress(addrs[2]), sdk.NewInt64Coin("steak", 10))
stakeHandler(ctx, delegator1Msg)
@ -402,6 +413,8 @@ func TestTallyDelgatorMultipleInherit(t *testing.T) {
delegator1Msg2 := stake.NewMsgDelegate(addrs[3], sdk.ValAddress(addrs[1]), sdk.NewInt64Coin("steak", 10))
stakeHandler(ctx, delegator1Msg2)
stake.EndBlocker(ctx, sk)
proposal := keeper.NewTextProposal(ctx, "Test", "description", ProposalTypeText)
proposalID := proposal.GetProposalID()
proposal.SetStatus(StatusVotingPeriod)
@ -432,6 +445,7 @@ func TestTallyJailedValidator(t *testing.T) {
}
createValidators(t, stakeHandler, ctx, valAddrs, []int64{25, 6, 7})
stake.EndBlocker(ctx, sk)
delegator1Msg := stake.NewMsgDelegate(addrs[3], sdk.ValAddress(addrs[2]), sdk.NewInt64Coin("steak", 10))
stakeHandler(ctx, delegator1Msg)
@ -443,6 +457,8 @@ func TestTallyJailedValidator(t *testing.T) {
require.True(t, found)
sk.Jail(ctx, sdk.ConsAddress(val2.ConsPubKey.Address()))
stake.EndBlocker(ctx, sk)
proposal := keeper.NewTextProposal(ctx, "Test", "description", ProposalTypeText)
proposalID := proposal.GetProposalID()
proposal.SetStatus(StatusVotingPeriod)

View File

@ -36,12 +36,8 @@ func initChain(r *rand.Rand, accounts []Account, setups []RandSetup, app *baseap
res := app.InitChain(abci.RequestInitChain{AppStateBytes: appStateFn(r, accounts)})
validators = make(map[string]mockValidator)
for _, validator := range res.Validators {
pubkey, err := tmtypes.PB2TM.PubKey(validator.PubKey)
if err != nil {
panic(err)
}
address := pubkey.Address()
validators[string(address)] = mockValidator{validator, GetMemberOfInitialState(r, initialLivenessWeightings)}
str := fmt.Sprintf("%v", validator.PubKey)
validators[str] = mockValidator{validator, GetMemberOfInitialState(r, initialLivenessWeightings)}
}
for i := 0; i < len(setups); i++ {
@ -400,22 +396,23 @@ func RandomRequestBeginBlock(r *rand.Rand, validators map[string]mockValidator,
func updateValidators(tb testing.TB, r *rand.Rand, current map[string]mockValidator, updates []abci.ValidatorUpdate, event func(string)) map[string]mockValidator {
for _, update := range updates {
str := fmt.Sprintf("%v", update.PubKey)
switch {
case update.Power == 0:
if _, ok := current[string(update.PubKey.Data)]; !ok {
if _, ok := current[str]; !ok {
tb.Fatalf("tried to delete a nonexistent validator")
}
event("endblock/validatorupdates/kicked")
delete(current, string(update.PubKey.Data))
delete(current, str)
default:
// Does validator already exist?
if mVal, ok := current[string(update.PubKey.Data)]; ok {
if mVal, ok := current[str]; ok {
mVal.val = update
event("endblock/validatorupdates/updated")
} else {
// Set this new validator
current[string(update.PubKey.Data)] = mockValidator{update, GetMemberOfInitialState(r, initialLivenessWeightings)}
current[str] = mockValidator{update, GetMemberOfInitialState(r, initialLivenessWeightings)}
event("endblock/validatorupdates/added")
}
}

View File

@ -45,6 +45,9 @@ func TestJailedValidatorDelegations(t *testing.T) {
got := stake.NewHandler(stakeKeeper)(ctx, msgCreateVal)
require.True(t, got.IsOK(), "expected create validator msg to be ok, got: %v", got)
// end block
stake.EndBlocker(ctx, stakeKeeper)
// set dummy signing info
newInfo := ValidatorSigningInfo{
StartHeight: int64(0),

View File

@ -85,13 +85,16 @@ func TestSlashingPeriodCap(t *testing.T) {
// double sign less than max age
keeper.handleDoubleSign(ctx, valConsPubKey.Address(), 0, time.Unix(0, 0), amtInt)
// should be jailed
require.True(t, sk.Validator(ctx, addr).GetJailed())
// end block
stake.EndBlocker(ctx, sk)
// update block height
ctx = ctx.WithBlockHeight(int64(1))
// unjail to measure power
sk.Unjail(ctx, valConsAddr)
// end block
stake.EndBlocker(ctx, sk)
// power should be reduced
expectedPower := sdk.NewDecFromInt(amt).Mul(sdk.NewDec(19).Quo(sdk.NewDec(20)))
require.Equal(t, expectedPower, sk.Validator(ctx, addr).GetPower())
@ -100,10 +103,14 @@ func TestSlashingPeriodCap(t *testing.T) {
keeper.handleDoubleSign(ctx, valConsPubKey.Address(), 0, time.Unix(0, 0), amtInt)
// should be jailed
require.True(t, sk.Validator(ctx, addr).GetJailed())
// end block
stake.EndBlocker(ctx, sk)
// update block height
ctx = ctx.WithBlockHeight(int64(2))
// unjail to measure power
sk.Unjail(ctx, valConsAddr)
// end block
stake.EndBlocker(ctx, sk)
// power should be equal, no more should have been slashed
expectedPower = sdk.NewDecFromInt(amt).Mul(sdk.NewDec(19).Quo(sdk.NewDec(20)))
require.Equal(t, expectedPower, sk.Validator(ctx, addr).GetPower())
@ -114,6 +121,8 @@ func TestSlashingPeriodCap(t *testing.T) {
require.True(t, sk.Validator(ctx, addr).GetJailed())
// unjail to measure power
sk.Unjail(ctx, valConsAddr)
// end block
stake.EndBlocker(ctx, sk)
// power should be reduced
expectedPower = sdk.NewDecFromInt(amt).Mul(sdk.NewDec(18).Quo(sdk.NewDec(20)))
require.Equal(t, expectedPower, sk.Validator(ctx, addr).GetPower())
@ -180,6 +189,9 @@ func TestHandleAbsentValidator(t *testing.T) {
require.Equal(t, int64(0), info.StartHeight)
require.Equal(t, keeper.SignedBlocksWindow(ctx)-keeper.MinSignedPerWindow(ctx)-1, info.SignedBlocksCounter)
// end block
stake.EndBlocker(ctx, sk)
// validator should have been jailed
validator, _ = sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val))
require.Equal(t, sdk.Unbonding, validator.GetStatus())
@ -193,6 +205,9 @@ func TestHandleAbsentValidator(t *testing.T) {
got = slh(ctx, NewMsgUnjail(addr))
require.True(t, got.IsOK())
// end block
stake.EndBlocker(ctx, sk)
// validator should be rebonded now
validator, _ = sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val))
require.Equal(t, sdk.Bonded, validator.GetStatus())
@ -222,12 +237,19 @@ func TestHandleAbsentValidator(t *testing.T) {
keeper.handleValidatorSignature(ctx, val.Address(), amtInt, false)
}
// end block
stake.EndBlocker(ctx, sk)
// validator should be jailed again after 500 unsigned blocks
nextHeight = height + keeper.MinSignedPerWindow(ctx) + 1
for ; height <= nextHeight; height++ {
ctx = ctx.WithBlockHeight(height)
keeper.handleValidatorSignature(ctx, val.Address(), amtInt, false)
}
// end block
stake.EndBlocker(ctx, sk)
validator, _ = sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val))
require.Equal(t, sdk.Unbonding, validator.GetStatus())
}
@ -296,6 +318,9 @@ func TestHandleAlreadyJailed(t *testing.T) {
keeper.handleValidatorSignature(ctx, val.Address(), amtInt, false)
}
// end block
stake.EndBlocker(ctx, sk)
// validator should have been jailed and slashed
validator, _ := sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val))
require.Equal(t, sdk.Unbonding, validator.GetStatus())

View File

@ -77,6 +77,9 @@ func TestBeginBlocker(t *testing.T) {
BeginBlocker(ctx, req, keeper)
}
// end block
stake.EndBlocker(ctx, sk)
// validator should be jailed
validator, found := sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(pk))
require.True(t, found)

View File

@ -17,7 +17,7 @@ import (
// Returns final validator set after applying all declaration and delegations
func InitGenesis(ctx sdk.Context, keeper Keeper, data types.GenesisState) (res []abci.ValidatorUpdate, err error) {
keeper.SetPool(ctx, data.Pool)
keeper.SetNewParams(ctx, data.Params)
keeper.SetParams(ctx, data.Params)
keeper.InitIntraTxCounter(ctx)
for i, validator := range data.Validators {
@ -31,26 +31,16 @@ func InitGenesis(ctx sdk.Context, keeper Keeper, data types.GenesisState) (res [
return res, errors.Errorf("genesis validator cannot have zero delegator shares, validator: %v", validator)
}
// Manually set indexes for the first time
// Manually set indices for the first time
keeper.SetValidatorByConsAddr(ctx, validator)
keeper.SetValidatorByPowerIndex(ctx, validator, data.Pool)
if validator.Status == sdk.Bonded {
keeper.SetValidatorBondedIndex(ctx, validator)
}
}
for _, bond := range data.Bonds {
keeper.SetDelegation(ctx, bond)
}
keeper.UpdateBondedValidatorsFull(ctx)
vals := keeper.GetValidatorsBonded(ctx)
res = make([]abci.ValidatorUpdate, len(vals))
for i, val := range vals {
res[i] = val.ABCIValidatorUpdate()
}
res = keeper.ApplyAndReturnValidatorSetUpdates(ctx)
return
}

View File

@ -52,7 +52,7 @@ func EndBlocker(ctx sdk.Context, k keeper.Keeper) (ValidatorUpdates []abci.Valid
k.SetIntraTxCounter(ctx, 0)
// calculate validator set changes
ValidatorUpdates = k.GetTendermintUpdates(ctx)
ValidatorUpdates = k.ApplyAndReturnValidatorSetUpdates(ctx)
return
}
@ -82,7 +82,6 @@ func handleMsgCreateValidator(ctx sdk.Context, msg types.MsgCreateValidator, k k
msg.Commission.Rate, msg.Commission.MaxChangeRate,
msg.Commission.MaxChangeRate, ctx.BlockHeader().Time,
)
validator, err := validator.SetInitialCommission(commission)
if err != nil {
return err.Result()
@ -90,6 +89,7 @@ func handleMsgCreateValidator(ctx sdk.Context, msg types.MsgCreateValidator, k k
k.SetValidator(ctx, validator)
k.SetValidatorByConsAddr(ctx, validator)
k.SetNewValidatorByPowerIndex(ctx, validator)
// move coins from the msg.Address account to a (self-delegation) delegator account
// the validator account and global shares are updated within here
@ -130,13 +130,13 @@ func handleMsgEditValidator(ctx sdk.Context, msg types.MsgEditValidator, k keepe
validator.Description = description
if msg.CommissionRate != nil {
if err := k.UpdateValidatorCommission(ctx, validator, *msg.CommissionRate); err != nil {
commission, err := k.UpdateValidatorCommission(ctx, validator, *msg.CommissionRate)
if err != nil {
return err.Result()
}
validator.Commission = commission
}
// We don't need to run through all the power update logic within k.UpdateValidator
// We just need to override the entry in state, since only the description has changed.
k.SetValidator(ctx, validator)
tags := sdk.NewTags(

View File

@ -63,6 +63,10 @@ func TestValidatorByPowerIndex(t *testing.T) {
got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
require.True(t, got.IsOK(), "expected create-validator to be ok, got %v", got)
// must end-block
updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx)
require.Equal(t, 1, len(updates))
// verify the self-delegation exists
bond, found := keeper.GetDelegation(ctx, sdk.AccAddress(validatorAddr), validatorAddr)
require.True(t, found)
@ -83,14 +87,20 @@ func TestValidatorByPowerIndex(t *testing.T) {
got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
require.True(t, got.IsOK(), "expected create-validator to be ok, got %v", got)
// must end-block
updates = keeper.ApplyAndReturnValidatorSetUpdates(ctx)
require.Equal(t, 1, len(updates))
// slash and jail the first validator
consAddr0 := sdk.ConsAddress(keep.PKs[0].Address())
keeper.Slash(ctx, consAddr0, 0, initBond, sdk.NewDecWithPrec(5, 1))
keeper.Jail(ctx, consAddr0)
keeper.ApplyAndReturnValidatorSetUpdates(ctx)
validator, found = keeper.GetValidator(ctx, validatorAddr)
require.True(t, found)
require.Equal(t, sdk.Unbonding, validator.Status) // ensure is unbonding
require.Equal(t, int64(500000), validator.Tokens.RoundInt64()) // ensure tokens slashed
keeper.Unjail(ctx, consAddr0)
// the old power record should have been deleted as the power changed
require.False(t, keep.ValidatorByPowerIndexExists(ctx, keeper, power))
@ -121,6 +131,8 @@ func TestValidatorByPowerIndex(t *testing.T) {
got = handleMsgCompleteUnbonding(ctx, msgCompleteUnbonding, keeper)
require.True(t, got.IsOK(), "expected msg to be ok, got %v", got)
EndBlocker(ctx, keeper)
// verify that by power key nolonger exists
_, found = keeper.GetValidator(ctx, validatorAddr)
require.False(t, found)
@ -136,8 +148,10 @@ func TestDuplicatesMsgCreateValidator(t *testing.T) {
msgCreateValidator1 := newTestMsgCreateValidator(addr1, pk1, 10)
got := handleMsgCreateValidator(ctx, msgCreateValidator1, keeper)
require.True(t, got.IsOK(), "%v", got)
validator, found := keeper.GetValidator(ctx, addr1)
keeper.ApplyAndReturnValidatorSetUpdates(ctx)
validator, found := keeper.GetValidator(ctx, addr1)
require.True(t, found)
assert.Equal(t, sdk.Bonded, validator.Status)
assert.Equal(t, addr1, validator.OperatorAddr)
@ -160,6 +174,11 @@ func TestDuplicatesMsgCreateValidator(t *testing.T) {
msgCreateValidator4 := newTestMsgCreateValidator(addr2, pk2, 10)
got = handleMsgCreateValidator(ctx, msgCreateValidator4, keeper)
require.True(t, got.IsOK(), "%v", got)
// must end-block
updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx)
require.Equal(t, 1, len(updates))
validator, found = keeper.GetValidator(ctx, addr2)
require.True(t, found)
@ -180,6 +199,11 @@ func TestDuplicatesMsgCreateValidatorOnBehalfOf(t *testing.T) {
msgCreateValidatorOnBehalfOf := newTestMsgCreateValidatorOnBehalfOf(delegatorAddr, validatorAddr, pk, 10)
got := handleMsgCreateValidator(ctx, msgCreateValidatorOnBehalfOf, keeper)
require.True(t, got.IsOK(), "%v", got)
// must end-block
updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx)
require.Equal(t, 1, len(updates))
validator, found := keeper.GetValidator(ctx, validatorAddr)
require.True(t, found)
@ -211,6 +235,10 @@ func TestLegacyValidatorDelegations(t *testing.T) {
got := handleMsgCreateValidator(ctx, msgCreateVal, keeper)
require.True(t, got.IsOK(), "expected create validator msg to be ok, got %v", got)
// must end-block
updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx)
require.Equal(t, 1, len(updates))
// verify the validator exists and has the correct attributes
validator, found := keeper.GetValidator(ctx, valAddr)
require.True(t, found)
@ -302,6 +330,9 @@ func TestIncrementsMsgDelegate(t *testing.T) {
got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
require.True(t, got.IsOK(), "expected create validator msg to be ok, got %v", got)
// apply TM updates
keeper.ApplyAndReturnValidatorSetUpdates(ctx)
validator, found := keeper.GetValidator(ctx, validatorAddr)
require.True(t, found)
require.Equal(t, sdk.Bonded, validator.Status)
@ -384,6 +415,9 @@ func TestIncrementsMsgUnbond(t *testing.T) {
amt2 := accMapper.GetAccount(ctx, delegatorAddr).GetCoins().AmountOf(denom)
require.Equal(t, amt1.Sub(sdk.NewInt(initBond)).Int64(), amt2.Int64(), "expected coins to be subtracted")
// apply TM updates
keeper.ApplyAndReturnValidatorSetUpdates(ctx)
validator, found := keeper.GetValidator(ctx, validatorAddr)
require.True(t, found)
require.Equal(t, initBond*2, validator.DelegatorShares.RoundInt64())
@ -533,11 +567,8 @@ func TestMultipleMsgDelegate(t *testing.T) {
// unbond them all
for i, delegatorAddr := range delegatorAddrs {
msgBeginUnbonding := NewMsgBeginUnbonding(delegatorAddr, validatorAddr, sdk.NewDec(10))
msgCompleteUnbonding := NewMsgCompleteUnbonding(delegatorAddr, validatorAddr)
got := handleMsgBeginUnbonding(ctx, msgBeginUnbonding, keeper)
require.True(t, got.IsOK(), "expected msg %d to be ok, got %v", i, got)
got = handleMsgCompleteUnbonding(ctx, msgCompleteUnbonding, keeper)
require.True(t, got.IsOK(), "expected msg %d to be ok, got %v", i, got)
//Check that the account is unbonded
_, found := keeper.GetDelegation(ctx, delegatorAddr, validatorAddr)
@ -564,11 +595,8 @@ func TestJailValidator(t *testing.T) {
// unbond the validators bond portion
msgBeginUnbondingValidator := NewMsgBeginUnbonding(sdk.AccAddress(validatorAddr), validatorAddr, sdk.NewDec(10))
msgCompleteUnbondingValidator := NewMsgCompleteUnbonding(sdk.AccAddress(validatorAddr), validatorAddr)
got = handleMsgBeginUnbonding(ctx, msgBeginUnbondingValidator, keeper)
require.True(t, got.IsOK(), "expected no error")
got = handleMsgCompleteUnbonding(ctx, msgCompleteUnbondingValidator, keeper)
require.True(t, got.IsOK(), "expected no error")
require.True(t, got.IsOK(), "expected no error: %v", got)
validator, found := keeper.GetValidator(ctx, validatorAddr)
require.True(t, found)
@ -750,16 +778,22 @@ func TestUnbondingWhenExcessValidators(t *testing.T) {
msgCreateValidator := newTestMsgCreateValidator(validatorAddr1, keep.PKs[0], 50)
got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
// apply TM updates
keeper.ApplyAndReturnValidatorSetUpdates(ctx)
require.Equal(t, 1, len(keeper.GetValidatorsBonded(ctx)))
msgCreateValidator = newTestMsgCreateValidator(validatorAddr2, keep.PKs[1], 30)
got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
// apply TM updates
keeper.ApplyAndReturnValidatorSetUpdates(ctx)
require.Equal(t, 2, len(keeper.GetValidatorsBonded(ctx)))
msgCreateValidator = newTestMsgCreateValidator(validatorAddr3, keep.PKs[2], 10)
got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
// apply TM updates
keeper.ApplyAndReturnValidatorSetUpdates(ctx)
require.Equal(t, 2, len(keeper.GetValidatorsBonded(ctx)))
// unbond the valdator-2
@ -767,6 +801,9 @@ func TestUnbondingWhenExcessValidators(t *testing.T) {
got = handleMsgBeginUnbonding(ctx, msgBeginUnbonding, keeper)
require.True(t, got.IsOK(), "expected no error on runMsgBeginUnbonding")
// apply TM updates
keeper.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
@ -777,142 +814,6 @@ func TestUnbondingWhenExcessValidators(t *testing.T) {
require.Equal(t, sdk.Bonded, val1.Status, "%v", val1)
}
func TestJoiningAsCliffValidator(t *testing.T) {
ctx, _, keeper := keep.CreateTestInput(t, false, 1000)
validatorAddr1, validatorAddr2 := sdk.ValAddress(keep.Addrs[0]), sdk.ValAddress(keep.Addrs[1])
// make sure that the cliff validator is nil to begin with
cliffVal := keeper.GetCliffValidator(ctx)
require.Equal(t, []byte(nil), cliffVal)
// set the unbonding time
params := keeper.GetParams(ctx)
params.UnbondingTime = 0
params.MaxValidators = 2
keeper.SetParams(ctx, params)
// add the first validator
msgCreateValidator := newTestMsgCreateValidator(validatorAddr1, keep.PKs[0], 50)
got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
// cliff validator should still be nil
cliffVal = keeper.GetCliffValidator(ctx)
require.Equal(t, []byte(nil), cliffVal)
// Add the second validator
msgCreateValidator = newTestMsgCreateValidator(validatorAddr2, keep.PKs[1], 30)
got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
// now that we've reached maximum validators, the val-2 should be added to the cliff (top)
cliffVal = keeper.GetCliffValidator(ctx)
require.Equal(t, validatorAddr2.Bytes(), cliffVal)
}
func TestJoiningToCreateFirstCliffValidator(t *testing.T) {
ctx, _, keeper := keep.CreateTestInput(t, false, 1000)
validatorAddr1, validatorAddr2 := sdk.ValAddress(keep.Addrs[0]), sdk.ValAddress(keep.Addrs[1])
// make sure that the cliff validator is nil to begin with
cliffVal := keeper.GetCliffValidator(ctx)
require.Equal(t, []byte(nil), cliffVal)
// set the unbonding time
params := keeper.GetParams(ctx)
params.UnbondingTime = 0
params.MaxValidators = 2
keeper.SetParams(ctx, params)
// add the first validator
msgCreateValidator := newTestMsgCreateValidator(validatorAddr1, keep.PKs[0], 50)
got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
// cliff validator should still be nil
cliffVal = keeper.GetCliffValidator(ctx)
require.Equal(t, []byte(nil), cliffVal)
// Add the second validator
msgCreateValidator = newTestMsgCreateValidator(validatorAddr2, keep.PKs[1], 60)
got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
// now that we've reached maximum validators, validator-1 should be added to the cliff (top)
cliffVal = keeper.GetCliffValidator(ctx)
require.Equal(t, validatorAddr1.Bytes(), cliffVal)
}
func TestCliffValidator(t *testing.T) {
ctx, _, keeper := keep.CreateTestInput(t, false, 1000)
validatorAddr1 := sdk.ValAddress(keep.Addrs[0])
validatorAddr2 := sdk.ValAddress(keep.Addrs[1])
validatorAddr3 := sdk.ValAddress(keep.Addrs[2])
// make sure that the cliff validator is nil to begin with
cliffVal := keeper.GetCliffValidator(ctx)
require.Equal(t, []byte(nil), cliffVal)
// set the unbonding time
params := keeper.GetParams(ctx)
params.UnbondingTime = 0
params.MaxValidators = 2
keeper.SetParams(ctx, params)
// add the first validator
msgCreateValidator := newTestMsgCreateValidator(validatorAddr1, keep.PKs[0], 50)
got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
// cliff validator should still be nil
cliffVal = keeper.GetCliffValidator(ctx)
require.Equal(t, []byte(nil), cliffVal)
// Add the second validator
msgCreateValidator = newTestMsgCreateValidator(validatorAddr2, keep.PKs[1], 30)
got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
// now that we've reached maximum validators, validator-2 should be added to the cliff (top)
cliffVal = keeper.GetCliffValidator(ctx)
require.Equal(t, validatorAddr2.Bytes(), cliffVal)
// add the third validator, which should not make it to being bonded,
// so the cliff validator should not change because nobody has been kicked out
msgCreateValidator = newTestMsgCreateValidator(validatorAddr3, keep.PKs[2], 10)
got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
cliffVal = keeper.GetCliffValidator(ctx)
require.Equal(t, validatorAddr2.Bytes(), cliffVal)
// unbond valdator-2
msgBeginUnbonding := NewMsgBeginUnbonding(sdk.AccAddress(validatorAddr2), validatorAddr2, sdk.NewDec(30))
got = handleMsgBeginUnbonding(ctx, msgBeginUnbonding, keeper)
require.True(t, got.IsOK(), "expected no error on runMsgBeginUnbonding")
vals := keeper.GetValidatorsBonded(ctx)
require.Equal(t, 2, len(vals))
// now the validator set should be updated,
// where val-3 enters the validator set on the cliff
cliffVal = keeper.GetCliffValidator(ctx)
require.Equal(t, validatorAddr3.Bytes(), cliffVal)
// unbond valdator-1
msgBeginUnbonding = NewMsgBeginUnbonding(sdk.AccAddress(validatorAddr1), validatorAddr1, sdk.NewDec(50))
got = handleMsgBeginUnbonding(ctx, msgBeginUnbonding, keeper)
require.True(t, got.IsOK(), "expected no error on runMsgBeginUnbonding")
// get bonded validators - should just be one
vals = keeper.GetValidatorsBonded(ctx)
require.Equal(t, 1, len(vals))
// cliff now should be empty
cliffVal = keeper.GetCliffValidator(ctx)
require.Equal(t, []byte(nil), cliffVal)
}
func TestBondUnbondRedelegateSlashTwice(t *testing.T) {
ctx, _, keeper := keep.CreateTestInput(t, false, 1000)
valA, valB, del := sdk.ValAddress(keep.Addrs[0]), sdk.ValAddress(keep.Addrs[1]), keep.Addrs[2]
@ -931,6 +832,10 @@ func TestBondUnbondRedelegateSlashTwice(t *testing.T) {
got = handleMsgDelegate(ctx, msgDelegate, keeper)
require.True(t, got.IsOK(), "expected no error on runMsgDelegate")
// apply Tendermint updates
updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx)
require.Equal(t, 2, len(updates))
// a block passes
ctx = ctx.WithBlockHeight(1)
@ -949,6 +854,10 @@ func TestBondUnbondRedelegateSlashTwice(t *testing.T) {
require.True(t, found)
require.Equal(t, sdk.NewDec(6), delegation.Shares)
// must apply validator updates
updates = keeper.ApplyAndReturnValidatorSetUpdates(ctx)
require.Equal(t, 2, len(updates))
// slash the validator by half
keeper.Slash(ctx, consAddr0, 0, 20, sdk.NewDecWithPrec(5, 1))
@ -991,6 +900,9 @@ func TestBondUnbondRedelegateSlashTwice(t *testing.T) {
require.True(t, found)
require.Equal(t, sdk.NewDec(3), delegation.Shares)
// end blocker
EndBlocker(ctx, keeper)
// validator power should have been reduced to zero
// ergo validator should have been removed from the store
_, found = keeper.GetValidator(ctx, valA)

View File

@ -41,9 +41,3 @@ prefixed areas of the staking store which are accessed in `x/stake/keeper.go`.
The transient store persists between transations but not between blocks
## Tendermint Updates
- Prefix Key Space: TendermintUpdatesTKey
- Key/Sort: Validator Operator Address
- Value: Tendermint ABCI Validator
- Contains: Validators are queued to affect the consensus validation set in Tendermint
- Used For: Informing Tendermint of the validator set updates

View File

@ -265,18 +265,13 @@ func (k Keeper) Delegate(ctx sdk.Context, delAddr sdk.AccAddress, bondAmt sdk.Co
}
}
pool := k.GetPool(ctx)
validator, pool, newShares = validator.AddTokensFromDel(pool, bondAmt.Amount)
validator, newShares = k.AddValidatorTokensAndShares(ctx, validator, bondAmt.Amount)
// Update delegation
delegation.Shares = delegation.Shares.Add(newShares)
// Update delegation height
delegation.Height = ctx.BlockHeight()
k.SetPool(ctx, pool)
k.SetDelegation(ctx, delegation)
k.UpdateValidator(ctx, validator)
return
return newShares, nil
}
// unbond the the delegation return
@ -313,8 +308,9 @@ func (k Keeper) unbond(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValA
// if the delegation is the operator of the validator then
// trigger a jail validator
if bytes.Equal(delegation.DelegatorAddr, validator.OperatorAddr) && validator.Jailed == false {
validator.Jailed = true
if bytes.Equal(delegation.DelegatorAddr, validator.OperatorAddr) && !validator.Jailed {
k.jailValidator(ctx, validator)
validator = k.mustGetValidator(ctx, validator.OperatorAddr)
}
k.RemoveDelegation(ctx, delegation)
@ -325,14 +321,10 @@ func (k Keeper) unbond(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValA
}
// remove the coins from the validator
pool := k.GetPool(ctx)
validator, pool, amount = validator.RemoveDelShares(pool, shares)
validator, amount = k.RemoveValidatorTokensAndShares(ctx, validator, shares)
k.SetPool(ctx, pool)
// update then remove validator if necessary
validator = k.UpdateValidator(ctx, validator)
if validator.DelegatorShares.IsZero() {
if validator.DelegatorShares.IsZero() && validator.Status != sdk.Bonded {
// if bonded, we must remove in EndBlocker instead
k.RemoveValidator(ctx, validator.OperatorAddr)
}
@ -368,7 +360,7 @@ func (k Keeper) getBeginInfo(ctx sdk.Context, params types.Params, valSrcAddr sd
}
}
// complete unbonding an unbonding record
// begin unbonding an unbonding record
func (k Keeper) BeginUnbonding(ctx sdk.Context,
delAddr sdk.AccAddress, valAddr sdk.ValAddress, sharesAmount sdk.Dec) sdk.Error {

View File

@ -25,9 +25,9 @@ func TestDelegation(t *testing.T) {
}
keeper.SetPool(ctx, pool)
validators[0] = keeper.UpdateValidator(ctx, validators[0])
validators[1] = keeper.UpdateValidator(ctx, validators[1])
validators[2] = keeper.UpdateValidator(ctx, validators[2])
validators[0] = testingUpdateValidator(keeper, ctx, validators[0])
validators[1] = testingUpdateValidator(keeper, ctx, validators[1])
validators[2] = testingUpdateValidator(keeper, ctx, validators[2])
// first add a validators[0] to delegate too
@ -184,7 +184,7 @@ func TestUnbondDelegation(t *testing.T) {
validator, pool, issuedShares := validator.AddTokensFromDel(pool, sdk.NewInt(10))
require.Equal(t, int64(10), issuedShares.RoundInt64())
keeper.SetPool(ctx, pool)
validator = keeper.UpdateValidator(ctx, validator)
validator = testingUpdateValidator(keeper, ctx, validator)
pool = keeper.GetPool(ctx)
require.Equal(t, int64(10), pool.BondedTokens.RoundInt64())
@ -226,7 +226,7 @@ func TestUndelegateSelfDelegation(t *testing.T) {
validator, pool, issuedShares := validator.AddTokensFromDel(pool, sdk.NewInt(10))
require.Equal(t, int64(10), issuedShares.RoundInt64())
keeper.SetPool(ctx, pool)
validator = keeper.UpdateValidator(ctx, validator)
validator = testingUpdateValidator(keeper, ctx, validator)
pool = keeper.GetPool(ctx)
selfDelegation := types.Delegation{
DelegatorAddr: sdk.AccAddress(addrVals[0].Bytes()),
@ -236,10 +236,11 @@ func TestUndelegateSelfDelegation(t *testing.T) {
keeper.SetDelegation(ctx, selfDelegation)
// create a second delegation to this validator
keeper.DeleteValidatorByPowerIndex(ctx, validator, pool)
validator, pool, issuedShares = validator.AddTokensFromDel(pool, sdk.NewInt(10))
require.Equal(t, int64(10), issuedShares.RoundInt64())
keeper.SetPool(ctx, pool)
validator = keeper.UpdateValidator(ctx, validator)
validator = testingUpdateValidator(keeper, ctx, validator)
pool = keeper.GetPool(ctx)
delegation := types.Delegation{
DelegatorAddr: addrDels[0],
@ -252,6 +253,10 @@ func TestUndelegateSelfDelegation(t *testing.T) {
err := keeper.BeginUnbonding(ctx, val0AccAddr, addrVals[0], sdk.NewDec(10))
require.NoError(t, err)
// end block
updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx)
require.Equal(t, 1, len(updates))
validator, found := keeper.GetValidator(ctx, addrVals[0])
require.True(t, found)
require.Equal(t, int64(10), validator.Tokens.RoundInt64())
@ -269,7 +274,7 @@ func TestUndelegateFromUnbondingValidator(t *testing.T) {
validator, pool, issuedShares := validator.AddTokensFromDel(pool, sdk.NewInt(10))
require.Equal(t, int64(10), issuedShares.RoundInt64())
keeper.SetPool(ctx, pool)
validator = keeper.UpdateValidator(ctx, validator)
validator = testingUpdateValidator(keeper, ctx, validator)
pool = keeper.GetPool(ctx)
selfDelegation := types.Delegation{
DelegatorAddr: sdk.AccAddress(addrVals[0].Bytes()),
@ -279,10 +284,11 @@ func TestUndelegateFromUnbondingValidator(t *testing.T) {
keeper.SetDelegation(ctx, selfDelegation)
// create a second delegation to this validator
keeper.DeleteValidatorByPowerIndex(ctx, validator, pool)
validator, pool, issuedShares = validator.AddTokensFromDel(pool, sdk.NewInt(10))
require.Equal(t, int64(10), issuedShares.RoundInt64())
keeper.SetPool(ctx, pool)
validator = keeper.UpdateValidator(ctx, validator)
validator = testingUpdateValidator(keeper, ctx, validator)
pool = keeper.GetPool(ctx)
delegation := types.Delegation{
DelegatorAddr: addrDels[0],
@ -303,6 +309,10 @@ func TestUndelegateFromUnbondingValidator(t *testing.T) {
err := keeper.BeginUnbonding(ctx, val0AccAddr, addrVals[0], sdk.NewDec(10))
require.NoError(t, err)
// end block
updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx)
require.Equal(t, 1, len(updates))
validator, found := keeper.GetValidator(ctx, addrVals[0])
require.True(t, found)
require.Equal(t, blockHeight, validator.UnbondingHeight)
@ -340,7 +350,7 @@ func TestUndelegateFromUnbondedValidator(t *testing.T) {
validator, pool, issuedShares := validator.AddTokensFromDel(pool, sdk.NewInt(10))
require.Equal(t, int64(10), issuedShares.RoundInt64())
keeper.SetPool(ctx, pool)
validator = keeper.UpdateValidator(ctx, validator)
validator = testingUpdateValidator(keeper, ctx, validator)
pool = keeper.GetPool(ctx)
val0AccAddr := sdk.AccAddress(addrVals[0].Bytes())
selfDelegation := types.Delegation{
@ -351,10 +361,11 @@ func TestUndelegateFromUnbondedValidator(t *testing.T) {
keeper.SetDelegation(ctx, selfDelegation)
// create a second delegation to this validator
keeper.DeleteValidatorByPowerIndex(ctx, validator, pool)
validator, pool, issuedShares = validator.AddTokensFromDel(pool, sdk.NewInt(10))
require.Equal(t, int64(10), issuedShares.RoundInt64())
keeper.SetPool(ctx, pool)
validator = keeper.UpdateValidator(ctx, validator)
validator = testingUpdateValidator(keeper, ctx, validator)
pool = keeper.GetPool(ctx)
delegation := types.Delegation{
DelegatorAddr: addrDels[0],
@ -374,6 +385,10 @@ func TestUndelegateFromUnbondedValidator(t *testing.T) {
err := keeper.BeginUnbonding(ctx, val0AccAddr, addrVals[0], sdk.NewDec(10))
require.NoError(t, err)
// end block
updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx)
require.Equal(t, 1, len(updates))
validator, found := keeper.GetValidator(ctx, addrVals[0])
require.True(t, found)
require.Equal(t, blockHeight, validator.UnbondingHeight)
@ -506,7 +521,7 @@ func TestRedelegateSelfDelegation(t *testing.T) {
validator, pool, issuedShares := validator.AddTokensFromDel(pool, sdk.NewInt(10))
require.Equal(t, int64(10), issuedShares.RoundInt64())
keeper.SetPool(ctx, pool)
validator = keeper.UpdateValidator(ctx, validator)
validator = testingUpdateValidator(keeper, ctx, validator)
pool = keeper.GetPool(ctx)
val0AccAddr := sdk.AccAddress(addrVals[0].Bytes())
selfDelegation := types.Delegation{
@ -520,14 +535,16 @@ func TestRedelegateSelfDelegation(t *testing.T) {
validator2 := types.NewValidator(addrVals[1], PKs[1], types.Description{})
validator2, pool, issuedShares = validator2.AddTokensFromDel(pool, sdk.NewInt(10))
require.Equal(t, int64(10), issuedShares.RoundInt64())
pool.BondedTokens = pool.BondedTokens.Add(sdk.NewDec(10))
keeper.SetPool(ctx, pool)
validator2 = keeper.UpdateValidator(ctx, validator2)
validator2 = testingUpdateValidator(keeper, ctx, validator2)
require.Equal(t, sdk.Bonded, validator2.Status)
// create a second delegation to this validator
validator, pool, issuedShares = validator.AddTokensFromDel(pool, sdk.NewInt(10))
require.Equal(t, int64(10), issuedShares.RoundInt64())
keeper.SetPool(ctx, pool)
validator = keeper.UpdateValidator(ctx, validator)
validator = testingUpdateValidator(keeper, ctx, validator)
pool = keeper.GetPool(ctx)
delegation := types.Delegation{
DelegatorAddr: addrDels[0],
@ -539,6 +556,10 @@ func TestRedelegateSelfDelegation(t *testing.T) {
err := keeper.BeginRedelegation(ctx, val0AccAddr, addrVals[0], addrVals[1], sdk.NewDec(10))
require.NoError(t, err)
// end block
updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx)
require.Equal(t, 2, len(updates))
validator, found := keeper.GetValidator(ctx, addrVals[0])
require.True(t, found)
require.Equal(t, int64(10), validator.Tokens.RoundInt64())
@ -552,11 +573,12 @@ func TestRedelegateFromUnbondingValidator(t *testing.T) {
//create a validator with a self-delegation
validator := types.NewValidator(addrVals[0], PKs[0], types.Description{})
validator.BondIntraTxCounter = 1
validator, pool, issuedShares := validator.AddTokensFromDel(pool, sdk.NewInt(10))
require.Equal(t, int64(10), issuedShares.RoundInt64())
keeper.SetPool(ctx, pool)
validator = keeper.UpdateValidator(ctx, validator)
validator = testingUpdateValidator(keeper, ctx, validator)
pool = keeper.GetPool(ctx)
val0AccAddr := sdk.AccAddress(addrVals[0].Bytes())
selfDelegation := types.Delegation{
@ -567,10 +589,11 @@ func TestRedelegateFromUnbondingValidator(t *testing.T) {
keeper.SetDelegation(ctx, selfDelegation)
// create a second delegation to this validator
keeper.DeleteValidatorByPowerIndex(ctx, validator, pool)
validator, pool, issuedShares = validator.AddTokensFromDel(pool, sdk.NewInt(10))
require.Equal(t, int64(10), issuedShares.RoundInt64())
keeper.SetPool(ctx, pool)
validator = keeper.UpdateValidator(ctx, validator)
validator = testingUpdateValidator(keeper, ctx, validator)
pool = keeper.GetPool(ctx)
delegation := types.Delegation{
DelegatorAddr: addrDels[0],
@ -581,10 +604,11 @@ func TestRedelegateFromUnbondingValidator(t *testing.T) {
// create a second validator
validator2 := types.NewValidator(addrVals[1], PKs[1], types.Description{})
validator2.BondIntraTxCounter = 2
validator2, pool, issuedShares = validator2.AddTokensFromDel(pool, sdk.NewInt(10))
require.Equal(t, int64(10), issuedShares.RoundInt64())
keeper.SetPool(ctx, pool)
validator2 = keeper.UpdateValidator(ctx, validator2)
validator2 = testingUpdateValidator(keeper, ctx, validator2)
header := ctx.BlockHeader()
blockHeight := int64(10)
@ -597,6 +621,10 @@ func TestRedelegateFromUnbondingValidator(t *testing.T) {
err := keeper.BeginUnbonding(ctx, val0AccAddr, addrVals[0], sdk.NewDec(10))
require.NoError(t, err)
// end block
updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx)
require.Equal(t, 1, len(updates))
validator, found := keeper.GetValidator(ctx, addrVals[0])
require.True(t, found)
require.Equal(t, blockHeight, validator.UnbondingHeight)
@ -634,7 +662,7 @@ func TestRedelegateFromUnbondedValidator(t *testing.T) {
validator, pool, issuedShares := validator.AddTokensFromDel(pool, sdk.NewInt(10))
require.Equal(t, int64(10), issuedShares.RoundInt64())
keeper.SetPool(ctx, pool)
validator = keeper.UpdateValidator(ctx, validator)
validator = testingUpdateValidator(keeper, ctx, validator)
pool = keeper.GetPool(ctx)
val0AccAddr := sdk.AccAddress(addrVals[0].Bytes())
selfDelegation := types.Delegation{
@ -645,10 +673,12 @@ func TestRedelegateFromUnbondedValidator(t *testing.T) {
keeper.SetDelegation(ctx, selfDelegation)
// create a second delegation to this validator
keeper.DeleteValidatorByPowerIndex(ctx, validator, pool)
validator, pool, issuedShares = validator.AddTokensFromDel(pool, sdk.NewInt(10))
validator.BondIntraTxCounter = 1
require.Equal(t, int64(10), issuedShares.RoundInt64())
keeper.SetPool(ctx, pool)
validator = keeper.UpdateValidator(ctx, validator)
validator = testingUpdateValidator(keeper, ctx, validator)
pool = keeper.GetPool(ctx)
delegation := types.Delegation{
DelegatorAddr: addrDels[0],
@ -659,10 +689,12 @@ func TestRedelegateFromUnbondedValidator(t *testing.T) {
// create a second validator
validator2 := types.NewValidator(addrVals[1], PKs[1], types.Description{})
validator2.BondIntraTxCounter = 2
validator2, pool, issuedShares = validator2.AddTokensFromDel(pool, sdk.NewInt(10))
require.Equal(t, int64(10), issuedShares.RoundInt64())
keeper.SetPool(ctx, pool)
validator2 = keeper.UpdateValidator(ctx, validator2)
validator2 = testingUpdateValidator(keeper, ctx, validator2)
require.Equal(t, sdk.Bonded, validator2.Status)
header := ctx.BlockHeader()
blockHeight := int64(10)
@ -675,6 +707,10 @@ func TestRedelegateFromUnbondedValidator(t *testing.T) {
err := keeper.BeginUnbonding(ctx, val0AccAddr, addrVals[0], sdk.NewDec(10))
require.NoError(t, err)
// end block
updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx)
require.Equal(t, 1, len(updates))
validator, found := keeper.GetValidator(ctx, addrVals[0])
require.True(t, found)
require.Equal(t, blockHeight, validator.UnbondingHeight)

View File

@ -64,25 +64,9 @@ func (k Keeper) GetParams(ctx sdk.Context) (params types.Params) {
return
}
// Need a distinct function because setParams depends on an existing previous
// record of params to exist (to check if maxValidators has changed) - and we
// panic on retrieval if it doesn't exist - hence if we use setParams for the very
// first params set it will panic.
func (k Keeper) SetNewParams(ctx sdk.Context, params types.Params) {
store := ctx.KVStore(k.storeKey)
b := k.cdc.MustMarshalBinary(params)
store.Set(ParamKey, b)
}
// set the params
func (k Keeper) SetParams(ctx sdk.Context, params types.Params) {
store := ctx.KVStore(k.storeKey)
exParams := k.GetParams(ctx)
// if max validator count changes, must recalculate validator set
if exParams.MaxValidators != params.MaxValidators {
k.UpdateBondedValidatorsFull(ctx)
}
b := k.cdc.MustMarshalBinary(params)
store.Set(ParamKey, b)
}

View File

@ -18,18 +18,13 @@ var (
ValidatorsByConsAddrKey = []byte{0x03} // prefix for each key to a validator index, by pubkey
ValidatorsBondedIndexKey = []byte{0x04} // prefix for each key to a validator index, for bonded validators
ValidatorsByPowerIndexKey = []byte{0x05} // prefix for each key to a validator index, sorted by power
ValidatorCliffIndexKey = []byte{0x06} // key for the validator index of the cliff validator
ValidatorPowerCliffKey = []byte{0x07} // key for the power of the validator on the cliff
IntraTxCounterKey = []byte{0x08} // key for intra-block tx index
DelegationKey = []byte{0x09} // key for a delegation
UnbondingDelegationKey = []byte{0x0A} // key for an unbonding-delegation
UnbondingDelegationByValIndexKey = []byte{0x0B} // prefix for each key for an unbonding-delegation, by validator operator
RedelegationKey = []byte{0x0C} // key for a redelegation
RedelegationByValSrcIndexKey = []byte{0x0D} // prefix for each key for an redelegation, by source validator operator
RedelegationByValDstIndexKey = []byte{0x0E} // prefix for each key for an redelegation, by destination validator operator
// Keys for store prefixes (transient)
TendermintUpdatesTKey = []byte{0x00} // prefix for each key to a validator which is being updated
IntraTxCounterKey = []byte{0x06} // key for intra-block tx index
DelegationKey = []byte{0x07} // key for a delegation
UnbondingDelegationKey = []byte{0x08} // key for an unbonding-delegation
UnbondingDelegationByValIndexKey = []byte{0x09} // prefix for each key for an unbonding-delegation, by validator operator
RedelegationKey = []byte{0x0A} // key for a redelegation
RedelegationByValSrcIndexKey = []byte{0x0B} // prefix for each key for an redelegation, by source validator operator
RedelegationByValDstIndexKey = []byte{0x0C} // prefix for each key for an redelegation, by destination validator operator
)
const maxDigitsForAccount = 12 // ~220,000,000 atoms created at launch
@ -46,12 +41,6 @@ func GetValidatorByConsAddrKey(addr sdk.ConsAddress) []byte {
return append(ValidatorsByConsAddrKey, addr.Bytes()...)
}
// gets the key for the current validator group
// VALUE: none (key rearrangement with GetValKeyFromValBondedIndexKey)
func GetValidatorsBondedIndexKey(operatorAddr sdk.ValAddress) []byte {
return append(ValidatorsBondedIndexKey, operatorAddr.Bytes()...)
}
// Get the validator operator address from ValBondedIndexKey
func GetAddressFromValBondedIndexKey(IndexKey []byte) []byte {
return IndexKey[1:] // remove prefix bytes
@ -66,6 +55,11 @@ func GetValidatorsByPowerIndexKey(validator types.Validator, pool types.Pool) []
return getValidatorPowerRank(validator, pool)
}
// get the bonded validator index key for an operator address
func GetBondedValidatorIndexKey(operator sdk.ValAddress) []byte {
return append(ValidatorsBondedIndexKey, operator...)
}
// get the power ranking of a validator
// NOTE the larger values are of higher value
// nolint: unparam
@ -74,34 +68,19 @@ func getValidatorPowerRank(validator types.Validator, pool types.Pool) []byte {
potentialPower := validator.Tokens
powerBytes := []byte(potentialPower.ToLeftPadded(maxDigitsForAccount)) // power big-endian (more powerful validators first)
jailedBytes := make([]byte, 1)
if validator.Jailed {
jailedBytes[0] = byte(0x00)
} else {
jailedBytes[0] = byte(0x01)
}
// heightBytes and counterBytes represent strings like powerBytes does
heightBytes := make([]byte, binary.MaxVarintLen64)
binary.BigEndian.PutUint64(heightBytes, ^uint64(validator.BondHeight)) // invert height (older validators first)
counterBytes := make([]byte, 2)
binary.BigEndian.PutUint16(counterBytes, ^uint16(validator.BondIntraTxCounter)) // invert counter (first txns have priority)
return append(append(append(append(
return append(append(append(
ValidatorsByPowerIndexKey,
jailedBytes...),
powerBytes...),
heightBytes...),
counterBytes...)
}
// get the key for the accumulated update validators
// VALUE: abci.Validator
// note records using these keys should never persist between blocks
func GetTendermintUpdatesTKey(operatorAddr sdk.ValAddress) []byte {
return append(TendermintUpdatesTKey, operatorAddr.Bytes()...)
}
//______________________________________________________________________________
// gets the key for delegator bond with validator

View File

@ -95,20 +95,18 @@ func (k Keeper) Slash(ctx sdk.Context, consAddr sdk.ConsAddress, infractionHeigh
}
}
// Cannot decrease balance below zero
// cannot decrease balance below zero
tokensToBurn := sdk.MinDec(remainingSlashAmount, validator.Tokens)
// burn validator's tokens
// burn validator's tokens and update the validator
validator = k.RemoveValidatorTokens(ctx, validator, tokensToBurn)
pool := k.GetPool(ctx)
validator, pool = validator.RemoveTokens(pool, tokensToBurn)
pool.LooseTokens = pool.LooseTokens.Sub(tokensToBurn)
k.SetPool(ctx, pool)
// update the validator, possibly kicking it out
validator = k.UpdateValidator(ctx, validator)
// remove validator if it has no more tokens
if validator.Tokens.IsZero() {
if validator.Tokens.IsZero() && validator.Status != sdk.Bonded {
// if bonded, we must remove in ApplyAndReturnValidatorSetUpdates instead
k.RemoveValidator(ctx, validator.OperatorAddr)
}
@ -123,7 +121,8 @@ func (k Keeper) Slash(ctx sdk.Context, consAddr sdk.ConsAddress, infractionHeigh
// jail a validator
func (k Keeper) Jail(ctx sdk.Context, consAddr sdk.ConsAddress) {
k.setJailed(ctx, consAddr, true)
validator := k.mustGetValidatorByConsAddr(ctx, consAddr)
k.jailValidator(ctx, validator)
logger := ctx.Logger().With("module", "x/stake")
logger.Info(fmt.Sprintf("validator %s jailed", consAddr))
// TODO Return event(s), blocked on https://github.com/tendermint/tendermint/pull/1803
@ -132,24 +131,14 @@ func (k Keeper) Jail(ctx sdk.Context, consAddr sdk.ConsAddress) {
// unjail a validator
func (k Keeper) Unjail(ctx sdk.Context, consAddr sdk.ConsAddress) {
k.setJailed(ctx, consAddr, false)
validator := k.mustGetValidatorByConsAddr(ctx, consAddr)
k.unjailValidator(ctx, validator)
logger := ctx.Logger().With("module", "x/stake")
logger.Info(fmt.Sprintf("validator %s unjailed", consAddr))
// TODO Return event(s), blocked on https://github.com/tendermint/tendermint/pull/1803
return
}
// set the jailed flag on a validator
func (k Keeper) setJailed(ctx sdk.Context, consAddr sdk.ConsAddress, isJailed bool) {
validator, found := k.GetValidatorByConsAddr(ctx, consAddr)
if !found {
panic(fmt.Errorf("validator with consensus-Address %s not found, cannot set jailed to %v", consAddr, isJailed))
}
validator.Jailed = isJailed
k.UpdateValidator(ctx, validator) // update validator, possibly unbonding or bonding it
return
}
// slash an unbonding delegation and update the pool
// return the amount that would have been slashed assuming
// the unbonding delegation had enough stake to slash

View File

@ -26,8 +26,10 @@ func setupHelper(t *testing.T, amt int64) (sdk.Context, Keeper, types.Params) {
for i := 0; i < numVals; i++ {
validator := types.NewValidator(addrVals[i], PKs[i], types.Description{})
validator, pool, _ = validator.AddTokensFromDel(pool, sdk.NewInt(amt))
validator.BondIntraTxCounter = int16(i)
pool.BondedTokens = pool.BondedTokens.Add(sdk.NewDec(amt))
keeper.SetPool(ctx, pool)
validator = keeper.UpdateValidator(ctx, validator)
validator = testingUpdateValidator(keeper, ctx, validator)
keeper.SetValidatorByConsAddr(ctx, validator)
}
pool = keeper.GetPool(ctx)
@ -161,6 +163,10 @@ func TestSlashRedelegation(t *testing.T) {
rd, found = keeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1])
require.True(t, found)
// end block
updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx)
require.Equal(t, 1, len(updates))
// initialbalance unchanged
require.Equal(t, sdk.NewInt64Coin(params.BondDenom, 10), rd.InitialBalance)
@ -201,6 +207,11 @@ func TestSlashValidatorAtCurrentHeight(t *testing.T) {
require.True(t, found)
newPool := keeper.GetPool(ctx)
// end block
updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx)
require.Equal(t, 1, len(updates), "cons addr: %v, updates: %v", []byte(consAddr), updates)
validator = keeper.mustGetValidator(ctx, validator.OperatorAddr)
// power decreased
require.Equal(t, sdk.NewDec(5), validator.GetPower())
// pool bonded shares decreased
@ -232,6 +243,10 @@ func TestSlashWithUnbondingDelegation(t *testing.T) {
require.True(t, found)
keeper.Slash(ctx, consAddr, 10, 10, fraction)
// end block
updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx)
require.Equal(t, 1, len(updates))
// read updating unbonding delegation
ubd, found = keeper.GetUnbondingDelegation(ctx, addrDels[0], addrVals[0])
require.True(t, found)
@ -301,6 +316,8 @@ func TestSlashWithUnbondingDelegation(t *testing.T) {
newPool = keeper.GetPool(ctx)
// just 1 bonded token burned again since that's all the validator now has
require.Equal(t, int64(10), oldPool.BondedTokens.Sub(newPool.BondedTokens).RoundInt64())
// apply TM updates
keeper.ApplyAndReturnValidatorSetUpdates(ctx)
// read updated validator
// power decreased by 1 again, validator is out of stake
// ergo validator should have been removed from the store
@ -402,6 +419,8 @@ func TestSlashWithRedelegation(t *testing.T) {
newPool = keeper.GetPool(ctx)
// four more bonded tokens burned
require.Equal(t, int64(16), oldPool.BondedTokens.Sub(newPool.BondedTokens).RoundInt64())
// apply TM updates
keeper.ApplyAndReturnValidatorSetUpdates(ctx)
// read updated validator
// validator decreased to zero power, should have been removed from the store
_, found = keeper.GetValidatorByConsAddr(ctx, consAddr)

View File

@ -111,7 +111,7 @@ func CreateTestInput(t *testing.T, isCheckTx bool, initCoins int64) (sdk.Context
ck := bank.NewBaseKeeper(accountMapper)
keeper := NewKeeper(cdc, keyStake, tkeyStake, ck, types.DefaultCodespace)
keeper.SetPool(ctx, types.InitialPool())
keeper.SetNewParams(ctx, types.DefaultParams())
keeper.SetParams(ctx, types.DefaultParams())
keeper.InitIntraTxCounter(ctx)
// fill all the addresses with some coins, set the loose pool tokens simultaneously
@ -202,5 +202,22 @@ func createTestPubKeys(numPubKeys int) []crypto.PubKey {
// does a certain by-power index record exist
func ValidatorByPowerIndexExists(ctx sdk.Context, keeper Keeper, power []byte) bool {
store := ctx.KVStore(keeper.storeKey)
return store.Get(power) != nil
return store.Has(power)
}
func testingUpdateValidator(keeper Keeper, ctx sdk.Context, validator types.Validator) types.Validator {
pool := keeper.GetPool(ctx)
keeper.SetValidator(ctx, validator)
keeper.SetValidatorByPowerIndex(ctx, validator, pool)
keeper.ApplyAndReturnValidatorSetUpdates(ctx)
validator, found := keeper.GetValidator(ctx, validator.OperatorAddr)
if !found {
panic("validator expected but not found")
}
return validator
}
func validatorByPowerIndexExists(k Keeper, ctx sdk.Context, power []byte) bool {
store := ctx.KVStore(k.storeKey)
return store.Has(power)
}

View File

@ -0,0 +1,268 @@
package keeper
import (
"bytes"
"fmt"
"sort"
abci "github.com/tendermint/tendermint/abci/types"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/stake/types"
)
// Apply and return accumulated updates to the bonded validator set
//
// CONTRACT: Only validators with non-zero power or zero-power that were bonded
// at the previous block height or were removed from the validator set entirely
// are returned to Tendermint.
func (k Keeper) ApplyAndReturnValidatorSetUpdates(ctx sdk.Context) (updates []abci.ValidatorUpdate) {
store := ctx.KVStore(k.storeKey)
maxValidators := k.GetParams(ctx).MaxValidators
// retrieve last validator set
last := k.retrieveLastValidatorSet(ctx)
// iterate over validators, highest power to lowest
iterator := sdk.KVStoreReversePrefixIterator(store, ValidatorsByPowerIndexKey)
count := 0
for ; iterator.Valid() && count < int(maxValidators); iterator.Next() {
// fetch the validator
operator := sdk.ValAddress(iterator.Value())
validator := k.mustGetValidator(ctx, operator)
if validator.Jailed {
panic("should never retrieve a jailed validator from the power store")
}
// if we get to a zero-power validator (which we don't bond),
// there are no more possible bonded validators
// note: we must check the ABCI power, since we round before sending to Tendermint
if validator.Tokens.RoundInt64() == int64(0) {
break
}
// apply the appropriate state change if necessary
switch validator.Status {
case sdk.Unbonded:
validator = k.unbondedToBonded(ctx, validator)
case sdk.Unbonding:
validator = k.unbondingToBonded(ctx, validator)
case sdk.Bonded:
// no state change
default:
panic("unexpected validator status")
}
// fetch the old power bytes
var operatorBytes [sdk.AddrLen]byte
copy(operatorBytes[:], operator[:])
oldPowerBytes, found := last[operatorBytes]
// calculate the new power bytes
newPowerBytes := validator.ABCIValidatorPowerBytes(k.cdc)
// update the validator set if power has changed
if !found || !bytes.Equal(oldPowerBytes, newPowerBytes) {
updates = append(updates, validator.ABCIValidatorUpdate())
}
// validator still in the validator set, so delete from the copy
delete(last, operatorBytes)
// set the bonded validator index
store.Set(GetBondedValidatorIndexKey(operator), newPowerBytes)
// keep count
count++
}
// sort the no-longer-bonded validators
noLongerBonded := k.sortNoLongerBonded(last)
// iterate through the sorted no-longer-bonded validators
for _, operator := range noLongerBonded {
// fetch the validator
validator := k.mustGetValidator(ctx, sdk.ValAddress(operator))
// bonded to unbonding
k.bondedToUnbonding(ctx, validator)
// remove validator if it has no more tokens
if validator.Tokens.IsZero() {
k.RemoveValidator(ctx, validator.OperatorAddr)
}
// delete from the bonded validator index
store.Delete(GetBondedValidatorIndexKey(operator))
// update the validator set
updates = append(updates, validator.ABCIValidatorUpdateZero())
}
return updates
}
// Validator state transitions
func (k Keeper) bondedToUnbonding(ctx sdk.Context, validator types.Validator) types.Validator {
if validator.Status != sdk.Bonded {
panic(fmt.Sprintf("bad state transition bondedToUnbonding, validator: %v\n", validator))
}
return k.beginUnbondingValidator(ctx, validator)
}
func (k Keeper) unbondingToBonded(ctx sdk.Context, validator types.Validator) types.Validator {
if validator.Status != sdk.Unbonding {
panic(fmt.Sprintf("bad state transition unbondingToBonded, validator: %v\n", validator))
}
return k.bondValidator(ctx, validator)
}
func (k Keeper) unbondedToBonded(ctx sdk.Context, validator types.Validator) types.Validator {
if validator.Status != sdk.Unbonded {
panic(fmt.Sprintf("bad state transition unbondedToBonded, validator: %v\n", validator))
}
return k.bondValidator(ctx, validator)
}
func (k Keeper) unbondingToUnbonded(ctx sdk.Context, validator types.Validator) types.Validator {
if validator.Status != sdk.Unbonded {
panic(fmt.Sprintf("bad state transition unbondingToBonded, validator: %v\n", validator))
}
return k.completeUnbondingValidator(ctx, validator)
}
// send a validator to jail
func (k Keeper) jailValidator(ctx sdk.Context, validator types.Validator) {
if validator.Jailed {
panic(fmt.Sprintf("cannot jail already jailed validator, validator: %v\n", validator))
}
pool := k.GetPool(ctx)
validator.Jailed = true
k.SetValidator(ctx, validator)
k.DeleteValidatorByPowerIndex(ctx, validator, pool)
}
// remove a validator from jail
func (k Keeper) unjailValidator(ctx sdk.Context, validator types.Validator) {
if !validator.Jailed {
panic(fmt.Sprintf("cannot unjail already unjailed validator, validator: %v\n", validator))
}
pool := k.GetPool(ctx)
validator.Jailed = false
k.SetValidator(ctx, validator)
k.SetValidatorByPowerIndex(ctx, validator, pool)
}
// perform all the store operations for when a validator status becomes bonded
func (k Keeper) bondValidator(ctx sdk.Context, validator types.Validator) types.Validator {
pool := k.GetPool(ctx)
k.DeleteValidatorByPowerIndex(ctx, validator, pool)
validator.BondHeight = ctx.BlockHeight()
// set the status
validator, pool = validator.UpdateStatus(pool, sdk.Bonded)
k.SetPool(ctx, pool)
// save the now bonded validator record to the three referenced stores
k.SetValidator(ctx, validator)
k.SetValidatorByPowerIndex(ctx, validator, pool)
// call the bond hook if present
if k.hooks != nil {
k.hooks.OnValidatorBonded(ctx, validator.ConsAddress())
}
return validator
}
// perform all the store operations for when a validator status begins unbonding
func (k Keeper) beginUnbondingValidator(ctx sdk.Context, validator types.Validator) types.Validator {
pool := k.GetPool(ctx)
params := k.GetParams(ctx)
k.DeleteValidatorByPowerIndex(ctx, validator, pool)
// sanity check
if validator.Status != sdk.Bonded {
panic(fmt.Sprintf("should not already be unbonded or unbonding, validator: %v\n", validator))
}
// set the status
validator, pool = validator.UpdateStatus(pool, sdk.Unbonding)
k.SetPool(ctx, pool)
validator.UnbondingMinTime = ctx.BlockHeader().Time.Add(params.UnbondingTime)
validator.UnbondingHeight = ctx.BlockHeader().Height
// save the now unbonded validator record
k.SetValidator(ctx, validator)
k.SetValidatorByPowerIndex(ctx, validator, pool)
// call the unbond hook if present
if k.hooks != nil {
k.hooks.OnValidatorBeginUnbonding(ctx, validator.ConsAddress())
}
return validator
}
// perform all the store operations for when a validator status becomes unbonded
func (k Keeper) completeUnbondingValidator(ctx sdk.Context, validator types.Validator) types.Validator {
pool := k.GetPool(ctx)
validator, pool = validator.UpdateStatus(pool, sdk.Unbonded)
k.SetPool(ctx, pool)
k.SetValidator(ctx, validator)
return validator
}
// map of operator addresses to serialized power
type validatorsByAddr map[[sdk.AddrLen]byte][]byte
// retrieve the last validator set
func (k Keeper) retrieveLastValidatorSet(ctx sdk.Context) validatorsByAddr {
last := make(validatorsByAddr)
store := ctx.KVStore(k.storeKey)
iterator := sdk.KVStorePrefixIterator(store, ValidatorsBondedIndexKey)
for ; iterator.Valid(); iterator.Next() {
var operator [sdk.AddrLen]byte
copy(operator[:], iterator.Key()[1:])
powerBytes := iterator.Value()
last[operator] = make([]byte, len(powerBytes))
copy(last[operator][:], powerBytes[:])
}
return last
}
// given a map of remaining validators to previous bonded power
// returns the list of validators to be unbonded, sorted by operator address
func (k Keeper) sortNoLongerBonded(last validatorsByAddr) [][]byte {
// sort the map keys for determinism
noLongerBonded := make([][]byte, len(last))
index := 0
for operatorBytes := range last {
operator := make([]byte, sdk.AddrLen)
copy(operator[:], operatorBytes[:])
noLongerBonded[index] = operator
index++
}
// sorted by address - order doesn't matter
sort.SliceStable(noLongerBonded, func(i, j int) bool {
return bytes.Compare(noLongerBonded[i], noLongerBonded[j]) == -1
})
return noLongerBonded
}

View File

@ -1,13 +1,9 @@
package keeper
import (
"bytes"
"container/list"
"fmt"
abci "github.com/tendermint/tendermint/abci/types"
tmtypes "github.com/tendermint/tendermint/types"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/stake/types"
)
@ -58,6 +54,14 @@ func (k Keeper) GetValidator(ctx sdk.Context, addr sdk.ValAddress) (validator ty
return validator, true
}
func (k Keeper) mustGetValidator(ctx sdk.Context, addr sdk.ValAddress) types.Validator {
validator, found := k.GetValidator(ctx, addr)
if !found {
panic(fmt.Sprintf("validator record not found for address: %X\n", addr))
}
return validator
}
// get a single validator by consensus address
func (k Keeper) GetValidatorByConsAddr(ctx sdk.Context, consAddr sdk.ConsAddress) (validator types.Validator, found bool) {
store := ctx.KVStore(k.storeKey)
@ -68,6 +72,16 @@ func (k Keeper) GetValidatorByConsAddr(ctx sdk.Context, consAddr sdk.ConsAddress
return k.GetValidator(ctx, opAddr)
}
func (k Keeper) mustGetValidatorByConsAddr(ctx sdk.Context, consAddr sdk.ConsAddress) types.Validator {
validator, found := k.GetValidatorByConsAddr(ctx, consAddr)
if !found {
panic(fmt.Errorf("validator with consensus-Address %s not found", consAddr))
}
return validator
}
//___________________________________________________________________________
// set the main record holding validator details
func (k Keeper) SetValidator(ctx sdk.Context, validator types.Validator) {
store := ctx.KVStore(k.storeKey)
@ -84,23 +98,110 @@ func (k Keeper) SetValidatorByConsAddr(ctx sdk.Context, validator types.Validato
// validator index
func (k Keeper) SetValidatorByPowerIndex(ctx sdk.Context, validator types.Validator, pool types.Pool) {
// jailed validators are not kept in the power index
if validator.Jailed {
return
}
store := ctx.KVStore(k.storeKey)
store.Set(GetValidatorsByPowerIndexKey(validator, pool), validator.OperatorAddr)
}
// validator index
func (k Keeper) SetValidatorBondedIndex(ctx sdk.Context, validator types.Validator) {
func (k Keeper) DeleteValidatorByPowerIndex(ctx sdk.Context, validator types.Validator, pool types.Pool) {
store := ctx.KVStore(k.storeKey)
store.Set(GetValidatorsBondedIndexKey(validator.OperatorAddr), []byte{})
store.Delete(GetValidatorsByPowerIndexKey(validator, pool))
}
// used in testing
func (k Keeper) validatorByPowerIndexExists(ctx sdk.Context, power []byte) bool {
// validator index
func (k Keeper) SetNewValidatorByPowerIndex(ctx sdk.Context, validator types.Validator) {
store := ctx.KVStore(k.storeKey)
return store.Get(power) != nil
pool := k.GetPool(ctx)
store.Set(GetValidatorsByPowerIndexKey(validator, pool), validator.OperatorAddr)
}
// Get the set of all validators with no limits, used during genesis dump
//___________________________________________________________________________
// Update the tokens of an existing validator, update the validators power index key
func (k Keeper) AddValidatorTokensAndShares(ctx sdk.Context, validator types.Validator,
tokensToAdd sdk.Int) (valOut types.Validator, addedShares sdk.Dec) {
pool := k.GetPool(ctx)
k.DeleteValidatorByPowerIndex(ctx, validator, pool)
validator, pool, addedShares = validator.AddTokensFromDel(pool, tokensToAdd)
// increment the intra-tx counter
// in case of a conflict, the validator which least recently changed power takes precedence
counter := k.GetIntraTxCounter(ctx)
validator.BondIntraTxCounter = counter
k.SetIntraTxCounter(ctx, counter+1)
k.SetValidator(ctx, validator)
k.SetPool(ctx, pool)
k.SetValidatorByPowerIndex(ctx, validator, pool)
return validator, addedShares
}
// Update the tokens of an existing validator, update the validators power index key
func (k Keeper) RemoveValidatorTokensAndShares(ctx sdk.Context, validator types.Validator,
sharesToRemove sdk.Dec) (valOut types.Validator, removedTokens sdk.Dec) {
pool := k.GetPool(ctx)
k.DeleteValidatorByPowerIndex(ctx, validator, pool)
validator, pool, removedTokens = validator.RemoveDelShares(pool, sharesToRemove)
k.SetValidator(ctx, validator)
k.SetPool(ctx, pool)
k.SetValidatorByPowerIndex(ctx, validator, pool)
return validator, removedTokens
}
// Update the tokens of an existing validator, update the validators power index key
func (k Keeper) RemoveValidatorTokens(ctx sdk.Context, validator types.Validator, tokensToRemove sdk.Dec) types.Validator {
pool := k.GetPool(ctx)
k.DeleteValidatorByPowerIndex(ctx, validator, pool)
validator, pool = validator.RemoveTokens(pool, tokensToRemove)
k.SetValidator(ctx, validator)
k.SetPool(ctx, pool)
k.SetValidatorByPowerIndex(ctx, validator, pool)
return validator
}
// UpdateValidatorCommission attempts to update a validator's commission rate.
// An error is returned if the new commission rate is invalid.
func (k Keeper) UpdateValidatorCommission(ctx sdk.Context, validator types.Validator, newRate sdk.Dec) (types.Commission, sdk.Error) {
commission := validator.Commission
blockTime := ctx.BlockHeader().Time
if err := commission.ValidateNewRate(newRate, blockTime); err != nil {
return commission, err
}
commission.Rate = newRate
commission.UpdateTime = blockTime
return commission, nil
}
// remove the validator record and associated indexes
// except for the bonded validator index which is only handled in ApplyAndReturnTendermintUpdates
func (k Keeper) RemoveValidator(ctx sdk.Context, address sdk.ValAddress) {
// first retrieve the old validator record
validator, found := k.GetValidator(ctx, address)
if !found {
return
}
// delete the old validator record
store := ctx.KVStore(k.storeKey)
pool := k.GetPool(ctx)
store.Delete(GetValidatorKey(address))
store.Delete(GetValidatorByConsAddrKey(sdk.ConsAddress(validator.ConsPubKey.Address())))
store.Delete(GetValidatorsByPowerIndexKey(validator, pool))
}
//___________________________________________________________________________
// get groups of validators
// get the set of all validators with no limits, used during genesis dump
func (k Keeper) GetAllValidators(ctx sdk.Context) (validators []types.Validator) {
store := ctx.KVStore(k.storeKey)
iterator := sdk.KVStorePrefixIterator(store, ValidatorsKey)
@ -132,8 +233,6 @@ func (k Keeper) GetValidators(ctx sdk.Context, maxRetrieve uint16) (validators [
return validators[:i] // trim if the array length < maxRetrieve
}
//___________________________________________________________________________
// get the group of the bonded validators
func (k Keeper) GetValidatorsBonded(ctx sdk.Context) (validators []types.Validator) {
store := ctx.KVStore(k.storeKey)
@ -153,8 +252,7 @@ func (k Keeper) GetValidatorsBonded(ctx sdk.Context) (validators []types.Validat
panic("maxValidators is less than the number of records in ValidatorsBonded Store, store should have been updated")
}
address := GetAddressFromValBondedIndexKey(iterator.Key())
validator, found := k.GetValidator(ctx, address)
ensureValidatorFound(found, address)
validator := k.mustGetValidator(ctx, address)
validators[i] = validator
i++
@ -163,9 +261,7 @@ func (k Keeper) GetValidatorsBonded(ctx sdk.Context) (validators []types.Validat
}
// get the group of bonded validators sorted by power-rank
//
// TODO: Rename to GetBondedValidatorsByPower or GetValidatorsByPower(ctx, status)
func (k Keeper) GetValidatorsByPower(ctx sdk.Context) []types.Validator {
func (k Keeper) GetBondedValidatorsByPower(ctx sdk.Context) []types.Validator {
store := ctx.KVStore(k.storeKey)
maxValidators := k.GetParams(ctx).MaxValidators
validators := make([]types.Validator, maxValidators)
@ -176,8 +272,7 @@ func (k Keeper) GetValidatorsByPower(ctx sdk.Context) []types.Validator {
i := 0
for ; iterator.Valid() && i < int(maxValidators); iterator.Next() {
address := iterator.Value()
validator, found := k.GetValidator(ctx, address)
ensureValidatorFound(found, address)
validator := k.mustGetValidator(ctx, address)
if validator.Status == sdk.Bonded {
validators[i] = validator
@ -186,554 +281,3 @@ func (k Keeper) GetValidatorsByPower(ctx sdk.Context) []types.Validator {
}
return validators[:i] // trim
}
//_________________________________________________________________________
// Accumulated updates to the active/bonded validator set for tendermint
// get the most recently updated validators
//
// CONTRACT: Only validators with non-zero power or zero-power that were bonded
// at the previous block height or were removed from the validator set entirely
// are returned to Tendermint.
func (k Keeper) GetTendermintUpdates(ctx sdk.Context) (updates []abci.ValidatorUpdate) {
tstore := ctx.TransientStore(k.storeTKey)
iterator := sdk.KVStorePrefixIterator(tstore, TendermintUpdatesTKey)
defer iterator.Close()
for ; iterator.Valid(); iterator.Next() {
var abciVal abci.ValidatorUpdate
abciValBytes := iterator.Value()
k.cdc.MustUnmarshalBinary(abciValBytes, &abciVal)
pub, err := tmtypes.PB2TM.PubKey(abciVal.GetPubKey())
if err != nil {
panic(err)
}
val, found := k.GetValidator(ctx, sdk.ValAddress(pub.Address()))
if found {
// The validator is new or already exists in the store and must adhere to
// Tendermint invariants.
prevBonded := val.BondHeight < ctx.BlockHeight() && val.BondHeight > val.UnbondingHeight
zeroPower := val.GetPower().Equal(sdk.ZeroDec())
if !zeroPower || zeroPower && prevBonded {
updates = append(updates, abciVal)
}
} else {
// Add the ABCI validator in such a case where the validator was removed
// from the store as it must have existed before.
updates = append(updates, abciVal)
}
}
return
}
//___________________________________________________________________________
// Perform all the necessary steps for when a validator changes its power. This
// function updates all validator stores as well as tendermint update store.
// It may kick out validators if a new validator is entering the bonded validator
// group.
//
// TODO: Remove above nolint, function needs to be simplified!
func (k Keeper) UpdateValidator(ctx sdk.Context, validator types.Validator) types.Validator {
tstore := ctx.TransientStore(k.storeTKey)
pool := k.GetPool(ctx)
oldValidator, oldFound := k.GetValidator(ctx, validator.OperatorAddr)
validator = k.updateForJailing(ctx, oldFound, oldValidator, validator)
powerIncreasing := k.getPowerIncreasing(ctx, oldFound, oldValidator, validator)
validator.BondHeight, validator.BondIntraTxCounter = k.bondIncrement(ctx, oldFound, oldValidator)
valPower := k.updateValidatorPower(ctx, oldFound, oldValidator, validator, pool)
cliffPower := k.GetCliffValidatorPower(ctx)
cliffValExists := (cliffPower != nil)
var valPowerLTcliffPower bool
if cliffValExists {
valPowerLTcliffPower = (bytes.Compare(valPower, cliffPower) == -1)
}
switch {
// if the validator is already bonded and the power is increasing, we need
// perform the following:
// a) update Tendermint
// b) check if the cliff validator needs to be updated
case powerIncreasing && !validator.Jailed &&
(oldFound && oldValidator.Status == sdk.Bonded):
bz := k.cdc.MustMarshalBinary(validator.ABCIValidatorUpdate())
tstore.Set(GetTendermintUpdatesTKey(validator.OperatorAddr), bz)
if cliffValExists {
cliffAddr := sdk.ValAddress(k.GetCliffValidator(ctx))
if bytes.Equal(cliffAddr, validator.OperatorAddr) {
k.updateCliffValidator(ctx, validator)
}
}
// if is a new validator and the new power is less than the cliff validator
case cliffValExists && !oldFound && valPowerLTcliffPower:
// skip to completion
// if was unbonded and the new power is less than the cliff validator
case cliffValExists &&
(oldFound && oldValidator.Status == sdk.Unbonded) &&
valPowerLTcliffPower: //(valPower < cliffPower
// skip to completion
default:
// default case - validator was either:
// a) not-bonded and now has power-rank greater than cliff validator
// b) bonded and now has decreased in power
// update the validator set for this validator
updatedVal, updated := k.UpdateBondedValidators(ctx, validator)
if updated {
// the validator has changed bonding status
validator = updatedVal
break
}
// if decreased in power but still bonded, update Tendermint validator
if oldFound && oldValidator.BondedTokens().GT(validator.BondedTokens()) {
bz := k.cdc.MustMarshalBinary(validator.ABCIValidatorUpdate())
tstore.Set(GetTendermintUpdatesTKey(validator.OperatorAddr), bz)
}
}
k.SetValidator(ctx, validator)
return validator
}
// updateCliffValidator determines if the current cliff validator needs to be
// updated or swapped. If the provided affected validator is the current cliff
// validator before it's power was increased, either the cliff power key will
// be updated or if it's power is greater than the next bonded validator by
// power, it'll be swapped.
func (k Keeper) updateCliffValidator(ctx sdk.Context, affectedVal types.Validator) {
var newCliffVal types.Validator
store := ctx.KVStore(k.storeKey)
pool := k.GetPool(ctx)
cliffAddr := sdk.ValAddress(k.GetCliffValidator(ctx))
oldCliffVal, found := k.GetValidator(ctx, cliffAddr)
if !found {
panic(fmt.Sprintf("cliff validator record not found for address: %X\n", cliffAddr))
}
// Create a validator iterator ranging from smallest to largest by power
// starting the current cliff validator's power.
start := GetValidatorsByPowerIndexKey(oldCliffVal, pool)
end := sdk.PrefixEndBytes(ValidatorsByPowerIndexKey)
iterator := store.Iterator(start, end)
if iterator.Valid() {
ownerAddr := iterator.Value()
currVal, found := k.GetValidator(ctx, ownerAddr)
ensureValidatorFound(found, ownerAddr)
if currVal.Status != sdk.Bonded || currVal.Jailed {
panic(fmt.Sprintf("unexpected jailed or unbonded validator for address: %X\n", ownerAddr))
}
newCliffVal = currVal
iterator.Close()
} else {
panic("failed to create valid validator power iterator")
}
affectedValRank := GetValidatorsByPowerIndexKey(affectedVal, pool)
newCliffValRank := GetValidatorsByPowerIndexKey(newCliffVal, pool)
if bytes.Equal(affectedVal.OperatorAddr, newCliffVal.OperatorAddr) {
// The affected validator remains the cliff validator, however, since
// the store does not contain the new power, update the new power rank.
store.Set(ValidatorPowerCliffKey, affectedValRank)
} else if bytes.Compare(affectedValRank, newCliffValRank) > 0 {
// The affected validator no longer remains the cliff validator as it's
// power is greater than the new cliff validator.
k.setCliffValidator(ctx, newCliffVal, pool)
} else {
panic("invariant broken: the cliff validator should change or it should remain the same")
}
}
func (k Keeper) updateForJailing(ctx sdk.Context, oldFound bool, oldValidator, newValidator types.Validator) types.Validator {
if newValidator.Jailed && oldFound && oldValidator.Status == sdk.Bonded {
newValidator = k.beginUnbondingValidator(ctx, newValidator)
// need to also clear the cliff validator spot because the jail has
// opened up a new spot which will be filled when
// updateValidatorsBonded is called
k.clearCliffValidator(ctx)
}
return newValidator
}
// nolint: unparam
func (k Keeper) getPowerIncreasing(ctx sdk.Context, oldFound bool, oldValidator, newValidator types.Validator) bool {
if oldFound && oldValidator.BondedTokens().LT(newValidator.BondedTokens()) {
return true
}
return false
}
// get the bond height and incremented intra-tx counter
// nolint: unparam
func (k Keeper) bondIncrement(
ctx sdk.Context, found bool, oldValidator types.Validator) (height int64, intraTxCounter int16) {
// if already a validator, copy the old block height and counter
if found && oldValidator.Status == sdk.Bonded {
height = oldValidator.BondHeight
intraTxCounter = oldValidator.BondIntraTxCounter
return
}
height = ctx.BlockHeight()
counter := k.GetIntraTxCounter(ctx)
intraTxCounter = counter
k.SetIntraTxCounter(ctx, counter+1)
return
}
func (k Keeper) updateValidatorPower(ctx sdk.Context, oldFound bool, oldValidator,
newValidator types.Validator, pool types.Pool) (valPower []byte) {
store := ctx.KVStore(k.storeKey)
// update the list ordered by voting power
if oldFound {
store.Delete(GetValidatorsByPowerIndexKey(oldValidator, pool))
}
valPower = GetValidatorsByPowerIndexKey(newValidator, pool)
store.Set(valPower, newValidator.OperatorAddr)
return valPower
}
// Update the bonded validator group based on a change to the validator
// affectedValidator. This function potentially adds the affectedValidator to
// the bonded validator group which kicks out the cliff validator. Under this
// situation this function returns the updated affectedValidator.
//
// The correct bonded subset of validators is retrieved by iterating through an
// index of the validators sorted by power, stored using the
// ValidatorsByPowerIndexKey. Simultaneously the current validator records are
// updated in store with the ValidatorsBondedIndexKey. This store is used to
// determine if a validator is a validator without needing to iterate over all
// validators.
func (k Keeper) UpdateBondedValidators(
ctx sdk.Context, affectedValidator types.Validator) (
updatedVal types.Validator, updated bool) {
store := ctx.KVStore(k.storeKey)
oldCliffValidatorAddr := k.GetCliffValidator(ctx)
maxValidators := k.GetParams(ctx).MaxValidators
bondedValidatorsCount := 0
var validator, validatorToBond types.Validator
newValidatorBonded := false
// create a validator iterator ranging from largest to smallest by power
iterator := sdk.KVStoreReversePrefixIterator(store, ValidatorsByPowerIndexKey)
for ; iterator.Valid() && bondedValidatorsCount < int(maxValidators); iterator.Next() {
// either retrieve the original validator from the store, or under the
// situation that this is the "affected validator" just use the
// validator provided because it has not yet been updated in the store
ownerAddr := iterator.Value()
if bytes.Equal(ownerAddr, affectedValidator.OperatorAddr) {
validator = affectedValidator
} else {
var found bool
validator, found = k.GetValidator(ctx, ownerAddr)
ensureValidatorFound(found, ownerAddr)
}
// if we've reached jailed validators no further bonded validators exist
if validator.Jailed {
if validator.Status == sdk.Bonded {
panic(fmt.Sprintf("jailed validator cannot be bonded, address: %X\n", ownerAddr))
}
break
}
// increment the total number of bonded validators and potentially mark
// the validator to bond
if validator.Status != sdk.Bonded {
validatorToBond = validator
if newValidatorBonded {
panic("already decided to bond a validator, can't bond another!")
}
newValidatorBonded = true
}
bondedValidatorsCount++
}
iterator.Close()
if newValidatorBonded && bytes.Equal(oldCliffValidatorAddr, validator.OperatorAddr) {
panic("cliff validator has not been changed, yet we bonded a new validator")
}
// clear or set the cliff validator
if bondedValidatorsCount == int(maxValidators) {
k.setCliffValidator(ctx, validator, k.GetPool(ctx))
} else if len(oldCliffValidatorAddr) > 0 {
k.clearCliffValidator(ctx)
}
// swap the cliff validator for a new validator if the affected validator
// was bonded
if newValidatorBonded {
if oldCliffValidatorAddr != nil {
oldCliffVal, found := k.GetValidator(ctx, oldCliffValidatorAddr)
ensureValidatorFound(found, oldCliffValidatorAddr)
if bytes.Equal(validatorToBond.OperatorAddr, affectedValidator.OperatorAddr) {
// begin unbonding the old cliff validator iff the affected
// validator was newly bonded and has greater power
k.beginUnbondingValidator(ctx, oldCliffVal)
} else {
// otherwise begin unbonding the affected validator, which must
// have been kicked out
affectedValidator = k.beginUnbondingValidator(ctx, affectedValidator)
}
}
validator = k.bondValidator(ctx, validatorToBond)
if bytes.Equal(validator.OperatorAddr, affectedValidator.OperatorAddr) {
return validator, true
}
return affectedValidator, true
}
return types.Validator{}, false
}
// full update of the bonded validator set, many can be added/kicked
func (k Keeper) UpdateBondedValidatorsFull(ctx sdk.Context) {
store := ctx.KVStore(k.storeKey)
// clear the current validators store, add to the ToKickOut temp store
toKickOut := make(map[string]byte)
iterator := sdk.KVStorePrefixIterator(store, ValidatorsBondedIndexKey)
for ; iterator.Valid(); iterator.Next() {
ownerAddr := GetAddressFromValBondedIndexKey(iterator.Key())
toKickOut[string(ownerAddr)] = 0
}
iterator.Close()
var validator types.Validator
oldCliffValidatorAddr := k.GetCliffValidator(ctx)
maxValidators := k.GetParams(ctx).MaxValidators
bondedValidatorsCount := 0
iterator = sdk.KVStoreReversePrefixIterator(store, ValidatorsByPowerIndexKey)
for ; iterator.Valid() && bondedValidatorsCount < int(maxValidators); iterator.Next() {
var found bool
ownerAddr := iterator.Value()
validator, found = k.GetValidator(ctx, ownerAddr)
ensureValidatorFound(found, ownerAddr)
_, found = toKickOut[string(ownerAddr)]
if found {
delete(toKickOut, string(ownerAddr))
} else {
// If the validator wasn't in the toKickOut group it means it wasn't
// previously a validator, therefor update the validator to enter
// the validator group.
validator = k.bondValidator(ctx, validator)
}
if validator.Jailed {
// we should no longer consider jailed validators as they are ranked
// lower than any non-jailed/bonded validators
if validator.Status == sdk.Bonded {
panic(fmt.Sprintf("jailed validator cannot be bonded for address: %s\n", ownerAddr))
}
break
}
bondedValidatorsCount++
}
iterator.Close()
// clear or set the cliff validator
if bondedValidatorsCount == int(maxValidators) {
k.setCliffValidator(ctx, validator, k.GetPool(ctx))
} else if len(oldCliffValidatorAddr) > 0 {
k.clearCliffValidator(ctx)
}
kickOutValidators(k, ctx, toKickOut)
return
}
func kickOutValidators(k Keeper, ctx sdk.Context, toKickOut map[string]byte) {
for key := range toKickOut {
ownerAddr := []byte(key)
validator, found := k.GetValidator(ctx, ownerAddr)
ensureValidatorFound(found, ownerAddr)
k.beginUnbondingValidator(ctx, validator)
}
}
// perform all the store operations for when a validator status becomes unbonded
func (k Keeper) beginUnbondingValidator(ctx sdk.Context, validator types.Validator) types.Validator {
store := ctx.KVStore(k.storeKey)
pool := k.GetPool(ctx)
params := k.GetParams(ctx)
// sanity check
if validator.Status == sdk.Unbonded ||
validator.Status == sdk.Unbonding {
panic(fmt.Sprintf("should not already be unbonded or unbonding, validator: %v\n", validator))
}
// set the status
validator, pool = validator.UpdateStatus(pool, sdk.Unbonding)
k.SetPool(ctx, pool)
validator.UnbondingMinTime = ctx.BlockHeader().Time.Add(params.UnbondingTime)
validator.UnbondingHeight = ctx.BlockHeader().Height
// save the now unbonded validator record
k.SetValidator(ctx, validator)
// add to accumulated changes for tendermint
bzABCI := k.cdc.MustMarshalBinary(validator.ABCIValidatorUpdateZero())
tstore := ctx.TransientStore(k.storeTKey)
tstore.Set(GetTendermintUpdatesTKey(validator.OperatorAddr), bzABCI)
// also remove from the Bonded types.Validators Store
store.Delete(GetValidatorsBondedIndexKey(validator.OperatorAddr))
k.OnValidatorBeginUnbonding(ctx, validator.ConsAddress())
return validator
}
// perform all the store operations for when a validator status becomes bonded
func (k Keeper) bondValidator(ctx sdk.Context, validator types.Validator) types.Validator {
store := ctx.KVStore(k.storeKey)
pool := k.GetPool(ctx)
// sanity check
if validator.Status == sdk.Bonded {
panic(fmt.Sprintf("should not already be bonded, validator: %v\n", validator))
}
validator.BondHeight = ctx.BlockHeight()
// set the status
validator, pool = validator.UpdateStatus(pool, sdk.Bonded)
k.SetPool(ctx, pool)
// save the now bonded validator record to the three referenced stores
k.SetValidator(ctx, validator)
store.Set(GetValidatorsBondedIndexKey(validator.OperatorAddr), []byte{})
// add to accumulated changes for tendermint
bzABCI := k.cdc.MustMarshalBinary(validator.ABCIValidatorUpdate())
tstore := ctx.TransientStore(k.storeTKey)
tstore.Set(GetTendermintUpdatesTKey(validator.OperatorAddr), bzABCI)
k.OnValidatorBonded(ctx, validator.ConsAddress())
return validator
}
// remove the validator record and associated indexes
func (k Keeper) RemoveValidator(ctx sdk.Context, address sdk.ValAddress) {
k.OnValidatorRemoved(ctx, address)
// first retrieve the old validator record
validator, found := k.GetValidator(ctx, address)
if !found {
return
}
// delete the old validator record
store := ctx.KVStore(k.storeKey)
pool := k.GetPool(ctx)
store.Delete(GetValidatorKey(address))
store.Delete(GetValidatorByConsAddrKey(sdk.ConsAddress(validator.ConsPubKey.Address())))
store.Delete(GetValidatorsByPowerIndexKey(validator, pool))
// delete from the current and power weighted validator groups if the validator
// is bonded - and add validator with zero power to the validator updates
if store.Get(GetValidatorsBondedIndexKey(validator.OperatorAddr)) == nil {
return
}
store.Delete(GetValidatorsBondedIndexKey(validator.OperatorAddr))
bz := k.cdc.MustMarshalBinary(validator.ABCIValidatorUpdateZero())
tstore := ctx.TransientStore(k.storeTKey)
tstore.Set(GetTendermintUpdatesTKey(address), bz)
}
// UpdateValidatorCommission attempts to update a validator's commission rate.
// An error is returned if the new commission rate is invalid.
func (k Keeper) UpdateValidatorCommission(ctx sdk.Context, validator types.Validator, newRate sdk.Dec) sdk.Error {
commission := validator.Commission
blockTime := ctx.BlockHeader().Time
if err := commission.ValidateNewRate(newRate, blockTime); err != nil {
return err
}
validator.Commission.Rate = newRate
validator.Commission.UpdateTime = blockTime
k.SetValidator(ctx, validator)
k.OnValidatorCommissionChange(ctx, validator.OperatorAddr)
return nil
}
//__________________________________________________________________________
// get the current validator on the cliff
func (k Keeper) GetCliffValidator(ctx sdk.Context) []byte {
store := ctx.KVStore(k.storeKey)
return store.Get(ValidatorCliffIndexKey)
}
// get the current power of the validator on the cliff
func (k Keeper) GetCliffValidatorPower(ctx sdk.Context) []byte {
store := ctx.KVStore(k.storeKey)
return store.Get(ValidatorPowerCliffKey)
}
// set the current validator and power of the validator on the cliff
func (k Keeper) setCliffValidator(ctx sdk.Context, validator types.Validator, pool types.Pool) {
store := ctx.KVStore(k.storeKey)
bz := GetValidatorsByPowerIndexKey(validator, pool)
store.Set(ValidatorPowerCliffKey, bz)
store.Set(ValidatorCliffIndexKey, validator.OperatorAddr)
}
// clear the current validator and power of the validator on the cliff
func (k Keeper) clearCliffValidator(ctx sdk.Context) {
store := ctx.KVStore(k.storeKey)
store.Delete(ValidatorPowerCliffKey)
store.Delete(ValidatorCliffIndexKey)
}
func ensureValidatorFound(found bool, ownerAddr []byte) {
if !found {
panic(fmt.Sprintf("validator record not found for address: %X\n", ownerAddr))
}
}

View File

@ -13,18 +13,6 @@ import (
"github.com/stretchr/testify/require"
)
// for testing, remove all validator update entries after applied to Tendermint
func clearTendermintUpdates(ctx sdk.Context, k Keeper) {
store := ctx.TransientStore(k.storeTKey)
// delete subspace
iterator := sdk.KVStorePrefixIterator(store, TendermintUpdatesTKey)
for ; iterator.Valid(); iterator.Next() {
store.Delete(iterator.Key())
}
iterator.Close()
}
//_______________________________________________________
func TestSetValidator(t *testing.T) {
@ -41,11 +29,17 @@ func TestSetValidator(t *testing.T) {
assert.True(sdk.DecEq(t, sdk.NewDec(10), validator.Tokens))
assert.True(sdk.DecEq(t, sdk.NewDec(10), validator.DelegatorShares))
keeper.SetPool(ctx, pool)
keeper.UpdateValidator(ctx, validator)
keeper.SetValidator(ctx, validator)
keeper.SetValidatorByPowerIndex(ctx, validator, pool)
// after the save the validator should be bonded
// ensure update
updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx)
validator, found := keeper.GetValidator(ctx, valAddr)
require.True(t, found)
require.Equal(t, 1, len(updates))
require.Equal(t, validator.ABCIValidatorUpdate(), updates[0])
// after the save the validator should be bonded
require.Equal(t, sdk.Bonded, validator.Status)
assert.True(sdk.DecEq(t, sdk.NewDec(10), validator.Tokens))
assert.True(sdk.DecEq(t, sdk.NewDec(10), validator.DelegatorShares))
@ -59,7 +53,7 @@ func TestSetValidator(t *testing.T) {
require.Equal(t, 1, len(resVals))
assert.True(ValEq(t, validator, resVals[0]))
resVals = keeper.GetValidatorsByPower(ctx)
resVals = keeper.GetBondedValidatorsByPower(ctx)
require.Equal(t, 1, len(resVals))
require.True(ValEq(t, validator, resVals[0]))
@ -71,10 +65,6 @@ func TestSetValidator(t *testing.T) {
require.Equal(t, 1, len(resVals))
require.True(ValEq(t, validator, resVals[0]))
updates := keeper.GetTendermintUpdates(ctx)
require.Equal(t, 1, len(updates))
require.Equal(t, validator.ABCIValidatorUpdate(), updates[0])
allVals := keeper.GetAllValidators(ctx)
require.Equal(t, 1, len(allVals))
}
@ -94,27 +84,28 @@ func TestUpdateValidatorByPowerIndex(t *testing.T) {
require.Equal(t, sdk.Unbonded, validator.Status)
require.Equal(t, int64(100), validator.Tokens.RoundInt64())
keeper.SetPool(ctx, pool)
keeper.UpdateValidator(ctx, validator)
testingUpdateValidator(keeper, ctx, validator)
validator, found := keeper.GetValidator(ctx, addrVals[0])
require.True(t, found)
require.Equal(t, int64(100), validator.Tokens.RoundInt64(), "\nvalidator %v\npool %v", validator, pool)
pool = keeper.GetPool(ctx)
power := GetValidatorsByPowerIndexKey(validator, pool)
require.True(t, keeper.validatorByPowerIndexExists(ctx, power))
require.True(t, validatorByPowerIndexExists(keeper, ctx, power))
// burn half the delegator shares
keeper.DeleteValidatorByPowerIndex(ctx, validator, pool)
validator, pool, burned := validator.RemoveDelShares(pool, delSharesCreated.Quo(sdk.NewDec(2)))
require.Equal(t, int64(50), burned.RoundInt64())
keeper.SetPool(ctx, pool) // update the pool
keeper.UpdateValidator(ctx, validator) // update the validator, possibly kicking it out
require.False(t, keeper.validatorByPowerIndexExists(ctx, power))
keeper.SetPool(ctx, pool) // update the pool
testingUpdateValidator(keeper, ctx, validator) // update the validator, possibly kicking it out
require.False(t, validatorByPowerIndexExists(keeper, ctx, power))
pool = keeper.GetPool(ctx)
validator, found = keeper.GetValidator(ctx, addrVals[0])
require.True(t, found)
power = GetValidatorsByPowerIndexKey(validator, pool)
require.True(t, keeper.validatorByPowerIndexExists(ctx, power))
require.True(t, validatorByPowerIndexExists(keeper, ctx, power))
}
func TestUpdateBondedValidatorsDecreaseCliff(t *testing.T) {
@ -144,7 +135,7 @@ func TestUpdateBondedValidatorsDecreaseCliff(t *testing.T) {
val, pool, _ = val.AddTokensFromDel(pool, sdk.NewInt(int64((i+1)*10)))
keeper.SetPool(ctx, pool)
val = keeper.UpdateValidator(ctx, val)
val = testingUpdateValidator(keeper, ctx, val)
validators[i] = val
}
@ -152,17 +143,10 @@ func TestUpdateBondedValidatorsDecreaseCliff(t *testing.T) {
// remove enough tokens to kick out the validator below the current cliff
// validator and next in line cliff validator
keeper.DeleteValidatorByPowerIndex(ctx, nextCliffVal, pool)
nextCliffVal, pool, _ = nextCliffVal.RemoveDelShares(pool, sdk.NewDec(21))
keeper.SetPool(ctx, pool)
nextCliffVal = keeper.UpdateValidator(ctx, nextCliffVal)
// require the cliff validator has changed
cliffVal := validators[numVals-maxVals-1]
require.Equal(t, cliffVal.OperatorAddr, sdk.ValAddress(keeper.GetCliffValidator(ctx)))
// require the cliff validator power has changed
cliffPower := keeper.GetCliffValidatorPower(ctx)
require.Equal(t, GetValidatorsByPowerIndexKey(cliffVal, pool), cliffPower)
nextCliffVal = testingUpdateValidator(keeper, ctx, nextCliffVal)
expectedValStatus := map[int]sdk.BondStatus{
9: sdk.Bonded, 8: sdk.Bonded, 7: sdk.Bonded, 5: sdk.Bonded, 4: sdk.Bonded,
@ -182,75 +166,6 @@ func TestUpdateBondedValidatorsDecreaseCliff(t *testing.T) {
}
}
func TestCliffValidatorChange(t *testing.T) {
numVals := 10
maxVals := 5
// create context, keeper, and pool for tests
ctx, _, keeper := CreateTestInput(t, false, 0)
pool := keeper.GetPool(ctx)
// create keeper parameters
params := keeper.GetParams(ctx)
params.MaxValidators = uint16(maxVals)
keeper.SetParams(ctx, params)
// create a random pool
pool.LooseTokens = sdk.NewDec(10000)
pool.BondedTokens = sdk.NewDec(1234)
keeper.SetPool(ctx, pool)
validators := make([]types.Validator, numVals)
for i := 0; i < len(validators); i++ {
moniker := fmt.Sprintf("val#%d", int64(i))
val := types.NewValidator(sdk.ValAddress(Addrs[i]), PKs[i], types.Description{Moniker: moniker})
val.BondHeight = int64(i)
val.BondIntraTxCounter = int16(i)
val, pool, _ = val.AddTokensFromDel(pool, sdk.NewInt(int64((i+1)*10)))
keeper.SetPool(ctx, pool)
val = keeper.UpdateValidator(ctx, val)
validators[i] = val
}
// add a large amount of tokens to current cliff validator
currCliffVal := validators[numVals-maxVals]
currCliffVal, pool, _ = currCliffVal.AddTokensFromDel(pool, sdk.NewInt(200))
keeper.SetPool(ctx, pool)
currCliffVal = keeper.UpdateValidator(ctx, currCliffVal)
// assert new cliff validator to be set to the second lowest bonded validator by power
newCliffVal := validators[numVals-maxVals+1]
require.Equal(t, newCliffVal.OperatorAddr, sdk.ValAddress(keeper.GetCliffValidator(ctx)))
// assert cliff validator power should have been updated
cliffPower := keeper.GetCliffValidatorPower(ctx)
require.Equal(t, GetValidatorsByPowerIndexKey(newCliffVal, pool), cliffPower)
// add small amount of tokens to new current cliff validator
newCliffVal, pool, _ = newCliffVal.AddTokensFromDel(pool, sdk.NewInt(1))
keeper.SetPool(ctx, pool)
newCliffVal = keeper.UpdateValidator(ctx, newCliffVal)
// assert cliff validator has not change but increased in power
cliffPower = keeper.GetCliffValidatorPower(ctx)
require.Equal(t, newCliffVal.OperatorAddr, sdk.ValAddress(keeper.GetCliffValidator(ctx)))
require.Equal(t, GetValidatorsByPowerIndexKey(newCliffVal, pool), cliffPower)
// add enough power to cliff validator to be equal in rank to next validator
newCliffVal, pool, _ = newCliffVal.AddTokensFromDel(pool, sdk.NewInt(9))
keeper.SetPool(ctx, pool)
newCliffVal = keeper.UpdateValidator(ctx, newCliffVal)
// assert new cliff validator due to power rank construction
newCliffVal = validators[numVals-maxVals+2]
require.Equal(t, newCliffVal.OperatorAddr, sdk.ValAddress(keeper.GetCliffValidator(ctx)))
// assert cliff validator power should have been updated
cliffPower = keeper.GetCliffValidatorPower(ctx)
require.Equal(t, GetValidatorsByPowerIndexKey(newCliffVal, pool), cliffPower)
}
func TestSlashToZeroPowerRemoved(t *testing.T) {
// initialize setup
ctx, _, keeper := CreateTestInput(t, false, 100)
@ -263,12 +178,14 @@ func TestSlashToZeroPowerRemoved(t *testing.T) {
require.Equal(t, int64(100), validator.Tokens.RoundInt64())
keeper.SetPool(ctx, pool)
keeper.SetValidatorByConsAddr(ctx, validator)
validator = keeper.UpdateValidator(ctx, validator)
validator = testingUpdateValidator(keeper, ctx, validator)
require.Equal(t, int64(100), validator.Tokens.RoundInt64(), "\nvalidator %v\npool %v", validator, pool)
// slash the validator by 100%
consAddr0 := sdk.ConsAddress(PKs[0].Address())
keeper.Slash(ctx, consAddr0, 0, 100, sdk.OneDec())
// apply TM updates
keeper.ApplyAndReturnValidatorSetUpdates(ctx)
// validator should have been deleted
_, found := keeper.GetValidator(ctx, addrVals[0])
require.False(t, found)
@ -306,7 +223,7 @@ func TestValidatorBasics(t *testing.T) {
assert.True(sdk.DecEq(t, sdk.ZeroDec(), pool.BondedTokens))
// set and retrieve a record
validators[0] = keeper.UpdateValidator(ctx, validators[0])
validators[0] = testingUpdateValidator(keeper, ctx, validators[0])
keeper.SetValidatorByConsAddr(ctx, validators[0])
resVal, found := keeper.GetValidator(ctx, addrVals[0])
require.True(t, found)
@ -333,7 +250,7 @@ func TestValidatorBasics(t *testing.T) {
validators[0].Status = sdk.Bonded
validators[0].Tokens = sdk.NewDec(10)
validators[0].DelegatorShares = sdk.NewDec(10)
validators[0] = keeper.UpdateValidator(ctx, validators[0])
validators[0] = testingUpdateValidator(keeper, ctx, validators[0])
resVal, found = keeper.GetValidator(ctx, addrVals[0])
require.True(t, found)
assert.True(ValEq(t, validators[0], resVal))
@ -343,8 +260,8 @@ func TestValidatorBasics(t *testing.T) {
assert.True(ValEq(t, validators[0], resVals[0]))
// add other validators
validators[1] = keeper.UpdateValidator(ctx, validators[1])
validators[2] = keeper.UpdateValidator(ctx, validators[2])
validators[1] = testingUpdateValidator(keeper, ctx, validators[1])
validators[2] = testingUpdateValidator(keeper, ctx, validators[2])
resVal, found = keeper.GetValidator(ctx, addrVals[1])
require.True(t, found)
assert.True(ValEq(t, validators[1], resVal))
@ -364,7 +281,7 @@ func TestValidatorBasics(t *testing.T) {
require.False(t, found)
}
// test how the validators are sorted, tests GetValidatorsByPower
// test how the validators are sorted, tests GetBondedValidatorsByPower
func GetValidatorSortingUnmixed(t *testing.T) {
ctx, _, keeper := CreateTestInput(t, false, 1000)
@ -377,11 +294,11 @@ func GetValidatorSortingUnmixed(t *testing.T) {
validators[i].Status = sdk.Bonded
validators[i].Tokens = sdk.NewDec(amt)
validators[i].DelegatorShares = sdk.NewDec(amt)
keeper.UpdateValidator(ctx, validators[i])
testingUpdateValidator(keeper, ctx, validators[i])
}
// first make sure everything made it in to the gotValidator group
resValidators := keeper.GetValidatorsByPower(ctx)
resValidators := keeper.GetBondedValidatorsByPower(ctx)
assert.Equal(t, n, len(resValidators))
assert.Equal(t, sdk.NewDec(400), resValidators[0].BondedTokens(), "%v", resValidators)
assert.Equal(t, sdk.NewDec(200), resValidators[1].BondedTokens(), "%v", resValidators)
@ -396,15 +313,15 @@ func GetValidatorSortingUnmixed(t *testing.T) {
// test a basic increase in voting power
validators[3].Tokens = sdk.NewDec(500)
keeper.UpdateValidator(ctx, validators[3])
resValidators = keeper.GetValidatorsByPower(ctx)
testingUpdateValidator(keeper, ctx, validators[3])
resValidators = keeper.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.NewDec(300)
keeper.UpdateValidator(ctx, validators[3])
resValidators = keeper.GetValidatorsByPower(ctx)
testingUpdateValidator(keeper, ctx, validators[3])
resValidators = keeper.GetBondedValidatorsByPower(ctx)
require.Equal(t, len(resValidators), n)
assert.True(ValEq(t, validators[3], resValidators[0]))
assert.True(ValEq(t, validators[4], resValidators[1]))
@ -412,8 +329,8 @@ func GetValidatorSortingUnmixed(t *testing.T) {
// test equal voting power, different age
validators[3].Tokens = sdk.NewDec(200)
ctx = ctx.WithBlockHeight(10)
keeper.UpdateValidator(ctx, validators[3])
resValidators = keeper.GetValidatorsByPower(ctx)
testingUpdateValidator(keeper, ctx, validators[3])
resValidators = keeper.GetBondedValidatorsByPower(ctx)
require.Equal(t, len(resValidators), n)
assert.True(ValEq(t, validators[3], resValidators[0]))
assert.True(ValEq(t, validators[4], resValidators[1]))
@ -422,8 +339,8 @@ func GetValidatorSortingUnmixed(t *testing.T) {
// no change in voting power - no change in sort
ctx = ctx.WithBlockHeight(20)
keeper.UpdateValidator(ctx, validators[4])
resValidators = keeper.GetValidatorsByPower(ctx)
testingUpdateValidator(keeper, ctx, validators[4])
resValidators = keeper.GetBondedValidatorsByPower(ctx)
require.Equal(t, len(resValidators), n)
assert.True(ValEq(t, validators[3], resValidators[0]))
assert.True(ValEq(t, validators[4], resValidators[1]))
@ -431,12 +348,12 @@ func GetValidatorSortingUnmixed(t *testing.T) {
// change in voting power of both validators, both still in v-set, no age change
validators[3].Tokens = sdk.NewDec(300)
validators[4].Tokens = sdk.NewDec(300)
keeper.UpdateValidator(ctx, validators[3])
resValidators = keeper.GetValidatorsByPower(ctx)
testingUpdateValidator(keeper, ctx, validators[3])
resValidators = keeper.GetBondedValidatorsByPower(ctx)
require.Equal(t, len(resValidators), n)
ctx = ctx.WithBlockHeight(30)
keeper.UpdateValidator(ctx, validators[4])
resValidators = keeper.GetValidatorsByPower(ctx)
testingUpdateValidator(keeper, ctx, validators[4])
resValidators = keeper.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]))
@ -473,7 +390,7 @@ func GetValidatorSortingMixed(t *testing.T) {
validators[4].Tokens = sdk.NewDec(amts[4])
for i := range amts {
keeper.UpdateValidator(ctx, validators[i])
testingUpdateValidator(keeper, ctx, validators[i])
}
val0, found := keeper.GetValidator(ctx, sdk.ValAddress(Addrs[0]))
require.True(t, found)
@ -492,7 +409,7 @@ func GetValidatorSortingMixed(t *testing.T) {
require.Equal(t, sdk.Bonded, val4.Status)
// first make sure everything made it in to the gotValidator group
resValidators := keeper.GetValidatorsByPower(ctx)
resValidators := keeper.GetBondedValidatorsByPower(ctx)
assert.Equal(t, n, len(resValidators))
assert.Equal(t, sdk.NewDec(400), resValidators[0].BondedTokens(), "%v", resValidators)
assert.Equal(t, sdk.NewDec(200), resValidators[1].BondedTokens(), "%v", resValidators)
@ -525,24 +442,26 @@ func TestGetValidatorsEdgeCases(t *testing.T) {
moniker := fmt.Sprintf("val#%d", int64(i))
validators[i] = types.NewValidator(sdk.ValAddress(Addrs[i]), PKs[i], types.Description{Moniker: moniker})
validators[i], pool, _ = validators[i].AddTokensFromDel(pool, sdk.NewInt(amt))
validators[i].BondIntraTxCounter = int16(i)
keeper.SetPool(ctx, pool)
validators[i] = keeper.UpdateValidator(ctx, validators[i])
validators[i] = testingUpdateValidator(keeper, ctx, validators[i])
}
for i := range amts {
validators[i], found = keeper.GetValidator(ctx, validators[i].OperatorAddr)
require.True(t, found)
}
resValidators := keeper.GetValidatorsByPower(ctx)
resValidators := keeper.GetBondedValidatorsByPower(ctx)
require.Equal(t, nMax, uint16(len(resValidators)))
assert.True(ValEq(t, validators[2], resValidators[0]))
assert.True(ValEq(t, validators[3], resValidators[1]))
pool := keeper.GetPool(ctx)
keeper.DeleteValidatorByPowerIndex(ctx, validators[0], pool)
validators[0], pool, _ = validators[0].AddTokensFromDel(pool, sdk.NewInt(500))
keeper.SetPool(ctx, pool)
validators[0] = keeper.UpdateValidator(ctx, validators[0])
resValidators = keeper.GetValidatorsByPower(ctx)
validators[0] = testingUpdateValidator(keeper, ctx, validators[0])
resValidators = keeper.GetBondedValidatorsByPower(ctx)
require.Equal(t, nMax, uint16(len(resValidators)))
assert.True(ValEq(t, validators[0], resValidators[0]))
assert.True(ValEq(t, validators[2], resValidators[1]))
@ -556,28 +475,31 @@ func TestGetValidatorsEdgeCases(t *testing.T) {
validators[3], found = keeper.GetValidator(ctx, validators[3].OperatorAddr)
require.True(t, found)
keeper.DeleteValidatorByPowerIndex(ctx, validators[3], pool)
validators[3], pool, _ = validators[3].AddTokensFromDel(pool, sdk.NewInt(1))
keeper.SetPool(ctx, pool)
validators[3] = keeper.UpdateValidator(ctx, validators[3])
resValidators = keeper.GetValidatorsByPower(ctx)
validators[3] = testingUpdateValidator(keeper, ctx, validators[3])
resValidators = keeper.GetBondedValidatorsByPower(ctx)
require.Equal(t, nMax, uint16(len(resValidators)))
assert.True(ValEq(t, validators[0], resValidators[0]))
assert.True(ValEq(t, validators[3], resValidators[1]))
// validator 3 kicked out temporarily
keeper.DeleteValidatorByPowerIndex(ctx, validators[3], pool)
validators[3], pool, _ = validators[3].RemoveDelShares(pool, sdk.NewDec(201))
keeper.SetPool(ctx, pool)
validators[3] = keeper.UpdateValidator(ctx, validators[3])
resValidators = keeper.GetValidatorsByPower(ctx)
validators[3] = testingUpdateValidator(keeper, ctx, validators[3])
resValidators = keeper.GetBondedValidatorsByPower(ctx)
require.Equal(t, nMax, uint16(len(resValidators)))
assert.True(ValEq(t, validators[0], resValidators[0]))
assert.True(ValEq(t, validators[2], resValidators[1]))
// validator 4 does not get spot back
keeper.DeleteValidatorByPowerIndex(ctx, validators[3], pool)
validators[3], pool, _ = validators[3].AddTokensFromDel(pool, sdk.NewInt(200))
keeper.SetPool(ctx, pool)
validators[3] = keeper.UpdateValidator(ctx, validators[3])
resValidators = keeper.GetValidatorsByPower(ctx)
validators[3] = testingUpdateValidator(keeper, ctx, validators[3])
resValidators = keeper.GetBondedValidatorsByPower(ctx)
require.Equal(t, nMax, uint16(len(resValidators)))
assert.True(ValEq(t, validators[0], resValidators[0]))
assert.True(ValEq(t, validators[2], resValidators[1]))
@ -600,34 +522,39 @@ func TestValidatorBondHeight(t *testing.T) {
validators[0] = types.NewValidator(sdk.ValAddress(Addrs[0]), PKs[0], types.Description{})
validators[1] = types.NewValidator(sdk.ValAddress(Addrs[1]), PKs[1], types.Description{})
validators[2] = types.NewValidator(sdk.ValAddress(Addrs[2]), PKs[2], types.Description{})
validators[0].BondIntraTxCounter = 0
validators[1].BondIntraTxCounter = 1
validators[2].BondIntraTxCounter = 2
validators[0], pool, _ = validators[0].AddTokensFromDel(pool, sdk.NewInt(200))
validators[1], pool, _ = validators[1].AddTokensFromDel(pool, sdk.NewInt(100))
validators[2], pool, _ = validators[2].AddTokensFromDel(pool, sdk.NewInt(100))
keeper.SetPool(ctx, pool)
validators[0] = keeper.UpdateValidator(ctx, validators[0])
validators[0] = testingUpdateValidator(keeper, ctx, validators[0])
////////////////////////////////////////
// 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.UpdateValidator(ctx, validators[1])
validators[2] = keeper.UpdateValidator(ctx, validators[2])
validators[1] = testingUpdateValidator(keeper, ctx, validators[1])
validators[2] = testingUpdateValidator(keeper, ctx, validators[2])
pool = keeper.GetPool(ctx)
resValidators := keeper.GetValidatorsByPower(ctx)
resValidators := keeper.GetBondedValidatorsByPower(ctx)
require.Equal(t, uint16(len(resValidators)), params.MaxValidators)
assert.True(ValEq(t, validators[0], resValidators[0]))
assert.True(ValEq(t, validators[1], resValidators[1]))
keeper.DeleteValidatorByPowerIndex(ctx, validators[1], pool)
keeper.DeleteValidatorByPowerIndex(ctx, validators[2], pool)
validators[1], pool, _ = validators[1].AddTokensFromDel(pool, sdk.NewInt(50))
validators[2], pool, _ = validators[2].AddTokensFromDel(pool, sdk.NewInt(50))
keeper.SetPool(ctx, pool)
validators[2] = keeper.UpdateValidator(ctx, validators[2])
resValidators = keeper.GetValidatorsByPower(ctx)
validators[2] = testingUpdateValidator(keeper, ctx, validators[2])
resValidators = keeper.GetBondedValidatorsByPower(ctx)
require.Equal(t, params.MaxValidators, uint16(len(resValidators)))
validators[1] = keeper.UpdateValidator(ctx, validators[1])
validators[1] = testingUpdateValidator(keeper, ctx, validators[1])
assert.True(ValEq(t, validators[0], resValidators[0]))
assert.True(ValEq(t, validators[2], resValidators[1]))
}
@ -646,20 +573,21 @@ func TestFullValidatorSetPowerChange(t *testing.T) {
pool := keeper.GetPool(ctx)
validators[i] = types.NewValidator(sdk.ValAddress(Addrs[i]), PKs[i], types.Description{})
validators[i], pool, _ = validators[i].AddTokensFromDel(pool, sdk.NewInt(amt))
validators[i].BondIntraTxCounter = int16(i)
keeper.SetPool(ctx, pool)
keeper.UpdateValidator(ctx, validators[i])
testingUpdateValidator(keeper, ctx, validators[i])
}
for i := range amts {
var found bool
validators[i], found = keeper.GetValidator(ctx, validators[i].OperatorAddr)
require.True(t, found)
}
assert.Equal(t, sdk.Unbonding, validators[0].Status)
assert.Equal(t, sdk.Unbonded, validators[0].Status)
assert.Equal(t, sdk.Unbonding, validators[1].Status)
assert.Equal(t, sdk.Bonded, validators[2].Status)
assert.Equal(t, sdk.Bonded, validators[3].Status)
assert.Equal(t, sdk.Unbonded, validators[4].Status)
resValidators := keeper.GetValidatorsByPower(ctx)
resValidators := keeper.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]))
@ -668,14 +596,14 @@ func TestFullValidatorSetPowerChange(t *testing.T) {
pool := keeper.GetPool(ctx)
validators[0], pool, _ = validators[0].AddTokensFromDel(pool, sdk.NewInt(600))
keeper.SetPool(ctx, pool)
validators[0] = keeper.UpdateValidator(ctx, validators[0])
resValidators = keeper.GetValidatorsByPower(ctx)
validators[0] = testingUpdateValidator(keeper, ctx, validators[0])
resValidators = keeper.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 TestGetTendermintUpdatesAllNone(t *testing.T) {
func TestApplyAndReturnValidatorSetUpdatesAllNone(t *testing.T) {
ctx, _, keeper := CreateTestInput(t, false, 1000)
amts := []int64{10, 20}
@ -693,17 +621,22 @@ func TestGetTendermintUpdatesAllNone(t *testing.T) {
// test from nothing to something
// tendermintUpdate set: {} -> {c1, c3}
require.Equal(t, 0, len(keeper.GetTendermintUpdates(ctx)))
validators[0] = keeper.UpdateValidator(ctx, validators[0])
validators[1] = keeper.UpdateValidator(ctx, validators[1])
require.Equal(t, 0, len(keeper.ApplyAndReturnValidatorSetUpdates(ctx)))
pool := keeper.GetPool(ctx)
keeper.SetValidator(ctx, validators[0])
keeper.SetValidatorByPowerIndex(ctx, validators[0], pool)
keeper.SetValidator(ctx, validators[1])
keeper.SetValidatorByPowerIndex(ctx, validators[1], pool)
updates := keeper.GetTendermintUpdates(ctx)
updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx)
assert.Equal(t, 2, len(updates))
assert.Equal(t, validators[0].ABCIValidatorUpdate(), updates[0])
assert.Equal(t, validators[1].ABCIValidatorUpdate(), updates[1])
validators[0], _ = keeper.GetValidator(ctx, validators[0].OperatorAddr)
validators[1], _ = keeper.GetValidator(ctx, validators[1].OperatorAddr)
assert.Equal(t, validators[0].ABCIValidatorUpdate(), updates[1])
assert.Equal(t, validators[1].ABCIValidatorUpdate(), updates[0])
}
func TestGetTendermintUpdatesIdentical(t *testing.T) {
func TestApplyAndReturnValidatorSetUpdatesIdentical(t *testing.T) {
ctx, _, keeper := CreateTestInput(t, false, 1000)
amts := []int64{10, 20}
@ -714,19 +647,18 @@ func TestGetTendermintUpdatesIdentical(t *testing.T) {
validators[i], pool, _ = validators[i].AddTokensFromDel(pool, sdk.NewInt(amt))
keeper.SetPool(ctx, pool)
}
validators[0] = keeper.UpdateValidator(ctx, validators[0])
validators[1] = keeper.UpdateValidator(ctx, validators[1])
clearTendermintUpdates(ctx, keeper)
require.Equal(t, 0, len(keeper.GetTendermintUpdates(ctx)))
validators[0] = testingUpdateValidator(keeper, ctx, validators[0])
validators[1] = testingUpdateValidator(keeper, ctx, validators[1])
require.Equal(t, 0, len(keeper.ApplyAndReturnValidatorSetUpdates(ctx)))
// test identical,
// tendermintUpdate set: {} -> {}
validators[0] = keeper.UpdateValidator(ctx, validators[0])
validators[1] = keeper.UpdateValidator(ctx, validators[1])
require.Equal(t, 0, len(keeper.GetTendermintUpdates(ctx)))
validators[0] = testingUpdateValidator(keeper, ctx, validators[0])
validators[1] = testingUpdateValidator(keeper, ctx, validators[1])
require.Equal(t, 0, len(keeper.ApplyAndReturnValidatorSetUpdates(ctx)))
}
func TestGetTendermintUpdatesSingleValueChange(t *testing.T) {
func TestApplyAndReturnValidatorSetUpdatesSingleValueChange(t *testing.T) {
ctx, _, keeper := CreateTestInput(t, false, 1000)
amts := []int64{10, 20}
@ -737,24 +669,23 @@ func TestGetTendermintUpdatesSingleValueChange(t *testing.T) {
validators[i], pool, _ = validators[i].AddTokensFromDel(pool, sdk.NewInt(amt))
keeper.SetPool(ctx, pool)
}
validators[0] = keeper.UpdateValidator(ctx, validators[0])
validators[1] = keeper.UpdateValidator(ctx, validators[1])
clearTendermintUpdates(ctx, keeper)
require.Equal(t, 0, len(keeper.GetTendermintUpdates(ctx)))
validators[0] = testingUpdateValidator(keeper, ctx, validators[0])
validators[1] = testingUpdateValidator(keeper, ctx, validators[1])
require.Equal(t, 0, len(keeper.ApplyAndReturnValidatorSetUpdates(ctx)))
// test single value change
// tendermintUpdate set: {} -> {c1'}
validators[0].Status = sdk.Bonded
validators[0].Tokens = sdk.NewDec(600)
validators[0] = keeper.UpdateValidator(ctx, validators[0])
validators[0] = testingUpdateValidator(keeper, ctx, validators[0])
updates := keeper.GetTendermintUpdates(ctx)
updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx)
require.Equal(t, 1, len(updates))
require.Equal(t, validators[0].ABCIValidatorUpdate(), updates[0])
}
func TestGetTendermintUpdatesMultipleValueChange(t *testing.T) {
func TestApplyAndReturnValidatorSetUpdatesMultipleValueChange(t *testing.T) {
ctx, _, keeper := CreateTestInput(t, false, 1000)
amts := []int64{10, 20}
@ -765,10 +696,9 @@ func TestGetTendermintUpdatesMultipleValueChange(t *testing.T) {
validators[i], pool, _ = validators[i].AddTokensFromDel(pool, sdk.NewInt(amt))
keeper.SetPool(ctx, pool)
}
validators[0] = keeper.UpdateValidator(ctx, validators[0])
validators[1] = keeper.UpdateValidator(ctx, validators[1])
clearTendermintUpdates(ctx, keeper)
require.Equal(t, 0, len(keeper.GetTendermintUpdates(ctx)))
validators[0] = testingUpdateValidator(keeper, ctx, validators[0])
validators[1] = testingUpdateValidator(keeper, ctx, validators[1])
require.Equal(t, 0, len(keeper.ApplyAndReturnValidatorSetUpdates(ctx)))
// test multiple value change
// tendermintUpdate set: {c1, c3} -> {c1', c3'}
@ -776,16 +706,16 @@ func TestGetTendermintUpdatesMultipleValueChange(t *testing.T) {
validators[0], pool, _ = validators[0].AddTokensFromDel(pool, sdk.NewInt(190))
validators[1], pool, _ = validators[1].AddTokensFromDel(pool, sdk.NewInt(80))
keeper.SetPool(ctx, pool)
validators[0] = keeper.UpdateValidator(ctx, validators[0])
validators[1] = keeper.UpdateValidator(ctx, validators[1])
validators[0] = testingUpdateValidator(keeper, ctx, validators[0])
validators[1] = testingUpdateValidator(keeper, ctx, validators[1])
updates := keeper.GetTendermintUpdates(ctx)
updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx)
require.Equal(t, 2, len(updates))
require.Equal(t, validators[0].ABCIValidatorUpdate(), updates[0])
require.Equal(t, validators[1].ABCIValidatorUpdate(), updates[1])
require.Equal(t, validators[0].ABCIValidatorUpdate(), updates[1])
require.Equal(t, validators[1].ABCIValidatorUpdate(), updates[0])
}
func TestGetTendermintUpdatesInserted(t *testing.T) {
func TestApplyAndReturnValidatorSetUpdatesInserted(t *testing.T) {
ctx, _, keeper := CreateTestInput(t, false, 1000)
amts := []int64{10, 20, 5, 15, 25}
@ -796,36 +726,42 @@ func TestGetTendermintUpdatesInserted(t *testing.T) {
validators[i], pool, _ = validators[i].AddTokensFromDel(pool, sdk.NewInt(amt))
keeper.SetPool(ctx, pool)
}
validators[0] = keeper.UpdateValidator(ctx, validators[0])
validators[1] = keeper.UpdateValidator(ctx, validators[1])
clearTendermintUpdates(ctx, keeper)
require.Equal(t, 0, len(keeper.GetTendermintUpdates(ctx)))
validators[0] = testingUpdateValidator(keeper, ctx, validators[0])
validators[1] = testingUpdateValidator(keeper, ctx, validators[1])
require.Equal(t, 0, len(keeper.ApplyAndReturnValidatorSetUpdates(ctx)))
// test validtor added at the beginning
// tendermintUpdate set: {} -> {c0}
validators[2] = keeper.UpdateValidator(ctx, validators[2])
updates := keeper.GetTendermintUpdates(ctx)
pool := keeper.GetPool(ctx)
keeper.SetValidator(ctx, validators[2])
keeper.SetValidatorByPowerIndex(ctx, validators[2], pool)
updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx)
validators[2], _ = keeper.GetValidator(ctx, validators[2].OperatorAddr)
require.Equal(t, 1, len(updates))
require.Equal(t, validators[2].ABCIValidatorUpdate(), updates[0])
// test validtor added at the beginning
// tendermintUpdate set: {} -> {c0}
clearTendermintUpdates(ctx, keeper)
validators[3] = keeper.UpdateValidator(ctx, validators[3])
updates = keeper.GetTendermintUpdates(ctx)
pool = keeper.GetPool(ctx)
keeper.SetValidator(ctx, validators[3])
keeper.SetValidatorByPowerIndex(ctx, validators[3], pool)
updates = keeper.ApplyAndReturnValidatorSetUpdates(ctx)
validators[3], _ = keeper.GetValidator(ctx, validators[3].OperatorAddr)
require.Equal(t, 1, len(updates))
require.Equal(t, validators[3].ABCIValidatorUpdate(), updates[0])
// test validtor added at the end
// tendermintUpdate set: {} -> {c0}
clearTendermintUpdates(ctx, keeper)
validators[4] = keeper.UpdateValidator(ctx, validators[4])
updates = keeper.GetTendermintUpdates(ctx)
pool = keeper.GetPool(ctx)
keeper.SetValidator(ctx, validators[4])
keeper.SetValidatorByPowerIndex(ctx, validators[4], pool)
updates = keeper.ApplyAndReturnValidatorSetUpdates(ctx)
validators[4], _ = keeper.GetValidator(ctx, validators[4].OperatorAddr)
require.Equal(t, 1, len(updates))
require.Equal(t, validators[4].ABCIValidatorUpdate(), updates[0])
}
func TestGetTendermintUpdatesWithCliffValidator(t *testing.T) {
func TestApplyAndReturnValidatorSetUpdatesWithCliffValidator(t *testing.T) {
ctx, _, keeper := CreateTestInput(t, false, 1000)
params := types.DefaultParams()
params.MaxValidators = 2
@ -839,34 +775,33 @@ func TestGetTendermintUpdatesWithCliffValidator(t *testing.T) {
validators[i], pool, _ = validators[i].AddTokensFromDel(pool, sdk.NewInt(amt))
keeper.SetPool(ctx, pool)
}
validators[0] = keeper.UpdateValidator(ctx, validators[0])
validators[1] = keeper.UpdateValidator(ctx, validators[1])
clearTendermintUpdates(ctx, keeper)
require.Equal(t, 0, len(keeper.GetTendermintUpdates(ctx)))
validators[0] = testingUpdateValidator(keeper, ctx, validators[0])
validators[1] = testingUpdateValidator(keeper, ctx, validators[1])
require.Equal(t, 0, len(keeper.ApplyAndReturnValidatorSetUpdates(ctx)))
// test validator added at the end but not inserted in the valset
// tendermintUpdate set: {} -> {}
keeper.UpdateValidator(ctx, validators[2])
updates := keeper.GetTendermintUpdates(ctx)
testingUpdateValidator(keeper, ctx, validators[2])
updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx)
require.Equal(t, 0, len(updates))
// test validator change its power and become a gotValidator (pushing out an existing)
// tendermintUpdate set: {} -> {c0, c4}
clearTendermintUpdates(ctx, keeper)
require.Equal(t, 0, len(keeper.GetTendermintUpdates(ctx)))
require.Equal(t, 0, len(keeper.ApplyAndReturnValidatorSetUpdates(ctx)))
pool := keeper.GetPool(ctx)
validators[2], pool, _ = validators[2].AddTokensFromDel(pool, sdk.NewInt(10))
keeper.SetPool(ctx, pool)
validators[2] = keeper.UpdateValidator(ctx, validators[2])
updates = keeper.GetTendermintUpdates(ctx)
keeper.SetValidator(ctx, validators[2])
keeper.SetValidatorByPowerIndex(ctx, validators[2], pool)
updates = keeper.ApplyAndReturnValidatorSetUpdates(ctx)
validators[2], _ = keeper.GetValidator(ctx, validators[2].OperatorAddr)
require.Equal(t, 2, len(updates), "%v", updates)
require.Equal(t, validators[0].ABCIValidatorUpdateZero(), updates[0])
require.Equal(t, validators[2].ABCIValidatorUpdate(), updates[1])
require.Equal(t, validators[0].ABCIValidatorUpdateZero(), updates[1])
require.Equal(t, validators[2].ABCIValidatorUpdate(), updates[0])
}
func TestGetTendermintUpdatesPowerDecrease(t *testing.T) {
func TestApplyAndReturnValidatorSetUpdatesPowerDecrease(t *testing.T) {
ctx, _, keeper := CreateTestInput(t, false, 1000)
amts := []int64{100, 100}
@ -875,12 +810,12 @@ func TestGetTendermintUpdatesPowerDecrease(t *testing.T) {
pool := keeper.GetPool(ctx)
validators[i] = types.NewValidator(sdk.ValAddress(Addrs[i]), PKs[i], types.Description{})
validators[i], pool, _ = validators[i].AddTokensFromDel(pool, sdk.NewInt(amt))
validators[i].BondIntraTxCounter = int16(i)
keeper.SetPool(ctx, pool)
}
validators[0] = keeper.UpdateValidator(ctx, validators[0])
validators[1] = keeper.UpdateValidator(ctx, validators[1])
clearTendermintUpdates(ctx, keeper)
require.Equal(t, 0, len(keeper.GetTendermintUpdates(ctx)))
validators[0] = testingUpdateValidator(keeper, ctx, validators[0])
validators[1] = testingUpdateValidator(keeper, ctx, validators[1])
require.Equal(t, 0, len(keeper.ApplyAndReturnValidatorSetUpdates(ctx)))
// check initial power
require.Equal(t, sdk.NewDec(100).RoundInt64(), validators[0].GetPower().RoundInt64())
@ -892,21 +827,21 @@ func TestGetTendermintUpdatesPowerDecrease(t *testing.T) {
validators[0], pool, _ = validators[0].RemoveDelShares(pool, sdk.NewDec(20))
validators[1], pool, _ = validators[1].RemoveDelShares(pool, sdk.NewDec(30))
keeper.SetPool(ctx, pool)
validators[0] = keeper.UpdateValidator(ctx, validators[0])
validators[1] = keeper.UpdateValidator(ctx, validators[1])
validators[0] = testingUpdateValidator(keeper, ctx, validators[0])
validators[1] = testingUpdateValidator(keeper, ctx, validators[1])
// power has changed
require.Equal(t, sdk.NewDec(80).RoundInt64(), validators[0].GetPower().RoundInt64())
require.Equal(t, sdk.NewDec(70).RoundInt64(), validators[1].GetPower().RoundInt64())
// Tendermint updates should reflect power change
updates := keeper.GetTendermintUpdates(ctx)
updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx)
require.Equal(t, 2, len(updates))
require.Equal(t, validators[0].ABCIValidatorUpdate(), updates[0])
require.Equal(t, validators[1].ABCIValidatorUpdate(), updates[1])
}
func TestGetTendermintUpdatesNewValidator(t *testing.T) {
func TestApplyAndReturnValidatorSetUpdatesNewValidator(t *testing.T) {
ctx, _, keeper := CreateTestInput(t, false, 1000)
params := keeper.GetParams(ctx)
params.MaxValidators = uint16(3)
@ -923,28 +858,33 @@ func TestGetTendermintUpdatesNewValidator(t *testing.T) {
valAddr := sdk.ValAddress(valPubKey.Address().Bytes())
validators[i] = types.NewValidator(valAddr, valPubKey, types.Description{})
validators[i].BondIntraTxCounter = int16(i)
validators[i], pool, _ = validators[i].AddTokensFromDel(pool, sdk.NewInt(amt))
keeper.SetPool(ctx, pool)
validators[i] = keeper.UpdateValidator(ctx, validators[i])
keeper.SetValidator(ctx, validators[i])
keeper.SetValidatorByPowerIndex(ctx, validators[i], pool)
}
// verify initial Tendermint updates are correct
updates := keeper.GetTendermintUpdates(ctx)
updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx)
require.Equal(t, len(validators), len(updates))
validators[0], _ = keeper.GetValidator(ctx, validators[0].OperatorAddr)
validators[1], _ = keeper.GetValidator(ctx, validators[1].OperatorAddr)
require.Equal(t, validators[0].ABCIValidatorUpdate(), updates[0])
require.Equal(t, validators[1].ABCIValidatorUpdate(), updates[1])
clearTendermintUpdates(ctx, keeper)
require.Equal(t, 0, len(keeper.GetTendermintUpdates(ctx)))
require.Equal(t, 0, len(keeper.ApplyAndReturnValidatorSetUpdates(ctx)))
// update initial validator set
for i, amt := range amts {
pool := keeper.GetPool(ctx)
keeper.DeleteValidatorByPowerIndex(ctx, validators[i], pool)
validators[i], pool, _ = validators[i].AddTokensFromDel(pool, sdk.NewInt(amt))
keeper.SetPool(ctx, pool)
validators[i] = keeper.UpdateValidator(ctx, validators[i])
keeper.SetValidator(ctx, validators[i])
keeper.SetValidatorByPowerIndex(ctx, validators[i], pool)
}
// add a new validator that goes from zero power, to non-zero power, back to
@ -958,10 +898,11 @@ func TestGetTendermintUpdatesNewValidator(t *testing.T) {
validator, pool, _ = validator.AddTokensFromDel(pool, amt)
keeper.SetPool(ctx, pool)
validator = keeper.UpdateValidator(ctx, validator)
keeper.SetValidator(ctx, validator)
validator, pool, _ = validator.RemoveDelShares(pool, sdk.NewDecFromInt(amt))
validator = keeper.UpdateValidator(ctx, validator)
keeper.SetValidator(ctx, validator)
keeper.SetValidatorByPowerIndex(ctx, validator, pool)
// add a new validator that increases in power
valPubKey = PKs[len(validators)+2]
@ -969,19 +910,22 @@ func TestGetTendermintUpdatesNewValidator(t *testing.T) {
validator = types.NewValidator(valAddr, valPubKey, types.Description{})
validator, pool, _ = validator.AddTokensFromDel(pool, sdk.NewInt(500))
keeper.SetValidator(ctx, validator)
keeper.SetValidatorByPowerIndex(ctx, validator, pool)
keeper.SetPool(ctx, pool)
validator = keeper.UpdateValidator(ctx, validator)
// verify initial Tendermint updates are correct
updates = keeper.GetTendermintUpdates(ctx)
updates = keeper.ApplyAndReturnValidatorSetUpdates(ctx)
validator, _ = keeper.GetValidator(ctx, validator.OperatorAddr)
validators[0], _ = keeper.GetValidator(ctx, validators[0].OperatorAddr)
validators[1], _ = keeper.GetValidator(ctx, validators[1].OperatorAddr)
require.Equal(t, len(validators)+1, len(updates))
require.Equal(t, validator.ABCIValidatorUpdate(), updates[0])
require.Equal(t, validators[0].ABCIValidatorUpdate(), updates[1])
require.Equal(t, validators[1].ABCIValidatorUpdate(), updates[2])
}
func TestGetTendermintUpdatesBondTransition(t *testing.T) {
func TestApplyAndReturnValidatorSetUpdatesBondTransition(t *testing.T) {
ctx, _, keeper := CreateTestInput(t, false, 1000)
params := keeper.GetParams(ctx)
params.MaxValidators = uint16(2)
@ -1000,60 +944,67 @@ func TestGetTendermintUpdatesBondTransition(t *testing.T) {
validators[i] = types.NewValidator(valAddr, valPubKey, types.Description{Moniker: moniker})
validators[i], pool, _ = validators[i].AddTokensFromDel(pool, sdk.NewInt(amt))
validators[i].BondIntraTxCounter = int16(i)
keeper.SetPool(ctx, pool)
validators[i] = keeper.UpdateValidator(ctx, validators[i])
keeper.SetValidator(ctx, validators[i])
keeper.SetValidatorByPowerIndex(ctx, validators[i], pool)
}
// verify initial Tendermint updates are correct
updates := keeper.GetTendermintUpdates(ctx)
updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx)
require.Equal(t, 2, len(updates))
validators[2], _ = keeper.GetValidator(ctx, validators[2].OperatorAddr)
validators[1], _ = keeper.GetValidator(ctx, validators[1].OperatorAddr)
require.Equal(t, validators[2].ABCIValidatorUpdate(), updates[0])
require.Equal(t, validators[1].ABCIValidatorUpdate(), updates[1])
clearTendermintUpdates(ctx, keeper)
require.Equal(t, 0, len(keeper.GetTendermintUpdates(ctx)))
require.Equal(t, 0, len(keeper.ApplyAndReturnValidatorSetUpdates(ctx)))
// delegate to validator with lowest power but not enough to bond
ctx = ctx.WithBlockHeight(1)
pool := keeper.GetPool(ctx)
validator, found := keeper.GetValidator(ctx, validators[0].OperatorAddr)
var found bool
validators[0], found = keeper.GetValidator(ctx, validators[0].OperatorAddr)
require.True(t, found)
validator, pool, _ = validator.AddTokensFromDel(pool, sdk.NewInt(1))
keeper.DeleteValidatorByPowerIndex(ctx, validators[0], pool)
validators[0], pool, _ = validators[0].AddTokensFromDel(pool, sdk.NewInt(1))
keeper.SetPool(ctx, pool)
validators[0] = keeper.UpdateValidator(ctx, validator)
keeper.SetValidator(ctx, validators[0])
keeper.SetValidatorByPowerIndex(ctx, validators[0], pool)
// verify initial Tendermint updates are correct
require.Equal(t, 0, len(keeper.GetTendermintUpdates(ctx)))
require.Equal(t, 0, len(keeper.ApplyAndReturnValidatorSetUpdates(ctx)))
// 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)
pool = keeper.GetPool(ctx)
validator, found = keeper.GetValidator(ctx, validators[1].OperatorAddr)
validators[1], found = keeper.GetValidator(ctx, validators[1].OperatorAddr)
require.True(t, found)
validator, pool, _ = validator.RemoveDelShares(pool, validator.DelegatorShares)
keeper.DeleteValidatorByPowerIndex(ctx, validators[0], pool)
validators[0], pool, _ = validators[0].RemoveDelShares(pool, validators[0].DelegatorShares)
keeper.SetPool(ctx, pool)
validator = keeper.UpdateValidator(ctx, validator)
validator, pool, _ = validator.AddTokensFromDel(pool, sdk.NewInt(250))
keeper.SetValidator(ctx, validators[0])
keeper.SetValidatorByPowerIndex(ctx, validators[0], pool)
updates = keeper.ApplyAndReturnValidatorSetUpdates(ctx)
require.Equal(t, 0, len(updates))
keeper.DeleteValidatorByPowerIndex(ctx, validators[1], pool)
validators[1], pool, _ = validators[1].AddTokensFromDel(pool, sdk.NewInt(250))
keeper.SetPool(ctx, pool)
validators[1] = keeper.UpdateValidator(ctx, validator)
keeper.SetValidator(ctx, validators[1])
keeper.SetValidatorByPowerIndex(ctx, validators[1], pool)
// verify initial Tendermint updates are correct
updates = keeper.GetTendermintUpdates(ctx)
updates = keeper.ApplyAndReturnValidatorSetUpdates(ctx)
require.Equal(t, 1, len(updates))
require.Equal(t, validators[1].ABCIValidatorUpdate(), updates[0])
clearTendermintUpdates(ctx, keeper)
require.Equal(t, 0, len(keeper.GetTendermintUpdates(ctx)))
require.Equal(t, 0, len(keeper.ApplyAndReturnValidatorSetUpdates(ctx)))
}
func TestUpdateValidatorCommission(t *testing.T) {
@ -1072,6 +1023,9 @@ func TestUpdateValidatorCommission(t *testing.T) {
val1, _ = val1.SetInitialCommission(commission1)
val2, _ = val2.SetInitialCommission(commission2)
keeper.SetValidator(ctx, val1)
keeper.SetValidator(ctx, val2)
testCases := []struct {
validator types.Validator
newRate sdk.Dec
@ -1085,11 +1039,13 @@ func TestUpdateValidatorCommission(t *testing.T) {
}
for i, tc := range testCases {
err := keeper.UpdateValidatorCommission(ctx, tc.validator, tc.newRate)
commission, err := keeper.UpdateValidatorCommission(ctx, tc.validator, tc.newRate)
if tc.expectedErr {
require.Error(t, err, "expected error for test case #%d with rate: %s", i, tc.newRate)
} else {
tc.validator.Commission = commission
keeper.SetValidator(ctx, tc.validator)
val, found := keeper.GetValidator(ctx, tc.validator.OperatorAddr)
require.True(t, found,

View File

@ -71,8 +71,8 @@ func TestQueryValidators(t *testing.T) {
validators[i], pool, _ = validators[i].AddTokensFromDel(pool, amt)
}
keeper.SetPool(ctx, pool)
validators[0] = keeper.UpdateValidator(ctx, validators[0])
validators[1] = keeper.UpdateValidator(ctx, validators[1])
keeper.SetValidator(ctx, validators[0])
keeper.SetValidator(ctx, validators[1])
// Query Validators
queriedValidators := keeper.GetValidators(ctx, params.MaxValidators)
@ -114,9 +114,14 @@ func TestQueryDelegation(t *testing.T) {
// Create Validators and Delegation
val1 := types.NewValidator(addrVal1, pk1, types.Description{})
keeper.SetValidator(ctx, val1)
pool := keeper.GetPool(ctx)
keeper.SetValidatorByPowerIndex(ctx, val1, pool)
keeper.Delegate(ctx, addrAcc2, sdk.NewCoin("steak", sdk.NewInt(20)), val1, true)
// apply TM updates
keeper.ApplyAndReturnValidatorSetUpdates(ctx)
// Query Delegator bonded validators
queryParams := newTestDelegatorQuery(addrAcc2)
bz, errRes := cdc.MarshalJSON(queryParams)

View File

@ -37,9 +37,7 @@ var (
GetValidatorKey = keeper.GetValidatorKey
GetValidatorByConsAddrKey = keeper.GetValidatorByConsAddrKey
GetValidatorsBondedIndexKey = keeper.GetValidatorsBondedIndexKey
GetValidatorsByPowerIndexKey = keeper.GetValidatorsByPowerIndexKey
GetTendermintUpdatesTKey = keeper.GetTendermintUpdatesTKey
GetDelegationKey = keeper.GetDelegationKey
GetDelegationsKey = keeper.GetDelegationsKey
ParamKey = keeper.ParamKey
@ -48,9 +46,6 @@ var (
ValidatorsByConsAddrKey = keeper.ValidatorsByConsAddrKey
ValidatorsBondedIndexKey = keeper.ValidatorsBondedIndexKey
ValidatorsByPowerIndexKey = keeper.ValidatorsByPowerIndexKey
ValidatorCliffIndexKey = keeper.ValidatorCliffIndexKey
ValidatorPowerCliffKey = keeper.ValidatorPowerCliffKey
TendermintUpdatesTKey = keeper.TendermintUpdatesTKey
DelegationKey = keeper.DelegationKey
IntraTxCounterKey = keeper.IntraTxCounterKey
GetUBDKey = keeper.GetUBDKey

View File

@ -314,6 +314,12 @@ func (v Validator) ABCIValidatorUpdate() abci.ValidatorUpdate {
}
}
// ABCIValidatorPowerBytes
func (v Validator) ABCIValidatorPowerBytes(cdc *codec.Codec) []byte {
power := v.BondedTokens().RoundInt64()
return cdc.MustMarshalBinary(power)
}
// ABCIValidatorUpdateZero returns an abci.ValidatorUpdate from a staked validator type
// with zero power used for validator updates.
func (v Validator) ABCIValidatorUpdateZero() abci.ValidatorUpdate {
@ -429,6 +435,7 @@ func (v Validator) BondedTokens() sdk.Dec {
return sdk.ZeroDec()
}
// TODO remove this once the validator queue logic is implemented
// Returns if the validator should be considered unbonded
func (v Validator) IsUnbonded(ctx sdk.Context) bool {
switch v.Status {