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  }