2018-05-23 13:25:56 -07:00
package slashing
import (
"fmt"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/wire"
2018-06-28 17:54:47 -07:00
"github.com/tendermint/tendermint/crypto"
2018-05-23 13:25:56 -07:00
)
// Keeper of the slashing store
type Keeper struct {
2018-06-01 15:27:37 -07:00
storeKey sdk . StoreKey
cdc * wire . Codec
validatorSet sdk . ValidatorSet
2018-05-23 13:25:56 -07:00
// codespace
codespace sdk . CodespaceType
}
// NewKeeper creates a slashing keeper
2018-06-01 15:27:37 -07:00
func NewKeeper ( cdc * wire . Codec , key sdk . StoreKey , vs sdk . ValidatorSet , codespace sdk . CodespaceType ) Keeper {
2018-05-23 13:25:56 -07:00
keeper := Keeper {
2018-06-01 15:27:37 -07:00
storeKey : key ,
cdc : cdc ,
validatorSet : vs ,
codespace : codespace ,
2018-05-23 13:25:56 -07:00
}
return keeper
}
// handle a validator signing two blocks at the same height
2018-06-29 20:34:55 -07:00
func ( k Keeper ) handleDoubleSign ( ctx sdk . Context , pubkey crypto . PubKey , infractionHeight int64 , timestamp int64 , power int64 ) {
2018-05-23 13:25:56 -07:00
logger := ctx . Logger ( ) . With ( "module" , "x/slashing" )
2018-06-29 20:34:55 -07:00
time := ctx . BlockHeader ( ) . Time
age := time - timestamp
address := pubkey . Address ( )
2018-05-28 15:10:52 -07:00
// Double sign too old
2018-05-23 13:25:56 -07:00
if age > MaxEvidenceAge {
2018-06-29 20:34:55 -07:00
logger . Info ( fmt . Sprintf ( "Ignored double sign from %s at height %d, age of %d past max age of %d" , pubkey . Address ( ) , infractionHeight , age , MaxEvidenceAge ) )
2018-05-23 13:25:56 -07:00
return
}
2018-05-28 15:10:52 -07:00
// Double sign confirmed
2018-06-29 20:34:55 -07:00
logger . Info ( fmt . Sprintf ( "Confirmed double sign from %s at height %d, age of %d less than max age of %d" , pubkey . Address ( ) , infractionHeight , age , MaxEvidenceAge ) )
// Slash validator
k . validatorSet . Slash ( ctx , pubkey , infractionHeight , power , SlashFractionDoubleSign )
// Revoke validator
k . validatorSet . Revoke ( ctx , pubkey )
// Jail validator
signInfo , found := k . getValidatorSigningInfo ( ctx , address )
if ! found {
panic ( fmt . Sprintf ( "Expected signing info for validator %s but not found" , address ) )
}
signInfo . JailedUntil = time + DoubleSignUnbondDuration
k . setValidatorSigningInfo ( ctx , address , signInfo )
2018-05-23 13:25:56 -07:00
}
// handle a validator signature, must be called once per validator per block
2018-06-29 20:34:55 -07:00
func ( k Keeper ) handleValidatorSignature ( ctx sdk . Context , pubkey crypto . PubKey , power int64 , signed bool ) {
2018-05-23 13:25:56 -07:00
logger := ctx . Logger ( ) . With ( "module" , "x/slashing" )
height := ctx . BlockHeight ( )
address := pubkey . Address ( )
2018-05-28 15:10:52 -07:00
// Local index, so counts blocks validator *should* have signed
2018-06-10 18:03:52 -07:00
// Will use the 0-value default signing info if not present, except for start height
2018-06-10 17:55:54 -07:00
signInfo , found := k . getValidatorSigningInfo ( ctx , address )
if ! found {
2018-06-11 12:47:35 -07:00
// If this validator has never been seen before, construct a new SigningInfo with the correct start height
signInfo = NewValidatorSigningInfo ( height , 0 , 0 , 0 )
2018-06-10 17:55:54 -07:00
}
2018-05-25 17:05:46 -07:00
index := signInfo . IndexOffset % SignedBlocksWindow
2018-05-28 14:46:08 -07:00
signInfo . IndexOffset ++
2018-05-28 15:10:52 -07:00
// Update signed block bit array & counter
2018-05-30 15:32:08 -07:00
// This counter just tracks the sum of the bit array
// That way we avoid needing to read/write the whole array each time
2018-05-23 13:25:56 -07:00
previous := k . getValidatorSigningBitArray ( ctx , address , index )
2018-05-28 17:48:29 -07:00
if previous == signed {
2018-05-30 15:32:08 -07:00
// Array value at this index has not changed, no need to update counter
2018-05-28 17:48:29 -07:00
} else if previous && ! signed {
2018-05-30 15:32:08 -07:00
// Array value has changed from signed to unsigned, decrement counter
2018-05-23 13:25:56 -07:00
k . setValidatorSigningBitArray ( ctx , address , index , false )
signInfo . SignedBlocksCounter --
} else if ! previous && signed {
2018-05-30 15:32:08 -07:00
// Array value has changed from unsigned to signed, increment counter
2018-05-23 13:25:56 -07:00
k . setValidatorSigningBitArray ( ctx , address , index , true )
signInfo . SignedBlocksCounter ++
}
2018-05-28 15:10:52 -07:00
2018-06-29 20:34:55 -07:00
if ! signed {
logger . Info ( fmt . Sprintf ( "Absent validator %s at height %d, %d signed, threshold %d" , pubkey . Address ( ) , height , signInfo . SignedBlocksCounter , MinSignedPerWindow ) )
}
2018-05-23 13:25:56 -07:00
minHeight := signInfo . StartHeight + SignedBlocksWindow
if height > minHeight && signInfo . SignedBlocksCounter < MinSignedPerWindow {
2018-05-28 15:10:52 -07:00
// Downtime confirmed, slash, revoke, and jail the validator
2018-05-25 15:13:29 -07:00
logger . Info ( fmt . Sprintf ( "Validator %s past min height of %d and below signed blocks threshold of %d" , pubkey . Address ( ) , minHeight , MinSignedPerWindow ) )
2018-06-29 20:34:55 -07:00
k . validatorSet . Slash ( ctx , pubkey , height , power , SlashFractionDowntime )
2018-06-01 15:27:37 -07:00
k . validatorSet . Revoke ( ctx , pubkey )
2018-05-28 13:08:13 -07:00
signInfo . JailedUntil = ctx . BlockHeader ( ) . Time + DowntimeUnbondDuration
2018-05-23 13:25:56 -07:00
}
2018-05-28 17:35:42 -07:00
// Set the updated signing info
k . setValidatorSigningInfo ( ctx , address , signInfo )
2018-05-23 13:25:56 -07:00
}