Skip to content

Commit

Permalink
evidence: modularise evidence by moving verification function into ev…
Browse files Browse the repository at this point in the history
…idence package (#5234)
  • Loading branch information
cmwaters authored Aug 20, 2020
1 parent 8ca24e2 commit b7f6e47
Show file tree
Hide file tree
Showing 19 changed files with 546 additions and 717 deletions.
2 changes: 1 addition & 1 deletion consensus/byzantine_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ func TestByzantinePrevoteEquivocation(t *testing.T) {

// Make a full instance of the evidence pool
evidenceDB := dbm.NewMemDB()
evpool, err := evidence.NewPool(stateDB, evidenceDB, blockStore)
evpool, err := evidence.NewPool(evidenceDB, evidence.NewEvidenceStateStore(stateDB), blockStore)
require.NoError(t, err)
evpool.SetLogger(logger.With("module", "evidence"))

Expand Down
2 changes: 1 addition & 1 deletion consensus/common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -452,7 +452,7 @@ func randStateWithEvpool(t *testing.T, nValidators int) (*State, []*validatorStu
}
stateDB := dbm.NewMemDB()
sm.SaveState(stateDB, state)
evpool, err := evidence.NewPool(stateDB, evidenceDB, blockStore)
evpool, err := evidence.NewPool(evidenceDB, evidence.NewEvidenceStateStore(stateDB), blockStore)
require.NoError(t, err)
blockExec := sm.NewBlockExecutor(stateDB, log.TestingLogger(), proxyAppConnCon, mempool, evpool)
cs := NewState(config.Consensus, state, blockExec, blockStore, mempool, evpool)
Expand Down
13 changes: 1 addition & 12 deletions consensus/reactor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -224,19 +224,8 @@ func (m *mockEvidencePool) Update(block *types.Block, state sm.State) {
}
m.height++
}
func (m *mockEvidencePool) IsCommitted(types.Evidence) bool { return false }
func (m *mockEvidencePool) IsPending(evidence types.Evidence) bool {
if m.height > 0 {
for _, e := range m.ev {
if e.Equal(evidence) {
return true
}
}
}
return false
}
func (m *mockEvidencePool) Verify(types.Evidence) error { return nil }
func (m *mockEvidencePool) AddPOLC(*types.ProofOfLockChange) error { return nil }
func (m *mockEvidencePool) Header(int64) *types.Header { return &types.Header{Time: defaultTestTime} }

//------------------------------------

Expand Down
1 change: 1 addition & 0 deletions consensus/replay_stubs.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ var _ sm.EvidencePool = emptyEvidencePool{}
func (emptyEvidencePool) PendingEvidence(uint32) []types.Evidence { return nil }
func (emptyEvidencePool) AddEvidence(types.Evidence) error { return nil }
func (emptyEvidencePool) Update(*types.Block, sm.State) {}
func (emptyEvidencePool) Verify(types.Evidence) error { return nil }
func (emptyEvidencePool) IsCommitted(types.Evidence) bool { return false }
func (emptyEvidencePool) IsPending(types.Evidence) bool { return false }
func (emptyEvidencePool) AddPOLC(*types.ProofOfLockChange) error { return nil }
Expand Down
1 change: 0 additions & 1 deletion evidence/mocks/block_store.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

77 changes: 42 additions & 35 deletions evidence/pool.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package evidence

import (
"errors"
"fmt"
"sync"
"time"
Expand Down Expand Up @@ -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

Expand All @@ -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{
Expand Down Expand Up @@ -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)
}
Expand All @@ -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)
}

Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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())
Expand Down
Loading

0 comments on commit b7f6e47

Please sign in to comment.