func newLightClientAttackEvidence(conflicted, trusted, common *types.LightBlock) *types.LightClientAttackEvidence {
	ev := &types.LightClientAttackEvidence{ConflictingBlock: conflicted}
	if ev.ConflictingHeaderIsInvalid(trusted.Header) {
		ev.CommonHeight = common.Height
		ev.Timestamp = common.Time
		ev.TotalVotingPower = common.ValidatorSet.TotalVotingPower()
	} else {
		ev.CommonHeight = trusted.Height
		ev.Timestamp = trusted.Time
		ev.TotalVotingPower = trusted.ValidatorSet.TotalVotingPower()
	}
	ev.ByzantineValidators = ev.GetByzantineValidators(common.ValidatorSet, trusted.SignedHeader)
	return ev
}
newLightClientAttackEvidence determines the type of attack and then forms the evidence filling out
all the fields such that it is ready to be sent to a full node.
package light
import (
	"bytes"
	"context"
	"errors"
	"fmt"
	"time"
	"github.com/tendermint/tendermint/light/provider"
	"github.com/tendermint/tendermint/types"
)
func (c *Client) detectDivergence(ctx context.Context, primaryTrace []*types.LightBlock, now time.Time) error {
	if primaryTrace == nil || len(primaryTrace) < 2 {
		return errors.New("nil or single block primary trace")
	}
	var (
		headerMatched      bool
		lastVerifiedHeader = primaryTrace[len(primaryTrace)-1].SignedHeader
		witnessesToRemove  = make([]int, 0)
	)
	c.logger.Debug("running detector against trace", "endBlockHeight", lastVerifiedHeader.Height,
		"endBlockHash", lastVerifiedHeader.Hash, "length", len(primaryTrace))
	c.providerMutex.Lock()
	defer c.providerMutex.Unlock()
	if len(c.witnesses) == 0 {
		return ErrNoWitnesses
	}
	errc := make(chan error, len(c.witnesses))
	for i, witness := range c.witnesses {
		go c.compareNewHeaderWithWitness(ctx, errc, lastVerifiedHeader, witness, i)
	}
	for i := 0; i < cap(errc); i++ {
		err := <-errc
		switch e := err.(type) {
		case nil:  
			headerMatched = true
		case errConflictingHeaders:
			err := c.handleConflictingHeaders(ctx, primaryTrace, e.Block, e.WitnessIndex, now)
			if err != nil {
				return err
			}
			witnessesToRemove = append(witnessesToRemove, e.WitnessIndex)
		case errBadWitness:
			c.logger.Info("witness returned an error during header comparison, removing...",
				"witness", c.witnesses[e.WitnessIndex], "err", err)
			witnessesToRemove = append(witnessesToRemove, e.WitnessIndex)
		default:
			if errors.Is(e, context.Canceled) || errors.Is(e, context.DeadlineExceeded) {
				return e
			}
			c.logger.Info("error in light block request to witness", "err", err)
		}
	}
	if err := c.removeWitnesses(witnessesToRemove); err != nil {
		return err
	}
	if headerMatched {
		return nil
	}
	return ErrFailedHeaderCrossReferencing
}
func (c *Client) compareNewHeaderWithWitness(ctx context.Context, errc chan error, h *types.SignedHeader,
	witness provider.Provider, witnessIndex int) {
	lightBlock, err := c.getLightBlock(ctx, witness, h.Height)
	switch err {
	case nil:
		break
	case provider.ErrNoResponse, provider.ErrLightBlockNotFound, context.DeadlineExceeded, context.Canceled:
		errc <- err
		return
	case provider.ErrHeightTooHigh:
		var isTargetHeight bool
		isTargetHeight, lightBlock, err = c.getTargetBlockOrLatest(ctx, h.Height, witness)
		if err != nil {
			if c.providerShouldBeRemoved(err) {
				errc <- errBadWitness{Reason: err, WitnessIndex: witnessIndex}
			} else {
				errc <- err
			}
			return
		}
		if isTargetHeight {
			break
		}
		if !lightBlock.Time.Before(h.Time) {
			errc <- errConflictingHeaders{Block: lightBlock, WitnessIndex: witnessIndex}
			return
		}
		time.Sleep(2*c.maxClockDrift + c.maxBlockLag)
		isTargetHeight, lightBlock, err = c.getTargetBlockOrLatest(ctx, h.Height, witness)
		if err != nil {
			if c.providerShouldBeRemoved(err) {
				errc <- errBadWitness{Reason: err, WitnessIndex: witnessIndex}
			} else {
				errc <- err
			}
			return
		}
		if isTargetHeight {
			break
		}
		if !lightBlock.Time.Before(h.Time) {
			errc <- errConflictingHeaders{Block: lightBlock, WitnessIndex: witnessIndex}
			return
		}
		errc <- provider.ErrNoResponse
		return
	default:
		errc <- errBadWitness{Reason: err, WitnessIndex: witnessIndex}
		return
	}
	if !bytes.Equal(h.Header.Hash(), lightBlock.Header.Hash()) {
		errc <- errConflictingHeaders{Block: lightBlock, WitnessIndex: witnessIndex}
	}
	c.logger.Debug("matching header received by witness", "height", h.Height, "witness", witnessIndex)
	errc <- nil
}
func (c *Client) sendEvidence(ctx context.Context, ev *types.LightClientAttackEvidence, receiver provider.Provider) {
	err := receiver.ReportEvidence(ctx, ev)
	if err != nil {
		c.logger.Error("failed to report evidence to provider", "ev", ev, "provider", receiver)
	}
}
func (c *Client) handleConflictingHeaders(
	ctx context.Context,
	primaryTrace []*types.LightBlock,
	challendingBlock *types.LightBlock,
	witnessIndex int,
	now time.Time,
) error {
	supportingWitness := c.witnesses[witnessIndex]
	witnessTrace, primaryBlock, err := c.examineConflictingHeaderAgainstTrace(
		ctx,
		primaryTrace,
		challendingBlock,
		supportingWitness,
		now,
	)
	if err != nil {
		c.logger.Info("error validating witness's divergent header", "witness", supportingWitness, "err", err)
		return nil
	}
	commonBlock, trustedBlock := witnessTrace[0], witnessTrace[len(witnessTrace)-1]
	evidenceAgainstPrimary := newLightClientAttackEvidence(primaryBlock, trustedBlock, commonBlock)
	c.logger.Error("ATTEMPTED ATTACK DETECTED. Sending evidence againt primary by witness", "ev", evidenceAgainstPrimary,
		"primary", c.primary, "witness", supportingWitness)
	c.sendEvidence(ctx, evidenceAgainstPrimary, supportingWitness)
	if primaryBlock.Commit.Round != witnessTrace[len(witnessTrace)-1].Commit.Round {
		c.logger.Info("The light client has detected, and prevented, an attempted amnesia attack." +
			" We think this attack is pretty unlikely, so if you see it, that's interesting to us." +
			" Can you let us know by opening an issue through https://github.com/tendermint/tendermint/issues/new?")
	}
	primaryTrace, witnessBlock, err := c.examineConflictingHeaderAgainstTrace(
		ctx,
		witnessTrace,
		primaryBlock,
		c.primary,
		now,
	)
	if err != nil {
		c.logger.Info("Error validating primary's divergent header", "primary", c.primary, "err", err)
		return ErrLightClientAttack
	}
	commonBlock, trustedBlock = primaryTrace[0], primaryTrace[len(primaryTrace)-1]
	evidenceAgainstWitness := newLightClientAttackEvidence(witnessBlock, trustedBlock, commonBlock)
	c.logger.Error("Sending evidence against witness by primary", "ev", evidenceAgainstWitness,
		"primary", c.primary, "witness", supportingWitness)
	c.sendEvidence(ctx, evidenceAgainstWitness, c.primary)
	return ErrLightClientAttack
}
func (c *Client) examineConflictingHeaderAgainstTrace(
	ctx context.Context,
	trace []*types.LightBlock,
	targetBlock *types.LightBlock,
	source provider.Provider, now time.Time,
) ([]*types.LightBlock, *types.LightBlock, error) {
	var (
		previouslyVerifiedBlock, sourceBlock *types.LightBlock
		sourceTrace                          []*types.LightBlock
		err                                  error
	)
	if targetBlock.Height < trace[0].Height {
		return nil, nil, fmt.Errorf("target block has a height lower than the trusted height (%d < %d)",
			targetBlock.Height, trace[0].Height)
	}
	for idx, traceBlock := range trace {
		if traceBlock.Height > targetBlock.Height {
			if traceBlock.Time.After(targetBlock.Time) {
				return nil, nil,
					errors.New("sanity check failed: expected traceblock to have a lesser time than the target block")
			}
			if previouslyVerifiedBlock.Height != targetBlock.Height {
				sourceTrace, err = c.verifySkipping(ctx, source, previouslyVerifiedBlock, targetBlock, now)
				if err != nil {
					return nil, nil, fmt.Errorf("verifySkipping of conflicting header failed: %w", err)
				}
			}
			return sourceTrace, traceBlock, nil
		}
		if traceBlock.Height == targetBlock.Height {
			sourceBlock = targetBlock
		} else {
			sourceBlock, err = c.getLightBlock(ctx, source, traceBlock.Height)
			if err != nil {
				return nil, nil, fmt.Errorf("failed to examine trace: %w", err)
			}
		}
		if idx == 0 {
			if shash, thash := sourceBlock.Hash(), traceBlock.Hash(); !bytes.Equal(shash, thash) {
				return nil, nil, fmt.Errorf("trusted block is different to the source's first block (%X = %X)",
					thash, shash)
			}
			previouslyVerifiedBlock = sourceBlock
			continue
		}
		sourceTrace, err = c.verifySkipping(ctx, source, previouslyVerifiedBlock, sourceBlock, now)
		if err != nil {
			return nil, nil, fmt.Errorf("verifySkipping of conflicting header failed: %w", err)
		}
		if shash, thash := sourceBlock.Hash(), traceBlock.Hash(); !bytes.Equal(shash, thash) {
			return sourceTrace, traceBlock, nil
		}
		previouslyVerifiedBlock = sourceBlock
	}
	return nil, nil, errNoDivergence
}
func (c *Client) getTargetBlockOrLatest(
	ctx context.Context,
	height int64,
	witness provider.Provider,
) (bool, *types.LightBlock, error) {
	lightBlock, err := c.getLightBlock(ctx, witness, 0)
	if err != nil {
		return false, nil, err
	}
	if lightBlock.Height == height {
		return true, lightBlock, nil
	}
	if lightBlock.Height > height {
		lightBlock, err := c.getLightBlock(ctx, witness, height)
		return true, lightBlock, err
	}
	return false, lightBlock, nil
}