github.com/ethereumproject/go-ethereum@v5.5.2+incompatible/p2p/dial_test.go (about)

     1  // Copyright 2015 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-ethereum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package p2p
    18  
    19  import (
    20  	"encoding/binary"
    21  	"net"
    22  	"reflect"
    23  	"testing"
    24  	"time"
    25  
    26  	"github.com/davecgh/go-spew/spew"
    27  	"github.com/ethereumproject/go-ethereum/p2p/discover"
    28  )
    29  
    30  func init() {
    31  	spew.Config.Indent = "\t"
    32  }
    33  
    34  type dialtest struct {
    35  	init   *dialstate // state before and after the test.
    36  	rounds []round
    37  }
    38  
    39  type round struct {
    40  	peers []*Peer // current peer set
    41  	done  []task  // tasks that got done this round
    42  	new   []task  // the result must match this one
    43  }
    44  
    45  func runDialTest(t *testing.T, test dialtest) {
    46  	var (
    47  		vtime   time.Time
    48  		running int
    49  	)
    50  	pm := func(ps []*Peer) map[discover.NodeID]*Peer {
    51  		m := make(map[discover.NodeID]*Peer)
    52  		for _, p := range ps {
    53  			m[p.rw.id] = p
    54  		}
    55  		return m
    56  	}
    57  	for i, round := range test.rounds {
    58  		for _, task := range round.done {
    59  			running--
    60  			if running < 0 {
    61  				panic("running task counter underflow")
    62  			}
    63  			test.init.taskDone(task, vtime)
    64  		}
    65  
    66  		new := test.init.newTasks(running, pm(round.peers), vtime)
    67  		if !sametasks(new, round.new) {
    68  			t.Errorf("round %d: new tasks mismatch:\ngot %v\nwant %v\nstate: %v\nrunning: %v\n",
    69  				i, spew.Sdump(new), spew.Sdump(round.new), spew.Sdump(test.init), spew.Sdump(running))
    70  		}
    71  
    72  		// Time advances by 16 seconds on every round.
    73  		vtime = vtime.Add(16 * time.Second)
    74  		running += len(new)
    75  	}
    76  }
    77  
    78  type fakeTable []*discover.Node
    79  
    80  func (t fakeTable) Self() *discover.Node                     { return new(discover.Node) }
    81  func (t fakeTable) Close()                                   {}
    82  func (t fakeTable) Lookup(discover.NodeID) []*discover.Node  { return nil }
    83  func (t fakeTable) Resolve(discover.NodeID) *discover.Node   { return nil }
    84  func (t fakeTable) ReadRandomNodes(buf []*discover.Node) int { return copy(buf, t) }
    85  
    86  // This test checks that dynamic dials are launched from discovery results.
    87  func TestDialStateDynDial(t *testing.T) {
    88  	runDialTest(t, dialtest{
    89  		init: newDialState(nil, fakeTable{}, 5),
    90  		rounds: []round{
    91  			// A discovery query is launched.
    92  			{
    93  				peers: []*Peer{
    94  					{rw: &conn{flags: staticDialedConn, id: uintID(0)}},
    95  					{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
    96  					{rw: &conn{flags: dynDialedConn, id: uintID(2)}},
    97  				},
    98  				new: []task{&discoverTask{}},
    99  			},
   100  			// Dynamic dials are launched when it completes.
   101  			{
   102  				peers: []*Peer{
   103  					{rw: &conn{flags: staticDialedConn, id: uintID(0)}},
   104  					{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
   105  					{rw: &conn{flags: dynDialedConn, id: uintID(2)}},
   106  				},
   107  				done: []task{
   108  					&discoverTask{results: []*discover.Node{
   109  						{ID: uintID(2)}, // this one is already connected and not dialed.
   110  						{ID: uintID(3)},
   111  						{ID: uintID(4)},
   112  						{ID: uintID(5)},
   113  						{ID: uintID(6)}, // these are not tried because max dyn dials is 5
   114  						{ID: uintID(7)}, // ...
   115  					}},
   116  				},
   117  				new: []task{
   118  					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(3)}},
   119  					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(4)}},
   120  					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(5)}},
   121  				},
   122  			},
   123  			// Some of the dials complete but no new ones are launched yet because
   124  			// the sum of active dial count and dynamic peer count is == maxDynDials.
   125  			{
   126  				peers: []*Peer{
   127  					{rw: &conn{flags: staticDialedConn, id: uintID(0)}},
   128  					{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
   129  					{rw: &conn{flags: dynDialedConn, id: uintID(2)}},
   130  					{rw: &conn{flags: dynDialedConn, id: uintID(3)}},
   131  					{rw: &conn{flags: dynDialedConn, id: uintID(4)}},
   132  				},
   133  				done: []task{
   134  					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(3)}},
   135  					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(4)}},
   136  				},
   137  			},
   138  			// No new dial tasks are launched in the this round because
   139  			// maxDynDials has been reached.
   140  			{
   141  				peers: []*Peer{
   142  					{rw: &conn{flags: staticDialedConn, id: uintID(0)}},
   143  					{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
   144  					{rw: &conn{flags: dynDialedConn, id: uintID(2)}},
   145  					{rw: &conn{flags: dynDialedConn, id: uintID(3)}},
   146  					{rw: &conn{flags: dynDialedConn, id: uintID(4)}},
   147  					{rw: &conn{flags: dynDialedConn, id: uintID(5)}},
   148  				},
   149  				done: []task{
   150  					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(5)}},
   151  				},
   152  				new: []task{
   153  					&waitExpireTask{Duration: 14 * time.Second},
   154  				},
   155  			},
   156  			// In this round, the peer with id 2 drops off. The query
   157  			// results from last discovery lookup are reused.
   158  			{
   159  				peers: []*Peer{
   160  					{rw: &conn{flags: staticDialedConn, id: uintID(0)}},
   161  					{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
   162  					{rw: &conn{flags: dynDialedConn, id: uintID(3)}},
   163  					{rw: &conn{flags: dynDialedConn, id: uintID(4)}},
   164  					{rw: &conn{flags: dynDialedConn, id: uintID(5)}},
   165  				},
   166  				new: []task{
   167  					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(6)}},
   168  				},
   169  			},
   170  			// More peers (3,4) drop off and dial for ID 6 completes.
   171  			// The last query result from the discovery lookup is reused
   172  			// and a new one is spawned because more candidates are needed.
   173  			{
   174  				peers: []*Peer{
   175  					{rw: &conn{flags: staticDialedConn, id: uintID(0)}},
   176  					{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
   177  					{rw: &conn{flags: dynDialedConn, id: uintID(5)}},
   178  				},
   179  				done: []task{
   180  					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(6)}},
   181  				},
   182  				new: []task{
   183  					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(7)}},
   184  					&discoverTask{},
   185  				},
   186  			},
   187  			// Peer 7 is connected, but there still aren't enough dynamic peers
   188  			// (4 out of 5). However, a discovery is already running, so ensure
   189  			// no new is started.
   190  			{
   191  				peers: []*Peer{
   192  					{rw: &conn{flags: staticDialedConn, id: uintID(0)}},
   193  					{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
   194  					{rw: &conn{flags: dynDialedConn, id: uintID(5)}},
   195  					{rw: &conn{flags: dynDialedConn, id: uintID(7)}},
   196  				},
   197  				done: []task{
   198  					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(7)}},
   199  				},
   200  			},
   201  			// Finish the running node discovery with an empty set. A new lookup
   202  			// should be immediately requested.
   203  			{
   204  				peers: []*Peer{
   205  					{rw: &conn{flags: staticDialedConn, id: uintID(0)}},
   206  					{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
   207  					{rw: &conn{flags: dynDialedConn, id: uintID(5)}},
   208  					{rw: &conn{flags: dynDialedConn, id: uintID(7)}},
   209  				},
   210  				done: []task{
   211  					&discoverTask{},
   212  				},
   213  				new: []task{
   214  					&discoverTask{},
   215  				},
   216  			},
   217  		},
   218  	})
   219  }
   220  
   221  func TestDialStateDynDialFromTable(t *testing.T) {
   222  	// This table always returns the same random nodes
   223  	// in the order given below.
   224  	table := fakeTable{
   225  		{ID: uintID(1)},
   226  		{ID: uintID(2)},
   227  		{ID: uintID(3)},
   228  		{ID: uintID(4)},
   229  		{ID: uintID(5)},
   230  		{ID: uintID(6)},
   231  		{ID: uintID(7)},
   232  		{ID: uintID(8)},
   233  	}
   234  
   235  	runDialTest(t, dialtest{
   236  		init: newDialState(nil, table, 10),
   237  		rounds: []round{
   238  			// 5 out of 8 of the nodes returned by ReadRandomNodes are dialed.
   239  			{
   240  				new: []task{
   241  					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(1)}},
   242  					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(2)}},
   243  					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(3)}},
   244  					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(4)}},
   245  					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(5)}},
   246  					&discoverTask{},
   247  				},
   248  			},
   249  			// Dialing nodes 1,2 succeeds. Dials from the lookup are launched.
   250  			{
   251  				peers: []*Peer{
   252  					{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
   253  					{rw: &conn{flags: dynDialedConn, id: uintID(2)}},
   254  				},
   255  				done: []task{
   256  					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(1)}},
   257  					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(2)}},
   258  					&discoverTask{results: []*discover.Node{
   259  						{ID: uintID(10)},
   260  						{ID: uintID(11)},
   261  						{ID: uintID(12)},
   262  					}},
   263  				},
   264  				new: []task{
   265  					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(10)}},
   266  					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(11)}},
   267  					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(12)}},
   268  					&discoverTask{},
   269  				},
   270  			},
   271  			// Dialing nodes 3,4,5 fails. The dials from the lookup succeed.
   272  			{
   273  				peers: []*Peer{
   274  					{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
   275  					{rw: &conn{flags: dynDialedConn, id: uintID(2)}},
   276  					{rw: &conn{flags: dynDialedConn, id: uintID(10)}},
   277  					{rw: &conn{flags: dynDialedConn, id: uintID(11)}},
   278  					{rw: &conn{flags: dynDialedConn, id: uintID(12)}},
   279  				},
   280  				done: []task{
   281  					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(3)}},
   282  					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(4)}},
   283  					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(5)}},
   284  					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(10)}},
   285  					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(11)}},
   286  					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(12)}},
   287  				},
   288  			},
   289  			// Waiting for expiry. No waitExpireTask is launched because the
   290  			// discovery query is still running.
   291  			{
   292  				peers: []*Peer{
   293  					{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
   294  					{rw: &conn{flags: dynDialedConn, id: uintID(2)}},
   295  					{rw: &conn{flags: dynDialedConn, id: uintID(10)}},
   296  					{rw: &conn{flags: dynDialedConn, id: uintID(11)}},
   297  					{rw: &conn{flags: dynDialedConn, id: uintID(12)}},
   298  				},
   299  			},
   300  			// Nodes 3,4 are not tried again because only the first two
   301  			// returned random nodes (nodes 1,2) are tried and they're
   302  			// already connected.
   303  			{
   304  				peers: []*Peer{
   305  					{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
   306  					{rw: &conn{flags: dynDialedConn, id: uintID(2)}},
   307  					{rw: &conn{flags: dynDialedConn, id: uintID(10)}},
   308  					{rw: &conn{flags: dynDialedConn, id: uintID(11)}},
   309  					{rw: &conn{flags: dynDialedConn, id: uintID(12)}},
   310  				},
   311  			},
   312  		},
   313  	})
   314  }
   315  
   316  // This test checks that static dials are launched.
   317  func TestDialStateStaticDial(t *testing.T) {
   318  	wantStatic := []*discover.Node{
   319  		{ID: uintID(1)},
   320  		{ID: uintID(2)},
   321  		{ID: uintID(3)},
   322  		{ID: uintID(4)},
   323  		{ID: uintID(5)},
   324  	}
   325  
   326  	runDialTest(t, dialtest{
   327  		init: newDialState(wantStatic, fakeTable{}, 0),
   328  		rounds: []round{
   329  			// Static dials are launched for the nodes that
   330  			// aren't yet connected.
   331  			{
   332  				peers: []*Peer{
   333  					{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
   334  					{rw: &conn{flags: dynDialedConn, id: uintID(2)}},
   335  				},
   336  				new: []task{
   337  					&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(3)}},
   338  					&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(4)}},
   339  					&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(5)}},
   340  				},
   341  			},
   342  			// No new tasks are launched in this round because all static
   343  			// nodes are either connected or still being dialed.
   344  			{
   345  				peers: []*Peer{
   346  					{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
   347  					{rw: &conn{flags: dynDialedConn, id: uintID(2)}},
   348  					{rw: &conn{flags: staticDialedConn, id: uintID(3)}},
   349  				},
   350  				done: []task{
   351  					&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(3)}},
   352  				},
   353  			},
   354  			// No new dial tasks are launched because all static
   355  			// nodes are now connected.
   356  			{
   357  				peers: []*Peer{
   358  					{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
   359  					{rw: &conn{flags: dynDialedConn, id: uintID(2)}},
   360  					{rw: &conn{flags: staticDialedConn, id: uintID(3)}},
   361  					{rw: &conn{flags: staticDialedConn, id: uintID(4)}},
   362  					{rw: &conn{flags: staticDialedConn, id: uintID(5)}},
   363  				},
   364  				done: []task{
   365  					&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(4)}},
   366  					&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(5)}},
   367  				},
   368  				new: []task{
   369  					&waitExpireTask{Duration: 14 * time.Second},
   370  				},
   371  			},
   372  			// Wait a round for dial history to expire, no new tasks should spawn.
   373  			{
   374  				peers: []*Peer{
   375  					{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
   376  					{rw: &conn{flags: dynDialedConn, id: uintID(2)}},
   377  					{rw: &conn{flags: staticDialedConn, id: uintID(3)}},
   378  					{rw: &conn{flags: staticDialedConn, id: uintID(4)}},
   379  					{rw: &conn{flags: staticDialedConn, id: uintID(5)}},
   380  				},
   381  			},
   382  			// If a static node is dropped, it should be immediately redialed,
   383  			// irrespective whether it was originally static or dynamic.
   384  			{
   385  				peers: []*Peer{
   386  					{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
   387  					{rw: &conn{flags: staticDialedConn, id: uintID(3)}},
   388  					{rw: &conn{flags: staticDialedConn, id: uintID(5)}},
   389  				},
   390  				new: []task{
   391  					&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(2)}},
   392  					&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(4)}},
   393  				},
   394  			},
   395  		},
   396  	})
   397  }
   398  
   399  // This test checks that past dials are not retried for some time.
   400  func TestDialStateCache(t *testing.T) {
   401  	wantStatic := []*discover.Node{
   402  		{ID: uintID(1)},
   403  		{ID: uintID(2)},
   404  		{ID: uintID(3)},
   405  	}
   406  
   407  	runDialTest(t, dialtest{
   408  		init: newDialState(wantStatic, fakeTable{}, 0),
   409  		rounds: []round{
   410  			// Static dials are launched for the nodes that
   411  			// aren't yet connected.
   412  			{
   413  				peers: nil,
   414  				new: []task{
   415  					&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(1)}},
   416  					&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(2)}},
   417  					&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(3)}},
   418  				},
   419  			},
   420  			// No new tasks are launched in this round because all static
   421  			// nodes are either connected or still being dialed.
   422  			{
   423  				peers: []*Peer{
   424  					{rw: &conn{flags: staticDialedConn, id: uintID(1)}},
   425  					{rw: &conn{flags: staticDialedConn, id: uintID(2)}},
   426  				},
   427  				done: []task{
   428  					&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(1)}},
   429  					&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(2)}},
   430  				},
   431  			},
   432  			// A salvage task is launched to wait for node 3's history
   433  			// entry to expire.
   434  			{
   435  				peers: []*Peer{
   436  					{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
   437  					{rw: &conn{flags: dynDialedConn, id: uintID(2)}},
   438  				},
   439  				done: []task{
   440  					&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(3)}},
   441  				},
   442  				new: []task{
   443  					&waitExpireTask{Duration: 14 * time.Second},
   444  				},
   445  			},
   446  			// Still waiting for node 3's entry to expire in the cache.
   447  			{
   448  				peers: []*Peer{
   449  					{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
   450  					{rw: &conn{flags: dynDialedConn, id: uintID(2)}},
   451  				},
   452  			},
   453  			// The cache entry for node 3 has expired and is retried.
   454  			{
   455  				peers: []*Peer{
   456  					{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
   457  					{rw: &conn{flags: dynDialedConn, id: uintID(2)}},
   458  				},
   459  				new: []task{
   460  					&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(3)}},
   461  				},
   462  			},
   463  		},
   464  	})
   465  }
   466  
   467  func TestDialResolve(t *testing.T) {
   468  	resolved := discover.NewNode(uintID(1), net.IP{127, 0, 55, 234}, 3333, 4444)
   469  	table := &resolveMock{answer: resolved}
   470  	state := newDialState(nil, table, 0)
   471  
   472  	// Check that the task is generated with an incomplete ID.
   473  	dest := discover.NewNode(uintID(1), nil, 0, 0)
   474  	state.addStatic(dest)
   475  	tasks := state.newTasks(0, nil, time.Time{})
   476  	if !reflect.DeepEqual(tasks, []task{&dialTask{flags: staticDialedConn, dest: dest}}) {
   477  		t.Fatalf("expected dial task, got %#v", tasks)
   478  	}
   479  
   480  	// Now run the task, it should resolve the ID once.
   481  	config := Config{Dialer: &net.Dialer{Deadline: time.Now().Add(-5 * time.Minute)}}
   482  	srv := &Server{ntab: table, Config: config}
   483  	tasks[0].Do(srv)
   484  	if !reflect.DeepEqual(table.resolveCalls, []discover.NodeID{dest.ID}) {
   485  		t.Fatalf("wrong resolve calls, got %v", table.resolveCalls)
   486  	}
   487  
   488  	// Report it as done to the dialer, which should update the static node record.
   489  	state.taskDone(tasks[0], time.Now())
   490  	if state.static[uintID(1)].dest != resolved {
   491  		t.Fatalf("state.dest not updated")
   492  	}
   493  }
   494  
   495  // compares task lists but doesn't care about the order.
   496  func sametasks(a, b []task) bool {
   497  	if len(a) != len(b) {
   498  		return false
   499  	}
   500  next:
   501  	for _, ta := range a {
   502  		for _, tb := range b {
   503  			if reflect.DeepEqual(ta, tb) {
   504  				continue next
   505  			}
   506  		}
   507  		return false
   508  	}
   509  	return true
   510  }
   511  
   512  func uintID(i uint32) discover.NodeID {
   513  	var id discover.NodeID
   514  	binary.BigEndian.PutUint32(id[:], i)
   515  	return id
   516  }
   517  
   518  // implements discoverTable for TestDialResolve
   519  type resolveMock struct {
   520  	resolveCalls []discover.NodeID
   521  	answer       *discover.Node
   522  }
   523  
   524  func (t *resolveMock) Resolve(id discover.NodeID) *discover.Node {
   525  	t.resolveCalls = append(t.resolveCalls, id)
   526  	return t.answer
   527  }
   528  
   529  func (t *resolveMock) Self() *discover.Node                     { return new(discover.Node) }
   530  func (t *resolveMock) Close()                                   {}
   531  func (t *resolveMock) Bootstrap([]*discover.Node)               {}
   532  func (t *resolveMock) Lookup(discover.NodeID) []*discover.Node  { return nil }
   533  func (t *resolveMock) ReadRandomNodes(buf []*discover.Node) int { return 0 }