gitlab.com/jokerrs1/Sia@v1.3.2/modules/gateway/peers_test.go (about) 1 package gateway 2 3 import ( 4 "errors" 5 "fmt" 6 "net" 7 "strconv" 8 "strings" 9 "testing" 10 "time" 11 12 "github.com/NebulousLabs/Sia/build" 13 "github.com/NebulousLabs/Sia/encoding" 14 "github.com/NebulousLabs/Sia/modules" 15 "github.com/NebulousLabs/Sia/types" 16 "github.com/NebulousLabs/fastrand" 17 ) 18 19 // dummyConn implements the net.Conn interface, but does not carry any actual 20 // data. 21 type dummyConn struct { 22 net.Conn 23 } 24 25 func (dc *dummyConn) Read(p []byte) (int, error) { return len(p), nil } 26 func (dc *dummyConn) Write(p []byte) (int, error) { return len(p), nil } 27 func (dc *dummyConn) Close() error { return nil } 28 func (dc *dummyConn) SetReadDeadline(time.Time) error { return nil } 29 func (dc *dummyConn) SetWriteDeadline(time.Time) error { return nil } 30 31 // TestAddPeer tries adding a peer to the gateway. 32 func TestAddPeer(t *testing.T) { 33 if testing.Short() { 34 t.SkipNow() 35 } 36 t.Parallel() 37 g := newTestingGateway(t) 38 defer g.Close() 39 40 g.mu.Lock() 41 defer g.mu.Unlock() 42 g.addPeer(&peer{ 43 Peer: modules.Peer{ 44 NetAddress: "foo.com:123", 45 }, 46 sess: newClientStream(new(dummyConn), build.Version), 47 }) 48 if len(g.peers) != 1 { 49 t.Fatal("gateway did not add peer") 50 } 51 } 52 53 // TestAcceptPeer tests that acceptPeer does't kick outbound or local peers. 54 func TestAcceptPeer(t *testing.T) { 55 if testing.Short() { 56 t.SkipNow() 57 } 58 t.Parallel() 59 g := newTestingGateway(t) 60 defer g.Close() 61 g.mu.Lock() 62 defer g.mu.Unlock() 63 64 // Add only unkickable peers. 65 var unkickablePeers []*peer 66 for i := 0; i < fullyConnectedThreshold+1; i++ { 67 addr := modules.NetAddress(fmt.Sprintf("1.2.3.%d", i)) 68 p := &peer{ 69 Peer: modules.Peer{ 70 NetAddress: addr, 71 Inbound: false, 72 Local: false, 73 }, 74 sess: newClientStream(new(dummyConn), build.Version), 75 } 76 unkickablePeers = append(unkickablePeers, p) 77 } 78 for i := 0; i < fullyConnectedThreshold+1; i++ { 79 addr := modules.NetAddress(fmt.Sprintf("127.0.0.1:%d", i)) 80 p := &peer{ 81 Peer: modules.Peer{ 82 NetAddress: addr, 83 Inbound: true, 84 Local: true, 85 }, 86 sess: newClientStream(new(dummyConn), build.Version), 87 } 88 unkickablePeers = append(unkickablePeers, p) 89 } 90 for _, p := range unkickablePeers { 91 g.addPeer(p) 92 } 93 94 // Test that accepting another peer doesn't kick any of the peers. 95 g.acceptPeer(&peer{ 96 Peer: modules.Peer{ 97 NetAddress: "9.9.9.9", 98 Inbound: true, 99 }, 100 sess: newClientStream(new(dummyConn), build.Version), 101 }) 102 for _, p := range unkickablePeers { 103 if _, exists := g.peers[p.NetAddress]; !exists { 104 t.Error("accept peer kicked an outbound or local peer") 105 } 106 } 107 108 // Add a kickable peer. 109 g.addPeer(&peer{ 110 Peer: modules.Peer{ 111 NetAddress: "9.9.9.9", 112 Inbound: true, 113 }, 114 sess: newClientStream(new(dummyConn), build.Version), 115 }) 116 // Test that accepting a local peer will kick a kickable peer. 117 g.acceptPeer(&peer{ 118 Peer: modules.Peer{ 119 NetAddress: "127.0.0.1:99", 120 Inbound: true, 121 Local: true, 122 }, 123 sess: newClientStream(new(dummyConn), build.Version), 124 }) 125 if _, exists := g.peers["9.9.9.9"]; exists { 126 t.Error("acceptPeer didn't kick a peer to make room for a local peer") 127 } 128 } 129 130 // TestRandomInbountPeer checks that randomOutboundPeer returns the correct 131 // peer. 132 func TestRandomOutboundPeer(t *testing.T) { 133 if testing.Short() { 134 t.SkipNow() 135 } 136 t.Parallel() 137 g := newTestingGateway(t) 138 defer g.Close() 139 g.mu.Lock() 140 defer g.mu.Unlock() 141 142 _, err := g.randomOutboundPeer() 143 if err != errNoPeers { 144 t.Fatal("expected errNoPeers, got", err) 145 } 146 147 g.addPeer(&peer{ 148 Peer: modules.Peer{ 149 NetAddress: "foo.com:123", 150 Inbound: false, 151 }, 152 sess: newClientStream(new(dummyConn), build.Version), 153 }) 154 if len(g.peers) != 1 { 155 t.Fatal("gateway did not add peer") 156 } 157 addr, err := g.randomOutboundPeer() 158 if err != nil || addr != "foo.com:123" { 159 t.Fatal("gateway did not select random peer") 160 } 161 } 162 163 // TestListen is a general test probling the connection listener. 164 func TestListen(t *testing.T) { 165 if testing.Short() { 166 t.SkipNow() 167 } 168 t.Parallel() 169 g := newTestingGateway(t) 170 defer g.Close() 171 172 // compliant connect with old version 173 conn, err := net.Dial("tcp", string(g.Address())) 174 if err != nil { 175 t.Fatal("dial failed:", err) 176 } 177 addr := modules.NetAddress(conn.LocalAddr().String()) 178 ack, err := connectVersionHandshake(conn, "0.1") 179 if err != errPeerRejectedConn { 180 t.Fatal(err) 181 } 182 if ack != "" { 183 t.Fatal("gateway should have rejected old version") 184 } 185 for i := 0; i < 10; i++ { 186 g.mu.RLock() 187 _, ok := g.peers[addr] 188 g.mu.RUnlock() 189 if ok { 190 t.Fatal("gateway should not have added an old peer") 191 } 192 time.Sleep(20 * time.Millisecond) 193 } 194 195 // a simple 'conn.Close' would not obey the stream disconnect protocol 196 newClientStream(conn, build.Version).Close() 197 198 // compliant connect with invalid net address 199 conn, err = net.Dial("tcp", string(g.Address())) 200 if err != nil { 201 t.Fatal("dial failed:", err) 202 } 203 addr = modules.NetAddress(conn.LocalAddr().String()) 204 ack, err = connectVersionHandshake(conn, build.Version) 205 if err != nil { 206 t.Fatal(err) 207 } 208 if ack != build.Version { 209 t.Fatal("gateway should have given ack") 210 } 211 212 header := sessionHeader{ 213 GenesisID: types.GenesisID, 214 UniqueID: gatewayID{}, 215 NetAddress: "fake", 216 } 217 218 err = exchangeOurHeader(conn, header) 219 if err == nil { 220 t.Fatal("expected error, got nil") 221 } 222 conn.Close() 223 224 // compliant connect 225 conn, err = net.Dial("tcp", string(g.Address())) 226 if err != nil { 227 t.Fatal("dial failed:", err) 228 } 229 addr = modules.NetAddress(conn.LocalAddr().String()) 230 ack, err = connectVersionHandshake(conn, build.Version) 231 if err != nil { 232 t.Fatal(err) 233 } 234 if ack != build.Version { 235 t.Fatal("gateway should have given ack") 236 } 237 238 header.NetAddress = modules.NetAddress(conn.LocalAddr().String()) 239 err = exchangeOurHeader(conn, header) 240 if err != nil { 241 t.Fatal(err) 242 } 243 _, err = exchangeRemoteHeader(conn, header) 244 if err != nil { 245 t.Fatal(err) 246 } 247 248 // g should add the peer 249 err = build.Retry(50, 100*time.Millisecond, func() error { 250 g.mu.RLock() 251 _, ok := g.peers[addr] 252 g.mu.RUnlock() 253 if !ok { 254 return errors.New("g should have added the peer") 255 } 256 return nil 257 }) 258 if err != nil { 259 t.Fatal(err) 260 } 261 262 // Disconnect. Now that connection has been established, need to shutdown 263 // via the stream multiplexer. 264 newClientStream(conn, build.Version).Close() 265 266 // g should remove the peer 267 err = build.Retry(50, 100*time.Millisecond, func() error { 268 g.mu.RLock() 269 _, ok := g.peers[addr] 270 g.mu.RUnlock() 271 if ok { 272 return errors.New("g should have removed the peer") 273 } 274 return nil 275 }) 276 if err != nil { 277 t.Fatal(err) 278 } 279 280 // uncompliant connect 281 conn, err = net.Dial("tcp", string(g.Address())) 282 if err != nil { 283 t.Fatal("dial failed:", err) 284 } 285 if _, err := conn.Write([]byte("missing length prefix")); err != nil { 286 t.Fatal("couldn't write malformed header") 287 } 288 // g should have closed the connection 289 if n, err := conn.Write([]byte("closed")); err != nil && n > 0 { 290 t.Error("write succeeded after closed connection") 291 } 292 } 293 294 // TestConnect verifies that connecting peers will add peer relationships to 295 // the gateway, and that certain edge cases are properly handled. 296 func TestConnect(t *testing.T) { 297 if testing.Short() { 298 t.SkipNow() 299 } 300 t.Parallel() 301 // create bootstrap peer 302 bootstrap := newNamedTestingGateway(t, "1") 303 defer bootstrap.Close() 304 305 // give it a node 306 bootstrap.mu.Lock() 307 bootstrap.addNode(dummyNode) 308 bootstrap.mu.Unlock() 309 310 // create peer who will connect to bootstrap 311 g := newNamedTestingGateway(t, "2") 312 defer g.Close() 313 314 // first simulate a "bad" connect, where bootstrap won't share its nodes 315 bootstrap.mu.Lock() 316 bootstrap.handlers[handlerName("ShareNodes")] = func(modules.PeerConn) error { 317 return nil 318 } 319 bootstrap.mu.Unlock() 320 // connect 321 err := g.Connect(bootstrap.Address()) 322 if err != nil { 323 t.Fatal(err) 324 } 325 // g should not have the node 326 if g.removeNode(dummyNode) == nil { 327 t.Fatal("bootstrapper should not have received dummyNode:", g.nodes) 328 } 329 330 // split 'em up 331 g.Disconnect(bootstrap.Address()) 332 bootstrap.Disconnect(g.Address()) 333 334 // now restore the correct ShareNodes RPC and try again 335 bootstrap.mu.Lock() 336 bootstrap.handlers[handlerName("ShareNodes")] = bootstrap.shareNodes 337 bootstrap.mu.Unlock() 338 err = g.Connect(bootstrap.Address()) 339 if err != nil { 340 t.Fatal(err) 341 } 342 // g should have the node 343 time.Sleep(200 * time.Millisecond) 344 g.mu.RLock() 345 if _, ok := g.nodes[dummyNode]; !ok { 346 g.mu.RUnlock() // Needed to prevent a deadlock if this error condition is reached. 347 t.Fatal("bootstrapper should have received dummyNode:", g.nodes) 348 } 349 g.mu.RUnlock() 350 } 351 352 // TestUnitAcceptableVersion tests that the acceptableVersion func returns an 353 // error for unacceptable versions. 354 func TestUnitAcceptableVersion(t *testing.T) { 355 invalidVersions := []string{ 356 // ascii gibberish 357 "foobar", 358 "foobar.0", 359 "foobar.9", 360 "0.foobar", 361 "9.foobar", 362 "foobar.0.0", 363 "foobar.9.9", 364 "0.foobar.0", 365 "9.foobar.9", 366 "0.0.foobar", 367 "9.9.foobar", 368 // utf-8 gibberish 369 "世界", 370 "世界.0", 371 "世界.9", 372 "0.世界", 373 "9.世界", 374 "世界.0.0", 375 "世界.9.9", 376 "0.世界.0", 377 "9.世界.9", 378 "0.0.世界", 379 "9.9.世界", 380 // missing numbers 381 ".", 382 "..", 383 "...", 384 "0.", 385 ".1", 386 "2..", 387 ".3.", 388 "..4", 389 "5.6.", 390 ".7.8", 391 ".9.0.", 392 } 393 for _, v := range invalidVersions { 394 err := acceptableVersion(v) 395 if _, ok := err.(invalidVersionError); err == nil || !ok { 396 t.Errorf("acceptableVersion returned %q for version %q, but expected invalidVersionError", err, v) 397 } 398 } 399 insufficientVersions := []string{ 400 // random small versions 401 "0", 402 "00", 403 "0000000000", 404 "0.0", 405 "0000000000.0", 406 "0.0000000000", 407 "0.0.0.0.0.0.0.0", 408 "0.0.9", 409 "0.0.999", 410 "0.0.99999999999", 411 "0.1.2", 412 "0.1.2.3.4.5.6.7.8.9", 413 // pre-hardfork versions 414 "0.3.3", 415 "0.3.9.9.9.9.9.9.9.9.9.9", 416 "0.3.9999999999", 417 } 418 for _, v := range insufficientVersions { 419 err := acceptableVersion(v) 420 if _, ok := err.(insufficientVersionError); err == nil || !ok { 421 t.Errorf("acceptableVersion returned %q for version %q, but expected insufficientVersionError", err, v) 422 } 423 } 424 validVersions := []string{ 425 minAcceptableVersion, 426 "1.4.0", 427 "1.6.0", 428 "1.6.1", 429 "1.9", 430 "1.999", 431 "1.9999999999", 432 "2", 433 "2.0", 434 "2.0.0", 435 "9", 436 "9.0", 437 "9.0.0", 438 "9.9.9", 439 } 440 for _, v := range validVersions { 441 err := acceptableVersion(v) 442 if err != nil { 443 t.Errorf("acceptableVersion returned %q for version %q, but expected nil", err, v) 444 } 445 } 446 } 447 448 // TestConnectRejectsInvalidAddrs tests that Connect only connects to valid IP 449 // addresses. 450 func TestConnectRejectsInvalidAddrs(t *testing.T) { 451 if testing.Short() { 452 t.SkipNow() 453 } 454 t.Parallel() 455 g := newNamedTestingGateway(t, "1") 456 defer g.Close() 457 458 g2 := newNamedTestingGateway(t, "2") 459 defer g2.Close() 460 461 _, g2Port, err := net.SplitHostPort(string(g2.Address())) 462 if err != nil { 463 t.Fatal(err) 464 } 465 466 tests := []struct { 467 addr modules.NetAddress 468 wantErr bool 469 msg string 470 }{ 471 { 472 addr: "127.0.0.1:123", 473 wantErr: true, 474 msg: "Connect should reject unreachable addresses", 475 }, 476 { 477 addr: "111.111.111.111:0", 478 wantErr: true, 479 msg: "Connect should reject invalid NetAddresses", 480 }, 481 { 482 addr: modules.NetAddress(net.JoinHostPort("localhost", g2Port)), 483 wantErr: true, 484 msg: "Connect should reject non-IP addresses", 485 }, 486 { 487 addr: g2.Address(), 488 msg: "Connect failed to connect to another gateway", 489 }, 490 { 491 addr: g2.Address(), 492 wantErr: true, 493 msg: "Connect should reject an address it's already connected to", 494 }, 495 } 496 for _, tt := range tests { 497 err := g.Connect(tt.addr) 498 if tt.wantErr != (err != nil) { 499 t.Errorf("%v, wantErr: %v, err: %v", tt.msg, tt.wantErr, err) 500 } 501 } 502 } 503 504 // TestConnectRejectsVersions tests that Gateway.Connect only accepts peers 505 // with sufficient and valid versions. 506 func TestConnectRejectsVersions(t *testing.T) { 507 if testing.Short() { 508 t.SkipNow() 509 } 510 g := newTestingGateway(t) 511 defer g.Close() 512 // Setup a listener that mocks Gateway.acceptConn, but sends the 513 // version sent over mockVersionChan instead of build.Version. 514 listener, err := net.Listen("tcp", "localhost:0") 515 if err != nil { 516 t.Fatal(err) 517 } 518 defer listener.Close() 519 520 tests := []struct { 521 version string 522 errWant string 523 localErrWant string 524 invalidVersion bool 525 insufficientVersion bool 526 msg string 527 // version required for this test 528 versionRequired string 529 // 1.2.0 sessionHeader extension to handshake protocol 530 genesisID types.BlockID 531 uniqueID gatewayID 532 }{ 533 // Test that Connect fails when the remote peer's version is "reject". 534 { 535 version: "reject", 536 errWant: errPeerRejectedConn.Error(), 537 msg: "Connect should fail when the remote peer rejects the connection", 538 }, 539 // Test that Connect fails when the remote peer's version is ascii gibberish. 540 { 541 version: "foobar", 542 invalidVersion: true, 543 msg: "Connect should fail when the remote peer's version is ascii gibberish", 544 }, 545 // Test that Connect fails when the remote peer's version is utf8 gibberish. 546 { 547 version: "世界", 548 invalidVersion: true, 549 msg: "Connect should fail when the remote peer's version is utf8 gibberish", 550 }, 551 // Test that Connect fails when the remote peer's version is < 0.4.0 (0). 552 { 553 version: "0", 554 insufficientVersion: true, 555 msg: "Connect should fail when the remote peer's version is 0", 556 }, 557 { 558 version: "0.0.0", 559 insufficientVersion: true, 560 msg: "Connect should fail when the remote peer's version is 0.0.0", 561 }, 562 { 563 version: "0000.0000.0000", 564 insufficientVersion: true, 565 msg: "Connect should fail when the remote peer's version is 0000.0000.0000", 566 }, 567 { 568 version: "0.3.9", 569 insufficientVersion: true, 570 msg: "Connect should fail when the remote peer's version is 0.3.9", 571 }, 572 { 573 version: "0.3.9999", 574 insufficientVersion: true, 575 msg: "Connect should fail when the remote peer's version is 0.3.9999", 576 }, 577 { 578 version: "0.3.9.9.9", 579 insufficientVersion: true, 580 msg: "Connect should fail when the remote peer's version is 0.3.9.9.9", 581 }, 582 // Test that Connect succeeds when the remote peer's version is 0.4.0. 583 { 584 version: "0.4.0", 585 msg: "Connect should succeed when the remote peer's version is 0.4.0", 586 }, 587 // Test that Connect succeeds when the remote peer's version is > 0.4.0. 588 { 589 version: "0.9.0", 590 msg: "Connect should succeed when the remote peer's version is 0.9.0", 591 }, 592 // Test that Connect /could/ succeed when the remote peer's version is >= 1.3.0. 593 { 594 version: minimumAcceptablePeerVersion, 595 msg: "Connect should succeed when the remote peer's version is 1.3.0 and sessionHeader checks out", 596 uniqueID: func() (id gatewayID) { fastrand.Read(id[:]); return }(), 597 genesisID: types.GenesisID, 598 versionRequired: minimumAcceptablePeerVersion, 599 }, 600 { 601 version: minimumAcceptablePeerVersion, 602 msg: "Connect should not succeed when peer is connecting to itself", 603 uniqueID: g.id, 604 genesisID: types.GenesisID, 605 errWant: errOurAddress.Error(), 606 localErrWant: errOurAddress.Error(), 607 versionRequired: minimumAcceptablePeerVersion, 608 }, 609 } 610 for testIndex, tt := range tests { 611 if tt.versionRequired != "" && build.VersionCmp(build.Version, tt.versionRequired) < 0 { 612 continue // skip, as we do not meet the required version 613 } 614 615 // create the listener 616 doneChan := make(chan struct{}) 617 go func() { 618 defer close(doneChan) 619 conn, err := listener.Accept() 620 if err != nil { 621 panic(fmt.Sprintf("test #%d failed: %s", testIndex, err)) 622 } 623 remoteVersion, err := acceptVersionHandshake(conn, tt.version) 624 if err != nil { 625 panic(fmt.Sprintf("test #%d failed: %s", testIndex, err)) 626 } 627 if remoteVersion != build.Version { 628 panic(fmt.Sprintf("test #%d failed: remoteVersion != build.Version", testIndex)) 629 } 630 631 if build.VersionCmp(tt.version, minimumAcceptablePeerVersion) >= 0 { 632 ourHeader := sessionHeader{ 633 GenesisID: tt.genesisID, 634 UniqueID: tt.uniqueID, 635 NetAddress: modules.NetAddress(conn.LocalAddr().String()), 636 } 637 _, err = exchangeRemoteHeader(conn, ourHeader) 638 exchangeOurHeader(conn, ourHeader) 639 } else if build.VersionCmp(tt.version, handshakeUpgradeVersion) >= 0 { 640 var dialbackPort string 641 err = encoding.ReadObject(conn, &dialbackPort, 13) 642 } else { 643 // no action taken for old peers 644 } 645 if (err == nil && tt.localErrWant != "") || (err != nil && !strings.Contains(err.Error(), tt.localErrWant)) { 646 panic(fmt.Sprintf("test #%d failed: %v != %v", testIndex, tt.localErrWant, err)) 647 } 648 }() 649 err = g.Connect(modules.NetAddress(listener.Addr().String())) 650 switch { 651 case tt.invalidVersion: 652 // Check that the error is the expected type. 653 if _, ok := err.(invalidVersionError); !ok { 654 t.Fatalf("expected Connect to error with invalidVersionError: %s", tt.msg) 655 } 656 case tt.insufficientVersion: 657 // Check that the error is the expected type. 658 if _, ok := err.(insufficientVersionError); !ok { 659 t.Fatalf("expected Connect to error with insufficientVersionError: %s", tt.msg) 660 } 661 default: 662 // Check that the error is the expected error. 663 if (err == nil && tt.errWant != "") || (err != nil && !strings.Contains(err.Error(), tt.errWant)) { 664 t.Fatalf("expected Connect to error with '%v', but got '%v': %s", tt.errWant, err, tt.msg) 665 } 666 } 667 <-doneChan 668 g.Disconnect(modules.NetAddress(listener.Addr().String())) 669 } 670 } 671 672 // TestAcceptConnRejectsVersions tests that Gateway.acceptConn only accepts 673 // peers with sufficient and valid versions. 674 func TestAcceptConnRejectsVersions(t *testing.T) { 675 if testing.Short() { 676 t.SkipNow() 677 } 678 t.Parallel() 679 g := newTestingGateway(t) 680 defer g.Close() 681 682 tests := []struct { 683 remoteVersion string 684 versionResponseWant string 685 errWant error 686 msg string 687 }{ 688 // Test that acceptConn fails when the remote peer's version is "reject". 689 { 690 remoteVersion: "reject", 691 versionResponseWant: "", 692 errWant: errPeerRejectedConn, 693 msg: "acceptConn shouldn't accept a remote peer whose version is \"reject\"", 694 }, 695 // Test that acceptConn fails when the remote peer's version is ascii gibberish. 696 { 697 remoteVersion: "foobar", 698 versionResponseWant: "", 699 errWant: errPeerRejectedConn, 700 msg: "acceptConn shouldn't accept a remote peer whose version is ascii gibberish", 701 }, 702 // Test that acceptConn fails when the remote peer's version is utf8 gibberish. 703 { 704 remoteVersion: "世界", 705 versionResponseWant: "", 706 errWant: errPeerRejectedConn, 707 msg: "acceptConn shouldn't accept a remote peer whose version is utf8 gibberish", 708 }, 709 // Test that acceptConn fails when the remote peer's version is < 0.4.0 (0). 710 { 711 remoteVersion: "0", 712 versionResponseWant: "", 713 errWant: errPeerRejectedConn, 714 msg: "acceptConn shouldn't accept a remote peer whose version is 0", 715 }, 716 { 717 remoteVersion: "0.0.0", 718 versionResponseWant: "", 719 errWant: errPeerRejectedConn, 720 msg: "acceptConn shouldn't accept a remote peer whose version is 0.0.0", 721 }, 722 { 723 remoteVersion: "0000.0000.0000", 724 versionResponseWant: "", 725 errWant: errPeerRejectedConn, 726 msg: "acceptConn shouldn't accept a remote peer whose version is 0000.000.000", 727 }, 728 { 729 remoteVersion: "0.3.9", 730 versionResponseWant: "", 731 errWant: errPeerRejectedConn, 732 msg: "acceptConn shouldn't accept a remote peer whose version is 0.3.9", 733 }, 734 { 735 remoteVersion: "0.3.9999", 736 versionResponseWant: "", 737 errWant: errPeerRejectedConn, 738 msg: "acceptConn shouldn't accept a remote peer whose version is 0.3.9999", 739 }, 740 { 741 remoteVersion: "0.3.9.9.9", 742 versionResponseWant: "", 743 errWant: errPeerRejectedConn, 744 msg: "acceptConn shouldn't accept a remote peer whose version is 0.3.9.9.9", 745 }, 746 // Test that acceptConn succeeds when the remote peer's version is 747 // minAcceptableVersion 748 { 749 remoteVersion: minAcceptableVersion, 750 versionResponseWant: build.Version, 751 msg: "acceptConn should accept a remote peer whose version is 0.4.0", 752 }, 753 // Test that acceptConn succeeds when the remote peer's version is 754 // above minAcceptableVersion 755 { 756 remoteVersion: "9", 757 versionResponseWant: build.Version, 758 msg: "acceptConn should accept a remote peer whose version is 9", 759 }, 760 { 761 remoteVersion: "9.9.9", 762 versionResponseWant: build.Version, 763 msg: "acceptConn should accept a remote peer whose version is 9.9.9", 764 }, 765 { 766 remoteVersion: "9999.9999.9999", 767 versionResponseWant: build.Version, 768 msg: "acceptConn should accept a remote peer whose version is 9999.9999.9999", 769 }, 770 } 771 for _, tt := range tests { 772 conn, err := net.DialTimeout("tcp", string(g.Address()), dialTimeout) 773 if err != nil { 774 t.Fatal(err) 775 } 776 remoteVersion, err := connectVersionHandshake(conn, tt.remoteVersion) 777 if err != tt.errWant { 778 t.Fatal(err) 779 } 780 if remoteVersion != tt.versionResponseWant { 781 t.Fatal(tt.msg) 782 } 783 conn.Close() 784 } 785 } 786 787 // TestDisconnect checks that calls to gateway.Disconnect correctly disconnect 788 // and remove peers from the gateway. 789 func TestDisconnect(t *testing.T) { 790 if testing.Short() { 791 t.SkipNow() 792 } 793 t.Parallel() 794 g := newTestingGateway(t) 795 defer g.Close() 796 g2 := newNamedTestingGateway(t, "2") 797 defer g2.Close() 798 // Try disconnecting from a peer that doesn't exist. 799 if err := g.Disconnect("bar.com:123"); err == nil { 800 t.Fatal("disconnect removed unconnected peer") 801 } 802 803 // Connect two peers to eachother. 804 err := g.Connect(g2.myAddr) 805 if err != nil { 806 t.Fatal(err) 807 } 808 g.mu.Lock() 809 _, exists := g.nodes[g2.myAddr] 810 if !exists { 811 t.Error("peer never made it into node list") 812 } 813 g.mu.Unlock() 814 815 // Disconnect the peer. 816 if err := g.Disconnect(g2.myAddr); err != nil { 817 t.Fatal("disconnect failed:", err) 818 } 819 g2.Disconnect(g.myAddr) // Prevents g2 from connecting back to g 820 peers := g.Peers() 821 for _, peer := range peers { 822 if peer.NetAddress == g2.myAddr { 823 t.Error("disconnect seems to have failed - still have this peer") 824 } 825 } 826 g.mu.Lock() 827 _, exists = g.nodes[g2.myAddr] 828 if exists { 829 t.Error("should be dropping peer from nodelist after disconnect") 830 } 831 g.mu.Unlock() 832 } 833 834 // TestPeerManager checks that the peer manager is properly spacing out peer 835 // connection requests. 836 func TestPeerManager(t *testing.T) { 837 if testing.Short() { 838 t.SkipNow() 839 } 840 t.Parallel() 841 g1 := newNamedTestingGateway(t, "1") 842 defer g1.Close() 843 844 // create a valid node to connect to 845 g2 := newNamedTestingGateway(t, "2") 846 defer g2.Close() 847 848 // g1's node list should only contain g2 849 g1.mu.Lock() 850 g1.nodes = map[modules.NetAddress]*node{} 851 g1.nodes[g2.Address()] = &node{NetAddress: g2.Address()} 852 g1.mu.Unlock() 853 854 // when peerManager wakes up, it should connect to g2. 855 time.Sleep(time.Second + noNodesDelay) 856 857 g1.mu.RLock() 858 defer g1.mu.RUnlock() 859 if len(g1.peers) != 1 || g1.peers[g2.Address()] == nil { 860 t.Fatal("gateway did not connect to g2:", g1.peers) 861 } 862 } 863 864 // TestOverloadedBootstrap creates a bunch of gateways and connects all of them 865 // to the first gateway, the bootstrap gateway. More gateways will be created 866 // than is allowed by the bootstrap for the total number of connections. After 867 // waiting, all peers should eventually get to the full number of outbound 868 // peers. 869 func TestOverloadedBootstrap(t *testing.T) { 870 if testing.Short() { 871 t.SkipNow() 872 } 873 t.Parallel() 874 875 // Create fullyConnectedThreshold*2 peers and connect them all to only the 876 // first node. 877 var gs []*Gateway 878 for i := 0; i < fullyConnectedThreshold*2; i++ { 879 gs = append(gs, newNamedTestingGateway(t, strconv.Itoa(i))) 880 // Connect this gateway to the first gateway. 881 if i == 0 { 882 continue 883 } 884 err := gs[i].Connect(gs[0].myAddr) 885 for j := 0; j < 100 && err != nil; j++ { 886 time.Sleep(time.Millisecond * 250) 887 err = gs[i].Connect(gs[0].myAddr) 888 } 889 if err != nil { 890 panic(err) 891 } 892 } 893 894 // Spin until all gateways have a complete number of outbound peers. 895 success := false 896 for i := 0; i < 100; i++ { 897 success = true 898 for _, g := range gs { 899 outboundPeers := 0 900 g.mu.RLock() 901 for _, p := range g.peers { 902 if !p.Inbound { 903 outboundPeers++ 904 } 905 } 906 g.mu.RUnlock() 907 908 if outboundPeers < wellConnectedThreshold { 909 success = false 910 break 911 } 912 } 913 if !success { 914 time.Sleep(time.Second) 915 } 916 } 917 if !success { 918 for i, g := range gs { 919 outboundPeers := 0 920 g.mu.RLock() 921 for _, p := range g.peers { 922 if !p.Inbound { 923 outboundPeers++ 924 } 925 } 926 g.mu.RUnlock() 927 t.Log("Gateway", i, ":", outboundPeers) 928 } 929 t.Fatal("after 100 seconds not all gateways able to become well connected") 930 } 931 932 // Randomly close many of the peers. For many peers, this should put them 933 // below the well connected threshold, but there are still enough nodes on 934 // the network that no partitions should occur. 935 var newGS []*Gateway 936 for _, i := range fastrand.Perm(len(gs)) { 937 newGS = append(newGS, gs[i]) 938 } 939 cutSize := len(newGS) / 4 940 // Close the first many of the now-randomly-sorted gateways. 941 for _, g := range newGS[:cutSize] { 942 err := g.Close() 943 if err != nil { 944 t.Fatal(err) 945 } 946 } 947 // Set 'gs' equal to the remaining gateways. 948 gs = newGS[cutSize:] 949 950 // Spin until all gateways have a complete number of outbound peers. The 951 // test can fail if there are network partitions, however not a huge 952 // magnitude of nodes are being removed, and they all started with 4 953 // connections. A partition is unlikely. 954 success = false 955 for i := 0; i < 100; i++ { 956 success = true 957 for _, g := range gs { 958 outboundPeers := 0 959 g.mu.RLock() 960 for _, p := range g.peers { 961 if !p.Inbound { 962 outboundPeers++ 963 } 964 } 965 g.mu.RUnlock() 966 967 if outboundPeers < wellConnectedThreshold { 968 success = false 969 break 970 } 971 } 972 if !success { 973 time.Sleep(time.Second) 974 } 975 } 976 if !success { 977 t.Fatal("after 100 seconds not all gateways able to become well connected") 978 } 979 980 // Close all remaining gateways. 981 for _, g := range gs { 982 err := g.Close() 983 if err != nil { 984 t.Error(err) 985 } 986 } 987 } 988 989 // TestPeerManagerPriority tests that the peer manager will prioritize 990 // connecting to previous outbound peers before inbound peers. 991 func TestPeerManagerPriority(t *testing.T) { 992 if testing.Short() { 993 t.SkipNow() 994 } 995 t.Parallel() 996 997 g1 := newNamedTestingGateway(t, "1") 998 defer g1.Close() 999 g2 := newNamedTestingGateway(t, "2") 1000 defer g2.Close() 1001 g3 := newNamedTestingGateway(t, "3") 1002 defer g3.Close() 1003 1004 // Connect g1 to g2. This will cause g2 to be saved as an outbound peer in 1005 // g1's node list. 1006 if err := g1.Connect(g2.Address()); err != nil { 1007 t.Fatal(err) 1008 } 1009 // Connect g3 to g1. This will cause g3 to be added to g1's node list, but 1010 // not as an outbound peer. 1011 if err := g3.Connect(g1.Address()); err != nil { 1012 t.Fatal(err) 1013 } 1014 1015 // Spin until the connections succeeded. 1016 for i := 0; i < 50; i++ { 1017 g1.mu.RLock() 1018 _, exists2 := g1.nodes[g2.Address()] 1019 _, exists3 := g1.nodes[g3.Address()] 1020 g1.mu.RUnlock() 1021 if exists2 && exists3 { 1022 break 1023 } 1024 time.Sleep(time.Millisecond * 100) 1025 } 1026 g1.mu.RLock() 1027 peer2, exists2 := g1.nodes[g2.Address()] 1028 peer3, exists3 := g1.nodes[g3.Address()] 1029 g1.mu.RUnlock() 1030 if !exists2 { 1031 t.Fatal("peer 2 not in gateway") 1032 } 1033 if !exists3 { 1034 t.Fatal("peer 3 not found") 1035 } 1036 1037 // Verify assumptions about node list. 1038 g1.mu.RLock() 1039 g2isOutbound := peer2.WasOutboundPeer 1040 g3isOutbound := peer3.WasOutboundPeer 1041 g1.mu.RUnlock() 1042 if !g2isOutbound { 1043 t.Fatal("g2 should be an outbound node") 1044 } 1045 if g3isOutbound { 1046 t.Fatal("g3 should not be an outbound node") 1047 } 1048 1049 // Disconnect everyone. 1050 g2.Disconnect(g1.Address()) 1051 g3.Disconnect(g1.Address()) 1052 1053 // Shutdown g1. 1054 err := g1.Close() 1055 if err != nil { 1056 t.Fatal(err) 1057 } 1058 1059 // Restart g1. It should immediately reconnect to g2, and then g3 after a 1060 // delay. 1061 g1, err = New(string(g1.myAddr), false, g1.persistDir) 1062 if err != nil { 1063 t.Fatal(err) 1064 } 1065 defer g1.Close() 1066 1067 // Wait until g1 connects to g2. 1068 for i := 0; i < 100; i++ { 1069 if peers := g1.Peers(); len(peers) == 0 { 1070 time.Sleep(10 * time.Millisecond) 1071 } else if len(peers) == 1 && peers[0].NetAddress == g2.Address() { 1072 break 1073 } else { 1074 t.Fatal("something wrong with the peer list:", peers) 1075 } 1076 } 1077 // Wait until g1 connects to g3. 1078 for i := 0; i < 100; i++ { 1079 if peers := g1.Peers(); len(peers) == 1 { 1080 time.Sleep(10 * time.Millisecond) 1081 } else if len(peers) == 2 { 1082 break 1083 } else { 1084 t.Fatal("something wrong with the peer list:", peers) 1085 } 1086 } 1087 } 1088 1089 // TestPeerManagerOutboundSave sets up an island of nodes and checks that they 1090 // can all connect to eachother, and that the all add eachother as 1091 // 'WasOutboundPeer'. 1092 func TestPeerManagerOutboundSave(t *testing.T) { 1093 if testing.Short() { 1094 t.SkipNow() 1095 } 1096 t.Parallel() 1097 1098 // Create enough gateways so that every gateway should automatically end up 1099 // with every other gateway as an outbound peer. 1100 var gs []*Gateway 1101 for i := 0; i < wellConnectedThreshold+1; i++ { 1102 gs = append(gs, newNamedTestingGateway(t, strconv.Itoa(i))) 1103 } 1104 // Connect g1 to each peer. This should be enough that every peer eventually 1105 // has the full set of outbound peers. 1106 for _, g := range gs[1:] { 1107 if err := gs[0].Connect(g.Address()); err != nil { 1108 t.Fatal(err) 1109 } 1110 } 1111 1112 // Block until every peer has wellConnectedThreshold outbound peers. 1113 err := build.Retry(100, time.Millisecond*200, func() error { 1114 for _, g := range gs { 1115 var outboundNodes, outboundPeers int 1116 g.mu.RLock() 1117 for _, node := range g.nodes { 1118 if node.WasOutboundPeer { 1119 outboundNodes++ 1120 } 1121 } 1122 for _, peer := range g.peers { 1123 if !peer.Inbound { 1124 outboundPeers++ 1125 } 1126 } 1127 g.mu.RUnlock() 1128 if outboundNodes < wellConnectedThreshold { 1129 return errors.New("not enough outbound nodes: " + strconv.Itoa(outboundNodes)) 1130 } 1131 if outboundPeers < wellConnectedThreshold { 1132 return errors.New("not enough outbound peers: " + strconv.Itoa(outboundPeers)) 1133 } 1134 } 1135 return nil 1136 }) 1137 if err != nil { 1138 t.Fatal(err) 1139 } 1140 } 1141 1142 // TestBuildPeerManagerNodeList tests the buildPeerManagerNodeList method. 1143 func TestBuildPeerManagerNodeList(t *testing.T) { 1144 g := &Gateway{ 1145 nodes: map[modules.NetAddress]*node{ 1146 "foo": {NetAddress: "foo", WasOutboundPeer: true}, 1147 "bar": {NetAddress: "bar", WasOutboundPeer: false}, 1148 "baz": {NetAddress: "baz", WasOutboundPeer: true}, 1149 "quux": {NetAddress: "quux", WasOutboundPeer: false}, 1150 }, 1151 } 1152 nodelist := g.buildPeerManagerNodeList() 1153 // all outbound nodes should be at the front of the list 1154 var i int 1155 for i < len(nodelist) && g.nodes[nodelist[i]].WasOutboundPeer { 1156 i++ 1157 } 1158 for i < len(nodelist) && !g.nodes[nodelist[i]].WasOutboundPeer { 1159 i++ 1160 } 1161 if i != len(nodelist) { 1162 t.Fatal("bad nodelist:", nodelist) 1163 } 1164 }