# 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) } ```