github.com/MetalBlockchain/metalgo@v1.11.9/network/ip_tracker_test.go (about) 1 // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. 2 // See the file LICENSE for licensing terms. 3 4 package network 5 6 import ( 7 "testing" 8 9 "github.com/prometheus/client_golang/prometheus" 10 "github.com/prometheus/client_golang/prometheus/testutil" 11 "github.com/stretchr/testify/require" 12 13 "github.com/MetalBlockchain/metalgo/ids" 14 "github.com/MetalBlockchain/metalgo/utils/bloom" 15 "github.com/MetalBlockchain/metalgo/utils/ips" 16 "github.com/MetalBlockchain/metalgo/utils/logging" 17 ) 18 19 func newTestIPTracker(t *testing.T) *ipTracker { 20 tracker, err := newIPTracker(logging.NoLog{}, prometheus.NewRegistry()) 21 require.NoError(t, err) 22 return tracker 23 } 24 25 func newerTestIP(ip *ips.ClaimedIPPort) *ips.ClaimedIPPort { 26 return ips.NewClaimedIPPort( 27 ip.Cert, 28 ip.AddrPort, 29 ip.Timestamp+1, 30 ip.Signature, 31 ) 32 } 33 34 func requireEqual(t *testing.T, expected, actual *ipTracker) { 35 require := require.New(t) 36 require.Equal(expected.manuallyTracked, actual.manuallyTracked) 37 require.Equal(expected.manuallyGossipable, actual.manuallyGossipable) 38 require.Equal(expected.mostRecentTrackedIPs, actual.mostRecentTrackedIPs) 39 require.Equal(expected.trackedIDs, actual.trackedIDs) 40 require.Equal(expected.bloomAdditions, actual.bloomAdditions) 41 require.Equal(expected.maxBloomCount, actual.maxBloomCount) 42 require.Equal(expected.connected, actual.connected) 43 require.Equal(expected.gossipableIndices, actual.gossipableIndices) 44 require.Equal(expected.gossipableIPs, actual.gossipableIPs) 45 require.Equal(expected.gossipableIDs, actual.gossipableIDs) 46 } 47 48 func requireMetricsConsistent(t *testing.T, tracker *ipTracker) { 49 require := require.New(t) 50 require.Equal(float64(len(tracker.mostRecentTrackedIPs)), testutil.ToFloat64(tracker.numTrackedIPs)) 51 require.Equal(float64(len(tracker.gossipableIPs)), testutil.ToFloat64(tracker.numGossipableIPs)) 52 require.Equal(float64(tracker.bloom.Count()), testutil.ToFloat64(tracker.bloomMetrics.Count)) 53 require.Equal(float64(tracker.maxBloomCount), testutil.ToFloat64(tracker.bloomMetrics.MaxCount)) 54 } 55 56 func TestIPTracker_ManuallyTrack(t *testing.T) { 57 tests := []struct { 58 name string 59 initialState *ipTracker 60 nodeID ids.NodeID 61 expectedState *ipTracker 62 }{ 63 { 64 name: "non-connected non-validator", 65 initialState: newTestIPTracker(t), 66 nodeID: ip.NodeID, 67 expectedState: func() *ipTracker { 68 tracker := newTestIPTracker(t) 69 tracker.manuallyTracked.Add(ip.NodeID) 70 tracker.trackedIDs.Add(ip.NodeID) 71 return tracker 72 }(), 73 }, 74 { 75 name: "connected non-validator", 76 initialState: func() *ipTracker { 77 tracker := newTestIPTracker(t) 78 tracker.Connected(ip) 79 return tracker 80 }(), 81 nodeID: ip.NodeID, 82 expectedState: func() *ipTracker { 83 tracker := newTestIPTracker(t) 84 tracker.Connected(ip) 85 tracker.manuallyTracked.Add(ip.NodeID) 86 tracker.mostRecentTrackedIPs[ip.NodeID] = ip 87 tracker.trackedIDs.Add(ip.NodeID) 88 tracker.bloomAdditions[ip.NodeID] = 1 89 return tracker 90 }(), 91 }, 92 { 93 name: "non-connected validator", 94 initialState: func() *ipTracker { 95 tracker := newTestIPTracker(t) 96 tracker.OnValidatorAdded(ip.NodeID, nil, ids.Empty, 0) 97 return tracker 98 }(), 99 nodeID: ip.NodeID, 100 expectedState: func() *ipTracker { 101 tracker := newTestIPTracker(t) 102 tracker.OnValidatorAdded(ip.NodeID, nil, ids.Empty, 0) 103 tracker.manuallyTracked.Add(ip.NodeID) 104 return tracker 105 }(), 106 }, 107 { 108 name: "connected validator", 109 initialState: func() *ipTracker { 110 tracker := newTestIPTracker(t) 111 tracker.Connected(ip) 112 tracker.OnValidatorAdded(ip.NodeID, nil, ids.Empty, 0) 113 return tracker 114 }(), 115 nodeID: ip.NodeID, 116 expectedState: func() *ipTracker { 117 tracker := newTestIPTracker(t) 118 tracker.Connected(ip) 119 tracker.OnValidatorAdded(ip.NodeID, nil, ids.Empty, 0) 120 tracker.manuallyTracked.Add(ip.NodeID) 121 return tracker 122 }(), 123 }, 124 } 125 for _, test := range tests { 126 t.Run(test.name, func(t *testing.T) { 127 test.initialState.ManuallyTrack(test.nodeID) 128 requireEqual(t, test.expectedState, test.initialState) 129 requireMetricsConsistent(t, test.initialState) 130 }) 131 } 132 } 133 134 func TestIPTracker_ManuallyGossip(t *testing.T) { 135 tests := []struct { 136 name string 137 initialState *ipTracker 138 nodeID ids.NodeID 139 expectedState *ipTracker 140 }{ 141 { 142 name: "non-connected non-validator", 143 initialState: newTestIPTracker(t), 144 nodeID: ip.NodeID, 145 expectedState: func() *ipTracker { 146 tracker := newTestIPTracker(t) 147 tracker.manuallyTracked.Add(ip.NodeID) 148 tracker.manuallyGossipable.Add(ip.NodeID) 149 tracker.trackedIDs.Add(ip.NodeID) 150 tracker.gossipableIDs.Add(ip.NodeID) 151 return tracker 152 }(), 153 }, 154 { 155 name: "connected non-validator", 156 initialState: func() *ipTracker { 157 tracker := newTestIPTracker(t) 158 tracker.Connected(ip) 159 return tracker 160 }(), 161 nodeID: ip.NodeID, 162 expectedState: func() *ipTracker { 163 tracker := newTestIPTracker(t) 164 tracker.Connected(ip) 165 tracker.manuallyTracked.Add(ip.NodeID) 166 tracker.manuallyGossipable.Add(ip.NodeID) 167 tracker.mostRecentTrackedIPs[ip.NodeID] = ip 168 tracker.trackedIDs.Add(ip.NodeID) 169 tracker.bloomAdditions[ip.NodeID] = 1 170 tracker.gossipableIndices[ip.NodeID] = 0 171 tracker.gossipableIPs = []*ips.ClaimedIPPort{ 172 ip, 173 } 174 tracker.gossipableIDs.Add(ip.NodeID) 175 return tracker 176 }(), 177 }, 178 { 179 name: "non-connected validator", 180 initialState: func() *ipTracker { 181 tracker := newTestIPTracker(t) 182 tracker.OnValidatorAdded(ip.NodeID, nil, ids.Empty, 0) 183 return tracker 184 }(), 185 nodeID: ip.NodeID, 186 expectedState: func() *ipTracker { 187 tracker := newTestIPTracker(t) 188 tracker.OnValidatorAdded(ip.NodeID, nil, ids.Empty, 0) 189 tracker.manuallyTracked.Add(ip.NodeID) 190 tracker.manuallyGossipable.Add(ip.NodeID) 191 return tracker 192 }(), 193 }, 194 { 195 name: "connected validator", 196 initialState: func() *ipTracker { 197 tracker := newTestIPTracker(t) 198 tracker.Connected(ip) 199 tracker.OnValidatorAdded(ip.NodeID, nil, ids.Empty, 0) 200 return tracker 201 }(), 202 nodeID: ip.NodeID, 203 expectedState: func() *ipTracker { 204 tracker := newTestIPTracker(t) 205 tracker.Connected(ip) 206 tracker.OnValidatorAdded(ip.NodeID, nil, ids.Empty, 0) 207 tracker.manuallyTracked.Add(ip.NodeID) 208 tracker.manuallyGossipable.Add(ip.NodeID) 209 return tracker 210 }(), 211 }, 212 } 213 for _, test := range tests { 214 t.Run(test.name, func(t *testing.T) { 215 test.initialState.ManuallyGossip(test.nodeID) 216 requireEqual(t, test.expectedState, test.initialState) 217 requireMetricsConsistent(t, test.initialState) 218 }) 219 } 220 } 221 222 func TestIPTracker_AddIP(t *testing.T) { 223 newerIP := newerTestIP(ip) 224 tests := []struct { 225 name string 226 initialState *ipTracker 227 ip *ips.ClaimedIPPort 228 expectedUpdated bool 229 expectedState *ipTracker 230 }{ 231 { 232 name: "non-validator", 233 initialState: newTestIPTracker(t), 234 ip: ip, 235 expectedUpdated: false, 236 expectedState: newTestIPTracker(t), 237 }, 238 { 239 name: "first known IP", 240 initialState: func() *ipTracker { 241 tracker := newTestIPTracker(t) 242 tracker.OnValidatorAdded(ip.NodeID, nil, ids.Empty, 0) 243 return tracker 244 }(), 245 ip: ip, 246 expectedUpdated: true, 247 expectedState: func() *ipTracker { 248 tracker := newTestIPTracker(t) 249 tracker.OnValidatorAdded(ip.NodeID, nil, ids.Empty, 0) 250 tracker.mostRecentTrackedIPs[ip.NodeID] = ip 251 tracker.bloomAdditions[ip.NodeID] = 1 252 return tracker 253 }(), 254 }, 255 { 256 name: "older IP", 257 initialState: func() *ipTracker { 258 tracker := newTestIPTracker(t) 259 tracker.OnValidatorAdded(ip.NodeID, nil, ids.Empty, 0) 260 require.True(t, tracker.AddIP(newerIP)) 261 return tracker 262 }(), 263 ip: ip, 264 expectedUpdated: false, 265 expectedState: func() *ipTracker { 266 tracker := newTestIPTracker(t) 267 tracker.OnValidatorAdded(ip.NodeID, nil, ids.Empty, 0) 268 require.True(t, tracker.AddIP(newerIP)) 269 return tracker 270 }(), 271 }, 272 { 273 name: "same IP", 274 initialState: func() *ipTracker { 275 tracker := newTestIPTracker(t) 276 tracker.OnValidatorAdded(ip.NodeID, nil, ids.Empty, 0) 277 require.True(t, tracker.AddIP(ip)) 278 return tracker 279 }(), 280 ip: ip, 281 expectedUpdated: false, 282 expectedState: func() *ipTracker { 283 tracker := newTestIPTracker(t) 284 tracker.OnValidatorAdded(ip.NodeID, nil, ids.Empty, 0) 285 require.True(t, tracker.AddIP(ip)) 286 return tracker 287 }(), 288 }, 289 { 290 name: "disconnected newer IP", 291 initialState: func() *ipTracker { 292 tracker := newTestIPTracker(t) 293 tracker.OnValidatorAdded(ip.NodeID, nil, ids.Empty, 0) 294 require.True(t, tracker.AddIP(ip)) 295 return tracker 296 }(), 297 ip: newerIP, 298 expectedUpdated: true, 299 expectedState: func() *ipTracker { 300 tracker := newTestIPTracker(t) 301 tracker.OnValidatorAdded(ip.NodeID, nil, ids.Empty, 0) 302 require.True(t, tracker.AddIP(ip)) 303 tracker.mostRecentTrackedIPs[newerIP.NodeID] = newerIP 304 tracker.bloomAdditions[newerIP.NodeID] = 2 305 return tracker 306 }(), 307 }, 308 { 309 name: "connected newer IP", 310 initialState: func() *ipTracker { 311 tracker := newTestIPTracker(t) 312 tracker.OnValidatorAdded(ip.NodeID, nil, ids.Empty, 0) 313 tracker.Connected(ip) 314 return tracker 315 }(), 316 ip: newerIP, 317 expectedUpdated: true, 318 expectedState: func() *ipTracker { 319 tracker := newTestIPTracker(t) 320 tracker.OnValidatorAdded(ip.NodeID, nil, ids.Empty, 0) 321 tracker.Connected(ip) 322 tracker.mostRecentTrackedIPs[newerIP.NodeID] = newerIP 323 tracker.bloomAdditions[newerIP.NodeID] = 2 324 delete(tracker.gossipableIndices, newerIP.NodeID) 325 tracker.gossipableIPs = tracker.gossipableIPs[:0] 326 return tracker 327 }(), 328 }, 329 } 330 for _, test := range tests { 331 t.Run(test.name, func(t *testing.T) { 332 updated := test.initialState.AddIP(test.ip) 333 require.Equal(t, test.expectedUpdated, updated) 334 requireEqual(t, test.expectedState, test.initialState) 335 requireMetricsConsistent(t, test.initialState) 336 }) 337 } 338 } 339 340 func TestIPTracker_Connected(t *testing.T) { 341 newerIP := newerTestIP(ip) 342 tests := []struct { 343 name string 344 initialState *ipTracker 345 ip *ips.ClaimedIPPort 346 expectedState *ipTracker 347 }{ 348 { 349 name: "non-validator", 350 initialState: newTestIPTracker(t), 351 ip: ip, 352 expectedState: func() *ipTracker { 353 tracker := newTestIPTracker(t) 354 tracker.connected[ip.NodeID] = ip 355 return tracker 356 }(), 357 }, 358 { 359 name: "first known IP", 360 initialState: func() *ipTracker { 361 tracker := newTestIPTracker(t) 362 tracker.OnValidatorAdded(ip.NodeID, nil, ids.Empty, 0) 363 return tracker 364 }(), 365 ip: ip, 366 expectedState: func() *ipTracker { 367 tracker := newTestIPTracker(t) 368 tracker.OnValidatorAdded(ip.NodeID, nil, ids.Empty, 0) 369 tracker.mostRecentTrackedIPs[ip.NodeID] = ip 370 tracker.bloomAdditions[ip.NodeID] = 1 371 tracker.connected[ip.NodeID] = ip 372 tracker.gossipableIndices[ip.NodeID] = 0 373 tracker.gossipableIPs = []*ips.ClaimedIPPort{ 374 ip, 375 } 376 return tracker 377 }(), 378 }, 379 { 380 name: "connected with older IP", 381 initialState: func() *ipTracker { 382 tracker := newTestIPTracker(t) 383 tracker.OnValidatorAdded(ip.NodeID, nil, ids.Empty, 0) 384 require.True(t, tracker.AddIP(newerIP)) 385 return tracker 386 }(), 387 ip: ip, 388 expectedState: func() *ipTracker { 389 tracker := newTestIPTracker(t) 390 tracker.OnValidatorAdded(ip.NodeID, nil, ids.Empty, 0) 391 require.True(t, tracker.AddIP(newerIP)) 392 tracker.connected[ip.NodeID] = ip 393 return tracker 394 }(), 395 }, 396 { 397 name: "connected with newer IP", 398 initialState: func() *ipTracker { 399 tracker := newTestIPTracker(t) 400 tracker.OnValidatorAdded(ip.NodeID, nil, ids.Empty, 0) 401 require.True(t, tracker.AddIP(ip)) 402 return tracker 403 }(), 404 ip: newerIP, 405 expectedState: func() *ipTracker { 406 tracker := newTestIPTracker(t) 407 tracker.OnValidatorAdded(ip.NodeID, nil, ids.Empty, 0) 408 require.True(t, tracker.AddIP(ip)) 409 tracker.mostRecentTrackedIPs[newerIP.NodeID] = newerIP 410 tracker.bloomAdditions[newerIP.NodeID] = 2 411 tracker.connected[newerIP.NodeID] = newerIP 412 tracker.gossipableIndices[newerIP.NodeID] = 0 413 tracker.gossipableIPs = []*ips.ClaimedIPPort{ 414 newerIP, 415 } 416 return tracker 417 }(), 418 }, 419 { 420 name: "connected with same IP", 421 initialState: func() *ipTracker { 422 tracker := newTestIPTracker(t) 423 tracker.OnValidatorAdded(ip.NodeID, nil, ids.Empty, 0) 424 require.True(t, tracker.AddIP(ip)) 425 return tracker 426 }(), 427 ip: ip, 428 expectedState: func() *ipTracker { 429 tracker := newTestIPTracker(t) 430 tracker.OnValidatorAdded(ip.NodeID, nil, ids.Empty, 0) 431 require.True(t, tracker.AddIP(ip)) 432 tracker.connected[ip.NodeID] = ip 433 tracker.gossipableIndices[ip.NodeID] = 0 434 tracker.gossipableIPs = []*ips.ClaimedIPPort{ 435 ip, 436 } 437 return tracker 438 }(), 439 }, 440 } 441 for _, test := range tests { 442 t.Run(test.name, func(t *testing.T) { 443 test.initialState.Connected(test.ip) 444 requireEqual(t, test.expectedState, test.initialState) 445 requireMetricsConsistent(t, test.initialState) 446 }) 447 } 448 } 449 450 func TestIPTracker_Disconnected(t *testing.T) { 451 tests := []struct { 452 name string 453 initialState *ipTracker 454 nodeID ids.NodeID 455 expectedState *ipTracker 456 }{ 457 { 458 name: "not tracked", 459 initialState: func() *ipTracker { 460 tracker := newTestIPTracker(t) 461 tracker.Connected(ip) 462 return tracker 463 }(), 464 nodeID: ip.NodeID, 465 expectedState: newTestIPTracker(t), 466 }, 467 { 468 name: "not gossipable", 469 initialState: func() *ipTracker { 470 tracker := newTestIPTracker(t) 471 tracker.Connected(ip) 472 tracker.ManuallyTrack(ip.NodeID) 473 return tracker 474 }(), 475 nodeID: ip.NodeID, 476 expectedState: func() *ipTracker { 477 tracker := newTestIPTracker(t) 478 tracker.Connected(ip) 479 tracker.ManuallyTrack(ip.NodeID) 480 delete(tracker.connected, ip.NodeID) 481 return tracker 482 }(), 483 }, 484 { 485 name: "latest gossipable", 486 initialState: func() *ipTracker { 487 tracker := newTestIPTracker(t) 488 tracker.OnValidatorAdded(ip.NodeID, nil, ids.Empty, 0) 489 tracker.Connected(ip) 490 return tracker 491 }(), 492 nodeID: ip.NodeID, 493 expectedState: func() *ipTracker { 494 tracker := newTestIPTracker(t) 495 tracker.OnValidatorAdded(ip.NodeID, nil, ids.Empty, 0) 496 tracker.Connected(ip) 497 delete(tracker.connected, ip.NodeID) 498 delete(tracker.gossipableIndices, ip.NodeID) 499 tracker.gossipableIPs = tracker.gossipableIPs[:0] 500 return tracker 501 }(), 502 }, 503 { 504 name: "non-latest gossipable", 505 initialState: func() *ipTracker { 506 tracker := newTestIPTracker(t) 507 tracker.OnValidatorAdded(ip.NodeID, nil, ids.Empty, 0) 508 tracker.Connected(ip) 509 tracker.OnValidatorAdded(otherIP.NodeID, nil, ids.Empty, 0) 510 tracker.Connected(otherIP) 511 return tracker 512 }(), 513 nodeID: ip.NodeID, 514 expectedState: func() *ipTracker { 515 tracker := newTestIPTracker(t) 516 tracker.OnValidatorAdded(ip.NodeID, nil, ids.Empty, 0) 517 tracker.Connected(ip) 518 tracker.OnValidatorAdded(otherIP.NodeID, nil, ids.Empty, 0) 519 tracker.Connected(otherIP) 520 delete(tracker.connected, ip.NodeID) 521 tracker.gossipableIndices = map[ids.NodeID]int{ 522 otherIP.NodeID: 0, 523 } 524 tracker.gossipableIPs = []*ips.ClaimedIPPort{ 525 otherIP, 526 } 527 return tracker 528 }(), 529 }, 530 } 531 for _, test := range tests { 532 t.Run(test.name, func(t *testing.T) { 533 test.initialState.Disconnected(test.nodeID) 534 requireEqual(t, test.expectedState, test.initialState) 535 requireMetricsConsistent(t, test.initialState) 536 }) 537 } 538 } 539 540 func TestIPTracker_OnValidatorAdded(t *testing.T) { 541 newerIP := newerTestIP(ip) 542 543 tests := []struct { 544 name string 545 initialState *ipTracker 546 nodeID ids.NodeID 547 expectedState *ipTracker 548 }{ 549 { 550 name: "manually tracked", 551 initialState: func() *ipTracker { 552 tracker := newTestIPTracker(t) 553 tracker.ManuallyTrack(ip.NodeID) 554 return tracker 555 }(), 556 nodeID: ip.NodeID, 557 expectedState: func() *ipTracker { 558 tracker := newTestIPTracker(t) 559 tracker.ManuallyTrack(ip.NodeID) 560 tracker.gossipableIDs.Add(ip.NodeID) 561 return tracker 562 }(), 563 }, 564 { 565 name: "manually tracked and connected with older IP", 566 initialState: func() *ipTracker { 567 tracker := newTestIPTracker(t) 568 tracker.ManuallyTrack(ip.NodeID) 569 tracker.Connected(ip) 570 require.True(t, tracker.AddIP(newerIP)) 571 return tracker 572 }(), 573 nodeID: ip.NodeID, 574 expectedState: func() *ipTracker { 575 tracker := newTestIPTracker(t) 576 tracker.ManuallyTrack(ip.NodeID) 577 tracker.Connected(ip) 578 require.True(t, tracker.AddIP(newerIP)) 579 tracker.gossipableIDs.Add(ip.NodeID) 580 return tracker 581 }(), 582 }, 583 { 584 name: "manually gossiped", 585 initialState: func() *ipTracker { 586 tracker := newTestIPTracker(t) 587 tracker.ManuallyGossip(ip.NodeID) 588 return tracker 589 }(), 590 nodeID: ip.NodeID, 591 expectedState: func() *ipTracker { 592 tracker := newTestIPTracker(t) 593 tracker.ManuallyGossip(ip.NodeID) 594 return tracker 595 }(), 596 }, 597 { 598 name: "disconnected", 599 initialState: newTestIPTracker(t), 600 nodeID: ip.NodeID, 601 expectedState: func() *ipTracker { 602 tracker := newTestIPTracker(t) 603 tracker.trackedIDs.Add(ip.NodeID) 604 tracker.gossipableIDs.Add(ip.NodeID) 605 return tracker 606 }(), 607 }, 608 { 609 name: "connected", 610 initialState: func() *ipTracker { 611 tracker := newTestIPTracker(t) 612 tracker.Connected(ip) 613 return tracker 614 }(), 615 nodeID: ip.NodeID, 616 expectedState: func() *ipTracker { 617 tracker := newTestIPTracker(t) 618 tracker.Connected(ip) 619 tracker.mostRecentTrackedIPs[ip.NodeID] = ip 620 tracker.trackedIDs.Add(ip.NodeID) 621 tracker.bloomAdditions[ip.NodeID] = 1 622 tracker.gossipableIndices[ip.NodeID] = 0 623 tracker.gossipableIPs = []*ips.ClaimedIPPort{ 624 ip, 625 } 626 tracker.gossipableIDs.Add(ip.NodeID) 627 return tracker 628 }(), 629 }, 630 } 631 for _, test := range tests { 632 t.Run(test.name, func(t *testing.T) { 633 test.initialState.OnValidatorAdded(test.nodeID, nil, ids.Empty, 0) 634 requireEqual(t, test.expectedState, test.initialState) 635 requireMetricsConsistent(t, test.initialState) 636 }) 637 } 638 } 639 640 func TestIPTracker_OnValidatorRemoved(t *testing.T) { 641 tests := []struct { 642 name string 643 initialState *ipTracker 644 nodeID ids.NodeID 645 expectedState *ipTracker 646 }{ 647 { 648 name: "manually tracked not gossipable", 649 initialState: func() *ipTracker { 650 tracker := newTestIPTracker(t) 651 tracker.ManuallyTrack(ip.NodeID) 652 tracker.OnValidatorAdded(ip.NodeID, nil, ids.Empty, 0) 653 require.True(t, tracker.AddIP(ip)) 654 return tracker 655 }(), 656 nodeID: ip.NodeID, 657 expectedState: func() *ipTracker { 658 tracker := newTestIPTracker(t) 659 tracker.ManuallyTrack(ip.NodeID) 660 tracker.OnValidatorAdded(ip.NodeID, nil, ids.Empty, 0) 661 require.True(t, tracker.AddIP(ip)) 662 tracker.gossipableIDs.Remove(ip.NodeID) 663 return tracker 664 }(), 665 }, 666 { 667 name: "manually tracked latest gossipable", 668 initialState: func() *ipTracker { 669 tracker := newTestIPTracker(t) 670 tracker.ManuallyTrack(ip.NodeID) 671 tracker.OnValidatorAdded(ip.NodeID, nil, ids.Empty, 0) 672 tracker.Connected(ip) 673 return tracker 674 }(), 675 nodeID: ip.NodeID, 676 expectedState: func() *ipTracker { 677 tracker := newTestIPTracker(t) 678 tracker.ManuallyTrack(ip.NodeID) 679 tracker.OnValidatorAdded(ip.NodeID, nil, ids.Empty, 0) 680 tracker.Connected(ip) 681 delete(tracker.gossipableIndices, ip.NodeID) 682 tracker.gossipableIPs = tracker.gossipableIPs[:0] 683 tracker.gossipableIDs.Remove(ip.NodeID) 684 return tracker 685 }(), 686 }, 687 { 688 name: "manually gossiped", 689 initialState: func() *ipTracker { 690 tracker := newTestIPTracker(t) 691 tracker.ManuallyGossip(ip.NodeID) 692 tracker.OnValidatorAdded(ip.NodeID, nil, ids.Empty, 0) 693 tracker.Connected(ip) 694 return tracker 695 }(), 696 nodeID: ip.NodeID, 697 expectedState: func() *ipTracker { 698 tracker := newTestIPTracker(t) 699 tracker.ManuallyGossip(ip.NodeID) 700 tracker.OnValidatorAdded(ip.NodeID, nil, ids.Empty, 0) 701 tracker.Connected(ip) 702 return tracker 703 }(), 704 }, 705 { 706 name: "not gossipable", 707 initialState: func() *ipTracker { 708 tracker := newTestIPTracker(t) 709 tracker.OnValidatorAdded(ip.NodeID, nil, ids.Empty, 0) 710 require.True(t, tracker.AddIP(ip)) 711 return tracker 712 }(), 713 nodeID: ip.NodeID, 714 expectedState: func() *ipTracker { 715 tracker := newTestIPTracker(t) 716 tracker.OnValidatorAdded(ip.NodeID, nil, ids.Empty, 0) 717 require.True(t, tracker.AddIP(ip)) 718 delete(tracker.mostRecentTrackedIPs, ip.NodeID) 719 tracker.trackedIDs.Remove(ip.NodeID) 720 tracker.gossipableIDs.Remove(ip.NodeID) 721 return tracker 722 }(), 723 }, 724 { 725 name: "latest gossipable", 726 initialState: func() *ipTracker { 727 tracker := newTestIPTracker(t) 728 tracker.OnValidatorAdded(ip.NodeID, nil, ids.Empty, 0) 729 tracker.Connected(ip) 730 return tracker 731 }(), 732 nodeID: ip.NodeID, 733 expectedState: func() *ipTracker { 734 tracker := newTestIPTracker(t) 735 tracker.OnValidatorAdded(ip.NodeID, nil, ids.Empty, 0) 736 tracker.Connected(ip) 737 delete(tracker.mostRecentTrackedIPs, ip.NodeID) 738 tracker.trackedIDs.Remove(ip.NodeID) 739 delete(tracker.gossipableIndices, ip.NodeID) 740 tracker.gossipableIPs = tracker.gossipableIPs[:0] 741 tracker.gossipableIDs.Remove(ip.NodeID) 742 return tracker 743 }(), 744 }, 745 { 746 name: "non-latest gossipable", 747 initialState: func() *ipTracker { 748 tracker := newTestIPTracker(t) 749 tracker.OnValidatorAdded(ip.NodeID, nil, ids.Empty, 0) 750 tracker.Connected(ip) 751 tracker.OnValidatorAdded(otherIP.NodeID, nil, ids.Empty, 0) 752 tracker.Connected(otherIP) 753 return tracker 754 }(), 755 nodeID: ip.NodeID, 756 expectedState: func() *ipTracker { 757 tracker := newTestIPTracker(t) 758 tracker.OnValidatorAdded(ip.NodeID, nil, ids.Empty, 0) 759 tracker.Connected(ip) 760 tracker.OnValidatorAdded(otherIP.NodeID, nil, ids.Empty, 0) 761 tracker.Connected(otherIP) 762 delete(tracker.mostRecentTrackedIPs, ip.NodeID) 763 tracker.trackedIDs.Remove(ip.NodeID) 764 tracker.gossipableIndices = map[ids.NodeID]int{ 765 otherIP.NodeID: 0, 766 } 767 tracker.gossipableIPs = []*ips.ClaimedIPPort{ 768 otherIP, 769 } 770 tracker.gossipableIDs.Remove(ip.NodeID) 771 return tracker 772 }(), 773 }, 774 } 775 for _, test := range tests { 776 t.Run(test.name, func(t *testing.T) { 777 test.initialState.OnValidatorRemoved(test.nodeID, 0) 778 requireEqual(t, test.expectedState, test.initialState) 779 requireMetricsConsistent(t, test.initialState) 780 }) 781 } 782 } 783 784 func TestIPTracker_GetGossipableIPs(t *testing.T) { 785 require := require.New(t) 786 787 tracker := newTestIPTracker(t) 788 tracker.Connected(ip) 789 tracker.Connected(otherIP) 790 tracker.OnValidatorAdded(ip.NodeID, nil, ids.Empty, 0) 791 tracker.OnValidatorAdded(otherIP.NodeID, nil, ids.Empty, 0) 792 793 gossipableIPs := tracker.GetGossipableIPs(ids.EmptyNodeID, bloom.EmptyFilter, nil, 2) 794 require.ElementsMatch([]*ips.ClaimedIPPort{ip, otherIP}, gossipableIPs) 795 796 gossipableIPs = tracker.GetGossipableIPs(ip.NodeID, bloom.EmptyFilter, nil, 2) 797 require.Equal([]*ips.ClaimedIPPort{otherIP}, gossipableIPs) 798 799 gossipableIPs = tracker.GetGossipableIPs(ids.EmptyNodeID, bloom.FullFilter, nil, 2) 800 require.Empty(gossipableIPs) 801 802 filter, err := bloom.New(8, 1024) 803 require.NoError(err) 804 bloom.Add(filter, ip.GossipID[:], nil) 805 806 readFilter, err := bloom.Parse(filter.Marshal()) 807 require.NoError(err) 808 809 gossipableIPs = tracker.GetGossipableIPs(ip.NodeID, readFilter, nil, 2) 810 require.Equal([]*ips.ClaimedIPPort{otherIP}, gossipableIPs) 811 } 812 813 func TestIPTracker_BloomFiltersEverything(t *testing.T) { 814 require := require.New(t) 815 816 tracker := newTestIPTracker(t) 817 tracker.Connected(ip) 818 tracker.Connected(otherIP) 819 tracker.OnValidatorAdded(ip.NodeID, nil, ids.Empty, 0) 820 tracker.OnValidatorAdded(otherIP.NodeID, nil, ids.Empty, 0) 821 822 bloomBytes, salt := tracker.Bloom() 823 readFilter, err := bloom.Parse(bloomBytes) 824 require.NoError(err) 825 826 gossipableIPs := tracker.GetGossipableIPs(ids.EmptyNodeID, readFilter, salt, 2) 827 require.Empty(gossipableIPs) 828 829 require.NoError(tracker.ResetBloom()) 830 } 831 832 func TestIPTracker_BloomGrows(t *testing.T) { 833 tests := []struct { 834 name string 835 add func(tracker *ipTracker) 836 }{ 837 { 838 name: "Add Validator", 839 add: func(tracker *ipTracker) { 840 tracker.OnValidatorAdded(ids.GenerateTestNodeID(), nil, ids.Empty, 0) 841 }, 842 }, 843 { 844 name: "Manually Track", 845 add: func(tracker *ipTracker) { 846 tracker.ManuallyTrack(ids.GenerateTestNodeID()) 847 }, 848 }, 849 } 850 for _, test := range tests { 851 t.Run(test.name, func(t *testing.T) { 852 require := require.New(t) 853 854 tracker := newTestIPTracker(t) 855 initialMaxBloomCount := tracker.maxBloomCount 856 for i := 0; i < 2048; i++ { 857 test.add(tracker) 858 } 859 requireMetricsConsistent(t, tracker) 860 861 require.NoError(tracker.ResetBloom()) 862 require.Greater(tracker.maxBloomCount, initialMaxBloomCount) 863 requireMetricsConsistent(t, tracker) 864 }) 865 } 866 } 867 868 func TestIPTracker_BloomResetsDynamically(t *testing.T) { 869 require := require.New(t) 870 871 tracker := newTestIPTracker(t) 872 tracker.Connected(ip) 873 tracker.OnValidatorAdded(ip.NodeID, nil, ids.Empty, 0) 874 tracker.OnValidatorRemoved(ip.NodeID, 0) 875 tracker.maxBloomCount = 1 876 tracker.Connected(otherIP) 877 tracker.OnValidatorAdded(otherIP.NodeID, nil, ids.Empty, 0) 878 requireMetricsConsistent(t, tracker) 879 880 bloomBytes, salt := tracker.Bloom() 881 readFilter, err := bloom.Parse(bloomBytes) 882 require.NoError(err) 883 884 require.False(bloom.Contains(readFilter, ip.GossipID[:], salt)) 885 require.True(bloom.Contains(readFilter, otherIP.GossipID[:], salt)) 886 } 887 888 func TestIPTracker_PreventBloomFilterAddition(t *testing.T) { 889 require := require.New(t) 890 891 newerIP := newerTestIP(ip) 892 newestIP := newerTestIP(newerIP) 893 894 tracker := newTestIPTracker(t) 895 tracker.ManuallyGossip(ip.NodeID) 896 require.True(tracker.AddIP(ip)) 897 require.True(tracker.AddIP(newerIP)) 898 require.True(tracker.AddIP(newestIP)) 899 require.Equal(maxIPEntriesPerNode, tracker.bloomAdditions[ip.NodeID]) 900 requireMetricsConsistent(t, tracker) 901 } 902 903 func TestIPTracker_ShouldVerifyIP(t *testing.T) { 904 require := require.New(t) 905 906 newerIP := newerTestIP(ip) 907 908 tracker := newTestIPTracker(t) 909 require.False(tracker.ShouldVerifyIP(ip)) 910 tracker.ManuallyTrack(ip.NodeID) 911 require.True(tracker.ShouldVerifyIP(ip)) 912 tracker.ManuallyGossip(ip.NodeID) 913 require.True(tracker.ShouldVerifyIP(ip)) 914 require.True(tracker.AddIP(ip)) 915 require.False(tracker.ShouldVerifyIP(ip)) 916 require.True(tracker.ShouldVerifyIP(newerIP)) 917 }