forked from cosmos/cosmos-sdk
-
Notifications
You must be signed in to change notification settings - Fork 0
/
slashing_period.go
134 lines (116 loc) · 5.76 KB
/
slashing_period.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
package slashing
import (
"encoding/binary"
"fmt"
sdk "github.com/cosmos/cosmos-sdk/types"
stake "github.com/cosmos/cosmos-sdk/x/stake/types"
)
// 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 {
panic(fmt.Sprintf("slashing period ended before infraction: validator %s, infraction height %d, slashing period ended at %d", address, infractionHeight, slashingPeriod.EndHeight))
}
// 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
}
// Stored by validator Tendermint address (not operator address)
// 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.
// Slashing periods are created upon validator bonding.
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() {
panic(fmt.Sprintf("expected to find slashing period for validator %s before height %d, but none was found", address, height))
}
slashingPeriod = k.unmarshalSlashingPeriodKeyValue(iterator.Key(), iterator.Value())
return
}
// Iterate over all slashing periods in the store, calling on each
// decode slashing period a provided handler function
// Stop if the provided handler function returns true
func (k Keeper) IterateValidatorSlashingPeriods(ctx sdk.Context, handler func(slashingPeriod ValidatorSlashingPeriod) (stop bool)) {
store := ctx.KVStore(k.storeKey)
iter := sdk.KVStorePrefixIterator(store, ValidatorSlashingPeriodKey)
defer iter.Close()
for ; iter.Valid(); iter.Next() {
slashingPeriod := k.unmarshalSlashingPeriodKeyValue(iter.Key(), iter.Value())
if handler(slashingPeriod) {
break
}
}
}
// Delete all slashing periods in the store.
func (k Keeper) DeleteValidatorSlashingPeriods(ctx sdk.Context) {
store := ctx.KVStore(k.storeKey)
iter := sdk.KVStorePrefixIterator(store, ValidatorSlashingPeriodKey)
for ; iter.Valid(); iter.Next() {
store.Delete(iter.Key())
}
iter.Close()
}
// Stored by validator Tendermint address (not operator address)
// 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.MustMarshalBinaryLengthPrefixed(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.MustUnmarshalBinaryLengthPrefixed(value, &slashingPeriodValue)
address := sdk.ConsAddress(key[1 : 1+sdk.AddrLen])
startHeight := int64(binary.BigEndian.Uint64(key[1+sdk.AddrLen:1+sdk.AddrLen+8]) - uint64(stake.ValidatorUpdateDelay))
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)
}