Merge PR #2176: Ensure Legacy Validator Delegation Invariants

* Test and allow jailed validator to self-bond

* Implement TestJailedValidatorDelegations

* Restructure TestJailedValidatorDelegations

* Add Delegation to Validator type and update handleMsgUnjail accordingly

* Update ErrMissingSelfDelegation error message

* Update democoin mock validator set impl

* Update pending log

* Add comment to ValidatorSet
This commit is contained in:
Alexander Bezobchuk
2018-08-31 00:10:51 -04:00
committed by Rigel
parent 2d92803b9f
commit df33452490
10 changed files with 199 additions and 13 deletions
+11 -3
View File
@@ -12,20 +12,28 @@ const (
// Default slashing codespace
DefaultCodespace sdk.CodespaceType = 10
CodeInvalidValidator CodeType = 101
CodeValidatorJailed CodeType = 102
CodeValidatorNotJailed CodeType = 103
CodeInvalidValidator CodeType = 101
CodeValidatorJailed CodeType = 102
CodeValidatorNotJailed CodeType = 103
CodeMissingSelfDelegation CodeType = 104
)
func ErrNoValidatorForAddress(codespace sdk.CodespaceType) sdk.Error {
return sdk.NewError(codespace, CodeInvalidValidator, "that address is not associated with any known validator")
}
func ErrBadValidatorAddr(codespace sdk.CodespaceType) sdk.Error {
return sdk.NewError(codespace, CodeInvalidValidator, "validator does not exist for that address")
}
func ErrValidatorJailed(codespace sdk.CodespaceType) sdk.Error {
return sdk.NewError(codespace, CodeValidatorJailed, "validator still jailed, cannot yet be unjailed")
}
func ErrValidatorNotJailed(codespace sdk.CodespaceType) sdk.Error {
return sdk.NewError(codespace, CodeValidatorNotJailed, "validator not jailed, cannot be unjailed")
}
func ErrMissingSelfDelegation(codespace sdk.CodespaceType) sdk.Error {
return sdk.NewError(codespace, CodeMissingSelfDelegation, "validator has no self-delegation; cannot be unjailed")
}
+9 -6
View File
@@ -19,35 +19,38 @@ func NewHandler(k Keeper) sdk.Handler {
// Validators must submit a transaction to unjail itself after
// having been jailed (and thus unbonded) for downtime
func handleMsgUnjail(ctx sdk.Context, msg MsgUnjail, k Keeper) sdk.Result {
// Validator must exist
validator := k.validatorSet.Validator(ctx, msg.ValidatorAddr)
if validator == nil {
return ErrNoValidatorForAddress(k.codespace).Result()
}
// cannot be unjailed if no self-delegation exists
selfDel := k.validatorSet.Delegation(ctx, msg.ValidatorAddr, msg.ValidatorAddr)
if selfDel == nil {
return ErrMissingSelfDelegation(k.codespace).Result()
}
if !validator.GetJailed() {
return ErrValidatorNotJailed(k.codespace).Result()
}
addr := sdk.ValAddress(validator.GetPubKey().Address())
// Signing info must exist
info, found := k.getValidatorSigningInfo(ctx, addr)
if !found {
return ErrNoValidatorForAddress(k.codespace).Result()
}
// Cannot be unjailed until out of jail
// cannot be unjailed until out of jail
if ctx.BlockHeader().Time.Before(info.JailedUntil) {
return ErrValidatorJailed(k.codespace).Result()
}
// Update the starting height (so the validator can't be immediately jailed again)
// update the starting height so the validator can't be immediately jailed
// again
info.StartHeight = ctx.BlockHeight()
k.setValidatorSigningInfo(ctx, addr, info)
// Unjail the validator
k.validatorSet.Unjail(ctx, validator.GetPubKey())
tags := sdk.NewTags("action", []byte("unjail"), "validator", []byte(msg.ValidatorAddr.String()))
+60
View File
@@ -2,6 +2,7 @@ package slashing
import (
"testing"
"time"
"github.com/stretchr/testify/require"
@@ -27,3 +28,62 @@ func TestCannotUnjailUnlessJailed(t *testing.T) {
require.False(t, got.IsOK(), "allowed unjail of non-jailed validator")
require.Equal(t, sdk.ToABCICode(DefaultCodespace, CodeValidatorNotJailed), got.Code)
}
func TestJailedValidatorDelegations(t *testing.T) {
ctx, _, stakeKeeper, _, slashingKeeper := createTestInput(t)
stakeParams := stakeKeeper.GetParams(ctx)
stakeParams.UnbondingTime = 0
stakeKeeper.SetParams(ctx, stakeParams)
// create a validator
amount := int64(10)
valAddr, valPubKey, bondAmount := addrs[0], pks[0], sdk.NewInt(amount)
msgCreateVal := newTestMsgCreateValidator(valAddr, valPubKey, bondAmount)
got := stake.NewHandler(stakeKeeper)(ctx, msgCreateVal)
require.True(t, got.IsOK(), "expected create validator msg to be ok, got: %v", got)
// set dummy signing info
newInfo := ValidatorSigningInfo{
StartHeight: int64(0),
IndexOffset: int64(0),
JailedUntil: time.Unix(0, 0),
SignedBlocksCounter: int64(0),
}
slashingKeeper.setValidatorSigningInfo(ctx, sdk.ValAddress(valAddr), newInfo)
// delegate tokens to the validator
delAddr := addrs[1]
msgDelegate := newTestMsgDelegate(delAddr, valAddr, bondAmount)
got = stake.NewHandler(stakeKeeper)(ctx, msgDelegate)
require.True(t, got.IsOK(), "expected delegation to be ok, got %v", got)
unbondShares := sdk.NewDec(10)
// unbond validator total self-delegations (which should jail the validator)
msgBeginUnbonding := stake.NewMsgBeginUnbonding(valAddr, valAddr, unbondShares)
got = stake.NewHandler(stakeKeeper)(ctx, msgBeginUnbonding)
require.True(t, got.IsOK(), "expected begin unbonding validator msg to be ok, got: %v", got)
msgCompleteUnbonding := stake.NewMsgCompleteUnbonding(valAddr, valAddr)
got = stake.NewHandler(stakeKeeper)(ctx, msgCompleteUnbonding)
require.True(t, got.IsOK(), "expected complete unbonding validator msg to be ok, got: %v", got)
// verify validator still exists and is jailed
validator, found := stakeKeeper.GetValidator(ctx, valAddr)
require.True(t, found)
require.True(t, validator.GetJailed())
// verify the validator cannot unjail itself
got = NewHandler(slashingKeeper)(ctx, NewMsgUnjail(valAddr))
require.False(t, got.IsOK(), "expected jailed validator to not be able to unjail, got: %v", got)
// self-delegate to validator
msgSelfDelegate := newTestMsgDelegate(valAddr, valAddr, bondAmount)
got = stake.NewHandler(stakeKeeper)(ctx, msgSelfDelegate)
require.True(t, got.IsOK(), "expected delegation to not be ok, got %v", got)
// verify the validator can now unjail itself
got = NewHandler(slashingKeeper)(ctx, NewMsgUnjail(valAddr))
require.True(t, got.IsOK(), "expected jailed validator to be able to unjail, got: %v", got)
}
+10 -1
View File
@@ -4,6 +4,7 @@ import (
"encoding/hex"
"os"
"testing"
"time"
"github.com/stretchr/testify/require"
@@ -61,7 +62,7 @@ func createTestInput(t *testing.T) (sdk.Context, bank.Keeper, stake.Keeper, para
ms.MountStoreWithDB(keyParams, sdk.StoreTypeIAVL, db)
err := ms.LoadLatestVersion()
require.Nil(t, err)
ctx := sdk.NewContext(ms, abci.Header{}, false, log.NewTMLogger(os.Stdout))
ctx := sdk.NewContext(ms, abci.Header{Time: time.Unix(0, 0)}, false, log.NewTMLogger(os.Stdout))
cdc := createTestCodec()
accountMapper := auth.NewAccountMapper(cdc, keyAcc, auth.ProtoBaseAccount)
ck := bank.NewKeeper(accountMapper)
@@ -108,3 +109,11 @@ func newTestMsgCreateValidator(address sdk.ValAddress, pubKey crypto.PubKey, amt
Delegation: sdk.Coin{"steak", amt},
}
}
func newTestMsgDelegate(delAddr, valAddr sdk.AccAddress, delAmount sdk.Int) stake.MsgDelegate {
return stake.MsgDelegate{
DelegatorAddr: delAddr,
ValidatorAddr: valAddr,
Delegation: sdk.Coin{"steak", delAmount},
}
}