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