github.com/okex/exchain@v1.8.0/libs/tendermint/blockchain/v0/pool_test.go (about)

     1  package v0
     2  
     3  import (
     4  	"fmt"
     5  	"testing"
     6  	"time"
     7  
     8  	"github.com/stretchr/testify/assert"
     9  	"github.com/stretchr/testify/require"
    10  
    11  	"github.com/okex/exchain/libs/tendermint/libs/log"
    12  	tmrand "github.com/okex/exchain/libs/tendermint/libs/rand"
    13  	"github.com/okex/exchain/libs/tendermint/p2p"
    14  	"github.com/okex/exchain/libs/tendermint/types"
    15  )
    16  
    17  func init() {
    18  	peerTimeout = 2 * time.Second
    19  }
    20  
    21  type testPeer struct {
    22  	id        p2p.ID
    23  	base      int64
    24  	height    int64
    25  	inputChan chan inputData //make sure each peer's data is sequential
    26  }
    27  
    28  type inputData struct {
    29  	t       *testing.T
    30  	pool    *BlockPool
    31  	request BlockRequest
    32  }
    33  
    34  func (p testPeer) runInputRoutine() {
    35  	go func() {
    36  		for input := range p.inputChan {
    37  			p.simulateInput(input)
    38  		}
    39  	}()
    40  }
    41  
    42  // Request desired, pretend like we got the block immediately.
    43  func (p testPeer) simulateInput(input inputData) {
    44  	block := &types.Block{Header: types.Header{Height: input.request.Height}}
    45  	input.pool.AddBlock(input.request.PeerID, &bcBlockResponseMessage{Block: block}, 123)
    46  	// TODO: uncommenting this creates a race which is detected by:
    47  	// https://github.com/golang/go/blob/2bd767b1022dd3254bcec469f0ee164024726486/src/testing/testing.go#L854-L856
    48  	// see: https://github.com/tendermint/tendermint/issues/3390#issue-418379890
    49  	// input.t.Logf("Added block from peer %v (height: %v)", input.request.PeerID, input.request.Height)
    50  }
    51  
    52  type testPeers map[p2p.ID]testPeer
    53  
    54  func (ps testPeers) start() {
    55  	for _, v := range ps {
    56  		v.runInputRoutine()
    57  	}
    58  }
    59  
    60  func (ps testPeers) stop() {
    61  	for _, v := range ps {
    62  		close(v.inputChan)
    63  	}
    64  }
    65  
    66  func makePeers(numPeers int, minHeight, maxHeight int64) testPeers {
    67  	peers := make(testPeers, numPeers)
    68  	for i := 0; i < numPeers; i++ {
    69  		peerID := p2p.ID(tmrand.Str(12))
    70  		height := minHeight + tmrand.Int63n(maxHeight-minHeight)
    71  		base := minHeight + int64(i)
    72  		if base > height {
    73  			base = height
    74  		}
    75  		peers[peerID] = testPeer{peerID, base, height, make(chan inputData, 10)}
    76  	}
    77  	return peers
    78  }
    79  
    80  func TestBlockPoolBasic(t *testing.T) {
    81  	start := int64(42)
    82  	peers := makePeers(10, start+1, 1000)
    83  	errorsCh := make(chan peerError, 1000)
    84  	requestsCh := make(chan BlockRequest, 1000)
    85  	pool := NewBlockPool(start, requestsCh, errorsCh)
    86  	pool.SetLogger(log.TestingLogger())
    87  
    88  	err := pool.Start()
    89  	if err != nil {
    90  		t.Error(err)
    91  	}
    92  
    93  	defer pool.Stop()
    94  
    95  	peers.start()
    96  	defer peers.stop()
    97  
    98  	// Introduce each peer.
    99  	go func() {
   100  		for _, peer := range peers {
   101  			pool.SetPeerRange(peer.id, peer.base, peer.height, 0)
   102  		}
   103  	}()
   104  
   105  	// Start a goroutine to pull blocks
   106  	go func() {
   107  		for {
   108  			if !pool.IsRunning() {
   109  				return
   110  			}
   111  			first, second, _ := pool.PeekTwoBlocks()
   112  			if first != nil && second != nil {
   113  				pool.PopRequest()
   114  			} else {
   115  				time.Sleep(1 * time.Second)
   116  			}
   117  		}
   118  	}()
   119  
   120  	// Pull from channels
   121  	for {
   122  		select {
   123  		case err := <-errorsCh:
   124  			t.Error(err)
   125  		case request := <-requestsCh:
   126  			t.Logf("Pulled new BlockRequest %v", request)
   127  			if request.Height == 300 {
   128  				return // Done!
   129  			}
   130  
   131  			peers[request.PeerID].inputChan <- inputData{t, pool, request}
   132  		}
   133  	}
   134  }
   135  
   136  func TestBlockPoolTimeout(t *testing.T) {
   137  	start := int64(42)
   138  	peers := makePeers(10, start+1, 1000)
   139  	errorsCh := make(chan peerError, 1000)
   140  	requestsCh := make(chan BlockRequest, 1000)
   141  	pool := NewBlockPool(start, requestsCh, errorsCh)
   142  	pool.SetLogger(log.TestingLogger())
   143  	err := pool.Start()
   144  	if err != nil {
   145  		t.Error(err)
   146  	}
   147  	defer pool.Stop()
   148  
   149  	for _, peer := range peers {
   150  		t.Logf("Peer %v", peer.id)
   151  	}
   152  
   153  	// Introduce each peer.
   154  	go func() {
   155  		for _, peer := range peers {
   156  			pool.SetPeerRange(peer.id, peer.base, peer.height, 0)
   157  		}
   158  	}()
   159  
   160  	// Start a goroutine to pull blocks
   161  	go func() {
   162  		for {
   163  			if !pool.IsRunning() {
   164  				return
   165  			}
   166  			first, second, _ := pool.PeekTwoBlocks()
   167  			if first != nil && second != nil {
   168  				pool.PopRequest()
   169  			} else {
   170  				time.Sleep(1 * time.Second)
   171  			}
   172  		}
   173  	}()
   174  
   175  	// Pull from channels
   176  	counter := 0
   177  	timedOut := map[p2p.ID]struct{}{}
   178  	for {
   179  		select {
   180  		case err := <-errorsCh:
   181  			t.Log(err)
   182  			// consider error to be always timeout here
   183  			if _, ok := timedOut[err.peerID]; !ok {
   184  				counter++
   185  				if counter == len(peers) {
   186  					return // Done!
   187  				}
   188  			}
   189  		case request := <-requestsCh:
   190  			t.Logf("Pulled new BlockRequest %+v", request)
   191  		}
   192  	}
   193  }
   194  
   195  func TestBlockPoolRemovePeer(t *testing.T) {
   196  	peers := make(testPeers, 10)
   197  	for i := 0; i < 10; i++ {
   198  		peerID := p2p.ID(fmt.Sprintf("%d", i+1))
   199  		height := int64(i + 1)
   200  		peers[peerID] = testPeer{peerID, 0, height, make(chan inputData)}
   201  	}
   202  	requestsCh := make(chan BlockRequest)
   203  	errorsCh := make(chan peerError)
   204  
   205  	pool := NewBlockPool(1, requestsCh, errorsCh)
   206  	pool.SetLogger(log.TestingLogger())
   207  	err := pool.Start()
   208  	require.NoError(t, err)
   209  	defer pool.Stop()
   210  
   211  	// add peers
   212  	for peerID, peer := range peers {
   213  		pool.SetPeerRange(peerID, peer.base, peer.height, 0)
   214  	}
   215  	assert.EqualValues(t, 10, pool.MaxPeerHeight())
   216  
   217  	// remove not-existing peer
   218  	assert.NotPanics(t, func() { pool.RemovePeer(p2p.ID("Superman")) })
   219  
   220  	// remove peer with biggest height
   221  	pool.RemovePeer(p2p.ID("10"))
   222  	assert.EqualValues(t, 9, pool.MaxPeerHeight())
   223  
   224  	// remove all peers
   225  	for peerID := range peers {
   226  		pool.RemovePeer(peerID)
   227  	}
   228  
   229  	assert.EqualValues(t, 0, pool.MaxPeerHeight())
   230  }