evidence: handling evidence from light client(s) (#4532)

Closes: #4530

This PR contains logic for both submitting an evidence by the light client (lite2 package) and receiving it on the Tendermint side (/broadcast_evidence RPC and/or EvidenceReactor#Receive). Upon receiving the ConflictingHeadersEvidence (introduced by this PR), the Tendermint validates it, then breaks it down into smaller pieces (DuplicateVoteEvidence, LunaticValidatorEvidence, PhantomValidatorEvidence, PotentialAmnesiaEvidence). Afterwards, each piece of evidence is verified against the state of the full node and added to the pool, from which it's reaped upon block creation.

* rpc/client: do not pass height param if height ptr is nil

* rpc/core: validate incoming evidence!

* only accept ConflictingHeadersEvidence if one

of the headers is committed from this full node's perspective

This simplifies the code. Plus, if there are multiple forks, we'll
likely to receive multiple ConflictingHeadersEvidence anyway.

* swap CommitSig with Vote in LunaticValidatorEvidence

Vote is needed to validate signature

* no need to embed client

http is a provider and should not be used as a client
This commit is contained in:
Anton Kaliaev
2020-04-22 11:29:05 +04:00
committed by GitHub
parent ae3d21cf71
commit 41c11ad2c1
31 changed files with 1833 additions and 476 deletions

View File

@@ -10,11 +10,11 @@ import (
clist "github.com/tendermint/tendermint/libs/clist"
"github.com/tendermint/tendermint/libs/log"
sm "github.com/tendermint/tendermint/state"
"github.com/tendermint/tendermint/store"
"github.com/tendermint/tendermint/types"
)
// Pool maintains a pool of valid evidence
// in an Store.
// Pool maintains a pool of valid evidence in an Store.
type Pool struct {
logger log.Logger
@@ -22,23 +22,43 @@ type Pool struct {
evidenceList *clist.CList // concurrent linked-list of evidence
// needed to load validators to verify evidence
stateDB dbm.DB
stateDB dbm.DB
blockStore *store.BlockStore
// a map of active validators and respective last heights validator is active
// if it was in validator set after EvidenceParams.MaxAgeNumBlocks or
// currently is (ie. [MaxAgeNumBlocks, CurrentHeight])
// In simple words, it means it's still bonded -> therefore slashable.
valToLastHeight valToLastHeightMap
// latest state
mtx sync.Mutex
state sm.State
}
func NewPool(stateDB, evidenceDB dbm.DB) *Pool {
store := NewStore(evidenceDB)
evpool := &Pool{
stateDB: stateDB,
state: sm.LoadState(stateDB),
logger: log.NewNopLogger(),
store: store,
evidenceList: clist.New(),
// Validator.Address -> Last height it was in validator set
type valToLastHeightMap map[string]int64
func NewPool(stateDB, evidenceDB dbm.DB, blockStore *store.BlockStore) (*Pool, error) {
var (
store = NewStore(evidenceDB)
state = sm.LoadState(stateDB)
)
valToLastHeight, err := buildValToLastHeightMap(state, stateDB, blockStore)
if err != nil {
return nil, err
}
return evpool
return &Pool{
stateDB: stateDB,
blockStore: blockStore,
state: state,
logger: log.NewNopLogger(),
store: store,
evidenceList: clist.New(),
valToLastHeight: valToLastHeight,
}, nil
}
func (evpool *Pool) EvidenceFront() *clist.CElement {
@@ -74,7 +94,6 @@ func (evpool *Pool) State() sm.State {
// Update loads the latest
func (evpool *Pool) Update(block *types.Block, state sm.State) {
// sanity check
if state.LastBlockHeight != block.Height {
panic(
@@ -92,43 +111,81 @@ func (evpool *Pool) Update(block *types.Block, state sm.State) {
// remove evidence from pending and mark committed
evpool.MarkEvidenceAsCommitted(block.Height, block.Time, block.Evidence.Evidence)
evpool.updateValToLastHeight(block.Height, state)
}
// AddEvidence checks the evidence is valid and adds it to the pool.
// AddEvidence checks the evidence is valid and adds it to the pool. If
// 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}
)
// check if evidence is already stored
if evpool.store.Has(evidence) {
return ErrEvidenceAlreadyStored{}
}
if err := sm.VerifyEvidence(evpool.stateDB, evpool.State(), evidence); err != nil {
return ErrInvalidEvidence{err}
}
// fetch the validator and return its voting power as its priority
// TODO: something better ?
valset, err := sm.LoadValidators(evpool.stateDB, evidence.Height())
valSet, err := sm.LoadValidators(evpool.stateDB, evidence.Height())
if err != nil {
return err
}
_, val := valset.GetByAddress(evidence.Address())
priority := val.VotingPower
_, err = evpool.store.AddNewEvidence(evidence, priority)
if err != nil {
return err
return fmt.Errorf("can't load validators at height #%d: %w", evidence.Height(), err)
}
evpool.logger.Info("Verified new evidence of byzantine behaviour", "evidence", evidence)
// Break composite evidence into smaller pieces.
if ce, ok := evidence.(types.CompositeEvidence); ok {
evpool.logger.Info("Breaking up composite evidence", "ev", evidence)
// add evidence to clist
evpool.evidenceList.PushBack(evidence)
blockMeta := evpool.blockStore.LoadBlockMeta(evidence.Height())
if blockMeta == nil {
return fmt.Errorf("don't have block meta at height #%d", evidence.Height())
}
if err := ce.VerifyComposite(&blockMeta.Header, valSet); err != nil {
return err
}
evList = ce.Split(&blockMeta.Header, valSet, evpool.valToLastHeight)
}
for _, ev := range evList {
if evpool.store.Has(evidence) {
return ErrEvidenceAlreadyStored{}
}
// 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 {
return fmt.Errorf("don't have block meta at height #%d", ev.Height())
}
header = &blockMeta.Header
}
// 1) Verify against state.
if err := sm.VerifyEvidence(evpool.stateDB, state, ev, header); err != nil {
return fmt.Errorf("failed to verify %v: %w", ev, err)
}
// 2) Compute priority.
_, val := valSet.GetByAddress(ev.Address())
priority := val.VotingPower
// 3) Save to store.
_, err := evpool.store.AddNewEvidence(ev, priority)
if err != nil {
return fmt.Errorf("failed to add new evidence %v: %w", ev, err)
}
// 4) Add evidence to clist.
evpool.evidenceList.PushBack(ev)
evpool.logger.Info("Verified new evidence of byzantine behaviour", "evidence", ev)
}
return nil
}
// MarkEvidenceAsCommitted marks all the evidence as committed and removes it from the queue.
// MarkEvidenceAsCommitted marks all the evidence as committed and removes it
// from the queue.
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{})
@@ -142,12 +199,25 @@ func (evpool *Pool) MarkEvidenceAsCommitted(height int64, lastBlockTime time.Tim
evpool.removeEvidence(height, lastBlockTime, evidenceParams, blockEvidenceMap)
}
// IsCommitted returns true if we have already seen this exact evidence and it is already marked as committed.
// IsCommitted returns true if we have already seen this exact evidence and it
// is already marked as committed.
func (evpool *Pool) IsCommitted(evidence types.Evidence) bool {
ei := evpool.store.getInfo(evidence)
return ei.Evidence != nil && ei.Committed
}
// 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 && >
// ConsensusParams.Evidence.MaxAgeNumBlocks).
func (evpool *Pool) ValidatorLastHeight(address []byte) int64 {
h, ok := evpool.valToLastHeight[string(address)]
if !ok {
return 0
}
return h
}
func (evpool *Pool) removeEvidence(
height int64,
lastBlockTime time.Time,
@@ -174,3 +244,83 @@ func (evpool *Pool) removeEvidence(
func evMapKey(ev types.Evidence) string {
return string(ev.Hash())
}
func (evpool *Pool) updateValToLastHeight(blockHeight int64, state sm.State) {
// Update current validators & add new ones.
for _, val := range state.Validators.Validators {
evpool.valToLastHeight[string(val.Address)] = blockHeight
}
// Remove validators outside of MaxAgeNumBlocks & MaxAgeDuration.
removeHeight := blockHeight - evpool.State().ConsensusParams.Evidence.MaxAgeNumBlocks
if removeHeight >= 1 {
valSet, err := sm.LoadValidators(evpool.stateDB, removeHeight)
if err != nil {
for _, val := range valSet.Validators {
h, ok := evpool.valToLastHeight[string(val.Address)]
if ok && h == removeHeight {
delete(evpool.valToLastHeight, string(val.Address))
}
}
}
}
}
func buildValToLastHeightMap(state sm.State, stateDB dbm.DB, blockStore *store.BlockStore) (valToLastHeightMap, error) {
var (
valToLastHeight = make(map[string]int64)
params = state.ConsensusParams.Evidence
numBlocks = int64(0)
minAgeTime = time.Now().Add(-params.MaxAgeDuration)
height = state.LastBlockHeight
)
if height == 0 {
return valToLastHeight, nil
}
meta := blockStore.LoadBlockMeta(height)
if meta == nil {
return nil, fmt.Errorf("block meta for height %d not found", height)
}
blockTime := meta.Header.Time
// From state.LastBlockHeight, build a map of "active" validators until
// MaxAgeNumBlocks is passed and block time is less than now() -
// MaxAgeDuration.
for height >= 1 && (numBlocks <= params.MaxAgeNumBlocks || !blockTime.Before(minAgeTime)) {
valSet, err := sm.LoadValidators(stateDB, height)
if err != nil {
// last stored height -> return
if _, ok := err.(sm.ErrNoValSetForHeight); ok {
return valToLastHeight, nil
}
return nil, fmt.Errorf("validator set for height %d not found", height)
}
for _, val := range valSet.Validators {
key := string(val.Address)
if _, ok := valToLastHeight[key]; !ok {
valToLastHeight[key] = height
}
}
height--
if height > 0 {
// NOTE: we assume here blockStore and state.Validators are in sync. I.e if
// block N is stored, then validators for height N are also stored in
// state.
meta := blockStore.LoadBlockMeta(height)
if meta == nil {
return nil, fmt.Errorf("block meta for height %d not found", height)
}
blockTime = meta.Header.Time
}
numBlocks++
}
return valToLastHeight, nil
}

View File

@@ -2,87 +2,65 @@ package evidence
import (
"os"
"sync"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
dbm "github.com/tendermint/tm-db"
sm "github.com/tendermint/tendermint/state"
"github.com/tendermint/tendermint/store"
"github.com/tendermint/tendermint/types"
tmtime "github.com/tendermint/tendermint/types/time"
)
func TestMain(m *testing.M) {
types.RegisterMockEvidences(cdc)
RegisterMockEvidences()
code := m.Run()
os.Exit(code)
}
func initializeValidatorState(valAddr []byte, height int64) dbm.DB {
stateDB := dbm.NewMemDB()
// create validator set and state
valSet := &types.ValidatorSet{
Validators: []*types.Validator{
{Address: valAddr},
},
}
state := sm.State{
LastBlockHeight: 0,
LastBlockTime: tmtime.Now(),
Validators: valSet,
NextValidators: valSet.CopyIncrementProposerPriority(1),
LastHeightValidatorsChanged: 1,
ConsensusParams: types.ConsensusParams{
Evidence: types.EvidenceParams{
MaxAgeNumBlocks: 10000,
MaxAgeDuration: 48 * time.Hour,
},
},
}
// save all states up to height
for i := int64(0); i < height; i++ {
state.LastBlockHeight = i
sm.SaveState(stateDB, state)
}
return stateDB
}
func TestEvidencePool(t *testing.T) {
var (
valAddr = []byte("val1")
height = int64(100002)
height = int64(52)
stateDB = initializeValidatorState(valAddr, height)
evidenceDB = dbm.NewMemDB()
pool = NewPool(stateDB, evidenceDB)
blockStoreDB = dbm.NewMemDB()
blockStore = initializeBlockStore(blockStoreDB, sm.LoadState(stateDB), valAddr)
evidenceTime = time.Date(2019, 1, 1, 0, 0, 0, 0, time.UTC)
goodEvidence = types.NewMockEvidence(height, time.Now(), 0, valAddr)
badEvidence = types.NewMockEvidence(1, evidenceTime, 0, valAddr)
)
goodEvidence := types.NewMockEvidence(height, time.Now(), 0, valAddr)
badEvidence := types.NewMockEvidence(1, evidenceTime, 0, valAddr)
pool, err := NewPool(stateDB, evidenceDB, blockStore)
require.NoError(t, err)
// bad evidence
err := pool.AddEvidence(badEvidence)
assert.Error(t, err)
// err: evidence created at 2019-01-01 00:00:00 +0000 UTC has expired. Evidence can not be older than: ...
err = pool.AddEvidence(badEvidence)
if assert.Error(t, err) {
assert.Contains(t, err.Error(), "is too old; min height is 32 and evidence can not be older than")
}
var wg sync.WaitGroup
wg.Add(1)
// good evidence
evAdded := make(chan struct{})
go func() {
<-pool.EvidenceWaitChan()
wg.Done()
close(evAdded)
}()
err = pool.AddEvidence(goodEvidence)
assert.NoError(t, err)
wg.Wait()
require.NoError(t, err)
select {
case <-evAdded:
case <-time.After(5 * time.Second):
t.Fatal("evidence was not added after 5s")
}
assert.Equal(t, 1, pool.evidenceList.Len())
@@ -93,16 +71,19 @@ func TestEvidencePool(t *testing.T) {
}
func TestEvidencePoolIsCommitted(t *testing.T) {
// Initialization:
var (
valAddr = []byte("validator_address")
height = int64(42)
height = int64(1)
lastBlockTime = time.Now()
stateDB = initializeValidatorState(valAddr, height)
evidenceDB = dbm.NewMemDB()
pool = NewPool(stateDB, evidenceDB)
blockStoreDB = dbm.NewMemDB()
blockStore = initializeBlockStore(blockStoreDB, sm.LoadState(stateDB), valAddr)
)
pool, err := NewPool(stateDB, evidenceDB, blockStore)
require.NoError(t, err)
// evidence not seen yet:
evidence := types.NewMockEvidence(height, time.Now(), 0, valAddr)
assert.False(t, pool.IsCommitted(evidence))
@@ -116,17 +97,20 @@ func TestEvidencePoolIsCommitted(t *testing.T) {
assert.True(t, pool.IsCommitted(evidence))
}
func TestAddEvidence(t *testing.T) {
func TestEvidencePoolAddEvidence(t *testing.T) {
var (
valAddr = []byte("val1")
height = int64(100002)
height = int64(30)
stateDB = initializeValidatorState(valAddr, height)
evidenceDB = dbm.NewMemDB()
pool = NewPool(stateDB, evidenceDB)
blockStoreDB = dbm.NewMemDB()
blockStore = initializeBlockStore(blockStoreDB, sm.LoadState(stateDB), valAddr)
evidenceTime = time.Date(2019, 1, 1, 0, 0, 0, 0, time.UTC)
)
pool, err := NewPool(stateDB, evidenceDB, blockStore)
require.NoError(t, err)
testCases := []struct {
evHeight int64
evTime time.Time
@@ -142,10 +126,127 @@ func TestAddEvidence(t *testing.T) {
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)
}
t.Run(tc.evDescription, func(t *testing.T) {
ev := types.NewMockEvidence(tc.evHeight, tc.evTime, 0, valAddr)
err := pool.AddEvidence(ev)
if tc.expErr {
assert.Error(t, err)
t.Log(err)
}
})
}
}
func TestEvidencePoolUpdate(t *testing.T) {
var (
valAddr = []byte("validator_address")
height = int64(1)
stateDB = initializeValidatorState(valAddr, height)
evidenceDB = dbm.NewMemDB()
blockStoreDB = dbm.NewMemDB()
state = sm.LoadState(stateDB)
blockStore = initializeBlockStore(blockStoreDB, state, valAddr)
)
pool, err := NewPool(stateDB, evidenceDB, blockStore)
require.NoError(t, err)
// create new block (no need to save it to blockStore)
evidence := types.NewMockEvidence(height, time.Now(), 0, valAddr)
lastCommit := makeCommit(height, valAddr)
block := types.MakeBlock(height+1, []types.Tx{}, lastCommit, []types.Evidence{evidence})
// update state (partially)
state.LastBlockHeight = height + 1
pool.Update(block, state)
// a) Update marks evidence as committed
assert.True(t, pool.IsCommitted(evidence))
// b) Update updates valToLastHeight map
assert.Equal(t, height+1, pool.ValidatorLastHeight(valAddr))
}
func TestEvidencePoolNewPool(t *testing.T) {
var (
valAddr = []byte("validator_address")
height = int64(1)
stateDB = initializeValidatorState(valAddr, height)
evidenceDB = dbm.NewMemDB()
blockStoreDB = dbm.NewMemDB()
state = sm.LoadState(stateDB)
blockStore = initializeBlockStore(blockStoreDB, state, valAddr)
)
pool, err := NewPool(stateDB, evidenceDB, blockStore)
require.NoError(t, err)
assert.Equal(t, height, pool.ValidatorLastHeight(valAddr))
assert.EqualValues(t, 0, pool.ValidatorLastHeight([]byte("non-existent-validator")))
}
func initializeValidatorState(valAddr []byte, height int64) dbm.DB {
stateDB := dbm.NewMemDB()
// create validator set and state
valSet := &types.ValidatorSet{
Validators: []*types.Validator{
{Address: valAddr, VotingPower: 0},
},
}
state := sm.State{
LastBlockHeight: height,
LastBlockTime: tmtime.Now(),
LastValidators: valSet,
Validators: valSet,
NextValidators: valSet.CopyIncrementProposerPriority(1),
LastHeightValidatorsChanged: 1,
ConsensusParams: types.ConsensusParams{
Block: types.BlockParams{
MaxBytes: 22020096,
MaxGas: -1,
},
Evidence: types.EvidenceParams{
MaxAgeNumBlocks: 20,
MaxAgeDuration: 48 * time.Hour,
},
},
}
// save all states up to height
for i := int64(0); i <= height; i++ {
state.LastBlockHeight = i
sm.SaveState(stateDB, state)
}
return stateDB
}
// initializeBlockStore creates a block storage and populates it w/ a dummy
// block at +height+.
func initializeBlockStore(db dbm.DB, state sm.State, valAddr []byte) *store.BlockStore {
blockStore := store.NewBlockStore(db)
for i := int64(1); i <= state.LastBlockHeight; i++ {
lastCommit := makeCommit(i-1, valAddr)
block, _ := state.MakeBlock(i, []types.Tx{}, lastCommit, nil,
state.Validators.GetProposer().Address)
const parts = 1
partSet := block.MakePartSet(parts)
seenCommit := makeCommit(i, valAddr)
blockStore.SaveBlock(block, partSet, seenCommit)
}
return blockStore
}
func makeCommit(height int64, valAddr []byte) *types.Commit {
commitSigs := []types.CommitSig{{
BlockIDFlag: types.BlockIDFlagCommit,
ValidatorAddress: valAddr,
Timestamp: time.Now(),
Signature: []byte("Signature"),
}}
return types.NewCommit(height, 0, types.BlockID{}, commitSigs)
}

View File

@@ -8,6 +8,7 @@ import (
"github.com/go-kit/kit/log/term"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
dbm "github.com/tendermint/tm-db"
@@ -15,6 +16,7 @@ import (
"github.com/tendermint/tendermint/crypto/secp256k1"
"github.com/tendermint/tendermint/libs/log"
"github.com/tendermint/tendermint/p2p"
sm "github.com/tendermint/tendermint/state"
"github.com/tendermint/tendermint/types"
)
@@ -34,12 +36,18 @@ func evidenceLogger() log.Logger {
// connect N evidence reactors through N switches
func makeAndConnectReactors(config *cfg.Config, stateDBs []dbm.DB) []*Reactor {
N := len(stateDBs)
reactors := make([]*Reactor, N)
logger := evidenceLogger()
for i := 0; i < N; i++ {
for i := 0; i < N; i++ {
evidenceDB := dbm.NewMemDB()
pool := NewPool(stateDBs[i], evidenceDB)
blockStoreDB := dbm.NewMemDB()
blockStore := initializeBlockStore(blockStoreDB, sm.LoadState(stateDBs[i]), []byte("myval"))
pool, err := NewPool(stateDBs[i], evidenceDB, blockStore)
if err != nil {
panic(err)
}
reactors[i] = NewReactor(pool)
reactors[i].SetLogger(logger.With("validator", i))
}
@@ -49,6 +57,7 @@ func makeAndConnectReactors(config *cfg.Config, stateDBs []dbm.DB) []*Reactor {
return s
}, p2p.Connect2Switches)
return reactors
}
@@ -67,7 +76,7 @@ func waitForEvidence(t *testing.T, evs types.EvidenceList, reactors []*Reactor)
close(done)
}()
timer := time.After(Timeout)
timer := time.After(timeout)
select {
case <-timer:
t.Fatal("Timed out waiting for evidence")
@@ -109,15 +118,15 @@ func sendEvidence(t *testing.T, evpool *Pool, valAddr []byte, n int) types.Evide
for i := 0; i < n; i++ {
ev := types.NewMockEvidence(int64(i+1), time.Now().UTC(), 0, valAddr)
err := evpool.AddEvidence(ev)
assert.Nil(t, err)
require.NoError(t, err)
evList[i] = ev
}
return evList
}
var (
NumEvidence = 10
Timeout = 120 * time.Second // ridiculously high because CircleCI is slow
numEvidence = 10
timeout = 120 * time.Second // ridiculously high because CircleCI is slow
)
func TestReactorBroadcastEvidence(t *testing.T) {
@@ -128,7 +137,7 @@ func TestReactorBroadcastEvidence(t *testing.T) {
stateDBs := make([]dbm.DB, N)
valAddr := []byte("myval")
// we need validators saved for heights at least as high as we have evidence for
height := int64(NumEvidence) + 10
height := int64(numEvidence) + 10
for i := 0; i < N; i++ {
stateDBs[i] = initializeValidatorState(valAddr, height)
}
@@ -146,7 +155,7 @@ func TestReactorBroadcastEvidence(t *testing.T) {
// send a bunch of valid evidence to the first reactor's evpool
// and wait for them all to be received in the others
evList := sendEvidence(t, reactors[0].evpool, valAddr, NumEvidence)
evList := sendEvidence(t, reactors[0].evpool, valAddr, numEvidence)
waitForEvidence(t, evList, reactors)
}
@@ -162,8 +171,8 @@ func TestReactorSelectiveBroadcast(t *testing.T) {
config := cfg.TestConfig()
valAddr := []byte("myval")
height1 := int64(NumEvidence) + 10
height2 := int64(NumEvidence) / 2
height1 := int64(numEvidence) + 10
height2 := int64(numEvidence) / 2
// DB1 is ahead of DB2
stateDB1 := initializeValidatorState(valAddr, height1)
@@ -186,10 +195,10 @@ func TestReactorSelectiveBroadcast(t *testing.T) {
peer.Set(types.PeerStateKey, ps)
// send a bunch of valid evidence to the first reactor's evpool
evList := sendEvidence(t, reactors[0].evpool, valAddr, NumEvidence)
evList := sendEvidence(t, reactors[0].evpool, valAddr, numEvidence)
// only ones less than the peers height should make it through
waitForEvidence(t, evList[:NumEvidence/2], reactors[1:2])
waitForEvidence(t, evList[:numEvidence/2], reactors[1:2])
// peers should still be connected
peers := reactors[1].Switch.Peers().List()

View File

@@ -6,6 +6,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
dbm "github.com/tendermint/tm-db"
"github.com/tendermint/tendermint/types"
@@ -14,8 +15,6 @@ import (
//-------------------------------------------
func TestStoreAddDuplicate(t *testing.T) {
assert := assert.New(t)
db := dbm.NewMemDB()
store := NewStore(db)
@@ -24,17 +23,15 @@ func TestStoreAddDuplicate(t *testing.T) {
added, err := store.AddNewEvidence(ev, priority)
require.NoError(t, err)
assert.True(added)
assert.True(t, added)
// cant add twice
added, err = store.AddNewEvidence(ev, priority)
require.NoError(t, err)
assert.False(added)
assert.False(t, added)
}
func TestStoreCommitDuplicate(t *testing.T) {
assert := assert.New(t)
db := dbm.NewMemDB()
store := NewStore(db)
@@ -45,65 +42,61 @@ func TestStoreCommitDuplicate(t *testing.T) {
added, err := store.AddNewEvidence(ev, priority)
require.NoError(t, err)
assert.False(added)
assert.False(t, added)
}
func TestStoreMark(t *testing.T) {
assert := assert.New(t)
db := dbm.NewMemDB()
store := NewStore(db)
// before we do anything, priority/pending are empty
priorityEv := store.PriorityEvidence()
pendingEv := store.PendingEvidence(-1)
assert.Equal(0, len(priorityEv))
assert.Equal(0, len(pendingEv))
assert.Equal(t, 0, len(priorityEv))
assert.Equal(t, 0, len(pendingEv))
priority := int64(10)
ev := types.NewMockEvidence(2, time.Now().UTC(), 1, []byte("val1"))
added, err := store.AddNewEvidence(ev, priority)
require.NoError(t, err)
assert.True(added)
assert.True(t, added)
// get the evidence. verify. should be uncommitted
ei := store.GetInfo(ev.Height(), ev.Hash())
assert.Equal(ev, ei.Evidence)
assert.Equal(priority, ei.Priority)
assert.False(ei.Committed)
assert.Equal(t, ev, ei.Evidence)
assert.Equal(t, priority, ei.Priority)
assert.False(t, ei.Committed)
// new evidence should be returns in priority/pending
priorityEv = store.PriorityEvidence()
pendingEv = store.PendingEvidence(-1)
assert.Equal(1, len(priorityEv))
assert.Equal(1, len(pendingEv))
assert.Equal(t, 1, len(priorityEv))
assert.Equal(t, 1, len(pendingEv))
// priority is now empty
store.MarkEvidenceAsBroadcasted(ev)
priorityEv = store.PriorityEvidence()
pendingEv = store.PendingEvidence(-1)
assert.Equal(0, len(priorityEv))
assert.Equal(1, len(pendingEv))
assert.Equal(t, 0, len(priorityEv))
assert.Equal(t, 1, len(pendingEv))
// priority and pending are now empty
store.MarkEvidenceAsCommitted(ev)
priorityEv = store.PriorityEvidence()
pendingEv = store.PendingEvidence(-1)
assert.Equal(0, len(priorityEv))
assert.Equal(0, len(pendingEv))
assert.Equal(t, 0, len(priorityEv))
assert.Equal(t, 0, len(pendingEv))
// evidence should show committed
newPriority := int64(0)
ei = store.GetInfo(ev.Height(), ev.Hash())
assert.Equal(ev, ei.Evidence)
assert.Equal(newPriority, ei.Priority)
assert.True(ei.Committed)
assert.Equal(t, ev, ei.Evidence)
assert.Equal(t, newPriority, ei.Priority)
assert.True(t, ei.Committed)
}
func TestStorePriority(t *testing.T) {
assert := assert.New(t)
db := dbm.NewMemDB()
store := NewStore(db)
@@ -123,11 +116,11 @@ func TestStorePriority(t *testing.T) {
for _, c := range cases {
added, err := store.AddNewEvidence(c.ev, c.priority)
require.NoError(t, err)
assert.True(added)
assert.True(t, added)
}
evList := store.PriorityEvidence()
for i, ev := range evList {
assert.Equal(ev, cases[i].ev)
assert.Equal(t, ev, cases[i].ev)
}
}