diff --git a/x/slashing/keeper.go b/x/slashing/keeper.go index 17cf349ad3..6ec093dc65 100644 --- a/x/slashing/keeper.go +++ b/x/slashing/keeper.go @@ -72,7 +72,6 @@ func (k Keeper) handleDoubleSign(ctx sdk.Context, height int64, timestamp int64, } logger.Info(fmt.Sprintf("Confirmed double sign from %v at height %d, age of %d less than max age of %d", pubkey.Address(), height, age, MaxEvidenceAge)) k.stakeKeeper.Slash(ctx, pubkey, height, SlashFractionDoubleSign) - logger.Info(fmt.Sprintf("Slashed validator %s by fraction %v for double-sign at height %d", pubkey.Address(), SlashFractionDoubleSign, height)) } // handle a validator signature, must be called once per validator per block @@ -99,8 +98,6 @@ func (k Keeper) handleValidatorSignature(ctx sdk.Context, pubkey crypto.PubKey, if height > minHeight && signInfo.SignedBlocksCounter < MinSignedPerWindow { k.stakeKeeper.Slash(ctx, pubkey, height, SlashFractionDowntime) k.stakeKeeper.ForceUnbond(ctx, pubkey, DowntimeUnbondDuration) - logger.Info(fmt.Sprintf("Slashed validator %s by fraction %v and unbonded for downtime at height %d, cannot rebond for %ds", - address, SlashFractionDowntime, height, DowntimeUnbondDuration)) } } diff --git a/x/slashing/keeper_test.go b/x/slashing/keeper_test.go index f360fd9e99..54461aa31e 100644 --- a/x/slashing/keeper_test.go +++ b/x/slashing/keeper_test.go @@ -2,6 +2,7 @@ package slashing import ( "encoding/hex" + "os" "testing" "github.com/stretchr/testify/require" @@ -52,7 +53,7 @@ func createTestInput(t *testing.T) (sdk.Context, bank.Keeper, stake.Keeper, Keep ms.MountStoreWithDB(keySlashing, sdk.StoreTypeIAVL, db) err := ms.LoadLatestVersion() require.Nil(t, err) - ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewNopLogger(), nil) + ctx := sdk.NewContext(ms, abci.Header{}, false, nil, log.NewTMLogger(os.Stdout), nil) cdc := createTestCodec() accountMapper := auth.NewAccountMapper(cdc, keyAcc, &auth.BaseAccount{}) ck := bank.NewKeeper(accountMapper) @@ -74,8 +75,9 @@ func TestHandleDoubleSign(t *testing.T) { require.True(t, got.IsOK()) _ = sk.Tick(ctx) require.Equal(t, ck.GetCoins(ctx, addr), sdk.Coins{{sk.GetParams(ctx).BondDenom, initCoins - amt}}) + require.Equal(t, sdk.NewRat(amt), sk.Validator(ctx, addr).GetPower()) keeper.handleDoubleSign(ctx, 0, 0, val) - // TODO + require.Equal(t, sdk.NewRat(amt).Mul(sdk.NewRat(19).Quo(sdk.NewRat(20))), sk.Validator(ctx, addr).GetPower()) } func TestHandleAbsentValidator(t *testing.T) { diff --git a/x/stake/keeper.go b/x/stake/keeper.go index 46790e8490..7d5073399b 100644 --- a/x/stake/keeper.go +++ b/x/stake/keeper.go @@ -779,8 +779,28 @@ func (k Keeper) IterateDelegators(ctx sdk.Context, delAddr sdk.Address, fn func( // slash a validator func (k Keeper) Slash(ctx sdk.Context, pubkey crypto.PubKey, height int64, fraction sdk.Rat) { + // TODO height ignored for now, see https://github.com/cosmos/cosmos-sdk/pull/1011#issuecomment-390253957 + val, found := k.GetValidatorByPubKey(ctx, pubkey) + if !found { + panic(fmt.Errorf("Attempted to slash a nonexistent validator with pubkey %s", pubkey)) + } + sharesToRemove := val.PoolShares.Amount.Mul(fraction) + pool := k.GetPool(ctx) + val, pool, burned := val.removePoolShares(pool, sharesToRemove) + k.setPool(ctx, pool) // update the pool + k.updateValidator(ctx, val) // update the validator, possibly kicking it out + ctx.Logger().With("module", "x/stake").Info(fmt.Sprintf("Validator %v slashed by fraction %v, removed %v shares and burned %d tokens", pubkey, fraction, sharesToRemove, burned)) + return } // force unbond a validator func (k Keeper) ForceUnbond(ctx sdk.Context, pubkey crypto.PubKey, jailDuration int64) { + // TODO Implement + /* + val, found := k.GetValidatorByPubKey(ctx, pubkey) + if !found { + ctx.Logger().Info("Validator with pubkey %s not found, cannot force unbond", pubkey) + return + } + */ } diff --git a/x/stake/validator.go b/x/stake/validator.go index 88f061f315..31ef290618 100644 --- a/x/stake/validator.go +++ b/x/stake/validator.go @@ -153,6 +153,21 @@ func (v Validator) UpdateStatus(pool Pool, NewStatus sdk.BondStatus) (Validator, return v, pool } +// Remove & burn pool shares, e.g. when slashing a validator +func (v Validator) removePoolShares(pool Pool, amt sdk.Rat) (Validator, Pool, int64) { + var tokens int64 + switch v.Status() { + case sdk.Unbonded: + pool, tokens = pool.removeSharesUnbonded(amt) + case sdk.Unbonding: + pool, tokens = pool.removeSharesUnbonding(amt) + case sdk.Bonded: + pool, tokens = pool.removeSharesBonded(amt) + } + v.PoolShares.Amount = v.PoolShares.Amount.Sub(amt) + return v, pool, tokens +} + // XXX TEST // get the power or potential power for a validator // if bonded, the power is the BondedShares