cosmos-sdk/docs/spec/slashing/end_block.md

111 lines
3.6 KiB
Markdown
Raw Normal View History

# End-Block
2018-05-25 15:52:34 -07:00
## Slashing
Messges which may compromise the safety of the underlying consensus protocol ("equivocations")
result in some amount of the offending validator's shares being removed ("slashed").
Currently, such messages include only the following:
- prevotes by the same validator for more than one BlockID at the same
Height and Round
- precommits by the same validator for more than one BlockID at the same
Height and Round
We call any such pair of conflicting votes `Evidence`. Full nodes in the network prioritize the
detection and gossipping of `Evidence` so that it may be rapidly included in blocks and the offending
validators punished.
For some `evidence` to be valid, it must satisfy:
`evidence.Timestamp >= block.Timestamp - MAX_EVIDENCE_AGE`
where `evidence.Timestamp` is the timestamp in the block at height
`evidence.Height` and `block.Timestamp` is the current block timestamp.
2018-06-14 20:26:21 -07:00
If valid evidence is included in a block, the validator's stake is reduced by `SLASH_PROPORTION` of
what their stake was when the equivocation occurred (rather than when the evidence was discovered):
2018-05-25 15:52:34 -07:00
```
2018-06-14 20:26:21 -07:00
curVal := validator
oldVal := loadValidator(evidence.Height, evidence.Address)
slashAmount := SLASH_PROPORTION * oldVal.Shares
curVal.Shares = max(0, curVal.Shares - slashAmount)
2018-05-25 15:52:34 -07:00
```
This ensures that offending validators are punished the same amount whether they
act as a single validator with X stake or as N validators with collectively X
stake.
2018-06-14 20:26:21 -07:00
We also need to loop through the unbondings and redelegations to slash them as
well:
```
unbondings := getUnbondings(validator.Address)
for unbond in unbondings {
if was not bonded before evidence.Height {
continue
}
unbond.InitialTokens
burn := unbond.InitialTokens * SLASH_PROPORTION
unbond.Tokens = max(0, unbond.Tokens - burn)
2018-06-14 20:26:21 -07:00
}
// only care if source gets slashed because we're already bonded to destination
// so if destination validator gets slashed our delegation just has same shares
// of smaller pool.
redels := getRedelegationsBySource(validator.Address)
for redel in redels {
if was not bonded before evidence.Height {
continue
}
burn := redel.InitialTokens * SLASH_PROPORTION
amount := unbondFromValidator(redel.Destination, burn)
destroy(amount)
}
```
2018-05-25 15:52:34 -07:00
## Automatic Unbonding
At the beginning of each block, we update the signing info for each validator and check if they should be automatically unbonded:
```
height := block.Height
2018-05-25 15:52:34 -07:00
for val in block.Validators:
signInfo = SigningInfo.Get(val.Address)
2018-06-14 20:26:21 -07:00
if signInfo == nil{
signInfo.StartHeight = height
}
index := signInfo.IndexOffset % SIGNED_BLOCKS_WINDOW
signInfo.IndexOffset++
previous = SigningBitArray.Get(val.Address, index)
// update counter if array has changed
if previous and val in block.AbsentValidators:
SigningBitArray.Set(val.Address, index, false)
signInfo.SignedBlocksCounter--
else if !previous and val not in block.AbsentValidators:
SigningBitArray.Set(val.Address, index, true)
signInfo.SignedBlocksCounter++
// else previous == val not in block.AbsentValidators, no change
// validator must be active for at least SIGNED_BLOCKS_WINDOW
// before they can be automatically unbonded for failing to be
// included in 50% of the recent LastCommits
minHeight = signInfo.StartHeight + SIGNED_BLOCKS_WINDOW
minSigned = SIGNED_BLOCKS_WINDOW / 2
if height > minHeight AND signInfo.SignedBlocksCounter < minSigned:
signInfo.JailedUntil = block.Time + DOWNTIME_UNBOND_DURATION
2018-06-14 20:26:21 -07:00
slash & unbond the validator
2018-06-14 20:26:21 -07:00
SigningInfo.Set(val.Address, signInfo)
2018-05-25 15:52:34 -07:00
```