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 // ----------------------------------------