github.com/NebulousLabs/Sia@v1.3.7/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 "1.3.0", 418 } 419 for _, v := range insufficientVersions { 420 err := acceptableVersion(v) 421 if _, ok := err.(insufficientVersionError); err == nil || !ok { 422 t.Errorf("acceptableVersion returned %q for version %q, but expected insufficientVersionError", err, v) 423 } 424 } 425 validVersions := []string{ 426 minimumAcceptablePeerVersion, 427 "1.4.0", 428 "1.3.1", 429 "1.6.0", 430 "1.6.1", 431 "1.9", 432 "1.999", 433 "1.9999999999", 434 "2", 435 "2.0", 436 "2.0.0", 437 "9", 438 "9.0", 439 "9.0.0", 440 "9.9.9", 441 } 442 for _, v := range validVersions { 443 err := acceptableVersion(v) 444 if err != nil { 445 t.Errorf("acceptableVersion returned %q for version %q, but expected nil", err, v) 446 } 447 } 448 } 449 450 // TestConnectRejectsInvalidAddrs tests that Connect only connects to valid IP 451 // addresses. 452 func TestConnectRejectsInvalidAddrs(t *testing.T) { 453 if testing.Short() { 454 t.SkipNow() 455 } 456 t.Parallel() 457 g := newNamedTestingGateway(t, "1") 458 defer g.Close() 459 460 g2 := newNamedTestingGateway(t, "2") 461 defer g2.Close() 462 463 _, g2Port, err := net.SplitHostPort(string(g2.Address())) 464 if err != nil { 465 t.Fatal(err) 466 } 467 468 tests := []struct { 469 addr modules.NetAddress 470 wantErr bool 471 msg string 472 }{ 473 { 474 addr: "127.0.0.1:123", 475 wantErr: true, 476 msg: "Connect should reject unreachable addresses", 477 }, 478 { 479 addr: "111.111.111.111:0", 480 wantErr: true, 481 msg: "Connect should reject invalid NetAddresses", 482 }, 483 { 484 addr: modules.NetAddress(net.JoinHostPort("localhost", g2Port)), 485 wantErr: true, 486 msg: "Connect should reject non-IP addresses", 487 }, 488 { 489 addr: g2.Address(), 490 msg: "Connect failed to connect to another gateway", 491 }, 492 { 493 addr: g2.Address(), 494 wantErr: true, 495 msg: "Connect should reject an address it's already connected to", 496 }, 497 } 498 for _, tt := range tests { 499 err := g.Connect(tt.addr) 500 if tt.wantErr != (err != nil) { 501 t.Errorf("%v, wantErr: %v, err: %v", tt.msg, tt.wantErr, err) 502 } 503 } 504 } 505 506 // TestConnectRejectsVersions tests that Gateway.Connect only accepts peers 507 // with sufficient and valid versions. 508 func TestConnectRejectsVersions(t *testing.T) { 509 if testing.Short() { 510 t.SkipNow() 511 } 512 g := newTestingGateway(t) 513 defer g.Close() 514 // Setup a listener that mocks Gateway.acceptConn, but sends the 515 // version sent over mockVersionChan instead of build.Version. 516 listener, err := net.Listen("tcp", "localhost:0") 517 if err != nil { 518 t.Fatal(err) 519 } 520 defer listener.Close() 521 522 tests := []struct { 523 version string 524 errWant string 525 localErrWant string 526 invalidVersion bool 527 insufficientVersion bool 528 msg string 529 // version required for this test 530 versionRequired string 531 // 1.2.0 sessionHeader extension to handshake protocol 532 genesisID types.BlockID 533 uniqueID gatewayID 534 }{ 535 // Test that Connect fails when the remote peer's version is "reject". 536 { 537 version: "reject", 538 errWant: errPeerRejectedConn.Error(), 539 msg: "Connect should fail when the remote peer rejects the connection", 540 }, 541 // Test that Connect fails when the remote peer's version is ascii gibberish. 542 { 543 version: "foobar", 544 invalidVersion: true, 545 msg: "Connect should fail when the remote peer's version is ascii gibberish", 546 }, 547 // Test that Connect fails when the remote peer's version is utf8 gibberish. 548 { 549 version: "世界", 550 invalidVersion: true, 551 msg: "Connect should fail when the remote peer's version is utf8 gibberish", 552 }, 553 // Test that Connect fails when the remote peer's version is < 0.4.0 (0). 554 { 555 version: "0", 556 insufficientVersion: true, 557 msg: "Connect should fail when the remote peer's version is 0", 558 }, 559 { 560 version: "0.0.0", 561 insufficientVersion: true, 562 msg: "Connect should fail when the remote peer's version is 0.0.0", 563 }, 564 { 565 version: "0000.0000.0000", 566 insufficientVersion: true, 567 msg: "Connect should fail when the remote peer's version is 0000.0000.0000", 568 }, 569 { 570 version: "0.3.9", 571 insufficientVersion: true, 572 msg: "Connect should fail when the remote peer's version is 0.3.9", 573 }, 574 { 575 version: "0.3.9999", 576 insufficientVersion: true, 577 msg: "Connect should fail when the remote peer's version is 0.3.9999", 578 }, 579 { 580 version: "0.3.9.9.9", 581 insufficientVersion: true, 582 msg: "Connect should fail when the remote peer's version is 0.3.9.9.9", 583 }, 584 // Test that Connect succeeds when the remote peer's version is 0.4.0. 585 { 586 version: "0.4.0", 587 msg: "Connect should succeed when the remote peer's version is 0.4.0", 588 }, 589 // Test that Connect succeeds when the remote peer's version is > 0.4.0. 590 { 591 version: "0.9.0", 592 msg: "Connect should succeed when the remote peer's version is 0.9.0", 593 }, 594 // Test that Connect /could/ succeed when the remote peer's version is >= 1.3.0. 595 { 596 version: minimumAcceptablePeerVersion, 597 msg: "Connect should succeed when the remote peer's version is 1.3.0 and sessionHeader checks out", 598 uniqueID: func() (id gatewayID) { fastrand.Read(id[:]); return }(), 599 genesisID: types.GenesisID, 600 versionRequired: minimumAcceptablePeerVersion, 601 }, 602 { 603 version: minimumAcceptablePeerVersion, 604 msg: "Connect should not succeed when peer is connecting to itself", 605 uniqueID: g.staticId, 606 genesisID: types.GenesisID, 607 errWant: errOurAddress.Error(), 608 localErrWant: errOurAddress.Error(), 609 versionRequired: minimumAcceptablePeerVersion, 610 }, 611 } 612 for testIndex, tt := range tests { 613 if tt.versionRequired != "" && build.VersionCmp(build.Version, tt.versionRequired) < 0 { 614 continue // skip, as we do not meet the required version 615 } 616 617 // create the listener 618 doneChan := make(chan struct{}) 619 go func() { 620 defer close(doneChan) 621 conn, err := listener.Accept() 622 if err != nil { 623 panic(fmt.Sprintf("test #%d failed: %s", testIndex, err)) 624 } 625 remoteVersion, err := acceptVersionHandshake(conn, tt.version) 626 if err != nil { 627 panic(fmt.Sprintf("test #%d failed: %s", testIndex, err)) 628 } 629 if remoteVersion != build.Version { 630 panic(fmt.Sprintf("test #%d failed: remoteVersion != build.Version", testIndex)) 631 } 632 633 if build.VersionCmp(tt.version, minimumAcceptablePeerVersion) >= 0 { 634 ourHeader := sessionHeader{ 635 GenesisID: tt.genesisID, 636 UniqueID: tt.uniqueID, 637 NetAddress: modules.NetAddress(conn.LocalAddr().String()), 638 } 639 _, err = exchangeRemoteHeader(conn, ourHeader) 640 exchangeOurHeader(conn, ourHeader) 641 } else if build.VersionCmp(tt.version, handshakeUpgradeVersion) >= 0 { 642 var dialbackPort string 643 err = encoding.ReadObject(conn, &dialbackPort, 13) 644 } else { 645 // no action taken for old peers 646 } 647 if (err == nil && tt.localErrWant != "") || (err != nil && !strings.Contains(err.Error(), tt.localErrWant)) { 648 panic(fmt.Sprintf("test #%d failed: %v != %v", testIndex, tt.localErrWant, err)) 649 } 650 }() 651 err = g.Connect(modules.NetAddress(listener.Addr().String())) 652 switch { 653 case tt.invalidVersion: 654 // Check that the error is the expected type. 655 if _, ok := err.(invalidVersionError); !ok { 656 t.Fatalf("expected Connect to error with invalidVersionError: %s", tt.msg) 657 } 658 case tt.insufficientVersion: 659 // Check that the error is the expected type. 660 if _, ok := err.(insufficientVersionError); !ok { 661 t.Fatalf("expected Connect to error with insufficientVersionError: %s", tt.msg) 662 } 663 default: 664 // Check that the error is the expected error. 665 if (err == nil && tt.errWant != "") || (err != nil && !strings.Contains(err.Error(), tt.errWant)) { 666 t.Fatalf("expected Connect to error with '%v', but got '%v': %s", tt.errWant, err, tt.msg) 667 } 668 } 669 <-doneChan 670 g.Disconnect(modules.NetAddress(listener.Addr().String())) 671 } 672 } 673 674 // TestAcceptConnRejectsVersions tests that Gateway.acceptConn only accepts 675 // peers with sufficient and valid versions. 676 func TestAcceptConnRejectsVersions(t *testing.T) { 677 if testing.Short() { 678 t.SkipNow() 679 } 680 t.Parallel() 681 g := newTestingGateway(t) 682 defer g.Close() 683 684 tests := []struct { 685 remoteVersion string 686 versionResponseWant string 687 errWant error 688 msg string 689 }{ 690 // Test that acceptConn fails when the remote peer's version is "reject". 691 { 692 remoteVersion: "reject", 693 versionResponseWant: "", 694 errWant: errPeerRejectedConn, 695 msg: "acceptConn shouldn't accept a remote peer whose version is \"reject\"", 696 }, 697 // Test that acceptConn fails when the remote peer's version is ascii gibberish. 698 { 699 remoteVersion: "foobar", 700 versionResponseWant: "", 701 errWant: errPeerRejectedConn, 702 msg: "acceptConn shouldn't accept a remote peer whose version is ascii gibberish", 703 }, 704 // Test that acceptConn fails when the remote peer's version is utf8 gibberish. 705 { 706 remoteVersion: "世界", 707 versionResponseWant: "", 708 errWant: errPeerRejectedConn, 709 msg: "acceptConn shouldn't accept a remote peer whose version is utf8 gibberish", 710 }, 711 // Test that acceptConn fails when the remote peer's version is < 0.4.0 (0). 712 { 713 remoteVersion: "0", 714 versionResponseWant: "", 715 errWant: errPeerRejectedConn, 716 msg: "acceptConn shouldn't accept a remote peer whose version is 0", 717 }, 718 { 719 remoteVersion: "0.0.0", 720 versionResponseWant: "", 721 errWant: errPeerRejectedConn, 722 msg: "acceptConn shouldn't accept a remote peer whose version is 0.0.0", 723 }, 724 { 725 remoteVersion: "0000.0000.0000", 726 versionResponseWant: "", 727 errWant: errPeerRejectedConn, 728 msg: "acceptConn shouldn't accept a remote peer whose version is 0000.000.000", 729 }, 730 { 731 remoteVersion: "0.3.9", 732 versionResponseWant: "", 733 errWant: errPeerRejectedConn, 734 msg: "acceptConn shouldn't accept a remote peer whose version is 0.3.9", 735 }, 736 { 737 remoteVersion: "0.3.9999", 738 versionResponseWant: "", 739 errWant: errPeerRejectedConn, 740 msg: "acceptConn shouldn't accept a remote peer whose version is 0.3.9999", 741 }, 742 { 743 remoteVersion: "0.3.9.9.9", 744 versionResponseWant: "", 745 errWant: errPeerRejectedConn, 746 msg: "acceptConn shouldn't accept a remote peer whose version is 0.3.9.9.9", 747 }, 748 // Test that acceptConn succeeds when the remote peer's version is 749 // minAcceptableVersion 750 { 751 remoteVersion: minimumAcceptablePeerVersion, 752 versionResponseWant: build.Version, 753 msg: "acceptConn should accept a remote peer whose version is 0.4.0", 754 }, 755 // Test that acceptConn succeeds when the remote peer's version is 756 // above minAcceptableVersion 757 { 758 remoteVersion: "9", 759 versionResponseWant: build.Version, 760 msg: "acceptConn should accept a remote peer whose version is 9", 761 }, 762 { 763 remoteVersion: "9.9.9", 764 versionResponseWant: build.Version, 765 msg: "acceptConn should accept a remote peer whose version is 9.9.9", 766 }, 767 { 768 remoteVersion: "9999.9999.9999", 769 versionResponseWant: build.Version, 770 msg: "acceptConn should accept a remote peer whose version is 9999.9999.9999", 771 }, 772 } 773 for _, tt := range tests { 774 conn, err := net.DialTimeout("tcp", string(g.Address()), dialTimeout) 775 if err != nil { 776 t.Fatal(err) 777 } 778 remoteVersion, err := connectVersionHandshake(conn, tt.remoteVersion) 779 if err != tt.errWant { 780 t.Fatal(err) 781 } 782 if remoteVersion != tt.versionResponseWant { 783 t.Fatal(tt.msg) 784 } 785 conn.Close() 786 } 787 } 788 789 // TestDisconnect checks that calls to gateway.Disconnect correctly disconnect 790 // and remove peers from the gateway. 791 func TestDisconnect(t *testing.T) { 792 if testing.Short() { 793 t.SkipNow() 794 } 795 t.Parallel() 796 g := newTestingGateway(t) 797 defer g.Close() 798 g2 := newNamedTestingGateway(t, "2") 799 defer g2.Close() 800 // Try disconnecting from a peer that doesn't exist. 801 if err := g.Disconnect("bar.com:123"); err == nil { 802 t.Fatal("disconnect removed unconnected peer") 803 } 804 805 // Connect two peers to eachother. 806 err := g.Connect(g2.myAddr) 807 if err != nil { 808 t.Fatal(err) 809 } 810 g.mu.Lock() 811 _, exists := g.nodes[g2.myAddr] 812 if !exists { 813 t.Error("peer never made it into node list") 814 } 815 g.mu.Unlock() 816 817 // Disconnect the peer. 818 if err := g.Disconnect(g2.myAddr); err != nil { 819 t.Fatal("disconnect failed:", err) 820 } 821 g2.Disconnect(g.myAddr) // Prevents g2 from connecting back to g 822 peers := g.Peers() 823 for _, peer := range peers { 824 if peer.NetAddress == g2.myAddr { 825 t.Error("disconnect seems to have failed - still have this peer") 826 } 827 } 828 g.mu.Lock() 829 _, exists = g.nodes[g2.myAddr] 830 if exists { 831 t.Error("should be dropping peer from nodelist after disconnect") 832 } 833 g.mu.Unlock() 834 } 835 836 // TestPeerManager checks that the peer manager is properly spacing out peer 837 // connection requests. 838 func TestPeerManager(t *testing.T) { 839 if testing.Short() { 840 t.SkipNow() 841 } 842 t.Parallel() 843 g1 := newNamedTestingGateway(t, "1") 844 defer g1.Close() 845 846 // create a valid node to connect to 847 g2 := newNamedTestingGateway(t, "2") 848 defer g2.Close() 849 850 // g1's node list should only contain g2 851 g1.mu.Lock() 852 g1.nodes = map[modules.NetAddress]*node{} 853 g1.nodes[g2.Address()] = &node{NetAddress: g2.Address()} 854 g1.mu.Unlock() 855 856 // when peerManager wakes up, it should connect to g2. 857 time.Sleep(time.Second + noNodesDelay) 858 859 g1.mu.RLock() 860 defer g1.mu.RUnlock() 861 if len(g1.peers) != 1 || g1.peers[g2.Address()] == nil { 862 t.Fatal("gateway did not connect to g2:", g1.peers) 863 } 864 } 865 866 // TestOverloadedBootstrap creates a bunch of gateways and connects all of them 867 // to the first gateway, the bootstrap gateway. More gateways will be created 868 // than is allowed by the bootstrap for the total number of connections. After 869 // waiting, all peers should eventually get to the full number of outbound 870 // peers. 871 func TestOverloadedBootstrap(t *testing.T) { 872 if testing.Short() { 873 t.SkipNow() 874 } 875 t.Parallel() 876 877 // Create fullyConnectedThreshold*2 peers and connect them all to only the 878 // first node. 879 var gs []*Gateway 880 for i := 0; i < fullyConnectedThreshold*2; i++ { 881 gs = append(gs, newNamedTestingGateway(t, strconv.Itoa(i))) 882 // Connect this gateway to the first gateway. 883 if i == 0 { 884 continue 885 } 886 err := gs[i].Connect(gs[0].myAddr) 887 for j := 0; j < 100 && err != nil; j++ { 888 time.Sleep(time.Millisecond * 250) 889 err = gs[i].Connect(gs[0].myAddr) 890 } 891 if err != nil { 892 panic(err) 893 } 894 } 895 896 // Spin until all gateways have a complete number of outbound peers. 897 success := false 898 for i := 0; i < 100; i++ { 899 success = true 900 for _, g := range gs { 901 outboundPeers := 0 902 g.mu.RLock() 903 for _, p := range g.peers { 904 if !p.Inbound { 905 outboundPeers++ 906 } 907 } 908 g.mu.RUnlock() 909 910 if outboundPeers < wellConnectedThreshold { 911 success = false 912 break 913 } 914 } 915 if !success { 916 time.Sleep(time.Second) 917 } 918 } 919 if !success { 920 for i, g := range gs { 921 outboundPeers := 0 922 g.mu.RLock() 923 for _, p := range g.peers { 924 if !p.Inbound { 925 outboundPeers++ 926 } 927 } 928 g.mu.RUnlock() 929 t.Log("Gateway", i, ":", outboundPeers) 930 } 931 t.Fatal("after 100 seconds not all gateways able to become well connected") 932 } 933 934 // Randomly close many of the peers. For many peers, this should put them 935 // below the well connected threshold, but there are still enough nodes on 936 // the network that no partitions should occur. 937 var newGS []*Gateway 938 for _, i := range fastrand.Perm(len(gs)) { 939 newGS = append(newGS, gs[i]) 940 } 941 cutSize := len(newGS) / 4 942 // Close the first many of the now-randomly-sorted gateways. 943 for _, g := range newGS[:cutSize] { 944 err := g.Close() 945 if err != nil { 946 t.Fatal(err) 947 } 948 } 949 // Set 'gs' equal to the remaining gateways. 950 gs = newGS[cutSize:] 951 952 // Spin until all gateways have a complete number of outbound peers. The 953 // test can fail if there are network partitions, however not a huge 954 // magnitude of nodes are being removed, and they all started with 4 955 // connections. A partition is unlikely. 956 success = false 957 for i := 0; i < 100; i++ { 958 success = true 959 for _, g := range gs { 960 outboundPeers := 0 961 g.mu.RLock() 962 for _, p := range g.peers { 963 if !p.Inbound { 964 outboundPeers++ 965 } 966 } 967 g.mu.RUnlock() 968 969 if outboundPeers < wellConnectedThreshold { 970 success = false 971 break 972 } 973 } 974 if !success { 975 time.Sleep(time.Second) 976 } 977 } 978 if !success { 979 t.Fatal("after 100 seconds not all gateways able to become well connected") 980 } 981 982 // Close all remaining gateways. 983 for _, g := range gs { 984 err := g.Close() 985 if err != nil { 986 t.Error(err) 987 } 988 } 989 } 990 991 // TestPeerManagerPriority tests that the peer manager will prioritize 992 // connecting to previous outbound peers before inbound peers. 993 func TestPeerManagerPriority(t *testing.T) { 994 if testing.Short() { 995 t.SkipNow() 996 } 997 t.Parallel() 998 999 g1 := newNamedTestingGateway(t, "1") 1000 defer g1.Close() 1001 g2 := newNamedTestingGateway(t, "2") 1002 defer g2.Close() 1003 g3 := newNamedTestingGateway(t, "3") 1004 defer g3.Close() 1005 1006 // Connect g1 to g2. This will cause g2 to be saved as an outbound peer in 1007 // g1's node list. 1008 if err := g1.Connect(g2.Address()); err != nil { 1009 t.Fatal(err) 1010 } 1011 // Connect g3 to g1. This will cause g3 to be added to g1's node list, but 1012 // not as an outbound peer. 1013 if err := g3.Connect(g1.Address()); err != nil { 1014 t.Fatal(err) 1015 } 1016 1017 // Spin until the connections succeeded. 1018 for i := 0; i < 50; i++ { 1019 g1.mu.RLock() 1020 _, exists2 := g1.nodes[g2.Address()] 1021 _, exists3 := g1.nodes[g3.Address()] 1022 g1.mu.RUnlock() 1023 if exists2 && exists3 { 1024 break 1025 } 1026 time.Sleep(time.Millisecond * 100) 1027 } 1028 g1.mu.RLock() 1029 peer2, exists2 := g1.nodes[g2.Address()] 1030 peer3, exists3 := g1.nodes[g3.Address()] 1031 g1.mu.RUnlock() 1032 if !exists2 { 1033 t.Fatal("peer 2 not in gateway") 1034 } 1035 if !exists3 { 1036 t.Fatal("peer 3 not found") 1037 } 1038 1039 // Verify assumptions about node list. 1040 g1.mu.RLock() 1041 g2isOutbound := peer2.WasOutboundPeer 1042 g3isOutbound := peer3.WasOutboundPeer 1043 g1.mu.RUnlock() 1044 if !g2isOutbound { 1045 t.Fatal("g2 should be an outbound node") 1046 } 1047 if g3isOutbound { 1048 t.Fatal("g3 should not be an outbound node") 1049 } 1050 1051 // Disconnect everyone. 1052 g2.Disconnect(g1.Address()) 1053 g3.Disconnect(g1.Address()) 1054 1055 // Shutdown g1. 1056 err := g1.Close() 1057 if err != nil { 1058 t.Fatal(err) 1059 } 1060 1061 // Restart g1. It should immediately reconnect to g2, and then g3 after a 1062 // delay. 1063 g1, err = New(string(g1.myAddr), false, g1.persistDir) 1064 if err != nil { 1065 t.Fatal(err) 1066 } 1067 defer g1.Close() 1068 1069 // Wait until g1 connects to g2. 1070 for i := 0; i < 100; i++ { 1071 if peers := g1.Peers(); len(peers) == 0 { 1072 time.Sleep(10 * time.Millisecond) 1073 } else if len(peers) == 1 && peers[0].NetAddress == g2.Address() { 1074 break 1075 } else { 1076 t.Fatal("something wrong with the peer list:", peers) 1077 } 1078 } 1079 // Wait until g1 connects to g3. 1080 for i := 0; i < 100; i++ { 1081 if peers := g1.Peers(); len(peers) == 1 { 1082 time.Sleep(10 * time.Millisecond) 1083 } else if len(peers) == 2 { 1084 break 1085 } else { 1086 t.Fatal("something wrong with the peer list:", peers) 1087 } 1088 } 1089 } 1090 1091 // TestPeerManagerOutboundSave sets up an island of nodes and checks that they 1092 // can all connect to eachother, and that the all add eachother as 1093 // 'WasOutboundPeer'. 1094 func TestPeerManagerOutboundSave(t *testing.T) { 1095 if testing.Short() { 1096 t.SkipNow() 1097 } 1098 t.Parallel() 1099 1100 // Create enough gateways so that every gateway should automatically end up 1101 // with every other gateway as an outbound peer. 1102 var gs []*Gateway 1103 for i := 0; i < wellConnectedThreshold+1; i++ { 1104 gs = append(gs, newNamedTestingGateway(t, strconv.Itoa(i))) 1105 } 1106 // Connect g1 to each peer. This should be enough that every peer eventually 1107 // has the full set of outbound peers. 1108 for _, g := range gs[1:] { 1109 if err := gs[0].Connect(g.Address()); err != nil { 1110 t.Fatal(err) 1111 } 1112 } 1113 1114 // Block until every peer has wellConnectedThreshold outbound peers. 1115 err := build.Retry(100, time.Millisecond*200, func() error { 1116 for _, g := range gs { 1117 var outboundNodes, outboundPeers int 1118 g.mu.RLock() 1119 for _, node := range g.nodes { 1120 if node.WasOutboundPeer { 1121 outboundNodes++ 1122 } 1123 } 1124 for _, peer := range g.peers { 1125 if !peer.Inbound { 1126 outboundPeers++ 1127 } 1128 } 1129 g.mu.RUnlock() 1130 if outboundNodes < wellConnectedThreshold { 1131 return errors.New("not enough outbound nodes: " + strconv.Itoa(outboundNodes)) 1132 } 1133 if outboundPeers < wellConnectedThreshold { 1134 return errors.New("not enough outbound peers: " + strconv.Itoa(outboundPeers)) 1135 } 1136 } 1137 return nil 1138 }) 1139 if err != nil { 1140 t.Fatal(err) 1141 } 1142 } 1143 1144 // TestBuildPeerManagerNodeList tests the buildPeerManagerNodeList method. 1145 func TestBuildPeerManagerNodeList(t *testing.T) { 1146 g := &Gateway{ 1147 nodes: map[modules.NetAddress]*node{ 1148 "foo": {NetAddress: "foo", WasOutboundPeer: true}, 1149 "bar": {NetAddress: "bar", WasOutboundPeer: false}, 1150 "baz": {NetAddress: "baz", WasOutboundPeer: true}, 1151 "quux": {NetAddress: "quux", WasOutboundPeer: false}, 1152 }, 1153 } 1154 nodelist := g.buildPeerManagerNodeList() 1155 // all outbound nodes should be at the front of the list 1156 var i int 1157 for i < len(nodelist) && g.nodes[nodelist[i]].WasOutboundPeer { 1158 i++ 1159 } 1160 for i < len(nodelist) && !g.nodes[nodelist[i]].WasOutboundPeer { 1161 i++ 1162 } 1163 if i != len(nodelist) { 1164 t.Fatal("bad nodelist:", nodelist) 1165 } 1166 }