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  }