95 lines
3.9 KiB
Markdown
95 lines
3.9 KiB
Markdown
# BeginBlock
|
|
|
|
## Liveness Tracking
|
|
|
|
At the beginning of each block, we update the `ValidatorSigningInfo` for each
|
|
validator and check if they've crossed below the liveness threshold over a
|
|
sliding window. This sliding window is defined by `SignedBlocksWindow` and the
|
|
index in this window is determined by `IndexOffset` found in the validator's
|
|
`ValidatorSigningInfo`. For each block processed, the `IndexOffset` is incremented
|
|
regardless if the validator signed or not. Once the index is determined, the
|
|
`MissedBlocksBitArray` and `MissedBlocksCounter` are updated accordingly.
|
|
|
|
Finally, in order to determine if a validator crosses below the liveness threshold,
|
|
we fetch the maximum number of blocks missed, `maxMissed`, which is
|
|
`SignedBlocksWindow - (MinSignedPerWindow * SignedBlocksWindow)` and the minimum
|
|
height at which we can determine liveness, `minHeight`. If the current block is
|
|
greater than `minHeight` and the validator's `MissedBlocksCounter` is greater than
|
|
`maxMissed`, they will be slashed by `SlashFractionDowntime`, will be jailed
|
|
for `DowntimeJailDuration`, and have the following values reset:
|
|
`MissedBlocksBitArray`, `MissedBlocksCounter`, and `IndexOffset`.
|
|
|
|
__Note__: Liveness slashes do **NOT** lead to a tombstombing.
|
|
|
|
```go
|
|
height := block.Height
|
|
|
|
for vote in block.LastCommitInfo.Votes {
|
|
signInfo := GetValidatorSigningInfo(vote.Validator.Address)
|
|
|
|
// This is a relative index, so we counts blocks the validator SHOULD have
|
|
// signed. We use the 0-value default signing info if not present, except for
|
|
// start height.
|
|
index := signInfo.IndexOffset % SignedBlocksWindow()
|
|
signInfo.IndexOffset++
|
|
|
|
// Update MissedBlocksBitArray and MissedBlocksCounter. The MissedBlocksCounter
|
|
// just tracks the sum of MissedBlocksBitArray. That way we avoid needing to
|
|
// read/write the whole array each time.
|
|
missedPrevious := GetValidatorMissedBlockBitArray(vote.Validator.Address, index)
|
|
missed := !signed
|
|
|
|
switch {
|
|
case !missedPrevious && missed:
|
|
// array index has changed from not missed to missed, increment counter
|
|
SetValidatorMissedBlockBitArray(vote.Validator.Address, index, true)
|
|
signInfo.MissedBlocksCounter++
|
|
|
|
case missedPrevious && !missed:
|
|
// array index has changed from missed to not missed, decrement counter
|
|
SetValidatorMissedBlockBitArray(vote.Validator.Address, index, false)
|
|
signInfo.MissedBlocksCounter--
|
|
|
|
default:
|
|
// array index at this index has not changed; no need to update counter
|
|
}
|
|
|
|
if missed {
|
|
// emit events...
|
|
}
|
|
|
|
minHeight := signInfo.StartHeight + SignedBlocksWindow()
|
|
maxMissed := SignedBlocksWindow() - MinSignedPerWindow()
|
|
|
|
// If we are past the minimum height and the validator has missed too many
|
|
// jail and slash them.
|
|
if height > minHeight && signInfo.MissedBlocksCounter > maxMissed {
|
|
validator := ValidatorByConsAddr(vote.Validator.Address)
|
|
|
|
// emit events...
|
|
|
|
// We need to retrieve the stake distribution which signed the block, so we
|
|
// subtract ValidatorUpdateDelay from the block height, and subtract an
|
|
// additional 1 since this is the LastCommit.
|
|
//
|
|
// Note, that this CAN result in a negative "distributionHeight" up to
|
|
// -ValidatorUpdateDelay-1, i.e. at the end of the pre-genesis block (none) = at the beginning of the genesis block.
|
|
// That's fine since this is just used to filter unbonding delegations & redelegations.
|
|
distributionHeight := height - sdk.ValidatorUpdateDelay - 1
|
|
|
|
Slash(vote.Validator.Address, distributionHeight, vote.Validator.Power, SlashFractionDowntime())
|
|
Jail(vote.Validator.Address)
|
|
|
|
signInfo.JailedUntil = block.Time.Add(DowntimeJailDuration())
|
|
|
|
// We need to reset the counter & array so that the validator won't be
|
|
// immediately slashed for downtime upon rebonding.
|
|
signInfo.MissedBlocksCounter = 0
|
|
signInfo.IndexOffset = 0
|
|
ClearValidatorMissedBlockBitArray(vote.Validator.Address)
|
|
}
|
|
|
|
SetValidatorSigningInfo(vote.Validator.Address, signInfo)
|
|
}
|
|
```
|