2018-08-31 17:01:23 -07:00
package slashing
import (
"encoding/binary"
"fmt"
sdk "github.com/cosmos/cosmos-sdk/types"
2018-10-05 10:56:17 -07:00
stake "github.com/cosmos/cosmos-sdk/x/stake/types"
2018-08-31 17:01:23 -07:00
)
// Cap an infraction's slash amount by the slashing period in which it was committed
func ( k Keeper ) capBySlashingPeriod ( ctx sdk . Context , address sdk . ConsAddress , fraction sdk . Dec , infractionHeight int64 ) ( revisedFraction sdk . Dec ) {
// Fetch the newest slashing period starting before this infraction was committed
slashingPeriod := k . getValidatorSlashingPeriodForHeight ( ctx , address , infractionHeight )
// Sanity check
if slashingPeriod . EndHeight > 0 && slashingPeriod . EndHeight < infractionHeight {
2018-10-05 10:42:52 -07:00
panic ( fmt . Sprintf ( "slashing period ended before infraction: validator %s, infraction height %d, slashing period ended at %d" , address , infractionHeight , slashingPeriod . EndHeight ) )
2018-08-31 17:01:23 -07:00
}
// Calculate the updated total slash amount
// This is capped at the slashing fraction for the worst infraction within this slashing period
totalToSlash := sdk . MaxDec ( slashingPeriod . SlashedSoFar , fraction )
// Calculate the remainder which we now must slash
revisedFraction = totalToSlash . Sub ( slashingPeriod . SlashedSoFar )
// Update the slashing period struct
slashingPeriod . SlashedSoFar = totalToSlash
k . addOrUpdateValidatorSlashingPeriod ( ctx , slashingPeriod )
return
}
2018-09-08 01:44:58 -07:00
// Stored by validator Tendermint address (not operator address)
2018-08-31 17:01:23 -07:00
// This function retrieves the most recent slashing period starting
// before a particular height - so the slashing period that was "in effect"
// at the time of an infraction committed at that height.
2018-10-22 01:18:10 -07:00
// Slashing periods are created upon validator bonding.
2018-08-31 17:01:23 -07:00
func ( k Keeper ) getValidatorSlashingPeriodForHeight ( ctx sdk . Context , address sdk . ConsAddress , height int64 ) ( slashingPeriod ValidatorSlashingPeriod ) {
store := ctx . KVStore ( k . storeKey )
// Get the most recent slashing period at or before the infraction height
start := GetValidatorSlashingPeriodPrefix ( address )
end := sdk . PrefixEndBytes ( GetValidatorSlashingPeriodKey ( address , height ) )
iterator := store . ReverseIterator ( start , end )
if ! iterator . Valid ( ) {
2018-10-05 10:42:52 -07:00
panic ( fmt . Sprintf ( "expected to find slashing period for validator %s before height %d, but none was found" , address , height ) )
2018-08-31 17:01:23 -07:00
}
slashingPeriod = k . unmarshalSlashingPeriodKeyValue ( iterator . Key ( ) , iterator . Value ( ) )
return
}
2018-09-08 01:44:58 -07:00
// Stored by validator Tendermint address (not operator address)
2018-08-31 17:01:23 -07:00
// This function sets a validator slashing period for a particular validator,
// start height, end height, and current slashed-so-far total, or updates
// an existing slashing period for the same validator and start height.
func ( k Keeper ) addOrUpdateValidatorSlashingPeriod ( ctx sdk . Context , slashingPeriod ValidatorSlashingPeriod ) {
slashingPeriodValue := ValidatorSlashingPeriodValue {
EndHeight : slashingPeriod . EndHeight ,
SlashedSoFar : slashingPeriod . SlashedSoFar ,
}
store := ctx . KVStore ( k . storeKey )
bz := k . cdc . MustMarshalBinary ( slashingPeriodValue )
store . Set ( GetValidatorSlashingPeriodKey ( slashingPeriod . ValidatorAddr , slashingPeriod . StartHeight ) , bz )
}
// Unmarshal key/value into a ValidatorSlashingPeriod
func ( k Keeper ) unmarshalSlashingPeriodKeyValue ( key [ ] byte , value [ ] byte ) ValidatorSlashingPeriod {
var slashingPeriodValue ValidatorSlashingPeriodValue
k . cdc . MustUnmarshalBinary ( value , & slashingPeriodValue )
address := sdk . ConsAddress ( key [ 1 : 1 + sdk . AddrLen ] )
2018-10-05 10:56:17 -07:00
startHeight := int64 ( binary . BigEndian . Uint64 ( key [ 1 + sdk . AddrLen : 1 + sdk . AddrLen + 8 ] ) - uint64 ( stake . ValidatorUpdateDelay ) )
2018-08-31 17:01:23 -07:00
return ValidatorSlashingPeriod {
ValidatorAddr : address ,
StartHeight : startHeight ,
EndHeight : slashingPeriodValue . EndHeight ,
SlashedSoFar : slashingPeriodValue . SlashedSoFar ,
}
}
// Construct a new `ValidatorSlashingPeriod` struct
func NewValidatorSlashingPeriod ( startHeight int64 , endHeight int64 , slashedSoFar sdk . Dec ) ValidatorSlashingPeriod {
return ValidatorSlashingPeriod {
StartHeight : startHeight ,
EndHeight : endHeight ,
SlashedSoFar : slashedSoFar ,
}
}
// Slashing period for a validator
type ValidatorSlashingPeriod struct {
ValidatorAddr sdk . ConsAddress ` json:"validator_addr" ` // validator which this slashing period is for
StartHeight int64 ` json:"start_height" ` // starting height of the slashing period
EndHeight int64 ` json:"end_height" ` // ending height of the slashing period, or sentinel value of 0 for in-progress
SlashedSoFar sdk . Dec ` json:"slashed_so_far" ` // fraction of validator stake slashed so far in this slashing period
}
// Value part of slashing period (validator address & start height are stored in the key)
type ValidatorSlashingPeriodValue struct {
EndHeight int64 ` json:"end_height" `
SlashedSoFar sdk . Dec ` json:"slashed_so_far" `
}
// Return human readable slashing period
func ( p ValidatorSlashingPeriod ) HumanReadableString ( ) string {
return fmt . Sprintf ( "Start height: %d, end height: %d, slashed so far: %v" ,
p . StartHeight , p . EndHeight , p . SlashedSoFar )
}