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