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  ```