From 26bea83694a3789c2f59b23cb0482bd30798b26c Mon Sep 17 00:00:00 2001 From: Callum Waters Date: Wed, 3 Jun 2020 06:44:06 +0200 Subject: [PATCH] evidence: retrieve header at height of evidence for validation (#4870) validation of lunatic evidence requires that the node retrieve the header at the height of the infringement from the block store for comparison --- consensus/reactor_test.go | 1 + consensus/replay_stubs.go | 1 + evidence/pool.go | 17 +++++++++++++---- state/mocks/evidence_pool.go | 16 ++++++++++++++++ state/services.go | 2 ++ state/validation.go | 11 ++++++++++- 6 files changed, 43 insertions(+), 5 deletions(-) diff --git a/consensus/reactor_test.go b/consensus/reactor_test.go index 44942a2af..55199a37d 100644 --- a/consensus/reactor_test.go +++ b/consensus/reactor_test.go @@ -236,6 +236,7 @@ func (m *mockEvidencePool) IsPending(evidence types.Evidence) bool { return false } func (m *mockEvidencePool) AddPOLC(types.ProofOfLockChange) error { return nil } +func (m *mockEvidencePool) Header(int64) *types.Header { return nil } //------------------------------------ diff --git a/consensus/replay_stubs.go b/consensus/replay_stubs.go index 180d556ca..36f3b03fe 100644 --- a/consensus/replay_stubs.go +++ b/consensus/replay_stubs.go @@ -56,6 +56,7 @@ func (emptyEvidencePool) Update(*types.Block, sm.State) {} func (emptyEvidencePool) IsCommitted(types.Evidence) bool { return false } func (emptyEvidencePool) IsPending(types.Evidence) bool { return false } func (emptyEvidencePool) AddPOLC(types.ProofOfLockChange) error { return nil } +func (emptyEvidencePool) Header(int64) *types.Header { return nil } //----------------------------------------------------------------------------- // mockProxyApp uses ABCIResponses to give the right results. diff --git a/evidence/pool.go b/evidence/pool.go index 633a0100b..a49de76cc 100644 --- a/evidence/pool.go +++ b/evidence/pool.go @@ -181,11 +181,10 @@ func (evpool *Pool) AddEvidence(evidence types.Evidence) error { // For lunatic validator evidence, a header needs to be fetched. var header *types.Header if _, ok := ev.(*types.LunaticValidatorEvidence); ok { - blockMeta := evpool.blockStore.LoadBlockMeta(ev.Height()) - if blockMeta == nil { + header = evpool.Header(ev.Height()) + if header == nil { return fmt.Errorf("don't have block meta at height #%d", ev.Height()) } - header = &blockMeta.Header } // 1) Verify against state. @@ -266,7 +265,7 @@ func (evpool *Pool) IsCommitted(evidence types.Evidence) bool { return ok } -// Checks whether the evidence is already pending. DB errors are passed to the logger. +// IsPending checks whether the evidence is already pending. DB errors are passed to the logger. func (evpool *Pool) IsPending(evidence types.Evidence) bool { key := keyPending(evidence) ok, err := evpool.evidenceStore.Has(key) @@ -306,6 +305,16 @@ func (evpool *Pool) SetLogger(l log.Logger) { evpool.logger = l } +// Header gets the header from the block store at a specified height. +// Is used for validation of LunaticValidatorEvidence +func (evpool *Pool) Header(height int64) *types.Header { + blockMeta := evpool.blockStore.LoadBlockMeta(height) + if blockMeta == nil { + return nil + } + return &blockMeta.Header +} + // ValidatorLastHeight returns the last height of the validator w/ the // given address. 0 - if address never was a validator or was such a // long time ago (> ConsensusParams.Evidence.MaxAgeDuration && > diff --git a/state/mocks/evidence_pool.go b/state/mocks/evidence_pool.go index e3fe4a032..afeb75238 100644 --- a/state/mocks/evidence_pool.go +++ b/state/mocks/evidence_pool.go @@ -28,6 +28,22 @@ func (_m *EvidencePool) AddEvidence(_a0 types.Evidence) error { return r0 } +// Header provides a mock function with given fields: _a0 +func (_m *EvidencePool) Header(_a0 int64) *types.Header { + ret := _m.Called(_a0) + + var r0 *types.Header + if rf, ok := ret.Get(0).(func(int64) *types.Header); ok { + r0 = rf(_a0) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.Header) + } + } + + return r0 +} + // IsCommitted provides a mock function with given fields: _a0 func (_m *EvidencePool) IsCommitted(_a0 types.Evidence) bool { ret := _m.Called(_a0) diff --git a/state/services.go b/state/services.go index 6bd06de01..3179db9f3 100644 --- a/state/services.go +++ b/state/services.go @@ -45,6 +45,7 @@ type EvidencePool interface { Update(*types.Block, State) IsCommitted(types.Evidence) bool IsPending(types.Evidence) bool + Header(int64) *types.Header } // MockEvidencePool is an empty implementation of EvidencePool, useful for testing. @@ -55,3 +56,4 @@ func (me MockEvidencePool) AddEvidence(types.Evidence) error { return nil func (me MockEvidencePool) Update(*types.Block, State) {} func (me MockEvidencePool) IsCommitted(types.Evidence) bool { return false } func (me MockEvidencePool) IsPending(types.Evidence) bool { return false } +func (me MockEvidencePool) Header(int64) *types.Header { return nil } diff --git a/state/validation.go b/state/validation.go index 44a8edd71..b00b0440e 100644 --- a/state/validation.go +++ b/state/validation.go @@ -142,7 +142,16 @@ func validateBlock(evidencePool EvidencePool, stateDB dbm.DB, state State, block continue } } - if err := VerifyEvidence(stateDB, state, ev, &block.Header); err != nil { + + var header *types.Header + if _, ok := ev.(*types.LunaticValidatorEvidence); ok { + header = evidencePool.Header(ev.Height()) + if header == nil { + return fmt.Errorf("don't have block meta at height #%d", ev.Height()) + } + } + + if err := VerifyEvidence(stateDB, state, ev, header); err != nil { return types.NewErrEvidenceInvalid(ev, err) } }