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 }