github.com/Finschia/finschia-sdk@v0.48.1/x/slashing/keeper/infractions.go (about) 1 package keeper 2 3 import ( 4 "fmt" 5 6 cryptotypes "github.com/Finschia/finschia-sdk/crypto/types" 7 sdk "github.com/Finschia/finschia-sdk/types" 8 "github.com/Finschia/finschia-sdk/x/slashing/types" 9 ) 10 11 // HandleValidatorSignature handles a validator signature, must be called once per validator per block. 12 func (k Keeper) HandleValidatorSignature(ctx sdk.Context, addr cryptotypes.Address, power int64, signed bool) { 13 logger := k.Logger(ctx) 14 height := ctx.BlockHeight() 15 16 // fetch the validator public key 17 consAddr := sdk.ConsAddress(addr) 18 if _, err := k.GetPubkey(ctx, addr); err != nil { 19 panic(fmt.Sprintf("Validator consensus-address %s not found", consAddr)) 20 } 21 22 // fetch signing info 23 signInfo, found := k.GetValidatorSigningInfo(ctx, consAddr) 24 if !found { 25 panic(fmt.Sprintf("Expected signing info for validator %s but not found", consAddr)) 26 } 27 28 // this is a relative index, so it counts blocks the validator *should* have signed 29 // will use the 0-value default signing info if not present, except for the beginning 30 index := signInfo.IndexOffset % k.SignedBlocksWindow(ctx) 31 signInfo.IndexOffset++ 32 33 // Update signed block bit array & counter 34 // This counter just tracks the sum of the bit array 35 // That way we avoid needing to read/write the whole array each time 36 previous := k.GetValidatorMissedBlockBitArray(ctx, consAddr, index) 37 missed := !signed 38 switch { 39 case !previous && missed: 40 // Array value has changed from not missed to missed, increment counter 41 k.SetValidatorMissedBlockBitArray(ctx, consAddr, index, true) 42 signInfo.MissedBlocksCounter++ 43 case previous && !missed: 44 // Array value has changed from missed to not missed, decrement counter 45 k.SetValidatorMissedBlockBitArray(ctx, consAddr, index, false) 46 signInfo.MissedBlocksCounter-- 47 default: 48 // Array value at this index has not changed, no need to update counter 49 } 50 51 minSignedPerWindow := k.MinSignedPerWindow(ctx) 52 53 if missed { 54 ctx.EventManager().EmitEvent( 55 sdk.NewEvent( 56 types.EventTypeLiveness, 57 sdk.NewAttribute(types.AttributeKeyAddress, consAddr.String()), 58 sdk.NewAttribute(types.AttributeKeyMissedBlocks, fmt.Sprintf("%d", signInfo.MissedBlocksCounter)), 59 sdk.NewAttribute(types.AttributeKeyHeight, fmt.Sprintf("%d", height)), 60 ), 61 ) 62 63 logger.Debug( 64 "absent validator", 65 "height", height, 66 "validator", consAddr.String(), 67 "missed", signInfo.MissedBlocksCounter, 68 "threshold", minSignedPerWindow, 69 ) 70 } 71 72 numVotes := signInfo.IndexOffset 73 minVotes := k.SignedBlocksWindow(ctx) 74 maxMissed := k.SignedBlocksWindow(ctx) - minSignedPerWindow 75 76 // if we have joined enough times to voter set and the validator has missed too many blocks, punish them 77 if numVotes >= minVotes && signInfo.MissedBlocksCounter > maxMissed { 78 validator := k.sk.ValidatorByConsAddr(ctx, consAddr) 79 if validator != nil && !validator.IsJailed() { 80 // Downtime confirmed: slash and jail the validator 81 // We need to retrieve the stake distribution which signed the block, so we subtract ValidatorUpdateDelay from the evidence height, 82 // and subtract an additional 1 since this is the LastCommit. 83 // Note that this *can* result in a negative "distributionHeight" up to -ValidatorUpdateDelay-1, 84 // i.e. at the end of the pre-genesis block (none) = at the beginning of the genesis block. 85 // That's fine since this is just used to filter unbonding delegations & redelegations. 86 distributionHeight := height - sdk.ValidatorUpdateDelay - 1 87 88 ctx.EventManager().EmitEvent( 89 sdk.NewEvent( 90 types.EventTypeSlash, 91 sdk.NewAttribute(types.AttributeKeyAddress, consAddr.String()), 92 sdk.NewAttribute(types.AttributeKeyPower, fmt.Sprintf("%d", power)), 93 sdk.NewAttribute(types.AttributeKeyReason, types.AttributeValueMissingSignature), 94 sdk.NewAttribute(types.AttributeKeyJailed, consAddr.String()), 95 ), 96 ) 97 k.sk.Slash(ctx, consAddr, distributionHeight, power, k.SlashFractionDowntime(ctx)) 98 k.sk.Jail(ctx, consAddr) 99 100 signInfo.JailedUntil = ctx.BlockHeader().Time.Add(k.DowntimeJailDuration(ctx)) 101 102 // We need to reset the counter & array so that the validator won't be immediately slashed for downtime upon rebonding. 103 signInfo.MissedBlocksCounter = 0 104 signInfo.IndexOffset = 0 105 k.clearValidatorMissedBlockBitArray(ctx, consAddr) 106 107 logger.Info( 108 "slashing and jailing validator due to liveness fault", 109 "height", height, 110 "validator", consAddr.String(), 111 "min_votes", minVotes, 112 "threshold", minSignedPerWindow, 113 "slashed", k.SlashFractionDowntime(ctx).String(), 114 "jailed_until", signInfo.JailedUntil, 115 ) 116 } else { 117 // validator was (a) not found or (b) already jailed so we do not slash 118 logger.Info( 119 "validator would have been slashed for downtime, but was either not found in store or already jailed", 120 "validator", consAddr.String(), 121 ) 122 } 123 } 124 125 // Set the updated signing info 126 k.SetValidatorSigningInfo(ctx, consAddr, signInfo) 127 }