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