package keeper
import (
    
	"fmt"
	sdk "github.com/cosmos/cosmos-sdk/types"
    "github.com/cosmos/cosmos-sdk/x/evidence/types"
	stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
)
// HandleEquivocationEvidence implements an equivocation evidence handler. Assuming the
// evidence is valid, the validator committing the misbehavior will be slashed,
// jailed and tombstoned. Once tombstoned, the validator will not be able to
// recover. Note, the evidence contains the block time and height at the time of
// the equivocation.
//
// The evidence is considered invalid if:
// - the evidence is too old
// - the validator is unbonded or does not exist
// - the signing info does not exist (will panic)
// - is already tombstoned
//
// TODO: Some of the invalid constraints listed above may need to be reconsidered
// in the case of a lunatic attack.
func (k Keeper)
HandleEquivocationEvidence(ctx sdk.Context, evidence *types.Equivocation) {
    logger := k.Logger(ctx)
    consAddr := evidence.GetConsensusAddress()
    if _, err := k.slashingKeeper.GetPubkey(ctx, consAddr.Bytes()); err != nil {
		// Ignore evidence that cannot be handled.
		//
		// NOTE: We used to panic with:
		// `panic(fmt.Sprintf("Validator consensus-address %v not found", consAddr))`,
		// but this couples the expectations of the app to both Tendermint and
		// the simulator.  Both are expected to provide the full range of
		// allowable but none of the disallowed evidence types.  Instead of
		// getting this coordination right, it is easier to relax the
		// constraints and ignore evidence that cannot be handled.
		return
}
	// calculate the age of the evidence
    infractionHeight := evidence.GetHeight()
    infractionTime := evidence.GetTime()
    ageDuration := ctx.BlockHeader().Time.Sub(infractionTime)
    ageBlocks := ctx.BlockHeader().Height - infractionHeight
	// Reject evidence if the double-sign is too old. Evidence is considered stale
	// if the difference in time and number of blocks is greater than the allowed
	// parameters defined.
    cp := ctx.ConsensusParams()
    if cp != nil && cp.Evidence != nil {
    if ageDuration > cp.Evidence.MaxAgeDuration && ageBlocks > cp.Evidence.MaxAgeNumBlocks {
    logger.Info(
				"ignored equivocation; evidence too old",
				"validator", consAddr,
				"infraction_height", infractionHeight,
				"max_age_num_blocks", cp.Evidence.MaxAgeNumBlocks,
				"infraction_time", infractionTime,
				"max_age_duration", cp.Evidence.MaxAgeDuration,
			)
return
}
	
}
    validator := k.stakingKeeper.ValidatorByConsAddr(ctx, consAddr)
    if validator == nil || validator.IsUnbonded() {
		// Defensive: Simulation doesn't take unbonding periods into account, and
		// Tendermint might break this assumption at some point.
		return
}
    if !validator.GetOperator().Empty() {
    if _, err := k.slashingKeeper.GetPubkey(ctx, consAddr.Bytes()); err != nil {
			// Ignore evidence that cannot be handled.
			//
			// NOTE: We used to panic with:
			// `panic(fmt.Sprintf("Validator consensus-address %v not found", consAddr))`,
			// but this couples the expectations of the app to both Tendermint and
			// the simulator.  Both are expected to provide the full range of
			// allowable but none of the disallowed evidence types.  Instead of
			// getting this coordination right, it is easier to relax the
			// constraints and ignore evidence that cannot be handled.
			return
}
	
}
    if ok := k.slashingKeeper.HasValidatorSigningInfo(ctx, consAddr); !ok {
    panic(fmt.Sprintf("expected signing info for validator %s but not found", consAddr))
}
	// ignore if the validator is already tombstoned
    if k.slashingKeeper.IsTombstoned(ctx, consAddr) {
    logger.Info(
			"ignored equivocation; validator already tombstoned",
			"validator", consAddr,
			"infraction_height", infractionHeight,
			"infraction_time", infractionTime,
		)
return
}
logger.Info(
		"confirmed equivocation",
		"validator", consAddr,
		"infraction_height", infractionHeight,
		"infraction_time", infractionTime,
	)
	// We need to retrieve the stake distribution which signed the block, so we
	// subtract ValidatorUpdateDelay from the evidence height.
	// Note, that this *can* result in a negative "distributionHeight", up to
	// -ValidatorUpdateDelay, i.e. at the end of the
	// pre-genesis block (none) = at the beginning of the genesis block.
	// That's fine since this is just used to filter unbonding delegations & redelegations.
    distributionHeight := infractionHeight - sdk.ValidatorUpdateDelay
	// Slash validator. The `power` is the int64 power of the validator as provided
	// to/by Tendermint. This value is validator.Tokens as sent to Tendermint via
	// ABCI, and now received as evidence. The fraction is passed in to separately
	// to slash unbonding and rebonding delegations.
	k.slashingKeeper.SlashWithInfractionReason(
		ctx,
		consAddr,
		k.slashingKeeper.SlashFractionDoubleSign(ctx),
		evidence.GetValidatorPower(), distributionHeight,
		stakingtypes.Infraction_INFRACTION_DOUBLE_SIGN,
	)
	// Jail the validator if not already jailed. This will begin unbonding the
	// validator if not already unbonding (tombstoned).
    if !validator.IsJailed() {
    k.slashingKeeper.Jail(ctx, consAddr)
}
k.slashingKeeper.JailUntil(ctx, consAddr, types.DoubleSignJailEndTime)
k.slashingKeeper.Tombstone(ctx, consAddr)
k.SetEvidence(ctx, evidence)
}