evidence: modularise evidence by moving verification function into evidence package (#5234)

This commit is contained in:
Callum Waters
2020-08-20 18:11:21 +02:00
committed by GitHub
parent 8ca24e2f5a
commit b7f6e47a42
19 changed files with 547 additions and 718 deletions

View File

@@ -1,6 +1,7 @@
package evidence
import (
"errors"
"fmt"
"sync"
"time"
@@ -31,7 +32,7 @@ type Pool struct {
evidenceList *clist.CList // concurrent linked-list of evidence
// needed to load validators to verify evidence
stateDB dbm.DB
stateDB StateStore
// needed to load headers to verify evidence
blockStore BlockStore
@@ -45,11 +46,11 @@ type Pool struct {
nextEvidenceTrialEndedHeight int64
}
// Creates a new pool. If using an existing evidence store, it will add all pending evidence
// to the concurrent list.
func NewPool(stateDB, evidenceDB dbm.DB, blockStore BlockStore) (*Pool, error) {
// NewPool creates an evidence pool. If using an existing evidence store,
// it will add all pending evidence to the concurrent list.
func NewPool(evidenceDB dbm.DB, stateDB StateStore, blockStore BlockStore) (*Pool, error) {
var (
state = sm.LoadState(stateDB)
state = stateDB.LoadState()
)
pool := &Pool{
@@ -145,14 +146,11 @@ func (evpool *Pool) AddPOLC(polc *types.ProofOfLockChange) error {
// evidence is composite (ConflictingHeadersEvidence), it will be broken up
// into smaller pieces.
func (evpool *Pool) AddEvidence(evidence types.Evidence) error {
var (
state = evpool.State()
evList = []types.Evidence{evidence}
)
var evList = []types.Evidence{evidence}
evpool.logger.Debug("Attempting to add evidence", "ev", evidence)
valSet, err := sm.LoadValidators(evpool.stateDB, evidence.Height())
valSet, err := evpool.stateDB.LoadValidators(evidence.Height())
if err != nil {
return fmt.Errorf("can't load validators at height #%d: %w", evidence.Height(), err)
}
@@ -177,36 +175,14 @@ func (evpool *Pool) AddEvidence(evidence types.Evidence) error {
if evpool.Has(ev) {
// if it is an amnesia evidence we have but POLC is not absent then
// we should still process it
// we should still process it else we loop to the next piece of evidence
if ae, ok := ev.(*types.AmnesiaEvidence); !ok || ae.Polc.IsAbsent() {
continue
}
}
// A header needs to be fetched. For lunatic evidence this is so we can verify
// that some of the fields are different to the ones we have. For all evidence it
// it so we can verify that the time of the evidence is correct
var 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 ev.Height() == evpool.State().LastBlockHeight+1 {
if ev.Time().Before(evpool.State().LastBlockTime) {
return fmt.Errorf("evidence is from an earlier time than the previous block: %v < %v",
ev.Time(),
evpool.State().LastBlockTime)
}
header = &types.Header{Time: ev.Time()}
} else { // if the evidence is from a prior height
header = evpool.Header(ev.Height())
if header == nil {
return fmt.Errorf("don't have header at height #%d", ev.Height())
}
}
// 1) Verify against state.
if err := sm.VerifyEvidence(evpool.stateDB, state, ev, header); err != nil {
evpool.logger.Debug("Inbound evidence is invalid", "evidence", ev, "err", err)
if err := evpool.verify(ev); err != nil {
return types.NewErrEvidenceInvalid(ev, err)
}
@@ -256,6 +232,37 @@ func (evpool *Pool) AddEvidence(evidence types.Evidence) error {
return nil
}
// Verify verifies the evidence against the node's (or evidence pool's) state. More specifically, to validate
// evidence against state is to validate it against the nodes own header and validator set for that height. This ensures
// as well as meeting the evidence's own validation rules, that the evidence hasn't expired, that the validator is still
// bonded and that the evidence can be committed to the chain.
func (evpool *Pool) Verify(evidence types.Evidence) error {
if evpool.IsCommitted(evidence) {
return errors.New("evidence was already committed")
}
// We have already verified this piece of evidence - no need to do it again
if evpool.IsPending(evidence) {
return nil
}
// if we don't already have amnesia evidence we need to add it to start our own trial period unless
// a) a valid polc has already been attached
// b) the accused node voted back on an earlier round
if ae, ok := evidence.(*types.AmnesiaEvidence); ok && ae.Polc.IsAbsent() && ae.PotentialAmnesiaEvidence.VoteA.Round <
ae.PotentialAmnesiaEvidence.VoteB.Round {
if err := evpool.AddEvidence(ae.PotentialAmnesiaEvidence); err != nil {
return fmt.Errorf("unknown amnesia evidence, trying to add to evidence pool, err: %w", err)
}
return errors.New("amnesia evidence is new and hasn't undergone trial period yet")
}
return evpool.verify(evidence)
}
func (evpool *Pool) verify(evidence types.Evidence) error {
return VerifyEvidence(evidence, evpool.State(), evpool.stateDB, evpool.blockStore)
}
// MarkEvidenceAsCommitted marks all the evidence as committed and removes it
// from the queue.
func (evpool *Pool) MarkEvidenceAsCommitted(height int64, evidence []types.Evidence) {
@@ -543,7 +550,7 @@ func (evpool *Pool) pruneExpiredPOLC() {
evpool.logger.Error("Unable to transition POLC from protobuf", "err", err)
continue
}
if !evpool.IsExpired(proof.Height()-1, proof.Time()) {
if !evpool.IsExpired(proof.Height(), proof.Time()) {
return
}
err = evpool.evidenceStore.Delete(iter.Key())