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)
	}
}
sendEvidence sends evidence to a provider on a best effort basis.
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
}