github.com/DFWallet/tendermint-cosmos@v0.0.2/blockchain/v1/reactor_fsm_test.go (about)

     1  package v1
     2  
     3  import (
     4  	"fmt"
     5  	"testing"
     6  	"time"
     7  
     8  	"github.com/stretchr/testify/assert"
     9  
    10  	"github.com/DFWallet/tendermint-cosmos/libs/log"
    11  	tmmath "github.com/DFWallet/tendermint-cosmos/libs/math"
    12  	tmrand "github.com/DFWallet/tendermint-cosmos/libs/rand"
    13  	"github.com/DFWallet/tendermint-cosmos/p2p"
    14  	"github.com/DFWallet/tendermint-cosmos/types"
    15  )
    16  
    17  type lastBlockRequestT struct {
    18  	peerID p2p.ID
    19  	height int64
    20  }
    21  
    22  type lastPeerErrorT struct {
    23  	peerID p2p.ID
    24  	err    error
    25  }
    26  
    27  // reactor for FSM testing
    28  type testReactor struct {
    29  	logger            log.Logger
    30  	fsm               *BcReactorFSM
    31  	numStatusRequests int
    32  	numBlockRequests  int
    33  	lastBlockRequest  lastBlockRequestT
    34  	lastPeerError     lastPeerErrorT
    35  	stateTimerStarts  map[string]int
    36  }
    37  
    38  func sendEventToFSM(fsm *BcReactorFSM, ev bReactorEvent, data bReactorEventData) error {
    39  	return fsm.Handle(&bcReactorMessage{event: ev, data: data})
    40  }
    41  
    42  type fsmStepTestValues struct {
    43  	currentState string
    44  	event        bReactorEvent
    45  	data         bReactorEventData
    46  
    47  	wantErr           error
    48  	wantState         string
    49  	wantStatusReqSent bool
    50  	wantReqIncreased  bool
    51  	wantNewBlocks     []int64
    52  	wantRemovedPeers  []p2p.ID
    53  }
    54  
    55  // ---------------------------------------------------------------------------
    56  // helper test function for different FSM events, state and expected behavior
    57  func sStopFSMEv(current, expected string) fsmStepTestValues {
    58  	return fsmStepTestValues{
    59  		currentState: current,
    60  		event:        stopFSMEv,
    61  		wantState:    expected,
    62  		wantErr:      errNoErrorFinished}
    63  }
    64  
    65  func sUnknownFSMEv(current string) fsmStepTestValues {
    66  	return fsmStepTestValues{
    67  		currentState: current,
    68  		event:        1234,
    69  		wantState:    current,
    70  		wantErr:      errInvalidEvent}
    71  }
    72  
    73  func sStartFSMEv() fsmStepTestValues {
    74  	return fsmStepTestValues{
    75  		currentState:      "unknown",
    76  		event:             startFSMEv,
    77  		wantState:         "waitForPeer",
    78  		wantStatusReqSent: true}
    79  }
    80  
    81  func sStateTimeoutEv(current, expected string, timedoutState string, wantErr error) fsmStepTestValues {
    82  	return fsmStepTestValues{
    83  		currentState: current,
    84  		event:        stateTimeoutEv,
    85  		data: bReactorEventData{
    86  			stateName: timedoutState,
    87  		},
    88  		wantState: expected,
    89  		wantErr:   wantErr,
    90  	}
    91  }
    92  
    93  func sProcessedBlockEv(current, expected string, reactorError error) fsmStepTestValues {
    94  	return fsmStepTestValues{
    95  		currentState: current,
    96  		event:        processedBlockEv,
    97  		data: bReactorEventData{
    98  			err: reactorError,
    99  		},
   100  		wantState: expected,
   101  		wantErr:   reactorError,
   102  	}
   103  }
   104  
   105  func sStatusEv(current, expected string, peerID p2p.ID, height int64, err error) fsmStepTestValues {
   106  	return fsmStepTestValues{
   107  		currentState: current,
   108  		event:        statusResponseEv,
   109  		data:         bReactorEventData{peerID: peerID, height: height},
   110  		wantState:    expected,
   111  		wantErr:      err}
   112  }
   113  
   114  func sMakeRequestsEv(current, expected string, maxPendingRequests int) fsmStepTestValues {
   115  	return fsmStepTestValues{
   116  		currentState:     current,
   117  		event:            makeRequestsEv,
   118  		data:             bReactorEventData{maxNumRequests: maxPendingRequests},
   119  		wantState:        expected,
   120  		wantReqIncreased: true,
   121  	}
   122  }
   123  
   124  func sMakeRequestsEvErrored(current, expected string,
   125  	maxPendingRequests int, err error, peersRemoved []p2p.ID) fsmStepTestValues {
   126  	return fsmStepTestValues{
   127  		currentState:     current,
   128  		event:            makeRequestsEv,
   129  		data:             bReactorEventData{maxNumRequests: maxPendingRequests},
   130  		wantState:        expected,
   131  		wantErr:          err,
   132  		wantRemovedPeers: peersRemoved,
   133  		wantReqIncreased: true,
   134  	}
   135  }
   136  
   137  func sBlockRespEv(current, expected string, peerID p2p.ID, height int64, prevBlocks []int64) fsmStepTestValues {
   138  	txs := []types.Tx{types.Tx("foo"), types.Tx("bar")}
   139  	return fsmStepTestValues{
   140  		currentState: current,
   141  		event:        blockResponseEv,
   142  		data: bReactorEventData{
   143  			peerID: peerID,
   144  			height: height,
   145  			block:  types.MakeBlock(height, txs, nil, nil),
   146  			length: 100},
   147  		wantState:     expected,
   148  		wantNewBlocks: append(prevBlocks, height),
   149  	}
   150  }
   151  
   152  func sBlockRespEvErrored(current, expected string,
   153  	peerID p2p.ID, height int64, prevBlocks []int64, wantErr error, peersRemoved []p2p.ID) fsmStepTestValues {
   154  	txs := []types.Tx{types.Tx("foo"), types.Tx("bar")}
   155  
   156  	return fsmStepTestValues{
   157  		currentState: current,
   158  		event:        blockResponseEv,
   159  		data: bReactorEventData{
   160  			peerID: peerID,
   161  			height: height,
   162  			block:  types.MakeBlock(height, txs, nil, nil),
   163  			length: 100},
   164  		wantState:        expected,
   165  		wantErr:          wantErr,
   166  		wantRemovedPeers: peersRemoved,
   167  		wantNewBlocks:    prevBlocks,
   168  	}
   169  }
   170  
   171  func sPeerRemoveEv(current, expected string, peerID p2p.ID, err error, peersRemoved []p2p.ID) fsmStepTestValues {
   172  	return fsmStepTestValues{
   173  		currentState: current,
   174  		event:        peerRemoveEv,
   175  		data: bReactorEventData{
   176  			peerID: peerID,
   177  			err:    err,
   178  		},
   179  		wantState:        expected,
   180  		wantRemovedPeers: peersRemoved,
   181  	}
   182  }
   183  
   184  // --------------------------------------------
   185  
   186  func newTestReactor(height int64) *testReactor {
   187  	testBcR := &testReactor{logger: log.TestingLogger(), stateTimerStarts: make(map[string]int)}
   188  	testBcR.fsm = NewFSM(height, testBcR)
   189  	testBcR.fsm.SetLogger(testBcR.logger)
   190  	return testBcR
   191  }
   192  
   193  func fixBlockResponseEvStep(step *fsmStepTestValues, testBcR *testReactor) {
   194  	// There is currently no good way to know to which peer a block request was sent.
   195  	// So in some cases where it does not matter, before we simulate a block response
   196  	// we cheat and look where it is expected from.
   197  	if step.event == blockResponseEv {
   198  		height := step.data.height
   199  		peerID, ok := testBcR.fsm.pool.blocks[height]
   200  		if ok {
   201  			step.data.peerID = peerID
   202  		}
   203  	}
   204  }
   205  
   206  type testFields struct {
   207  	name               string
   208  	startingHeight     int64
   209  	maxRequestsPerPeer int
   210  	maxPendingRequests int
   211  	steps              []fsmStepTestValues
   212  }
   213  
   214  func executeFSMTests(t *testing.T, tests []testFields, matchRespToReq bool) {
   215  	for _, tt := range tests {
   216  		tt := tt
   217  		t.Run(tt.name, func(t *testing.T) {
   218  			// Create test reactor
   219  			testBcR := newTestReactor(tt.startingHeight)
   220  
   221  			if tt.maxRequestsPerPeer != 0 {
   222  				maxRequestsPerPeer = tt.maxRequestsPerPeer
   223  			}
   224  
   225  			for _, step := range tt.steps {
   226  				step := step
   227  				assert.Equal(t, step.currentState, testBcR.fsm.state.name)
   228  
   229  				var heightBefore int64
   230  				if step.event == processedBlockEv && step.data.err == errBlockVerificationFailure {
   231  					heightBefore = testBcR.fsm.pool.Height
   232  				}
   233  				oldNumStatusRequests := testBcR.numStatusRequests
   234  				oldNumBlockRequests := testBcR.numBlockRequests
   235  				if matchRespToReq {
   236  					fixBlockResponseEvStep(&step, testBcR)
   237  				}
   238  
   239  				fsmErr := sendEventToFSM(testBcR.fsm, step.event, step.data)
   240  				assert.Equal(t, step.wantErr, fsmErr)
   241  
   242  				if step.wantStatusReqSent {
   243  					assert.Equal(t, oldNumStatusRequests+1, testBcR.numStatusRequests)
   244  				} else {
   245  					assert.Equal(t, oldNumStatusRequests, testBcR.numStatusRequests)
   246  				}
   247  
   248  				if step.wantReqIncreased {
   249  					assert.True(t, oldNumBlockRequests < testBcR.numBlockRequests)
   250  				} else {
   251  					assert.Equal(t, oldNumBlockRequests, testBcR.numBlockRequests)
   252  				}
   253  
   254  				for _, height := range step.wantNewBlocks {
   255  					_, err := testBcR.fsm.pool.BlockAndPeerAtHeight(height)
   256  					assert.Nil(t, err)
   257  				}
   258  				if step.event == processedBlockEv && step.data.err == errBlockVerificationFailure {
   259  					heightAfter := testBcR.fsm.pool.Height
   260  					assert.Equal(t, heightBefore, heightAfter)
   261  					firstAfter, err1 := testBcR.fsm.pool.BlockAndPeerAtHeight(testBcR.fsm.pool.Height)
   262  					secondAfter, err2 := testBcR.fsm.pool.BlockAndPeerAtHeight(testBcR.fsm.pool.Height + 1)
   263  					assert.NotNil(t, err1)
   264  					assert.NotNil(t, err2)
   265  					assert.Nil(t, firstAfter)
   266  					assert.Nil(t, secondAfter)
   267  				}
   268  
   269  				assert.Equal(t, step.wantState, testBcR.fsm.state.name)
   270  
   271  				if step.wantState == "finished" {
   272  					assert.True(t, testBcR.fsm.isCaughtUp())
   273  				}
   274  			}
   275  		})
   276  	}
   277  }
   278  
   279  func TestFSMBasic(t *testing.T) {
   280  	tests := []testFields{
   281  		{
   282  			name:               "one block, one peer - TS2",
   283  			startingHeight:     1,
   284  			maxRequestsPerPeer: 2,
   285  			steps: []fsmStepTestValues{
   286  				sStartFSMEv(),
   287  				sStatusEv("waitForPeer", "waitForBlock", "P1", 2, nil),
   288  				sMakeRequestsEv("waitForBlock", "waitForBlock", maxNumRequests),
   289  				sBlockRespEv("waitForBlock", "waitForBlock", "P1", 1, []int64{}),
   290  				sBlockRespEv("waitForBlock", "waitForBlock", "P2", 2, []int64{1}),
   291  				sProcessedBlockEv("waitForBlock", "finished", nil),
   292  			},
   293  		},
   294  		{
   295  			name:               "multi block, multi peer - TS2",
   296  			startingHeight:     1,
   297  			maxRequestsPerPeer: 2,
   298  			steps: []fsmStepTestValues{
   299  				sStartFSMEv(),
   300  				sStatusEv("waitForPeer", "waitForBlock", "P1", 4, nil),
   301  				sStatusEv("waitForBlock", "waitForBlock", "P2", 4, nil),
   302  				sMakeRequestsEv("waitForBlock", "waitForBlock", maxNumRequests),
   303  
   304  				sBlockRespEv("waitForBlock", "waitForBlock", "P1", 1, []int64{}),
   305  				sBlockRespEv("waitForBlock", "waitForBlock", "P1", 2, []int64{1}),
   306  				sBlockRespEv("waitForBlock", "waitForBlock", "P2", 3, []int64{1, 2}),
   307  				sBlockRespEv("waitForBlock", "waitForBlock", "P2", 4, []int64{1, 2, 3}),
   308  
   309  				sProcessedBlockEv("waitForBlock", "waitForBlock", nil),
   310  				sProcessedBlockEv("waitForBlock", "waitForBlock", nil),
   311  				sProcessedBlockEv("waitForBlock", "finished", nil),
   312  			},
   313  		},
   314  	}
   315  
   316  	executeFSMTests(t, tests, true)
   317  }
   318  
   319  func TestFSMBlockVerificationFailure(t *testing.T) {
   320  	tests := []testFields{
   321  		{
   322  			name:               "block verification failure - TS2 variant",
   323  			startingHeight:     1,
   324  			maxRequestsPerPeer: 3,
   325  			steps: []fsmStepTestValues{
   326  				sStartFSMEv(),
   327  
   328  				// add P1 and get blocks 1-3 from it
   329  				sStatusEv("waitForPeer", "waitForBlock", "P1", 3, nil),
   330  				sMakeRequestsEv("waitForBlock", "waitForBlock", maxNumRequests),
   331  				sBlockRespEv("waitForBlock", "waitForBlock", "P1", 1, []int64{}),
   332  				sBlockRespEv("waitForBlock", "waitForBlock", "P1", 2, []int64{1}),
   333  				sBlockRespEv("waitForBlock", "waitForBlock", "P1", 3, []int64{1, 2}),
   334  
   335  				// add P2
   336  				sStatusEv("waitForBlock", "waitForBlock", "P2", 3, nil),
   337  
   338  				// process block failure, should remove P1 and all blocks
   339  				sProcessedBlockEv("waitForBlock", "waitForBlock", errBlockVerificationFailure),
   340  
   341  				// get blocks 1-3 from P2
   342  				sMakeRequestsEv("waitForBlock", "waitForBlock", maxNumRequests),
   343  				sBlockRespEv("waitForBlock", "waitForBlock", "P2", 1, []int64{}),
   344  				sBlockRespEv("waitForBlock", "waitForBlock", "P2", 2, []int64{1}),
   345  				sBlockRespEv("waitForBlock", "waitForBlock", "P2", 3, []int64{1, 2}),
   346  
   347  				// finish after processing blocks 1 and 2
   348  				sProcessedBlockEv("waitForBlock", "waitForBlock", nil),
   349  				sProcessedBlockEv("waitForBlock", "finished", nil),
   350  			},
   351  		},
   352  	}
   353  
   354  	executeFSMTests(t, tests, false)
   355  }
   356  
   357  func TestFSMBadBlockFromPeer(t *testing.T) {
   358  	tests := []testFields{
   359  		{
   360  			name:               "block we haven't asked for",
   361  			startingHeight:     1,
   362  			maxRequestsPerPeer: 3,
   363  			steps: []fsmStepTestValues{
   364  				sStartFSMEv(),
   365  				// add P1 and ask for blocks 1-3
   366  				sStatusEv("waitForPeer", "waitForBlock", "P1", 300, nil),
   367  				sMakeRequestsEv("waitForBlock", "waitForBlock", maxNumRequests),
   368  
   369  				// blockResponseEv for height 100 should cause an error
   370  				sBlockRespEvErrored("waitForBlock", "waitForPeer",
   371  					"P1", 100, []int64{}, errMissingBlock, []p2p.ID{}),
   372  			},
   373  		},
   374  		{
   375  			name:               "block we already have",
   376  			startingHeight:     1,
   377  			maxRequestsPerPeer: 3,
   378  			steps: []fsmStepTestValues{
   379  				sStartFSMEv(),
   380  				// add P1 and get block 1
   381  				sStatusEv("waitForPeer", "waitForBlock", "P1", 100, nil),
   382  				sMakeRequestsEv("waitForBlock", "waitForBlock", maxNumRequests),
   383  				sBlockRespEv("waitForBlock", "waitForBlock",
   384  					"P1", 1, []int64{}),
   385  
   386  				// Get block 1 again. Since peer is removed together with block 1,
   387  				// the blocks present in the pool should be {}
   388  				sBlockRespEvErrored("waitForBlock", "waitForPeer",
   389  					"P1", 1, []int64{}, errDuplicateBlock, []p2p.ID{"P1"}),
   390  			},
   391  		},
   392  		{
   393  			name:               "block from unknown peer",
   394  			startingHeight:     1,
   395  			maxRequestsPerPeer: 3,
   396  			steps: []fsmStepTestValues{
   397  				sStartFSMEv(),
   398  				// add P1 and get block 1
   399  				sStatusEv("waitForPeer", "waitForBlock", "P1", 3, nil),
   400  
   401  				// get block 1 from unknown peer P2
   402  				sMakeRequestsEv("waitForBlock", "waitForBlock", maxNumRequests),
   403  				sBlockRespEvErrored("waitForBlock", "waitForBlock",
   404  					"P2", 1, []int64{}, errBadDataFromPeer, []p2p.ID{"P2"}),
   405  			},
   406  		},
   407  		{
   408  			name:               "block from wrong peer",
   409  			startingHeight:     1,
   410  			maxRequestsPerPeer: 3,
   411  			steps: []fsmStepTestValues{
   412  				sStartFSMEv(),
   413  				// add P1, make requests for blocks 1-3 to P1
   414  				sStatusEv("waitForPeer", "waitForBlock", "P1", 3, nil),
   415  				sMakeRequestsEv("waitForBlock", "waitForBlock", maxNumRequests),
   416  
   417  				// add P2
   418  				sStatusEv("waitForBlock", "waitForBlock", "P2", 3, nil),
   419  
   420  				// receive block 1 from P2
   421  				sBlockRespEvErrored("waitForBlock", "waitForBlock",
   422  					"P2", 1, []int64{}, errBadDataFromPeer, []p2p.ID{"P2"}),
   423  			},
   424  		},
   425  	}
   426  
   427  	executeFSMTests(t, tests, false)
   428  }
   429  
   430  func TestFSMBlockAtCurrentHeightDoesNotArriveInTime(t *testing.T) {
   431  	tests := []testFields{
   432  		{
   433  			name:               "block at current height undelivered - TS5",
   434  			startingHeight:     1,
   435  			maxRequestsPerPeer: 3,
   436  			steps: []fsmStepTestValues{
   437  				sStartFSMEv(),
   438  				// add P1, get blocks 1 and 2, process block 1
   439  				sStatusEv("waitForPeer", "waitForBlock", "P1", 3, nil),
   440  				sMakeRequestsEv("waitForBlock", "waitForBlock", maxNumRequests),
   441  				sBlockRespEv("waitForBlock", "waitForBlock",
   442  					"P1", 1, []int64{}),
   443  				sBlockRespEv("waitForBlock", "waitForBlock",
   444  					"P1", 2, []int64{1}),
   445  				sProcessedBlockEv("waitForBlock", "waitForBlock", nil),
   446  
   447  				// add P2
   448  				sStatusEv("waitForBlock", "waitForBlock", "P2", 3, nil),
   449  
   450  				// timeout on block 3, P1 should be removed
   451  				sStateTimeoutEv("waitForBlock", "waitForBlock", "waitForBlock", errNoPeerResponseForCurrentHeights),
   452  
   453  				// make requests and finish by receiving blocks 2 and 3 from P2
   454  				sMakeRequestsEv("waitForBlock", "waitForBlock", maxNumRequests),
   455  				sBlockRespEv("waitForBlock", "waitForBlock", "P2", 2, []int64{}),
   456  				sBlockRespEv("waitForBlock", "waitForBlock", "P2", 3, []int64{2}),
   457  				sProcessedBlockEv("waitForBlock", "finished", nil),
   458  			},
   459  		},
   460  		{
   461  			name:               "block at current height undelivered, at maxPeerHeight after peer removal - TS3",
   462  			startingHeight:     1,
   463  			maxRequestsPerPeer: 3,
   464  			steps: []fsmStepTestValues{
   465  				sStartFSMEv(),
   466  				// add P1, request blocks 1-3 from P1
   467  				sStatusEv("waitForPeer", "waitForBlock", "P1", 3, nil),
   468  				sMakeRequestsEv("waitForBlock", "waitForBlock", maxNumRequests),
   469  
   470  				// add P2 (tallest)
   471  				sStatusEv("waitForBlock", "waitForBlock", "P2", 30, nil),
   472  				sMakeRequestsEv("waitForBlock", "waitForBlock", maxNumRequests),
   473  
   474  				// receive blocks 1-3 from P1
   475  				sBlockRespEv("waitForBlock", "waitForBlock", "P1", 1, []int64{}),
   476  				sBlockRespEv("waitForBlock", "waitForBlock", "P1", 2, []int64{1}),
   477  				sBlockRespEv("waitForBlock", "waitForBlock", "P1", 3, []int64{1, 2}),
   478  
   479  				// process blocks at heights 1 and 2
   480  				sProcessedBlockEv("waitForBlock", "waitForBlock", nil),
   481  				sProcessedBlockEv("waitForBlock", "waitForBlock", nil),
   482  
   483  				// timeout on block at height 4
   484  				sStateTimeoutEv("waitForBlock", "finished", "waitForBlock", nil),
   485  			},
   486  		},
   487  	}
   488  
   489  	executeFSMTests(t, tests, true)
   490  }
   491  
   492  func TestFSMPeerRelatedEvents(t *testing.T) {
   493  	tests := []testFields{
   494  		{
   495  			name:           "peer remove event with no blocks",
   496  			startingHeight: 1,
   497  			steps: []fsmStepTestValues{
   498  				sStartFSMEv(),
   499  				// add P1, P2, P3
   500  				sStatusEv("waitForPeer", "waitForBlock", "P1", 3, nil),
   501  				sStatusEv("waitForBlock", "waitForBlock", "P2", 3, nil),
   502  				sStatusEv("waitForBlock", "waitForBlock", "P3", 3, nil),
   503  
   504  				// switch removes P2
   505  				sPeerRemoveEv("waitForBlock", "waitForBlock", "P2", errSwitchRemovesPeer, []p2p.ID{"P2"}),
   506  			},
   507  		},
   508  		{
   509  			name:           "only peer removed while in waitForBlock state",
   510  			startingHeight: 100,
   511  			steps: []fsmStepTestValues{
   512  				sStartFSMEv(),
   513  				// add P1
   514  				sStatusEv("waitForPeer", "waitForBlock", "P1", 200, nil),
   515  
   516  				// switch removes P1
   517  				sPeerRemoveEv("waitForBlock", "waitForPeer", "P1", errSwitchRemovesPeer, []p2p.ID{"P1"}),
   518  			},
   519  		},
   520  		{
   521  			name:               "highest peer removed while in waitForBlock state, node reaches maxPeerHeight - TS4 ",
   522  			startingHeight:     100,
   523  			maxRequestsPerPeer: 3,
   524  			steps: []fsmStepTestValues{
   525  				sStartFSMEv(),
   526  				// add P1 and make requests
   527  				sStatusEv("waitForPeer", "waitForBlock", "P1", 101, nil),
   528  				sMakeRequestsEv("waitForBlock", "waitForBlock", maxNumRequests),
   529  				// add P2
   530  				sStatusEv("waitForBlock", "waitForBlock", "P2", 200, nil),
   531  
   532  				// get blocks 100 and 101 from P1 and process block at height 100
   533  				sBlockRespEv("waitForBlock", "waitForBlock", "P1", 100, []int64{}),
   534  				sBlockRespEv("waitForBlock", "waitForBlock", "P1", 101, []int64{100}),
   535  				sProcessedBlockEv("waitForBlock", "waitForBlock", nil),
   536  
   537  				// switch removes peer P1, should be finished
   538  				sPeerRemoveEv("waitForBlock", "finished", "P2", errSwitchRemovesPeer, []p2p.ID{"P2"}),
   539  			},
   540  		},
   541  		{
   542  			name:               "highest peer lowers its height in waitForBlock state, node reaches maxPeerHeight - TS4",
   543  			startingHeight:     100,
   544  			maxRequestsPerPeer: 3,
   545  			steps: []fsmStepTestValues{
   546  				sStartFSMEv(),
   547  				// add P1 and make requests
   548  				sStatusEv("waitForPeer", "waitForBlock", "P1", 101, nil),
   549  				sMakeRequestsEv("waitForBlock", "waitForBlock", maxNumRequests),
   550  
   551  				// add P2
   552  				sStatusEv("waitForBlock", "waitForBlock", "P2", 200, nil),
   553  
   554  				// get blocks 100 and 101 from P1
   555  				sBlockRespEv("waitForBlock", "waitForBlock", "P1", 100, []int64{}),
   556  				sBlockRespEv("waitForBlock", "waitForBlock", "P1", 101, []int64{100}),
   557  
   558  				// processed block at heights 100
   559  				sProcessedBlockEv("waitForBlock", "waitForBlock", nil),
   560  
   561  				// P2 becomes short
   562  				sStatusEv("waitForBlock", "finished", "P2", 100, errPeerLowersItsHeight),
   563  			},
   564  		},
   565  		{
   566  			name:           "new short peer while in waitForPeer state",
   567  			startingHeight: 100,
   568  			steps: []fsmStepTestValues{
   569  				sStartFSMEv(),
   570  				sStatusEv("waitForPeer", "waitForPeer", "P1", 3, errPeerTooShort),
   571  			},
   572  		},
   573  		{
   574  			name:           "new short peer while in waitForBlock state",
   575  			startingHeight: 100,
   576  			steps: []fsmStepTestValues{
   577  				sStartFSMEv(),
   578  				sStatusEv("waitForPeer", "waitForBlock", "P1", 200, nil),
   579  				sStatusEv("waitForBlock", "waitForBlock", "P2", 3, errPeerTooShort),
   580  			},
   581  		},
   582  		{
   583  			name:           "only peer updated with low height while in waitForBlock state",
   584  			startingHeight: 100,
   585  			steps: []fsmStepTestValues{
   586  				sStartFSMEv(),
   587  				sStatusEv("waitForPeer", "waitForBlock", "P1", 200, nil),
   588  				sStatusEv("waitForBlock", "waitForPeer", "P1", 3, errPeerLowersItsHeight),
   589  			},
   590  		},
   591  		{
   592  			name:               "peer does not exist in the switch",
   593  			startingHeight:     9999999,
   594  			maxRequestsPerPeer: 3,
   595  			steps: []fsmStepTestValues{
   596  				sStartFSMEv(),
   597  				// add P1
   598  				sStatusEv("waitForPeer", "waitForBlock", "P1", 20000000, nil),
   599  				// send request for block 9999999
   600  				// Note: For this block request the "switch missing the peer" error is simulated,
   601  				// see implementation of bcReactor interface, sendBlockRequest(), in this file.
   602  				sMakeRequestsEvErrored("waitForBlock", "waitForBlock",
   603  					maxNumRequests, nil, []p2p.ID{"P1"}),
   604  			},
   605  		},
   606  	}
   607  
   608  	executeFSMTests(t, tests, true)
   609  }
   610  
   611  func TestFSMStopFSM(t *testing.T) {
   612  	tests := []testFields{
   613  		{
   614  			name: "stopFSMEv in unknown",
   615  			steps: []fsmStepTestValues{
   616  				sStopFSMEv("unknown", "finished"),
   617  			},
   618  		},
   619  		{
   620  			name:           "stopFSMEv in waitForPeer",
   621  			startingHeight: 1,
   622  			steps: []fsmStepTestValues{
   623  				sStartFSMEv(),
   624  				sStopFSMEv("waitForPeer", "finished"),
   625  			},
   626  		},
   627  		{
   628  			name:           "stopFSMEv in waitForBlock",
   629  			startingHeight: 1,
   630  			steps: []fsmStepTestValues{
   631  				sStartFSMEv(),
   632  				sStatusEv("waitForPeer", "waitForBlock", "P1", 3, nil),
   633  				sStopFSMEv("waitForBlock", "finished"),
   634  			},
   635  		},
   636  	}
   637  
   638  	executeFSMTests(t, tests, false)
   639  }
   640  
   641  func TestFSMUnknownElements(t *testing.T) {
   642  	tests := []testFields{
   643  		{
   644  			name: "unknown event for state unknown",
   645  			steps: []fsmStepTestValues{
   646  				sUnknownFSMEv("unknown"),
   647  			},
   648  		},
   649  		{
   650  			name: "unknown event for state waitForPeer",
   651  			steps: []fsmStepTestValues{
   652  				sStartFSMEv(),
   653  				sUnknownFSMEv("waitForPeer"),
   654  			},
   655  		},
   656  		{
   657  			name:           "unknown event for state waitForBlock",
   658  			startingHeight: 1,
   659  			steps: []fsmStepTestValues{
   660  				sStartFSMEv(),
   661  				sStatusEv("waitForPeer", "waitForBlock", "P1", 3, nil),
   662  				sUnknownFSMEv("waitForBlock"),
   663  			},
   664  		},
   665  	}
   666  
   667  	executeFSMTests(t, tests, false)
   668  }
   669  
   670  func TestFSMPeerStateTimeoutEvent(t *testing.T) {
   671  	tests := []testFields{
   672  		{
   673  			name:               "timeout event for state waitForPeer while in state waitForPeer - TS1",
   674  			startingHeight:     1,
   675  			maxRequestsPerPeer: 3,
   676  			steps: []fsmStepTestValues{
   677  				sStartFSMEv(),
   678  				sStateTimeoutEv("waitForPeer", "finished", "waitForPeer", errNoTallerPeer),
   679  			},
   680  		},
   681  		{
   682  			name:               "timeout event for state waitForPeer while in a state != waitForPeer",
   683  			startingHeight:     1,
   684  			maxRequestsPerPeer: 3,
   685  			steps: []fsmStepTestValues{
   686  				sStartFSMEv(),
   687  				sStateTimeoutEv("waitForPeer", "waitForPeer", "waitForBlock", errTimeoutEventWrongState),
   688  			},
   689  		},
   690  		{
   691  			name:               "timeout event for state waitForBlock while in state waitForBlock ",
   692  			startingHeight:     1,
   693  			maxRequestsPerPeer: 3,
   694  			steps: []fsmStepTestValues{
   695  				sStartFSMEv(),
   696  				sStatusEv("waitForPeer", "waitForBlock", "P1", 3, nil),
   697  				sMakeRequestsEv("waitForBlock", "waitForBlock", maxNumRequests),
   698  				sStateTimeoutEv("waitForBlock", "waitForPeer", "waitForBlock", errNoPeerResponseForCurrentHeights),
   699  			},
   700  		},
   701  		{
   702  			name:               "timeout event for state waitForBlock while in a state != waitForBlock",
   703  			startingHeight:     1,
   704  			maxRequestsPerPeer: 3,
   705  			steps: []fsmStepTestValues{
   706  				sStartFSMEv(),
   707  				sStatusEv("waitForPeer", "waitForBlock", "P1", 3, nil),
   708  				sMakeRequestsEv("waitForBlock", "waitForBlock", maxNumRequests),
   709  				sStateTimeoutEv("waitForBlock", "waitForBlock", "waitForPeer", errTimeoutEventWrongState),
   710  			},
   711  		},
   712  		{
   713  			name:               "timeout event for state waitForBlock with multiple peers",
   714  			startingHeight:     1,
   715  			maxRequestsPerPeer: 3,
   716  			steps: []fsmStepTestValues{
   717  				sStartFSMEv(),
   718  				sStatusEv("waitForPeer", "waitForBlock", "P1", 3, nil),
   719  				sMakeRequestsEv("waitForBlock", "waitForBlock", maxNumRequests),
   720  				sStatusEv("waitForBlock", "waitForBlock", "P2", 3, nil),
   721  				sStateTimeoutEv("waitForBlock", "waitForBlock", "waitForBlock", errNoPeerResponseForCurrentHeights),
   722  			},
   723  		},
   724  	}
   725  
   726  	executeFSMTests(t, tests, false)
   727  }
   728  
   729  func makeCorrectTransitionSequence(startingHeight int64, numBlocks int64, numPeers int, randomPeerHeights bool,
   730  	maxRequestsPerPeer int, maxPendingRequests int) testFields {
   731  
   732  	// Generate numPeers peers with random or numBlocks heights according to the randomPeerHeights flag.
   733  	peerHeights := make([]int64, numPeers)
   734  	for i := 0; i < numPeers; i++ {
   735  		if i == 0 {
   736  			peerHeights[0] = numBlocks
   737  			continue
   738  		}
   739  		if randomPeerHeights {
   740  			peerHeights[i] = int64(tmmath.MaxInt(tmrand.Intn(int(numBlocks)), int(startingHeight)+1))
   741  		} else {
   742  			peerHeights[i] = numBlocks
   743  		}
   744  	}
   745  
   746  	// Approximate the slice capacity to save time for appends.
   747  	testSteps := make([]fsmStepTestValues, 0, 3*numBlocks+int64(numPeers))
   748  
   749  	testName := fmt.Sprintf("%v-blocks %v-startingHeight %v-peers %v-maxRequestsPerPeer %v-maxNumRequests",
   750  		numBlocks, startingHeight, numPeers, maxRequestsPerPeer, maxPendingRequests)
   751  
   752  	// Add startFSMEv step.
   753  	testSteps = append(testSteps, sStartFSMEv())
   754  
   755  	// For each peer, add statusResponseEv step.
   756  	for i := 0; i < numPeers; i++ {
   757  		peerName := fmt.Sprintf("P%d", i)
   758  		if i == 0 {
   759  			testSteps = append(
   760  				testSteps,
   761  				sStatusEv("waitForPeer", "waitForBlock", p2p.ID(peerName), peerHeights[i], nil))
   762  		} else {
   763  			testSteps = append(testSteps,
   764  				sStatusEv("waitForBlock", "waitForBlock", p2p.ID(peerName), peerHeights[i], nil))
   765  		}
   766  	}
   767  
   768  	height := startingHeight
   769  	numBlocksReceived := 0
   770  	prevBlocks := make([]int64, 0, maxPendingRequests)
   771  
   772  forLoop:
   773  	for i := 0; i < int(numBlocks); i++ {
   774  
   775  		// Add the makeRequestEv step periodically.
   776  		if i%maxRequestsPerPeer == 0 {
   777  			testSteps = append(
   778  				testSteps,
   779  				sMakeRequestsEv("waitForBlock", "waitForBlock", maxNumRequests),
   780  			)
   781  		}
   782  
   783  		// Add the blockRespEv step
   784  		testSteps = append(
   785  			testSteps,
   786  			sBlockRespEv("waitForBlock", "waitForBlock",
   787  				"P0", height, prevBlocks))
   788  		prevBlocks = append(prevBlocks, height)
   789  		height++
   790  		numBlocksReceived++
   791  
   792  		// Add the processedBlockEv step periodically.
   793  		if numBlocksReceived >= maxRequestsPerPeer || height >= numBlocks {
   794  			for j := int(height) - numBlocksReceived; j < int(height); j++ {
   795  				if j >= int(numBlocks) {
   796  					// This is the last block that is processed, we should be in "finished" state.
   797  					testSteps = append(
   798  						testSteps,
   799  						sProcessedBlockEv("waitForBlock", "finished", nil))
   800  					break forLoop
   801  				}
   802  				testSteps = append(
   803  					testSteps,
   804  					sProcessedBlockEv("waitForBlock", "waitForBlock", nil))
   805  			}
   806  			numBlocksReceived = 0
   807  			prevBlocks = make([]int64, 0, maxPendingRequests)
   808  		}
   809  	}
   810  
   811  	return testFields{
   812  		name:               testName,
   813  		startingHeight:     startingHeight,
   814  		maxRequestsPerPeer: maxRequestsPerPeer,
   815  		maxPendingRequests: maxPendingRequests,
   816  		steps:              testSteps,
   817  	}
   818  }
   819  
   820  const (
   821  	maxStartingHeightTest       = 100
   822  	maxRequestsPerPeerTest      = 20
   823  	maxTotalPendingRequestsTest = 600
   824  	maxNumPeersTest             = 1000
   825  	maxNumBlocksInChainTest     = 10000 // should be smaller than 9999999
   826  )
   827  
   828  func makeCorrectTransitionSequenceWithRandomParameters() testFields {
   829  	// Generate a starting height for fast sync.
   830  	startingHeight := int64(tmrand.Intn(maxStartingHeightTest) + 1)
   831  
   832  	// Generate the number of requests per peer.
   833  	maxRequestsPerPeer := tmrand.Intn(maxRequestsPerPeerTest) + 1
   834  
   835  	// Generate the maximum number of total pending requests, >= maxRequestsPerPeer.
   836  	maxPendingRequests := tmrand.Intn(maxTotalPendingRequestsTest-maxRequestsPerPeer) + maxRequestsPerPeer
   837  
   838  	// Generate the number of blocks to be synced.
   839  	numBlocks := int64(tmrand.Intn(maxNumBlocksInChainTest)) + startingHeight
   840  
   841  	// Generate a number of peers.
   842  	numPeers := tmrand.Intn(maxNumPeersTest) + 1
   843  
   844  	return makeCorrectTransitionSequence(startingHeight, numBlocks, numPeers, true, maxRequestsPerPeer, maxPendingRequests)
   845  }
   846  
   847  func shouldApplyProcessedBlockEvStep(step *fsmStepTestValues, testBcR *testReactor) bool {
   848  	if step.event == processedBlockEv {
   849  		_, err := testBcR.fsm.pool.BlockAndPeerAtHeight(testBcR.fsm.pool.Height)
   850  		if err == errMissingBlock {
   851  			return false
   852  		}
   853  		_, err = testBcR.fsm.pool.BlockAndPeerAtHeight(testBcR.fsm.pool.Height + 1)
   854  		if err == errMissingBlock {
   855  			return false
   856  		}
   857  	}
   858  	return true
   859  }
   860  
   861  func TestFSMCorrectTransitionSequences(t *testing.T) {
   862  
   863  	tests := []testFields{
   864  		makeCorrectTransitionSequence(1, 100, 10, true, 10, 40),
   865  		makeCorrectTransitionSequenceWithRandomParameters(),
   866  	}
   867  
   868  	for _, tt := range tests {
   869  		tt := tt
   870  		t.Run(tt.name, func(t *testing.T) {
   871  			// Create test reactor
   872  			testBcR := newTestReactor(tt.startingHeight)
   873  
   874  			if tt.maxRequestsPerPeer != 0 {
   875  				maxRequestsPerPeer = tt.maxRequestsPerPeer
   876  			}
   877  
   878  			for _, step := range tt.steps {
   879  				step := step
   880  				assert.Equal(t, step.currentState, testBcR.fsm.state.name)
   881  
   882  				oldNumStatusRequests := testBcR.numStatusRequests
   883  				fixBlockResponseEvStep(&step, testBcR)
   884  				if !shouldApplyProcessedBlockEvStep(&step, testBcR) {
   885  					continue
   886  				}
   887  
   888  				fsmErr := sendEventToFSM(testBcR.fsm, step.event, step.data)
   889  				assert.Equal(t, step.wantErr, fsmErr)
   890  
   891  				if step.wantStatusReqSent {
   892  					assert.Equal(t, oldNumStatusRequests+1, testBcR.numStatusRequests)
   893  				} else {
   894  					assert.Equal(t, oldNumStatusRequests, testBcR.numStatusRequests)
   895  				}
   896  
   897  				assert.Equal(t, step.wantState, testBcR.fsm.state.name)
   898  				if step.wantState == "finished" {
   899  					assert.True(t, testBcR.fsm.isCaughtUp())
   900  				}
   901  			}
   902  
   903  		})
   904  	}
   905  }
   906  
   907  // ----------------------------------------
   908  // implements the bcRNotifier
   909  func (testR *testReactor) sendPeerError(err error, peerID p2p.ID) {
   910  	testR.logger.Info("Reactor received sendPeerError call from FSM", "peer", peerID, "err", err)
   911  	testR.lastPeerError.peerID = peerID
   912  	testR.lastPeerError.err = err
   913  }
   914  
   915  func (testR *testReactor) sendStatusRequest() {
   916  	testR.logger.Info("Reactor received sendStatusRequest call from FSM")
   917  	testR.numStatusRequests++
   918  }
   919  
   920  func (testR *testReactor) sendBlockRequest(peerID p2p.ID, height int64) error {
   921  	testR.logger.Info("Reactor received sendBlockRequest call from FSM", "peer", peerID, "height", height)
   922  	testR.numBlockRequests++
   923  	testR.lastBlockRequest.peerID = peerID
   924  	testR.lastBlockRequest.height = height
   925  	if height == 9999999 {
   926  		// simulate switch does not have peer
   927  		return errNilPeerForBlockRequest
   928  	}
   929  	return nil
   930  }
   931  
   932  func (testR *testReactor) resetStateTimer(name string, timer **time.Timer, timeout time.Duration) {
   933  	testR.logger.Info("Reactor received resetStateTimer call from FSM", "state", name, "timeout", timeout)
   934  	if _, ok := testR.stateTimerStarts[name]; !ok {
   935  		testR.stateTimerStarts[name] = 1
   936  	} else {
   937  		testR.stateTimerStarts[name]++
   938  	}
   939  }
   940  
   941  func (testR *testReactor) switchToConsensus() {
   942  }
   943  
   944  // ----------------------------------------