Merge PR #4717: update x/slashing to match module spec

This commit is contained in:
Federico Kunze 2019-07-19 15:43:38 +02:00 committed by Alexander Bezobchuk
parent 72ce6df7b6
commit 52edb032ca
40 changed files with 567 additions and 495 deletions

View File

@ -0,0 +1 @@
#4717 refactor `x/slashing` to match the new module spec

View File

@ -21,7 +21,7 @@ handleMsgUnjail(tx TxUnjail)
if !validator.Jailed
fail with "Validator not jailed, cannot unjail"
info = getValidatorSigningInfo(operator)
info = GetValidatorSigningInfo(operator)
if info.Tombstoned
fail with "Tombstoned validator cannot be unjailed"
if block time < info.JailedUntil

View File

@ -10,7 +10,7 @@ now-bonded validator, which `StartHeight` of the current block.
```
onValidatorBonded(address sdk.ValAddress)
signingInfo, found = getValidatorSigningInfo(address)
signingInfo, found = GetValidatorSigningInfo(address)
if !found {
signingInfo = ValidatorSigningInfo {
StartHeight : CurrentHeight,

View File

@ -25,6 +25,7 @@ const (
var (
// functions aliases
NewParamSetPair = subspace.NewParamSetPair
NewSubspace = subspace.NewSubspace
NewKeyTable = subspace.NewKeyTable
DefaultTestComponents = subspace.DefaultTestComponents

View File

@ -1,15 +1,20 @@
package subspace
// Used for associating paramsubspace key and field of param structs
// ParamSetPair is used for associating paramsubspace key and field of param structs
type ParamSetPair struct {
Key []byte
Value interface{}
}
// Slice of KeyFieldPair
// NewParamSetPair creates a new ParamSetPair instance
func NewParamSetPair(key []byte, value interface{}) ParamSetPair {
return ParamSetPair{key, value}
}
// ParamSetPairs Slice of KeyFieldPair
type ParamSetPairs []ParamSetPair
// Interface for structs containing parameters for a module
// ParamSet defines an interface for structs containing parameters for a module
type ParamSet interface {
ParamSetPairs() ParamSetPairs
}

View File

@ -9,13 +9,14 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
)
// slashing begin block functionality
func BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock, sk Keeper) {
// BeginBlocker check for infraction evidence or downtime of validators
// on every begin block
func BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock, k Keeper) {
// Iterate over all the validators which *should* have signed this block
// store whether or not they have actually signed it and slash/unbond any
// which have missed too many blocks in a row (downtime slashing)
for _, voteInfo := range req.LastCommitInfo.GetVotes() {
sk.HandleValidatorSignature(ctx, voteInfo.Validator.Address, voteInfo.Validator.Power, voteInfo.SignedLastBlock)
k.HandleValidatorSignature(ctx, voteInfo.Validator.Address, voteInfo.Validator.Power, voteInfo.SignedLastBlock)
}
// Iterate through any newly discovered evidence of infraction
@ -24,9 +25,9 @@ func BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock, sk Keeper) {
for _, evidence := range req.ByzantineValidators {
switch evidence.Type {
case tmtypes.ABCIEvidenceTypeDuplicateVote:
sk.HandleDoubleSign(ctx, evidence.Validator.Address, evidence.Height, evidence.Time, evidence.Validator.Power)
k.HandleDoubleSign(ctx, evidence.Validator.Address, evidence.Height, evidence.Time, evidence.Validator.Power)
default:
sk.Logger(ctx).Error(fmt.Sprintf("ignored unknown evidence type: %s", evidence.Type))
k.Logger(ctx).Error(fmt.Sprintf("ignored unknown evidence type: %s", evidence.Type))
}
}
}

View File

@ -9,22 +9,23 @@ import (
abci "github.com/tendermint/tendermint/abci/types"
sdk "github.com/cosmos/cosmos-sdk/types"
slashingkeeper "github.com/cosmos/cosmos-sdk/x/slashing/internal/keeper"
"github.com/cosmos/cosmos-sdk/x/staking"
)
func TestBeginBlocker(t *testing.T) {
ctx, ck, sk, _, keeper := createTestInput(t, DefaultParams())
ctx, ck, sk, _, keeper := slashingkeeper.CreateTestInput(t, DefaultParams())
power := int64(100)
amt := sdk.TokensFromConsensusPower(power)
addr, pk := addrs[2], pks[2]
addr, pk := slashingkeeper.Addrs[2], slashingkeeper.Pks[2]
// bond the validator
got := staking.NewHandler(sk)(ctx, NewTestMsgCreateValidator(addr, pk, amt))
got := staking.NewHandler(sk)(ctx, slashingkeeper.NewTestMsgCreateValidator(addr, pk, amt))
require.True(t, got.IsOK())
staking.EndBlocker(ctx, sk)
require.Equal(
t, ck.GetCoins(ctx, sdk.AccAddress(addr)),
sdk.NewCoins(sdk.NewCoin(sk.GetParams(ctx).BondDenom, initTokens.Sub(amt))),
sdk.NewCoins(sdk.NewCoin(sk.GetParams(ctx).BondDenom, slashingkeeper.InitTokens.Sub(amt))),
)
require.Equal(t, amt, sk.Validator(ctx, addr).GetBondedTokens())
@ -44,7 +45,7 @@ func TestBeginBlocker(t *testing.T) {
}
BeginBlocker(ctx, req, keeper)
info, found := keeper.getValidatorSigningInfo(ctx, sdk.ConsAddress(pk.Address()))
info, found := keeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(pk.Address()))
require.True(t, found)
require.Equal(t, ctx.BlockHeight(), info.StartHeight)
require.Equal(t, int64(1), info.IndexOffset)

View File

@ -1,11 +1,13 @@
// nolint
// autogenerated code using github.com/rigelrozanski/multitool
// aliases generated for the following subdirectories:
// ALIASGEN: github.com/cosmos/cosmos-sdk/x/slashing/types
// ALIASGEN: github.com/cosmos/cosmos-sdk/x/slashing/internal/keeper
// ALIASGEN: github.com/cosmos/cosmos-sdk/x/slashing/internal/types
package slashing
import (
"github.com/cosmos/cosmos-sdk/x/slashing/types"
"github.com/cosmos/cosmos-sdk/x/slashing/internal/keeper"
"github.com/cosmos/cosmos-sdk/x/slashing/internal/types"
)
const (
@ -20,17 +22,19 @@ const (
StoreKey = types.StoreKey
RouterKey = types.RouterKey
QuerierRoute = types.QuerierRoute
QueryParameters = types.QueryParameters
QuerySigningInfo = types.QuerySigningInfo
QuerySigningInfos = types.QuerySigningInfos
DefaultParamspace = types.DefaultParamspace
DefaultMaxEvidenceAge = types.DefaultMaxEvidenceAge
DefaultSignedBlocksWindow = types.DefaultSignedBlocksWindow
DefaultDowntimeJailDuration = types.DefaultDowntimeJailDuration
QueryParameters = types.QueryParameters
QuerySigningInfo = types.QuerySigningInfo
QuerySigningInfos = types.QuerySigningInfos
)
var (
// functions aliases
NewKeeper = keeper.NewKeeper
NewQuerier = keeper.NewQuerier
RegisterCodec = types.RegisterCodec
ErrNoValidatorForAddress = types.ErrNoValidatorForAddress
ErrBadValidatorAddr = types.ErrBadValidatorAddr
@ -40,6 +44,7 @@ var (
ErrSelfDelegationTooLowToUnjail = types.ErrSelfDelegationTooLowToUnjail
ErrNoSigningInfoFound = types.ErrNoSigningInfoFound
NewGenesisState = types.NewGenesisState
NewMissedBlock = types.NewMissedBlock
DefaultGenesisState = types.DefaultGenesisState
ValidateGenesis = types.ValidateGenesis
GetValidatorSigningInfoKey = types.GetValidatorSigningInfoKey
@ -73,6 +78,8 @@ var (
)
type (
Hooks = keeper.Hooks
Keeper = keeper.Keeper
CodeType = types.CodeType
GenesisState = types.GenesisState
MissedBlock = types.MissedBlock

View File

@ -1,3 +1,5 @@
// nolint
// DONTCOVER
package slashing
import (
@ -97,7 +99,7 @@ func checkValidator(t *testing.T, mapp *mock.App, keeper staking.Keeper,
func checkValidatorSigningInfo(t *testing.T, mapp *mock.App, keeper Keeper,
addr sdk.ConsAddress, expFound bool) ValidatorSigningInfo {
ctxCheck := mapp.BaseApp.NewContext(true, abci.Header{})
signingInfo, found := keeper.getValidatorSigningInfo(ctxCheck, addr)
signingInfo, found := keeper.GetValidatorSigningInfo(ctxCheck, addr)
require.Equal(t, expFound, found)
return signingInfo
}

View File

@ -11,7 +11,7 @@ import (
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/slashing/types"
"github.com/cosmos/cosmos-sdk/x/slashing/internal/types"
)
// GetQueryCmd returns the cli query commands for this module

View File

@ -9,7 +9,7 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/auth/client/utils"
"github.com/cosmos/cosmos-sdk/x/slashing/types"
"github.com/cosmos/cosmos-sdk/x/slashing/internal/types"
)
// GetTxCmd returns the transaction commands for this module

View File

@ -9,7 +9,7 @@ import (
"github.com/cosmos/cosmos-sdk/client/context"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/rest"
"github.com/cosmos/cosmos-sdk/x/slashing/types"
"github.com/cosmos/cosmos-sdk/x/slashing/internal/types"
)
func registerQueryRoutes(cliCtx context.CLIContext, r *mux.Router) {

View File

@ -10,7 +10,7 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/rest"
"github.com/cosmos/cosmos-sdk/x/auth/client/utils"
"github.com/cosmos/cosmos-sdk/x/slashing/types"
"github.com/cosmos/cosmos-sdk/x/slashing/internal/types"
)
func registerTxRoutes(cliCtx context.CLIContext, r *mux.Router) {

View File

@ -2,7 +2,7 @@ package slashing
import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/slashing/types"
"github.com/cosmos/cosmos-sdk/x/slashing/internal/types"
"github.com/cosmos/cosmos-sdk/x/staking/exported"
)
@ -11,7 +11,7 @@ import (
func InitGenesis(ctx sdk.Context, keeper Keeper, stakingKeeper types.StakingKeeper, data types.GenesisState) {
stakingKeeper.IterateValidators(ctx,
func(index int64, validator exported.ValidatorI) bool {
keeper.addPubkey(ctx, validator.GetConsPubKey())
keeper.AddPubkey(ctx, validator.GetConsPubKey())
return false
},
)
@ -30,20 +30,18 @@ func InitGenesis(ctx sdk.Context, keeper Keeper, stakingKeeper types.StakingKeep
panic(err)
}
for _, missed := range array {
keeper.setValidatorMissedBlockBitArray(ctx, address, missed.Index, missed.Missed)
keeper.SetValidatorMissedBlockBitArray(ctx, address, missed.Index, missed.Missed)
}
}
keeper.paramspace.SetParamSet(ctx, &data.Params)
keeper.SetParams(ctx, data.Params)
}
// ExportGenesis writes the current store values
// to a genesis file, which can be imported again
// with InitGenesis
func ExportGenesis(ctx sdk.Context, keeper Keeper) (data types.GenesisState) {
var params types.Params
keeper.paramspace.GetParamSet(ctx, &params)
params := keeper.GetParams(ctx)
signingInfos := make(map[string]types.ValidatorSigningInfo)
missedBlocks := make(map[string][]types.MissedBlock)
keeper.IterateValidatorSigningInfos(ctx, func(address sdk.ConsAddress, info types.ValidatorSigningInfo) (stop bool) {
@ -52,7 +50,7 @@ func ExportGenesis(ctx sdk.Context, keeper Keeper) (data types.GenesisState) {
localMissedBlocks := []types.MissedBlock{}
keeper.IterateValidatorMissedBlockBitArray(ctx, address, func(index int64, missed bool) (stop bool) {
localMissedBlocks = append(localMissedBlocks, types.MissedBlock{index, missed})
localMissedBlocks = append(localMissedBlocks, types.NewMissedBlock(index, missed))
return false
})
missedBlocks[bechAddr] = localMissedBlocks
@ -60,9 +58,5 @@ func ExportGenesis(ctx sdk.Context, keeper Keeper) (data types.GenesisState) {
return false
})
return types.GenesisState{
Params: params,
SigningInfos: signingInfos,
MissedBlocks: missedBlocks,
}
return types.NewGenesisState(params, signingInfos, missedBlocks)
}

View File

@ -4,9 +4,10 @@ import (
"fmt"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/slashing/types"
"github.com/cosmos/cosmos-sdk/x/slashing/internal/types"
)
// NewHandler creates an sdk.Handler for all the slashing type messages
func NewHandler(k Keeper) sdk.Handler {
return func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
ctx = ctx.WithEventManager(sdk.NewEventManager())
@ -25,45 +26,12 @@ 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 := k.sk.Validator(ctx, msg.ValidatorAddr)
if validator == nil {
return ErrNoValidatorForAddress(k.codespace).Result()
err := k.Unjail(ctx, msg.ValidatorAddr)
if err != nil {
return err.Result()
}
// cannot be unjailed if no self-delegation exists
selfDel := k.sk.Delegation(ctx, sdk.AccAddress(msg.ValidatorAddr), msg.ValidatorAddr)
if selfDel == nil {
return ErrMissingSelfDelegation(k.codespace).Result()
}
if validator.TokensFromShares(selfDel.GetShares()).TruncateInt().LT(validator.GetMinSelfDelegation()) {
return ErrSelfDelegationTooLowToUnjail(k.codespace).Result()
}
// cannot be unjailed if not jailed
if !validator.IsJailed() {
return ErrValidatorNotJailed(k.codespace).Result()
}
consAddr := sdk.ConsAddress(validator.GetConsPubKey().Address())
info, found := k.getValidatorSigningInfo(ctx, consAddr)
if !found {
return ErrNoValidatorForAddress(k.codespace).Result()
}
// cannot be unjailed if tombstoned
if info.Tombstoned {
return ErrValidatorJailed(k.codespace).Result()
}
// cannot be unjailed until out of jail
if ctx.BlockHeader().Time.Before(info.JailedUntil) {
return ErrValidatorJailed(k.codespace).Result()
}
k.sk.Unjail(ctx, consAddr)
ctx.EventManager().EmitEvent(
sdk.NewEvent(
sdk.EventTypeMessage,

View File

@ -9,23 +9,25 @@ import (
abci "github.com/tendermint/tendermint/abci/types"
sdk "github.com/cosmos/cosmos-sdk/types"
slashingkeeper "github.com/cosmos/cosmos-sdk/x/slashing/internal/keeper"
"github.com/cosmos/cosmos-sdk/x/slashing/internal/types"
"github.com/cosmos/cosmos-sdk/x/staking"
)
func TestCannotUnjailUnlessJailed(t *testing.T) {
// initial setup
ctx, ck, sk, _, keeper := createTestInput(t, DefaultParams())
ctx, ck, sk, _, keeper := slashingkeeper.CreateTestInput(t, DefaultParams())
slh := NewHandler(keeper)
amt := sdk.TokensFromConsensusPower(100)
addr, val := addrs[0], pks[0]
msg := NewTestMsgCreateValidator(addr, val, amt)
addr, val := slashingkeeper.Addrs[0], slashingkeeper.Pks[0]
msg := slashingkeeper.NewTestMsgCreateValidator(addr, val, amt)
got := staking.NewHandler(sk)(ctx, msg)
require.True(t, got.IsOK(), "%v", got)
staking.EndBlocker(ctx, sk)
require.Equal(
t, ck.GetCoins(ctx, sdk.AccAddress(addr)),
sdk.Coins{sdk.NewCoin(sk.GetParams(ctx).BondDenom, initTokens.Sub(amt))},
sdk.Coins{sdk.NewCoin(sk.GetParams(ctx).BondDenom, slashingkeeper.InitTokens.Sub(amt))},
)
require.Equal(t, amt, sk.Validator(ctx, addr).GetBondedTokens())
@ -38,11 +40,11 @@ func TestCannotUnjailUnlessJailed(t *testing.T) {
func TestCannotUnjailUnlessMeetMinSelfDelegation(t *testing.T) {
// initial setup
ctx, ck, sk, _, keeper := createTestInput(t, DefaultParams())
ctx, ck, sk, _, keeper := slashingkeeper.CreateTestInput(t, DefaultParams())
slh := NewHandler(keeper)
amtInt := int64(100)
addr, val, amt := addrs[0], pks[0], sdk.TokensFromConsensusPower(amtInt)
msg := NewTestMsgCreateValidator(addr, val, amt)
addr, val, amt := slashingkeeper.Addrs[0], slashingkeeper.Pks[0], sdk.TokensFromConsensusPower(amtInt)
msg := slashingkeeper.NewTestMsgCreateValidator(addr, val, amt)
msg.MinSelfDelegation = amt
got := staking.NewHandler(sk)(ctx, msg)
require.True(t, got.IsOK())
@ -50,7 +52,7 @@ func TestCannotUnjailUnlessMeetMinSelfDelegation(t *testing.T) {
require.Equal(
t, ck.GetCoins(ctx, sdk.AccAddress(addr)),
sdk.Coins{sdk.NewCoin(sk.GetParams(ctx).BondDenom, initTokens.Sub(amt))},
sdk.Coins{sdk.NewCoin(sk.GetParams(ctx).BondDenom, slashingkeeper.InitTokens.Sub(amt))},
)
unbondAmt := sdk.NewCoin(sk.GetParams(ctx).BondDenom, sdk.OneInt())
@ -67,7 +69,7 @@ func TestCannotUnjailUnlessMeetMinSelfDelegation(t *testing.T) {
}
func TestJailedValidatorDelegations(t *testing.T) {
ctx, _, stakingKeeper, _, slashingKeeper := createTestInput(t, DefaultParams())
ctx, _, stakingKeeper, _, slashingKeeper := slashingkeeper.CreateTestInput(t, DefaultParams())
stakingParams := stakingKeeper.GetParams(ctx)
stakingParams.UnbondingTime = 0
@ -75,10 +77,10 @@ func TestJailedValidatorDelegations(t *testing.T) {
// create a validator
bondAmount := sdk.TokensFromConsensusPower(10)
valPubKey := pks[0]
valAddr, consAddr := addrs[1], sdk.ConsAddress(addrs[0])
valPubKey := slashingkeeper.Pks[0]
valAddr, consAddr := slashingkeeper.Addrs[1], sdk.ConsAddress(slashingkeeper.Addrs[0])
msgCreateVal := NewTestMsgCreateValidator(valAddr, valPubKey, bondAmount)
msgCreateVal := slashingkeeper.NewTestMsgCreateValidator(valAddr, valPubKey, bondAmount)
got := staking.NewHandler(stakingKeeper)(ctx, msgCreateVal)
require.True(t, got.IsOK(), "expected create validator msg to be ok, got: %v", got)
@ -90,8 +92,8 @@ func TestJailedValidatorDelegations(t *testing.T) {
slashingKeeper.SetValidatorSigningInfo(ctx, consAddr, newInfo)
// delegate tokens to the validator
delAddr := sdk.AccAddress(addrs[2])
msgDelegate := newTestMsgDelegate(delAddr, valAddr, bondAmount)
delAddr := sdk.AccAddress(slashingkeeper.Addrs[2])
msgDelegate := slashingkeeper.NewTestMsgDelegate(delAddr, valAddr, bondAmount)
got = staking.NewHandler(stakingKeeper)(ctx, msgDelegate)
require.True(t, got.IsOK(), "expected delegation to be ok, got %v", got)
@ -115,7 +117,7 @@ func TestJailedValidatorDelegations(t *testing.T) {
require.False(t, got.IsOK(), "expected jailed validator to not be able to unjail, got: %v", got)
// self-delegate to validator
msgSelfDelegate := newTestMsgDelegate(sdk.AccAddress(valAddr), valAddr, bondAmount)
msgSelfDelegate := slashingkeeper.NewTestMsgDelegate(sdk.AccAddress(valAddr), valAddr, bondAmount)
got = staking.NewHandler(stakingKeeper)(ctx, msgSelfDelegate)
require.True(t, got.IsOK(), "expected delegation to not be ok, got %v", got)
@ -132,3 +134,154 @@ func TestInvalidMsg(t *testing.T) {
require.False(t, res.IsOK())
require.True(t, strings.Contains(res.Log, "unrecognized slashing message type"))
}
// Test a validator through uptime, downtime, revocation,
// unrevocation, starting height reset, and revocation again
func TestHandleAbsentValidator(t *testing.T) {
// initial setup
ctx, ck, sk, _, keeper := slashingkeeper.CreateTestInput(t, slashingkeeper.TestParams())
power := int64(100)
amt := sdk.TokensFromConsensusPower(power)
addr, val := slashingkeeper.Addrs[0], slashingkeeper.Pks[0]
sh := staking.NewHandler(sk)
slh := NewHandler(keeper)
got := sh(ctx, slashingkeeper.NewTestMsgCreateValidator(addr, val, amt))
require.True(t, got.IsOK())
staking.EndBlocker(ctx, sk)
require.Equal(
t, ck.GetCoins(ctx, sdk.AccAddress(addr)),
sdk.NewCoins(sdk.NewCoin(sk.GetParams(ctx).BondDenom, slashingkeeper.InitTokens.Sub(amt))),
)
require.Equal(t, amt, sk.Validator(ctx, addr).GetBondedTokens())
// will exist since the validator has been bonded
info, found := keeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address()))
require.True(t, found)
require.Equal(t, int64(0), info.StartHeight)
require.Equal(t, int64(0), info.IndexOffset)
require.Equal(t, int64(0), info.MissedBlocksCounter)
require.Equal(t, time.Unix(0, 0).UTC(), info.JailedUntil)
height := int64(0)
// 1000 first blocks OK
for ; height < keeper.SignedBlocksWindow(ctx); height++ {
ctx = ctx.WithBlockHeight(height)
keeper.HandleValidatorSignature(ctx, val.Address(), power, true)
}
info, found = keeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address()))
require.True(t, found)
require.Equal(t, int64(0), info.StartHeight)
require.Equal(t, int64(0), info.MissedBlocksCounter)
// 500 blocks missed
for ; height < keeper.SignedBlocksWindow(ctx)+(keeper.SignedBlocksWindow(ctx)-keeper.MinSignedPerWindow(ctx)); height++ {
ctx = ctx.WithBlockHeight(height)
keeper.HandleValidatorSignature(ctx, val.Address(), power, false)
}
info, found = keeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address()))
require.True(t, found)
require.Equal(t, int64(0), info.StartHeight)
require.Equal(t, keeper.SignedBlocksWindow(ctx)-keeper.MinSignedPerWindow(ctx), info.MissedBlocksCounter)
// validator should be bonded still
validator, _ := sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val))
require.Equal(t, sdk.Bonded, validator.GetStatus())
bondPool := sk.GetBondedPool(ctx)
require.True(sdk.IntEq(t, amt, bondPool.GetCoins().AmountOf(sk.BondDenom(ctx))))
// 501st block missed
ctx = ctx.WithBlockHeight(height)
keeper.HandleValidatorSignature(ctx, val.Address(), power, false)
info, found = keeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address()))
require.True(t, found)
require.Equal(t, int64(0), info.StartHeight)
// counter now reset to zero
require.Equal(t, int64(0), info.MissedBlocksCounter)
// end block
staking.EndBlocker(ctx, sk)
// validator should have been jailed
validator, _ = sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val))
require.Equal(t, sdk.Unbonding, validator.GetStatus())
slashAmt := amt.ToDec().Mul(keeper.SlashFractionDowntime(ctx)).RoundInt64()
// validator should have been slashed
require.Equal(t, amt.Int64()-slashAmt, validator.GetTokens().Int64())
// 502nd block *also* missed (since the LastCommit would have still included the just-unbonded validator)
height++
ctx = ctx.WithBlockHeight(height)
keeper.HandleValidatorSignature(ctx, val.Address(), power, false)
info, found = keeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address()))
require.True(t, found)
require.Equal(t, int64(0), info.StartHeight)
require.Equal(t, int64(1), info.MissedBlocksCounter)
// end block
staking.EndBlocker(ctx, sk)
// validator should not have been slashed any more, since it was already jailed
validator, _ = sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val))
require.Equal(t, amt.Int64()-slashAmt, validator.GetTokens().Int64())
// unrevocation should fail prior to jail expiration
got = slh(ctx, types.NewMsgUnjail(addr))
require.False(t, got.IsOK())
// unrevocation should succeed after jail expiration
ctx = ctx.WithBlockHeader(abci.Header{Time: time.Unix(1, 0).Add(keeper.DowntimeJailDuration(ctx))})
got = slh(ctx, types.NewMsgUnjail(addr))
require.True(t, got.IsOK())
// end block
staking.EndBlocker(ctx, sk)
// validator should be rebonded now
validator, _ = sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val))
require.Equal(t, sdk.Bonded, validator.GetStatus())
// validator should have been slashed
bondPool = sk.GetBondedPool(ctx)
require.Equal(t, amt.Int64()-slashAmt, bondPool.GetCoins().AmountOf(sk.BondDenom(ctx)).Int64())
// Validator start height should not have been changed
info, found = keeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address()))
require.True(t, found)
require.Equal(t, int64(0), info.StartHeight)
// 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, int64(1), info.MissedBlocksCounter)
// validator should not be immediately jailed again
height++
ctx = ctx.WithBlockHeight(height)
keeper.HandleValidatorSignature(ctx, val.Address(), power, false)
validator, _ = sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val))
require.Equal(t, sdk.Bonded, validator.GetStatus())
// 500 signed blocks
nextHeight := height + keeper.MinSignedPerWindow(ctx) + 1
for ; height < nextHeight; height++ {
ctx = ctx.WithBlockHeight(height)
keeper.HandleValidatorSignature(ctx, val.Address(), power, false)
}
// end block
staking.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(), power, false)
}
// end block
staking.EndBlocker(ctx, sk)
validator, _ = sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val))
require.Equal(t, sdk.Unbonding, validator.GetStatus())
}

View File

@ -1,5 +1,5 @@
// nolint
package slashing
package keeper
import (
"time"
@ -7,12 +7,12 @@ import (
"github.com/tendermint/tendermint/crypto"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/slashing/types"
"github.com/cosmos/cosmos-sdk/x/slashing/internal/types"
)
func (k Keeper) AfterValidatorBonded(ctx sdk.Context, address sdk.ConsAddress, _ sdk.ValAddress) {
// Update the signing info start height or create a new signing info
_, found := k.getValidatorSigningInfo(ctx, address)
_, found := k.GetValidatorSigningInfo(ctx, address)
if !found {
signingInfo := types.NewValidatorSigningInfo(
address,
@ -29,7 +29,7 @@ func (k Keeper) AfterValidatorBonded(ctx sdk.Context, address sdk.ConsAddress, _
// When a validator is created, add the address-pubkey relation.
func (k Keeper) AfterValidatorCreated(ctx sdk.Context, valAddr sdk.ValAddress) {
validator := k.sk.Validator(ctx, valAddr)
k.addPubkey(ctx, validator.GetConsPubKey())
k.AddPubkey(ctx, validator.GetConsPubKey())
}
// When a validator is removed, delete the address-pubkey relation.

View File

@ -1,47 +1,16 @@
package slashing
package keeper
import (
"fmt"
"time"
"github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/libs/log"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/params"
"github.com/cosmos/cosmos-sdk/x/slashing/types"
"github.com/cosmos/cosmos-sdk/x/slashing/internal/types"
)
// Keeper of the slashing store
type Keeper struct {
storeKey sdk.StoreKey
cdc *codec.Codec
sk types.StakingKeeper
paramspace params.Subspace
// codespace
codespace sdk.CodespaceType
}
// NewKeeper creates a slashing keeper
func NewKeeper(cdc *codec.Codec, key sdk.StoreKey, sk types.StakingKeeper, paramspace params.Subspace, codespace sdk.CodespaceType) Keeper {
keeper := Keeper{
storeKey: key,
cdc: cdc,
sk: sk,
paramspace: paramspace.WithKeyTable(ParamKeyTable()),
codespace: codespace,
}
return keeper
}
// Logger returns a module-specific logger.
func (k Keeper) Logger(ctx sdk.Context) log.Logger {
return ctx.Logger().With("module", fmt.Sprintf("x/%s", types.ModuleName))
}
// handle a validator signing two blocks at the same height
// HandleDoubleSign handles a validator signing two blocks at the same height.
// power: power of the double-signing validator at the height of infraction
func (k Keeper) HandleDoubleSign(ctx sdk.Context, addr crypto.Address, infractionHeight int64, timestamp time.Time, power int64) {
logger := k.Logger(ctx)
@ -52,7 +21,7 @@ func (k Keeper) HandleDoubleSign(ctx sdk.Context, addr crypto.Address, infractio
// fetch the validator public key
consAddr := sdk.ConsAddress(addr)
pubkey, err := k.getPubkey(ctx, addr)
pubkey, err := k.GetPubkey(ctx, addr)
if err != nil {
// Ignore evidence that cannot be handled.
// NOTE:
@ -83,7 +52,7 @@ func (k Keeper) HandleDoubleSign(ctx sdk.Context, addr crypto.Address, infractio
}
// fetch the validator signing info
signInfo, found := k.getValidatorSigningInfo(ctx, consAddr)
signInfo, found := k.GetValidatorSigningInfo(ctx, consAddr)
if !found {
panic(fmt.Sprintf("Expected signing info for validator %s but not found", consAddr))
}
@ -143,19 +112,20 @@ func (k Keeper) HandleDoubleSign(ctx sdk.Context, addr crypto.Address, infractio
k.SetValidatorSigningInfo(ctx, consAddr, signInfo)
}
// handle a validator signature, must be called once per validator per block
// TODO refactor to take in a consensus address, additionally should maybe just take in the pubkey too
// HandleValidatorSignature handles a validator signature, must be called once per validator per block.
func (k Keeper) HandleValidatorSignature(ctx sdk.Context, addr crypto.Address, power int64, signed bool) {
logger := k.Logger(ctx)
height := ctx.BlockHeight()
// fetch the validator public key
consAddr := sdk.ConsAddress(addr)
pubkey, err := k.getPubkey(ctx, addr)
pubkey, err := k.GetPubkey(ctx, addr)
if err != nil {
panic(fmt.Sprintf("Validator consensus-address %s not found", consAddr))
}
// fetch signing info
signInfo, found := k.getValidatorSigningInfo(ctx, consAddr)
signInfo, found := k.GetValidatorSigningInfo(ctx, consAddr)
if !found {
panic(fmt.Sprintf("Expected signing info for validator %s but not found", consAddr))
}
@ -168,16 +138,16 @@ func (k Keeper) HandleValidatorSignature(ctx sdk.Context, addr crypto.Address, p
// Update signed block bit array & counter
// This counter just tracks the sum of the bit array
// That way we avoid needing to read/write the whole array each time
previous := k.getValidatorMissedBlockBitArray(ctx, consAddr, index)
previous := k.GetValidatorMissedBlockBitArray(ctx, consAddr, index)
missed := !signed
switch {
case !previous && missed:
// Array value has changed from not missed to missed, increment counter
k.setValidatorMissedBlockBitArray(ctx, consAddr, index, true)
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)
k.SetValidatorMissedBlockBitArray(ctx, consAddr, index, false)
signInfo.MissedBlocksCounter--
default:
// Array value at this index has not changed, no need to update counter
@ -245,29 +215,3 @@ func (k Keeper) HandleValidatorSignature(ctx sdk.Context, addr crypto.Address, p
// Set the updated signing info
k.SetValidatorSigningInfo(ctx, consAddr, signInfo)
}
func (k Keeper) addPubkey(ctx sdk.Context, pubkey crypto.PubKey) {
addr := pubkey.Address()
k.setAddrPubkeyRelation(ctx, addr, pubkey)
}
func (k Keeper) getPubkey(ctx sdk.Context, address crypto.Address) (crypto.PubKey, error) {
store := ctx.KVStore(k.storeKey)
var pubkey crypto.PubKey
err := k.cdc.UnmarshalBinaryLengthPrefixed(store.Get(types.GetAddrPubkeyRelationKey(address)), &pubkey)
if err != nil {
return nil, fmt.Errorf("address %s not found", sdk.ConsAddress(address))
}
return pubkey, nil
}
func (k Keeper) setAddrPubkeyRelation(ctx sdk.Context, addr crypto.Address, pubkey crypto.PubKey) {
store := ctx.KVStore(k.storeKey)
bz := k.cdc.MustMarshalBinaryLengthPrefixed(pubkey)
store.Set(types.GetAddrPubkeyRelationKey(addr), bz)
}
func (k Keeper) deleteAddrPubkeyRelation(ctx sdk.Context, addr crypto.Address) {
store := ctx.KVStore(k.storeKey)
store.Delete(types.GetAddrPubkeyRelationKey(addr))
}

View File

@ -0,0 +1,66 @@
package keeper
import (
"fmt"
"github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/libs/log"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/slashing/internal/types"
)
// Keeper of the slashing store
type Keeper struct {
storeKey sdk.StoreKey
cdc *codec.Codec
sk types.StakingKeeper
paramspace types.ParamSubspace
codespace sdk.CodespaceType
}
// NewKeeper creates a slashing keeper
func NewKeeper(cdc *codec.Codec, key sdk.StoreKey, sk types.StakingKeeper, paramspace types.ParamSubspace, codespace sdk.CodespaceType) Keeper {
keeper := Keeper{
storeKey: key,
cdc: cdc,
sk: sk,
paramspace: paramspace.WithKeyTable(types.ParamKeyTable()),
codespace: codespace,
}
return keeper
}
// Logger returns a module-specific logger.
func (k Keeper) Logger(ctx sdk.Context) log.Logger {
return ctx.Logger().With("module", fmt.Sprintf("x/%s", types.ModuleName))
}
// AddPubkey sets a address-pubkey relation
func (k Keeper) AddPubkey(ctx sdk.Context, pubkey crypto.PubKey) {
addr := pubkey.Address()
k.setAddrPubkeyRelation(ctx, addr, pubkey)
}
// GetPubkey returns the pubkey from the adddress-pubkey relation
func (k Keeper) GetPubkey(ctx sdk.Context, address crypto.Address) (crypto.PubKey, error) {
store := ctx.KVStore(k.storeKey)
var pubkey crypto.PubKey
err := k.cdc.UnmarshalBinaryLengthPrefixed(store.Get(types.GetAddrPubkeyRelationKey(address)), &pubkey)
if err != nil {
return nil, fmt.Errorf("address %s not found", sdk.ConsAddress(address))
}
return pubkey, nil
}
func (k Keeper) setAddrPubkeyRelation(ctx sdk.Context, addr crypto.Address, pubkey crypto.PubKey) {
store := ctx.KVStore(k.storeKey)
bz := k.cdc.MustMarshalBinaryLengthPrefixed(pubkey)
store.Set(types.GetAddrPubkeyRelationKey(addr), bz)
}
func (k Keeper) deleteAddrPubkeyRelation(ctx sdk.Context, addr crypto.Address) {
store := ctx.KVStore(k.storeKey)
store.Delete(types.GetAddrPubkeyRelationKey(addr))
}

View File

@ -1,4 +1,4 @@
package slashing
package keeper
import (
"testing"
@ -8,19 +8,10 @@ import (
abci "github.com/tendermint/tendermint/abci/types"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/slashing/types"
"github.com/cosmos/cosmos-sdk/x/slashing/internal/types"
"github.com/cosmos/cosmos-sdk/x/staking"
)
// Have to change these parameters for tests
// lest the tests take forever
func keeperTestParams() types.Params {
params := types.DefaultParams()
params.SignedBlocksWindow = 1000
params.DowntimeJailDuration = 60 * 60
return params
}
// ______________________________________________________________
// Test that a validator is slashed correctly
@ -28,18 +19,18 @@ func keeperTestParams() types.Params {
func TestHandleDoubleSign(t *testing.T) {
// initial setup
ctx, ck, sk, _, keeper := createTestInput(t, keeperTestParams())
ctx, ck, sk, _, keeper := CreateTestInput(t, TestParams())
// validator added pre-genesis
ctx = ctx.WithBlockHeight(-1)
power := int64(100)
amt := sdk.TokensFromConsensusPower(power)
operatorAddr, val := addrs[0], pks[0]
operatorAddr, val := Addrs[0], Pks[0]
got := staking.NewHandler(sk)(ctx, NewTestMsgCreateValidator(operatorAddr, val, amt))
require.True(t, got.IsOK())
staking.EndBlocker(ctx, sk)
require.Equal(
t, ck.GetCoins(ctx, sdk.AccAddress(operatorAddr)),
sdk.NewCoins(sdk.NewCoin(sk.GetParams(ctx).BondDenom, initTokens.Sub(amt))),
sdk.NewCoins(sdk.NewCoin(sk.GetParams(ctx).BondDenom, InitTokens.Sub(amt))),
)
require.Equal(t, amt, sk.Validator(ctx, operatorAddr).GetBondedTokens())
@ -68,9 +59,7 @@ func TestHandleDoubleSign(t *testing.T) {
ctx = ctx.WithBlockHeader(abci.Header{Time: time.Unix(1, 0).Add(sk.GetParams(ctx).UnbondingTime)})
// Still shouldn't be able to unjail
msgUnjail := types.NewMsgUnjail(operatorAddr)
res := handleMsgUnjail(ctx, msgUnjail, keeper)
require.False(t, res.IsOK())
require.Error(t, keeper.Unjail(ctx, operatorAddr))
// Should be able to unbond now
del, _ := sk.GetDelegation(ctx, sdk.AccAddress(operatorAddr), operatorAddr)
@ -78,7 +67,7 @@ func TestHandleDoubleSign(t *testing.T) {
totalBond := validator.TokensFromShares(del.GetShares()).TruncateInt()
msgUnbond := staking.NewMsgUndelegate(sdk.AccAddress(operatorAddr), operatorAddr, sdk.NewCoin(sk.GetParams(ctx).BondDenom, totalBond))
res = staking.NewHandler(sk)(ctx, msgUnbond)
res := staking.NewHandler(sk)(ctx, msgUnbond)
require.True(t, res.IsOK())
}
@ -89,18 +78,18 @@ func TestHandleDoubleSign(t *testing.T) {
func TestPastMaxEvidenceAge(t *testing.T) {
// initial setup
ctx, ck, sk, _, keeper := createTestInput(t, keeperTestParams())
ctx, ck, sk, _, keeper := CreateTestInput(t, TestParams())
// validator added pre-genesis
ctx = ctx.WithBlockHeight(-1)
power := int64(100)
amt := sdk.TokensFromConsensusPower(power)
operatorAddr, val := addrs[0], pks[0]
operatorAddr, val := Addrs[0], Pks[0]
got := staking.NewHandler(sk)(ctx, NewTestMsgCreateValidator(operatorAddr, val, amt))
require.True(t, got.IsOK())
staking.EndBlocker(ctx, sk)
require.Equal(
t, ck.GetCoins(ctx, sdk.AccAddress(operatorAddr)),
sdk.NewCoins(sdk.NewCoin(sk.GetParams(ctx).BondDenom, initTokens.Sub(amt))),
sdk.NewCoins(sdk.NewCoin(sk.GetParams(ctx).BondDenom, InitTokens.Sub(amt))),
)
require.Equal(t, amt, sk.Validator(ctx, operatorAddr).GetBondedTokens())
@ -121,164 +110,13 @@ func TestPastMaxEvidenceAge(t *testing.T) {
require.Equal(t, oldPower, sk.Validator(ctx, operatorAddr).GetConsensusPower())
}
// Test a validator through uptime, downtime, revocation,
// unrevocation, starting height reset, and revocation again
func TestHandleAbsentValidator(t *testing.T) {
// initial setup
ctx, ck, sk, _, keeper := createTestInput(t, keeperTestParams())
power := int64(100)
amt := sdk.TokensFromConsensusPower(power)
addr, val := addrs[0], pks[0]
sh := staking.NewHandler(sk)
slh := NewHandler(keeper)
got := sh(ctx, NewTestMsgCreateValidator(addr, val, amt))
require.True(t, got.IsOK())
staking.EndBlocker(ctx, sk)
require.Equal(
t, ck.GetCoins(ctx, sdk.AccAddress(addr)),
sdk.NewCoins(sdk.NewCoin(sk.GetParams(ctx).BondDenom, initTokens.Sub(amt))),
)
require.Equal(t, amt, sk.Validator(ctx, addr).GetBondedTokens())
// will exist since the validator has been bonded
info, found := keeper.getValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address()))
require.True(t, found)
require.Equal(t, int64(0), info.StartHeight)
require.Equal(t, int64(0), info.IndexOffset)
require.Equal(t, int64(0), info.MissedBlocksCounter)
require.Equal(t, time.Unix(0, 0).UTC(), info.JailedUntil)
height := int64(0)
// 1000 first blocks OK
for ; height < keeper.SignedBlocksWindow(ctx); height++ {
ctx = ctx.WithBlockHeight(height)
keeper.HandleValidatorSignature(ctx, val.Address(), power, true)
}
info, found = keeper.getValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address()))
require.True(t, found)
require.Equal(t, int64(0), info.StartHeight)
require.Equal(t, int64(0), info.MissedBlocksCounter)
// 500 blocks missed
for ; height < keeper.SignedBlocksWindow(ctx)+(keeper.SignedBlocksWindow(ctx)-keeper.MinSignedPerWindow(ctx)); height++ {
ctx = ctx.WithBlockHeight(height)
keeper.HandleValidatorSignature(ctx, val.Address(), power, false)
}
info, found = keeper.getValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address()))
require.True(t, found)
require.Equal(t, int64(0), info.StartHeight)
require.Equal(t, keeper.SignedBlocksWindow(ctx)-keeper.MinSignedPerWindow(ctx), info.MissedBlocksCounter)
// validator should be bonded still
validator, _ := sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val))
require.Equal(t, sdk.Bonded, validator.GetStatus())
bondPool := sk.GetBondedPool(ctx)
require.True(sdk.IntEq(t, amt, bondPool.GetCoins().AmountOf(sk.BondDenom(ctx))))
// 501st block missed
ctx = ctx.WithBlockHeight(height)
keeper.HandleValidatorSignature(ctx, val.Address(), power, false)
info, found = keeper.getValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address()))
require.True(t, found)
require.Equal(t, int64(0), info.StartHeight)
// counter now reset to zero
require.Equal(t, int64(0), info.MissedBlocksCounter)
// end block
staking.EndBlocker(ctx, sk)
// validator should have been jailed
validator, _ = sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val))
require.Equal(t, sdk.Unbonding, validator.GetStatus())
slashAmt := amt.ToDec().Mul(keeper.SlashFractionDowntime(ctx)).RoundInt64()
// validator should have been slashed
require.Equal(t, amt.Int64()-slashAmt, validator.GetTokens().Int64())
// 502nd block *also* missed (since the LastCommit would have still included the just-unbonded validator)
height++
ctx = ctx.WithBlockHeight(height)
keeper.HandleValidatorSignature(ctx, val.Address(), power, false)
info, found = keeper.getValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address()))
require.True(t, found)
require.Equal(t, int64(0), info.StartHeight)
require.Equal(t, int64(1), info.MissedBlocksCounter)
// end block
staking.EndBlocker(ctx, sk)
// validator should not have been slashed any more, since it was already jailed
validator, _ = sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val))
require.Equal(t, amt.Int64()-slashAmt, validator.GetTokens().Int64())
// unrevocation should fail prior to jail expiration
got = slh(ctx, NewMsgUnjail(addr))
require.False(t, got.IsOK())
// unrevocation should succeed after jail expiration
ctx = ctx.WithBlockHeader(abci.Header{Time: time.Unix(1, 0).Add(keeper.DowntimeJailDuration(ctx))})
got = slh(ctx, NewMsgUnjail(addr))
require.True(t, got.IsOK())
// end block
staking.EndBlocker(ctx, sk)
// validator should be rebonded now
validator, _ = sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val))
require.Equal(t, sdk.Bonded, validator.GetStatus())
// validator should have been slashed
bondPool = sk.GetBondedPool(ctx)
require.Equal(t, amt.Int64()-slashAmt, bondPool.GetCoins().AmountOf(sk.BondDenom(ctx)).Int64())
// Validator start height should not have been changed
info, found = keeper.getValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address()))
require.True(t, found)
require.Equal(t, int64(0), info.StartHeight)
// 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, int64(1), info.MissedBlocksCounter)
// validator should not be immediately jailed again
height++
ctx = ctx.WithBlockHeight(height)
keeper.HandleValidatorSignature(ctx, val.Address(), power, false)
validator, _ = sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val))
require.Equal(t, sdk.Bonded, validator.GetStatus())
// 500 signed blocks
nextHeight := height + keeper.MinSignedPerWindow(ctx) + 1
for ; height < nextHeight; height++ {
ctx = ctx.WithBlockHeight(height)
keeper.HandleValidatorSignature(ctx, val.Address(), power, false)
}
// end block
staking.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(), power, false)
}
// end block
staking.EndBlocker(ctx, sk)
validator, _ = sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val))
require.Equal(t, sdk.Unbonding, validator.GetStatus())
}
// Test a new validator entering the validator set
// Ensure that SigningInfo.StartHeight is set correctly
// and that they are not immediately jailed
func TestHandleNewValidator(t *testing.T) {
// initial setup
ctx, ck, sk, _, keeper := createTestInput(t, keeperTestParams())
addr, val := addrs[0], pks[0]
ctx, ck, sk, _, keeper := CreateTestInput(t, TestParams())
addr, val := Addrs[0], Pks[0]
amt := sdk.TokensFromConsensusPower(100)
sh := staking.NewHandler(sk)
@ -292,7 +130,7 @@ func TestHandleNewValidator(t *testing.T) {
require.Equal(
t, ck.GetCoins(ctx, sdk.AccAddress(addr)),
sdk.NewCoins(sdk.NewCoin(sk.GetParams(ctx).BondDenom, initTokens.Sub(amt))),
sdk.NewCoins(sdk.NewCoin(sk.GetParams(ctx).BondDenom, InitTokens.Sub(amt))),
)
require.Equal(t, amt, sk.Validator(ctx, addr).GetBondedTokens())
@ -301,7 +139,7 @@ func TestHandleNewValidator(t *testing.T) {
ctx = ctx.WithBlockHeight(keeper.SignedBlocksWindow(ctx) + 2)
keeper.HandleValidatorSignature(ctx, val.Address(), 100, false)
info, found := keeper.getValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address()))
info, found := keeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address()))
require.True(t, found)
require.Equal(t, keeper.SignedBlocksWindow(ctx)+1, info.StartHeight)
require.Equal(t, int64(2), info.IndexOffset)
@ -321,10 +159,10 @@ func TestHandleNewValidator(t *testing.T) {
func TestHandleAlreadyJailed(t *testing.T) {
// initial setup
ctx, _, sk, _, keeper := createTestInput(t, DefaultParams())
ctx, _, sk, _, keeper := CreateTestInput(t, types.DefaultParams())
power := int64(100)
amt := sdk.TokensFromConsensusPower(power)
addr, val := addrs[0], pks[0]
addr, val := Addrs[0], Pks[0]
sh := staking.NewHandler(sk)
got := sh(ctx, NewTestMsgCreateValidator(addr, val, amt))
require.True(t, got.IsOK())
@ -370,14 +208,14 @@ func TestHandleAlreadyJailed(t *testing.T) {
func TestValidatorDippingInAndOut(t *testing.T) {
// initial setup
// keeperTestParams set the SignedBlocksWindow to 1000 and MaxMissedBlocksPerWindow to 500
ctx, _, sk, _, keeper := createTestInput(t, keeperTestParams())
// TestParams set the SignedBlocksWindow to 1000 and MaxMissedBlocksPerWindow to 500
ctx, _, sk, _, keeper := CreateTestInput(t, TestParams())
params := sk.GetParams(ctx)
params.MaxValidators = 1
sk.SetParams(ctx, params)
power := int64(100)
amt := sdk.TokensFromConsensusPower(power)
addr, val := addrs[0], pks[0]
addr, val := Addrs[0], Pks[0]
consAddr := sdk.ConsAddress(addr)
sh := staking.NewHandler(sk)
got := sh(ctx, NewTestMsgCreateValidator(addr, val, amt))
@ -393,7 +231,7 @@ func TestValidatorDippingInAndOut(t *testing.T) {
// kick first validator out of validator set
newAmt := sdk.TokensFromConsensusPower(101)
got = sh(ctx, NewTestMsgCreateValidator(addrs[1], pks[1], newAmt))
got = sh(ctx, NewTestMsgCreateValidator(Addrs[1], Pks[1], newAmt))
require.True(t, got.IsOK())
validatorUpdates := staking.EndBlocker(ctx, sk)
require.Equal(t, 2, len(validatorUpdates))
@ -406,7 +244,7 @@ func TestValidatorDippingInAndOut(t *testing.T) {
// validator added back in
delTokens := sdk.TokensFromConsensusPower(50)
got = sh(ctx, newTestMsgDelegate(sdk.AccAddress(addrs[2]), addrs[0], delTokens))
got = sh(ctx, NewTestMsgDelegate(sdk.AccAddress(Addrs[2]), Addrs[0], delTokens))
require.True(t, got.IsOK())
validatorUpdates = staking.EndBlocker(ctx, sk)
require.Equal(t, 2, len(validatorUpdates))
@ -435,13 +273,13 @@ func TestValidatorDippingInAndOut(t *testing.T) {
require.Equal(t, sdk.Unbonding, validator.Status)
// check all the signing information
signInfo, found := keeper.getValidatorSigningInfo(ctx, consAddr)
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)
missed := keeper.GetValidatorMissedBlockBitArray(ctx, consAddr, offset)
require.False(t, missed)
}

View File

@ -1,10 +1,10 @@
package slashing
package keeper
import (
"time"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/slashing/types"
"github.com/cosmos/cosmos-sdk/x/slashing/internal/types"
)
// MaxEvidenceAge - max age for evidence
@ -19,7 +19,7 @@ func (k Keeper) SignedBlocksWindow(ctx sdk.Context) (res int64) {
return
}
// Downtime slashing threshold
// MinSignedPerWindow - minimum blocks signed per window
func (k Keeper) MinSignedPerWindow(ctx sdk.Context) int64 {
var minSignedPerWindow sdk.Dec
k.paramspace.Get(ctx, types.KeyMinSignedPerWindow, &minSignedPerWindow)
@ -30,19 +30,19 @@ func (k Keeper) MinSignedPerWindow(ctx sdk.Context) int64 {
return minSignedPerWindow.MulInt64(signedBlocksWindow).RoundInt64()
}
// Downtime unbond duration
// DowntimeJailDuration - Downtime unbond duration
func (k Keeper) DowntimeJailDuration(ctx sdk.Context) (res time.Duration) {
k.paramspace.Get(ctx, types.KeyDowntimeJailDuration, &res)
return
}
// SlashFractionDoubleSign
// SlashFractionDoubleSign - fraction of power slashed in case of double sign
func (k Keeper) SlashFractionDoubleSign(ctx sdk.Context) (res sdk.Dec) {
k.paramspace.Get(ctx, types.KeySlashFractionDoubleSign, &res)
return
}
// SlashFractionDowntime
// SlashFractionDowntime - fraction of power slashed for downtime
func (k Keeper) SlashFractionDowntime(ctx sdk.Context) (res sdk.Dec) {
k.paramspace.Get(ctx, types.KeySlashFractionDowntime, &res)
return
@ -53,3 +53,8 @@ func (k Keeper) GetParams(ctx sdk.Context) (params types.Params) {
k.paramspace.GetParamSet(ctx, &params)
return params
}
// SetParams sets the slashing parameters to the param space.
func (k Keeper) SetParams(ctx sdk.Context, params types.Params) {
k.paramspace.SetParamSet(ctx, &params)
}

View File

@ -1,4 +1,4 @@
package slashing
package keeper
import (
"fmt"
@ -8,17 +8,18 @@ import (
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/slashing/internal/types"
)
// NewQuerier creates a new querier for slashing clients.
func NewQuerier(k Keeper) sdk.Querier {
return func(ctx sdk.Context, path []string, req abci.RequestQuery) ([]byte, sdk.Error) {
switch path[0] {
case QueryParameters:
case types.QueryParameters:
return queryParams(ctx, k)
case QuerySigningInfo:
case types.QuerySigningInfo:
return querySigningInfo(ctx, req, k)
case QuerySigningInfos:
case types.QuerySigningInfos:
return querySigningInfos(ctx, req, k)
default:
return nil, sdk.ErrUnknownRequest("unknown staking query endpoint")
@ -29,7 +30,7 @@ func NewQuerier(k Keeper) sdk.Querier {
func queryParams(ctx sdk.Context, k Keeper) ([]byte, sdk.Error) {
params := k.GetParams(ctx)
res, err := codec.MarshalJSONIndent(ModuleCdc, params)
res, err := codec.MarshalJSONIndent(types.ModuleCdc, params)
if err != nil {
return nil, sdk.ErrInternal(sdk.AppendMsgToErr("failed to marshal JSON", err.Error()))
}
@ -38,19 +39,19 @@ func queryParams(ctx sdk.Context, k Keeper) ([]byte, sdk.Error) {
}
func querySigningInfo(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, sdk.Error) {
var params QuerySigningInfoParams
var params types.QuerySigningInfoParams
err := ModuleCdc.UnmarshalJSON(req.Data, &params)
err := types.ModuleCdc.UnmarshalJSON(req.Data, &params)
if err != nil {
return nil, sdk.ErrInternal(fmt.Sprintf("failed to parse params: %s", err))
}
signingInfo, found := k.getValidatorSigningInfo(ctx, params.ConsAddress)
signingInfo, found := k.GetValidatorSigningInfo(ctx, params.ConsAddress)
if !found {
return nil, ErrNoSigningInfoFound(DefaultCodespace, params.ConsAddress)
return nil, types.ErrNoSigningInfoFound(types.DefaultCodespace, params.ConsAddress)
}
res, err := codec.MarshalJSONIndent(ModuleCdc, signingInfo)
res, err := codec.MarshalJSONIndent(types.ModuleCdc, signingInfo)
if err != nil {
return nil, sdk.ErrInternal(sdk.AppendMsgToErr("failed to JSON marshal result: %s", err.Error()))
}
@ -59,28 +60,28 @@ func querySigningInfo(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte,
}
func querySigningInfos(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, sdk.Error) {
var params QuerySigningInfosParams
var params types.QuerySigningInfosParams
err := ModuleCdc.UnmarshalJSON(req.Data, &params)
err := types.ModuleCdc.UnmarshalJSON(req.Data, &params)
if err != nil {
return nil, sdk.ErrInternal(fmt.Sprintf("failed to parse params: %s", err))
}
var signingInfos []ValidatorSigningInfo
var signingInfos []types.ValidatorSigningInfo
k.IterateValidatorSigningInfos(ctx, func(consAddr sdk.ConsAddress, info ValidatorSigningInfo) (stop bool) {
k.IterateValidatorSigningInfos(ctx, func(consAddr sdk.ConsAddress, info types.ValidatorSigningInfo) (stop bool) {
signingInfos = append(signingInfos, info)
return false
})
start, end := client.Paginate(len(signingInfos), params.Page, params.Limit, int(k.sk.MaxValidators(ctx)))
if start < 0 || end < 0 {
signingInfos = []ValidatorSigningInfo{}
signingInfos = []types.ValidatorSigningInfo{}
} else {
signingInfos = signingInfos[start:end]
}
res, err := codec.MarshalJSONIndent(ModuleCdc, signingInfos)
res, err := codec.MarshalJSONIndent(types.ModuleCdc, signingInfos)
if err != nil {
return nil, sdk.ErrInternal(sdk.AppendMsgToErr("failed to JSON marshal result: %s", err.Error()))
}

View File

@ -1,4 +1,4 @@
package slashing
package keeper
import (
"testing"
@ -7,10 +7,11 @@ import (
abci "github.com/tendermint/tendermint/abci/types"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/x/slashing/internal/types"
)
func TestNewQuerier(t *testing.T) {
ctx, _, _, _, keeper := createTestInput(t, keeperTestParams())
ctx, _, _, _, keeper := CreateTestInput(t, TestParams())
querier := NewQuerier(keeper)
query := abci.RequestQuery{
@ -24,9 +25,9 @@ func TestNewQuerier(t *testing.T) {
func TestQueryParams(t *testing.T) {
cdc := codec.New()
ctx, _, _, _, keeper := createTestInput(t, keeperTestParams())
ctx, _, _, _, keeper := CreateTestInput(t, TestParams())
var params Params
var params types.Params
res, errRes := queryParams(ctx, keeper)
require.NoError(t, errRes)

View File

@ -1,12 +1,13 @@
package slashing
package keeper
import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/slashing/types"
"github.com/cosmos/cosmos-sdk/x/slashing/internal/types"
)
// Stored by *validator* address (not operator address)
func (k Keeper) getValidatorSigningInfo(ctx sdk.Context, address sdk.ConsAddress) (info types.ValidatorSigningInfo, found bool) {
// GetValidatorSigningInfo retruns the ValidatorSigningInfo for a specific validator
// ConsAddress
func (k Keeper) GetValidatorSigningInfo(ctx sdk.Context, address sdk.ConsAddress) (info types.ValidatorSigningInfo, found bool) {
store := ctx.KVStore(k.storeKey)
bz := store.Get(types.GetValidatorSigningInfoKey(address))
if bz == nil {
@ -18,7 +19,15 @@ func (k Keeper) getValidatorSigningInfo(ctx sdk.Context, address sdk.ConsAddress
return
}
// Stored by *validator* address (not operator address)
// SetValidatorSigningInfo sets the validator signing info to a consensus address key
func (k Keeper) SetValidatorSigningInfo(ctx sdk.Context, address sdk.ConsAddress, info types.ValidatorSigningInfo) {
store := ctx.KVStore(k.storeKey)
bz := k.cdc.MustMarshalBinaryLengthPrefixed(info)
store.Set(types.GetValidatorSigningInfoKey(address), bz)
}
// IterateValidatorSigningInfos iterates over the stored ValidatorSigningInfo
func (k Keeper) IterateValidatorSigningInfos(ctx sdk.Context,
handler func(address sdk.ConsAddress, info types.ValidatorSigningInfo) (stop bool)) {
@ -35,15 +44,8 @@ func (k Keeper) IterateValidatorSigningInfos(ctx sdk.Context,
}
}
// Stored by *validator* address (not operator address)
func (k Keeper) SetValidatorSigningInfo(ctx sdk.Context, address sdk.ConsAddress, info types.ValidatorSigningInfo) {
store := ctx.KVStore(k.storeKey)
bz := k.cdc.MustMarshalBinaryLengthPrefixed(info)
store.Set(types.GetValidatorSigningInfoKey(address), bz)
}
// Stored by *validator* address (not operator address)
func (k Keeper) getValidatorMissedBlockBitArray(ctx sdk.Context, address sdk.ConsAddress, index int64) (missed bool) {
// GetValidatorMissedBlockBitArray gets the bit for the missed blocks array
func (k Keeper) GetValidatorMissedBlockBitArray(ctx sdk.Context, address sdk.ConsAddress, index int64) (missed bool) {
store := ctx.KVStore(k.storeKey)
bz := store.Get(types.GetValidatorMissedBlockBitArrayKey(address, index))
if bz == nil {
@ -55,7 +57,8 @@ func (k Keeper) getValidatorMissedBlockBitArray(ctx sdk.Context, address sdk.Con
return
}
// Stored by *validator* address (not operator address)
// IterateValidatorMissedBlockBitArray iterates over the signed blocks window
// and performs a callback function
func (k Keeper) IterateValidatorMissedBlockBitArray(ctx sdk.Context,
address sdk.ConsAddress, handler func(index int64, missed bool) (stop bool)) {
@ -75,14 +78,15 @@ func (k Keeper) IterateValidatorMissedBlockBitArray(ctx sdk.Context,
}
}
// Stored by *validator* address (not operator address)
func (k Keeper) setValidatorMissedBlockBitArray(ctx sdk.Context, address sdk.ConsAddress, index int64, missed bool) {
// SetValidatorMissedBlockBitArray sets the bit that checks if the validator has
// missed a block in the current window
func (k Keeper) SetValidatorMissedBlockBitArray(ctx sdk.Context, address sdk.ConsAddress, index int64, missed bool) {
store := ctx.KVStore(k.storeKey)
bz := k.cdc.MustMarshalBinaryLengthPrefixed(missed)
store.Set(types.GetValidatorMissedBlockBitArrayKey(address, index), bz)
}
// Stored by *validator* address (not operator address)
// clearValidatorMissedBlockBitArray deletes every instance of ValidatorMissedBlockBitArray in the store
func (k Keeper) clearValidatorMissedBlockBitArray(ctx sdk.Context, address sdk.ConsAddress) {
store := ctx.KVStore(k.storeKey)
iter := sdk.KVStorePrefixIterator(store, types.GetValidatorMissedBlockBitArrayPrefixKey(address))

View File

@ -0,0 +1,41 @@
package keeper
import (
"testing"
"time"
"github.com/stretchr/testify/require"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/slashing/internal/types"
)
func TestGetSetValidatorSigningInfo(t *testing.T) {
ctx, _, _, _, keeper := CreateTestInput(t, types.DefaultParams())
info, found := keeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(Addrs[0]))
require.False(t, found)
newInfo := types.NewValidatorSigningInfo(
sdk.ConsAddress(Addrs[0]),
int64(4),
int64(3),
time.Unix(2, 0),
false,
int64(10),
)
keeper.SetValidatorSigningInfo(ctx, sdk.ConsAddress(Addrs[0]), newInfo)
info, found = keeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(Addrs[0]))
require.True(t, found)
require.Equal(t, info.StartHeight, int64(4))
require.Equal(t, info.IndexOffset, int64(3))
require.Equal(t, info.JailedUntil, time.Unix(2, 0).UTC())
require.Equal(t, info.MissedBlocksCounter, int64(10))
}
func TestGetSetValidatorMissedBlockBitArray(t *testing.T) {
ctx, _, _, _, keeper := CreateTestInput(t, types.DefaultParams())
missed := keeper.GetValidatorMissedBlockBitArray(ctx, sdk.ConsAddress(Addrs[0]), 0)
require.False(t, missed) // treat empty key as not missed
keeper.SetValidatorMissedBlockBitArray(ctx, sdk.ConsAddress(Addrs[0]), 0, true)
missed = keeper.GetValidatorMissedBlockBitArray(ctx, sdk.ConsAddress(Addrs[0]), 0)
require.True(t, missed) // now should be missed
}

View File

@ -1,5 +1,7 @@
// nolint:deadcode unused
package slashing
// DONTCOVER
// noalias
package keeper
import (
"encoding/hex"
@ -20,6 +22,7 @@ import (
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/bank"
"github.com/cosmos/cosmos-sdk/x/params"
"github.com/cosmos/cosmos-sdk/x/slashing/internal/types"
"github.com/cosmos/cosmos-sdk/x/staking"
"github.com/cosmos/cosmos-sdk/x/supply"
)
@ -27,18 +30,18 @@ import (
// TODO remove dependencies on staking (should only refer to validator set type from sdk)
var (
pks = []crypto.PubKey{
Pks = []crypto.PubKey{
newPubKey("0B485CFC0EECC619440448436F8FC9DF40566F2369E72400281454CB552AFB50"),
newPubKey("0B485CFC0EECC619440448436F8FC9DF40566F2369E72400281454CB552AFB51"),
newPubKey("0B485CFC0EECC619440448436F8FC9DF40566F2369E72400281454CB552AFB52"),
}
addrs = []sdk.ValAddress{
sdk.ValAddress(pks[0].Address()),
sdk.ValAddress(pks[1].Address()),
sdk.ValAddress(pks[2].Address()),
Addrs = []sdk.ValAddress{
sdk.ValAddress(Pks[0].Address()),
sdk.ValAddress(Pks[1].Address()),
sdk.ValAddress(Pks[2].Address()),
}
initTokens = sdk.TokensFromConsensusPower(200)
initCoins = sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, initTokens))
InitTokens = sdk.TokensFromConsensusPower(200)
initCoins = sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, InitTokens))
)
func createTestCodec() *codec.Codec {
@ -52,11 +55,11 @@ func createTestCodec() *codec.Codec {
return cdc
}
func createTestInput(t *testing.T, defaults Params) (sdk.Context, bank.Keeper, staking.Keeper, params.Subspace, Keeper) {
func CreateTestInput(t *testing.T, defaults types.Params) (sdk.Context, bank.Keeper, staking.Keeper, params.Subspace, Keeper) {
keyAcc := sdk.NewKVStoreKey(auth.StoreKey)
keyStaking := sdk.NewKVStoreKey(staking.StoreKey)
tkeyStaking := sdk.NewTransientStoreKey(staking.TStoreKey)
keySlashing := sdk.NewKVStoreKey(StoreKey)
keySlashing := sdk.NewKVStoreKey(types.StoreKey)
keySupply := sdk.NewKVStoreKey(supply.StoreKey)
keyParams := sdk.NewKVStoreKey(params.StoreKey)
tkeyParams := sdk.NewTransientStoreKey(params.TStoreKey)
@ -84,7 +87,7 @@ func createTestInput(t *testing.T, defaults Params) (sdk.Context, bank.Keeper, s
}
supplyKeeper := supply.NewKeeper(cdc, keySupply, accountKeeper, bk, supply.DefaultCodespace, maccPerms)
totalSupply := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, initTokens.MulRaw(int64(len(addrs)))))
totalSupply := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, InitTokens.MulRaw(int64(len(Addrs)))))
supplyKeeper.SetSupply(ctx, supply.NewSupply(totalSupply))
sk := staking.NewKeeper(cdc, keyStaking, tkeyStaking, supplyKeeper, paramsKeeper.Subspace(staking.DefaultParamspace), staking.DefaultCodespace)
@ -101,17 +104,15 @@ func createTestInput(t *testing.T, defaults Params) (sdk.Context, bank.Keeper, s
_ = staking.InitGenesis(ctx, sk, accountKeeper, supplyKeeper, genesis)
for _, addr := range addrs {
for _, addr := range Addrs {
_, err = bk.AddCoins(ctx, sdk.AccAddress(addr), initCoins)
}
require.Nil(t, err)
paramstore := paramsKeeper.Subspace(DefaultParamspace)
keeper := NewKeeper(cdc, keySlashing, &sk, paramstore, DefaultCodespace)
sk.SetHooks(keeper.Hooks())
paramstore := paramsKeeper.Subspace(types.DefaultParamspace)
keeper := NewKeeper(cdc, keySlashing, &sk, paramstore, types.DefaultCodespace)
require.NotPanics(t, func() {
InitGenesis(ctx, keeper, sk, GenesisState{defaults, nil, nil})
})
keeper.SetParams(ctx, defaults)
sk.SetHooks(keeper.Hooks())
return ctx, bk, sk, paramstore, keeper
}
@ -126,9 +127,13 @@ func newPubKey(pk string) (res crypto.PubKey) {
return pkEd
}
func testAddr(addr string) sdk.AccAddress {
res := []byte(addr)
return res
// Have to change these parameters for tests
// lest the tests take forever
func TestParams() types.Params {
params := types.DefaultParams()
params.SignedBlocksWindow = 1000
params.DowntimeJailDuration = 60 * 60
return params
}
func NewTestMsgCreateValidator(address sdk.ValAddress, pubKey crypto.PubKey, amt sdk.Int) staking.MsgCreateValidator {
@ -139,7 +144,7 @@ func NewTestMsgCreateValidator(address sdk.ValAddress, pubKey crypto.PubKey, amt
)
}
func newTestMsgDelegate(delAddr sdk.AccAddress, valAddr sdk.ValAddress, delAmount sdk.Int) staking.MsgDelegate {
func NewTestMsgDelegate(delAddr sdk.AccAddress, valAddr sdk.ValAddress, delAmount sdk.Int) staking.MsgDelegate {
amount := sdk.NewCoin(sdk.DefaultBondDenom, delAmount)
return staking.NewMsgDelegate(delAddr, valAddr, amount)
}

View File

@ -0,0 +1,50 @@
package keeper
import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/slashing/internal/types"
)
// Unjail calls the staking Unjail function to unjail a validator if the
// jailed period has concluded
func (k Keeper) Unjail(ctx sdk.Context, validatorAddr sdk.ValAddress) sdk.Error {
validator := k.sk.Validator(ctx, validatorAddr)
if validator == nil {
return types.ErrNoValidatorForAddress(k.codespace)
}
// cannot be unjailed if no self-delegation exists
selfDel := k.sk.Delegation(ctx, sdk.AccAddress(validatorAddr), validatorAddr)
if selfDel == nil {
return types.ErrMissingSelfDelegation(k.codespace)
}
if validator.TokensFromShares(selfDel.GetShares()).TruncateInt().LT(validator.GetMinSelfDelegation()) {
return types.ErrSelfDelegationTooLowToUnjail(k.codespace)
}
// cannot be unjailed if not jailed
if !validator.IsJailed() {
return types.ErrValidatorNotJailed(k.codespace)
}
consAddr := sdk.ConsAddress(validator.GetConsPubKey().Address())
info, found := k.GetValidatorSigningInfo(ctx, consAddr)
if !found {
return types.ErrNoValidatorForAddress(k.codespace)
}
// cannot be unjailed if tombstoned
if info.Tombstoned {
return types.ErrValidatorJailed(k.codespace)
}
// cannot be unjailed until out of jail
if ctx.BlockHeader().Time.Before(info.JailedUntil) {
return types.ErrValidatorJailed(k.codespace)
}
k.sk.Unjail(ctx, consAddr)
return nil
}

View File

@ -4,12 +4,12 @@ import (
"github.com/cosmos/cosmos-sdk/codec"
)
// Register concrete types on codec codec
// RegisterCodec registers concrete types on codec
func RegisterCodec(cdc *codec.Codec) {
cdc.RegisterConcrete(MsgUnjail{}, "cosmos-sdk/MsgUnjail", nil)
}
// module codec
// ModuleCdc defines the module codec
var ModuleCdc *codec.Codec
func init() {

View File

@ -7,6 +7,8 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
)
// DONTCOVER
// Local code type
type CodeType = sdk.CodeType

View File

@ -1,7 +1,8 @@
//noalias
package types
// Slashing module event types
var (
const (
EventTypeSlash = "slash"
EventTypeLiveness = "liveness"

View File

@ -1,8 +1,11 @@
package types // noalias
// noalias
// DONTCOVER
package types
import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/params"
"github.com/cosmos/cosmos-sdk/x/staking/exported"
)
@ -11,6 +14,14 @@ type AccountKeeper interface {
IterateAccounts(ctx sdk.Context, process func(auth.Account) (stop bool))
}
// ParamSubspace defines the expected Subspace interfacace
type ParamSubspace interface {
WithKeyTable(table params.KeyTable) params.Subspace
Get(ctx sdk.Context, key []byte, ptr interface{})
GetParamSet(ctx sdk.Context, ps params.ParamSet)
SetParamSet(ctx sdk.Context, ps params.ParamSet)
}
// StakingKeeper expected staking keeper
type StakingKeeper interface {
// iterate through validators by operator address, execute func for each validator
@ -33,7 +44,7 @@ type StakingKeeper interface {
MaxValidators(sdk.Context) uint16
}
// StakingHooks event hooks for staking validator object
// StakingHooks event hooks for staking validator object (noalias)
type StakingHooks interface {
AfterValidatorCreated(ctx sdk.Context, valAddr sdk.ValAddress) // Must be called when a validator is created
AfterValidatorRemoved(ctx sdk.Context, consAddr sdk.ConsAddress, valAddr sdk.ValAddress) // Must be called when a validator is deleted

View File

@ -32,6 +32,14 @@ type MissedBlock struct {
Missed bool `json:"missed" yaml:"missed"`
}
// NewMissedBlock creates a new MissedBlock instance
func NewMissedBlock(index int64, missed bool) MissedBlock {
return MissedBlock{
Index: index,
Missed: missed,
}
}
// DefaultGenesisState - default GenesisState used by Cosmos Hub
func DefaultGenesisState() GenesisState {
return GenesisState{

View File

@ -7,7 +7,7 @@ import (
)
const (
// module name
// ModuleName is the name of the module
ModuleName = "slashing"
// StoreKey is the store key string for slashing
@ -20,13 +20,6 @@ const (
QuerierRoute = ModuleName
)
// Query endpoints supported by the slashing querier
const (
QueryParameters = "parameters"
QuerySigningInfo = "signingInfo"
QuerySigningInfos = "signingInfos"
)
// Keys for slashing store
// Items are stored with the following key: values
//
@ -41,12 +34,12 @@ var (
AddrPubkeyRelationKey = []byte{0x03} // Prefix for address-pubkey relation
)
// stored by *Consensus* address (not operator address)
// GetValidatorSigningInfoKey - stored by *Consensus* address (not operator address)
func GetValidatorSigningInfoKey(v sdk.ConsAddress) []byte {
return append(ValidatorSigningInfoKey, v.Bytes()...)
}
// extract the address from a validator signing info key
// GetValidatorSigningInfoAddress - extract the address from a validator signing info key
func GetValidatorSigningInfoAddress(key []byte) (v sdk.ConsAddress) {
addr := key[1:]
if len(addr) != sdk.AddrLen {
@ -55,19 +48,19 @@ func GetValidatorSigningInfoAddress(key []byte) (v sdk.ConsAddress) {
return sdk.ConsAddress(addr)
}
// stored by *Consensus* address (not operator address)
// GetValidatorMissedBlockBitArrayPrefixKey - stored by *Consensus* address (not operator address)
func GetValidatorMissedBlockBitArrayPrefixKey(v sdk.ConsAddress) []byte {
return append(ValidatorMissedBlockBitArrayKey, v.Bytes()...)
}
// stored by *Consensus* address (not operator address)
// GetValidatorMissedBlockBitArrayKey - stored by *Consensus* address (not operator address)
func GetValidatorMissedBlockBitArrayKey(v sdk.ConsAddress, i int64) []byte {
b := make([]byte, 8)
binary.LittleEndian.PutUint64(b, uint64(i))
return append(GetValidatorMissedBlockBitArrayPrefixKey(v), b...)
}
// get pubkey relation key used to get the pubkey from the address
// GetAddrPubkeyRelationKey gets pubkey relation key used to get the pubkey from the address
func GetAddrPubkeyRelationKey(address []byte) []byte {
return append(AddrPubkeyRelationKey, address...)
}

View File

@ -12,6 +12,7 @@ type MsgUnjail struct {
ValidatorAddr sdk.ValAddress `json:"address" yaml:"address"` // address of the validator operator
}
// NewMsgUnjail creates a new MsgUnjail instance
func NewMsgUnjail(validatorAddr sdk.ValAddress) MsgUnjail {
return MsgUnjail{
ValidatorAddr: validatorAddr,
@ -25,13 +26,13 @@ func (msg MsgUnjail) GetSigners() []sdk.AccAddress {
return []sdk.AccAddress{sdk.AccAddress(msg.ValidatorAddr)}
}
// get the bytes for the message signer to sign on
// GetSignBytes gets the bytes for the message signer to sign on
func (msg MsgUnjail) GetSignBytes() []byte {
bz := ModuleCdc.MustMarshalJSON(msg)
return sdk.MustSortJSON(bz)
}
// quick validity check
// ValidateBasic validity check for the AnteHandler
func (msg MsgUnjail) ValidateBasic() sdk.Error {
if msg.ValidatorAddr.Empty() {
return ErrBadValidatorAddr(DefaultCodespace)

View File

@ -64,6 +64,7 @@ func NewParams(maxEvidenceAge time.Duration, signedBlocksWindow int64,
}
}
// String implements the stringer interface for Params
func (p Params) String() string {
return fmt.Sprintf(`Slashing Params:
MaxEvidenceAge: %s
@ -77,26 +78,22 @@ func (p Params) String() string {
p.SlashFractionDowntime)
}
// Implements params.ParamSet
// ParamSetPairs - Implements params.ParamSet
func (p *Params) ParamSetPairs() params.ParamSetPairs {
return params.ParamSetPairs{
{KeyMaxEvidenceAge, &p.MaxEvidenceAge},
{KeySignedBlocksWindow, &p.SignedBlocksWindow},
{KeyMinSignedPerWindow, &p.MinSignedPerWindow},
{KeyDowntimeJailDuration, &p.DowntimeJailDuration},
{KeySlashFractionDoubleSign, &p.SlashFractionDoubleSign},
{KeySlashFractionDowntime, &p.SlashFractionDowntime},
params.NewParamSetPair(KeyMaxEvidenceAge, &p.MaxEvidenceAge),
params.NewParamSetPair(KeySignedBlocksWindow, &p.SignedBlocksWindow),
params.NewParamSetPair(KeyMinSignedPerWindow, &p.MinSignedPerWindow),
params.NewParamSetPair(KeyDowntimeJailDuration, &p.DowntimeJailDuration),
params.NewParamSetPair(KeySlashFractionDoubleSign, &p.SlashFractionDoubleSign),
params.NewParamSetPair(KeySlashFractionDowntime, &p.SlashFractionDowntime),
}
}
// Default parameters for this module
// DefaultParams defines the parameters for this module
func DefaultParams() Params {
return Params{
MaxEvidenceAge: DefaultMaxEvidenceAge,
SignedBlocksWindow: DefaultSignedBlocksWindow,
MinSignedPerWindow: DefaultMinSignedPerWindow,
DowntimeJailDuration: DefaultDowntimeJailDuration,
SlashFractionDoubleSign: DefaultSlashFractionDoubleSign,
SlashFractionDowntime: DefaultSlashFractionDowntime,
}
return NewParams(
DefaultMaxEvidenceAge, DefaultSignedBlocksWindow, DefaultMinSignedPerWindow,
DefaultDowntimeJailDuration, DefaultSlashFractionDoubleSign, DefaultSlashFractionDowntime,
)
}

View File

@ -4,12 +4,22 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
)
// DONTCOVER
// Query endpoints supported by the slashing querier
const (
QueryParameters = "parameters"
QuerySigningInfo = "signingInfo"
QuerySigningInfos = "signingInfos"
)
// QuerySigningInfoParams defines the params for the following queries:
// - 'custom/slashing/signingInfo'
type QuerySigningInfoParams struct {
ConsAddress sdk.ConsAddress
}
// NewQuerySigningInfoParams creates a new QuerySigningInfoParams instance
func NewQuerySigningInfoParams(consAddr sdk.ConsAddress) QuerySigningInfoParams {
return QuerySigningInfoParams{consAddr}
}
@ -20,6 +30,7 @@ type QuerySigningInfosParams struct {
Page, Limit int
}
// NewQuerySigningInfosParams creates a new QuerySigningInfosParams instance
func NewQuerySigningInfosParams(page, limit int) QuerySigningInfosParams {
return QuerySigningInfosParams{page, limit}
}

View File

@ -7,7 +7,7 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
)
// Signing info for a validator
// ValidatorSigningInfo defines the signing info for a validator
type ValidatorSigningInfo struct {
Address sdk.ConsAddress `json:"address" yaml:"address"` // validator consensus address
StartHeight int64 `json:"start_height" yaml:"start_height"` // height at which validator was first a candidate OR was unjailed
@ -17,7 +17,7 @@ type ValidatorSigningInfo struct {
MissedBlocksCounter int64 `json:"missed_blocks_counter" yaml:"missed_blocks_counter"` // missed blocks counter (to avoid scanning the array every time)
}
// Construct a new `ValidatorSigningInfo` struct
// NewValidatorSigningInfo creates a new ValidatorSigningInfo instance
func NewValidatorSigningInfo(
condAddr sdk.ConsAddress, startHeight, indexOffset int64,
jailedUntil time.Time, tombstoned bool, missedBlocksCounter int64,
@ -33,7 +33,7 @@ func NewValidatorSigningInfo(
}
}
// Return human readable signing info
// String implements the stringer interface for ValidatorSigningInfo
func (i ValidatorSigningInfo) String() string {
return fmt.Sprintf(`Validator Signing Info:
Address: %s

View File

@ -14,7 +14,7 @@ import (
"github.com/cosmos/cosmos-sdk/types/module"
"github.com/cosmos/cosmos-sdk/x/slashing/client/cli"
"github.com/cosmos/cosmos-sdk/x/slashing/client/rest"
"github.com/cosmos/cosmos-sdk/x/slashing/types"
"github.com/cosmos/cosmos-sdk/x/slashing/internal/types"
)
var (

View File

@ -1,40 +0,0 @@
package slashing
import (
"testing"
"time"
"github.com/stretchr/testify/require"
sdk "github.com/cosmos/cosmos-sdk/types"
)
func TestGetSetValidatorSigningInfo(t *testing.T) {
ctx, _, _, _, keeper := createTestInput(t, DefaultParams())
info, found := keeper.getValidatorSigningInfo(ctx, sdk.ConsAddress(addrs[0]))
require.False(t, found)
newInfo := NewValidatorSigningInfo(
sdk.ConsAddress(addrs[0]),
int64(4),
int64(3),
time.Unix(2, 0),
false,
int64(10),
)
keeper.SetValidatorSigningInfo(ctx, sdk.ConsAddress(addrs[0]), newInfo)
info, found = keeper.getValidatorSigningInfo(ctx, sdk.ConsAddress(addrs[0]))
require.True(t, found)
require.Equal(t, info.StartHeight, int64(4))
require.Equal(t, info.IndexOffset, int64(3))
require.Equal(t, info.JailedUntil, time.Unix(2, 0).UTC())
require.Equal(t, info.MissedBlocksCounter, int64(10))
}
func TestGetSetValidatorMissedBlockBitArray(t *testing.T) {
ctx, _, _, _, keeper := createTestInput(t, DefaultParams())
missed := keeper.getValidatorMissedBlockBitArray(ctx, sdk.ConsAddress(addrs[0]), 0)
require.False(t, missed) // treat empty key as not missed
keeper.setValidatorMissedBlockBitArray(ctx, sdk.ConsAddress(addrs[0]), 0, true)
missed = keeper.getValidatorMissedBlockBitArray(ctx, sdk.ConsAddress(addrs[0]), 0)
require.True(t, missed) // now should be missed
}