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