evidence: introduce time.Duration to evidence params (#4254)

* evidence: introduce time.Duration to evidence params

- add time.duration to evidence
- this pr is taking pr #2606 and updating it to use both time and height

- closes #2565

Signed-off-by: Marko Baricevic <marbar3778@yahoo.com>

* fix testing and genesis cfg in signer harness

* remove debugging fmt

* change maxageheight to maxagenumblocks, rename other things to block instead of height

* further check of duration

* check duration to not send peers outdated evidence

* change some lines, onward and upward

* refactor evidence package

* add a changelog pending entry

* make mockbadevidence have time and use it

* add what could possibly be called a test case

* remove mockbadevidence and mockgoodevidence in favor of mockevidence

* add a comment for err that is returned

* add a changelog for removal of good & bad evidence

* add a test for adding evidence

* fix test

* add ev to types in testcase

* Update evidence/pool_test.go

Co-Authored-By: Anton Kaliaev <anton.kalyaev@gmail.com>

* Update evidence/pool_test.go

Co-Authored-By: Anton Kaliaev <anton.kalyaev@gmail.com>

* fix tests

* fix linting

Co-authored-by: Anton Kaliaev <anton.kalyaev@gmail.com>
This commit is contained in:
Marko
2020-01-08 10:46:37 +01:00
committed by GitHub
co-authored by Anton Kaliaev
parent d7f4ce30ca
commit 6d91c1faf4
26 changed files with 540 additions and 382 deletions
+19 -11
View File
@@ -3,6 +3,7 @@ package evidence
import (
"fmt"
"sync"
"time"
clist "github.com/tendermint/tendermint/libs/clist"
"github.com/tendermint/tendermint/libs/log"
@@ -90,7 +91,7 @@ func (evpool *Pool) Update(block *types.Block, state sm.State) {
evpool.mtx.Unlock()
// remove evidence from pending and mark committed
evpool.MarkEvidenceAsCommitted(block.Height, block.Evidence.Evidence)
evpool.MarkEvidenceAsCommitted(block.Height, block.Time, block.Evidence.Evidence)
}
// AddEvidence checks the evidence is valid and adds it to the pool.
@@ -124,7 +125,7 @@ func (evpool *Pool) AddEvidence(evidence types.Evidence) (err error) {
}
// MarkEvidenceAsCommitted marks all the evidence as committed and removes it from the queue.
func (evpool *Pool) MarkEvidenceAsCommitted(height int64, evidence []types.Evidence) {
func (evpool *Pool) MarkEvidenceAsCommitted(height int64, lastBlockTime time.Time, evidence []types.Evidence) {
// make a map of committed evidence to remove from the clist
blockEvidenceMap := make(map[string]struct{})
for _, ev := range evidence {
@@ -133,9 +134,8 @@ func (evpool *Pool) MarkEvidenceAsCommitted(height int64, evidence []types.Evide
}
// remove committed evidence from the clist
maxAge := evpool.State().ConsensusParams.Evidence.MaxAge
evpool.removeEvidence(height, maxAge, blockEvidenceMap)
evidenceParams := evpool.State().ConsensusParams.Evidence
evpool.removeEvidence(height, lastBlockTime, evidenceParams, blockEvidenceMap)
}
// IsCommitted returns true if we have already seen this exact evidence and it is already marked as committed.
@@ -144,15 +144,23 @@ func (evpool *Pool) IsCommitted(evidence types.Evidence) bool {
return ei.Evidence != nil && ei.Committed
}
func (evpool *Pool) removeEvidence(height, maxAge int64, blockEvidenceMap map[string]struct{}) {
func (evpool *Pool) removeEvidence(
height int64,
lastBlockTime time.Time,
params types.EvidenceParams,
blockEvidenceMap map[string]struct{}) {
for e := evpool.evidenceList.Front(); e != nil; e = e.Next() {
ev := e.Value.(types.Evidence)
var (
ev = e.Value.(types.Evidence)
ageDuration = lastBlockTime.Sub(ev.Time())
ageNumBlocks = height - ev.Height()
)
// Remove the evidence if it's already in a block
// or if it's now too old.
// Remove the evidence if it's already in a block or if it's now too old.
if _, ok := blockEvidenceMap[evMapKey(ev)]; ok ||
ev.Height() < height-maxAge {
ageNumBlocks > params.MaxAgeNumBlocks ||
ageDuration > params.MaxAgeDuration {
// remove from clist
evpool.evidenceList.Remove(e)
e.DetachPrev()
+58 -15
View File
@@ -4,6 +4,7 @@ import (
"os"
"sync"
"testing"
"time"
"github.com/stretchr/testify/assert"
@@ -37,7 +38,8 @@ func initializeValidatorState(valAddr []byte, height int64) dbm.DB {
LastHeightValidatorsChanged: 1,
ConsensusParams: types.ConsensusParams{
Evidence: types.EvidenceParams{
MaxAge: 1000000,
MaxAgeNumBlocks: 10000,
MaxAgeDuration: 48 * time.Hour,
},
},
}
@@ -53,18 +55,22 @@ func initializeValidatorState(valAddr []byte, height int64) dbm.DB {
func TestEvidencePool(t *testing.T) {
valAddr := []byte("val1")
height := int64(5)
stateDB := initializeValidatorState(valAddr, height)
evidenceDB := dbm.NewMemDB()
pool := NewPool(stateDB, evidenceDB)
var (
valAddr = []byte("val1")
height = int64(5)
stateDB = initializeValidatorState(valAddr, height)
evidenceDB = dbm.NewMemDB()
pool = NewPool(stateDB, evidenceDB)
evidenceTime = time.Date(2019, 1, 1, 0, 0, 0, 0, time.UTC)
)
goodEvidence := types.NewMockGoodEvidence(height, 0, valAddr)
badEvidence := types.MockBadEvidence{MockGoodEvidence: goodEvidence}
goodEvidence := types.NewMockEvidence(height, time.Now(), 0, valAddr)
badEvidence := types.NewMockEvidence(height, evidenceTime, 0, valAddr)
// bad evidence
err := pool.AddEvidence(badEvidence)
assert.NotNil(t, err)
// err: evidence created at 2019-01-01 00:00:00 +0000 UTC has expired. Evidence can not be older than: ...
var wg sync.WaitGroup
wg.Add(1)
@@ -87,14 +93,17 @@ func TestEvidencePool(t *testing.T) {
func TestEvidencePoolIsCommitted(t *testing.T) {
// Initialization:
valAddr := []byte("validator_address")
height := int64(42)
stateDB := initializeValidatorState(valAddr, height)
evidenceDB := dbm.NewMemDB()
pool := NewPool(stateDB, evidenceDB)
var (
valAddr = []byte("validator_address")
height = int64(42)
lastBlockTime = time.Now()
stateDB = initializeValidatorState(valAddr, height)
evidenceDB = dbm.NewMemDB()
pool = NewPool(stateDB, evidenceDB)
)
// evidence not seen yet:
evidence := types.NewMockGoodEvidence(height, 0, valAddr)
evidence := types.NewMockEvidence(height, time.Now(), 0, valAddr)
assert.False(t, pool.IsCommitted(evidence))
// evidence seen but not yet committed:
@@ -102,6 +111,40 @@ func TestEvidencePoolIsCommitted(t *testing.T) {
assert.False(t, pool.IsCommitted(evidence))
// evidence seen and committed:
pool.MarkEvidenceAsCommitted(height, []types.Evidence{evidence})
pool.MarkEvidenceAsCommitted(height, lastBlockTime, []types.Evidence{evidence})
assert.True(t, pool.IsCommitted(evidence))
}
func TestAddEvidence(t *testing.T) {
var (
valAddr = []byte("val1")
height = int64(100002)
stateDB = initializeValidatorState(valAddr, height)
evidenceDB = dbm.NewMemDB()
pool = NewPool(stateDB, evidenceDB)
evidenceTime = time.Date(2019, 1, 1, 0, 0, 0, 0, time.UTC)
)
testCases := []struct {
evHeight int64
evTime time.Time
expErr bool
evDescription string
}{
{height, time.Now(), false, "valid evidence"},
{height, evidenceTime, true, "evidence created at 2019-01-01 00:00:00 +0000 UTC has expired"},
{int64(1), time.Now(), true, "evidence from height 1 is too old"},
{int64(1), evidenceTime, true,
"evidence from height 1 is too old & evidence created at 2019-01-01 00:00:00 +0000 UTC has expired"},
}
for _, tc := range testCases {
tc := tc
ev := types.NewMockEvidence(tc.evHeight, tc.evTime, 0, valAddr)
err := pool.AddEvidence(ev)
if tc.expErr {
assert.Error(t, err)
}
}
}
+25 -13
View File
@@ -93,14 +93,14 @@ func (evR *Reactor) Receive(chID byte, src p2p.Peer, msgBytes []byte) {
}
}
// SetEventSwitch implements events.Eventable.
// SetEventBus implements events.Eventable.
func (evR *Reactor) SetEventBus(b *types.EventBus) {
evR.eventBus = b
}
// Modeled after the mempool routine.
// - Evidence accumulates in a clist.
// - Each peer has a routien that iterates through the clist,
// - Each peer has a routine that iterates through the clist,
// sending available evidence to the peer.
// - If we're waiting for new evidence and the list is not empty,
// start iterating from the beginning again.
@@ -158,6 +158,7 @@ func (evR Reactor) checkSendEvidenceMessage(
peer p2p.Peer,
ev types.Evidence,
) (msg Message, retry bool) {
// make sure the peer is up to date
evHeight := ev.Height()
peerState, ok := peer.Get(types.PeerStateKey).(PeerState)
@@ -172,20 +173,31 @@ func (evR Reactor) checkSendEvidenceMessage(
// NOTE: We only send evidence to peers where
// peerHeight - maxAge < evidenceHeight < peerHeight
maxAge := evR.evpool.State().ConsensusParams.Evidence.MaxAge
peerHeight := peerState.GetHeight()
if peerHeight < evHeight {
// peer is behind. sleep while he catches up
// and
// lastBlockTime - maxDuration < evidenceTime
var (
peerHeight = peerState.GetHeight()
params = evR.evpool.State().ConsensusParams.Evidence
ageDuration = evR.evpool.State().LastBlockTime.Sub(ev.Time())
ageNumBlocks = peerHeight - evHeight
)
if peerHeight < evHeight { // peer is behind. sleep while he catches up
return nil, true
} else if peerHeight > evHeight+maxAge {
// evidence is too old, skip
// NOTE: if evidence is too old for an honest peer,
// then we're behind and either it already got committed or it never will!
evR.Logger.Info(
"Not sending peer old evidence",
} else if ageNumBlocks > params.MaxAgeNumBlocks ||
ageDuration > params.MaxAgeDuration { // evidence is too old, skip
// NOTE: if evidence is too old for an honest peer, then we're behind and
// either it already got committed or it never will!
evR.Logger.Info("Not sending peer old evidence",
"peerHeight", peerHeight,
"evHeight", evHeight,
"maxAge", maxAge,
"maxAgeNumBlocks", params.MaxAgeNumBlocks,
"lastBlockTime", evR.evpool.State().LastBlockTime,
"evTime", ev.Time(),
"maxAgeDuration", params.MaxAgeDuration,
"peer", peer,
)
+2 -2
View File
@@ -106,7 +106,7 @@ func _waitForEvidence(
func sendEvidence(t *testing.T, evpool *Pool, valAddr []byte, n int) types.EvidenceList {
evList := make([]types.Evidence, n)
for i := 0; i < n; i++ {
ev := types.NewMockGoodEvidence(int64(i+1), 0, valAddr)
ev := types.NewMockEvidence(int64(i+1), time.Now().UTC(), 0, valAddr)
err := evpool.AddEvidence(ev)
assert.Nil(t, err)
evList[i] = ev
@@ -215,7 +215,7 @@ func TestListMessageValidationBasic(t *testing.T) {
valAddr := []byte("myval")
evListMsg.Evidence = make([]types.Evidence, n)
for i := 0; i < n; i++ {
evListMsg.Evidence[i] = types.NewMockGoodEvidence(int64(i+1), 0, valAddr)
evListMsg.Evidence[i] = types.NewMockEvidence(int64(i+1), time.Now(), 0, valAddr)
}
tc.malleateEvListMsg(evListMsg)
assert.Equal(t, tc.expectErr, evListMsg.ValidateBasic() != nil, "Validate Basic had an unexpected result")
+11 -10
View File
@@ -2,6 +2,7 @@ package evidence
import (
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/tendermint/tendermint/types"
@@ -17,7 +18,7 @@ func TestStoreAddDuplicate(t *testing.T) {
store := NewStore(db)
priority := int64(10)
ev := types.NewMockGoodEvidence(2, 1, []byte("val1"))
ev := types.NewMockEvidence(2, time.Now().UTC(), 1, []byte("val1"))
added := store.AddNewEvidence(ev, priority)
assert.True(added)
@@ -34,7 +35,7 @@ func TestStoreCommitDuplicate(t *testing.T) {
store := NewStore(db)
priority := int64(10)
ev := types.NewMockGoodEvidence(2, 1, []byte("val1"))
ev := types.NewMockEvidence(2, time.Now().UTC(), 1, []byte("val1"))
store.MarkEvidenceAsCommitted(ev)
@@ -55,7 +56,7 @@ func TestStoreMark(t *testing.T) {
assert.Equal(0, len(pendingEv))
priority := int64(10)
ev := types.NewMockGoodEvidence(2, 1, []byte("val1"))
ev := types.NewMockEvidence(2, time.Now().UTC(), 1, []byte("val1"))
added := store.AddNewEvidence(ev, priority)
assert.True(added)
@@ -102,15 +103,15 @@ func TestStorePriority(t *testing.T) {
// sorted by priority and then height
cases := []struct {
ev types.MockGoodEvidence
ev types.MockEvidence
priority int64
}{
{types.NewMockGoodEvidence(2, 1, []byte("val1")), 17},
{types.NewMockGoodEvidence(5, 2, []byte("val2")), 15},
{types.NewMockGoodEvidence(10, 2, []byte("val2")), 13},
{types.NewMockGoodEvidence(100, 2, []byte("val2")), 11},
{types.NewMockGoodEvidence(90, 2, []byte("val2")), 11},
{types.NewMockGoodEvidence(80, 2, []byte("val2")), 11},
{types.NewMockEvidence(2, time.Now().UTC(), 1, []byte("val1")), 17},
{types.NewMockEvidence(5, time.Now().UTC(), 2, []byte("val2")), 15},
{types.NewMockEvidence(10, time.Now().UTC(), 2, []byte("val2")), 13},
{types.NewMockEvidence(100, time.Now().UTC(), 2, []byte("val2")), 11},
{types.NewMockEvidence(90, time.Now().UTC(), 2, []byte("val2")), 11},
{types.NewMockEvidence(80, time.Now().UTC(), 2, []byte("val2")), 11},
}
for _, c := range cases {