Merge PR #2480: Fix signing info handling bugs & faulty slashing
This commit is contained in:
commit
55f4f61493
|
@ -69,6 +69,7 @@ BREAKING CHANGES
|
||||||
* [x/staking] \#2244 staking now holds a consensus-address-index instead of a consensus-pubkey-index
|
* [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/staking] \#2236 more distribution hooks for distribution
|
||||||
* [x/stake] \#2394 Split up UpdateValidator into distinct state transitions applied only in EndBlock
|
* [x/stake] \#2394 Split up UpdateValidator into distinct state transitions applied only in EndBlock
|
||||||
|
* [x/slashing] \#2480 Fix signing info handling bugs & faulty slashing
|
||||||
* [x/stake] \#2412 Added an unbonding validator queue to EndBlock to automatically update validator.Status when finished Unbonding
|
* [x/stake] \#2412 Added an unbonding validator queue to EndBlock to automatically update validator.Status when finished Unbonding
|
||||||
* [x/stake] \#2500 Block conflicting redelegations until we add an index
|
* [x/stake] \#2500 Block conflicting redelegations until we add an index
|
||||||
* [x/params] Global Paramstore refactored
|
* [x/params] Global Paramstore refactored
|
||||||
|
|
|
@ -713,7 +713,7 @@ func TestUnjail(t *testing.T) {
|
||||||
tests.WaitForHeight(4, port)
|
tests.WaitForHeight(4, port)
|
||||||
require.Equal(t, true, signingInfo.IndexOffset > 0)
|
require.Equal(t, true, signingInfo.IndexOffset > 0)
|
||||||
require.Equal(t, time.Unix(0, 0).UTC(), signingInfo.JailedUntil)
|
require.Equal(t, time.Unix(0, 0).UTC(), signingInfo.JailedUntil)
|
||||||
require.Equal(t, true, signingInfo.SignedBlocksCounter > 0)
|
require.Equal(t, true, signingInfo.MissedBlocksCounter == 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestProposalsQuery(t *testing.T) {
|
func TestProposalsQuery(t *testing.T) {
|
||||||
|
|
|
@ -93,25 +93,27 @@ for val in block.Validators:
|
||||||
|
|
||||||
index := signInfo.IndexOffset % SIGNED_BLOCKS_WINDOW
|
index := signInfo.IndexOffset % SIGNED_BLOCKS_WINDOW
|
||||||
signInfo.IndexOffset++
|
signInfo.IndexOffset++
|
||||||
previous = SigningBitArray.Get(val.Address, index)
|
previous = MissedBlockBitArray.Get(val.Address, index)
|
||||||
|
|
||||||
// update counter if array has changed
|
// update counter if array has changed
|
||||||
if previous and val in block.AbsentValidators:
|
if !previous and val in block.AbsentValidators:
|
||||||
SigningBitArray.Set(val.Address, index, false)
|
MissedBlockBitArray.Set(val.Address, index, true)
|
||||||
signInfo.SignedBlocksCounter--
|
signInfo.MissedBlocksCounter++
|
||||||
else if !previous and val not in block.AbsentValidators:
|
else if previous and val not in block.AbsentValidators:
|
||||||
SigningBitArray.Set(val.Address, index, true)
|
MissedBlockBitArray.Set(val.Address, index, false)
|
||||||
signInfo.SignedBlocksCounter++
|
signInfo.MissedBlocksCounter--
|
||||||
// else previous == val not in block.AbsentValidators, no change
|
// else previous == val not in block.AbsentValidators, no change
|
||||||
|
|
||||||
// validator must be active for at least SIGNED_BLOCKS_WINDOW
|
// validator must be active for at least SIGNED_BLOCKS_WINDOW
|
||||||
// before they can be automatically unbonded for failing to be
|
// before they can be automatically unbonded for failing to be
|
||||||
// included in 50% of the recent LastCommits
|
// included in 50% of the recent LastCommits
|
||||||
minHeight = signInfo.StartHeight + SIGNED_BLOCKS_WINDOW
|
minHeight = signInfo.StartHeight + SIGNED_BLOCKS_WINDOW
|
||||||
minSigned = SIGNED_BLOCKS_WINDOW / 2
|
maxMissed = SIGNED_BLOCKS_WINDOW / 2
|
||||||
if height > minHeight AND signInfo.SignedBlocksCounter < minSigned:
|
if height > minHeight AND signInfo.MissedBlocksCounter > maxMissed:
|
||||||
signInfo.JailedUntil = block.Time + DOWNTIME_UNBOND_DURATION
|
signInfo.JailedUntil = block.Time + DOWNTIME_UNBOND_DURATION
|
||||||
|
signInfo.IndexOffset = 0
|
||||||
|
signInfo.MissedBlocksCounter = 0
|
||||||
|
clearMissedBlockBitArray()
|
||||||
slash & unbond the validator
|
slash & unbond the validator
|
||||||
|
|
||||||
SigningInfo.Set(val.Address, signInfo)
|
SigningInfo.Set(val.Address, signInfo)
|
||||||
|
|
|
@ -12,6 +12,17 @@ and `SlashedSoFar` of `0`:
|
||||||
```
|
```
|
||||||
onValidatorBonded(address sdk.ValAddress)
|
onValidatorBonded(address sdk.ValAddress)
|
||||||
|
|
||||||
|
signingInfo, found = getValidatorSigningInfo(address)
|
||||||
|
if !found {
|
||||||
|
signingInfo = ValidatorSigningInfo {
|
||||||
|
StartHeight : CurrentHeight,
|
||||||
|
IndexOffset : 0,
|
||||||
|
JailedUntil : time.Unix(0, 0),
|
||||||
|
MissedBloskCounter : 0
|
||||||
|
}
|
||||||
|
setValidatorSigningInfo(signingInfo)
|
||||||
|
}
|
||||||
|
|
||||||
slashingPeriod = SlashingPeriod{
|
slashingPeriod = SlashingPeriod{
|
||||||
ValidatorAddr : address,
|
ValidatorAddr : address,
|
||||||
StartHeight : CurrentHeight,
|
StartHeight : CurrentHeight,
|
||||||
|
|
|
@ -17,18 +17,18 @@ Information about validator activity is tracked in a `ValidatorSigningInfo`.
|
||||||
It is indexed in the store as follows:
|
It is indexed in the store as follows:
|
||||||
|
|
||||||
- SigningInfo: ` 0x01 | ValTendermintAddr -> amino(valSigningInfo)`
|
- SigningInfo: ` 0x01 | ValTendermintAddr -> amino(valSigningInfo)`
|
||||||
- SigningBitArray: ` 0x02 | ValTendermintAddr | LittleEndianUint64(signArrayIndex) -> VarInt(didSign)`
|
- MissedBlocksBitArray: ` 0x02 | ValTendermintAddr | LittleEndianUint64(signArrayIndex) -> VarInt(didMiss)`
|
||||||
|
|
||||||
The first map allows us to easily lookup the recent signing info for a
|
The first map allows us to easily lookup the recent signing info for a
|
||||||
validator, according to the Tendermint validator address. The second map acts as
|
validator, according to the Tendermint validator address. The second map acts as
|
||||||
a bit-array of size `SIGNED_BLOCKS_WINDOW` that tells us if the validator signed for a given index in the bit-array.
|
a bit-array of size `SIGNED_BLOCKS_WINDOW` that tells us if the validator missed the block for a given index in the bit-array.
|
||||||
|
|
||||||
The index in the bit-array is given as little endian uint64.
|
The index in the bit-array is given as little endian uint64.
|
||||||
|
|
||||||
The result is a `varint` that takes on `0` or `1`, where `0` indicates the
|
The result is a `varint` that takes on `0` or `1`, where `0` indicates the
|
||||||
validator did not sign the corresponding block, and `1` indicates they did.
|
validator did not miss (did sign) the corresponding block, and `1` indicates they missed the block (did not sign).
|
||||||
|
|
||||||
Note that the SigningBitArray is not explicitly initialized up-front. Keys are
|
Note that the MissedBlocksBitArray is not explicitly initialized up-front. Keys are
|
||||||
added as we progress through the first `SIGNED_BLOCKS_WINDOW` blocks for a newly
|
added as we progress through the first `SIGNED_BLOCKS_WINDOW` blocks for a newly
|
||||||
bonded validator.
|
bonded validator.
|
||||||
|
|
||||||
|
@ -40,7 +40,7 @@ type ValidatorSigningInfo struct {
|
||||||
IndexOffset int64 // Offset into the signed block bit array
|
IndexOffset int64 // Offset into the signed block bit array
|
||||||
JailedUntilHeight int64 // Block height until which the validator is jailed,
|
JailedUntilHeight int64 // Block height until which the validator is jailed,
|
||||||
// or sentinel value of 0 for not jailed
|
// or sentinel value of 0 for not jailed
|
||||||
SignedBlocksCounter int64 // Running counter of signed blocks
|
MissedBlocksCounter int64 // Running counter of missed blocks
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
@ -49,7 +49,7 @@ Where:
|
||||||
* `StartHeight` is set to the height that the candidate became an active validator (with non-zero voting power).
|
* `StartHeight` is set to the height that the candidate became an active validator (with non-zero voting power).
|
||||||
* `IndexOffset` is incremented each time the candidate was a bonded validator in a block (and may have signed a precommit or not).
|
* `IndexOffset` is incremented each time the candidate was a bonded validator in a block (and may have signed a precommit or not).
|
||||||
* `JailedUntil` is set whenever the candidate is jailed due to downtime
|
* `JailedUntil` is set whenever the candidate is jailed due to downtime
|
||||||
* `SignedBlocksCounter` is a counter kept to avoid unnecessary array reads. `SignedBlocksBitArray.Sum() == SignedBlocksCounter` always.
|
* `MissedBlocksCounter` is a counter kept to avoid unnecessary array reads. `MissedBlocksBitArray.Sum() == MissedBlocksCounter` always.
|
||||||
|
|
||||||
## Slashing Period
|
## Slashing Period
|
||||||
|
|
||||||
|
|
|
@ -25,10 +25,6 @@ handleMsgUnjail(tx TxUnjail)
|
||||||
if block time < info.JailedUntil
|
if block time < info.JailedUntil
|
||||||
fail with "Validator still jailed, cannot unjail until period has expired"
|
fail with "Validator still jailed, cannot unjail until period has expired"
|
||||||
|
|
||||||
// Update the start height so the validator won't be immediately unbonded again
|
|
||||||
info.StartHeight = BlockHeight
|
|
||||||
setValidatorSigningInfo(info)
|
|
||||||
|
|
||||||
validator.Jailed = false
|
validator.Jailed = false
|
||||||
setValidator(validator)
|
setValidator(validator)
|
||||||
|
|
||||||
|
|
|
@ -13,9 +13,9 @@ var (
|
||||||
ProposerKey = []byte{0x04} // key for storing the proposer operator address
|
ProposerKey = []byte{0x04} // key for storing the proposer operator address
|
||||||
|
|
||||||
// params store
|
// params store
|
||||||
ParamStoreKeyCommunityTax = []byte("community-tax")
|
ParamStoreKeyCommunityTax = []byte("communitytax")
|
||||||
ParamStoreKeyBaseProposerReward = []byte("base-proposer-reward")
|
ParamStoreKeyBaseProposerReward = []byte("baseproposerreward")
|
||||||
ParamStoreKeyBonusProposerReward = []byte("bonus-proposer-reward")
|
ParamStoreKeyBonusProposerReward = []byte("bonusproposerreward")
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
|
@ -46,11 +46,7 @@ func handleMsgUnjail(ctx sdk.Context, msg MsgUnjail, k Keeper) sdk.Result {
|
||||||
return ErrValidatorJailed(k.codespace).Result()
|
return ErrValidatorJailed(k.codespace).Result()
|
||||||
}
|
}
|
||||||
|
|
||||||
// update the starting height so the validator can't be immediately jailed
|
// unjail the validator
|
||||||
// again
|
|
||||||
info.StartHeight = ctx.BlockHeight()
|
|
||||||
k.setValidatorSigningInfo(ctx, consAddr, info)
|
|
||||||
|
|
||||||
k.validatorSet.Unjail(ctx, consAddr)
|
k.validatorSet.Unjail(ctx, consAddr)
|
||||||
|
|
||||||
tags := sdk.NewTags("action", []byte("unjail"), "validator", []byte(msg.ValidatorAddr.String()))
|
tags := sdk.NewTags("action", []byte("unjail"), "validator", []byte(msg.ValidatorAddr.String()))
|
||||||
|
|
|
@ -53,7 +53,7 @@ func TestJailedValidatorDelegations(t *testing.T) {
|
||||||
StartHeight: int64(0),
|
StartHeight: int64(0),
|
||||||
IndexOffset: int64(0),
|
IndexOffset: int64(0),
|
||||||
JailedUntil: time.Unix(0, 0),
|
JailedUntil: time.Unix(0, 0),
|
||||||
SignedBlocksCounter: int64(0),
|
MissedBlocksCounter: int64(0),
|
||||||
}
|
}
|
||||||
slashingKeeper.setValidatorSigningInfo(ctx, consAddr, newInfo)
|
slashingKeeper.setValidatorSigningInfo(ctx, consAddr, newInfo)
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,25 @@
|
||||||
package slashing
|
package slashing
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Create a new slashing period when a validator is bonded
|
|
||||||
func (k Keeper) onValidatorBonded(ctx sdk.Context, address sdk.ConsAddress) {
|
func (k Keeper) onValidatorBonded(ctx sdk.Context, address sdk.ConsAddress) {
|
||||||
|
// Update the signing info start height or create a new signing info
|
||||||
|
_, found := k.getValidatorSigningInfo(ctx, address)
|
||||||
|
if !found {
|
||||||
|
signingInfo := ValidatorSigningInfo{
|
||||||
|
StartHeight: ctx.BlockHeight(),
|
||||||
|
IndexOffset: 0,
|
||||||
|
JailedUntil: time.Unix(0, 0),
|
||||||
|
MissedBlocksCounter: 0,
|
||||||
|
}
|
||||||
|
k.setValidatorSigningInfo(ctx, address, signingInfo)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new slashing period when a validator is bonded
|
||||||
slashingPeriod := ValidatorSlashingPeriod{
|
slashingPeriod := ValidatorSlashingPeriod{
|
||||||
ValidatorAddr: address,
|
ValidatorAddr: address,
|
||||||
StartHeight: ctx.BlockHeight(),
|
StartHeight: ctx.BlockHeight(),
|
||||||
|
|
|
@ -102,8 +102,7 @@ func (k Keeper) handleValidatorSignature(ctx sdk.Context, addr crypto.Address, p
|
||||||
// Will use the 0-value default signing info if not present, except for start height
|
// Will use the 0-value default signing info if not present, except for start height
|
||||||
signInfo, found := k.getValidatorSigningInfo(ctx, consAddr)
|
signInfo, found := k.getValidatorSigningInfo(ctx, consAddr)
|
||||||
if !found {
|
if !found {
|
||||||
// If this validator has never been seen before, construct a new SigningInfo with the correct start height
|
panic(fmt.Sprintf("Expected signing info for validator %s but not found", consAddr))
|
||||||
signInfo = NewValidatorSigningInfo(height, 0, time.Unix(0, 0), 0)
|
|
||||||
}
|
}
|
||||||
index := signInfo.IndexOffset % k.SignedBlocksWindow(ctx)
|
index := signInfo.IndexOffset % k.SignedBlocksWindow(ctx)
|
||||||
signInfo.IndexOffset++
|
signInfo.IndexOffset++
|
||||||
|
@ -111,24 +110,27 @@ func (k Keeper) handleValidatorSignature(ctx sdk.Context, addr crypto.Address, p
|
||||||
// Update signed block bit array & counter
|
// Update signed block bit array & counter
|
||||||
// This counter just tracks the sum of the bit array
|
// This counter just tracks the sum of the bit array
|
||||||
// That way we avoid needing to read/write the whole array each time
|
// That way we avoid needing to read/write the whole array each time
|
||||||
previous := k.getValidatorSigningBitArray(ctx, consAddr, index)
|
previous := k.getValidatorMissedBlockBitArray(ctx, consAddr, index)
|
||||||
if previous == signed {
|
missed := !signed
|
||||||
|
switch {
|
||||||
|
case !previous && missed:
|
||||||
|
// Array value has changed from not missed to missed, increment counter
|
||||||
|
k.setValidatorMissedBlockBitArray(ctx, consAddr, index, true)
|
||||||
|
signInfo.MissedBlocksCounter++
|
||||||
|
case previous && !missed:
|
||||||
|
// Array value has changed from missed to not missed, decrement counter
|
||||||
|
k.setValidatorMissedBlockBitArray(ctx, consAddr, index, false)
|
||||||
|
signInfo.MissedBlocksCounter--
|
||||||
|
default:
|
||||||
// Array value at this index has not changed, no need to update counter
|
// Array value at this index has not changed, no need to update counter
|
||||||
} else if previous && !signed {
|
|
||||||
// Array value has changed from signed to unsigned, decrement counter
|
|
||||||
k.setValidatorSigningBitArray(ctx, consAddr, index, false)
|
|
||||||
signInfo.SignedBlocksCounter--
|
|
||||||
} else if !previous && signed {
|
|
||||||
// Array value has changed from unsigned to signed, increment counter
|
|
||||||
k.setValidatorSigningBitArray(ctx, consAddr, index, true)
|
|
||||||
signInfo.SignedBlocksCounter++
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if !signed {
|
if missed {
|
||||||
logger.Info(fmt.Sprintf("Absent validator %s at height %d, %d signed, threshold %d", addr, height, signInfo.SignedBlocksCounter, k.MinSignedPerWindow(ctx)))
|
logger.Info(fmt.Sprintf("Absent validator %s at height %d, %d missed, threshold %d", addr, height, signInfo.MissedBlocksCounter, k.MinSignedPerWindow(ctx)))
|
||||||
}
|
}
|
||||||
minHeight := signInfo.StartHeight + k.SignedBlocksWindow(ctx)
|
minHeight := signInfo.StartHeight + k.SignedBlocksWindow(ctx)
|
||||||
if height > minHeight && signInfo.SignedBlocksCounter < k.MinSignedPerWindow(ctx) {
|
maxMissed := k.SignedBlocksWindow(ctx) - k.MinSignedPerWindow(ctx)
|
||||||
|
if height > minHeight && signInfo.MissedBlocksCounter > maxMissed {
|
||||||
validator := k.validatorSet.ValidatorByConsAddr(ctx, consAddr)
|
validator := k.validatorSet.ValidatorByConsAddr(ctx, consAddr)
|
||||||
if validator != nil && !validator.GetJailed() {
|
if validator != nil && !validator.GetJailed() {
|
||||||
// Downtime confirmed: slash and jail the validator
|
// Downtime confirmed: slash and jail the validator
|
||||||
|
@ -143,6 +145,10 @@ func (k Keeper) handleValidatorSignature(ctx sdk.Context, addr crypto.Address, p
|
||||||
k.validatorSet.Slash(ctx, consAddr, distributionHeight, power, k.SlashFractionDowntime(ctx))
|
k.validatorSet.Slash(ctx, consAddr, distributionHeight, power, k.SlashFractionDowntime(ctx))
|
||||||
k.validatorSet.Jail(ctx, consAddr)
|
k.validatorSet.Jail(ctx, consAddr)
|
||||||
signInfo.JailedUntil = ctx.BlockHeader().Time.Add(k.DowntimeUnbondDuration(ctx))
|
signInfo.JailedUntil = ctx.BlockHeader().Time.Add(k.DowntimeUnbondDuration(ctx))
|
||||||
|
// We need to reset the counter & array so that the validator won't be immediately slashed for downtime upon rebonding.
|
||||||
|
signInfo.MissedBlocksCounter = 0
|
||||||
|
signInfo.IndexOffset = 0
|
||||||
|
k.clearValidatorMissedBlockBitArray(ctx, consAddr)
|
||||||
} else {
|
} else {
|
||||||
// Validator was (a) not found or (b) already jailed, don't slash
|
// Validator was (a) not found or (b) already jailed, don't slash
|
||||||
logger.Info(fmt.Sprintf("Validator %s would have been slashed for downtime, but was either not found in store or already jailed",
|
logger.Info(fmt.Sprintf("Validator %s would have been slashed for downtime, but was either not found in store or already jailed",
|
||||||
|
|
|
@ -30,7 +30,6 @@ func TestHandleDoubleSign(t *testing.T) {
|
||||||
ctx, ck, sk, _, keeper := createTestInput(t, keeperTestParams())
|
ctx, ck, sk, _, keeper := createTestInput(t, keeperTestParams())
|
||||||
// validator added pre-genesis
|
// validator added pre-genesis
|
||||||
ctx = ctx.WithBlockHeight(-1)
|
ctx = ctx.WithBlockHeight(-1)
|
||||||
sk = sk.WithHooks(keeper.Hooks())
|
|
||||||
amtInt := int64(100)
|
amtInt := int64(100)
|
||||||
operatorAddr, val, amt := addrs[0], pks[0], sdk.NewInt(amtInt)
|
operatorAddr, val, amt := addrs[0], pks[0], sdk.NewInt(amtInt)
|
||||||
got := stake.NewHandler(sk)(ctx, NewTestMsgCreateValidator(operatorAddr, val, amt))
|
got := stake.NewHandler(sk)(ctx, NewTestMsgCreateValidator(operatorAddr, val, amt))
|
||||||
|
@ -71,7 +70,6 @@ func TestSlashingPeriodCap(t *testing.T) {
|
||||||
|
|
||||||
// initial setup
|
// initial setup
|
||||||
ctx, ck, sk, _, keeper := createTestInput(t, DefaultParams())
|
ctx, ck, sk, _, keeper := createTestInput(t, DefaultParams())
|
||||||
sk = sk.WithHooks(keeper.Hooks())
|
|
||||||
amtInt := int64(100)
|
amtInt := int64(100)
|
||||||
operatorAddr, amt := addrs[0], sdk.NewInt(amtInt)
|
operatorAddr, amt := addrs[0], sdk.NewInt(amtInt)
|
||||||
valConsPubKey, valConsAddr := pks[0], pks[0].Address()
|
valConsPubKey, valConsAddr := pks[0], pks[0].Address()
|
||||||
|
@ -137,7 +135,6 @@ func TestHandleAbsentValidator(t *testing.T) {
|
||||||
|
|
||||||
// initial setup
|
// initial setup
|
||||||
ctx, ck, sk, _, keeper := createTestInput(t, keeperTestParams())
|
ctx, ck, sk, _, keeper := createTestInput(t, keeperTestParams())
|
||||||
sk = sk.WithHooks(keeper.Hooks())
|
|
||||||
amtInt := int64(100)
|
amtInt := int64(100)
|
||||||
addr, val, amt := addrs[0], pks[0], sdk.NewInt(amtInt)
|
addr, val, amt := addrs[0], pks[0], sdk.NewInt(amtInt)
|
||||||
sh := stake.NewHandler(sk)
|
sh := stake.NewHandler(sk)
|
||||||
|
@ -148,14 +145,13 @@ func TestHandleAbsentValidator(t *testing.T) {
|
||||||
keeper.AddValidators(ctx, validatorUpdates)
|
keeper.AddValidators(ctx, validatorUpdates)
|
||||||
require.Equal(t, ck.GetCoins(ctx, sdk.AccAddress(addr)), sdk.Coins{{sk.GetParams(ctx).BondDenom, initCoins.Sub(amt)}})
|
require.Equal(t, ck.GetCoins(ctx, sdk.AccAddress(addr)), sdk.Coins{{sk.GetParams(ctx).BondDenom, initCoins.Sub(amt)}})
|
||||||
require.True(t, sdk.NewDecFromInt(amt).Equal(sk.Validator(ctx, addr).GetPower()))
|
require.True(t, sdk.NewDecFromInt(amt).Equal(sk.Validator(ctx, addr).GetPower()))
|
||||||
|
// will exist since the validator has been bonded
|
||||||
info, found := keeper.getValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address()))
|
info, found := keeper.getValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address()))
|
||||||
require.False(t, found)
|
require.True(t, found)
|
||||||
require.Equal(t, int64(0), info.StartHeight)
|
require.Equal(t, int64(0), info.StartHeight)
|
||||||
require.Equal(t, int64(0), info.IndexOffset)
|
require.Equal(t, int64(0), info.IndexOffset)
|
||||||
require.Equal(t, int64(0), info.SignedBlocksCounter)
|
require.Equal(t, int64(0), info.MissedBlocksCounter)
|
||||||
// default time.Time value
|
require.Equal(t, time.Unix(0, 0).UTC(), info.JailedUntil)
|
||||||
var blankTime time.Time
|
|
||||||
require.Equal(t, blankTime, info.JailedUntil)
|
|
||||||
height := int64(0)
|
height := int64(0)
|
||||||
|
|
||||||
// 1000 first blocks OK
|
// 1000 first blocks OK
|
||||||
|
@ -166,7 +162,7 @@ func TestHandleAbsentValidator(t *testing.T) {
|
||||||
info, found = keeper.getValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address()))
|
info, found = keeper.getValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address()))
|
||||||
require.True(t, found)
|
require.True(t, found)
|
||||||
require.Equal(t, int64(0), info.StartHeight)
|
require.Equal(t, int64(0), info.StartHeight)
|
||||||
require.Equal(t, keeper.SignedBlocksWindow(ctx), info.SignedBlocksCounter)
|
require.Equal(t, int64(0), info.MissedBlocksCounter)
|
||||||
|
|
||||||
// 500 blocks missed
|
// 500 blocks missed
|
||||||
for ; height < keeper.SignedBlocksWindow(ctx)+(keeper.SignedBlocksWindow(ctx)-keeper.MinSignedPerWindow(ctx)); height++ {
|
for ; height < keeper.SignedBlocksWindow(ctx)+(keeper.SignedBlocksWindow(ctx)-keeper.MinSignedPerWindow(ctx)); height++ {
|
||||||
|
@ -176,7 +172,7 @@ func TestHandleAbsentValidator(t *testing.T) {
|
||||||
info, found = keeper.getValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address()))
|
info, found = keeper.getValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address()))
|
||||||
require.True(t, found)
|
require.True(t, found)
|
||||||
require.Equal(t, int64(0), info.StartHeight)
|
require.Equal(t, int64(0), info.StartHeight)
|
||||||
require.Equal(t, keeper.SignedBlocksWindow(ctx)-keeper.MinSignedPerWindow(ctx), info.SignedBlocksCounter)
|
require.Equal(t, keeper.SignedBlocksWindow(ctx)-keeper.MinSignedPerWindow(ctx), info.MissedBlocksCounter)
|
||||||
|
|
||||||
// validator should be bonded still
|
// validator should be bonded still
|
||||||
validator, _ := sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val))
|
validator, _ := sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val))
|
||||||
|
@ -190,7 +186,8 @@ func TestHandleAbsentValidator(t *testing.T) {
|
||||||
info, found = keeper.getValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address()))
|
info, found = keeper.getValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address()))
|
||||||
require.True(t, found)
|
require.True(t, found)
|
||||||
require.Equal(t, int64(0), info.StartHeight)
|
require.Equal(t, int64(0), info.StartHeight)
|
||||||
require.Equal(t, keeper.SignedBlocksWindow(ctx)-keeper.MinSignedPerWindow(ctx)-1, info.SignedBlocksCounter)
|
// counter now reset to zero
|
||||||
|
require.Equal(t, int64(0), info.MissedBlocksCounter)
|
||||||
|
|
||||||
// end block
|
// end block
|
||||||
stake.EndBlocker(ctx, sk)
|
stake.EndBlocker(ctx, sk)
|
||||||
|
@ -211,7 +208,7 @@ func TestHandleAbsentValidator(t *testing.T) {
|
||||||
info, found = keeper.getValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address()))
|
info, found = keeper.getValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address()))
|
||||||
require.True(t, found)
|
require.True(t, found)
|
||||||
require.Equal(t, int64(0), info.StartHeight)
|
require.Equal(t, int64(0), info.StartHeight)
|
||||||
require.Equal(t, keeper.SignedBlocksWindow(ctx)-keeper.MinSignedPerWindow(ctx)-2, info.SignedBlocksCounter)
|
require.Equal(t, int64(1), info.MissedBlocksCounter)
|
||||||
|
|
||||||
// end block
|
// end block
|
||||||
stake.EndBlocker(ctx, sk)
|
stake.EndBlocker(ctx, sk)
|
||||||
|
@ -248,12 +245,12 @@ func TestHandleAbsentValidator(t *testing.T) {
|
||||||
pool = sk.GetPool(ctx)
|
pool = sk.GetPool(ctx)
|
||||||
require.Equal(t, amtInt-slashAmt-secondSlashAmt, pool.BondedTokens.RoundInt64())
|
require.Equal(t, amtInt-slashAmt-secondSlashAmt, pool.BondedTokens.RoundInt64())
|
||||||
|
|
||||||
// validator start height should have been changed
|
// validator start height should not have been changed
|
||||||
info, found = keeper.getValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address()))
|
info, found = keeper.getValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address()))
|
||||||
require.True(t, found)
|
require.True(t, found)
|
||||||
require.Equal(t, height, info.StartHeight)
|
require.Equal(t, int64(0), info.StartHeight)
|
||||||
// we've missed 2 blocks more than the maximum
|
// we've missed 2 blocks more than the maximum, so the counter was reset to 0 at 1 block more and is now 1
|
||||||
require.Equal(t, keeper.SignedBlocksWindow(ctx)-keeper.MinSignedPerWindow(ctx)-2, info.SignedBlocksCounter)
|
require.Equal(t, int64(1), info.MissedBlocksCounter)
|
||||||
|
|
||||||
// validator should not be immediately jailed again
|
// validator should not be immediately jailed again
|
||||||
height++
|
height++
|
||||||
|
@ -294,6 +291,11 @@ func TestHandleNewValidator(t *testing.T) {
|
||||||
ctx, ck, sk, _, keeper := createTestInput(t, keeperTestParams())
|
ctx, ck, sk, _, keeper := createTestInput(t, keeperTestParams())
|
||||||
addr, val, amt := addrs[0], pks[0], int64(100)
|
addr, val, amt := addrs[0], pks[0], int64(100)
|
||||||
sh := stake.NewHandler(sk)
|
sh := stake.NewHandler(sk)
|
||||||
|
|
||||||
|
// 1000 first blocks not a validator
|
||||||
|
ctx = ctx.WithBlockHeight(keeper.SignedBlocksWindow(ctx) + 1)
|
||||||
|
|
||||||
|
// Validator created
|
||||||
got := sh(ctx, NewTestMsgCreateValidator(addr, val, sdk.NewInt(amt)))
|
got := sh(ctx, NewTestMsgCreateValidator(addr, val, sdk.NewInt(amt)))
|
||||||
require.True(t, got.IsOK())
|
require.True(t, got.IsOK())
|
||||||
validatorUpdates := stake.EndBlocker(ctx, sk)
|
validatorUpdates := stake.EndBlocker(ctx, sk)
|
||||||
|
@ -301,9 +303,6 @@ func TestHandleNewValidator(t *testing.T) {
|
||||||
require.Equal(t, ck.GetCoins(ctx, sdk.AccAddress(addr)), sdk.Coins{{sk.GetParams(ctx).BondDenom, initCoins.SubRaw(amt)}})
|
require.Equal(t, ck.GetCoins(ctx, sdk.AccAddress(addr)), sdk.Coins{{sk.GetParams(ctx).BondDenom, initCoins.SubRaw(amt)}})
|
||||||
require.Equal(t, sdk.NewDec(amt), sk.Validator(ctx, addr).GetPower())
|
require.Equal(t, sdk.NewDec(amt), sk.Validator(ctx, addr).GetPower())
|
||||||
|
|
||||||
// 1000 first blocks not a validator
|
|
||||||
ctx = ctx.WithBlockHeight(keeper.SignedBlocksWindow(ctx) + 1)
|
|
||||||
|
|
||||||
// Now a validator, for two blocks
|
// Now a validator, for two blocks
|
||||||
keeper.handleValidatorSignature(ctx, val.Address(), 100, true)
|
keeper.handleValidatorSignature(ctx, val.Address(), 100, true)
|
||||||
ctx = ctx.WithBlockHeight(keeper.SignedBlocksWindow(ctx) + 2)
|
ctx = ctx.WithBlockHeight(keeper.SignedBlocksWindow(ctx) + 2)
|
||||||
|
@ -313,7 +312,7 @@ func TestHandleNewValidator(t *testing.T) {
|
||||||
require.True(t, found)
|
require.True(t, found)
|
||||||
require.Equal(t, keeper.SignedBlocksWindow(ctx)+1, info.StartHeight)
|
require.Equal(t, keeper.SignedBlocksWindow(ctx)+1, info.StartHeight)
|
||||||
require.Equal(t, int64(2), info.IndexOffset)
|
require.Equal(t, int64(2), info.IndexOffset)
|
||||||
require.Equal(t, int64(1), info.SignedBlocksCounter)
|
require.Equal(t, int64(1), info.MissedBlocksCounter)
|
||||||
require.Equal(t, time.Unix(0, 0).UTC(), info.JailedUntil)
|
require.Equal(t, time.Unix(0, 0).UTC(), info.JailedUntil)
|
||||||
|
|
||||||
// validator should be bonded still, should not have been jailed or slashed
|
// validator should be bonded still, should not have been jailed or slashed
|
||||||
|
@ -369,3 +368,112 @@ func TestHandleAlreadyJailed(t *testing.T) {
|
||||||
require.Equal(t, amtInt-1, validator.GetTokens().RoundInt64())
|
require.Equal(t, amtInt-1, validator.GetTokens().RoundInt64())
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test a validator dipping in and out of the validator set
|
||||||
|
// Ensure that missed blocks are tracked correctly and that
|
||||||
|
// the start height of the signing info is reset correctly
|
||||||
|
func TestValidatorDippingInAndOut(t *testing.T) {
|
||||||
|
|
||||||
|
// initial setup
|
||||||
|
// keeperTestParams set the SignedBlocksWindow to 1000 and MaxMissedBlocksPerWindow to 500
|
||||||
|
ctx, _, sk, _, keeper := createTestInput(t, keeperTestParams())
|
||||||
|
params := sk.GetParams(ctx)
|
||||||
|
params.MaxValidators = 1
|
||||||
|
sk.SetParams(ctx, params)
|
||||||
|
amtInt := int64(100)
|
||||||
|
addr, val, amt := addrs[0], pks[0], sdk.NewInt(amtInt)
|
||||||
|
consAddr := sdk.ConsAddress(addr)
|
||||||
|
sh := stake.NewHandler(sk)
|
||||||
|
got := sh(ctx, NewTestMsgCreateValidator(addr, val, amt))
|
||||||
|
require.True(t, got.IsOK())
|
||||||
|
validatorUpdates := stake.EndBlocker(ctx, sk)
|
||||||
|
keeper.AddValidators(ctx, validatorUpdates)
|
||||||
|
|
||||||
|
// 100 first blocks OK
|
||||||
|
height := int64(0)
|
||||||
|
for ; height < int64(100); height++ {
|
||||||
|
ctx = ctx.WithBlockHeight(height)
|
||||||
|
keeper.handleValidatorSignature(ctx, val.Address(), amtInt, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// validator kicked out of validator set
|
||||||
|
newAmt := int64(101)
|
||||||
|
got = sh(ctx, NewTestMsgCreateValidator(addrs[1], pks[1], sdk.NewInt(newAmt)))
|
||||||
|
require.True(t, got.IsOK())
|
||||||
|
validatorUpdates = stake.EndBlocker(ctx, sk)
|
||||||
|
require.Equal(t, 2, len(validatorUpdates))
|
||||||
|
keeper.AddValidators(ctx, validatorUpdates)
|
||||||
|
validator, _ := sk.GetValidator(ctx, addr)
|
||||||
|
require.Equal(t, sdk.Unbonding, validator.Status)
|
||||||
|
|
||||||
|
// 600 more blocks happened
|
||||||
|
height = int64(700)
|
||||||
|
ctx = ctx.WithBlockHeight(height)
|
||||||
|
|
||||||
|
// validator added back in
|
||||||
|
got = sh(ctx, newTestMsgDelegate(sdk.AccAddress(addrs[2]), addrs[0], sdk.NewInt(2)))
|
||||||
|
require.True(t, got.IsOK())
|
||||||
|
validatorUpdates = stake.EndBlocker(ctx, sk)
|
||||||
|
require.Equal(t, 2, len(validatorUpdates))
|
||||||
|
validator, _ = sk.GetValidator(ctx, addr)
|
||||||
|
require.Equal(t, sdk.Bonded, validator.Status)
|
||||||
|
newAmt = int64(102)
|
||||||
|
|
||||||
|
// validator misses a block
|
||||||
|
keeper.handleValidatorSignature(ctx, val.Address(), newAmt, false)
|
||||||
|
height++
|
||||||
|
|
||||||
|
// shouldn't be jailed/kicked yet
|
||||||
|
validator, _ = sk.GetValidator(ctx, addr)
|
||||||
|
require.Equal(t, sdk.Bonded, validator.Status)
|
||||||
|
|
||||||
|
// validator misses 500 more blocks, 501 total
|
||||||
|
latest := height
|
||||||
|
for ; height < latest+500; height++ {
|
||||||
|
ctx = ctx.WithBlockHeight(height)
|
||||||
|
keeper.handleValidatorSignature(ctx, val.Address(), newAmt, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// should now be jailed & kicked
|
||||||
|
stake.EndBlocker(ctx, sk)
|
||||||
|
validator, _ = sk.GetValidator(ctx, addr)
|
||||||
|
require.Equal(t, sdk.Unbonding, validator.Status)
|
||||||
|
|
||||||
|
// check all the signing information
|
||||||
|
signInfo, found := keeper.getValidatorSigningInfo(ctx, consAddr)
|
||||||
|
require.True(t, found)
|
||||||
|
require.Equal(t, int64(0), signInfo.MissedBlocksCounter)
|
||||||
|
require.Equal(t, int64(0), signInfo.IndexOffset)
|
||||||
|
// array should be cleared
|
||||||
|
for offset := int64(0); offset < keeper.SignedBlocksWindow(ctx); offset++ {
|
||||||
|
missed := keeper.getValidatorMissedBlockBitArray(ctx, consAddr, offset)
|
||||||
|
require.False(t, missed)
|
||||||
|
}
|
||||||
|
|
||||||
|
// some blocks pass
|
||||||
|
height = int64(5000)
|
||||||
|
ctx = ctx.WithBlockHeight(height)
|
||||||
|
|
||||||
|
// validator rejoins and starts signing again
|
||||||
|
sk.Unjail(ctx, consAddr)
|
||||||
|
keeper.handleValidatorSignature(ctx, val.Address(), newAmt, true)
|
||||||
|
height++
|
||||||
|
|
||||||
|
// validator should not be kicked since we reset counter/array when it was jailed
|
||||||
|
stake.EndBlocker(ctx, sk)
|
||||||
|
validator, _ = sk.GetValidator(ctx, addr)
|
||||||
|
require.Equal(t, sdk.Bonded, validator.Status)
|
||||||
|
|
||||||
|
// validator misses 501 blocks
|
||||||
|
latest = height
|
||||||
|
for ; height < latest+501; height++ {
|
||||||
|
ctx = ctx.WithBlockHeight(height)
|
||||||
|
keeper.handleValidatorSignature(ctx, val.Address(), newAmt, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// validator should now be jailed & kicked
|
||||||
|
stake.EndBlocker(ctx, sk)
|
||||||
|
validator, _ = sk.GetValidator(ctx, addr)
|
||||||
|
require.Equal(t, sdk.Unbonding, validator.Status)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ import (
|
||||||
// key prefix bytes
|
// key prefix bytes
|
||||||
var (
|
var (
|
||||||
ValidatorSigningInfoKey = []byte{0x01} // Prefix for signing info
|
ValidatorSigningInfoKey = []byte{0x01} // Prefix for signing info
|
||||||
ValidatorSigningBitArrayKey = []byte{0x02} // Prefix for signature bit array
|
ValidatorMissedBlockBitArrayKey = []byte{0x02} // Prefix for missed block bit array
|
||||||
ValidatorSlashingPeriodKey = []byte{0x03} // Prefix for slashing period
|
ValidatorSlashingPeriodKey = []byte{0x03} // Prefix for slashing period
|
||||||
AddrPubkeyRelationKey = []byte{0x04} // Prefix for address-pubkey relation
|
AddrPubkeyRelationKey = []byte{0x04} // Prefix for address-pubkey relation
|
||||||
)
|
)
|
||||||
|
@ -21,10 +21,15 @@ func GetValidatorSigningInfoKey(v sdk.ConsAddress) []byte {
|
||||||
}
|
}
|
||||||
|
|
||||||
// stored by *Tendermint* address (not operator address)
|
// stored by *Tendermint* address (not operator address)
|
||||||
func GetValidatorSigningBitArrayKey(v sdk.ConsAddress, i int64) []byte {
|
func GetValidatorMissedBlockBitArrayPrefixKey(v sdk.ConsAddress) []byte {
|
||||||
|
return append(ValidatorMissedBlockBitArrayKey, v.Bytes()...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// stored by *Tendermint* address (not operator address)
|
||||||
|
func GetValidatorMissedBlockBitArrayKey(v sdk.ConsAddress, i int64) []byte {
|
||||||
b := make([]byte, 8)
|
b := make([]byte, 8)
|
||||||
binary.LittleEndian.PutUint64(b, uint64(i))
|
binary.LittleEndian.PutUint64(b, uint64(i))
|
||||||
return append(ValidatorSigningBitArrayKey, append(v.Bytes(), b...)...)
|
return append(GetValidatorMissedBlockBitArrayPrefixKey(v), b...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// stored by *Tendermint* address (not operator address)
|
// stored by *Tendermint* address (not operator address)
|
||||||
|
|
|
@ -28,32 +28,42 @@ func (k Keeper) setValidatorSigningInfo(ctx sdk.Context, address sdk.ConsAddress
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stored by *validator* address (not operator address)
|
// Stored by *validator* address (not operator address)
|
||||||
func (k Keeper) getValidatorSigningBitArray(ctx sdk.Context, address sdk.ConsAddress, index int64) (signed bool) {
|
func (k Keeper) getValidatorMissedBlockBitArray(ctx sdk.Context, address sdk.ConsAddress, index int64) (missed bool) {
|
||||||
store := ctx.KVStore(k.storeKey)
|
store := ctx.KVStore(k.storeKey)
|
||||||
bz := store.Get(GetValidatorSigningBitArrayKey(address, index))
|
bz := store.Get(GetValidatorMissedBlockBitArrayKey(address, index))
|
||||||
if bz == nil {
|
if bz == nil {
|
||||||
// lazy: treat empty key as unsigned
|
// lazy: treat empty key as not missed
|
||||||
signed = false
|
missed = false
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
k.cdc.MustUnmarshalBinary(bz, &signed)
|
k.cdc.MustUnmarshalBinary(bz, &missed)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stored by *validator* address (not operator address)
|
// Stored by *validator* address (not operator address)
|
||||||
func (k Keeper) setValidatorSigningBitArray(ctx sdk.Context, address sdk.ConsAddress, index int64, signed bool) {
|
func (k Keeper) setValidatorMissedBlockBitArray(ctx sdk.Context, address sdk.ConsAddress, index int64, missed bool) {
|
||||||
store := ctx.KVStore(k.storeKey)
|
store := ctx.KVStore(k.storeKey)
|
||||||
bz := k.cdc.MustMarshalBinary(signed)
|
bz := k.cdc.MustMarshalBinary(missed)
|
||||||
store.Set(GetValidatorSigningBitArrayKey(address, index), bz)
|
store.Set(GetValidatorMissedBlockBitArrayKey(address, index), bz)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stored by *validator* address (not operator address)
|
||||||
|
func (k Keeper) clearValidatorMissedBlockBitArray(ctx sdk.Context, address sdk.ConsAddress) {
|
||||||
|
store := ctx.KVStore(k.storeKey)
|
||||||
|
iter := sdk.KVStorePrefixIterator(store, GetValidatorMissedBlockBitArrayPrefixKey(address))
|
||||||
|
for ; iter.Valid(); iter.Next() {
|
||||||
|
store.Delete(iter.Key())
|
||||||
|
}
|
||||||
|
iter.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Construct a new `ValidatorSigningInfo` struct
|
// Construct a new `ValidatorSigningInfo` struct
|
||||||
func NewValidatorSigningInfo(startHeight int64, indexOffset int64, jailedUntil time.Time, signedBlocksCounter int64) ValidatorSigningInfo {
|
func NewValidatorSigningInfo(startHeight int64, indexOffset int64, jailedUntil time.Time, missedBlocksCounter int64) ValidatorSigningInfo {
|
||||||
return ValidatorSigningInfo{
|
return ValidatorSigningInfo{
|
||||||
StartHeight: startHeight,
|
StartHeight: startHeight,
|
||||||
IndexOffset: indexOffset,
|
IndexOffset: indexOffset,
|
||||||
JailedUntil: jailedUntil,
|
JailedUntil: jailedUntil,
|
||||||
SignedBlocksCounter: signedBlocksCounter,
|
MissedBlocksCounter: missedBlocksCounter,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,11 +72,11 @@ type ValidatorSigningInfo struct {
|
||||||
StartHeight int64 `json:"start_height"` // height at which validator was first a candidate OR was unjailed
|
StartHeight int64 `json:"start_height"` // height at which validator was first a candidate OR was unjailed
|
||||||
IndexOffset int64 `json:"index_offset"` // index offset into signed block bit array
|
IndexOffset int64 `json:"index_offset"` // index offset into signed block bit array
|
||||||
JailedUntil time.Time `json:"jailed_until"` // timestamp validator cannot be unjailed until
|
JailedUntil time.Time `json:"jailed_until"` // timestamp validator cannot be unjailed until
|
||||||
SignedBlocksCounter int64 `json:"signed_blocks_counter"` // signed blocks counter (to avoid scanning the array every time)
|
MissedBlocksCounter int64 `json:"missed_blocks_counter"` // missed blocks counter (to avoid scanning the array every time)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return human readable signing info
|
// Return human readable signing info
|
||||||
func (i ValidatorSigningInfo) HumanReadableString() string {
|
func (i ValidatorSigningInfo) HumanReadableString() string {
|
||||||
return fmt.Sprintf("Start height: %d, index offset: %d, jailed until: %v, signed blocks counter: %d",
|
return fmt.Sprintf("Start height: %d, index offset: %d, jailed until: %v, missed blocks counter: %d",
|
||||||
i.StartHeight, i.IndexOffset, i.JailedUntil, i.SignedBlocksCounter)
|
i.StartHeight, i.IndexOffset, i.JailedUntil, i.MissedBlocksCounter)
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@ func TestGetSetValidatorSigningInfo(t *testing.T) {
|
||||||
StartHeight: int64(4),
|
StartHeight: int64(4),
|
||||||
IndexOffset: int64(3),
|
IndexOffset: int64(3),
|
||||||
JailedUntil: time.Unix(2, 0),
|
JailedUntil: time.Unix(2, 0),
|
||||||
SignedBlocksCounter: int64(10),
|
MissedBlocksCounter: int64(10),
|
||||||
}
|
}
|
||||||
keeper.setValidatorSigningInfo(ctx, sdk.ConsAddress(addrs[0]), newInfo)
|
keeper.setValidatorSigningInfo(ctx, sdk.ConsAddress(addrs[0]), newInfo)
|
||||||
info, found = keeper.getValidatorSigningInfo(ctx, sdk.ConsAddress(addrs[0]))
|
info, found = keeper.getValidatorSigningInfo(ctx, sdk.ConsAddress(addrs[0]))
|
||||||
|
@ -25,14 +25,14 @@ func TestGetSetValidatorSigningInfo(t *testing.T) {
|
||||||
require.Equal(t, info.StartHeight, int64(4))
|
require.Equal(t, info.StartHeight, int64(4))
|
||||||
require.Equal(t, info.IndexOffset, int64(3))
|
require.Equal(t, info.IndexOffset, int64(3))
|
||||||
require.Equal(t, info.JailedUntil, time.Unix(2, 0).UTC())
|
require.Equal(t, info.JailedUntil, time.Unix(2, 0).UTC())
|
||||||
require.Equal(t, info.SignedBlocksCounter, int64(10))
|
require.Equal(t, info.MissedBlocksCounter, int64(10))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetSetValidatorSigningBitArray(t *testing.T) {
|
func TestGetSetValidatorMissedBlockBitArray(t *testing.T) {
|
||||||
ctx, _, _, _, keeper := createTestInput(t, DefaultParams())
|
ctx, _, _, _, keeper := createTestInput(t, DefaultParams())
|
||||||
signed := keeper.getValidatorSigningBitArray(ctx, sdk.ConsAddress(addrs[0]), 0)
|
missed := keeper.getValidatorMissedBlockBitArray(ctx, sdk.ConsAddress(addrs[0]), 0)
|
||||||
require.False(t, signed) // treat empty key as unsigned
|
require.False(t, missed) // treat empty key as not missed
|
||||||
keeper.setValidatorSigningBitArray(ctx, sdk.ConsAddress(addrs[0]), 0, true)
|
keeper.setValidatorMissedBlockBitArray(ctx, sdk.ConsAddress(addrs[0]), 0, true)
|
||||||
signed = keeper.getValidatorSigningBitArray(ctx, sdk.ConsAddress(addrs[0]), 0)
|
missed = keeper.getValidatorMissedBlockBitArray(ctx, sdk.ConsAddress(addrs[0]), 0)
|
||||||
require.True(t, signed) // now should be signed
|
require.True(t, missed) // now should be missed
|
||||||
}
|
}
|
||||||
|
|
|
@ -88,6 +88,7 @@ func createTestInput(t *testing.T, defaults Params) (sdk.Context, bank.Keeper, s
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
paramstore := paramsKeeper.Subspace(DefaultParamspace)
|
paramstore := paramsKeeper.Subspace(DefaultParamspace)
|
||||||
keeper := NewKeeper(cdc, keySlashing, sk, paramstore, DefaultCodespace)
|
keeper := NewKeeper(cdc, keySlashing, sk, paramstore, DefaultCodespace)
|
||||||
|
sk = sk.WithHooks(keeper.Hooks())
|
||||||
|
|
||||||
require.NotPanics(t, func() {
|
require.NotPanics(t, func() {
|
||||||
InitGenesis(ctx, keeper, GenesisState{defaults}, genesis)
|
InitGenesis(ctx, keeper, GenesisState{defaults}, genesis)
|
||||||
|
|
|
@ -45,7 +45,7 @@ func TestBeginBlocker(t *testing.T) {
|
||||||
require.Equal(t, ctx.BlockHeight(), info.StartHeight)
|
require.Equal(t, ctx.BlockHeight(), info.StartHeight)
|
||||||
require.Equal(t, int64(1), info.IndexOffset)
|
require.Equal(t, int64(1), info.IndexOffset)
|
||||||
require.Equal(t, time.Unix(0, 0).UTC(), info.JailedUntil)
|
require.Equal(t, time.Unix(0, 0).UTC(), info.JailedUntil)
|
||||||
require.Equal(t, int64(1), info.SignedBlocksCounter)
|
require.Equal(t, int64(0), info.MissedBlocksCounter)
|
||||||
|
|
||||||
height := int64(0)
|
height := int64(0)
|
||||||
|
|
||||||
|
|
|
@ -847,11 +847,11 @@ func TestConflictingRedelegation(t *testing.T) {
|
||||||
keeper.SetParams(ctx, params)
|
keeper.SetParams(ctx, params)
|
||||||
|
|
||||||
// create the validators
|
// create the validators
|
||||||
msgCreateValidator := newTestMsgCreateValidator(validatorAddr, keep.PKs[0], 10)
|
msgCreateValidator := NewTestMsgCreateValidator(validatorAddr, keep.PKs[0], 10)
|
||||||
got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
|
got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
|
||||||
require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
|
require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
|
||||||
|
|
||||||
msgCreateValidator = newTestMsgCreateValidator(validatorAddr2, keep.PKs[1], 10)
|
msgCreateValidator = NewTestMsgCreateValidator(validatorAddr2, keep.PKs[1], 10)
|
||||||
got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
|
got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
|
||||||
require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
|
require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue