github.com/Finschia/finschia-sdk@v0.49.1/x/evidence/keeper/infraction.go (about)

     1  package keeper
     2  
     3  import (
     4  	"fmt"
     5  
     6  	sdk "github.com/Finschia/finschia-sdk/types"
     7  	"github.com/Finschia/finschia-sdk/x/evidence/types"
     8  )
     9  
    10  // HandleEquivocationEvidence implements an equivocation evidence handler. Assuming the
    11  // evidence is valid, the validator committing the misbehavior will be slashed,
    12  // jailed and tombstoned. Once tombstoned, the validator will not be able to
    13  // recover. Note, the evidence contains the block time and height at the time of
    14  // the equivocation.
    15  //
    16  // The evidence is considered invalid if:
    17  // - the evidence is too old
    18  // - the validator is unbonded or does not exist
    19  // - the signing info does not exist (will panic)
    20  // - is already tombstoned
    21  //
    22  // TODO: Some of the invalid constraints listed above may need to be reconsidered
    23  // in the case of a lunatic attack.
    24  func (k Keeper) HandleEquivocationEvidence(ctx sdk.Context, evidence *types.Equivocation) {
    25  	logger := k.Logger(ctx)
    26  	consAddr := evidence.GetConsensusAddress()
    27  
    28  	if _, err := k.slashingKeeper.GetPubkey(ctx, consAddr.Bytes()); err != nil {
    29  		// Ignore evidence that cannot be handled.
    30  		//
    31  		// NOTE: We used to panic with:
    32  		// `panic(fmt.Sprintf("Validator consensus-address %v not found", consAddr))`,
    33  		// but this couples the expectations of the app to both Tendermint and
    34  		// the simulator.  Both are expected to provide the full range of
    35  		// allowable but none of the disallowed evidence types.  Instead of
    36  		// getting this coordination right, it is easier to relax the
    37  		// constraints and ignore evidence that cannot be handled.
    38  		return
    39  	}
    40  
    41  	// calculate the age of the evidence
    42  	infractionHeight := evidence.GetHeight()
    43  	infractionTime := evidence.GetTime()
    44  	ageDuration := ctx.BlockHeader().Time.Sub(infractionTime)
    45  	ageBlocks := ctx.BlockHeader().Height - infractionHeight
    46  
    47  	// Reject evidence if the double-sign is too old. Evidence is considered stale
    48  	// if the difference in time and number of blocks is greater than the allowed
    49  	// parameters defined.
    50  	cp := ctx.ConsensusParams()
    51  	if cp != nil && cp.Evidence != nil {
    52  		if ageDuration > cp.Evidence.MaxAgeDuration && ageBlocks > cp.Evidence.MaxAgeNumBlocks {
    53  			logger.Info(
    54  				"ignored equivocation; evidence too old",
    55  				"validator", consAddr,
    56  				"infraction_height", infractionHeight,
    57  				"max_age_num_blocks", cp.Evidence.MaxAgeNumBlocks,
    58  				"infraction_time", infractionTime,
    59  				"max_age_duration", cp.Evidence.MaxAgeDuration,
    60  			)
    61  			return
    62  		}
    63  	}
    64  
    65  	validator := k.stakingKeeper.ValidatorByConsAddr(ctx, consAddr)
    66  	if validator == nil || validator.IsUnbonded() {
    67  		// Defensive: Simulation doesn't take unbonding periods into account, and
    68  		// Tendermint might break this assumption at some point.
    69  		return
    70  	}
    71  
    72  	if ok := k.slashingKeeper.HasValidatorSigningInfo(ctx, consAddr); !ok {
    73  		panic(fmt.Sprintf("expected signing info for validator %s but not found", consAddr))
    74  	}
    75  
    76  	// ignore if the validator is already tombstoned
    77  	if k.slashingKeeper.IsTombstoned(ctx, consAddr) {
    78  		logger.Info(
    79  			"ignored equivocation; validator already tombstoned",
    80  			"validator", consAddr,
    81  			"infraction_height", infractionHeight,
    82  			"infraction_time", infractionTime,
    83  		)
    84  		return
    85  	}
    86  
    87  	logger.Info(
    88  		"confirmed equivocation",
    89  		"validator", consAddr,
    90  		"infraction_height", infractionHeight,
    91  		"infraction_time", infractionTime,
    92  	)
    93  
    94  	// We need to retrieve the stake distribution which signed the block, so we
    95  	// subtract ValidatorUpdateDelay from the evidence height.
    96  	// Note, that this *can* result in a negative "distributionHeight", up to
    97  	// -ValidatorUpdateDelay, i.e. at the end of the
    98  	// pre-genesis block (none) = at the beginning of the genesis block.
    99  	// That's fine since this is just used to filter unbonding delegations & redelegations.
   100  	distributionHeight := infractionHeight - sdk.ValidatorUpdateDelay
   101  
   102  	// Slash validator. The `power` is the int64 power of the validator as provided
   103  	// to/by Tendermint. This value is validator.Tokens as sent to Tendermint via
   104  	// ABCI, and now received as evidence. The fraction is passed in to separately
   105  	// to slash unbonding and rebonding delegations.
   106  	k.slashingKeeper.Slash(
   107  		ctx,
   108  		consAddr,
   109  		k.slashingKeeper.SlashFractionDoubleSign(ctx),
   110  		evidence.GetValidatorPower(), distributionHeight,
   111  	)
   112  
   113  	// Jail the validator if not already jailed. This will begin unbonding the
   114  	// validator if not already unbonding (tombstoned).
   115  	if !validator.IsJailed() {
   116  		k.slashingKeeper.Jail(ctx, consAddr)
   117  	}
   118  
   119  	k.slashingKeeper.JailUntil(ctx, consAddr, types.DoubleSignJailEndTime)
   120  	k.slashingKeeper.Tombstone(ctx, consAddr)
   121  	k.SetEvidence(ctx, evidence)
   122  }