2015-03-22 19:00:08 -07:00
package blockchain
2015-03-22 12:46:53 -07:00
import (
2019-03-20 03:59:33 +03:00
"fmt"
2015-03-22 12:46:53 -07:00
"testing"
2015-03-22 19:20:54 -07:00
"time"
2015-03-22 12:46:53 -07:00
2019-03-20 03:59:33 +03:00
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
2018-07-01 22:36:49 -04:00
cmn "github.com/tendermint/tendermint/libs/common"
"github.com/tendermint/tendermint/libs/log"
2018-01-03 11:29:19 +01:00
"github.com/tendermint/tendermint/p2p"
"github.com/tendermint/tendermint/types"
2015-03-22 12:46:53 -07:00
)
2016-02-29 16:15:23 -05:00
func init ( ) {
2018-02-26 17:35:01 +04:00
peerTimeout = 2 * time . Second
2016-02-29 16:15:23 -05:00
}
2015-03-22 12:46:53 -07:00
type testPeer struct {
2018-11-26 15:31:11 -05:00
id p2p . ID
height int64
inputChan chan inputData //make sure each peer's data is sequential
2015-03-22 12:46:53 -07:00
}
2018-11-26 15:31:11 -05:00
type inputData struct {
t * testing . T
pool * BlockPool
request BlockRequest
}
func ( p testPeer ) runInputRoutine ( ) {
go func ( ) {
for input := range p . inputChan {
p . simulateInput ( input )
}
} ( )
}
// Request desired, pretend like we got the block immediately.
func ( p testPeer ) simulateInput ( input inputData ) {
block := & types . Block { Header : types . Header { Height : input . request . Height } }
input . pool . AddBlock ( input . request . PeerID , block , 123 )
2019-03-28 17:39:09 +01:00
// TODO: uncommenting this creates a race which is detected by: https://github.com/golang/go/blob/2bd767b1022dd3254bcec469f0ee164024726486/src/testing/testing.go#L854-L856
// see: https://github.com/tendermint/tendermint/issues/3390#issue-418379890
// input.t.Logf("Added block from peer %v (height: %v)", input.request.PeerID, input.request.Height)
2018-11-26 15:31:11 -05:00
}
type testPeers map [ p2p . ID ] testPeer
func ( ps testPeers ) start ( ) {
for _ , v := range ps {
v . runInputRoutine ( )
}
}
func ( ps testPeers ) stop ( ) {
for _ , v := range ps {
close ( v . inputChan )
}
}
func makePeers ( numPeers int , minHeight , maxHeight int64 ) testPeers {
peers := make ( testPeers , numPeers )
2015-03-22 12:46:53 -07:00
for i := 0 ; i < numPeers ; i ++ {
2018-01-01 21:27:38 -05:00
peerID := p2p . ID ( cmn . RandStr ( 12 ) )
2018-07-16 00:20:37 -07:00
height := minHeight + cmn . RandInt63n ( maxHeight - minHeight )
2018-11-26 15:31:11 -05:00
peers [ peerID ] = testPeer { peerID , height , make ( chan inputData , 10 ) }
2015-03-22 12:46:53 -07:00
}
return peers
}
2019-03-20 03:59:33 +03:00
func TestBlockPoolBasic ( t * testing . T ) {
2017-12-01 19:04:53 -06:00
start := int64 ( 42 )
2015-09-09 21:44:48 -07:00
peers := makePeers ( 10 , start + 1 , 1000 )
2018-03-04 13:42:26 +04:00
errorsCh := make ( chan peerError , 1000 )
requestsCh := make ( chan BlockRequest , 1000 )
2018-02-26 17:35:01 +04:00
pool := NewBlockPool ( start , requestsCh , errorsCh )
2017-05-02 11:53:32 +04:00
pool . SetLogger ( log . TestingLogger ( ) )
2017-09-06 11:50:43 -04:00
2017-11-06 13:20:39 -05:00
err := pool . Start ( )
2017-09-06 11:50:43 -04:00
if err != nil {
t . Error ( err )
}
2017-01-19 13:33:58 +04:00
defer pool . Stop ( )
2015-03-22 12:46:53 -07:00
2018-11-26 15:31:11 -05:00
peers . start ( )
defer peers . stop ( )
2015-03-22 12:46:53 -07:00
// Introduce each peer.
go func ( ) {
for _ , peer := range peers {
2015-03-24 11:02:30 -07:00
pool . SetPeerHeight ( peer . id , peer . height )
2015-03-22 12:46:53 -07:00
}
} ( )
2015-03-24 11:02:30 -07:00
// Start a goroutine to pull blocks
go func ( ) {
for {
if ! pool . IsRunning ( ) {
return
}
first , second := pool . PeekTwoBlocks ( )
if first != nil && second != nil {
pool . PopRequest ( )
} else {
time . Sleep ( 1 * time . Second )
}
}
} ( )
2015-03-22 12:46:53 -07:00
// Pull from channels
for {
select {
2018-02-26 17:35:01 +04:00
case err := <- errorsCh :
t . Error ( err )
2015-03-22 12:46:53 -07:00
case request := <- requestsCh :
2017-05-02 11:53:32 +04:00
t . Logf ( "Pulled new BlockRequest %v" , request )
2015-03-24 11:02:30 -07:00
if request . Height == 300 {
return // Done!
}
2018-11-26 15:31:11 -05:00
peers [ request . PeerID ] . inputChan <- inputData { t , pool , request }
2015-03-22 12:46:53 -07:00
}
}
2015-03-22 16:23:24 -07:00
}
2019-03-20 03:59:33 +03:00
func TestBlockPoolTimeout ( t * testing . T ) {
2017-12-01 19:04:53 -06:00
start := int64 ( 42 )
2015-09-09 21:44:48 -07:00
peers := makePeers ( 10 , start + 1 , 1000 )
2018-03-04 13:42:26 +04:00
errorsCh := make ( chan peerError , 1000 )
requestsCh := make ( chan BlockRequest , 1000 )
2018-02-26 17:35:01 +04:00
pool := NewBlockPool ( start , requestsCh , errorsCh )
2017-05-02 11:53:32 +04:00
pool . SetLogger ( log . TestingLogger ( ) )
2017-11-06 13:20:39 -05:00
err := pool . Start ( )
2017-09-06 11:50:43 -04:00
if err != nil {
t . Error ( err )
}
2017-01-19 13:33:58 +04:00
defer pool . Stop ( )
2015-03-22 16:23:24 -07:00
2015-09-09 21:44:48 -07:00
for _ , peer := range peers {
2017-05-12 17:40:57 +02:00
t . Logf ( "Peer %v" , peer . id )
2015-09-09 21:44:48 -07:00
}
2015-03-22 16:23:24 -07:00
// Introduce each peer.
go func ( ) {
for _ , peer := range peers {
2015-03-24 11:02:30 -07:00
pool . SetPeerHeight ( peer . id , peer . height )
}
} ( )
// Start a goroutine to pull blocks
go func ( ) {
for {
if ! pool . IsRunning ( ) {
return
}
first , second := pool . PeekTwoBlocks ( )
if first != nil && second != nil {
pool . PopRequest ( )
} else {
time . Sleep ( 1 * time . Second )
}
2015-03-22 16:23:24 -07:00
}
} ( )
// Pull from channels
2015-03-24 11:02:30 -07:00
counter := 0
2018-01-01 21:27:38 -05:00
timedOut := map [ p2p . ID ] struct { } { }
2015-03-22 16:23:24 -07:00
for {
select {
2018-02-26 17:35:01 +04:00
case err := <- errorsCh :
t . Log ( err )
// consider error to be always timeout here
if _ , ok := timedOut [ err . peerID ] ; ! ok {
2015-03-24 11:02:30 -07:00
counter ++
if counter == len ( peers ) {
return // Done!
}
2015-03-22 16:23:24 -07:00
}
2015-03-24 11:02:30 -07:00
case request := <- requestsCh :
2017-05-12 17:40:57 +02:00
t . Logf ( "Pulled new BlockRequest %+v" , request )
2015-03-22 16:23:24 -07:00
}
}
2015-03-22 12:46:53 -07:00
}
2019-03-20 03:59:33 +03:00
func TestBlockPoolRemovePeer ( t * testing . T ) {
peers := make ( testPeers , 10 )
for i := 0 ; i < 10 ; i ++ {
peerID := p2p . ID ( fmt . Sprintf ( "%d" , i + 1 ) )
height := int64 ( i + 1 )
peers [ peerID ] = testPeer { peerID , height , make ( chan inputData ) }
}
requestsCh := make ( chan BlockRequest )
errorsCh := make ( chan peerError )
pool := NewBlockPool ( 1 , requestsCh , errorsCh )
pool . SetLogger ( log . TestingLogger ( ) )
err := pool . Start ( )
require . NoError ( t , err )
defer pool . Stop ( )
// add peers
for peerID , peer := range peers {
pool . SetPeerHeight ( peerID , peer . height )
}
assert . EqualValues ( t , 10 , pool . MaxPeerHeight ( ) )
// remove not-existing peer
assert . NotPanics ( t , func ( ) { pool . RemovePeer ( p2p . ID ( "Superman" ) ) } )
// remove peer with biggest height
pool . RemovePeer ( p2p . ID ( "10" ) )
assert . EqualValues ( t , 9 , pool . MaxPeerHeight ( ) )
// remove all peers
for peerID := range peers {
pool . RemovePeer ( peerID )
}
assert . EqualValues ( t , 0 , pool . MaxPeerHeight ( ) )
}