Add some explanatory comments
This commit is contained in:
parent
26f22dbe4d
commit
f4f8cc66d9
|
@ -16,7 +16,11 @@ func NewHandler(k Keeper) sdk.Handler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Validators must submit a transaction to unrevoke themselves after
|
||||||
|
// having been revoked (and thus unbonded) for downtime
|
||||||
func handleMsgUnrevoke(ctx sdk.Context, msg MsgUnrevoke, k Keeper) sdk.Result {
|
func handleMsgUnrevoke(ctx sdk.Context, msg MsgUnrevoke, k Keeper) sdk.Result {
|
||||||
|
|
||||||
|
// Validator must exist
|
||||||
validator := k.stakeKeeper.Validator(ctx, msg.ValidatorAddr)
|
validator := k.stakeKeeper.Validator(ctx, msg.ValidatorAddr)
|
||||||
if validator == nil {
|
if validator == nil {
|
||||||
return ErrNoValidatorForAddress(k.codespace).Result()
|
return ErrNoValidatorForAddress(k.codespace).Result()
|
||||||
|
@ -24,11 +28,13 @@ func handleMsgUnrevoke(ctx sdk.Context, msg MsgUnrevoke, k Keeper) sdk.Result {
|
||||||
|
|
||||||
addr := validator.GetPubKey().Address()
|
addr := validator.GetPubKey().Address()
|
||||||
|
|
||||||
|
// Signing info must exist
|
||||||
info, found := k.getValidatorSigningInfo(ctx, addr)
|
info, found := k.getValidatorSigningInfo(ctx, addr)
|
||||||
if !found {
|
if !found {
|
||||||
return ErrNoValidatorForAddress(k.codespace).Result()
|
return ErrNoValidatorForAddress(k.codespace).Result()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Cannot be unrevoked until out of jail
|
||||||
if ctx.BlockHeader().Time < info.JailedUntil {
|
if ctx.BlockHeader().Time < info.JailedUntil {
|
||||||
return ErrValidatorJailed(k.codespace).Result()
|
return ErrValidatorJailed(k.codespace).Result()
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,10 +34,14 @@ func NewKeeper(cdc *wire.Codec, key sdk.StoreKey, sk stake.Keeper, codespace sdk
|
||||||
func (k Keeper) handleDoubleSign(ctx sdk.Context, height int64, timestamp int64, pubkey crypto.PubKey) {
|
func (k Keeper) handleDoubleSign(ctx sdk.Context, height int64, timestamp int64, pubkey crypto.PubKey) {
|
||||||
logger := ctx.Logger().With("module", "x/slashing")
|
logger := ctx.Logger().With("module", "x/slashing")
|
||||||
age := ctx.BlockHeader().Time - timestamp
|
age := ctx.BlockHeader().Time - timestamp
|
||||||
|
|
||||||
|
// Double sign too old
|
||||||
if age > MaxEvidenceAge {
|
if age > MaxEvidenceAge {
|
||||||
logger.Info(fmt.Sprintf("Ignored double sign from %s at height %d, age of %d past max age of %d", pubkey.Address(), height, age, MaxEvidenceAge))
|
logger.Info(fmt.Sprintf("Ignored double sign from %s at height %d, age of %d past max age of %d", pubkey.Address(), height, age, MaxEvidenceAge))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Double sign confirmed
|
||||||
logger.Info(fmt.Sprintf("Confirmed double sign from %s at height %d, age of %d less than max age of %d", pubkey.Address(), height, age, MaxEvidenceAge))
|
logger.Info(fmt.Sprintf("Confirmed double sign from %s at height %d, age of %d less than max age of %d", pubkey.Address(), height, age, MaxEvidenceAge))
|
||||||
k.stakeKeeper.Slash(ctx, pubkey, height, SlashFractionDoubleSign)
|
k.stakeKeeper.Slash(ctx, pubkey, height, SlashFractionDoubleSign)
|
||||||
}
|
}
|
||||||
|
@ -50,9 +54,13 @@ func (k Keeper) handleValidatorSignature(ctx sdk.Context, pubkey crypto.PubKey,
|
||||||
logger.Info(fmt.Sprintf("Absent validator %s at height %d", pubkey.Address(), height))
|
logger.Info(fmt.Sprintf("Absent validator %s at height %d", pubkey.Address(), height))
|
||||||
}
|
}
|
||||||
address := pubkey.Address()
|
address := pubkey.Address()
|
||||||
|
|
||||||
|
// Local index, so counts blocks validator *should* have signed
|
||||||
signInfo, _ := k.getValidatorSigningInfo(ctx, address)
|
signInfo, _ := k.getValidatorSigningInfo(ctx, address)
|
||||||
index := signInfo.IndexOffset % SignedBlocksWindow
|
index := signInfo.IndexOffset % SignedBlocksWindow
|
||||||
signInfo.IndexOffset++
|
signInfo.IndexOffset++
|
||||||
|
|
||||||
|
// Update signed block bit array & counter
|
||||||
previous := k.getValidatorSigningBitArray(ctx, address, index)
|
previous := k.getValidatorSigningBitArray(ctx, address, index)
|
||||||
if previous && !signed {
|
if previous && !signed {
|
||||||
k.setValidatorSigningBitArray(ctx, address, index, false)
|
k.setValidatorSigningBitArray(ctx, address, index, false)
|
||||||
|
@ -63,8 +71,10 @@ func (k Keeper) handleValidatorSignature(ctx sdk.Context, pubkey crypto.PubKey,
|
||||||
signInfo.SignedBlocksCounter++
|
signInfo.SignedBlocksCounter++
|
||||||
k.setValidatorSigningInfo(ctx, address, signInfo)
|
k.setValidatorSigningInfo(ctx, address, signInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
minHeight := signInfo.StartHeight + SignedBlocksWindow
|
minHeight := signInfo.StartHeight + SignedBlocksWindow
|
||||||
if height > minHeight && signInfo.SignedBlocksCounter < MinSignedPerWindow {
|
if height > minHeight && signInfo.SignedBlocksCounter < MinSignedPerWindow {
|
||||||
|
// Downtime confirmed, slash, revoke, and jail the validator
|
||||||
logger.Info(fmt.Sprintf("Validator %s past min height of %d and below signed blocks threshold of %d", pubkey.Address(), minHeight, MinSignedPerWindow))
|
logger.Info(fmt.Sprintf("Validator %s past min height of %d and below signed blocks threshold of %d", pubkey.Address(), minHeight, MinSignedPerWindow))
|
||||||
k.stakeKeeper.Slash(ctx, pubkey, height, SlashFractionDowntime)
|
k.stakeKeeper.Slash(ctx, pubkey, height, SlashFractionDowntime)
|
||||||
k.stakeKeeper.Revoke(ctx, pubkey)
|
k.stakeKeeper.Revoke(ctx, pubkey)
|
||||||
|
|
|
@ -39,7 +39,9 @@ func TestHandleAbsentValidator(t *testing.T) {
|
||||||
info, found := keeper.getValidatorSigningInfo(ctx, val.Address())
|
info, found := keeper.getValidatorSigningInfo(ctx, val.Address())
|
||||||
require.False(t, found)
|
require.False(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.SignedBlocksCounter)
|
require.Equal(t, int64(0), info.SignedBlocksCounter)
|
||||||
|
require.Equal(t, int64(0), info.JailedUntil)
|
||||||
height := int64(0)
|
height := int64(0)
|
||||||
// 1000 blocks OK
|
// 1000 blocks OK
|
||||||
for ; height < 1000; height++ {
|
for ; height < 1000; height++ {
|
||||||
|
@ -59,7 +61,7 @@ func TestHandleAbsentValidator(t *testing.T) {
|
||||||
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, SignedBlocksWindow-50, info.SignedBlocksCounter)
|
require.Equal(t, SignedBlocksWindow-50, info.SignedBlocksCounter)
|
||||||
// should be bonded still
|
// validator should be bonded still
|
||||||
validator := sk.ValidatorByPubKey(ctx, val)
|
validator := sk.ValidatorByPubKey(ctx, val)
|
||||||
require.Equal(t, sdk.Bonded, validator.GetStatus())
|
require.Equal(t, sdk.Bonded, validator.GetStatus())
|
||||||
pool := sk.GetPool(ctx)
|
pool := sk.GetPool(ctx)
|
||||||
|
@ -71,31 +73,34 @@ func TestHandleAbsentValidator(t *testing.T) {
|
||||||
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, SignedBlocksWindow-51, info.SignedBlocksCounter)
|
require.Equal(t, SignedBlocksWindow-51, info.SignedBlocksCounter)
|
||||||
// should have been revoked
|
// validator should have been revoked
|
||||||
validator = sk.ValidatorByPubKey(ctx, val)
|
validator = sk.ValidatorByPubKey(ctx, val)
|
||||||
require.Equal(t, sdk.Unbonded, validator.GetStatus())
|
require.Equal(t, sdk.Unbonded, validator.GetStatus())
|
||||||
|
// unrevocation should fail prior to jail expiration
|
||||||
got = slh(ctx, NewMsgUnrevoke(addr))
|
got = slh(ctx, NewMsgUnrevoke(addr))
|
||||||
require.False(t, got.IsOK()) // should fail prior to jail expiration
|
require.False(t, got.IsOK())
|
||||||
|
// unrevocation should succeed after jail expiration
|
||||||
ctx = ctx.WithBlockHeader(abci.Header{Time: int64(86400 * 2)})
|
ctx = ctx.WithBlockHeader(abci.Header{Time: int64(86400 * 2)})
|
||||||
got = slh(ctx, NewMsgUnrevoke(addr))
|
got = slh(ctx, NewMsgUnrevoke(addr))
|
||||||
require.True(t, got.IsOK()) // should succeed after jail expiration
|
require.True(t, got.IsOK())
|
||||||
|
// validator should be rebonded now
|
||||||
validator = sk.ValidatorByPubKey(ctx, val)
|
validator = sk.ValidatorByPubKey(ctx, val)
|
||||||
require.Equal(t, sdk.Bonded, validator.GetStatus())
|
require.Equal(t, sdk.Bonded, validator.GetStatus())
|
||||||
// should have been slashed
|
// validator should have been slashed
|
||||||
pool = sk.GetPool(ctx)
|
pool = sk.GetPool(ctx)
|
||||||
require.Equal(t, int64(99), pool.BondedTokens)
|
require.Equal(t, int64(99), pool.BondedTokens)
|
||||||
// start height should have been changed
|
// validator start height should have been changed
|
||||||
info, found = keeper.getValidatorSigningInfo(ctx, val.Address())
|
info, found = keeper.getValidatorSigningInfo(ctx, val.Address())
|
||||||
require.True(t, found)
|
require.True(t, found)
|
||||||
require.Equal(t, height, info.StartHeight)
|
require.Equal(t, height, info.StartHeight)
|
||||||
require.Equal(t, SignedBlocksWindow-51, info.SignedBlocksCounter)
|
require.Equal(t, SignedBlocksWindow-51, info.SignedBlocksCounter)
|
||||||
// should not be immediately revoked again
|
// validator should not be immediately revoked again
|
||||||
height++
|
height++
|
||||||
ctx = ctx.WithBlockHeight(height)
|
ctx = ctx.WithBlockHeight(height)
|
||||||
keeper.handleValidatorSignature(ctx, val, false)
|
keeper.handleValidatorSignature(ctx, val, false)
|
||||||
validator = sk.ValidatorByPubKey(ctx, val)
|
validator = sk.ValidatorByPubKey(ctx, val)
|
||||||
require.Equal(t, sdk.Bonded, validator.GetStatus())
|
require.Equal(t, sdk.Bonded, validator.GetStatus())
|
||||||
// should be revoked again after 100 blocks
|
// validator should be revoked again after 100 unsigned blocks
|
||||||
nextHeight := height + 100
|
nextHeight := height + 100
|
||||||
for ; height <= nextHeight; height++ {
|
for ; height <= nextHeight; height++ {
|
||||||
ctx = ctx.WithBlockHeight(height)
|
ctx = ctx.WithBlockHeight(height)
|
||||||
|
|
|
@ -12,9 +12,12 @@ import (
|
||||||
|
|
||||||
func NewBeginBlocker(sk Keeper) sdk.BeginBlocker {
|
func NewBeginBlocker(sk Keeper) sdk.BeginBlocker {
|
||||||
return func(ctx sdk.Context, req abci.RequestBeginBlock) abci.ResponseBeginBlock {
|
return func(ctx sdk.Context, req abci.RequestBeginBlock) abci.ResponseBeginBlock {
|
||||||
|
// Tag the height
|
||||||
heightBytes := make([]byte, 8)
|
heightBytes := make([]byte, 8)
|
||||||
binary.LittleEndian.PutUint64(heightBytes, uint64(req.Header.Height))
|
binary.LittleEndian.PutUint64(heightBytes, uint64(req.Header.Height))
|
||||||
tags := sdk.NewTags("height", heightBytes)
|
tags := sdk.NewTags("height", heightBytes)
|
||||||
|
|
||||||
|
// Deal with any equivocation evidence
|
||||||
for _, evidence := range req.ByzantineValidators {
|
for _, evidence := range req.ByzantineValidators {
|
||||||
var pk crypto.PubKey
|
var pk crypto.PubKey
|
||||||
sk.cdc.MustUnmarshalBinary(evidence.PubKey, &pk)
|
sk.cdc.MustUnmarshalBinary(evidence.PubKey, &pk)
|
||||||
|
@ -25,18 +28,25 @@ func NewBeginBlocker(sk Keeper) sdk.BeginBlocker {
|
||||||
ctx.Logger().With("module", "x/slashing").Error(fmt.Sprintf("Ignored unknown evidence type: %s", string(evidence.Type)))
|
ctx.Logger().With("module", "x/slashing").Error(fmt.Sprintf("Ignored unknown evidence type: %s", string(evidence.Type)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Figure out which validators were absent
|
||||||
absent := make(map[string]bool)
|
absent := make(map[string]bool)
|
||||||
for _, pubkey := range req.AbsentValidators {
|
for _, pubkey := range req.AbsentValidators {
|
||||||
var pk crypto.PubKey
|
var pk crypto.PubKey
|
||||||
sk.cdc.MustUnmarshalBinary(pubkey, &pk)
|
sk.cdc.MustUnmarshalBinary(pubkey, &pk)
|
||||||
absent[string(pk.Bytes())] = true
|
absent[string(pk.Bytes())] = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Iterate over all the validators which *should* have signed this block
|
||||||
sk.stakeKeeper.IterateValidatorsBonded(ctx, func(_ int64, validator sdk.Validator) (stop bool) {
|
sk.stakeKeeper.IterateValidatorsBonded(ctx, func(_ int64, validator sdk.Validator) (stop bool) {
|
||||||
pubkey := validator.GetPubKey()
|
pubkey := validator.GetPubKey()
|
||||||
sk.handleValidatorSignature(ctx, pubkey, !absent[string(pubkey.Bytes())])
|
sk.handleValidatorSignature(ctx, pubkey, !absent[string(pubkey.Bytes())])
|
||||||
return false
|
return false
|
||||||
})
|
})
|
||||||
// TODO Add some more tags so clients can track slashing
|
|
||||||
|
// Return the begin block response
|
||||||
|
// TODO Return something composable, so other modules can also have BeginBlockers
|
||||||
|
// TODO Add some more tags so clients can track slashing events
|
||||||
return abci.ResponseBeginBlock{
|
return abci.ResponseBeginBlock{
|
||||||
Tags: tags.ToKVPairs(),
|
Tags: tags.ToKVPairs(),
|
||||||
}
|
}
|
||||||
|
|
|
@ -799,7 +799,8 @@ func (k Keeper) Revoke(ctx sdk.Context, pubkey crypto.PubKey) {
|
||||||
logger := ctx.Logger().With("module", "x/stake")
|
logger := ctx.Logger().With("module", "x/stake")
|
||||||
val, found := k.GetValidatorByPubKey(ctx, pubkey)
|
val, found := k.GetValidatorByPubKey(ctx, pubkey)
|
||||||
if !found {
|
if !found {
|
||||||
ctx.Logger().Info("Validator with pubkey %s not found, cannot force unbond", pubkey)
|
// TODO Should we panic?
|
||||||
|
ctx.Logger().Info("Validator with pubkey %s not found, cannot revoke", pubkey)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
val.Revoked = true
|
val.Revoked = true
|
||||||
|
@ -813,7 +814,8 @@ func (k Keeper) Unrevoke(ctx sdk.Context, pubkey crypto.PubKey) {
|
||||||
logger := ctx.Logger().With("module", "x/stake")
|
logger := ctx.Logger().With("module", "x/stake")
|
||||||
val, found := k.GetValidatorByPubKey(ctx, pubkey)
|
val, found := k.GetValidatorByPubKey(ctx, pubkey)
|
||||||
if !found {
|
if !found {
|
||||||
ctx.Logger().Info("Validator with pubkey %s not found, cannot force unbond", pubkey)
|
// TODO Should we panic?
|
||||||
|
ctx.Logger().Info("Validator with pubkey %s not found, cannot unrevoke", pubkey)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
val.Revoked = false
|
val.Revoked = false
|
||||||
|
|
Loading…
Reference in New Issue