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  }