github.com/Finschia/finschia-sdk@v0.48.1/x/slashing/spec/04_begin_block.md (about) 1 <!-- 2 order: 4 3 --> 4 5 # BeginBlock 6 7 ## Liveness Tracking 8 9 At the beginning of each block, we update the `ValidatorSigningInfo` for each 10 voter and check if they've crossed below the liveness threshold over a 11 sliding window. This sliding window is defined by `SignedBlocksWindow` and the 12 index in this window is determined by `VoterSetCounter` found in the voter's 13 `ValidatorSigningInfo`. For each vote processed, the `VoterSetCounter` is incremented 14 regardless if the voter signed or not. Once the index is determined, the 15 `MissedBlocksBitArray` and `MissedBlocksCounter` are updated accordingly. 16 17 Finally, in order to determine if a voter crosses below the liveness threshold, 18 we fetch the maximum number of blocks missed, `maxMissed`, which is 19 `SignedBlocksWindow - (MinSignedPerWindow * SignedBlocksWindow)` and the minimum 20 height at which we can determine liveness, `minVoterSetCount`. If the voter set counter is 21 greater than `minVoterSetCount` and the voter's `MissedBlocksCounter` is greater than 22 `maxMissed`, they will be slashed by `SlashFractionDowntime`, will be jailed 23 for `DowntimeJailDuration`, and have the following values reset: 24 `MissedBlocksBitArray` and `MissedBlocksCounter`. 25 26 **Note**: Liveness slashes do **NOT** lead to a tombstombing. 27 28 ```go 29 height := ctx.BlockHeight() 30 31 for _, voteInfo := range req.LastCommitInfo.getVotes() { 32 // fetch the validator public key 33 consAddr := sdk.BytesToConsAddress(voteInfo.Validator.Address) 34 if _, err := k.GetPubkey(ctx, addr); err != nil { 35 panic(fmt.Sprintf("Validator consensus-address %s not found", consAddr)) 36 } 37 38 // fetch signing info 39 signInfo, found := k.GetValidatorSigningInfo(ctx, consAddr) 40 if !found { 41 panic(fmt.Sprintf("Expected signing info for validator %s but not found", consAddr)) 42 } 43 44 // this is a relative index, so it counts blocks the validator *should* have signed 45 // will use the 0-value default signing info if not present, except for the beginning 46 voterSetCounter := signInfo.VoterSetCounter 47 signInfo.VoterSetCounter++ 48 index := voterSetCounter % k.SignedBlocksWindow(ctx) 49 50 // Update signed block bit array & counter 51 // This counter just tracks the sum of the bit array 52 // That way we avoid needing to read/write the whole array each time 53 previous := k.GetValidatorMissedBlockBitArray(ctx, consAddr, index) 54 missed := !voteInfo.SignedLastBlock 55 switch { 56 case !previous && missed: 57 // Array value has changed from not missed to missed, increment counter 58 k.SetValidatorMissedBlockBitArray(ctx, consAddr, index, true) 59 signInfo.MissedBlocksCounter++ 60 case previous && !missed: 61 // Array value has changed from missed to not missed, decrement counter 62 k.SetValidatorMissedBlockBitArray(ctx, consAddr, index, false) 63 signInfo.MissedBlocksCounter-- 64 default: 65 // Array value at this index has not changed, no need to update counter 66 } 67 68 minSignedPerWindow := k.MinSignedPerWindow(ctx) 69 70 if missed { 71 // emit events... 72 } 73 74 minVoterSetCount := k.SignedBlocksWindow(ctx) 75 maxMissed := k.SignedBlocksWindow(ctx) - minSignedPerWindow 76 77 // if we have joined enough times to voter set and the validator has missed too many blocks, punish them 78 if voterSetCounter >= minVoterSetCount && signInfo.MissedBlocksCounter > maxMissed { 79 validator := k.sk.ValidatorByConsAddr(ctx, consAddr) 80 if validator != nil && !validator.IsJailed() { 81 // Downtime confirmed: slash and jail the validator 82 // We need to retrieve the stake distribution which signed the block, so we subtract ValidatorUpdateDelay from the evidence height, 83 // and subtract an additional 1 since this is the LastCommit. 84 // Note that this *can* result in a negative "distributionHeight" up to -ValidatorUpdateDelay-1, 85 // i.e. at the end of the pre-genesis block (none) = at the beginning of the genesis block. 86 // That's fine since this is just used to filter unbonding delegations & redelegations. 87 distributionHeight := height - sdk.ValidatorUpdateDelay - 1 88 89 // emit events... 90 91 k.sk.Slash(ctx, consAddr, distributionHeight, voteInfo.Validator.Power, k.SlashFractionDowntime(ctx)) 92 k.sk.Jail(ctx, consAddr) 93 94 signInfo.JailedUntil = ctx.BlockHeader().Time.Add(k.DowntimeJailDuration(ctx)) 95 96 // We need to reset the counter & array so that the validator won't be immediately slashed for downtime upon rebonding. 97 signInfo.MissedBlocksCounter = 0 98 signInfo.VoterSetCounter = 0 99 k.clearValidatorMissedBlockBitArray(ctx, consAddr) 100 101 // log events... 102 } else { 103 // validator was (a) not found or (b) already jailed so we do not slash 104 105 // log events... 106 } 107 } 108 109 // Set the updated signing info 110 k.SetValidatorSigningInfo(ctx, consAddr, signInfo) 111 } 112 ```