package evidence import ( "fmt" sm "github.com/tendermint/tendermint/state" "github.com/tendermint/tendermint/types" ) // VerifyEvidence verifies the evidence fully by checking: // - it is sufficiently recent (MaxAge) // - it is from a key who was a validator at the given height // - it is internally consistent // - it was properly signed by the alleged equivocator func VerifyEvidence(evidence types.Evidence, state sm.State, stateDB StateStore, blockStore BlockStore) error { var ( height = state.LastBlockHeight evidenceParams = state.ConsensusParams.Evidence ageDuration = state.LastBlockTime.Sub(evidence.Time()) ageNumBlocks = height - evidence.Height() header *types.Header ) // if the evidence is from the current height - this means the evidence is fresh from the consensus // and we won't have it in the block store. We thus check that the time isn't before the previous block if evidence.Height() == height+1 { if evidence.Time().Before(state.LastBlockTime) { return fmt.Errorf("evidence is from an earlier time than the previous block: %v < %v", evidence.Time(), state.LastBlockTime) } } else { // try to retrieve header from blockstore blockMeta := blockStore.LoadBlockMeta(evidence.Height()) header = &blockMeta.Header if header == nil { return fmt.Errorf("don't have header at height #%d", evidence.Height()) } if header.Time != evidence.Time() { return fmt.Errorf("evidence time (%v) is different to the time of the header we have for the same height (%v)", evidence.Time(), header.Time, ) } } if ageDuration > evidenceParams.MaxAgeDuration && ageNumBlocks > evidenceParams.MaxAgeNumBlocks { return fmt.Errorf( "evidence from height %d (created at: %v) is too old; min height is %d and evidence can not be older than %v", evidence.Height(), evidence.Time(), height-evidenceParams.MaxAgeNumBlocks, state.LastBlockTime.Add(evidenceParams.MaxAgeDuration), ) } // If in the case of lunatic validator evidence we need our committed header again to verify the evidence if ev, ok := evidence.(*types.LunaticValidatorEvidence); ok { if err := ev.VerifyHeader(header); err != nil { return err } } valset, err := stateDB.LoadValidators(evidence.Height()) if err != nil { return err } if ae, ok := evidence.(*types.AmnesiaEvidence); ok { // check the validator set against the polc to make sure that a majority of valid votes was reached if !ae.Polc.IsAbsent() { err = ae.Polc.ValidateVotes(valset, state.ChainID) if err != nil { return fmt.Errorf("amnesia evidence contains invalid polc, err: %w", err) } } } addr := evidence.Address() var val *types.Validator // For all other types, expect evidence.Address to be a validator at height // evidence.Height. _, val = valset.GetByAddress(addr) if val == nil { return fmt.Errorf("address %X was not a validator at height %d", addr, evidence.Height()) } if err := evidence.Verify(state.ChainID, val.PubKey); err != nil { return err } return nil }