@ -11,6 +11,7 @@ import (
dbm "github.com/tendermint/tm-db"
dbm "github.com/tendermint/tm-db"
"github.com/tendermint/tendermint/internal/eventbus"
"github.com/tendermint/tendermint/internal/evidence"
"github.com/tendermint/tendermint/internal/evidence"
"github.com/tendermint/tendermint/internal/evidence/mocks"
"github.com/tendermint/tendermint/internal/evidence/mocks"
sm "github.com/tendermint/tendermint/internal/state"
sm "github.com/tendermint/tendermint/internal/state"
@ -21,6 +22,9 @@ import (
"github.com/tendermint/tendermint/libs/log"
"github.com/tendermint/tendermint/libs/log"
"github.com/tendermint/tendermint/types"
"github.com/tendermint/tendermint/types"
"github.com/tendermint/tendermint/version"
"github.com/tendermint/tendermint/version"
tmpubsub "github.com/tendermint/tendermint/internal/pubsub"
tmquery "github.com/tendermint/tendermint/internal/pubsub/query"
)
)
const evidenceChainID = "test_chain"
const evidenceChainID = "test_chain"
@ -40,7 +44,6 @@ func TestEvidencePoolBasic(t *testing.T) {
ctx , cancel := context . WithCancel ( context . Background ( ) )
ctx , cancel := context . WithCancel ( context . Background ( ) )
defer cancel ( )
defer cancel ( )
valSet , privVals := factory . ValidatorSet ( ctx , t , 1 , 10 )
valSet , privVals := factory . ValidatorSet ( ctx , t , 1 , 10 )
blockStore . On ( "LoadBlockMeta" , mock . AnythingOfType ( "int64" ) ) . Return (
blockStore . On ( "LoadBlockMeta" , mock . AnythingOfType ( "int64" ) ) . Return (
& types . BlockMeta { Header : types . Header { Time : defaultEvidenceTime } } ,
& types . BlockMeta { Header : types . Header { Time : defaultEvidenceTime } } ,
@ -48,9 +51,9 @@ func TestEvidencePoolBasic(t *testing.T) {
stateStore . On ( "LoadValidators" , mock . AnythingOfType ( "int64" ) ) . Return ( valSet , nil )
stateStore . On ( "LoadValidators" , mock . AnythingOfType ( "int64" ) ) . Return ( valSet , nil )
stateStore . On ( "Load" ) . Return ( createState ( height + 1 , valSet ) , nil )
stateStore . On ( "Load" ) . Return ( createState ( height + 1 , valSet ) , nil )
pool , err := evidence . NewPool ( log . TestingLogger ( ) , evidenceDB , stateStore , blockStore )
pool , err := evidence . NewPool ( log . TestingLogger ( ) , evidenceDB , stateStore , blockStore , evidence . NopMetrics ( ) )
require . NoError ( t , err )
require . NoError ( t , err )
require . NoError ( t , setupEventBus ( ctx , pool ) )
// evidence not seen yet:
// evidence not seen yet:
evs , size := pool . PendingEvidence ( defaultEvidenceMaxBytes )
evs , size := pool . PendingEvidence ( defaultEvidenceMaxBytes )
require . Equal ( t , 0 , len ( evs ) )
require . Equal ( t , 0 , len ( evs ) )
@ -66,7 +69,8 @@ func TestEvidencePoolBasic(t *testing.T) {
} ( )
} ( )
// evidence seen but not yet committed:
// evidence seen but not yet committed:
require . NoError ( t , pool . AddEvidence ( ev ) )
err = pool . AddEvidence ( ctx , ev )
require . NoError ( t , err )
select {
select {
case <- evAdded :
case <- evAdded :
@ -83,7 +87,8 @@ func TestEvidencePoolBasic(t *testing.T) {
require . Equal ( t , evidenceBytes , size ) // check that the size of the single evidence in bytes is correct
require . Equal ( t , evidenceBytes , size ) // check that the size of the single evidence in bytes is correct
// shouldn't be able to add evidence twice
// shouldn't be able to add evidence twice
require . NoError ( t , pool . AddEvidence ( ev ) )
err = pool . AddEvidence ( ctx , ev )
require . NoError ( t , err )
evs , _ = pool . PendingEvidence ( defaultEvidenceMaxBytes )
evs , _ = pool . PendingEvidence ( defaultEvidenceMaxBytes )
require . Equal ( t , 1 , len ( evs ) )
require . Equal ( t , 1 , len ( evs ) )
}
}
@ -110,9 +115,11 @@ func TestAddExpiredEvidence(t *testing.T) {
return & types . BlockMeta { Header : types . Header { Time : expiredEvidenceTime } }
return & types . BlockMeta { Header : types . Header { Time : expiredEvidenceTime } }
} )
} )
pool , err := evidence . NewPool ( log . TestingLogger ( ) , evidenceDB , stateStore , blockStore )
pool , err := evidence . NewPool ( log . TestingLogger ( ) , evidenceDB , stateStore , blockStore , evidence . NopMetrics ( ) )
require . NoError ( t , err )
require . NoError ( t , err )
require . NoError ( t , setupEventBus ( ctx , pool ) )
testCases := [ ] struct {
testCases := [ ] struct {
evHeight int64
evHeight int64
evTime time . Time
evTime time . Time
@ -136,7 +143,7 @@ func TestAddExpiredEvidence(t *testing.T) {
ev , err := types . NewMockDuplicateVoteEvidenceWithValidator ( ctx , tc . evHeight , tc . evTime , val , evidenceChainID )
ev , err := types . NewMockDuplicateVoteEvidenceWithValidator ( ctx , tc . evHeight , tc . evTime , val , evidenceChainID )
require . NoError ( t , err )
require . NoError ( t , err )
err = pool . AddEvidence ( ev )
err = pool . AddEvidence ( ctx , ev )
if tc . expErr {
if tc . expErr {
require . Error ( t , err )
require . Error ( t , err )
} else {
} else {
@ -153,6 +160,9 @@ func TestReportConflictingVotes(t *testing.T) {
defer cancel ( )
defer cancel ( )
pool , pv := defaultTestPool ( ctx , t , height )
pool , pv := defaultTestPool ( ctx , t , height )
require . NoError ( t , setupEventBus ( ctx , pool ) )
val := types . NewValidator ( pv . PrivKey . PubKey ( ) , 10 )
val := types . NewValidator ( pv . PrivKey . PubKey ( ) , 10 )
ev , err := types . NewMockDuplicateVoteEvidenceWithValidator ( ctx , height + 1 , defaultEvidenceTime , pv , evidenceChainID )
ev , err := types . NewMockDuplicateVoteEvidenceWithValidator ( ctx , height + 1 , defaultEvidenceTime , pv , evidenceChainID )
@ -176,7 +186,7 @@ func TestReportConflictingVotes(t *testing.T) {
state . LastBlockHeight ++
state . LastBlockHeight ++
state . LastBlockTime = ev . Time ( )
state . LastBlockTime = ev . Time ( )
state . LastValidators = types . NewValidatorSet ( [ ] * types . Validator { val } )
state . LastValidators = types . NewValidatorSet ( [ ] * types . Validator { val } )
pool . Update ( state , [ ] types . Evidence { } )
pool . Update ( ctx , state , [ ] types . Evidence { } )
// should be able to retrieve evidence from pool
// should be able to retrieve evidence from pool
evList , _ = pool . PendingEvidence ( defaultEvidenceMaxBytes )
evList , _ = pool . PendingEvidence ( defaultEvidenceMaxBytes )
@ -192,6 +202,9 @@ func TestEvidencePoolUpdate(t *testing.T) {
defer cancel ( )
defer cancel ( )
pool , val := defaultTestPool ( ctx , t , height )
pool , val := defaultTestPool ( ctx , t , height )
require . NoError ( t , setupEventBus ( ctx , pool ) )
state := pool . State ( )
state := pool . State ( )
// create two lots of old evidence that we expect to be pruned when we update
// create two lots of old evidence that we expect to be pruned when we update
@ -211,8 +224,8 @@ func TestEvidencePoolUpdate(t *testing.T) {
)
)
require . NoError ( t , err )
require . NoError ( t , err )
require . NoError ( t , pool . AddEvidence ( prunedEv ) )
require . NoError ( t , pool . AddEvidence ( notPrunedEv ) )
require . NoError ( t , pool . AddEvidence ( ctx , prunedEv ) )
require . NoError ( t , pool . AddEvidence ( ctx , notPrunedEv ) )
ev , err := types . NewMockDuplicateVoteEvidenceWithValidator (
ev , err := types . NewMockDuplicateVoteEvidenceWithValidator (
ctx ,
ctx ,
@ -234,21 +247,21 @@ func TestEvidencePoolUpdate(t *testing.T) {
require . Equal ( t , uint32 ( 2 ) , pool . Size ( ) )
require . Equal ( t , uint32 ( 2 ) , pool . Size ( ) )
require . NoError ( t , pool . CheckEvidence ( types . EvidenceList { ev } ) )
require . NoError ( t , pool . CheckEvidence ( ctx , types . EvidenceList { ev } ) )
evList , _ = pool . PendingEvidence ( 3 * defaultEvidenceMaxBytes )
evList , _ = pool . PendingEvidence ( 3 * defaultEvidenceMaxBytes )
require . Equal ( t , 3 , len ( evList ) )
require . Equal ( t , 3 , len ( evList ) )
require . Equal ( t , uint32 ( 3 ) , pool . Size ( ) )
require . Equal ( t , uint32 ( 3 ) , pool . Size ( ) )
pool . Update ( state , block . Evidence )
pool . Update ( ctx , state , block . Evidence )
// a) Update marks evidence as committed so pending evidence should be empty
// a) Update marks evidence as committed so pending evidence should be empty
evList , _ = pool . PendingEvidence ( defaultEvidenceMaxBytes )
evList , _ = pool . PendingEvidence ( defaultEvidenceMaxBytes )
require . Equal ( t , [ ] types . Evidence { notPrunedEv } , evList )
require . Equal ( t , [ ] types . Evidence { notPrunedEv } , evList )
// b) If we try to check this evidence again it should fail because it has already been committed
// b) If we try to check this evidence again it should fail because it has already been committed
err = pool . CheckEvidence ( types . EvidenceList { ev } )
err = pool . CheckEvidence ( ctx , types . EvidenceList { ev } )
if assert . Error ( t , err ) {
if assert . Error ( t , err ) {
assert . Equal ( t , "evidence was already committed" , err . ( * types . ErrInvalidEvidence ) . Reason . Error ( ) )
assert . Equal ( t , "evidence was already committed" , err . ( * types . ErrInvalidEvidence ) . Reason . Error ( ) )
}
}
@ -261,6 +274,9 @@ func TestVerifyPendingEvidencePasses(t *testing.T) {
defer cancel ( )
defer cancel ( )
pool , val := defaultTestPool ( ctx , t , height )
pool , val := defaultTestPool ( ctx , t , height )
require . NoError ( t , setupEventBus ( ctx , pool ) )
ev , err := types . NewMockDuplicateVoteEvidenceWithValidator (
ev , err := types . NewMockDuplicateVoteEvidenceWithValidator (
ctx ,
ctx ,
height ,
height ,
@ -269,8 +285,8 @@ func TestVerifyPendingEvidencePasses(t *testing.T) {
evidenceChainID ,
evidenceChainID ,
)
)
require . NoError ( t , err )
require . NoError ( t , err )
require . NoError ( t , pool . AddEvidence ( ev ) )
require . NoError ( t , pool . CheckEvidence ( types . EvidenceList { ev } ) )
require . NoError ( t , pool . AddEvidence ( ctx , ev ) )
require . NoError ( t , pool . CheckEvidence ( ctx , types . EvidenceList { ev } ) )
}
}
func TestVerifyDuplicatedEvidenceFails ( t * testing . T ) {
func TestVerifyDuplicatedEvidenceFails ( t * testing . T ) {
@ -281,6 +297,8 @@ func TestVerifyDuplicatedEvidenceFails(t *testing.T) {
pool , val := defaultTestPool ( ctx , t , height )
pool , val := defaultTestPool ( ctx , t , height )
require . NoError ( t , setupEventBus ( ctx , pool ) )
ev , err := types . NewMockDuplicateVoteEvidenceWithValidator (
ev , err := types . NewMockDuplicateVoteEvidenceWithValidator (
ctx ,
ctx ,
height ,
height ,
@ -290,12 +308,62 @@ func TestVerifyDuplicatedEvidenceFails(t *testing.T) {
)
)
require . NoError ( t , err )
require . NoError ( t , err )
err = pool . CheckEvidence ( types . EvidenceList { ev , ev } )
err = pool . CheckEvidence ( ctx , types . EvidenceList { ev , ev } )
if assert . Error ( t , err ) {
if assert . Error ( t , err ) {
assert . Equal ( t , "duplicate evidence" , err . ( * types . ErrInvalidEvidence ) . Reason . Error ( ) )
assert . Equal ( t , "duplicate evidence" , err . ( * types . ErrInvalidEvidence ) . Reason . Error ( ) )
}
}
}
}
// Check that we generate events when evidence is added into the evidence pool
func TestEventOnEvidenceValidated ( t * testing . T ) {
const height = 1
ctx , cancel := context . WithCancel ( context . Background ( ) )
defer cancel ( )
pool , val := defaultTestPool ( ctx , t , height )
ev , err := types . NewMockDuplicateVoteEvidenceWithValidator (
ctx ,
height ,
defaultEvidenceTime . Add ( 1 * time . Minute ) ,
val ,
evidenceChainID ,
)
require . NoError ( t , err )
eventBus := eventbus . NewDefault ( log . TestingLogger ( ) )
require . NoError ( t , eventBus . Start ( ctx ) )
pool . SetEventBus ( eventBus )
const query = ` tm.event='EvidenceValidated' `
evSub , err := eventBus . SubscribeWithArgs ( ctx , tmpubsub . SubscribeArgs {
ClientID : "test" ,
Query : tmquery . MustCompile ( query ) ,
} )
require . NoError ( t , err )
done := make ( chan struct { } )
go func ( ) {
defer close ( done )
msg , err := evSub . Next ( ctx )
assert . NoError ( t , err )
edt := msg . Data ( ) . ( types . EventDataEvidenceValidated )
assert . Equal ( t , ev , edt . Evidence )
} ( )
err = pool . AddEvidence ( ctx , ev )
require . NoError ( t , err )
select {
case <- done :
case <- time . After ( 1 * time . Second ) :
t . Fatal ( "did not receive a block header after 1 sec." )
}
}
// check that valid light client evidence is correctly validated and stored in
// check that valid light client evidence is correctly validated and stored in
// evidence pool
// evidence pool
func TestLightClientAttackEvidenceLifecycle ( t * testing . T ) {
func TestLightClientAttackEvidenceLifecycle ( t * testing . T ) {
@ -326,32 +394,38 @@ func TestLightClientAttackEvidenceLifecycle(t *testing.T) {
blockStore . On ( "LoadBlockCommit" , height ) . Return ( trusted . Commit )
blockStore . On ( "LoadBlockCommit" , height ) . Return ( trusted . Commit )
blockStore . On ( "LoadBlockCommit" , commonHeight ) . Return ( common . Commit )
blockStore . On ( "LoadBlockCommit" , commonHeight ) . Return ( common . Commit )
pool , err := evidence . NewPool ( log . TestingLogger ( ) , dbm . NewMemDB ( ) , stateStore , blockStore )
pool , err := evidence . NewPool ( log . TestingLogger ( ) , dbm . NewMemDB ( ) , stateStore , blockStore , evidence . NopMetrics ( ) )
require . NoError ( t , err )
require . NoError ( t , err )
require . NoError ( t , setupEventBus ( ctx , pool ) )
hash := ev . Hash ( )
hash := ev . Hash ( )
require . NoError ( t , pool . AddEvidence ( ev ) )
require . NoError ( t , pool . AddEvidence ( ev ) )
err = pool . AddEvidence ( ctx , ev )
require . NoError ( t , err )
err = pool . AddEvidence ( ctx , ev )
require . NoError ( t , err )
pendingEv , _ := pool . PendingEvidence ( state . ConsensusParams . Evidence . MaxBytes )
pendingEv , _ := pool . PendingEvidence ( state . ConsensusParams . Evidence . MaxBytes )
require . Equal ( t , 1 , len ( pendingEv ) )
require . Equal ( t , 1 , len ( pendingEv ) )
require . Equal ( t , ev , pendingEv [ 0 ] )
require . Equal ( t , ev , pendingEv [ 0 ] )
require . NoError ( t , pool . CheckEvidence ( pendingEv ) )
require . NoError ( t , pool . CheckEvidence ( ctx , pendingEv ) )
require . Equal ( t , ev , pendingEv [ 0 ] )
require . Equal ( t , ev , pendingEv [ 0 ] )
state . LastBlockHeight ++
state . LastBlockHeight ++
state . LastBlockTime = state . LastBlockTime . Add ( 1 * time . Minute )
state . LastBlockTime = state . LastBlockTime . Add ( 1 * time . Minute )
pool . Update ( state , pendingEv )
pool . Update ( ctx , state , pendingEv )
require . Equal ( t , hash , pendingEv [ 0 ] . Hash ( ) )
require . Equal ( t , hash , pendingEv [ 0 ] . Hash ( ) )
remaindingEv , _ := pool . PendingEvidence ( state . ConsensusParams . Evidence . MaxBytes )
remaindingEv , _ := pool . PendingEvidence ( state . ConsensusParams . Evidence . MaxBytes )
require . Empty ( t , remaindingEv )
require . Empty ( t , remaindingEv )
// evidence is already committed so it shouldn't pass
// evidence is already committed so it shouldn't pass
require . Error ( t , pool . CheckEvidence ( types . EvidenceList { ev } ) )
require . NoError ( t , pool . AddEvidence ( ev ) )
require . Error ( t , pool . CheckEvidence ( ctx , types . EvidenceList { ev } ) )
err = pool . AddEvidence ( ctx , ev )
require . NoError ( t , err )
remaindingEv , _ = pool . PendingEvidence ( state . ConsensusParams . Evidence . MaxBytes )
remaindingEv , _ = pool . PendingEvidence ( state . ConsensusParams . Evidence . MaxBytes )
require . Empty ( t , remaindingEv )
require . Empty ( t , remaindingEv )
@ -376,9 +450,11 @@ func TestRecoverPendingEvidence(t *testing.T) {
require . NoError ( t , err )
require . NoError ( t , err )
// create previous pool and populate it
// create previous pool and populate it
pool , err := evidence . NewPool ( log . TestingLogger ( ) , evidenceDB , stateStore , blockStore )
pool , err := evidence . NewPool ( log . TestingLogger ( ) , evidenceDB , stateStore , blockStore , evidence . NopMetrics ( ) )
require . NoError ( t , err )
require . NoError ( t , err )
require . NoError ( t , setupEventBus ( ctx , pool ) )
goodEvidence , err := types . NewMockDuplicateVoteEvidenceWithValidator (
goodEvidence , err := types . NewMockDuplicateVoteEvidenceWithValidator (
ctx ,
ctx ,
height ,
height ,
@ -396,8 +472,10 @@ func TestRecoverPendingEvidence(t *testing.T) {
)
)
require . NoError ( t , err )
require . NoError ( t , err )
require . NoError ( t , pool . AddEvidence ( goodEvidence ) )
require . NoError ( t , pool . AddEvidence ( expiredEvidence ) )
err = pool . AddEvidence ( ctx , goodEvidence )
require . NoError ( t , err )
err = pool . AddEvidence ( ctx , expiredEvidence )
require . NoError ( t , err )
// now recover from the previous pool at a different time
// now recover from the previous pool at a different time
newStateStore := & smmocks . Store { }
newStateStore := & smmocks . Store { }
@ -417,7 +495,7 @@ func TestRecoverPendingEvidence(t *testing.T) {
} ,
} ,
} , nil )
} , nil )
newPool , err := evidence . NewPool ( log . TestingLogger ( ) , evidenceDB , newStateStore , blockStore )
newPool , err := evidence . NewPool ( log . TestingLogger ( ) , evidenceDB , newStateStore , blockStore , evidence . NopMetrics ( ) )
require . NoError ( t , err )
require . NoError ( t , err )
evList , _ := newPool . PendingEvidence ( defaultEvidenceMaxBytes )
evList , _ := newPool . PendingEvidence ( defaultEvidenceMaxBytes )
@ -523,7 +601,7 @@ func defaultTestPool(ctx context.Context, t *testing.T, height int64) (*evidence
blockStore , err := initializeBlockStore ( dbm . NewMemDB ( ) , state , valAddress )
blockStore , err := initializeBlockStore ( dbm . NewMemDB ( ) , state , valAddress )
require . NoError ( t , err )
require . NoError ( t , err )
pool , err := evidence . NewPool ( log . TestingLogger ( ) , evidenceDB , stateStore , blockStore )
pool , err := evidence . NewPool ( log . TestingLogger ( ) , evidenceDB , stateStore , blockStore , evidence . NopMetrics ( ) )
require . NoError ( t , err , "test evidence pool could not be created" )
require . NoError ( t , err , "test evidence pool could not be created" )
return pool , val
return pool , val
@ -538,3 +616,12 @@ func createState(height int64, valSet *types.ValidatorSet) sm.State {
ConsensusParams : * types . DefaultConsensusParams ( ) ,
ConsensusParams : * types . DefaultConsensusParams ( ) ,
}
}
}
}
func setupEventBus ( ctx context . Context , evpool * evidence . Pool ) error {
eventBus := eventbus . NewDefault ( log . TestingLogger ( ) )
if err := eventBus . Start ( ctx ) ; err != nil {
return err
}
evpool . SetEventBus ( eventBus )
return nil
}