github.com/number571/tendermint@v0.34.11-gost/internal/blockchain/v2/scheduler_test.go (about)

     1  package v2
     2  
     3  import (
     4  	"fmt"
     5  	"math"
     6  	"sort"
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/stretchr/testify/assert"
    11  	"github.com/stretchr/testify/require"
    12  
    13  	"github.com/number571/tendermint/state"
    14  	"github.com/number571/tendermint/types"
    15  )
    16  
    17  type scTestParams struct {
    18  	peers         map[string]*scPeer
    19  	initHeight    int64
    20  	height        int64
    21  	allB          []int64
    22  	pending       map[int64]types.NodeID
    23  	pendingTime   map[int64]time.Time
    24  	received      map[int64]types.NodeID
    25  	peerTimeout   time.Duration
    26  	minRecvRate   int64
    27  	targetPending int
    28  	startTime     time.Time
    29  	syncTimeout   time.Duration
    30  }
    31  
    32  func verifyScheduler(sc *scheduler) {
    33  	missing := 0
    34  	if sc.maxHeight() >= sc.height {
    35  		missing = int(math.Min(float64(sc.targetPending), float64(sc.maxHeight()-sc.height+1)))
    36  	}
    37  	if len(sc.blockStates) != missing {
    38  		panic(fmt.Sprintf("scheduler block length %d different than target %d", len(sc.blockStates), missing))
    39  	}
    40  }
    41  
    42  func newTestScheduler(params scTestParams) *scheduler {
    43  	peers := make(map[types.NodeID]*scPeer)
    44  	var maxHeight int64
    45  
    46  	initHeight := params.initHeight
    47  	if initHeight == 0 {
    48  		initHeight = 1
    49  	}
    50  	sc := newScheduler(initHeight, params.startTime)
    51  	if params.height != 0 {
    52  		sc.height = params.height
    53  	}
    54  
    55  	for id, peer := range params.peers {
    56  		peer.peerID = types.NodeID(id)
    57  		peers[types.NodeID(id)] = peer
    58  		if maxHeight < peer.height {
    59  			maxHeight = peer.height
    60  		}
    61  	}
    62  	for _, h := range params.allB {
    63  		sc.blockStates[h] = blockStateNew
    64  	}
    65  	for h, pid := range params.pending {
    66  		sc.blockStates[h] = blockStatePending
    67  		sc.pendingBlocks[h] = pid
    68  	}
    69  	for h, tm := range params.pendingTime {
    70  		sc.pendingTime[h] = tm
    71  	}
    72  	for h, pid := range params.received {
    73  		sc.blockStates[h] = blockStateReceived
    74  		sc.receivedBlocks[h] = pid
    75  	}
    76  
    77  	sc.peers = peers
    78  	sc.peerTimeout = params.peerTimeout
    79  	if params.syncTimeout == 0 {
    80  		sc.syncTimeout = 10 * time.Second
    81  	} else {
    82  		sc.syncTimeout = params.syncTimeout
    83  	}
    84  
    85  	if params.targetPending == 0 {
    86  		sc.targetPending = 10
    87  	} else {
    88  		sc.targetPending = params.targetPending
    89  	}
    90  
    91  	sc.minRecvRate = params.minRecvRate
    92  
    93  	verifyScheduler(sc)
    94  
    95  	return sc
    96  }
    97  
    98  func TestScInit(t *testing.T) {
    99  	var (
   100  		initHeight int64 = 5
   101  		sc               = newScheduler(initHeight, time.Now())
   102  	)
   103  	assert.Equal(t, blockStateProcessed, sc.getStateAtHeight(initHeight-1))
   104  	assert.Equal(t, blockStateUnknown, sc.getStateAtHeight(initHeight))
   105  	assert.Equal(t, blockStateUnknown, sc.getStateAtHeight(initHeight+1))
   106  }
   107  
   108  func TestScMaxHeights(t *testing.T) {
   109  
   110  	tests := []struct {
   111  		name    string
   112  		sc      scheduler
   113  		wantMax int64
   114  	}{
   115  		{
   116  			name:    "no peers",
   117  			sc:      scheduler{height: 11},
   118  			wantMax: 10,
   119  		},
   120  		{
   121  			name: "one ready peer",
   122  			sc: scheduler{
   123  				height: 3,
   124  				peers:  map[types.NodeID]*scPeer{"P1": {height: 6, state: peerStateReady}},
   125  			},
   126  			wantMax: 6,
   127  		},
   128  		{
   129  			name: "ready and removed peers",
   130  			sc: scheduler{
   131  				height: 1,
   132  				peers: map[types.NodeID]*scPeer{
   133  					"P1": {height: 4, state: peerStateReady},
   134  					"P2": {height: 10, state: peerStateRemoved}},
   135  			},
   136  			wantMax: 4,
   137  		},
   138  		{
   139  			name: "removed peers",
   140  			sc: scheduler{
   141  				height: 1,
   142  				peers: map[types.NodeID]*scPeer{
   143  					"P1": {height: 4, state: peerStateRemoved},
   144  					"P2": {height: 10, state: peerStateRemoved}},
   145  			},
   146  			wantMax: 0,
   147  		},
   148  		{
   149  			name: "new peers",
   150  			sc: scheduler{
   151  				height: 1,
   152  				peers: map[types.NodeID]*scPeer{
   153  					"P1": {base: -1, height: -1, state: peerStateNew},
   154  					"P2": {base: -1, height: -1, state: peerStateNew}},
   155  			},
   156  			wantMax: 0,
   157  		},
   158  		{
   159  			name: "mixed peers",
   160  			sc: scheduler{
   161  				height: 1,
   162  				peers: map[types.NodeID]*scPeer{
   163  					"P1": {height: -1, state: peerStateNew},
   164  					"P2": {height: 10, state: peerStateReady},
   165  					"P3": {height: 20, state: peerStateRemoved},
   166  					"P4": {height: 22, state: peerStateReady},
   167  				},
   168  			},
   169  			wantMax: 22,
   170  		},
   171  	}
   172  
   173  	for _, tt := range tests {
   174  		tt := tt
   175  		t.Run(tt.name, func(t *testing.T) {
   176  			// maxHeight() should not mutate the scheduler
   177  			wantSc := tt.sc
   178  
   179  			resMax := tt.sc.maxHeight()
   180  			assert.Equal(t, tt.wantMax, resMax)
   181  			assert.Equal(t, wantSc, tt.sc)
   182  		})
   183  	}
   184  }
   185  
   186  func TestScEnsurePeer(t *testing.T) {
   187  
   188  	type args struct {
   189  		peerID types.NodeID
   190  	}
   191  	tests := []struct {
   192  		name       string
   193  		fields     scTestParams
   194  		args       args
   195  		wantFields scTestParams
   196  	}{
   197  		{
   198  			name:       "add first peer",
   199  			fields:     scTestParams{},
   200  			args:       args{peerID: "P1"},
   201  			wantFields: scTestParams{peers: map[string]*scPeer{"P1": {base: -1, height: -1, state: peerStateNew}}},
   202  		},
   203  		{
   204  			name:   "add second peer",
   205  			fields: scTestParams{peers: map[string]*scPeer{"P1": {base: -1, height: -1, state: peerStateNew}}},
   206  			args:   args{peerID: "P2"},
   207  			wantFields: scTestParams{peers: map[string]*scPeer{
   208  				"P1": {base: -1, height: -1, state: peerStateNew},
   209  				"P2": {base: -1, height: -1, state: peerStateNew}}},
   210  		},
   211  		{
   212  			name:       "add duplicate peer is fine",
   213  			fields:     scTestParams{peers: map[string]*scPeer{"P1": {height: -1}}},
   214  			args:       args{peerID: "P1"},
   215  			wantFields: scTestParams{peers: map[string]*scPeer{"P1": {height: -1}}},
   216  		},
   217  		{
   218  			name: "add duplicate peer with existing peer in Ready state is noop",
   219  			fields: scTestParams{
   220  				peers: map[string]*scPeer{"P1": {state: peerStateReady, height: 3}},
   221  				allB:  []int64{1, 2, 3},
   222  			},
   223  			args: args{peerID: "P1"},
   224  			wantFields: scTestParams{
   225  				peers: map[string]*scPeer{"P1": {state: peerStateReady, height: 3}},
   226  				allB:  []int64{1, 2, 3},
   227  			},
   228  		},
   229  	}
   230  
   231  	for _, tt := range tests {
   232  		tt := tt
   233  		t.Run(tt.name, func(t *testing.T) {
   234  			sc := newTestScheduler(tt.fields)
   235  			sc.ensurePeer(tt.args.peerID)
   236  			wantSc := newTestScheduler(tt.wantFields)
   237  			assert.Equal(t, wantSc, sc, "wanted peers %v, got %v", wantSc.peers, sc.peers)
   238  		})
   239  	}
   240  }
   241  
   242  func TestScTouchPeer(t *testing.T) {
   243  	now := time.Now()
   244  
   245  	type args struct {
   246  		peerID types.NodeID
   247  		time   time.Time
   248  	}
   249  
   250  	tests := []struct {
   251  		name       string
   252  		fields     scTestParams
   253  		args       args
   254  		wantFields scTestParams
   255  		wantErr    bool
   256  	}{
   257  		{
   258  			name: "attempt to touch non existing peer",
   259  			fields: scTestParams{
   260  				peers: map[string]*scPeer{"P1": {state: peerStateReady, height: 5}},
   261  				allB:  []int64{1, 2, 3, 4, 5},
   262  			},
   263  			args: args{peerID: "P2", time: now},
   264  			wantFields: scTestParams{peers: map[string]*scPeer{"P1": {state: peerStateReady, height: 5}},
   265  				allB: []int64{1, 2, 3, 4, 5},
   266  			},
   267  			wantErr: true,
   268  		},
   269  		{
   270  			name:       "attempt to touch peer in state New",
   271  			fields:     scTestParams{peers: map[string]*scPeer{"P1": {}}},
   272  			args:       args{peerID: "P1", time: now},
   273  			wantFields: scTestParams{peers: map[string]*scPeer{"P1": {}}},
   274  			wantErr:    true,
   275  		},
   276  		{
   277  			name:       "attempt to touch peer in state Removed",
   278  			fields:     scTestParams{peers: map[string]*scPeer{"P1": {state: peerStateRemoved}, "P2": {state: peerStateReady}}},
   279  			args:       args{peerID: "P1", time: now},
   280  			wantFields: scTestParams{peers: map[string]*scPeer{"P1": {state: peerStateRemoved}, "P2": {state: peerStateReady}}},
   281  			wantErr:    true,
   282  		},
   283  		{
   284  			name:   "touch peer in state Ready",
   285  			fields: scTestParams{peers: map[string]*scPeer{"P1": {state: peerStateReady, lastTouched: now}}},
   286  			args:   args{peerID: "P1", time: now.Add(3 * time.Second)},
   287  			wantFields: scTestParams{peers: map[string]*scPeer{
   288  				"P1": {state: peerStateReady, lastTouched: now.Add(3 * time.Second)}}},
   289  		},
   290  	}
   291  
   292  	for _, tt := range tests {
   293  		tt := tt
   294  		t.Run(tt.name, func(t *testing.T) {
   295  			sc := newTestScheduler(tt.fields)
   296  			if err := sc.touchPeer(tt.args.peerID, tt.args.time); (err != nil) != tt.wantErr {
   297  				t.Errorf("touchPeer() wantErr %v, error = %v", tt.wantErr, err)
   298  			}
   299  			wantSc := newTestScheduler(tt.wantFields)
   300  			assert.Equal(t, wantSc, sc, "wanted peers %v, got %v", wantSc.peers, sc.peers)
   301  		})
   302  	}
   303  }
   304  
   305  func TestScPrunablePeers(t *testing.T) {
   306  	now := time.Now()
   307  
   308  	type args struct {
   309  		threshold time.Duration
   310  		time      time.Time
   311  		minSpeed  int64
   312  	}
   313  
   314  	tests := []struct {
   315  		name       string
   316  		fields     scTestParams
   317  		args       args
   318  		wantResult []types.NodeID
   319  	}{
   320  		{
   321  			name:       "no peers",
   322  			fields:     scTestParams{peers: map[string]*scPeer{}},
   323  			args:       args{threshold: time.Second, time: now.Add(time.Second + time.Millisecond), minSpeed: 100},
   324  			wantResult: []types.NodeID{},
   325  		},
   326  		{
   327  			name: "mixed peers",
   328  			fields: scTestParams{peers: map[string]*scPeer{
   329  				// X - removed, active, fast
   330  				"P1": {state: peerStateRemoved, lastTouched: now.Add(time.Second), lastRate: 101},
   331  				// X - ready, active, fast
   332  				"P2": {state: peerStateReady, lastTouched: now.Add(time.Second), lastRate: 101},
   333  				// X - removed, active, equal
   334  				"P3": {state: peerStateRemoved, lastTouched: now.Add(time.Second), lastRate: 100},
   335  				// V - ready, inactive, equal
   336  				"P4": {state: peerStateReady, lastTouched: now, lastRate: 100},
   337  				// V - ready, inactive, slow
   338  				"P5": {state: peerStateReady, lastTouched: now, lastRate: 99},
   339  				//  V - ready, active, slow
   340  				"P6": {state: peerStateReady, lastTouched: now.Add(time.Second), lastRate: 90},
   341  			}},
   342  			args:       args{threshold: time.Second, time: now.Add(time.Second + time.Millisecond), minSpeed: 100},
   343  			wantResult: []types.NodeID{"P4", "P5", "P6"},
   344  		},
   345  	}
   346  
   347  	for _, tt := range tests {
   348  		tt := tt
   349  		t.Run(tt.name, func(t *testing.T) {
   350  			sc := newTestScheduler(tt.fields)
   351  			// peersSlowerThan should not mutate the scheduler
   352  			wantSc := sc
   353  			res := sc.prunablePeers(tt.args.threshold, tt.args.minSpeed, tt.args.time)
   354  			assert.Equal(t, tt.wantResult, res)
   355  			assert.Equal(t, wantSc, sc)
   356  		})
   357  	}
   358  }
   359  
   360  func TestScRemovePeer(t *testing.T) {
   361  
   362  	type args struct {
   363  		peerID types.NodeID
   364  	}
   365  	tests := []struct {
   366  		name       string
   367  		fields     scTestParams
   368  		args       args
   369  		wantFields scTestParams
   370  		wantErr    bool
   371  	}{
   372  		{
   373  			name:       "remove non existing peer",
   374  			fields:     scTestParams{peers: map[string]*scPeer{"P1": {height: -1}}},
   375  			args:       args{peerID: "P2"},
   376  			wantFields: scTestParams{peers: map[string]*scPeer{"P1": {height: -1}}},
   377  		},
   378  		{
   379  			name:       "remove single New peer",
   380  			fields:     scTestParams{peers: map[string]*scPeer{"P1": {height: -1}}},
   381  			args:       args{peerID: "P1"},
   382  			wantFields: scTestParams{peers: map[string]*scPeer{"P1": {height: -1, state: peerStateRemoved}}},
   383  		},
   384  		{
   385  			name:       "remove one of two New peers",
   386  			fields:     scTestParams{peers: map[string]*scPeer{"P1": {height: -1}, "P2": {height: -1}}},
   387  			args:       args{peerID: "P1"},
   388  			wantFields: scTestParams{peers: map[string]*scPeer{"P1": {height: -1, state: peerStateRemoved}, "P2": {height: -1}}},
   389  		},
   390  		{
   391  			name: "remove one Ready peer, all peers removed",
   392  			fields: scTestParams{
   393  				peers: map[string]*scPeer{
   394  					"P1": {height: 10, state: peerStateRemoved},
   395  					"P2": {height: 5, state: peerStateReady}},
   396  				allB: []int64{1, 2, 3, 4, 5},
   397  			},
   398  			args: args{peerID: "P2"},
   399  			wantFields: scTestParams{peers: map[string]*scPeer{
   400  				"P1": {height: 10, state: peerStateRemoved},
   401  				"P2": {height: 5, state: peerStateRemoved}},
   402  			},
   403  		},
   404  		{
   405  			name: "attempt to remove already removed peer",
   406  			fields: scTestParams{
   407  				height: 8,
   408  				peers: map[string]*scPeer{
   409  					"P1": {height: 10, state: peerStateRemoved},
   410  					"P2": {height: 11, state: peerStateReady}},
   411  				allB: []int64{8, 9, 10, 11},
   412  			},
   413  			args: args{peerID: "P1"},
   414  			wantFields: scTestParams{
   415  				height: 8,
   416  				peers: map[string]*scPeer{
   417  					"P1": {height: 10, state: peerStateRemoved},
   418  					"P2": {height: 11, state: peerStateReady}},
   419  				allB: []int64{8, 9, 10, 11}},
   420  		},
   421  		{
   422  			name: "remove Ready peer with blocks requested",
   423  			fields: scTestParams{
   424  				peers:   map[string]*scPeer{"P1": {height: 3, state: peerStateReady}},
   425  				allB:    []int64{1, 2, 3},
   426  				pending: map[int64]types.NodeID{1: "P1"},
   427  			},
   428  			args: args{peerID: "P1"},
   429  			wantFields: scTestParams{
   430  				peers:   map[string]*scPeer{"P1": {height: 3, state: peerStateRemoved}},
   431  				allB:    []int64{},
   432  				pending: map[int64]types.NodeID{},
   433  			},
   434  		},
   435  		{
   436  			name: "remove Ready peer with blocks received",
   437  			fields: scTestParams{
   438  				peers:    map[string]*scPeer{"P1": {height: 3, state: peerStateReady}},
   439  				allB:     []int64{1, 2, 3},
   440  				received: map[int64]types.NodeID{1: "P1"},
   441  			},
   442  			args: args{peerID: "P1"},
   443  			wantFields: scTestParams{
   444  				peers:    map[string]*scPeer{"P1": {height: 3, state: peerStateRemoved}},
   445  				allB:     []int64{},
   446  				received: map[int64]types.NodeID{},
   447  			},
   448  		},
   449  		{
   450  			name: "remove Ready peer with blocks received and requested (not yet received)",
   451  			fields: scTestParams{
   452  				peers:    map[string]*scPeer{"P1": {height: 4, state: peerStateReady}},
   453  				allB:     []int64{1, 2, 3, 4},
   454  				pending:  map[int64]types.NodeID{1: "P1", 3: "P1"},
   455  				received: map[int64]types.NodeID{2: "P1", 4: "P1"},
   456  			},
   457  			args: args{peerID: "P1"},
   458  			wantFields: scTestParams{
   459  				peers:    map[string]*scPeer{"P1": {height: 4, state: peerStateRemoved}},
   460  				allB:     []int64{},
   461  				pending:  map[int64]types.NodeID{},
   462  				received: map[int64]types.NodeID{},
   463  			},
   464  		},
   465  		{
   466  			name: "remove Ready peer from multiple peers set, with blocks received and requested (not yet received)",
   467  			fields: scTestParams{
   468  				peers: map[string]*scPeer{
   469  					"P1": {height: 6, state: peerStateReady},
   470  					"P2": {height: 6, state: peerStateReady},
   471  				},
   472  				allB:     []int64{1, 2, 3, 4, 5, 6},
   473  				pending:  map[int64]types.NodeID{1: "P1", 3: "P2", 6: "P1"},
   474  				received: map[int64]types.NodeID{2: "P1", 4: "P2", 5: "P2"},
   475  			},
   476  			args: args{peerID: "P1"},
   477  			wantFields: scTestParams{
   478  				peers: map[string]*scPeer{
   479  					"P1": {height: 6, state: peerStateRemoved},
   480  					"P2": {height: 6, state: peerStateReady},
   481  				},
   482  				allB:     []int64{1, 2, 3, 4, 5, 6},
   483  				pending:  map[int64]types.NodeID{3: "P2"},
   484  				received: map[int64]types.NodeID{4: "P2", 5: "P2"},
   485  			},
   486  		},
   487  	}
   488  
   489  	for _, tt := range tests {
   490  		tt := tt
   491  		t.Run(tt.name, func(t *testing.T) {
   492  			sc := newTestScheduler(tt.fields)
   493  			sc.removePeer(tt.args.peerID)
   494  			wantSc := newTestScheduler(tt.wantFields)
   495  			assert.Equal(t, wantSc, sc, "wanted peers %v, got %v", wantSc.peers, sc.peers)
   496  		})
   497  	}
   498  }
   499  
   500  func TestScSetPeerRange(t *testing.T) {
   501  
   502  	type args struct {
   503  		peerID types.NodeID
   504  		base   int64
   505  		height int64
   506  	}
   507  	tests := []struct {
   508  		name       string
   509  		fields     scTestParams
   510  		args       args
   511  		wantFields scTestParams
   512  		wantErr    bool
   513  	}{
   514  		{
   515  			name: "change height of non existing peer",
   516  			fields: scTestParams{
   517  				peers: map[string]*scPeer{"P1": {height: 2, state: peerStateReady}},
   518  				allB:  []int64{1, 2}},
   519  			args: args{peerID: "P2", height: 4},
   520  			wantFields: scTestParams{
   521  				peers: map[string]*scPeer{
   522  					"P1": {height: 2, state: peerStateReady},
   523  					"P2": {height: 4, state: peerStateReady},
   524  				},
   525  				allB: []int64{1, 2, 3, 4}},
   526  		},
   527  		{
   528  			name: "increase height of removed peer",
   529  			fields: scTestParams{
   530  				peers: map[string]*scPeer{"P1": {height: 2, state: peerStateRemoved}}},
   531  			args:       args{peerID: "P1", height: 4},
   532  			wantFields: scTestParams{peers: map[string]*scPeer{"P1": {height: 2, state: peerStateRemoved}}},
   533  		},
   534  		{
   535  			name: "decrease height of single peer",
   536  			fields: scTestParams{
   537  				peers: map[string]*scPeer{"P1": {height: 4, state: peerStateReady}},
   538  				allB:  []int64{1, 2, 3, 4}},
   539  			args: args{peerID: "P1", height: 2},
   540  			wantFields: scTestParams{
   541  				peers: map[string]*scPeer{"P1": {height: 4, state: peerStateRemoved}},
   542  				allB:  []int64{}},
   543  			wantErr: true,
   544  		},
   545  		{
   546  			name: "increase height of single peer",
   547  			fields: scTestParams{
   548  				peers: map[string]*scPeer{"P1": {height: 2, state: peerStateReady}},
   549  				allB:  []int64{1, 2}},
   550  			args: args{peerID: "P1", height: 4},
   551  			wantFields: scTestParams{
   552  				peers: map[string]*scPeer{"P1": {height: 4, state: peerStateReady}},
   553  				allB:  []int64{1, 2, 3, 4}},
   554  		},
   555  		{
   556  			name: "noop height change of single peer",
   557  			fields: scTestParams{
   558  				peers: map[string]*scPeer{"P1": {height: 4, state: peerStateReady}},
   559  				allB:  []int64{1, 2, 3, 4}},
   560  			args: args{peerID: "P1", height: 4},
   561  			wantFields: scTestParams{
   562  				peers: map[string]*scPeer{"P1": {height: 4, state: peerStateReady}},
   563  				allB:  []int64{1, 2, 3, 4}},
   564  		},
   565  		{
   566  			name: "add peer with huge height 10**10 ",
   567  			fields: scTestParams{
   568  				peers:         map[string]*scPeer{"P2": {height: -1, state: peerStateNew}},
   569  				targetPending: 4,
   570  			},
   571  			args: args{peerID: "P2", height: 10000000000},
   572  			wantFields: scTestParams{
   573  				targetPending: 4,
   574  				peers:         map[string]*scPeer{"P2": {height: 10000000000, state: peerStateReady}},
   575  				allB:          []int64{1, 2, 3, 4}},
   576  		},
   577  		{
   578  			name: "add peer with base > height should error",
   579  			fields: scTestParams{
   580  				peers: map[string]*scPeer{"P1": {height: 4, state: peerStateReady}},
   581  				allB:  []int64{1, 2, 3, 4}},
   582  			args: args{peerID: "P1", base: 6, height: 5},
   583  			wantFields: scTestParams{
   584  				peers: map[string]*scPeer{"P1": {height: 4, state: peerStateRemoved}}},
   585  			wantErr: true,
   586  		},
   587  		{
   588  			name: "add peer with base == height is fine",
   589  			fields: scTestParams{
   590  				peers:         map[string]*scPeer{"P1": {height: 4, state: peerStateNew}},
   591  				targetPending: 4,
   592  			},
   593  			args: args{peerID: "P1", base: 6, height: 6},
   594  			wantFields: scTestParams{
   595  				targetPending: 4,
   596  				peers:         map[string]*scPeer{"P1": {base: 6, height: 6, state: peerStateReady}},
   597  				allB:          []int64{1, 2, 3, 4}},
   598  		},
   599  	}
   600  
   601  	for _, tt := range tests {
   602  		tt := tt
   603  		t.Run(tt.name, func(t *testing.T) {
   604  			sc := newTestScheduler(tt.fields)
   605  			err := sc.setPeerRange(tt.args.peerID, tt.args.base, tt.args.height)
   606  			if (err != nil) != tt.wantErr {
   607  				t.Errorf("setPeerHeight() wantErr %v, error = %v", tt.wantErr, err)
   608  			}
   609  			wantSc := newTestScheduler(tt.wantFields)
   610  			assert.Equal(t, wantSc, sc, "wanted peers %v, got %v", wantSc.peers, sc.peers)
   611  		})
   612  	}
   613  }
   614  
   615  func TestScGetPeersWithHeight(t *testing.T) {
   616  
   617  	type args struct {
   618  		height int64
   619  	}
   620  	tests := []struct {
   621  		name       string
   622  		fields     scTestParams
   623  		args       args
   624  		wantResult []types.NodeID
   625  	}{
   626  		{
   627  			name:       "no peers",
   628  			fields:     scTestParams{peers: map[string]*scPeer{}},
   629  			args:       args{height: 10},
   630  			wantResult: []types.NodeID{},
   631  		},
   632  		{
   633  			name:       "only new peers",
   634  			fields:     scTestParams{peers: map[string]*scPeer{"P1": {height: -1, state: peerStateNew}}},
   635  			args:       args{height: 10},
   636  			wantResult: []types.NodeID{},
   637  		},
   638  		{
   639  			name:       "only Removed peers",
   640  			fields:     scTestParams{peers: map[string]*scPeer{"P1": {height: 4, state: peerStateRemoved}}},
   641  			args:       args{height: 2},
   642  			wantResult: []types.NodeID{},
   643  		},
   644  		{
   645  			name: "one Ready shorter peer",
   646  			fields: scTestParams{
   647  				peers: map[string]*scPeer{"P1": {height: 4, state: peerStateReady}},
   648  				allB:  []int64{1, 2, 3, 4},
   649  			},
   650  			args:       args{height: 5},
   651  			wantResult: []types.NodeID{},
   652  		},
   653  		{
   654  			name: "one Ready equal peer",
   655  			fields: scTestParams{
   656  				peers: map[string]*scPeer{"P1": {height: 4, state: peerStateReady}},
   657  				allB:  []int64{1, 2, 3, 4},
   658  			},
   659  			args:       args{height: 4},
   660  			wantResult: []types.NodeID{"P1"},
   661  		},
   662  		{
   663  			name: "one Ready higher peer",
   664  			fields: scTestParams{
   665  				targetPending: 4,
   666  				peers:         map[string]*scPeer{"P1": {height: 20, state: peerStateReady}},
   667  				allB:          []int64{1, 2, 3, 4},
   668  			},
   669  			args:       args{height: 4},
   670  			wantResult: []types.NodeID{"P1"},
   671  		},
   672  		{
   673  			name: "one Ready higher peer at base",
   674  			fields: scTestParams{
   675  				targetPending: 4,
   676  				peers:         map[string]*scPeer{"P1": {base: 4, height: 20, state: peerStateReady}},
   677  				allB:          []int64{1, 2, 3, 4},
   678  			},
   679  			args:       args{height: 4},
   680  			wantResult: []types.NodeID{"P1"},
   681  		},
   682  		{
   683  			name: "one Ready higher peer with higher base",
   684  			fields: scTestParams{
   685  				targetPending: 4,
   686  				peers:         map[string]*scPeer{"P1": {base: 10, height: 20, state: peerStateReady}},
   687  				allB:          []int64{1, 2, 3, 4},
   688  			},
   689  			args:       args{height: 4},
   690  			wantResult: []types.NodeID{},
   691  		},
   692  		{
   693  			name: "multiple mixed peers",
   694  			fields: scTestParams{
   695  				height: 8,
   696  				peers: map[string]*scPeer{
   697  					"P1": {height: -1, state: peerStateNew},
   698  					"P2": {height: 10, state: peerStateReady},
   699  					"P3": {height: 5, state: peerStateReady},
   700  					"P4": {height: 20, state: peerStateRemoved},
   701  					"P5": {height: 11, state: peerStateReady}},
   702  				allB: []int64{8, 9, 10, 11},
   703  			},
   704  			args:       args{height: 8},
   705  			wantResult: []types.NodeID{"P2", "P5"},
   706  		},
   707  	}
   708  
   709  	for _, tt := range tests {
   710  		tt := tt
   711  		t.Run(tt.name, func(t *testing.T) {
   712  			sc := newTestScheduler(tt.fields)
   713  			// getPeersWithHeight should not mutate the scheduler
   714  			wantSc := sc
   715  			res := sc.getPeersWithHeight(tt.args.height)
   716  			sort.Sort(PeerByID(res))
   717  			assert.Equal(t, tt.wantResult, res)
   718  			assert.Equal(t, wantSc, sc)
   719  		})
   720  	}
   721  }
   722  
   723  func TestScMarkPending(t *testing.T) {
   724  	now := time.Now()
   725  
   726  	type args struct {
   727  		peerID types.NodeID
   728  		height int64
   729  		tm     time.Time
   730  	}
   731  	tests := []struct {
   732  		name       string
   733  		fields     scTestParams
   734  		args       args
   735  		wantFields scTestParams
   736  		wantErr    bool
   737  	}{
   738  		{
   739  			name: "attempt mark pending an unknown block above height",
   740  			fields: scTestParams{
   741  				peers: map[string]*scPeer{"P1": {height: 2, state: peerStateReady}},
   742  				allB:  []int64{1, 2}},
   743  			args: args{peerID: "P1", height: 3, tm: now},
   744  			wantFields: scTestParams{
   745  				peers: map[string]*scPeer{"P1": {height: 2, state: peerStateReady}},
   746  				allB:  []int64{1, 2}},
   747  			wantErr: true,
   748  		},
   749  		{
   750  			name: "attempt mark pending an unknown block below base",
   751  			fields: scTestParams{
   752  				peers: map[string]*scPeer{"P1": {base: 4, height: 6, state: peerStateReady}},
   753  				allB:  []int64{1, 2, 3, 4, 5, 6}},
   754  			args: args{peerID: "P1", height: 3, tm: now},
   755  			wantFields: scTestParams{
   756  				peers: map[string]*scPeer{"P1": {base: 4, height: 6, state: peerStateReady}},
   757  				allB:  []int64{1, 2, 3, 4, 5, 6}},
   758  			wantErr: true,
   759  		},
   760  		{
   761  			name: "attempt mark pending from non existing peer",
   762  			fields: scTestParams{
   763  				peers: map[string]*scPeer{"P1": {height: 2, state: peerStateReady}},
   764  				allB:  []int64{1, 2}},
   765  			args: args{peerID: "P2", height: 1, tm: now},
   766  			wantFields: scTestParams{
   767  				peers: map[string]*scPeer{"P1": {height: 2, state: peerStateReady}},
   768  				allB:  []int64{1, 2}},
   769  			wantErr: true,
   770  		},
   771  		{
   772  			name: "mark pending from Removed peer",
   773  			fields: scTestParams{
   774  				peers: map[string]*scPeer{"P1": {height: 2, state: peerStateRemoved}}},
   775  			args: args{peerID: "P1", height: 1, tm: now},
   776  			wantFields: scTestParams{
   777  				peers: map[string]*scPeer{"P1": {height: 2, state: peerStateRemoved}}},
   778  			wantErr: true,
   779  		},
   780  		{
   781  			name: "mark pending from New peer",
   782  			fields: scTestParams{
   783  				peers: map[string]*scPeer{
   784  					"P1": {height: 4, state: peerStateReady},
   785  					"P2": {height: 4, state: peerStateNew},
   786  				},
   787  				allB: []int64{1, 2, 3, 4},
   788  			},
   789  			args: args{peerID: "P2", height: 2, tm: now},
   790  			wantFields: scTestParams{
   791  				peers: map[string]*scPeer{
   792  					"P1": {height: 4, state: peerStateReady},
   793  					"P2": {height: 4, state: peerStateNew},
   794  				},
   795  				allB: []int64{1, 2, 3, 4},
   796  			},
   797  			wantErr: true,
   798  		},
   799  		{
   800  			name: "mark pending from short peer",
   801  			fields: scTestParams{
   802  				peers: map[string]*scPeer{
   803  					"P1": {height: 4, state: peerStateReady},
   804  					"P2": {height: 2, state: peerStateReady},
   805  				},
   806  				allB: []int64{1, 2, 3, 4},
   807  			},
   808  			args: args{peerID: "P2", height: 3, tm: now},
   809  			wantFields: scTestParams{
   810  				peers: map[string]*scPeer{
   811  					"P1": {height: 4, state: peerStateReady},
   812  					"P2": {height: 2, state: peerStateReady},
   813  				},
   814  				allB: []int64{1, 2, 3, 4},
   815  			},
   816  			wantErr: true,
   817  		},
   818  		{
   819  			name: "mark pending all good",
   820  			fields: scTestParams{
   821  				peers:       map[string]*scPeer{"P1": {height: 2, state: peerStateReady}},
   822  				allB:        []int64{1, 2},
   823  				pending:     map[int64]types.NodeID{1: "P1"},
   824  				pendingTime: map[int64]time.Time{1: now},
   825  			},
   826  			args: args{peerID: "P1", height: 2, tm: now.Add(time.Millisecond)},
   827  			wantFields: scTestParams{
   828  				peers:       map[string]*scPeer{"P1": {height: 2, state: peerStateReady}},
   829  				allB:        []int64{1, 2},
   830  				pending:     map[int64]types.NodeID{1: "P1", 2: "P1"},
   831  				pendingTime: map[int64]time.Time{1: now, 2: now.Add(time.Millisecond)},
   832  			},
   833  		},
   834  	}
   835  
   836  	for _, tt := range tests {
   837  		tt := tt
   838  		t.Run(tt.name, func(t *testing.T) {
   839  			sc := newTestScheduler(tt.fields)
   840  			if err := sc.markPending(tt.args.peerID, tt.args.height, tt.args.tm); (err != nil) != tt.wantErr {
   841  				t.Errorf("markPending() wantErr %v, error = %v", tt.wantErr, err)
   842  			}
   843  			wantSc := newTestScheduler(tt.wantFields)
   844  			assert.Equal(t, wantSc, sc)
   845  		})
   846  	}
   847  }
   848  
   849  func TestScMarkReceived(t *testing.T) {
   850  	now := time.Now()
   851  
   852  	type args struct {
   853  		peerID types.NodeID
   854  		height int64
   855  		size   int64
   856  		tm     time.Time
   857  	}
   858  	tests := []struct {
   859  		name       string
   860  		fields     scTestParams
   861  		args       args
   862  		wantFields scTestParams
   863  		wantErr    bool
   864  	}{
   865  		{
   866  			name: "received from non existing peer",
   867  			fields: scTestParams{
   868  				peers: map[string]*scPeer{"P1": {height: 2, state: peerStateReady}},
   869  				allB:  []int64{1, 2}},
   870  			args: args{peerID: "P2", height: 1, size: 1000, tm: now},
   871  			wantFields: scTestParams{
   872  				peers: map[string]*scPeer{"P1": {height: 2, state: peerStateReady}},
   873  				allB:  []int64{1, 2}},
   874  			wantErr: true,
   875  		},
   876  		{
   877  			name: "received from removed peer",
   878  			fields: scTestParams{
   879  				peers: map[string]*scPeer{"P1": {height: 2, state: peerStateRemoved}}},
   880  			args: args{peerID: "P1", height: 1, size: 1000, tm: now},
   881  			wantFields: scTestParams{
   882  				peers: map[string]*scPeer{"P1": {height: 2, state: peerStateRemoved}}},
   883  			wantErr: true,
   884  		},
   885  		{
   886  			name: "received from unsolicited peer",
   887  			fields: scTestParams{
   888  				peers: map[string]*scPeer{
   889  					"P1": {height: 4, state: peerStateReady},
   890  					"P2": {height: 4, state: peerStateReady},
   891  				},
   892  				allB:    []int64{1, 2, 3, 4},
   893  				pending: map[int64]types.NodeID{1: "P1", 2: "P2", 3: "P2", 4: "P1"},
   894  			},
   895  			args: args{peerID: "P1", height: 2, size: 1000, tm: now},
   896  			wantFields: scTestParams{
   897  				peers: map[string]*scPeer{
   898  					"P1": {height: 4, state: peerStateReady},
   899  					"P2": {height: 4, state: peerStateReady},
   900  				},
   901  				allB:    []int64{1, 2, 3, 4},
   902  				pending: map[int64]types.NodeID{1: "P1", 2: "P2", 3: "P2", 4: "P1"},
   903  			},
   904  			wantErr: true,
   905  		},
   906  		{
   907  			name: "received but blockRequest not sent",
   908  			fields: scTestParams{
   909  				peers:   map[string]*scPeer{"P1": {height: 4, state: peerStateReady}},
   910  				allB:    []int64{1, 2, 3, 4},
   911  				pending: map[int64]types.NodeID{},
   912  			},
   913  			args: args{peerID: "P1", height: 2, size: 1000, tm: now},
   914  			wantFields: scTestParams{
   915  				peers:   map[string]*scPeer{"P1": {height: 4, state: peerStateReady}},
   916  				allB:    []int64{1, 2, 3, 4},
   917  				pending: map[int64]types.NodeID{},
   918  			},
   919  			wantErr: true,
   920  		},
   921  		{
   922  			name: "received with bad timestamp",
   923  			fields: scTestParams{
   924  				peers:       map[string]*scPeer{"P1": {height: 2, state: peerStateReady}},
   925  				allB:        []int64{1, 2},
   926  				pending:     map[int64]types.NodeID{1: "P1", 2: "P1"},
   927  				pendingTime: map[int64]time.Time{1: now, 2: now.Add(time.Second)},
   928  			},
   929  			args: args{peerID: "P1", height: 2, size: 1000, tm: now},
   930  			wantFields: scTestParams{
   931  				peers:       map[string]*scPeer{"P1": {height: 2, state: peerStateReady}},
   932  				allB:        []int64{1, 2},
   933  				pending:     map[int64]types.NodeID{1: "P1", 2: "P1"},
   934  				pendingTime: map[int64]time.Time{1: now, 2: now.Add(time.Second)},
   935  			},
   936  			wantErr: true,
   937  		},
   938  		{
   939  			name: "received all good",
   940  			fields: scTestParams{
   941  				peers:       map[string]*scPeer{"P1": {height: 2, state: peerStateReady}},
   942  				allB:        []int64{1, 2},
   943  				pending:     map[int64]types.NodeID{1: "P1", 2: "P1"},
   944  				pendingTime: map[int64]time.Time{1: now, 2: now},
   945  			},
   946  			args: args{peerID: "P1", height: 2, size: 1000, tm: now.Add(time.Millisecond)},
   947  			wantFields: scTestParams{
   948  				peers:       map[string]*scPeer{"P1": {height: 2, state: peerStateReady}},
   949  				allB:        []int64{1, 2},
   950  				pending:     map[int64]types.NodeID{1: "P1"},
   951  				pendingTime: map[int64]time.Time{1: now},
   952  				received:    map[int64]types.NodeID{2: "P1"},
   953  			},
   954  		},
   955  	}
   956  
   957  	for _, tt := range tests {
   958  		tt := tt
   959  		t.Run(tt.name, func(t *testing.T) {
   960  			sc := newTestScheduler(tt.fields)
   961  			if err := sc.markReceived(
   962  				tt.args.peerID,
   963  				tt.args.height,
   964  				tt.args.size,
   965  				now.Add(time.Second)); (err != nil) != tt.wantErr {
   966  				t.Errorf("markReceived() wantErr %v, error = %v", tt.wantErr, err)
   967  			}
   968  			wantSc := newTestScheduler(tt.wantFields)
   969  			assert.Equal(t, wantSc, sc)
   970  		})
   971  	}
   972  }
   973  
   974  func TestScMarkProcessed(t *testing.T) {
   975  	now := time.Now()
   976  
   977  	type args struct {
   978  		height int64
   979  	}
   980  	tests := []struct {
   981  		name       string
   982  		fields     scTestParams
   983  		args       args
   984  		wantFields scTestParams
   985  		wantErr    bool
   986  	}{
   987  		{
   988  			name: "processed an unreceived block",
   989  			fields: scTestParams{
   990  				height:        2,
   991  				peers:         map[string]*scPeer{"P1": {height: 4, state: peerStateReady}},
   992  				allB:          []int64{2},
   993  				pending:       map[int64]types.NodeID{2: "P1"},
   994  				pendingTime:   map[int64]time.Time{2: now},
   995  				targetPending: 1,
   996  			},
   997  			args: args{height: 2},
   998  			wantFields: scTestParams{
   999  				height:        3,
  1000  				peers:         map[string]*scPeer{"P1": {height: 4, state: peerStateReady}},
  1001  				allB:          []int64{3},
  1002  				targetPending: 1,
  1003  			},
  1004  		},
  1005  		{
  1006  			name: "mark processed success",
  1007  			fields: scTestParams{
  1008  				height:      1,
  1009  				peers:       map[string]*scPeer{"P1": {height: 2, state: peerStateReady}},
  1010  				allB:        []int64{1, 2},
  1011  				pending:     map[int64]types.NodeID{2: "P1"},
  1012  				pendingTime: map[int64]time.Time{2: now},
  1013  				received:    map[int64]types.NodeID{1: "P1"}},
  1014  			args: args{height: 1},
  1015  			wantFields: scTestParams{
  1016  				height:      2,
  1017  				peers:       map[string]*scPeer{"P1": {height: 2, state: peerStateReady}},
  1018  				allB:        []int64{2},
  1019  				pending:     map[int64]types.NodeID{2: "P1"},
  1020  				pendingTime: map[int64]time.Time{2: now}},
  1021  		},
  1022  	}
  1023  
  1024  	for _, tt := range tests {
  1025  		tt := tt
  1026  		t.Run(tt.name, func(t *testing.T) {
  1027  			sc := newTestScheduler(tt.fields)
  1028  			oldBlockState := sc.getStateAtHeight(tt.args.height)
  1029  			if err := sc.markProcessed(tt.args.height); (err != nil) != tt.wantErr {
  1030  				t.Errorf("markProcessed() wantErr %v, error = %v", tt.wantErr, err)
  1031  			}
  1032  			if tt.wantErr {
  1033  				assert.Equal(t, oldBlockState, sc.getStateAtHeight(tt.args.height))
  1034  			} else {
  1035  				assert.Equal(t, blockStateProcessed, sc.getStateAtHeight(tt.args.height))
  1036  			}
  1037  			wantSc := newTestScheduler(tt.wantFields)
  1038  			checkSameScheduler(t, wantSc, sc)
  1039  		})
  1040  	}
  1041  }
  1042  
  1043  func TestScResetState(t *testing.T) {
  1044  	tests := []struct {
  1045  		name       string
  1046  		fields     scTestParams
  1047  		state      state.State
  1048  		wantFields scTestParams
  1049  	}{
  1050  		{
  1051  			name: "updates height and initHeight",
  1052  			fields: scTestParams{
  1053  				height:     0,
  1054  				initHeight: 0,
  1055  			},
  1056  			state: state.State{LastBlockHeight: 7},
  1057  			wantFields: scTestParams{
  1058  				height:     8,
  1059  				initHeight: 8,
  1060  			},
  1061  		},
  1062  	}
  1063  
  1064  	for _, tt := range tests {
  1065  		tt := tt
  1066  		t.Run(tt.name, func(t *testing.T) {
  1067  			sc := newTestScheduler(tt.fields)
  1068  			e, err := sc.handleResetState(bcResetState{state: tt.state})
  1069  			require.NoError(t, err)
  1070  			assert.Equal(t, e, noOp)
  1071  			wantSc := newTestScheduler(tt.wantFields)
  1072  			checkSameScheduler(t, wantSc, sc)
  1073  		})
  1074  	}
  1075  }
  1076  
  1077  func TestScAllBlocksProcessed(t *testing.T) {
  1078  	now := time.Now()
  1079  
  1080  	tests := []struct {
  1081  		name       string
  1082  		fields     scTestParams
  1083  		wantResult bool
  1084  	}{
  1085  		{
  1086  			name:       "no blocks, no peers",
  1087  			fields:     scTestParams{},
  1088  			wantResult: false,
  1089  		},
  1090  		{
  1091  			name: "only New blocks",
  1092  			fields: scTestParams{
  1093  				peers: map[string]*scPeer{"P1": {height: 4, state: peerStateReady}},
  1094  				allB:  []int64{1, 2, 3, 4},
  1095  			},
  1096  			wantResult: false,
  1097  		},
  1098  		{
  1099  			name: "only Pending blocks",
  1100  			fields: scTestParams{
  1101  				peers:       map[string]*scPeer{"P1": {height: 4, state: peerStateReady}},
  1102  				allB:        []int64{1, 2, 3, 4},
  1103  				pending:     map[int64]types.NodeID{1: "P1", 2: "P1", 3: "P1", 4: "P1"},
  1104  				pendingTime: map[int64]time.Time{1: now, 2: now, 3: now, 4: now},
  1105  			},
  1106  			wantResult: false,
  1107  		},
  1108  		{
  1109  			name: "only Received blocks",
  1110  			fields: scTestParams{
  1111  				peers:    map[string]*scPeer{"P1": {height: 4, state: peerStateReady}},
  1112  				allB:     []int64{1, 2, 3, 4},
  1113  				received: map[int64]types.NodeID{1: "P1", 2: "P1", 3: "P1", 4: "P1"},
  1114  			},
  1115  			wantResult: false,
  1116  		},
  1117  		{
  1118  			name: "only Processed blocks plus highest is received",
  1119  			fields: scTestParams{
  1120  				height: 4,
  1121  				peers: map[string]*scPeer{
  1122  					"P1": {height: 4, state: peerStateReady}},
  1123  				allB:     []int64{4},
  1124  				received: map[int64]types.NodeID{4: "P1"},
  1125  			},
  1126  			wantResult: true,
  1127  		},
  1128  		{
  1129  			name: "mixed block states",
  1130  			fields: scTestParams{
  1131  				peers:       map[string]*scPeer{"P1": {height: 4, state: peerStateReady}},
  1132  				allB:        []int64{1, 2, 3, 4},
  1133  				pending:     map[int64]types.NodeID{2: "P1", 4: "P1"},
  1134  				pendingTime: map[int64]time.Time{2: now, 4: now},
  1135  			},
  1136  			wantResult: false,
  1137  		},
  1138  	}
  1139  
  1140  	for _, tt := range tests {
  1141  		tt := tt
  1142  		t.Run(tt.name, func(t *testing.T) {
  1143  			sc := newTestScheduler(tt.fields)
  1144  			// allBlocksProcessed() should not mutate the scheduler
  1145  			wantSc := sc
  1146  			res := sc.allBlocksProcessed()
  1147  			assert.Equal(t, tt.wantResult, res)
  1148  			checkSameScheduler(t, wantSc, sc)
  1149  		})
  1150  	}
  1151  }
  1152  
  1153  func TestScNextHeightToSchedule(t *testing.T) {
  1154  	now := time.Now()
  1155  
  1156  	tests := []struct {
  1157  		name       string
  1158  		fields     scTestParams
  1159  		wantHeight int64
  1160  	}{
  1161  		{
  1162  			name:       "no blocks",
  1163  			fields:     scTestParams{initHeight: 11, height: 11},
  1164  			wantHeight: -1,
  1165  		},
  1166  		{
  1167  			name: "only New blocks",
  1168  			fields: scTestParams{
  1169  				initHeight: 3,
  1170  				peers:      map[string]*scPeer{"P1": {height: 6, state: peerStateReady}},
  1171  				allB:       []int64{3, 4, 5, 6},
  1172  			},
  1173  			wantHeight: 3,
  1174  		},
  1175  		{
  1176  			name: "only Pending blocks",
  1177  			fields: scTestParams{
  1178  				initHeight:  1,
  1179  				peers:       map[string]*scPeer{"P1": {height: 4, state: peerStateReady}},
  1180  				allB:        []int64{1, 2, 3, 4},
  1181  				pending:     map[int64]types.NodeID{1: "P1", 2: "P1", 3: "P1", 4: "P1"},
  1182  				pendingTime: map[int64]time.Time{1: now, 2: now, 3: now, 4: now},
  1183  			},
  1184  			wantHeight: -1,
  1185  		},
  1186  		{
  1187  			name: "only Received blocks",
  1188  			fields: scTestParams{
  1189  				initHeight: 1,
  1190  				peers:      map[string]*scPeer{"P1": {height: 4, state: peerStateReady}},
  1191  				allB:       []int64{1, 2, 3, 4},
  1192  				received:   map[int64]types.NodeID{1: "P1", 2: "P1", 3: "P1", 4: "P1"},
  1193  			},
  1194  			wantHeight: -1,
  1195  		},
  1196  		{
  1197  			name: "only Processed blocks",
  1198  			fields: scTestParams{
  1199  				initHeight: 1,
  1200  				peers:      map[string]*scPeer{"P1": {height: 4, state: peerStateReady}},
  1201  				allB:       []int64{1, 2, 3, 4},
  1202  			},
  1203  			wantHeight: 1,
  1204  		},
  1205  		{
  1206  			name: "mixed block states",
  1207  			fields: scTestParams{
  1208  				initHeight:  1,
  1209  				peers:       map[string]*scPeer{"P1": {height: 4, state: peerStateReady}},
  1210  				allB:        []int64{1, 2, 3, 4},
  1211  				pending:     map[int64]types.NodeID{2: "P1"},
  1212  				pendingTime: map[int64]time.Time{2: now},
  1213  			},
  1214  			wantHeight: 1,
  1215  		},
  1216  	}
  1217  
  1218  	for _, tt := range tests {
  1219  		tt := tt
  1220  		t.Run(tt.name, func(t *testing.T) {
  1221  			sc := newTestScheduler(tt.fields)
  1222  			// nextHeightToSchedule() should not mutate the scheduler
  1223  			wantSc := sc
  1224  
  1225  			resMin := sc.nextHeightToSchedule()
  1226  			assert.Equal(t, tt.wantHeight, resMin)
  1227  			checkSameScheduler(t, wantSc, sc)
  1228  		})
  1229  	}
  1230  }
  1231  
  1232  func TestScSelectPeer(t *testing.T) {
  1233  
  1234  	type args struct {
  1235  		height int64
  1236  	}
  1237  	tests := []struct {
  1238  		name       string
  1239  		fields     scTestParams
  1240  		args       args
  1241  		wantResult types.NodeID
  1242  		wantError  bool
  1243  	}{
  1244  		{
  1245  			name:       "no peers",
  1246  			fields:     scTestParams{peers: map[string]*scPeer{}},
  1247  			args:       args{height: 10},
  1248  			wantResult: "",
  1249  			wantError:  true,
  1250  		},
  1251  		{
  1252  			name:       "only new peers",
  1253  			fields:     scTestParams{peers: map[string]*scPeer{"P1": {height: -1, state: peerStateNew}}},
  1254  			args:       args{height: 10},
  1255  			wantResult: "",
  1256  			wantError:  true,
  1257  		},
  1258  		{
  1259  			name:       "only Removed peers",
  1260  			fields:     scTestParams{peers: map[string]*scPeer{"P1": {height: 4, state: peerStateRemoved}}},
  1261  			args:       args{height: 2},
  1262  			wantResult: "",
  1263  			wantError:  true,
  1264  		},
  1265  		{
  1266  			name: "one Ready shorter peer",
  1267  			fields: scTestParams{
  1268  				peers: map[string]*scPeer{"P1": {height: 4, state: peerStateReady}},
  1269  				allB:  []int64{1, 2, 3, 4},
  1270  			},
  1271  			args:       args{height: 5},
  1272  			wantResult: "",
  1273  			wantError:  true,
  1274  		},
  1275  		{
  1276  			name: "one Ready equal peer",
  1277  			fields: scTestParams{peers: map[string]*scPeer{"P1": {height: 4, state: peerStateReady}},
  1278  				allB: []int64{1, 2, 3, 4},
  1279  			},
  1280  			args:       args{height: 4},
  1281  			wantResult: "P1",
  1282  		},
  1283  		{
  1284  			name: "one Ready higher peer",
  1285  			fields: scTestParams{peers: map[string]*scPeer{"P1": {height: 6, state: peerStateReady}},
  1286  				allB: []int64{1, 2, 3, 4, 5, 6},
  1287  			},
  1288  			args:       args{height: 4},
  1289  			wantResult: "P1",
  1290  		},
  1291  		{
  1292  			name: "one Ready higher peer with higher base",
  1293  			fields: scTestParams{
  1294  				peers: map[string]*scPeer{"P1": {base: 4, height: 6, state: peerStateReady}},
  1295  				allB:  []int64{1, 2, 3, 4, 5, 6},
  1296  			},
  1297  			args:       args{height: 3},
  1298  			wantResult: "",
  1299  			wantError:  true,
  1300  		},
  1301  		{
  1302  			name: "many Ready higher peers with different number of pending requests",
  1303  			fields: scTestParams{
  1304  				height: 4,
  1305  				peers: map[string]*scPeer{
  1306  					"P1": {height: 8, state: peerStateReady},
  1307  					"P2": {height: 9, state: peerStateReady}},
  1308  				allB: []int64{4, 5, 6, 7, 8, 9},
  1309  				pending: map[int64]types.NodeID{
  1310  					4: "P1", 6: "P1",
  1311  					5: "P2",
  1312  				},
  1313  			},
  1314  			args:       args{height: 4},
  1315  			wantResult: "P2",
  1316  		},
  1317  		{
  1318  			name: "many Ready higher peers with same number of pending requests",
  1319  			fields: scTestParams{
  1320  				peers: map[string]*scPeer{
  1321  					"P2": {height: 20, state: peerStateReady},
  1322  					"P1": {height: 15, state: peerStateReady},
  1323  					"P3": {height: 15, state: peerStateReady}},
  1324  				allB: []int64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
  1325  				pending: map[int64]types.NodeID{
  1326  					1: "P1", 2: "P1",
  1327  					3: "P3", 4: "P3",
  1328  					5: "P2", 6: "P2",
  1329  				},
  1330  			},
  1331  			args:       args{height: 7},
  1332  			wantResult: "P1",
  1333  		},
  1334  	}
  1335  
  1336  	for _, tt := range tests {
  1337  		tt := tt
  1338  		t.Run(tt.name, func(t *testing.T) {
  1339  			sc := newTestScheduler(tt.fields)
  1340  			// selectPeer should not mutate the scheduler
  1341  			wantSc := sc
  1342  			res, err := sc.selectPeer(tt.args.height)
  1343  			assert.Equal(t, tt.wantResult, res)
  1344  			assert.Equal(t, tt.wantError, err != nil)
  1345  			checkSameScheduler(t, wantSc, sc)
  1346  		})
  1347  	}
  1348  }
  1349  
  1350  // makeScBlock makes an empty block.
  1351  func makeScBlock(height int64) *types.Block {
  1352  	return &types.Block{Header: types.Header{Height: height}}
  1353  }
  1354  
  1355  // used in place of assert.Equal(t, want, actual) to avoid failures due to
  1356  // scheduler.lastAdvanced timestamp inequalities.
  1357  func checkSameScheduler(t *testing.T, want *scheduler, actual *scheduler) {
  1358  	assert.Equal(t, want.initHeight, actual.initHeight)
  1359  	assert.Equal(t, want.height, actual.height)
  1360  	assert.Equal(t, want.peers, actual.peers)
  1361  	assert.Equal(t, want.blockStates, actual.blockStates)
  1362  	assert.Equal(t, want.pendingBlocks, actual.pendingBlocks)
  1363  	assert.Equal(t, want.pendingTime, actual.pendingTime)
  1364  	assert.Equal(t, want.blockStates, actual.blockStates)
  1365  	assert.Equal(t, want.receivedBlocks, actual.receivedBlocks)
  1366  	assert.Equal(t, want.blockStates, actual.blockStates)
  1367  }
  1368  
  1369  // checkScResults checks scheduler handler test results
  1370  func checkScResults(t *testing.T, wantErr bool, err error, wantEvent Event, event Event) {
  1371  	if (err != nil) != wantErr {
  1372  		t.Errorf("error = %v, wantErr %v", err, wantErr)
  1373  		return
  1374  	}
  1375  	if !assert.IsType(t, wantEvent, event) {
  1376  		t.Log(fmt.Sprintf("Wrong type received, got: %v", event))
  1377  	}
  1378  	switch wantEvent := wantEvent.(type) {
  1379  	case scPeerError:
  1380  		assert.Equal(t, wantEvent.peerID, event.(scPeerError).peerID)
  1381  		assert.Equal(t, wantEvent.reason != nil, event.(scPeerError).reason != nil)
  1382  	case scBlockReceived:
  1383  		assert.Equal(t, wantEvent.peerID, event.(scBlockReceived).peerID)
  1384  		assert.Equal(t, wantEvent.block, event.(scBlockReceived).block)
  1385  	case scSchedulerFail:
  1386  		assert.Equal(t, wantEvent.reason != nil, event.(scSchedulerFail).reason != nil)
  1387  	}
  1388  }
  1389  
  1390  func TestScHandleBlockResponse(t *testing.T) {
  1391  	now := time.Now()
  1392  	block6FromP1 := bcBlockResponse{
  1393  		time:   now.Add(time.Millisecond),
  1394  		peerID: types.NodeID("P1"),
  1395  		size:   100,
  1396  		block:  makeScBlock(6),
  1397  	}
  1398  
  1399  	type args struct {
  1400  		event bcBlockResponse
  1401  	}
  1402  
  1403  	tests := []struct {
  1404  		name      string
  1405  		fields    scTestParams
  1406  		args      args
  1407  		wantEvent Event
  1408  		wantErr   bool
  1409  	}{
  1410  		{
  1411  			name:      "empty scheduler",
  1412  			fields:    scTestParams{},
  1413  			args:      args{event: block6FromP1},
  1414  			wantEvent: noOpEvent{},
  1415  		},
  1416  		{
  1417  			name:      "block from removed peer",
  1418  			fields:    scTestParams{peers: map[string]*scPeer{"P1": {height: 8, state: peerStateRemoved}}},
  1419  			args:      args{event: block6FromP1},
  1420  			wantEvent: noOpEvent{},
  1421  		},
  1422  		{
  1423  			name: "block we haven't asked for",
  1424  			fields: scTestParams{
  1425  				peers: map[string]*scPeer{"P1": {height: 8, state: peerStateReady}},
  1426  				allB:  []int64{1, 2, 3, 4, 5, 6, 7, 8}},
  1427  			args:      args{event: block6FromP1},
  1428  			wantEvent: scPeerError{peerID: "P1", reason: fmt.Errorf("some error")},
  1429  		},
  1430  		{
  1431  			name: "block from wrong peer",
  1432  			fields: scTestParams{
  1433  				peers:       map[string]*scPeer{"P2": {height: 8, state: peerStateReady}},
  1434  				allB:        []int64{1, 2, 3, 4, 5, 6, 7, 8},
  1435  				pending:     map[int64]types.NodeID{6: "P2"},
  1436  				pendingTime: map[int64]time.Time{6: now},
  1437  			},
  1438  			args:      args{event: block6FromP1},
  1439  			wantEvent: noOpEvent{},
  1440  		},
  1441  		{
  1442  			name: "block with bad timestamp",
  1443  			fields: scTestParams{
  1444  				peers:       map[string]*scPeer{"P1": {height: 8, state: peerStateReady}},
  1445  				allB:        []int64{1, 2, 3, 4, 5, 6, 7, 8},
  1446  				pending:     map[int64]types.NodeID{6: "P1"},
  1447  				pendingTime: map[int64]time.Time{6: now.Add(time.Second)},
  1448  			},
  1449  			args:      args{event: block6FromP1},
  1450  			wantEvent: scPeerError{peerID: "P1", reason: fmt.Errorf("some error")},
  1451  		},
  1452  		{
  1453  			name: "good block, accept",
  1454  			fields: scTestParams{
  1455  				peers:       map[string]*scPeer{"P1": {height: 8, state: peerStateReady}},
  1456  				allB:        []int64{1, 2, 3, 4, 5, 6, 7, 8},
  1457  				pending:     map[int64]types.NodeID{6: "P1"},
  1458  				pendingTime: map[int64]time.Time{6: now},
  1459  			},
  1460  			args:      args{event: block6FromP1},
  1461  			wantEvent: scBlockReceived{peerID: "P1", block: block6FromP1.block},
  1462  		},
  1463  	}
  1464  
  1465  	for _, tt := range tests {
  1466  		tt := tt
  1467  		t.Run(tt.name, func(t *testing.T) {
  1468  			sc := newTestScheduler(tt.fields)
  1469  			event, err := sc.handleBlockResponse(tt.args.event)
  1470  			checkScResults(t, tt.wantErr, err, tt.wantEvent, event)
  1471  		})
  1472  	}
  1473  }
  1474  
  1475  func TestScHandleNoBlockResponse(t *testing.T) {
  1476  	now := time.Now()
  1477  	noBlock6FromP1 := bcNoBlockResponse{
  1478  		time:   now.Add(time.Millisecond),
  1479  		peerID: types.NodeID("P1"),
  1480  		height: 6,
  1481  	}
  1482  
  1483  	tests := []struct {
  1484  		name       string
  1485  		fields     scTestParams
  1486  		wantEvent  Event
  1487  		wantFields scTestParams
  1488  		wantErr    bool
  1489  	}{
  1490  		{
  1491  			name:       "empty scheduler",
  1492  			fields:     scTestParams{},
  1493  			wantEvent:  noOpEvent{},
  1494  			wantFields: scTestParams{},
  1495  		},
  1496  		{
  1497  			name:       "noBlock from removed peer",
  1498  			fields:     scTestParams{peers: map[string]*scPeer{"P1": {height: 8, state: peerStateRemoved}}},
  1499  			wantEvent:  noOpEvent{},
  1500  			wantFields: scTestParams{peers: map[string]*scPeer{"P1": {height: 8, state: peerStateRemoved}}},
  1501  		},
  1502  		{
  1503  			name: "for block we haven't asked for",
  1504  			fields: scTestParams{
  1505  				peers: map[string]*scPeer{"P1": {height: 8, state: peerStateReady}},
  1506  				allB:  []int64{1, 2, 3, 4, 5, 6, 7, 8}},
  1507  			wantEvent:  scPeerError{peerID: "P1", reason: fmt.Errorf("some error")},
  1508  			wantFields: scTestParams{peers: map[string]*scPeer{"P1": {height: 8, state: peerStateRemoved}}},
  1509  		},
  1510  		{
  1511  			name: "noBlock from peer we don't have",
  1512  			fields: scTestParams{
  1513  				peers:       map[string]*scPeer{"P2": {height: 8, state: peerStateReady}},
  1514  				allB:        []int64{1, 2, 3, 4, 5, 6, 7, 8},
  1515  				pending:     map[int64]types.NodeID{6: "P2"},
  1516  				pendingTime: map[int64]time.Time{6: now},
  1517  			},
  1518  			wantEvent: noOpEvent{},
  1519  			wantFields: scTestParams{
  1520  				peers:       map[string]*scPeer{"P2": {height: 8, state: peerStateReady}},
  1521  				allB:        []int64{1, 2, 3, 4, 5, 6, 7, 8},
  1522  				pending:     map[int64]types.NodeID{6: "P2"},
  1523  				pendingTime: map[int64]time.Time{6: now},
  1524  			},
  1525  		},
  1526  		{
  1527  			name: "noBlock from existing peer",
  1528  			fields: scTestParams{
  1529  				peers:       map[string]*scPeer{"P1": {height: 8, state: peerStateReady}},
  1530  				allB:        []int64{1, 2, 3, 4, 5, 6, 7, 8},
  1531  				pending:     map[int64]types.NodeID{6: "P1"},
  1532  				pendingTime: map[int64]time.Time{6: now},
  1533  			},
  1534  			wantEvent:  scPeerError{peerID: "P1", reason: fmt.Errorf("some error")},
  1535  			wantFields: scTestParams{peers: map[string]*scPeer{"P1": {height: 8, state: peerStateRemoved}}},
  1536  		},
  1537  	}
  1538  
  1539  	for _, tt := range tests {
  1540  		tt := tt
  1541  		t.Run(tt.name, func(t *testing.T) {
  1542  			sc := newTestScheduler(tt.fields)
  1543  			event, err := sc.handleNoBlockResponse(noBlock6FromP1)
  1544  			checkScResults(t, tt.wantErr, err, tt.wantEvent, event)
  1545  			wantSc := newTestScheduler(tt.wantFields)
  1546  			assert.Equal(t, wantSc, sc)
  1547  		})
  1548  	}
  1549  }
  1550  
  1551  func TestScHandleBlockProcessed(t *testing.T) {
  1552  	now := time.Now()
  1553  	processed6FromP1 := pcBlockProcessed{
  1554  		peerID: types.NodeID("P1"),
  1555  		height: 6,
  1556  	}
  1557  
  1558  	type args struct {
  1559  		event pcBlockProcessed
  1560  	}
  1561  
  1562  	tests := []struct {
  1563  		name      string
  1564  		fields    scTestParams
  1565  		args      args
  1566  		wantEvent Event
  1567  		wantErr   bool
  1568  	}{
  1569  		{
  1570  			name:      "empty scheduler",
  1571  			fields:    scTestParams{height: 6},
  1572  			args:      args{event: processed6FromP1},
  1573  			wantEvent: noOpEvent{},
  1574  		},
  1575  		{
  1576  			name: "processed block we don't have",
  1577  			fields: scTestParams{
  1578  				initHeight:  6,
  1579  				peers:       map[string]*scPeer{"P1": {height: 8, state: peerStateReady}},
  1580  				allB:        []int64{6, 7, 8},
  1581  				pending:     map[int64]types.NodeID{6: "P1"},
  1582  				pendingTime: map[int64]time.Time{6: now},
  1583  			},
  1584  			args:      args{event: processed6FromP1},
  1585  			wantEvent: noOpEvent{},
  1586  		},
  1587  		{
  1588  			name: "processed block ok, we processed all blocks",
  1589  			fields: scTestParams{
  1590  				initHeight: 6,
  1591  				peers:      map[string]*scPeer{"P1": {height: 7, state: peerStateReady}},
  1592  				allB:       []int64{6, 7},
  1593  				received:   map[int64]types.NodeID{6: "P1", 7: "P1"},
  1594  			},
  1595  			args:      args{event: processed6FromP1},
  1596  			wantEvent: scFinishedEv{},
  1597  		},
  1598  		{
  1599  			name: "processed block ok, we still have blocks to process",
  1600  			fields: scTestParams{
  1601  				initHeight: 6,
  1602  				peers:      map[string]*scPeer{"P1": {height: 8, state: peerStateReady}},
  1603  				allB:       []int64{6, 7, 8},
  1604  				pending:    map[int64]types.NodeID{7: "P1", 8: "P1"},
  1605  				received:   map[int64]types.NodeID{6: "P1"},
  1606  			},
  1607  			args:      args{event: processed6FromP1},
  1608  			wantEvent: noOpEvent{},
  1609  		},
  1610  	}
  1611  
  1612  	for _, tt := range tests {
  1613  		tt := tt
  1614  		t.Run(tt.name, func(t *testing.T) {
  1615  			sc := newTestScheduler(tt.fields)
  1616  			event, err := sc.handleBlockProcessed(tt.args.event)
  1617  			checkScResults(t, tt.wantErr, err, tt.wantEvent, event)
  1618  		})
  1619  	}
  1620  }
  1621  
  1622  func TestScHandleBlockVerificationFailure(t *testing.T) {
  1623  	now := time.Now()
  1624  
  1625  	type args struct {
  1626  		event pcBlockVerificationFailure
  1627  	}
  1628  
  1629  	tests := []struct {
  1630  		name      string
  1631  		fields    scTestParams
  1632  		args      args
  1633  		wantEvent Event
  1634  		wantErr   bool
  1635  	}{
  1636  		{
  1637  			name:      "empty scheduler",
  1638  			fields:    scTestParams{},
  1639  			args:      args{event: pcBlockVerificationFailure{height: 10, firstPeerID: "P1", secondPeerID: "P1"}},
  1640  			wantEvent: noOpEvent{},
  1641  		},
  1642  		{
  1643  			name: "failed block we don't have, single peer is still removed",
  1644  			fields: scTestParams{
  1645  				initHeight:  6,
  1646  				peers:       map[string]*scPeer{"P1": {height: 8, state: peerStateReady}},
  1647  				allB:        []int64{6, 7, 8},
  1648  				pending:     map[int64]types.NodeID{6: "P1"},
  1649  				pendingTime: map[int64]time.Time{6: now},
  1650  			},
  1651  			args:      args{event: pcBlockVerificationFailure{height: 10, firstPeerID: "P1", secondPeerID: "P1"}},
  1652  			wantEvent: scFinishedEv{},
  1653  		},
  1654  		{
  1655  			name: "failed block we don't have, one of two peers are removed",
  1656  			fields: scTestParams{
  1657  				initHeight:  6,
  1658  				peers:       map[string]*scPeer{"P1": {height: 8, state: peerStateReady}, "P2": {height: 8, state: peerStateReady}},
  1659  				allB:        []int64{6, 7, 8},
  1660  				pending:     map[int64]types.NodeID{6: "P1"},
  1661  				pendingTime: map[int64]time.Time{6: now},
  1662  			},
  1663  			args:      args{event: pcBlockVerificationFailure{height: 10, firstPeerID: "P1", secondPeerID: "P1"}},
  1664  			wantEvent: noOpEvent{},
  1665  		},
  1666  		{
  1667  			name: "failed block, all blocks are processed after removal",
  1668  			fields: scTestParams{
  1669  				initHeight: 6,
  1670  				peers:      map[string]*scPeer{"P1": {height: 7, state: peerStateReady}},
  1671  				allB:       []int64{6, 7},
  1672  				received:   map[int64]types.NodeID{6: "P1", 7: "P1"},
  1673  			},
  1674  			args:      args{event: pcBlockVerificationFailure{height: 7, firstPeerID: "P1", secondPeerID: "P1"}},
  1675  			wantEvent: scFinishedEv{},
  1676  		},
  1677  		{
  1678  			name: "failed block, we still have blocks to process",
  1679  			fields: scTestParams{
  1680  				initHeight: 5,
  1681  				peers:      map[string]*scPeer{"P1": {height: 8, state: peerStateReady}, "P2": {height: 8, state: peerStateReady}},
  1682  				allB:       []int64{5, 6, 7, 8},
  1683  				pending:    map[int64]types.NodeID{7: "P1", 8: "P1"},
  1684  				received:   map[int64]types.NodeID{5: "P1", 6: "P1"},
  1685  			},
  1686  			args:      args{event: pcBlockVerificationFailure{height: 5, firstPeerID: "P1", secondPeerID: "P1"}},
  1687  			wantEvent: noOpEvent{},
  1688  		},
  1689  		{
  1690  			name: "failed block, H+1 and H+2 delivered by different peers, we still have blocks to process",
  1691  			fields: scTestParams{
  1692  				initHeight: 5,
  1693  				peers: map[string]*scPeer{
  1694  					"P1": {height: 8, state: peerStateReady},
  1695  					"P2": {height: 8, state: peerStateReady},
  1696  					"P3": {height: 8, state: peerStateReady},
  1697  				},
  1698  				allB:     []int64{5, 6, 7, 8},
  1699  				pending:  map[int64]types.NodeID{7: "P1", 8: "P1"},
  1700  				received: map[int64]types.NodeID{5: "P1", 6: "P1"},
  1701  			},
  1702  			args:      args{event: pcBlockVerificationFailure{height: 5, firstPeerID: "P1", secondPeerID: "P2"}},
  1703  			wantEvent: noOpEvent{},
  1704  		},
  1705  	}
  1706  
  1707  	for _, tt := range tests {
  1708  		tt := tt
  1709  		t.Run(tt.name, func(t *testing.T) {
  1710  			sc := newTestScheduler(tt.fields)
  1711  			event, err := sc.handleBlockProcessError(tt.args.event)
  1712  			checkScResults(t, tt.wantErr, err, tt.wantEvent, event)
  1713  		})
  1714  	}
  1715  }
  1716  
  1717  func TestScHandleAddNewPeer(t *testing.T) {
  1718  	addP1 := bcAddNewPeer{
  1719  		peerID: types.NodeID("P1"),
  1720  	}
  1721  	type args struct {
  1722  		event bcAddNewPeer
  1723  	}
  1724  
  1725  	tests := []struct {
  1726  		name      string
  1727  		fields    scTestParams
  1728  		args      args
  1729  		wantEvent Event
  1730  		wantErr   bool
  1731  	}{
  1732  		{
  1733  			name:      "add P1 to empty scheduler",
  1734  			fields:    scTestParams{},
  1735  			args:      args{event: addP1},
  1736  			wantEvent: noOpEvent{},
  1737  		},
  1738  		{
  1739  			name: "add duplicate peer",
  1740  			fields: scTestParams{
  1741  				initHeight: 6,
  1742  				peers:      map[string]*scPeer{"P1": {height: 8, state: peerStateReady}},
  1743  				allB:       []int64{6, 7, 8},
  1744  			},
  1745  			args:      args{event: addP1},
  1746  			wantEvent: noOpEvent{},
  1747  		},
  1748  		{
  1749  			name: "add P1 to non empty scheduler",
  1750  			fields: scTestParams{
  1751  				initHeight: 6,
  1752  				peers:      map[string]*scPeer{"P2": {height: 8, state: peerStateReady}},
  1753  				allB:       []int64{6, 7, 8},
  1754  			},
  1755  			args:      args{event: addP1},
  1756  			wantEvent: noOpEvent{},
  1757  		},
  1758  	}
  1759  
  1760  	for _, tt := range tests {
  1761  		tt := tt
  1762  		t.Run(tt.name, func(t *testing.T) {
  1763  			sc := newTestScheduler(tt.fields)
  1764  			event, err := sc.handleAddNewPeer(tt.args.event)
  1765  			checkScResults(t, tt.wantErr, err, tt.wantEvent, event)
  1766  		})
  1767  	}
  1768  }
  1769  
  1770  func TestScHandleTryPrunePeer(t *testing.T) {
  1771  	now := time.Now()
  1772  
  1773  	pruneEv := rTryPrunePeer{
  1774  		time: now.Add(time.Second + time.Millisecond),
  1775  	}
  1776  	type args struct {
  1777  		event rTryPrunePeer
  1778  	}
  1779  
  1780  	tests := []struct {
  1781  		name      string
  1782  		fields    scTestParams
  1783  		args      args
  1784  		wantEvent Event
  1785  		wantErr   bool
  1786  	}{
  1787  		{
  1788  			name:      "no peers",
  1789  			fields:    scTestParams{},
  1790  			args:      args{event: pruneEv},
  1791  			wantEvent: noOpEvent{},
  1792  		},
  1793  		{
  1794  			name: "no prunable peers",
  1795  			fields: scTestParams{
  1796  				minRecvRate: 100,
  1797  				peers: map[string]*scPeer{
  1798  					// X - removed, active, fast
  1799  					"P1": {state: peerStateRemoved, lastTouched: now.Add(time.Second), lastRate: 101},
  1800  					// X - ready, active, fast
  1801  					"P2": {state: peerStateReady, lastTouched: now.Add(time.Second), lastRate: 101},
  1802  					// X - removed, active, equal
  1803  					"P3": {state: peerStateRemoved, lastTouched: now.Add(time.Second), lastRate: 100}},
  1804  				peerTimeout: time.Second,
  1805  			},
  1806  			args:      args{event: pruneEv},
  1807  			wantEvent: noOpEvent{},
  1808  		},
  1809  		{
  1810  			name: "mixed peers",
  1811  			fields: scTestParams{
  1812  				minRecvRate: 100,
  1813  				peers: map[string]*scPeer{
  1814  					// X - removed, active, fast
  1815  					"P1": {state: peerStateRemoved, lastTouched: now.Add(time.Second), lastRate: 101, height: 5},
  1816  					// X - ready, active, fast
  1817  					"P2": {state: peerStateReady, lastTouched: now.Add(time.Second), lastRate: 101, height: 5},
  1818  					// X - removed, active, equal
  1819  					"P3": {state: peerStateRemoved, lastTouched: now.Add(time.Second), lastRate: 100, height: 5},
  1820  					// V - ready, inactive, equal
  1821  					"P4": {state: peerStateReady, lastTouched: now, lastRate: 100, height: 7},
  1822  					// V - ready, inactive, slow
  1823  					"P5": {state: peerStateReady, lastTouched: now, lastRate: 99, height: 7},
  1824  					//  V - ready, active, slow
  1825  					"P6": {state: peerStateReady, lastTouched: now.Add(time.Second), lastRate: 90, height: 7},
  1826  				},
  1827  				allB:        []int64{1, 2, 3, 4, 5, 6, 7},
  1828  				peerTimeout: time.Second},
  1829  			args:      args{event: pruneEv},
  1830  			wantEvent: scPeersPruned{peers: []types.NodeID{"P4", "P5", "P6"}},
  1831  		},
  1832  		{
  1833  			name: "mixed peers, finish after pruning",
  1834  			fields: scTestParams{
  1835  				minRecvRate: 100,
  1836  				height:      6,
  1837  				peers: map[string]*scPeer{
  1838  					// X - removed, active, fast
  1839  					"P1": {state: peerStateRemoved, lastTouched: now.Add(time.Second), lastRate: 101, height: 5},
  1840  					// X - ready, active, fast
  1841  					"P2": {state: peerStateReady, lastTouched: now.Add(time.Second), lastRate: 101, height: 5},
  1842  					// X - removed, active, equal
  1843  					"P3": {state: peerStateRemoved, lastTouched: now.Add(time.Second), lastRate: 100, height: 5},
  1844  					// V - ready, inactive, equal
  1845  					"P4": {state: peerStateReady, lastTouched: now, lastRate: 100, height: 7},
  1846  					// V - ready, inactive, slow
  1847  					"P5": {state: peerStateReady, lastTouched: now, lastRate: 99, height: 7},
  1848  					//  V - ready, active, slow
  1849  					"P6": {state: peerStateReady, lastTouched: now.Add(time.Second), lastRate: 90, height: 7},
  1850  				},
  1851  				allB:        []int64{6, 7},
  1852  				peerTimeout: time.Second},
  1853  			args:      args{event: pruneEv},
  1854  			wantEvent: scFinishedEv{},
  1855  		},
  1856  	}
  1857  
  1858  	for _, tt := range tests {
  1859  		tt := tt
  1860  		t.Run(tt.name, func(t *testing.T) {
  1861  			sc := newTestScheduler(tt.fields)
  1862  			event, err := sc.handleTryPrunePeer(tt.args.event)
  1863  			checkScResults(t, tt.wantErr, err, tt.wantEvent, event)
  1864  		})
  1865  	}
  1866  }
  1867  
  1868  func TestScHandleTrySchedule(t *testing.T) {
  1869  	now := time.Now()
  1870  	tryEv := rTrySchedule{
  1871  		time: now.Add(time.Second + time.Millisecond),
  1872  	}
  1873  
  1874  	type args struct {
  1875  		event rTrySchedule
  1876  	}
  1877  	tests := []struct {
  1878  		name      string
  1879  		fields    scTestParams
  1880  		args      args
  1881  		wantEvent Event
  1882  		wantErr   bool
  1883  	}{
  1884  		{
  1885  			name:      "no peers",
  1886  			fields:    scTestParams{startTime: now, peers: map[string]*scPeer{}},
  1887  			args:      args{event: tryEv},
  1888  			wantEvent: noOpEvent{},
  1889  		},
  1890  		{
  1891  			name:      "only new peers",
  1892  			fields:    scTestParams{startTime: now, peers: map[string]*scPeer{"P1": {height: -1, state: peerStateNew}}},
  1893  			args:      args{event: tryEv},
  1894  			wantEvent: noOpEvent{},
  1895  		},
  1896  		{
  1897  			name:      "only Removed peers",
  1898  			fields:    scTestParams{startTime: now, peers: map[string]*scPeer{"P1": {height: 4, state: peerStateRemoved}}},
  1899  			args:      args{event: tryEv},
  1900  			wantEvent: noOpEvent{},
  1901  		},
  1902  		{
  1903  			name: "one Ready shorter peer",
  1904  			fields: scTestParams{
  1905  				startTime: now,
  1906  				height:    6,
  1907  				peers:     map[string]*scPeer{"P1": {height: 4, state: peerStateReady}}},
  1908  			args:      args{event: tryEv},
  1909  			wantEvent: noOpEvent{},
  1910  		},
  1911  		{
  1912  			name: "one Ready equal peer",
  1913  			fields: scTestParams{
  1914  				startTime: now,
  1915  				peers:     map[string]*scPeer{"P1": {height: 4, state: peerStateReady}},
  1916  				allB:      []int64{1, 2, 3, 4}},
  1917  			args:      args{event: tryEv},
  1918  			wantEvent: scBlockRequest{peerID: "P1", height: 1},
  1919  		},
  1920  		{
  1921  			name: "many Ready higher peers with different number of pending requests",
  1922  			fields: scTestParams{
  1923  				startTime: now,
  1924  				peers: map[string]*scPeer{
  1925  					"P1": {height: 4, state: peerStateReady},
  1926  					"P2": {height: 5, state: peerStateReady}},
  1927  				allB: []int64{1, 2, 3, 4, 5},
  1928  				pending: map[int64]types.NodeID{
  1929  					1: "P1", 2: "P1",
  1930  					3: "P2",
  1931  				},
  1932  			},
  1933  			args:      args{event: tryEv},
  1934  			wantEvent: scBlockRequest{peerID: "P2", height: 4},
  1935  		},
  1936  
  1937  		{
  1938  			name: "many Ready higher peers with same number of pending requests",
  1939  			fields: scTestParams{
  1940  				startTime: now,
  1941  				peers: map[string]*scPeer{
  1942  					"P2": {height: 8, state: peerStateReady},
  1943  					"P1": {height: 8, state: peerStateReady},
  1944  					"P3": {height: 8, state: peerStateReady}},
  1945  				allB: []int64{1, 2, 3, 4, 5, 6, 7, 8},
  1946  				pending: map[int64]types.NodeID{
  1947  					1: "P1", 2: "P1",
  1948  					3: "P3", 4: "P3",
  1949  					5: "P2", 6: "P2",
  1950  				},
  1951  			},
  1952  			args:      args{event: tryEv},
  1953  			wantEvent: scBlockRequest{peerID: "P1", height: 7},
  1954  		},
  1955  	}
  1956  
  1957  	for _, tt := range tests {
  1958  		tt := tt
  1959  		t.Run(tt.name, func(t *testing.T) {
  1960  			sc := newTestScheduler(tt.fields)
  1961  			event, err := sc.handleTrySchedule(tt.args.event)
  1962  			checkScResults(t, tt.wantErr, err, tt.wantEvent, event)
  1963  		})
  1964  	}
  1965  }
  1966  
  1967  func TestScHandleStatusResponse(t *testing.T) {
  1968  	now := time.Now()
  1969  	statusRespP1Ev := bcStatusResponse{
  1970  		time:   now.Add(time.Second + time.Millisecond),
  1971  		peerID: "P1",
  1972  		height: 6,
  1973  	}
  1974  
  1975  	type args struct {
  1976  		event bcStatusResponse
  1977  	}
  1978  	tests := []struct {
  1979  		name      string
  1980  		fields    scTestParams
  1981  		args      args
  1982  		wantEvent Event
  1983  		wantErr   bool
  1984  	}{
  1985  		{
  1986  			name: "change height of non existing peer",
  1987  			fields: scTestParams{
  1988  				peers: map[string]*scPeer{"P2": {height: 2, state: peerStateReady}},
  1989  				allB:  []int64{1, 2},
  1990  			},
  1991  			args:      args{event: statusRespP1Ev},
  1992  			wantEvent: noOpEvent{},
  1993  		},
  1994  
  1995  		{
  1996  			name:      "increase height of removed peer",
  1997  			fields:    scTestParams{peers: map[string]*scPeer{"P1": {height: 2, state: peerStateRemoved}}},
  1998  			args:      args{event: statusRespP1Ev},
  1999  			wantEvent: noOpEvent{},
  2000  		},
  2001  
  2002  		{
  2003  			name: "decrease height of single peer",
  2004  			fields: scTestParams{
  2005  				height: 5,
  2006  				peers:  map[string]*scPeer{"P1": {height: 10, state: peerStateReady}},
  2007  				allB:   []int64{5, 6, 7, 8, 9, 10},
  2008  			},
  2009  			args:      args{event: statusRespP1Ev},
  2010  			wantEvent: scPeerError{peerID: "P1", reason: fmt.Errorf("some error")},
  2011  		},
  2012  
  2013  		{
  2014  			name: "increase height of single peer",
  2015  			fields: scTestParams{
  2016  				peers: map[string]*scPeer{"P1": {height: 2, state: peerStateReady}},
  2017  				allB:  []int64{1, 2}},
  2018  			args:      args{event: statusRespP1Ev},
  2019  			wantEvent: noOpEvent{},
  2020  		},
  2021  		{
  2022  			name: "noop height change of single peer",
  2023  			fields: scTestParams{
  2024  				peers: map[string]*scPeer{"P1": {height: 6, state: peerStateReady}},
  2025  				allB:  []int64{1, 2, 3, 4, 5, 6}},
  2026  			args:      args{event: statusRespP1Ev},
  2027  			wantEvent: noOpEvent{},
  2028  		},
  2029  	}
  2030  
  2031  	for _, tt := range tests {
  2032  		tt := tt
  2033  		t.Run(tt.name, func(t *testing.T) {
  2034  			sc := newTestScheduler(tt.fields)
  2035  			event, err := sc.handleStatusResponse(tt.args.event)
  2036  			checkScResults(t, tt.wantErr, err, tt.wantEvent, event)
  2037  		})
  2038  	}
  2039  }
  2040  
  2041  func TestScHandle(t *testing.T) {
  2042  	now := time.Now()
  2043  
  2044  	type unknownEv struct {
  2045  		priorityNormal
  2046  	}
  2047  
  2048  	block1, block2, block3 := makeScBlock(1), makeScBlock(2), makeScBlock(3)
  2049  
  2050  	t0 := time.Now()
  2051  	tick := make([]time.Time, 100)
  2052  	for i := range tick {
  2053  		tick[i] = t0.Add(time.Duration(i) * time.Millisecond)
  2054  	}
  2055  
  2056  	type args struct {
  2057  		event Event
  2058  	}
  2059  	type scStep struct {
  2060  		currentSc *scTestParams
  2061  		args      args
  2062  		wantEvent Event
  2063  		wantErr   bool
  2064  		wantSc    *scTestParams
  2065  	}
  2066  	tests := []struct {
  2067  		name  string
  2068  		steps []scStep
  2069  	}{
  2070  		{
  2071  			name: "unknown event",
  2072  			steps: []scStep{
  2073  				{ // add P1
  2074  					currentSc: &scTestParams{},
  2075  					args:      args{event: unknownEv{}},
  2076  					wantEvent: scSchedulerFail{reason: fmt.Errorf("some error")},
  2077  					wantSc:    &scTestParams{},
  2078  				},
  2079  			},
  2080  		},
  2081  		{
  2082  			name: "single peer, sync 3 blocks",
  2083  			steps: []scStep{
  2084  				{ // add P1
  2085  					currentSc: &scTestParams{startTime: now, peers: map[string]*scPeer{}, height: 1},
  2086  					args:      args{event: bcAddNewPeer{peerID: "P1"}},
  2087  					wantEvent: noOpEvent{},
  2088  					wantSc: &scTestParams{startTime: now, peers: map[string]*scPeer{
  2089  						"P1": {base: -1, height: -1, state: peerStateNew}}, height: 1},
  2090  				},
  2091  				{ // set height of P1
  2092  					args:      args{event: bcStatusResponse{peerID: "P1", time: tick[0], height: 3}},
  2093  					wantEvent: noOpEvent{},
  2094  					wantSc: &scTestParams{
  2095  						startTime: now,
  2096  						peers:     map[string]*scPeer{"P1": {height: 3, state: peerStateReady}},
  2097  						allB:      []int64{1, 2, 3},
  2098  						height:    1,
  2099  					},
  2100  				},
  2101  				{ // schedule block 1
  2102  					args:      args{event: rTrySchedule{time: tick[1]}},
  2103  					wantEvent: scBlockRequest{peerID: "P1", height: 1},
  2104  					wantSc: &scTestParams{
  2105  						startTime:   now,
  2106  						peers:       map[string]*scPeer{"P1": {height: 3, state: peerStateReady}},
  2107  						allB:        []int64{1, 2, 3},
  2108  						pending:     map[int64]types.NodeID{1: "P1"},
  2109  						pendingTime: map[int64]time.Time{1: tick[1]},
  2110  						height:      1,
  2111  					},
  2112  				},
  2113  				{ // schedule block 2
  2114  					args:      args{event: rTrySchedule{time: tick[2]}},
  2115  					wantEvent: scBlockRequest{peerID: "P1", height: 2},
  2116  					wantSc: &scTestParams{
  2117  						startTime:   now,
  2118  						peers:       map[string]*scPeer{"P1": {height: 3, state: peerStateReady}},
  2119  						allB:        []int64{1, 2, 3},
  2120  						pending:     map[int64]types.NodeID{1: "P1", 2: "P1"},
  2121  						pendingTime: map[int64]time.Time{1: tick[1], 2: tick[2]},
  2122  						height:      1,
  2123  					},
  2124  				},
  2125  				{ // schedule block 3
  2126  					args:      args{event: rTrySchedule{time: tick[3]}},
  2127  					wantEvent: scBlockRequest{peerID: "P1", height: 3},
  2128  					wantSc: &scTestParams{
  2129  						startTime:   now,
  2130  						peers:       map[string]*scPeer{"P1": {height: 3, state: peerStateReady}},
  2131  						allB:        []int64{1, 2, 3},
  2132  						pending:     map[int64]types.NodeID{1: "P1", 2: "P1", 3: "P1"},
  2133  						pendingTime: map[int64]time.Time{1: tick[1], 2: tick[2], 3: tick[3]},
  2134  						height:      1,
  2135  					},
  2136  				},
  2137  				{ // block response 1
  2138  					args:      args{event: bcBlockResponse{peerID: "P1", time: tick[4], size: 100, block: block1}},
  2139  					wantEvent: scBlockReceived{peerID: "P1", block: block1},
  2140  					wantSc: &scTestParams{
  2141  						startTime:   now,
  2142  						peers:       map[string]*scPeer{"P1": {height: 3, state: peerStateReady, lastTouched: tick[4]}},
  2143  						allB:        []int64{1, 2, 3},
  2144  						pending:     map[int64]types.NodeID{2: "P1", 3: "P1"},
  2145  						pendingTime: map[int64]time.Time{2: tick[2], 3: tick[3]},
  2146  						received:    map[int64]types.NodeID{1: "P1"},
  2147  						height:      1,
  2148  					},
  2149  				},
  2150  				{ // block response 2
  2151  					args:      args{event: bcBlockResponse{peerID: "P1", time: tick[5], size: 100, block: block2}},
  2152  					wantEvent: scBlockReceived{peerID: "P1", block: block2},
  2153  					wantSc: &scTestParams{
  2154  						startTime:   now,
  2155  						peers:       map[string]*scPeer{"P1": {height: 3, state: peerStateReady, lastTouched: tick[5]}},
  2156  						allB:        []int64{1, 2, 3},
  2157  						pending:     map[int64]types.NodeID{3: "P1"},
  2158  						pendingTime: map[int64]time.Time{3: tick[3]},
  2159  						received:    map[int64]types.NodeID{1: "P1", 2: "P1"},
  2160  						height:      1,
  2161  					},
  2162  				},
  2163  				{ // block response 3
  2164  					args:      args{event: bcBlockResponse{peerID: "P1", time: tick[6], size: 100, block: block3}},
  2165  					wantEvent: scBlockReceived{peerID: "P1", block: block3},
  2166  					wantSc: &scTestParams{
  2167  						startTime: now,
  2168  						peers:     map[string]*scPeer{"P1": {height: 3, state: peerStateReady, lastTouched: tick[6]}},
  2169  						allB:      []int64{1, 2, 3},
  2170  						received:  map[int64]types.NodeID{1: "P1", 2: "P1", 3: "P1"},
  2171  						height:    1,
  2172  					},
  2173  				},
  2174  				{ // processed block 1
  2175  					args:      args{event: pcBlockProcessed{peerID: types.NodeID("P1"), height: 1}},
  2176  					wantEvent: noOpEvent{},
  2177  					wantSc: &scTestParams{
  2178  						startTime: now,
  2179  						peers:     map[string]*scPeer{"P1": {height: 3, state: peerStateReady, lastTouched: tick[6]}},
  2180  						allB:      []int64{2, 3},
  2181  						received:  map[int64]types.NodeID{2: "P1", 3: "P1"},
  2182  						height:    2,
  2183  					},
  2184  				},
  2185  				{ // processed block 2
  2186  					args:      args{event: pcBlockProcessed{peerID: types.NodeID("P1"), height: 2}},
  2187  					wantEvent: scFinishedEv{},
  2188  					wantSc: &scTestParams{
  2189  						startTime: now,
  2190  						peers:     map[string]*scPeer{"P1": {height: 3, state: peerStateReady, lastTouched: tick[6]}},
  2191  						allB:      []int64{3},
  2192  						received:  map[int64]types.NodeID{3: "P1"},
  2193  						height:    3,
  2194  					},
  2195  				},
  2196  			},
  2197  		},
  2198  		{
  2199  			name: "block verification failure",
  2200  			steps: []scStep{
  2201  				{ // failure processing block 1
  2202  					currentSc: &scTestParams{
  2203  						startTime: now,
  2204  						peers: map[string]*scPeer{
  2205  							"P1": {height: 4, state: peerStateReady, lastTouched: tick[6]},
  2206  							"P2": {height: 3, state: peerStateReady, lastTouched: tick[6]}},
  2207  						allB:     []int64{1, 2, 3, 4},
  2208  						received: map[int64]types.NodeID{1: "P1", 2: "P1", 3: "P1"},
  2209  						height:   1,
  2210  					},
  2211  					args:      args{event: pcBlockVerificationFailure{height: 1, firstPeerID: "P1", secondPeerID: "P1"}},
  2212  					wantEvent: noOpEvent{},
  2213  					wantSc: &scTestParams{
  2214  						startTime: now,
  2215  						peers: map[string]*scPeer{
  2216  							"P1": {height: 4, state: peerStateRemoved, lastTouched: tick[6]},
  2217  							"P2": {height: 3, state: peerStateReady, lastTouched: tick[6]}},
  2218  						allB:     []int64{1, 2, 3},
  2219  						received: map[int64]types.NodeID{},
  2220  						height:   1,
  2221  					},
  2222  				},
  2223  			},
  2224  		},
  2225  	}
  2226  
  2227  	for _, tt := range tests {
  2228  		tt := tt
  2229  		t.Run(tt.name, func(t *testing.T) {
  2230  			var sc *scheduler
  2231  			for i, step := range tt.steps {
  2232  				// First step must always initialize the currentState as state.
  2233  				if step.currentSc != nil {
  2234  					sc = newTestScheduler(*step.currentSc)
  2235  				}
  2236  				if sc == nil {
  2237  					panic("Bad (initial?) step")
  2238  				}
  2239  
  2240  				nextEvent, err := sc.handle(step.args.event)
  2241  				wantSc := newTestScheduler(*step.wantSc)
  2242  
  2243  				t.Logf("step %d(%v): %s", i, step.args.event, sc)
  2244  				checkSameScheduler(t, wantSc, sc)
  2245  
  2246  				checkScResults(t, step.wantErr, err, step.wantEvent, nextEvent)
  2247  
  2248  				// Next step may use the wantedState as their currentState.
  2249  				sc = newTestScheduler(*step.wantSc)
  2250  			}
  2251  		})
  2252  	}
  2253  }