mirror of
https://github.com/tendermint/tendermint.git
synced 2026-09-20 06:54:41 +00:00
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:
+19
-11
@@ -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
@@ -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
@@ -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,
|
||||
)
|
||||
|
||||
|
||||
@@ -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
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user