github.com/line/ostracon@v1.0.10-0.20230328032236-7f20145f065d/blockchain/v1/pool_test.go (about)

     1  package v1
     2  
     3  import (
     4  	"testing"
     5  	"time"
     6  
     7  	"github.com/stretchr/testify/assert"
     8  
     9  	"github.com/line/ostracon/libs/log"
    10  	"github.com/line/ostracon/p2p"
    11  	sm "github.com/line/ostracon/state"
    12  	"github.com/line/ostracon/types"
    13  )
    14  
    15  type testPeer struct {
    16  	id     p2p.ID
    17  	base   int64
    18  	height int64
    19  }
    20  
    21  type testBcR struct {
    22  	logger log.Logger
    23  }
    24  
    25  type testValues struct {
    26  	numRequestsSent int
    27  }
    28  
    29  var testResults testValues
    30  
    31  func resetPoolTestResults() {
    32  	testResults.numRequestsSent = 0
    33  }
    34  
    35  func (testR *testBcR) sendPeerError(err error, peerID p2p.ID) {
    36  }
    37  
    38  func (testR *testBcR) sendStatusRequest() {
    39  }
    40  
    41  func (testR *testBcR) sendBlockRequest(peerID p2p.ID, height int64) error {
    42  	testResults.numRequestsSent++
    43  	return nil
    44  }
    45  
    46  func (testR *testBcR) resetStateTimer(name string, timer **time.Timer, timeout time.Duration) {
    47  }
    48  
    49  func (testR *testBcR) switchToConsensus() {
    50  
    51  }
    52  
    53  func newTestBcR() *testBcR {
    54  	testBcR := &testBcR{logger: log.TestingLogger()}
    55  	return testBcR
    56  }
    57  
    58  type tPBlocks struct {
    59  	id     p2p.ID
    60  	create bool
    61  }
    62  
    63  // Makes a block pool with specified current height, list of peers, block requests and block responses
    64  func makeBlockPool(bcr *testBcR, height int64, peers []BpPeer, blocks map[int64]tPBlocks) *BlockPool {
    65  	bPool := NewBlockPool(height, bcr)
    66  	bPool.SetLogger(bcr.logger)
    67  
    68  	txs := []types.Tx{types.Tx("foo"), types.Tx("bar")}
    69  
    70  	var maxH int64
    71  	for _, p := range peers {
    72  		if p.Height > maxH {
    73  			maxH = p.Height
    74  		}
    75  		bPool.peers[p.ID] = NewBpPeer(p.ID, p.Base, p.Height, bcr.sendPeerError, nil)
    76  		bPool.peers[p.ID].SetLogger(bcr.logger)
    77  
    78  	}
    79  	bPool.MaxPeerHeight = maxH
    80  	for h, p := range blocks {
    81  		bPool.blocks[h] = p.id
    82  		bPool.peers[p.id].RequestSent(h)
    83  		if p.create {
    84  			// simulate that a block at height h has been received
    85  			_ = bPool.peers[p.id].AddBlock(
    86  				types.MakeBlock(h, txs, nil, nil, sm.InitStateVersion.Consensus),
    87  				100)
    88  		}
    89  	}
    90  	return bPool
    91  }
    92  
    93  func assertPeerSetsEquivalent(t *testing.T, set1 map[p2p.ID]*BpPeer, set2 map[p2p.ID]*BpPeer) {
    94  	assert.Equal(t, len(set1), len(set2))
    95  	for peerID, peer1 := range set1 {
    96  		peer2 := set2[peerID]
    97  		assert.NotNil(t, peer2)
    98  		assert.Equal(t, peer1.NumPendingBlockRequests, peer2.NumPendingBlockRequests)
    99  		assert.Equal(t, peer1.Height, peer2.Height)
   100  		assert.Equal(t, peer1.Base, peer2.Base)
   101  		assert.Equal(t, len(peer1.blocks), len(peer2.blocks))
   102  		for h, block1 := range peer1.blocks {
   103  			block2 := peer2.blocks[h]
   104  			// block1 and block2 could be nil if a request was made but no block was received
   105  			assert.Equal(t, block1, block2)
   106  		}
   107  	}
   108  }
   109  
   110  func assertBlockPoolEquivalent(t *testing.T, poolWanted, pool *BlockPool) {
   111  	assert.Equal(t, poolWanted.blocks, pool.blocks)
   112  	assertPeerSetsEquivalent(t, poolWanted.peers, pool.peers)
   113  	assert.Equal(t, poolWanted.MaxPeerHeight, pool.MaxPeerHeight)
   114  	assert.Equal(t, poolWanted.Height, pool.Height)
   115  
   116  }
   117  
   118  func TestBlockPoolUpdatePeer(t *testing.T) {
   119  	testBcR := newTestBcR()
   120  
   121  	tests := []struct {
   122  		name       string
   123  		pool       *BlockPool
   124  		args       testPeer
   125  		poolWanted *BlockPool
   126  		errWanted  error
   127  	}{
   128  		{
   129  			name:       "add a first short peer",
   130  			pool:       makeBlockPool(testBcR, 100, []BpPeer{}, map[int64]tPBlocks{}),
   131  			args:       testPeer{"P1", 0, 50},
   132  			errWanted:  errPeerTooShort,
   133  			poolWanted: makeBlockPool(testBcR, 100, []BpPeer{}, map[int64]tPBlocks{}),
   134  		},
   135  		{
   136  			name:       "add a first good peer",
   137  			pool:       makeBlockPool(testBcR, 100, []BpPeer{}, map[int64]tPBlocks{}),
   138  			args:       testPeer{"P1", 0, 101},
   139  			poolWanted: makeBlockPool(testBcR, 100, []BpPeer{{ID: "P1", Height: 101}}, map[int64]tPBlocks{}),
   140  		},
   141  		{
   142  			name:       "add a first good peer with base",
   143  			pool:       makeBlockPool(testBcR, 100, []BpPeer{}, map[int64]tPBlocks{}),
   144  			args:       testPeer{"P1", 10, 101},
   145  			poolWanted: makeBlockPool(testBcR, 100, []BpPeer{{ID: "P1", Base: 10, Height: 101}}, map[int64]tPBlocks{}),
   146  		},
   147  		{
   148  			name:       "increase the height of P1 from 120 to 123",
   149  			pool:       makeBlockPool(testBcR, 100, []BpPeer{{ID: "P1", Height: 120}}, map[int64]tPBlocks{}),
   150  			args:       testPeer{"P1", 0, 123},
   151  			poolWanted: makeBlockPool(testBcR, 100, []BpPeer{{ID: "P1", Height: 123}}, map[int64]tPBlocks{}),
   152  		},
   153  		{
   154  			name:       "decrease the height of P1 from 120 to 110",
   155  			pool:       makeBlockPool(testBcR, 100, []BpPeer{{ID: "P1", Height: 120}}, map[int64]tPBlocks{}),
   156  			args:       testPeer{"P1", 0, 110},
   157  			errWanted:  errPeerLowersItsHeight,
   158  			poolWanted: makeBlockPool(testBcR, 100, []BpPeer{}, map[int64]tPBlocks{}),
   159  		},
   160  		{
   161  			name: "decrease the height of P1 from 105 to 102 with blocks",
   162  			pool: makeBlockPool(testBcR, 100, []BpPeer{{ID: "P1", Height: 105}},
   163  				map[int64]tPBlocks{
   164  					100: {"P1", true}, 101: {"P1", true}, 102: {"P1", true}}),
   165  			args:      testPeer{"P1", 0, 102},
   166  			errWanted: errPeerLowersItsHeight,
   167  			poolWanted: makeBlockPool(testBcR, 100, []BpPeer{},
   168  				map[int64]tPBlocks{}),
   169  		},
   170  	}
   171  
   172  	for _, tt := range tests {
   173  		tt := tt
   174  		t.Run(tt.name, func(t *testing.T) {
   175  			pool := tt.pool
   176  			err := pool.UpdatePeer(tt.args.id, tt.args.base, tt.args.height)
   177  			assert.Equal(t, tt.errWanted, err)
   178  			assert.Equal(t, tt.poolWanted.blocks, tt.pool.blocks)
   179  			assertPeerSetsEquivalent(t, tt.poolWanted.peers, tt.pool.peers)
   180  			assert.Equal(t, tt.poolWanted.MaxPeerHeight, tt.pool.MaxPeerHeight)
   181  		})
   182  	}
   183  }
   184  
   185  func TestBlockPoolRemovePeer(t *testing.T) {
   186  	testBcR := newTestBcR()
   187  
   188  	type args struct {
   189  		peerID p2p.ID
   190  		err    error
   191  	}
   192  
   193  	tests := []struct {
   194  		name       string
   195  		pool       *BlockPool
   196  		args       args
   197  		poolWanted *BlockPool
   198  	}{
   199  		{
   200  			name:       "attempt to delete non-existing peer",
   201  			pool:       makeBlockPool(testBcR, 100, []BpPeer{{ID: "P1", Height: 120}}, map[int64]tPBlocks{}),
   202  			args:       args{"P99", nil},
   203  			poolWanted: makeBlockPool(testBcR, 100, []BpPeer{{ID: "P1", Height: 120}}, map[int64]tPBlocks{}),
   204  		},
   205  		{
   206  			name:       "delete the only peer without blocks",
   207  			pool:       makeBlockPool(testBcR, 100, []BpPeer{{ID: "P1", Height: 120}}, map[int64]tPBlocks{}),
   208  			args:       args{"P1", nil},
   209  			poolWanted: makeBlockPool(testBcR, 100, []BpPeer{}, map[int64]tPBlocks{}),
   210  		},
   211  		{
   212  			name: "delete the shortest of two peers without blocks",
   213  			pool: makeBlockPool(
   214  				testBcR,
   215  				100,
   216  				[]BpPeer{{ID: "P1", Height: 100}, {ID: "P2", Height: 120}},
   217  				map[int64]tPBlocks{}),
   218  			args:       args{"P1", nil},
   219  			poolWanted: makeBlockPool(testBcR, 100, []BpPeer{{ID: "P2", Height: 120}}, map[int64]tPBlocks{}),
   220  		},
   221  		{
   222  			name: "delete the tallest of two peers without blocks",
   223  			pool: makeBlockPool(
   224  				testBcR,
   225  				100,
   226  				[]BpPeer{{ID: "P1", Height: 100}, {ID: "P2", Height: 120}},
   227  				map[int64]tPBlocks{}),
   228  			args:       args{"P2", nil},
   229  			poolWanted: makeBlockPool(testBcR, 100, []BpPeer{{ID: "P1", Height: 100}}, map[int64]tPBlocks{}),
   230  		},
   231  		{
   232  			name: "delete the only peer with block requests sent and blocks received",
   233  			pool: makeBlockPool(testBcR, 100, []BpPeer{{ID: "P1", Height: 120}},
   234  				map[int64]tPBlocks{100: {"P1", true}, 101: {"P1", false}}),
   235  			args:       args{"P1", nil},
   236  			poolWanted: makeBlockPool(testBcR, 100, []BpPeer{}, map[int64]tPBlocks{}),
   237  		},
   238  		{
   239  			name: "delete the shortest of two peers with block requests sent and blocks received",
   240  			pool: makeBlockPool(testBcR, 100, []BpPeer{{ID: "P1", Height: 120}, {ID: "P2", Height: 200}},
   241  				map[int64]tPBlocks{100: {"P1", true}, 101: {"P1", false}}),
   242  			args:       args{"P1", nil},
   243  			poolWanted: makeBlockPool(testBcR, 100, []BpPeer{{ID: "P2", Height: 200}}, map[int64]tPBlocks{}),
   244  		},
   245  		{
   246  			name: "delete the tallest of two peers with block requests sent and blocks received",
   247  			pool: makeBlockPool(testBcR, 100, []BpPeer{{ID: "P1", Height: 120}, {ID: "P2", Height: 110}},
   248  				map[int64]tPBlocks{100: {"P1", true}, 101: {"P1", false}}),
   249  			args:       args{"P1", nil},
   250  			poolWanted: makeBlockPool(testBcR, 100, []BpPeer{{ID: "P2", Height: 110}}, map[int64]tPBlocks{}),
   251  		},
   252  	}
   253  
   254  	for _, tt := range tests {
   255  		tt := tt
   256  		t.Run(tt.name, func(t *testing.T) {
   257  			tt.pool.RemovePeer(tt.args.peerID, tt.args.err)
   258  			assertBlockPoolEquivalent(t, tt.poolWanted, tt.pool)
   259  		})
   260  	}
   261  }
   262  
   263  func TestBlockPoolRemoveShortPeers(t *testing.T) {
   264  	testBcR := newTestBcR()
   265  
   266  	tests := []struct {
   267  		name       string
   268  		pool       *BlockPool
   269  		poolWanted *BlockPool
   270  	}{
   271  		{
   272  			name: "no short peers",
   273  			pool: makeBlockPool(testBcR, 100,
   274  				[]BpPeer{{ID: "P1", Height: 100}, {ID: "P2", Height: 110}, {ID: "P3", Height: 120}}, map[int64]tPBlocks{}),
   275  			poolWanted: makeBlockPool(testBcR, 100,
   276  				[]BpPeer{{ID: "P1", Height: 100}, {ID: "P2", Height: 110}, {ID: "P3", Height: 120}}, map[int64]tPBlocks{}),
   277  		},
   278  
   279  		{
   280  			name: "one short peer",
   281  			pool: makeBlockPool(testBcR, 100,
   282  				[]BpPeer{{ID: "P1", Height: 100}, {ID: "P2", Height: 90}, {ID: "P3", Height: 120}}, map[int64]tPBlocks{}),
   283  			poolWanted: makeBlockPool(testBcR, 100,
   284  				[]BpPeer{{ID: "P1", Height: 100}, {ID: "P3", Height: 120}}, map[int64]tPBlocks{}),
   285  		},
   286  
   287  		{
   288  			name: "all short peers",
   289  			pool: makeBlockPool(testBcR, 100,
   290  				[]BpPeer{{ID: "P1", Height: 90}, {ID: "P2", Height: 91}, {ID: "P3", Height: 92}}, map[int64]tPBlocks{}),
   291  			poolWanted: makeBlockPool(testBcR, 100, []BpPeer{}, map[int64]tPBlocks{}),
   292  		},
   293  	}
   294  
   295  	for _, tt := range tests {
   296  		tt := tt
   297  		t.Run(tt.name, func(t *testing.T) {
   298  			pool := tt.pool
   299  			pool.removeShortPeers()
   300  			assertBlockPoolEquivalent(t, tt.poolWanted, tt.pool)
   301  		})
   302  	}
   303  }
   304  
   305  func TestBlockPoolSendRequestBatch(t *testing.T) {
   306  	type testPeerResult struct {
   307  		id                      p2p.ID
   308  		numPendingBlockRequests int
   309  	}
   310  
   311  	testBcR := newTestBcR()
   312  
   313  	tests := []struct {
   314  		name               string
   315  		pool               *BlockPool
   316  		maxRequestsPerPeer int
   317  		expRequests        map[int64]bool
   318  		expRequestsSent    int
   319  		expPeerResults     []testPeerResult
   320  	}{
   321  		{
   322  			name:               "one peer - send up to maxRequestsPerPeer block requests",
   323  			pool:               makeBlockPool(testBcR, 10, []BpPeer{{ID: "P1", Height: 100}}, map[int64]tPBlocks{}),
   324  			maxRequestsPerPeer: 2,
   325  			expRequests:        map[int64]bool{10: true, 11: true},
   326  			expRequestsSent:    2,
   327  			expPeerResults:     []testPeerResult{{id: "P1", numPendingBlockRequests: 2}},
   328  		},
   329  		{
   330  			name: "multiple peers - stops at gap between height and base",
   331  			pool: makeBlockPool(testBcR, 10, []BpPeer{
   332  				{ID: "P1", Base: 1, Height: 12},
   333  				{ID: "P2", Base: 15, Height: 100},
   334  			}, map[int64]tPBlocks{}),
   335  			maxRequestsPerPeer: 10,
   336  			expRequests:        map[int64]bool{10: true, 11: true, 12: true},
   337  			expRequestsSent:    3,
   338  			expPeerResults: []testPeerResult{
   339  				{id: "P1", numPendingBlockRequests: 3},
   340  				{id: "P2", numPendingBlockRequests: 0},
   341  			},
   342  		},
   343  		{
   344  			name: "n peers - send n*maxRequestsPerPeer block requests",
   345  			pool: makeBlockPool(
   346  				testBcR,
   347  				10,
   348  				[]BpPeer{{ID: "P1", Height: 100}, {ID: "P2", Height: 100}},
   349  				map[int64]tPBlocks{}),
   350  			maxRequestsPerPeer: 2,
   351  			expRequests:        map[int64]bool{10: true, 11: true},
   352  			expRequestsSent:    4,
   353  			expPeerResults: []testPeerResult{
   354  				{id: "P1", numPendingBlockRequests: 2},
   355  				{id: "P2", numPendingBlockRequests: 2}},
   356  		},
   357  	}
   358  
   359  	for _, tt := range tests {
   360  		tt := tt
   361  		t.Run(tt.name, func(t *testing.T) {
   362  			resetPoolTestResults()
   363  
   364  			var pool = tt.pool
   365  			maxRequestsPerPeer = tt.maxRequestsPerPeer
   366  			pool.MakeNextRequests(10)
   367  
   368  			assert.Equal(t, tt.expRequestsSent, testResults.numRequestsSent)
   369  			for _, tPeer := range tt.expPeerResults {
   370  				var peer = pool.peers[tPeer.id]
   371  				assert.NotNil(t, peer)
   372  				assert.Equal(t, tPeer.numPendingBlockRequests, peer.NumPendingBlockRequests)
   373  			}
   374  		})
   375  	}
   376  }
   377  
   378  func TestBlockPoolAddBlock(t *testing.T) {
   379  	testBcR := newTestBcR()
   380  	txs := []types.Tx{types.Tx("foo"), types.Tx("bar")}
   381  
   382  	type args struct {
   383  		peerID    p2p.ID
   384  		block     *types.Block
   385  		blockSize int
   386  	}
   387  	tests := []struct {
   388  		name       string
   389  		pool       *BlockPool
   390  		args       args
   391  		poolWanted *BlockPool
   392  		errWanted  error
   393  	}{
   394  		{name: "block from unknown peer",
   395  			pool: makeBlockPool(testBcR, 10, []BpPeer{{ID: "P1", Height: 100}}, map[int64]tPBlocks{}),
   396  			args: args{
   397  				peerID:    "P2",
   398  				block:     types.MakeBlock(int64(10), txs, nil, nil, sm.InitStateVersion.Consensus),
   399  				blockSize: 100,
   400  			},
   401  			poolWanted: makeBlockPool(testBcR, 10, []BpPeer{{ID: "P1", Height: 100}}, map[int64]tPBlocks{}),
   402  			errWanted:  errBadDataFromPeer,
   403  		},
   404  		{name: "unexpected block 11 from known peer - waiting for 10",
   405  			pool: makeBlockPool(testBcR, 10,
   406  				[]BpPeer{{ID: "P1", Height: 100}},
   407  				map[int64]tPBlocks{10: {"P1", false}}),
   408  			args: args{
   409  				peerID:    "P1",
   410  				block:     types.MakeBlock(int64(11), txs, nil, nil, sm.InitStateVersion.Consensus),
   411  				blockSize: 100,
   412  			},
   413  			poolWanted: makeBlockPool(testBcR, 10,
   414  				[]BpPeer{{ID: "P1", Height: 100}},
   415  				map[int64]tPBlocks{10: {"P1", false}}),
   416  			errWanted: errMissingBlock,
   417  		},
   418  		{name: "unexpected block 10 from known peer - already have 10",
   419  			pool: makeBlockPool(testBcR, 10,
   420  				[]BpPeer{{ID: "P1", Height: 100}},
   421  				map[int64]tPBlocks{10: {"P1", true}, 11: {"P1", false}}),
   422  			args: args{
   423  				peerID:    "P1",
   424  				block:     types.MakeBlock(int64(10), txs, nil, nil, sm.InitStateVersion.Consensus),
   425  				blockSize: 100,
   426  			},
   427  			poolWanted: makeBlockPool(testBcR, 10,
   428  				[]BpPeer{{ID: "P1", Height: 100}},
   429  				map[int64]tPBlocks{10: {"P1", true}, 11: {"P1", false}}),
   430  			errWanted: errDuplicateBlock,
   431  		},
   432  		{name: "unexpected block 10 from known peer P2 - expected 10 to come from P1",
   433  			pool: makeBlockPool(testBcR, 10,
   434  				[]BpPeer{{ID: "P1", Height: 100}, {ID: "P2", Height: 100}},
   435  				map[int64]tPBlocks{10: {"P1", false}}),
   436  			args: args{
   437  				peerID:    "P2",
   438  				block:     types.MakeBlock(int64(10), txs, nil, nil, sm.InitStateVersion.Consensus),
   439  				blockSize: 100,
   440  			},
   441  			poolWanted: makeBlockPool(testBcR, 10,
   442  				[]BpPeer{{ID: "P1", Height: 100}, {ID: "P2", Height: 100}},
   443  				map[int64]tPBlocks{10: {"P1", false}}),
   444  			errWanted: errBadDataFromPeer,
   445  		},
   446  		{name: "expected block from known peer",
   447  			pool: makeBlockPool(testBcR, 10,
   448  				[]BpPeer{{ID: "P1", Height: 100}},
   449  				map[int64]tPBlocks{10: {"P1", false}}),
   450  			args: args{
   451  				peerID:    "P1",
   452  				block:     types.MakeBlock(int64(10), txs, nil, nil, sm.InitStateVersion.Consensus),
   453  				blockSize: 100,
   454  			},
   455  			poolWanted: makeBlockPool(testBcR, 10,
   456  				[]BpPeer{{ID: "P1", Height: 100}},
   457  				map[int64]tPBlocks{10: {"P1", true}}),
   458  			errWanted: nil,
   459  		},
   460  	}
   461  
   462  	for _, tt := range tests {
   463  		tt := tt
   464  		t.Run(tt.name, func(t *testing.T) {
   465  			err := tt.pool.AddBlock(tt.args.peerID, tt.args.block, tt.args.blockSize)
   466  			assert.Equal(t, tt.errWanted, err)
   467  			assertBlockPoolEquivalent(t, tt.poolWanted, tt.pool)
   468  		})
   469  	}
   470  }
   471  
   472  func TestBlockPoolFirstTwoBlocksAndPeers(t *testing.T) {
   473  	testBcR := newTestBcR()
   474  
   475  	tests := []struct {
   476  		name         string
   477  		pool         *BlockPool
   478  		firstWanted  int64
   479  		secondWanted int64
   480  		errWanted    error
   481  	}{
   482  		{
   483  			name: "both blocks missing",
   484  			pool: makeBlockPool(testBcR, 10,
   485  				[]BpPeer{{ID: "P1", Height: 100}, {ID: "P2", Height: 100}},
   486  				map[int64]tPBlocks{15: {"P1", true}, 16: {"P2", true}}),
   487  			errWanted: errMissingBlock,
   488  		},
   489  		{
   490  			name: "second block missing",
   491  			pool: makeBlockPool(testBcR, 15,
   492  				[]BpPeer{{ID: "P1", Height: 100}, {ID: "P2", Height: 100}},
   493  				map[int64]tPBlocks{15: {"P1", true}, 18: {"P2", true}}),
   494  			firstWanted: 15,
   495  			errWanted:   errMissingBlock,
   496  		},
   497  		{
   498  			name: "first block missing",
   499  			pool: makeBlockPool(testBcR, 15,
   500  				[]BpPeer{{ID: "P1", Height: 100}, {ID: "P2", Height: 100}},
   501  				map[int64]tPBlocks{16: {"P2", true}, 18: {"P2", true}}),
   502  			secondWanted: 16,
   503  			errWanted:    errMissingBlock,
   504  		},
   505  		{
   506  			name: "both blocks present",
   507  			pool: makeBlockPool(testBcR, 10,
   508  				[]BpPeer{{ID: "P1", Height: 100}, {ID: "P2", Height: 100}},
   509  				map[int64]tPBlocks{10: {"P1", true}, 11: {"P2", true}}),
   510  			firstWanted:  10,
   511  			secondWanted: 11,
   512  		},
   513  	}
   514  
   515  	for _, tt := range tests {
   516  		tt := tt
   517  		t.Run(tt.name, func(t *testing.T) {
   518  			pool := tt.pool
   519  			gotFirst, gotSecond, err := pool.FirstTwoBlocksAndPeers()
   520  			assert.Equal(t, tt.errWanted, err)
   521  
   522  			if tt.firstWanted != 0 {
   523  				peer := pool.blocks[tt.firstWanted]
   524  				block := pool.peers[peer].blocks[tt.firstWanted]
   525  				assert.Equal(t, block, gotFirst.block,
   526  					"BlockPool.FirstTwoBlocksAndPeers() gotFirst = %v, want %v",
   527  					tt.firstWanted, gotFirst.block.Height)
   528  			}
   529  
   530  			if tt.secondWanted != 0 {
   531  				peer := pool.blocks[tt.secondWanted]
   532  				block := pool.peers[peer].blocks[tt.secondWanted]
   533  				assert.Equal(t, block, gotSecond.block,
   534  					"BlockPool.FirstTwoBlocksAndPeers() gotFirst = %v, want %v",
   535  					tt.secondWanted, gotSecond.block.Height)
   536  			}
   537  		})
   538  	}
   539  }
   540  
   541  func TestBlockPoolInvalidateFirstTwoBlocks(t *testing.T) {
   542  	testBcR := newTestBcR()
   543  
   544  	tests := []struct {
   545  		name       string
   546  		pool       *BlockPool
   547  		poolWanted *BlockPool
   548  	}{
   549  		{
   550  			name: "both blocks missing",
   551  			pool: makeBlockPool(testBcR, 10,
   552  				[]BpPeer{{ID: "P1", Height: 100}, {ID: "P2", Height: 100}},
   553  				map[int64]tPBlocks{15: {"P1", true}, 16: {"P2", true}}),
   554  			poolWanted: makeBlockPool(testBcR, 10,
   555  				[]BpPeer{{ID: "P1", Height: 100}, {ID: "P2", Height: 100}},
   556  				map[int64]tPBlocks{15: {"P1", true}, 16: {"P2", true}}),
   557  		},
   558  		{
   559  			name: "second block missing",
   560  			pool: makeBlockPool(testBcR, 15,
   561  				[]BpPeer{{ID: "P1", Height: 100}, {ID: "P2", Height: 100}},
   562  				map[int64]tPBlocks{15: {"P1", true}, 18: {"P2", true}}),
   563  			poolWanted: makeBlockPool(testBcR, 15,
   564  				[]BpPeer{{ID: "P2", Height: 100}},
   565  				map[int64]tPBlocks{18: {"P2", true}}),
   566  		},
   567  		{
   568  			name: "first block missing",
   569  			pool: makeBlockPool(testBcR, 15,
   570  				[]BpPeer{{ID: "P1", Height: 100}, {ID: "P2", Height: 100}},
   571  				map[int64]tPBlocks{18: {"P1", true}, 16: {"P2", true}}),
   572  			poolWanted: makeBlockPool(testBcR, 15,
   573  				[]BpPeer{{ID: "P1", Height: 100}},
   574  				map[int64]tPBlocks{18: {"P1", true}}),
   575  		},
   576  		{
   577  			name: "both blocks present",
   578  			pool: makeBlockPool(testBcR, 10,
   579  				[]BpPeer{{ID: "P1", Height: 100}, {ID: "P2", Height: 100}},
   580  				map[int64]tPBlocks{10: {"P1", true}, 11: {"P2", true}}),
   581  			poolWanted: makeBlockPool(testBcR, 10,
   582  				[]BpPeer{},
   583  				map[int64]tPBlocks{}),
   584  		},
   585  	}
   586  
   587  	for _, tt := range tests {
   588  		tt := tt
   589  		t.Run(tt.name, func(t *testing.T) {
   590  			tt.pool.InvalidateFirstTwoBlocks(errNoPeerResponse)
   591  			assertBlockPoolEquivalent(t, tt.poolWanted, tt.pool)
   592  		})
   593  	}
   594  }
   595  
   596  func TestProcessedCurrentHeightBlock(t *testing.T) {
   597  	testBcR := newTestBcR()
   598  
   599  	tests := []struct {
   600  		name       string
   601  		pool       *BlockPool
   602  		poolWanted *BlockPool
   603  	}{
   604  		{
   605  			name: "one peer",
   606  			pool: makeBlockPool(testBcR, 100, []BpPeer{{ID: "P1", Height: 120}},
   607  				map[int64]tPBlocks{100: {"P1", true}, 101: {"P1", true}}),
   608  			poolWanted: makeBlockPool(testBcR, 101, []BpPeer{{ID: "P1", Height: 120}},
   609  				map[int64]tPBlocks{101: {"P1", true}}),
   610  		},
   611  		{
   612  			name: "multiple peers",
   613  			pool: makeBlockPool(testBcR, 100,
   614  				[]BpPeer{{ID: "P1", Height: 120}, {ID: "P2", Height: 120}, {ID: "P3", Height: 130}},
   615  				map[int64]tPBlocks{
   616  					100: {"P1", true}, 104: {"P1", true}, 105: {"P1", false},
   617  					101: {"P2", true}, 103: {"P2", false},
   618  					102: {"P3", true}, 106: {"P3", true}}),
   619  			poolWanted: makeBlockPool(testBcR, 101,
   620  				[]BpPeer{{ID: "P1", Height: 120}, {ID: "P2", Height: 120}, {ID: "P3", Height: 130}},
   621  				map[int64]tPBlocks{
   622  					104: {"P1", true}, 105: {"P1", false},
   623  					101: {"P2", true}, 103: {"P2", false},
   624  					102: {"P3", true}, 106: {"P3", true}}),
   625  		},
   626  	}
   627  
   628  	for _, tt := range tests {
   629  		tt := tt
   630  		t.Run(tt.name, func(t *testing.T) {
   631  			tt.pool.ProcessedCurrentHeightBlock()
   632  			assertBlockPoolEquivalent(t, tt.poolWanted, tt.pool)
   633  		})
   634  	}
   635  }
   636  
   637  func TestRemovePeerAtCurrentHeight(t *testing.T) {
   638  	testBcR := newTestBcR()
   639  
   640  	tests := []struct {
   641  		name       string
   642  		pool       *BlockPool
   643  		poolWanted *BlockPool
   644  	}{
   645  		{
   646  			name: "one peer, remove peer for block at H",
   647  			pool: makeBlockPool(testBcR, 100, []BpPeer{{ID: "P1", Height: 120}},
   648  				map[int64]tPBlocks{100: {"P1", false}, 101: {"P1", true}}),
   649  			poolWanted: makeBlockPool(testBcR, 100, []BpPeer{}, map[int64]tPBlocks{}),
   650  		},
   651  		{
   652  			name: "one peer, remove peer for block at H+1",
   653  			pool: makeBlockPool(testBcR, 100, []BpPeer{{ID: "P1", Height: 120}},
   654  				map[int64]tPBlocks{100: {"P1", true}, 101: {"P1", false}}),
   655  			poolWanted: makeBlockPool(testBcR, 100, []BpPeer{}, map[int64]tPBlocks{}),
   656  		},
   657  		{
   658  			name: "multiple peers, remove peer for block at H",
   659  			pool: makeBlockPool(testBcR, 100,
   660  				[]BpPeer{{ID: "P1", Height: 120}, {ID: "P2", Height: 120}, {ID: "P3", Height: 130}},
   661  				map[int64]tPBlocks{
   662  					100: {"P1", false}, 104: {"P1", true}, 105: {"P1", false},
   663  					101: {"P2", true}, 103: {"P2", false},
   664  					102: {"P3", true}, 106: {"P3", true}}),
   665  			poolWanted: makeBlockPool(testBcR, 100,
   666  				[]BpPeer{{ID: "P2", Height: 120}, {ID: "P3", Height: 130}},
   667  				map[int64]tPBlocks{
   668  					101: {"P2", true}, 103: {"P2", false},
   669  					102: {"P3", true}, 106: {"P3", true}}),
   670  		},
   671  		{
   672  			name: "multiple peers, remove peer for block at H+1",
   673  			pool: makeBlockPool(testBcR, 100,
   674  				[]BpPeer{{ID: "P1", Height: 120}, {ID: "P2", Height: 120}, {ID: "P3", Height: 130}},
   675  				map[int64]tPBlocks{
   676  					100: {"P1", true}, 104: {"P1", true}, 105: {"P1", false},
   677  					101: {"P2", false}, 103: {"P2", false},
   678  					102: {"P3", true}, 106: {"P3", true}}),
   679  			poolWanted: makeBlockPool(testBcR, 100,
   680  				[]BpPeer{{ID: "P1", Height: 120}, {ID: "P3", Height: 130}},
   681  				map[int64]tPBlocks{
   682  					100: {"P1", true}, 104: {"P1", true}, 105: {"P1", false},
   683  					102: {"P3", true}, 106: {"P3", true}}),
   684  		},
   685  	}
   686  
   687  	for _, tt := range tests {
   688  		tt := tt
   689  		t.Run(tt.name, func(t *testing.T) {
   690  			tt.pool.RemovePeerAtCurrentHeights(errNoPeerResponse)
   691  			assertBlockPoolEquivalent(t, tt.poolWanted, tt.pool)
   692  		})
   693  	}
   694  }