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 if !validator.Jailed
fail with "Validator not jailed, cannot unjail" fail with "Validator not jailed, cannot unjail"
info = getValidatorSigningInfo(operator) info = GetValidatorSigningInfo(operator)
if info.Tombstoned if info.Tombstoned
fail with "Tombstoned validator cannot be unjailed" fail with "Tombstoned validator cannot be unjailed"
if block time < info.JailedUntil if block time < info.JailedUntil

View File

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

View File

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

View File

@ -1,15 +1,20 @@
package subspace 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 { type ParamSetPair struct {
Key []byte Key []byte
Value interface{} 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 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 { type ParamSet interface {
ParamSetPairs() ParamSetPairs ParamSetPairs() ParamSetPairs
} }

View File

@ -9,13 +9,14 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
) )
// slashing begin block functionality // BeginBlocker check for infraction evidence or downtime of validators
func BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock, sk Keeper) { // 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 // Iterate over all the validators which *should* have signed this block
// store whether or not they have actually signed it and slash/unbond any // store whether or not they have actually signed it and slash/unbond any
// which have missed too many blocks in a row (downtime slashing) // which have missed too many blocks in a row (downtime slashing)
for _, voteInfo := range req.LastCommitInfo.GetVotes() { 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 // 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 { for _, evidence := range req.ByzantineValidators {
switch evidence.Type { switch evidence.Type {
case tmtypes.ABCIEvidenceTypeDuplicateVote: 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: 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" abci "github.com/tendermint/tendermint/abci/types"
sdk "github.com/cosmos/cosmos-sdk/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" "github.com/cosmos/cosmos-sdk/x/staking"
) )
func TestBeginBlocker(t *testing.T) { func TestBeginBlocker(t *testing.T) {
ctx, ck, sk, _, keeper := createTestInput(t, DefaultParams()) ctx, ck, sk, _, keeper := slashingkeeper.CreateTestInput(t, DefaultParams())
power := int64(100) power := int64(100)
amt := sdk.TokensFromConsensusPower(power) amt := sdk.TokensFromConsensusPower(power)
addr, pk := addrs[2], pks[2] addr, pk := slashingkeeper.Addrs[2], slashingkeeper.Pks[2]
// bond the validator // 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()) require.True(t, got.IsOK())
staking.EndBlocker(ctx, sk) staking.EndBlocker(ctx, sk)
require.Equal( require.Equal(
t, ck.GetCoins(ctx, sdk.AccAddress(addr)), 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()) require.Equal(t, amt, sk.Validator(ctx, addr).GetBondedTokens())
@ -44,7 +45,7 @@ func TestBeginBlocker(t *testing.T) {
} }
BeginBlocker(ctx, req, keeper) 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.True(t, found)
require.Equal(t, ctx.BlockHeight(), info.StartHeight) require.Equal(t, ctx.BlockHeight(), info.StartHeight)
require.Equal(t, int64(1), info.IndexOffset) require.Equal(t, int64(1), info.IndexOffset)

View File

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

View File

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

View File

@ -11,7 +11,7 @@ import (
"github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/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"
) )
// GetQueryCmd returns the cli query commands for this module // GetQueryCmd returns the cli query commands for this module

View File

@ -9,7 +9,7 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth" "github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/auth/client/utils" "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 // GetTxCmd returns the transaction commands for this module

View File

@ -9,7 +9,7 @@ import (
"github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/context"
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/rest" "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) { func registerQueryRoutes(cliCtx context.CLIContext, r *mux.Router) {

View File

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

View File

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

View File

@ -4,9 +4,10 @@ import (
"fmt" "fmt"
sdk "github.com/cosmos/cosmos-sdk/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"
) )
// NewHandler creates an sdk.Handler for all the slashing type messages
func NewHandler(k Keeper) sdk.Handler { func NewHandler(k Keeper) sdk.Handler {
return func(ctx sdk.Context, msg sdk.Msg) sdk.Result { return func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
ctx = ctx.WithEventManager(sdk.NewEventManager()) ctx = ctx.WithEventManager(sdk.NewEventManager())
@ -25,45 +26,12 @@ func NewHandler(k Keeper) sdk.Handler {
// Validators must submit a transaction to unjail itself after // Validators must submit a transaction to unjail itself after
// having been jailed (and thus unbonded) for downtime // having been jailed (and thus unbonded) for downtime
func handleMsgUnjail(ctx sdk.Context, msg MsgUnjail, k Keeper) sdk.Result { func handleMsgUnjail(ctx sdk.Context, msg MsgUnjail, k Keeper) sdk.Result {
validator := k.sk.Validator(ctx, msg.ValidatorAddr)
if validator == nil { err := k.Unjail(ctx, msg.ValidatorAddr)
return ErrNoValidatorForAddress(k.codespace).Result() 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( ctx.EventManager().EmitEvent(
sdk.NewEvent( sdk.NewEvent(
sdk.EventTypeMessage, sdk.EventTypeMessage,

View File

@ -9,23 +9,25 @@ import (
abci "github.com/tendermint/tendermint/abci/types" abci "github.com/tendermint/tendermint/abci/types"
sdk "github.com/cosmos/cosmos-sdk/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" "github.com/cosmos/cosmos-sdk/x/staking"
) )
func TestCannotUnjailUnlessJailed(t *testing.T) { func TestCannotUnjailUnlessJailed(t *testing.T) {
// initial setup // initial setup
ctx, ck, sk, _, keeper := createTestInput(t, DefaultParams()) ctx, ck, sk, _, keeper := slashingkeeper.CreateTestInput(t, DefaultParams())
slh := NewHandler(keeper) slh := NewHandler(keeper)
amt := sdk.TokensFromConsensusPower(100) amt := sdk.TokensFromConsensusPower(100)
addr, val := addrs[0], pks[0] addr, val := slashingkeeper.Addrs[0], slashingkeeper.Pks[0]
msg := NewTestMsgCreateValidator(addr, val, amt) msg := slashingkeeper.NewTestMsgCreateValidator(addr, val, amt)
got := staking.NewHandler(sk)(ctx, msg) got := staking.NewHandler(sk)(ctx, msg)
require.True(t, got.IsOK(), "%v", got) require.True(t, got.IsOK(), "%v", got)
staking.EndBlocker(ctx, sk) staking.EndBlocker(ctx, sk)
require.Equal( require.Equal(
t, ck.GetCoins(ctx, sdk.AccAddress(addr)), 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()) require.Equal(t, amt, sk.Validator(ctx, addr).GetBondedTokens())
@ -38,11 +40,11 @@ func TestCannotUnjailUnlessJailed(t *testing.T) {
func TestCannotUnjailUnlessMeetMinSelfDelegation(t *testing.T) { func TestCannotUnjailUnlessMeetMinSelfDelegation(t *testing.T) {
// initial setup // initial setup
ctx, ck, sk, _, keeper := createTestInput(t, DefaultParams()) ctx, ck, sk, _, keeper := slashingkeeper.CreateTestInput(t, DefaultParams())
slh := NewHandler(keeper) slh := NewHandler(keeper)
amtInt := int64(100) amtInt := int64(100)
addr, val, amt := addrs[0], pks[0], sdk.TokensFromConsensusPower(amtInt) addr, val, amt := slashingkeeper.Addrs[0], slashingkeeper.Pks[0], sdk.TokensFromConsensusPower(amtInt)
msg := NewTestMsgCreateValidator(addr, val, amt) msg := slashingkeeper.NewTestMsgCreateValidator(addr, val, amt)
msg.MinSelfDelegation = amt msg.MinSelfDelegation = amt
got := staking.NewHandler(sk)(ctx, msg) got := staking.NewHandler(sk)(ctx, msg)
require.True(t, got.IsOK()) require.True(t, got.IsOK())
@ -50,7 +52,7 @@ func TestCannotUnjailUnlessMeetMinSelfDelegation(t *testing.T) {
require.Equal( require.Equal(
t, ck.GetCoins(ctx, sdk.AccAddress(addr)), 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()) unbondAmt := sdk.NewCoin(sk.GetParams(ctx).BondDenom, sdk.OneInt())
@ -67,7 +69,7 @@ func TestCannotUnjailUnlessMeetMinSelfDelegation(t *testing.T) {
} }
func TestJailedValidatorDelegations(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 := stakingKeeper.GetParams(ctx)
stakingParams.UnbondingTime = 0 stakingParams.UnbondingTime = 0
@ -75,10 +77,10 @@ func TestJailedValidatorDelegations(t *testing.T) {
// create a validator // create a validator
bondAmount := sdk.TokensFromConsensusPower(10) bondAmount := sdk.TokensFromConsensusPower(10)
valPubKey := pks[0] valPubKey := slashingkeeper.Pks[0]
valAddr, consAddr := addrs[1], sdk.ConsAddress(addrs[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) got := staking.NewHandler(stakingKeeper)(ctx, msgCreateVal)
require.True(t, got.IsOK(), "expected create validator msg to be ok, got: %v", got) 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) slashingKeeper.SetValidatorSigningInfo(ctx, consAddr, newInfo)
// delegate tokens to the validator // delegate tokens to the validator
delAddr := sdk.AccAddress(addrs[2]) delAddr := sdk.AccAddress(slashingkeeper.Addrs[2])
msgDelegate := newTestMsgDelegate(delAddr, valAddr, bondAmount) msgDelegate := slashingkeeper.NewTestMsgDelegate(delAddr, valAddr, bondAmount)
got = staking.NewHandler(stakingKeeper)(ctx, msgDelegate) got = staking.NewHandler(stakingKeeper)(ctx, msgDelegate)
require.True(t, got.IsOK(), "expected delegation to be ok, got %v", got) 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) require.False(t, got.IsOK(), "expected jailed validator to not be able to unjail, got: %v", got)
// self-delegate to validator // 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) got = staking.NewHandler(stakingKeeper)(ctx, msgSelfDelegate)
require.True(t, got.IsOK(), "expected delegation to not be ok, got %v", got) 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.False(t, res.IsOK())
require.True(t, strings.Contains(res.Log, "unrecognized slashing message type")) 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 // nolint
package slashing package keeper
import ( import (
"time" "time"
@ -7,12 +7,12 @@ import (
"github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/crypto"
sdk "github.com/cosmos/cosmos-sdk/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"
) )
func (k Keeper) AfterValidatorBonded(ctx sdk.Context, address sdk.ConsAddress, _ sdk.ValAddress) { func (k Keeper) AfterValidatorBonded(ctx sdk.Context, address sdk.ConsAddress, _ sdk.ValAddress) {
// Update the signing info start height or create a new signing info // Update the signing info start height or create a new signing info
_, found := k.getValidatorSigningInfo(ctx, address) _, found := k.GetValidatorSigningInfo(ctx, address)
if !found { if !found {
signingInfo := types.NewValidatorSigningInfo( signingInfo := types.NewValidatorSigningInfo(
address, 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. // When a validator is created, add the address-pubkey relation.
func (k Keeper) AfterValidatorCreated(ctx sdk.Context, valAddr sdk.ValAddress) { func (k Keeper) AfterValidatorCreated(ctx sdk.Context, valAddr sdk.ValAddress) {
validator := k.sk.Validator(ctx, valAddr) 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. // When a validator is removed, delete the address-pubkey relation.

View File

@ -1,47 +1,16 @@
package slashing package keeper
import ( import (
"fmt" "fmt"
"time" "time"
"github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/libs/log"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/params" "github.com/cosmos/cosmos-sdk/x/slashing/internal/types"
"github.com/cosmos/cosmos-sdk/x/slashing/types"
) )
// Keeper of the slashing store // HandleDoubleSign handles a validator signing two blocks at the same height.
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
// power: power of the double-signing validator at the height of infraction // 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) { func (k Keeper) HandleDoubleSign(ctx sdk.Context, addr crypto.Address, infractionHeight int64, timestamp time.Time, power int64) {
logger := k.Logger(ctx) logger := k.Logger(ctx)
@ -52,7 +21,7 @@ func (k Keeper) HandleDoubleSign(ctx sdk.Context, addr crypto.Address, infractio
// fetch the validator public key // fetch the validator public key
consAddr := sdk.ConsAddress(addr) consAddr := sdk.ConsAddress(addr)
pubkey, err := k.getPubkey(ctx, addr) pubkey, err := k.GetPubkey(ctx, addr)
if err != nil { if err != nil {
// Ignore evidence that cannot be handled. // Ignore evidence that cannot be handled.
// NOTE: // NOTE:
@ -83,7 +52,7 @@ func (k Keeper) HandleDoubleSign(ctx sdk.Context, addr crypto.Address, infractio
} }
// fetch the validator signing info // fetch the validator signing info
signInfo, found := k.getValidatorSigningInfo(ctx, consAddr) signInfo, found := k.GetValidatorSigningInfo(ctx, consAddr)
if !found { if !found {
panic(fmt.Sprintf("Expected signing info for validator %s but not found", consAddr)) 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) k.SetValidatorSigningInfo(ctx, consAddr, signInfo)
} }
// handle a validator signature, must be called once per validator per block // HandleValidatorSignature handles 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
func (k Keeper) HandleValidatorSignature(ctx sdk.Context, addr crypto.Address, power int64, signed bool) { func (k Keeper) HandleValidatorSignature(ctx sdk.Context, addr crypto.Address, power int64, signed bool) {
logger := k.Logger(ctx) logger := k.Logger(ctx)
height := ctx.BlockHeight() height := ctx.BlockHeight()
// fetch the validator public key
consAddr := sdk.ConsAddress(addr) consAddr := sdk.ConsAddress(addr)
pubkey, err := k.getPubkey(ctx, addr) pubkey, err := k.GetPubkey(ctx, addr)
if err != nil { if err != nil {
panic(fmt.Sprintf("Validator consensus-address %s not found", consAddr)) panic(fmt.Sprintf("Validator consensus-address %s not found", consAddr))
} }
// fetch signing info // fetch signing info
signInfo, found := k.getValidatorSigningInfo(ctx, consAddr) signInfo, found := k.GetValidatorSigningInfo(ctx, consAddr)
if !found { if !found {
panic(fmt.Sprintf("Expected signing info for validator %s but not found", consAddr)) 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 // Update signed block bit array & counter
// This counter just tracks the sum of the bit array // This counter just tracks the sum of the bit array
// That way we avoid needing to read/write the whole array each time // That way we avoid needing to read/write the whole array each time
previous := k.getValidatorMissedBlockBitArray(ctx, consAddr, index) previous := k.GetValidatorMissedBlockBitArray(ctx, consAddr, index)
missed := !signed missed := !signed
switch { switch {
case !previous && missed: case !previous && missed:
// Array value has changed from not missed to missed, increment counter // 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++ signInfo.MissedBlocksCounter++
case previous && !missed: case previous && !missed:
// Array value has changed from missed to not missed, decrement counter // 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-- signInfo.MissedBlocksCounter--
default: default:
// Array value at this index has not changed, no need to update counter // Array value at this index has not changed, no need to update counter
@ -245,29 +215,3 @@ func (k Keeper) HandleValidatorSignature(ctx sdk.Context, addr crypto.Address, p
// Set the updated signing info // Set the updated signing info
k.SetValidatorSigningInfo(ctx, consAddr, signInfo) 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 ( import (
"testing" "testing"
@ -8,19 +8,10 @@ import (
abci "github.com/tendermint/tendermint/abci/types" abci "github.com/tendermint/tendermint/abci/types"
sdk "github.com/cosmos/cosmos-sdk/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" "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 // Test that a validator is slashed correctly
@ -28,18 +19,18 @@ func keeperTestParams() types.Params {
func TestHandleDoubleSign(t *testing.T) { func TestHandleDoubleSign(t *testing.T) {
// initial setup // initial setup
ctx, ck, sk, _, keeper := createTestInput(t, keeperTestParams()) ctx, ck, sk, _, keeper := CreateTestInput(t, TestParams())
// validator added pre-genesis // validator added pre-genesis
ctx = ctx.WithBlockHeight(-1) ctx = ctx.WithBlockHeight(-1)
power := int64(100) power := int64(100)
amt := sdk.TokensFromConsensusPower(power) 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)) got := staking.NewHandler(sk)(ctx, NewTestMsgCreateValidator(operatorAddr, val, amt))
require.True(t, got.IsOK()) require.True(t, got.IsOK())
staking.EndBlocker(ctx, sk) staking.EndBlocker(ctx, sk)
require.Equal( require.Equal(
t, ck.GetCoins(ctx, sdk.AccAddress(operatorAddr)), 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()) 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)}) ctx = ctx.WithBlockHeader(abci.Header{Time: time.Unix(1, 0).Add(sk.GetParams(ctx).UnbondingTime)})
// Still shouldn't be able to unjail // Still shouldn't be able to unjail
msgUnjail := types.NewMsgUnjail(operatorAddr) require.Error(t, keeper.Unjail(ctx, operatorAddr))
res := handleMsgUnjail(ctx, msgUnjail, keeper)
require.False(t, res.IsOK())
// Should be able to unbond now // Should be able to unbond now
del, _ := sk.GetDelegation(ctx, sdk.AccAddress(operatorAddr), operatorAddr) del, _ := sk.GetDelegation(ctx, sdk.AccAddress(operatorAddr), operatorAddr)
@ -78,7 +67,7 @@ func TestHandleDoubleSign(t *testing.T) {
totalBond := validator.TokensFromShares(del.GetShares()).TruncateInt() totalBond := validator.TokensFromShares(del.GetShares()).TruncateInt()
msgUnbond := staking.NewMsgUndelegate(sdk.AccAddress(operatorAddr), operatorAddr, sdk.NewCoin(sk.GetParams(ctx).BondDenom, totalBond)) 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()) require.True(t, res.IsOK())
} }
@ -89,18 +78,18 @@ func TestHandleDoubleSign(t *testing.T) {
func TestPastMaxEvidenceAge(t *testing.T) { func TestPastMaxEvidenceAge(t *testing.T) {
// initial setup // initial setup
ctx, ck, sk, _, keeper := createTestInput(t, keeperTestParams()) ctx, ck, sk, _, keeper := CreateTestInput(t, TestParams())
// validator added pre-genesis // validator added pre-genesis
ctx = ctx.WithBlockHeight(-1) ctx = ctx.WithBlockHeight(-1)
power := int64(100) power := int64(100)
amt := sdk.TokensFromConsensusPower(power) 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)) got := staking.NewHandler(sk)(ctx, NewTestMsgCreateValidator(operatorAddr, val, amt))
require.True(t, got.IsOK()) require.True(t, got.IsOK())
staking.EndBlocker(ctx, sk) staking.EndBlocker(ctx, sk)
require.Equal( require.Equal(
t, ck.GetCoins(ctx, sdk.AccAddress(operatorAddr)), 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()) 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()) 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 // Test a new validator entering the validator set
// Ensure that SigningInfo.StartHeight is set correctly // Ensure that SigningInfo.StartHeight is set correctly
// and that they are not immediately jailed // and that they are not immediately jailed
func TestHandleNewValidator(t *testing.T) { func TestHandleNewValidator(t *testing.T) {
// initial setup // initial setup
ctx, ck, sk, _, keeper := createTestInput(t, keeperTestParams()) ctx, ck, sk, _, keeper := CreateTestInput(t, TestParams())
addr, val := addrs[0], pks[0] addr, val := Addrs[0], Pks[0]
amt := sdk.TokensFromConsensusPower(100) amt := sdk.TokensFromConsensusPower(100)
sh := staking.NewHandler(sk) sh := staking.NewHandler(sk)
@ -292,7 +130,7 @@ func TestHandleNewValidator(t *testing.T) {
require.Equal( require.Equal(
t, ck.GetCoins(ctx, sdk.AccAddress(addr)), 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()) 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) ctx = ctx.WithBlockHeight(keeper.SignedBlocksWindow(ctx) + 2)
keeper.HandleValidatorSignature(ctx, val.Address(), 100, false) 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.True(t, found)
require.Equal(t, keeper.SignedBlocksWindow(ctx)+1, info.StartHeight) require.Equal(t, keeper.SignedBlocksWindow(ctx)+1, info.StartHeight)
require.Equal(t, int64(2), info.IndexOffset) require.Equal(t, int64(2), info.IndexOffset)
@ -321,10 +159,10 @@ func TestHandleNewValidator(t *testing.T) {
func TestHandleAlreadyJailed(t *testing.T) { func TestHandleAlreadyJailed(t *testing.T) {
// initial setup // initial setup
ctx, _, sk, _, keeper := createTestInput(t, DefaultParams()) ctx, _, sk, _, keeper := CreateTestInput(t, types.DefaultParams())
power := int64(100) power := int64(100)
amt := sdk.TokensFromConsensusPower(power) amt := sdk.TokensFromConsensusPower(power)
addr, val := addrs[0], pks[0] addr, val := Addrs[0], Pks[0]
sh := staking.NewHandler(sk) sh := staking.NewHandler(sk)
got := sh(ctx, NewTestMsgCreateValidator(addr, val, amt)) got := sh(ctx, NewTestMsgCreateValidator(addr, val, amt))
require.True(t, got.IsOK()) require.True(t, got.IsOK())
@ -370,14 +208,14 @@ func TestHandleAlreadyJailed(t *testing.T) {
func TestValidatorDippingInAndOut(t *testing.T) { func TestValidatorDippingInAndOut(t *testing.T) {
// initial setup // initial setup
// keeperTestParams set the SignedBlocksWindow to 1000 and MaxMissedBlocksPerWindow to 500 // TestParams set the SignedBlocksWindow to 1000 and MaxMissedBlocksPerWindow to 500
ctx, _, sk, _, keeper := createTestInput(t, keeperTestParams()) ctx, _, sk, _, keeper := CreateTestInput(t, TestParams())
params := sk.GetParams(ctx) params := sk.GetParams(ctx)
params.MaxValidators = 1 params.MaxValidators = 1
sk.SetParams(ctx, params) sk.SetParams(ctx, params)
power := int64(100) power := int64(100)
amt := sdk.TokensFromConsensusPower(power) amt := sdk.TokensFromConsensusPower(power)
addr, val := addrs[0], pks[0] addr, val := Addrs[0], Pks[0]
consAddr := sdk.ConsAddress(addr) consAddr := sdk.ConsAddress(addr)
sh := staking.NewHandler(sk) sh := staking.NewHandler(sk)
got := sh(ctx, NewTestMsgCreateValidator(addr, val, amt)) got := sh(ctx, NewTestMsgCreateValidator(addr, val, amt))
@ -393,7 +231,7 @@ func TestValidatorDippingInAndOut(t *testing.T) {
// kick first validator out of validator set // kick first validator out of validator set
newAmt := sdk.TokensFromConsensusPower(101) 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()) require.True(t, got.IsOK())
validatorUpdates := staking.EndBlocker(ctx, sk) validatorUpdates := staking.EndBlocker(ctx, sk)
require.Equal(t, 2, len(validatorUpdates)) require.Equal(t, 2, len(validatorUpdates))
@ -406,7 +244,7 @@ func TestValidatorDippingInAndOut(t *testing.T) {
// validator added back in // validator added back in
delTokens := sdk.TokensFromConsensusPower(50) 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()) require.True(t, got.IsOK())
validatorUpdates = staking.EndBlocker(ctx, sk) validatorUpdates = staking.EndBlocker(ctx, sk)
require.Equal(t, 2, len(validatorUpdates)) require.Equal(t, 2, len(validatorUpdates))
@ -435,13 +273,13 @@ func TestValidatorDippingInAndOut(t *testing.T) {
require.Equal(t, sdk.Unbonding, validator.Status) require.Equal(t, sdk.Unbonding, validator.Status)
// check all the signing information // check all the signing information
signInfo, found := keeper.getValidatorSigningInfo(ctx, consAddr) signInfo, found := keeper.GetValidatorSigningInfo(ctx, consAddr)
require.True(t, found) require.True(t, found)
require.Equal(t, int64(0), signInfo.MissedBlocksCounter) require.Equal(t, int64(0), signInfo.MissedBlocksCounter)
require.Equal(t, int64(0), signInfo.IndexOffset) require.Equal(t, int64(0), signInfo.IndexOffset)
// array should be cleared // array should be cleared
for offset := int64(0); offset < keeper.SignedBlocksWindow(ctx); offset++ { 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) require.False(t, missed)
} }

View File

@ -1,10 +1,10 @@
package slashing package keeper
import ( import (
"time" "time"
sdk "github.com/cosmos/cosmos-sdk/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"
) )
// MaxEvidenceAge - max age for evidence // MaxEvidenceAge - max age for evidence
@ -19,7 +19,7 @@ func (k Keeper) SignedBlocksWindow(ctx sdk.Context) (res int64) {
return return
} }
// Downtime slashing threshold // MinSignedPerWindow - minimum blocks signed per window
func (k Keeper) MinSignedPerWindow(ctx sdk.Context) int64 { func (k Keeper) MinSignedPerWindow(ctx sdk.Context) int64 {
var minSignedPerWindow sdk.Dec var minSignedPerWindow sdk.Dec
k.paramspace.Get(ctx, types.KeyMinSignedPerWindow, &minSignedPerWindow) k.paramspace.Get(ctx, types.KeyMinSignedPerWindow, &minSignedPerWindow)
@ -30,19 +30,19 @@ func (k Keeper) MinSignedPerWindow(ctx sdk.Context) int64 {
return minSignedPerWindow.MulInt64(signedBlocksWindow).RoundInt64() return minSignedPerWindow.MulInt64(signedBlocksWindow).RoundInt64()
} }
// Downtime unbond duration // DowntimeJailDuration - Downtime unbond duration
func (k Keeper) DowntimeJailDuration(ctx sdk.Context) (res time.Duration) { func (k Keeper) DowntimeJailDuration(ctx sdk.Context) (res time.Duration) {
k.paramspace.Get(ctx, types.KeyDowntimeJailDuration, &res) k.paramspace.Get(ctx, types.KeyDowntimeJailDuration, &res)
return return
} }
// SlashFractionDoubleSign // SlashFractionDoubleSign - fraction of power slashed in case of double sign
func (k Keeper) SlashFractionDoubleSign(ctx sdk.Context) (res sdk.Dec) { func (k Keeper) SlashFractionDoubleSign(ctx sdk.Context) (res sdk.Dec) {
k.paramspace.Get(ctx, types.KeySlashFractionDoubleSign, &res) k.paramspace.Get(ctx, types.KeySlashFractionDoubleSign, &res)
return return
} }
// SlashFractionDowntime // SlashFractionDowntime - fraction of power slashed for downtime
func (k Keeper) SlashFractionDowntime(ctx sdk.Context) (res sdk.Dec) { func (k Keeper) SlashFractionDowntime(ctx sdk.Context) (res sdk.Dec) {
k.paramspace.Get(ctx, types.KeySlashFractionDowntime, &res) k.paramspace.Get(ctx, types.KeySlashFractionDowntime, &res)
return return
@ -53,3 +53,8 @@ func (k Keeper) GetParams(ctx sdk.Context) (params types.Params) {
k.paramspace.GetParamSet(ctx, &params) k.paramspace.GetParamSet(ctx, &params)
return 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 ( import (
"fmt" "fmt"
@ -8,17 +8,18 @@ import (
"github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/slashing/internal/types"
) )
// NewQuerier creates a new querier for slashing clients. // NewQuerier creates a new querier for slashing clients.
func NewQuerier(k Keeper) sdk.Querier { func NewQuerier(k Keeper) sdk.Querier {
return func(ctx sdk.Context, path []string, req abci.RequestQuery) ([]byte, sdk.Error) { return func(ctx sdk.Context, path []string, req abci.RequestQuery) ([]byte, sdk.Error) {
switch path[0] { switch path[0] {
case QueryParameters: case types.QueryParameters:
return queryParams(ctx, k) return queryParams(ctx, k)
case QuerySigningInfo: case types.QuerySigningInfo:
return querySigningInfo(ctx, req, k) return querySigningInfo(ctx, req, k)
case QuerySigningInfos: case types.QuerySigningInfos:
return querySigningInfos(ctx, req, k) return querySigningInfos(ctx, req, k)
default: default:
return nil, sdk.ErrUnknownRequest("unknown staking query endpoint") 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) { func queryParams(ctx sdk.Context, k Keeper) ([]byte, sdk.Error) {
params := k.GetParams(ctx) params := k.GetParams(ctx)
res, err := codec.MarshalJSONIndent(ModuleCdc, params) res, err := codec.MarshalJSONIndent(types.ModuleCdc, params)
if err != nil { if err != nil {
return nil, sdk.ErrInternal(sdk.AppendMsgToErr("failed to marshal JSON", err.Error())) 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) { 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 { if err != nil {
return nil, sdk.ErrInternal(fmt.Sprintf("failed to parse params: %s", err)) 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 { 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 { if err != nil {
return nil, sdk.ErrInternal(sdk.AppendMsgToErr("failed to JSON marshal result: %s", err.Error())) 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) { 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 { if err != nil {
return nil, sdk.ErrInternal(fmt.Sprintf("failed to parse params: %s", err)) 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) signingInfos = append(signingInfos, info)
return false return false
}) })
start, end := client.Paginate(len(signingInfos), params.Page, params.Limit, int(k.sk.MaxValidators(ctx))) start, end := client.Paginate(len(signingInfos), params.Page, params.Limit, int(k.sk.MaxValidators(ctx)))
if start < 0 || end < 0 { if start < 0 || end < 0 {
signingInfos = []ValidatorSigningInfo{} signingInfos = []types.ValidatorSigningInfo{}
} else { } else {
signingInfos = signingInfos[start:end] signingInfos = signingInfos[start:end]
} }
res, err := codec.MarshalJSONIndent(ModuleCdc, signingInfos) res, err := codec.MarshalJSONIndent(types.ModuleCdc, signingInfos)
if err != nil { if err != nil {
return nil, sdk.ErrInternal(sdk.AppendMsgToErr("failed to JSON marshal result: %s", err.Error())) 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 ( import (
"testing" "testing"
@ -7,10 +7,11 @@ import (
abci "github.com/tendermint/tendermint/abci/types" abci "github.com/tendermint/tendermint/abci/types"
"github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/x/slashing/internal/types"
) )
func TestNewQuerier(t *testing.T) { func TestNewQuerier(t *testing.T) {
ctx, _, _, _, keeper := createTestInput(t, keeperTestParams()) ctx, _, _, _, keeper := CreateTestInput(t, TestParams())
querier := NewQuerier(keeper) querier := NewQuerier(keeper)
query := abci.RequestQuery{ query := abci.RequestQuery{
@ -24,9 +25,9 @@ func TestNewQuerier(t *testing.T) {
func TestQueryParams(t *testing.T) { func TestQueryParams(t *testing.T) {
cdc := codec.New() 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) res, errRes := queryParams(ctx, keeper)
require.NoError(t, errRes) require.NoError(t, errRes)

View File

@ -1,12 +1,13 @@
package slashing package keeper
import ( import (
sdk "github.com/cosmos/cosmos-sdk/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"
) )
// Stored by *validator* address (not operator address) // GetValidatorSigningInfo retruns the ValidatorSigningInfo for a specific validator
func (k Keeper) getValidatorSigningInfo(ctx sdk.Context, address sdk.ConsAddress) (info types.ValidatorSigningInfo, found bool) { // ConsAddress
func (k Keeper) GetValidatorSigningInfo(ctx sdk.Context, address sdk.ConsAddress) (info types.ValidatorSigningInfo, found bool) {
store := ctx.KVStore(k.storeKey) store := ctx.KVStore(k.storeKey)
bz := store.Get(types.GetValidatorSigningInfoKey(address)) bz := store.Get(types.GetValidatorSigningInfoKey(address))
if bz == nil { if bz == nil {
@ -18,7 +19,15 @@ func (k Keeper) getValidatorSigningInfo(ctx sdk.Context, address sdk.ConsAddress
return 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, func (k Keeper) IterateValidatorSigningInfos(ctx sdk.Context,
handler func(address sdk.ConsAddress, info types.ValidatorSigningInfo) (stop bool)) { 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) // GetValidatorMissedBlockBitArray gets the bit for the missed blocks array
func (k Keeper) SetValidatorSigningInfo(ctx sdk.Context, address sdk.ConsAddress, info types.ValidatorSigningInfo) { func (k Keeper) GetValidatorMissedBlockBitArray(ctx sdk.Context, address sdk.ConsAddress, index int64) (missed bool) {
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) {
store := ctx.KVStore(k.storeKey) store := ctx.KVStore(k.storeKey)
bz := store.Get(types.GetValidatorMissedBlockBitArrayKey(address, index)) bz := store.Get(types.GetValidatorMissedBlockBitArrayKey(address, index))
if bz == nil { if bz == nil {
@ -55,7 +57,8 @@ func (k Keeper) getValidatorMissedBlockBitArray(ctx sdk.Context, address sdk.Con
return 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, func (k Keeper) IterateValidatorMissedBlockBitArray(ctx sdk.Context,
address sdk.ConsAddress, handler func(index int64, missed bool) (stop bool)) { 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) // SetValidatorMissedBlockBitArray sets the bit that checks if the validator has
func (k Keeper) setValidatorMissedBlockBitArray(ctx sdk.Context, address sdk.ConsAddress, index int64, missed bool) { // 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) store := ctx.KVStore(k.storeKey)
bz := k.cdc.MustMarshalBinaryLengthPrefixed(missed) bz := k.cdc.MustMarshalBinaryLengthPrefixed(missed)
store.Set(types.GetValidatorMissedBlockBitArrayKey(address, index), bz) 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) { func (k Keeper) clearValidatorMissedBlockBitArray(ctx sdk.Context, address sdk.ConsAddress) {
store := ctx.KVStore(k.storeKey) store := ctx.KVStore(k.storeKey)
iter := sdk.KVStorePrefixIterator(store, types.GetValidatorMissedBlockBitArrayPrefixKey(address)) 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 // nolint:deadcode unused
package slashing // DONTCOVER
// noalias
package keeper
import ( import (
"encoding/hex" "encoding/hex"
@ -20,6 +22,7 @@ import (
"github.com/cosmos/cosmos-sdk/x/auth" "github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/bank" "github.com/cosmos/cosmos-sdk/x/bank"
"github.com/cosmos/cosmos-sdk/x/params" "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/staking"
"github.com/cosmos/cosmos-sdk/x/supply" "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) // TODO remove dependencies on staking (should only refer to validator set type from sdk)
var ( var (
pks = []crypto.PubKey{ Pks = []crypto.PubKey{
newPubKey("0B485CFC0EECC619440448436F8FC9DF40566F2369E72400281454CB552AFB50"), newPubKey("0B485CFC0EECC619440448436F8FC9DF40566F2369E72400281454CB552AFB50"),
newPubKey("0B485CFC0EECC619440448436F8FC9DF40566F2369E72400281454CB552AFB51"), newPubKey("0B485CFC0EECC619440448436F8FC9DF40566F2369E72400281454CB552AFB51"),
newPubKey("0B485CFC0EECC619440448436F8FC9DF40566F2369E72400281454CB552AFB52"), newPubKey("0B485CFC0EECC619440448436F8FC9DF40566F2369E72400281454CB552AFB52"),
} }
addrs = []sdk.ValAddress{ Addrs = []sdk.ValAddress{
sdk.ValAddress(pks[0].Address()), sdk.ValAddress(Pks[0].Address()),
sdk.ValAddress(pks[1].Address()), sdk.ValAddress(Pks[1].Address()),
sdk.ValAddress(pks[2].Address()), sdk.ValAddress(Pks[2].Address()),
} }
initTokens = sdk.TokensFromConsensusPower(200) InitTokens = sdk.TokensFromConsensusPower(200)
initCoins = sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, initTokens)) initCoins = sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, InitTokens))
) )
func createTestCodec() *codec.Codec { func createTestCodec() *codec.Codec {
@ -52,11 +55,11 @@ func createTestCodec() *codec.Codec {
return cdc 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) keyAcc := sdk.NewKVStoreKey(auth.StoreKey)
keyStaking := sdk.NewKVStoreKey(staking.StoreKey) keyStaking := sdk.NewKVStoreKey(staking.StoreKey)
tkeyStaking := sdk.NewTransientStoreKey(staking.TStoreKey) tkeyStaking := sdk.NewTransientStoreKey(staking.TStoreKey)
keySlashing := sdk.NewKVStoreKey(StoreKey) keySlashing := sdk.NewKVStoreKey(types.StoreKey)
keySupply := sdk.NewKVStoreKey(supply.StoreKey) keySupply := sdk.NewKVStoreKey(supply.StoreKey)
keyParams := sdk.NewKVStoreKey(params.StoreKey) keyParams := sdk.NewKVStoreKey(params.StoreKey)
tkeyParams := sdk.NewTransientStoreKey(params.TStoreKey) 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) 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)) supplyKeeper.SetSupply(ctx, supply.NewSupply(totalSupply))
sk := staking.NewKeeper(cdc, keyStaking, tkeyStaking, supplyKeeper, paramsKeeper.Subspace(staking.DefaultParamspace), staking.DefaultCodespace) 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) _ = staking.InitGenesis(ctx, sk, accountKeeper, supplyKeeper, genesis)
for _, addr := range addrs { for _, addr := range Addrs {
_, err = bk.AddCoins(ctx, sdk.AccAddress(addr), initCoins) _, err = bk.AddCoins(ctx, sdk.AccAddress(addr), initCoins)
} }
require.Nil(t, err) require.Nil(t, err)
paramstore := paramsKeeper.Subspace(DefaultParamspace) paramstore := paramsKeeper.Subspace(types.DefaultParamspace)
keeper := NewKeeper(cdc, keySlashing, &sk, paramstore, DefaultCodespace) keeper := NewKeeper(cdc, keySlashing, &sk, paramstore, types.DefaultCodespace)
sk.SetHooks(keeper.Hooks())
require.NotPanics(t, func() { keeper.SetParams(ctx, defaults)
InitGenesis(ctx, keeper, sk, GenesisState{defaults, nil, nil}) sk.SetHooks(keeper.Hooks())
})
return ctx, bk, sk, paramstore, keeper return ctx, bk, sk, paramstore, keeper
} }
@ -126,9 +127,13 @@ func newPubKey(pk string) (res crypto.PubKey) {
return pkEd return pkEd
} }
func testAddr(addr string) sdk.AccAddress { // Have to change these parameters for tests
res := []byte(addr) // lest the tests take forever
return res 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 { 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) amount := sdk.NewCoin(sdk.DefaultBondDenom, delAmount)
return staking.NewMsgDelegate(delAddr, valAddr, amount) 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" "github.com/cosmos/cosmos-sdk/codec"
) )
// Register concrete types on codec codec // RegisterCodec registers concrete types on codec
func RegisterCodec(cdc *codec.Codec) { func RegisterCodec(cdc *codec.Codec) {
cdc.RegisterConcrete(MsgUnjail{}, "cosmos-sdk/MsgUnjail", nil) cdc.RegisterConcrete(MsgUnjail{}, "cosmos-sdk/MsgUnjail", nil)
} }
// module codec // ModuleCdc defines the module codec
var ModuleCdc *codec.Codec var ModuleCdc *codec.Codec
func init() { func init() {

View File

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

View File

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

View File

@ -1,8 +1,11 @@
package types // noalias // noalias
// DONTCOVER
package types
import ( import (
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth" "github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/params"
"github.com/cosmos/cosmos-sdk/x/staking/exported" "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)) 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 // StakingKeeper expected staking keeper
type StakingKeeper interface { type StakingKeeper interface {
// iterate through validators by operator address, execute func for each validator // iterate through validators by operator address, execute func for each validator
@ -33,7 +44,7 @@ type StakingKeeper interface {
MaxValidators(sdk.Context) uint16 MaxValidators(sdk.Context) uint16
} }
// StakingHooks event hooks for staking validator object // StakingHooks event hooks for staking validator object (noalias)
type StakingHooks interface { type StakingHooks interface {
AfterValidatorCreated(ctx sdk.Context, valAddr sdk.ValAddress) // Must be called when a validator is created 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 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"` 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 // DefaultGenesisState - default GenesisState used by Cosmos Hub
func DefaultGenesisState() GenesisState { func DefaultGenesisState() GenesisState {
return GenesisState{ return GenesisState{

View File

@ -7,7 +7,7 @@ import (
) )
const ( const (
// module name // ModuleName is the name of the module
ModuleName = "slashing" ModuleName = "slashing"
// StoreKey is the store key string for slashing // StoreKey is the store key string for slashing
@ -20,13 +20,6 @@ const (
QuerierRoute = ModuleName QuerierRoute = ModuleName
) )
// Query endpoints supported by the slashing querier
const (
QueryParameters = "parameters"
QuerySigningInfo = "signingInfo"
QuerySigningInfos = "signingInfos"
)
// Keys for slashing store // Keys for slashing store
// Items are stored with the following key: values // Items are stored with the following key: values
// //
@ -41,12 +34,12 @@ var (
AddrPubkeyRelationKey = []byte{0x03} // Prefix for address-pubkey relation 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 { func GetValidatorSigningInfoKey(v sdk.ConsAddress) []byte {
return append(ValidatorSigningInfoKey, v.Bytes()...) 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) { func GetValidatorSigningInfoAddress(key []byte) (v sdk.ConsAddress) {
addr := key[1:] addr := key[1:]
if len(addr) != sdk.AddrLen { if len(addr) != sdk.AddrLen {
@ -55,19 +48,19 @@ func GetValidatorSigningInfoAddress(key []byte) (v sdk.ConsAddress) {
return sdk.ConsAddress(addr) 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 { func GetValidatorMissedBlockBitArrayPrefixKey(v sdk.ConsAddress) []byte {
return append(ValidatorMissedBlockBitArrayKey, v.Bytes()...) 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 { func GetValidatorMissedBlockBitArrayKey(v sdk.ConsAddress, i int64) []byte {
b := make([]byte, 8) b := make([]byte, 8)
binary.LittleEndian.PutUint64(b, uint64(i)) binary.LittleEndian.PutUint64(b, uint64(i))
return append(GetValidatorMissedBlockBitArrayPrefixKey(v), b...) 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 { func GetAddrPubkeyRelationKey(address []byte) []byte {
return append(AddrPubkeyRelationKey, address...) 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 ValidatorAddr sdk.ValAddress `json:"address" yaml:"address"` // address of the validator operator
} }
// NewMsgUnjail creates a new MsgUnjail instance
func NewMsgUnjail(validatorAddr sdk.ValAddress) MsgUnjail { func NewMsgUnjail(validatorAddr sdk.ValAddress) MsgUnjail {
return MsgUnjail{ return MsgUnjail{
ValidatorAddr: validatorAddr, ValidatorAddr: validatorAddr,
@ -25,13 +26,13 @@ func (msg MsgUnjail) GetSigners() []sdk.AccAddress {
return []sdk.AccAddress{sdk.AccAddress(msg.ValidatorAddr)} 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 { func (msg MsgUnjail) GetSignBytes() []byte {
bz := ModuleCdc.MustMarshalJSON(msg) bz := ModuleCdc.MustMarshalJSON(msg)
return sdk.MustSortJSON(bz) return sdk.MustSortJSON(bz)
} }
// quick validity check // ValidateBasic validity check for the AnteHandler
func (msg MsgUnjail) ValidateBasic() sdk.Error { func (msg MsgUnjail) ValidateBasic() sdk.Error {
if msg.ValidatorAddr.Empty() { if msg.ValidatorAddr.Empty() {
return ErrBadValidatorAddr(DefaultCodespace) 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 { func (p Params) String() string {
return fmt.Sprintf(`Slashing Params: return fmt.Sprintf(`Slashing Params:
MaxEvidenceAge: %s MaxEvidenceAge: %s
@ -77,26 +78,22 @@ func (p Params) String() string {
p.SlashFractionDowntime) p.SlashFractionDowntime)
} }
// Implements params.ParamSet // ParamSetPairs - Implements params.ParamSet
func (p *Params) ParamSetPairs() params.ParamSetPairs { func (p *Params) ParamSetPairs() params.ParamSetPairs {
return params.ParamSetPairs{ return params.ParamSetPairs{
{KeyMaxEvidenceAge, &p.MaxEvidenceAge}, params.NewParamSetPair(KeyMaxEvidenceAge, &p.MaxEvidenceAge),
{KeySignedBlocksWindow, &p.SignedBlocksWindow}, params.NewParamSetPair(KeySignedBlocksWindow, &p.SignedBlocksWindow),
{KeyMinSignedPerWindow, &p.MinSignedPerWindow}, params.NewParamSetPair(KeyMinSignedPerWindow, &p.MinSignedPerWindow),
{KeyDowntimeJailDuration, &p.DowntimeJailDuration}, params.NewParamSetPair(KeyDowntimeJailDuration, &p.DowntimeJailDuration),
{KeySlashFractionDoubleSign, &p.SlashFractionDoubleSign}, params.NewParamSetPair(KeySlashFractionDoubleSign, &p.SlashFractionDoubleSign),
{KeySlashFractionDowntime, &p.SlashFractionDowntime}, params.NewParamSetPair(KeySlashFractionDowntime, &p.SlashFractionDowntime),
} }
} }
// Default parameters for this module // DefaultParams defines the parameters for this module
func DefaultParams() Params { func DefaultParams() Params {
return Params{ return NewParams(
MaxEvidenceAge: DefaultMaxEvidenceAge, DefaultMaxEvidenceAge, DefaultSignedBlocksWindow, DefaultMinSignedPerWindow,
SignedBlocksWindow: DefaultSignedBlocksWindow, DefaultDowntimeJailDuration, DefaultSlashFractionDoubleSign, DefaultSlashFractionDowntime,
MinSignedPerWindow: DefaultMinSignedPerWindow, )
DowntimeJailDuration: DefaultDowntimeJailDuration,
SlashFractionDoubleSign: DefaultSlashFractionDoubleSign,
SlashFractionDowntime: DefaultSlashFractionDowntime,
}
} }

View File

@ -4,12 +4,22 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types" 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: // QuerySigningInfoParams defines the params for the following queries:
// - 'custom/slashing/signingInfo' // - 'custom/slashing/signingInfo'
type QuerySigningInfoParams struct { type QuerySigningInfoParams struct {
ConsAddress sdk.ConsAddress ConsAddress sdk.ConsAddress
} }
// NewQuerySigningInfoParams creates a new QuerySigningInfoParams instance
func NewQuerySigningInfoParams(consAddr sdk.ConsAddress) QuerySigningInfoParams { func NewQuerySigningInfoParams(consAddr sdk.ConsAddress) QuerySigningInfoParams {
return QuerySigningInfoParams{consAddr} return QuerySigningInfoParams{consAddr}
} }
@ -20,6 +30,7 @@ type QuerySigningInfosParams struct {
Page, Limit int Page, Limit int
} }
// NewQuerySigningInfosParams creates a new QuerySigningInfosParams instance
func NewQuerySigningInfosParams(page, limit int) QuerySigningInfosParams { func NewQuerySigningInfosParams(page, limit int) QuerySigningInfosParams {
return QuerySigningInfosParams{page, limit} return QuerySigningInfosParams{page, limit}
} }

View File

@ -7,7 +7,7 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
) )
// Signing info for a validator // ValidatorSigningInfo defines the signing info for a validator
type ValidatorSigningInfo struct { type ValidatorSigningInfo struct {
Address sdk.ConsAddress `json:"address" yaml:"address"` // validator consensus address 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 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) 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( func NewValidatorSigningInfo(
condAddr sdk.ConsAddress, startHeight, indexOffset int64, condAddr sdk.ConsAddress, startHeight, indexOffset int64,
jailedUntil time.Time, tombstoned bool, missedBlocksCounter 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 { func (i ValidatorSigningInfo) String() string {
return fmt.Sprintf(`Validator Signing Info: return fmt.Sprintf(`Validator Signing Info:
Address: %s Address: %s

View File

@ -14,7 +14,7 @@ import (
"github.com/cosmos/cosmos-sdk/types/module" "github.com/cosmos/cosmos-sdk/types/module"
"github.com/cosmos/cosmos-sdk/x/slashing/client/cli" "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/client/rest"
"github.com/cosmos/cosmos-sdk/x/slashing/types" "github.com/cosmos/cosmos-sdk/x/slashing/internal/types"
) )
var ( 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
}