github.com/SmartMeshFoundation/Spectrum@v0.0.0-20220621030607-452a266fee1e/p2p/dial_test.go (about)

     1  // Copyright 2015 The Spectrum Authors
     2  // This file is part of the Spectrum library.
     3  //
     4  // The Spectrum 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 Spectrum 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 Spectrum 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/SmartMeshFoundation/Spectrum/p2p/discover"
    27  	"github.com/SmartMeshFoundation/Spectrum/p2p/netutil"
    28  	"github.com/davecgh/go-spew/spew"
    29  )
    30  
    31  func init() {
    32  	spew.Config.Indent = "\t"
    33  }
    34  
    35  type dialtest struct {
    36  	init   *dialstate // state before and after the test.
    37  	rounds []round
    38  }
    39  
    40  type round struct {
    41  	peers []*Peer // current peer set
    42  	done  []task  // tasks that got done this round
    43  	new   []task  // the result must match this one
    44  }
    45  
    46  func runDialTest(t *testing.T, test dialtest) {
    47  	var (
    48  		vtime   time.Time
    49  		running int
    50  	)
    51  	pm := func(ps []*Peer) map[discover.NodeID]*Peer {
    52  		m := make(map[discover.NodeID]*Peer)
    53  		for _, p := range ps {
    54  			m[p.rw.id] = p
    55  		}
    56  		return m
    57  	}
    58  	for i, round := range test.rounds {
    59  		for _, task := range round.done {
    60  			running--
    61  			if running < 0 {
    62  				panic("running task counter underflow")
    63  			}
    64  			test.init.taskDone(task, vtime)
    65  		}
    66  
    67  		new := test.init.newTasks(running, pm(round.peers), vtime)
    68  		if !sametasks(new, round.new) {
    69  			t.Errorf("round %d: new tasks mismatch:\ngot %v\nwant %v\nstate: %v\nrunning: %v\n",
    70  				i, spew.Sdump(new), spew.Sdump(round.new), spew.Sdump(test.init), spew.Sdump(running))
    71  		}
    72  
    73  		// Time advances by 16 seconds on every round.
    74  		vtime = vtime.Add(16 * time.Second)
    75  		running += len(new)
    76  	}
    77  }
    78  
    79  type fakeTable []*discover.Node
    80  
    81  func (t fakeTable) Self() *discover.Node                     { return new(discover.Node) }
    82  func (t fakeTable) Close()                                   {}
    83  func (t fakeTable) Lookup(discover.NodeID) []*discover.Node  { return nil }
    84  func (t fakeTable) Resolve(discover.NodeID) *discover.Node   { return nil }
    85  func (t fakeTable) ReadRandomNodes(buf []*discover.Node) int { return copy(buf, t) }
    86  
    87  // This test checks that dynamic dials are launched from discovery results.
    88  func TestDialStateDynDial(t *testing.T) {
    89  	runDialTest(t, dialtest{
    90  		init: newDialState(nil, nil, fakeTable{}, 5, nil),
    91  		rounds: []round{
    92  			// A discovery query is launched.
    93  			{
    94  				peers: []*Peer{
    95  					{rw: &conn{flags: staticDialedConn, id: uintID(0)}},
    96  					{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
    97  					{rw: &conn{flags: dynDialedConn, id: uintID(2)}},
    98  				},
    99  				new: []task{&discoverTask{}},
   100  			},
   101  			// Dynamic dials are launched when it completes.
   102  			{
   103  				peers: []*Peer{
   104  					{rw: &conn{flags: staticDialedConn, id: uintID(0)}},
   105  					{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
   106  					{rw: &conn{flags: dynDialedConn, id: uintID(2)}},
   107  				},
   108  				done: []task{
   109  					&discoverTask{results: []*discover.Node{
   110  						{ID: uintID(2)}, // this one is already connected and not dialed.
   111  						{ID: uintID(3)},
   112  						{ID: uintID(4)},
   113  						{ID: uintID(5)},
   114  						{ID: uintID(6)}, // these are not tried because max dyn dials is 5
   115  						{ID: uintID(7)}, // ...
   116  					}},
   117  				},
   118  				new: []task{
   119  					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(3)}},
   120  					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(4)}},
   121  					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(5)}},
   122  				},
   123  			},
   124  			// Some of the dials complete but no new ones are launched yet because
   125  			// the sum of active dial count and dynamic peer count is == maxDynDials.
   126  			{
   127  				peers: []*Peer{
   128  					{rw: &conn{flags: staticDialedConn, id: uintID(0)}},
   129  					{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
   130  					{rw: &conn{flags: dynDialedConn, id: uintID(2)}},
   131  					{rw: &conn{flags: dynDialedConn, id: uintID(3)}},
   132  					{rw: &conn{flags: dynDialedConn, id: uintID(4)}},
   133  				},
   134  				done: []task{
   135  					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(3)}},
   136  					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(4)}},
   137  				},
   138  			},
   139  			// No new dial tasks are launched in the this round because
   140  			// maxDynDials has been reached.
   141  			{
   142  				peers: []*Peer{
   143  					{rw: &conn{flags: staticDialedConn, id: uintID(0)}},
   144  					{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
   145  					{rw: &conn{flags: dynDialedConn, id: uintID(2)}},
   146  					{rw: &conn{flags: dynDialedConn, id: uintID(3)}},
   147  					{rw: &conn{flags: dynDialedConn, id: uintID(4)}},
   148  					{rw: &conn{flags: dynDialedConn, id: uintID(5)}},
   149  				},
   150  				done: []task{
   151  					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(5)}},
   152  				},
   153  				new: []task{
   154  					&waitExpireTask{Duration: 14 * time.Second},
   155  				},
   156  			},
   157  			// In this round, the peer with id 2 drops off. The query
   158  			// results from last discovery lookup are reused.
   159  			{
   160  				peers: []*Peer{
   161  					{rw: &conn{flags: staticDialedConn, id: uintID(0)}},
   162  					{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
   163  					{rw: &conn{flags: dynDialedConn, id: uintID(3)}},
   164  					{rw: &conn{flags: dynDialedConn, id: uintID(4)}},
   165  					{rw: &conn{flags: dynDialedConn, id: uintID(5)}},
   166  				},
   167  				new: []task{
   168  					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(6)}},
   169  				},
   170  			},
   171  			// More peers (3,4) drop off and dial for ID 6 completes.
   172  			// The last query result from the discovery lookup is reused
   173  			// and a new one is spawned because more candidates are needed.
   174  			{
   175  				peers: []*Peer{
   176  					{rw: &conn{flags: staticDialedConn, id: uintID(0)}},
   177  					{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
   178  					{rw: &conn{flags: dynDialedConn, id: uintID(5)}},
   179  				},
   180  				done: []task{
   181  					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(6)}},
   182  				},
   183  				new: []task{
   184  					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(7)}},
   185  					&discoverTask{},
   186  				},
   187  			},
   188  			// Peer 7 is connected, but there still aren't enough dynamic peers
   189  			// (4 out of 5). However, a discovery is already running, so ensure
   190  			// no new is started.
   191  			{
   192  				peers: []*Peer{
   193  					{rw: &conn{flags: staticDialedConn, id: uintID(0)}},
   194  					{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
   195  					{rw: &conn{flags: dynDialedConn, id: uintID(5)}},
   196  					{rw: &conn{flags: dynDialedConn, id: uintID(7)}},
   197  				},
   198  				done: []task{
   199  					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(7)}},
   200  				},
   201  			},
   202  			// Finish the running node discovery with an empty set. A new lookup
   203  			// should be immediately requested.
   204  			{
   205  				peers: []*Peer{
   206  					{rw: &conn{flags: staticDialedConn, id: uintID(0)}},
   207  					{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
   208  					{rw: &conn{flags: dynDialedConn, id: uintID(5)}},
   209  					{rw: &conn{flags: dynDialedConn, id: uintID(7)}},
   210  				},
   211  				done: []task{
   212  					&discoverTask{},
   213  				},
   214  				new: []task{
   215  					&discoverTask{},
   216  				},
   217  			},
   218  		},
   219  	})
   220  }
   221  
   222  // Tests that bootnodes are dialed if no peers are connectd, but not otherwise.
   223  func TestDialStateDynDialBootnode(t *testing.T) {
   224  	bootnodes := []*discover.Node{
   225  		{ID: uintID(1)},
   226  		{ID: uintID(2)},
   227  		{ID: uintID(3)},
   228  	}
   229  	table := fakeTable{
   230  		{ID: uintID(4)},
   231  		{ID: uintID(5)},
   232  		{ID: uintID(6)},
   233  		{ID: uintID(7)},
   234  		{ID: uintID(8)},
   235  	}
   236  	runDialTest(t, dialtest{
   237  		init: newDialState(nil, bootnodes, table, 5, nil),
   238  		rounds: []round{
   239  			// 2 dynamic dials attempted, bootnodes pending fallback interval
   240  			{
   241  				new: []task{
   242  					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(4)}},
   243  					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(5)}},
   244  					&discoverTask{},
   245  				},
   246  			},
   247  			// No dials succeed, bootnodes still pending fallback interval
   248  			{
   249  				done: []task{
   250  					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(4)}},
   251  					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(5)}},
   252  				},
   253  			},
   254  			// No dials succeed, bootnodes still pending fallback interval
   255  			{},
   256  			// No dials succeed, 2 dynamic dials attempted and 1 bootnode too as fallback interval was reached
   257  			{
   258  				new: []task{
   259  					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(1)}},
   260  					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(4)}},
   261  					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(5)}},
   262  				},
   263  			},
   264  			// No dials succeed, 2nd bootnode is attempted
   265  			{
   266  				done: []task{
   267  					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(1)}},
   268  					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(4)}},
   269  					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(5)}},
   270  				},
   271  				new: []task{
   272  					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(2)}},
   273  				},
   274  			},
   275  			// No dials succeed, 3rd bootnode is attempted
   276  			{
   277  				done: []task{
   278  					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(2)}},
   279  				},
   280  				new: []task{
   281  					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(3)}},
   282  				},
   283  			},
   284  			// No dials succeed, 1st bootnode is attempted again, expired random nodes retried
   285  			{
   286  				done: []task{
   287  					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(3)}},
   288  				},
   289  				new: []task{
   290  					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(1)}},
   291  					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(4)}},
   292  					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(5)}},
   293  				},
   294  			},
   295  			// Random dial succeeds, no more bootnodes are attempted
   296  			{
   297  				peers: []*Peer{
   298  					{rw: &conn{flags: dynDialedConn, id: uintID(4)}},
   299  				},
   300  				done: []task{
   301  					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(1)}},
   302  					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(4)}},
   303  					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(5)}},
   304  				},
   305  			},
   306  		},
   307  	})
   308  }
   309  
   310  func TestDialStateDynDialFromTable(t *testing.T) {
   311  	// This table always returns the same random nodes
   312  	// in the order given below.
   313  	table := fakeTable{
   314  		{ID: uintID(1)},
   315  		{ID: uintID(2)},
   316  		{ID: uintID(3)},
   317  		{ID: uintID(4)},
   318  		{ID: uintID(5)},
   319  		{ID: uintID(6)},
   320  		{ID: uintID(7)},
   321  		{ID: uintID(8)},
   322  	}
   323  
   324  	runDialTest(t, dialtest{
   325  		init: newDialState(nil, nil, table, 10, nil),
   326  		rounds: []round{
   327  			// 5 out of 8 of the nodes returned by ReadRandomNodes are dialed.
   328  			{
   329  				new: []task{
   330  					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(1)}},
   331  					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(2)}},
   332  					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(3)}},
   333  					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(4)}},
   334  					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(5)}},
   335  					&discoverTask{},
   336  				},
   337  			},
   338  			// Dialing nodes 1,2 succeeds. Dials from the lookup are launched.
   339  			{
   340  				peers: []*Peer{
   341  					{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
   342  					{rw: &conn{flags: dynDialedConn, id: uintID(2)}},
   343  				},
   344  				done: []task{
   345  					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(1)}},
   346  					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(2)}},
   347  					&discoverTask{results: []*discover.Node{
   348  						{ID: uintID(10)},
   349  						{ID: uintID(11)},
   350  						{ID: uintID(12)},
   351  					}},
   352  				},
   353  				new: []task{
   354  					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(10)}},
   355  					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(11)}},
   356  					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(12)}},
   357  					&discoverTask{},
   358  				},
   359  			},
   360  			// Dialing nodes 3,4,5 fails. The dials from the lookup succeed.
   361  			{
   362  				peers: []*Peer{
   363  					{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
   364  					{rw: &conn{flags: dynDialedConn, id: uintID(2)}},
   365  					{rw: &conn{flags: dynDialedConn, id: uintID(10)}},
   366  					{rw: &conn{flags: dynDialedConn, id: uintID(11)}},
   367  					{rw: &conn{flags: dynDialedConn, id: uintID(12)}},
   368  				},
   369  				done: []task{
   370  					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(3)}},
   371  					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(4)}},
   372  					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(5)}},
   373  					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(10)}},
   374  					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(11)}},
   375  					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(12)}},
   376  				},
   377  			},
   378  			// Waiting for expiry. No waitExpireTask is launched because the
   379  			// discovery query is still running.
   380  			{
   381  				peers: []*Peer{
   382  					{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
   383  					{rw: &conn{flags: dynDialedConn, id: uintID(2)}},
   384  					{rw: &conn{flags: dynDialedConn, id: uintID(10)}},
   385  					{rw: &conn{flags: dynDialedConn, id: uintID(11)}},
   386  					{rw: &conn{flags: dynDialedConn, id: uintID(12)}},
   387  				},
   388  			},
   389  			// Nodes 3,4 are not tried again because only the first two
   390  			// returned random nodes (nodes 1,2) are tried and they're
   391  			// already connected.
   392  			{
   393  				peers: []*Peer{
   394  					{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
   395  					{rw: &conn{flags: dynDialedConn, id: uintID(2)}},
   396  					{rw: &conn{flags: dynDialedConn, id: uintID(10)}},
   397  					{rw: &conn{flags: dynDialedConn, id: uintID(11)}},
   398  					{rw: &conn{flags: dynDialedConn, id: uintID(12)}},
   399  				},
   400  			},
   401  		},
   402  	})
   403  }
   404  
   405  // This test checks that candidates that do not match the netrestrict list are not dialed.
   406  func TestDialStateNetRestrict(t *testing.T) {
   407  	// This table always returns the same random nodes
   408  	// in the order given below.
   409  	table := fakeTable{
   410  		{ID: uintID(1), IP: net.ParseIP("127.0.0.1")},
   411  		{ID: uintID(2), IP: net.ParseIP("127.0.0.2")},
   412  		{ID: uintID(3), IP: net.ParseIP("127.0.0.3")},
   413  		{ID: uintID(4), IP: net.ParseIP("127.0.0.4")},
   414  		{ID: uintID(5), IP: net.ParseIP("127.0.2.5")},
   415  		{ID: uintID(6), IP: net.ParseIP("127.0.2.6")},
   416  		{ID: uintID(7), IP: net.ParseIP("127.0.2.7")},
   417  		{ID: uintID(8), IP: net.ParseIP("127.0.2.8")},
   418  	}
   419  	restrict := new(netutil.Netlist)
   420  	restrict.Add("127.0.2.0/24")
   421  
   422  	runDialTest(t, dialtest{
   423  		init: newDialState(nil, nil, table, 10, restrict),
   424  		rounds: []round{
   425  			{
   426  				new: []task{
   427  					&dialTask{flags: dynDialedConn, dest: table[4]},
   428  					&discoverTask{},
   429  				},
   430  			},
   431  		},
   432  	})
   433  }
   434  
   435  // This test checks that static dials are launched.
   436  func TestDialStateStaticDial(t *testing.T) {
   437  	wantStatic := []*discover.Node{
   438  		{ID: uintID(1)},
   439  		{ID: uintID(2)},
   440  		{ID: uintID(3)},
   441  		{ID: uintID(4)},
   442  		{ID: uintID(5)},
   443  	}
   444  
   445  	runDialTest(t, dialtest{
   446  		init: newDialState(wantStatic, nil, fakeTable{}, 0, nil),
   447  		rounds: []round{
   448  			// Static dials are launched for the nodes that
   449  			// aren't yet connected.
   450  			{
   451  				peers: []*Peer{
   452  					{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
   453  					{rw: &conn{flags: dynDialedConn, id: uintID(2)}},
   454  				},
   455  				new: []task{
   456  					&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(3)}},
   457  					&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(4)}},
   458  					&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(5)}},
   459  				},
   460  			},
   461  			// No new tasks are launched in this round because all static
   462  			// nodes are either connected or still being dialed.
   463  			{
   464  				peers: []*Peer{
   465  					{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
   466  					{rw: &conn{flags: dynDialedConn, id: uintID(2)}},
   467  					{rw: &conn{flags: staticDialedConn, id: uintID(3)}},
   468  				},
   469  				done: []task{
   470  					&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(3)}},
   471  				},
   472  			},
   473  			// No new dial tasks are launched because all static
   474  			// nodes are now connected.
   475  			{
   476  				peers: []*Peer{
   477  					{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
   478  					{rw: &conn{flags: dynDialedConn, id: uintID(2)}},
   479  					{rw: &conn{flags: staticDialedConn, id: uintID(3)}},
   480  					{rw: &conn{flags: staticDialedConn, id: uintID(4)}},
   481  					{rw: &conn{flags: staticDialedConn, id: uintID(5)}},
   482  				},
   483  				done: []task{
   484  					&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(4)}},
   485  					&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(5)}},
   486  				},
   487  				new: []task{
   488  					&waitExpireTask{Duration: 14 * time.Second},
   489  				},
   490  			},
   491  			// Wait a round for dial history to expire, no new tasks should spawn.
   492  			{
   493  				peers: []*Peer{
   494  					{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
   495  					{rw: &conn{flags: dynDialedConn, id: uintID(2)}},
   496  					{rw: &conn{flags: staticDialedConn, id: uintID(3)}},
   497  					{rw: &conn{flags: staticDialedConn, id: uintID(4)}},
   498  					{rw: &conn{flags: staticDialedConn, id: uintID(5)}},
   499  				},
   500  			},
   501  			// If a static node is dropped, it should be immediately redialed,
   502  			// irrespective whether it was originally static or dynamic.
   503  			{
   504  				peers: []*Peer{
   505  					{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
   506  					{rw: &conn{flags: staticDialedConn, id: uintID(3)}},
   507  					{rw: &conn{flags: staticDialedConn, id: uintID(5)}},
   508  				},
   509  				new: []task{
   510  					&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(2)}},
   511  					&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(4)}},
   512  				},
   513  			},
   514  		},
   515  	})
   516  }
   517  
   518  // This test checks that past dials are not retried for some time.
   519  func TestDialStateCache(t *testing.T) {
   520  	wantStatic := []*discover.Node{
   521  		{ID: uintID(1)},
   522  		{ID: uintID(2)},
   523  		{ID: uintID(3)},
   524  	}
   525  
   526  	runDialTest(t, dialtest{
   527  		init: newDialState(wantStatic, nil, fakeTable{}, 0, nil),
   528  		rounds: []round{
   529  			// Static dials are launched for the nodes that
   530  			// aren't yet connected.
   531  			{
   532  				peers: nil,
   533  				new: []task{
   534  					&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(1)}},
   535  					&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(2)}},
   536  					&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(3)}},
   537  				},
   538  			},
   539  			// No new tasks are launched in this round because all static
   540  			// nodes are either connected or still being dialed.
   541  			{
   542  				peers: []*Peer{
   543  					{rw: &conn{flags: staticDialedConn, id: uintID(1)}},
   544  					{rw: &conn{flags: staticDialedConn, id: uintID(2)}},
   545  				},
   546  				done: []task{
   547  					&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(1)}},
   548  					&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(2)}},
   549  				},
   550  			},
   551  			// A salvage task is launched to wait for node 3's history
   552  			// entry to expire.
   553  			{
   554  				peers: []*Peer{
   555  					{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
   556  					{rw: &conn{flags: dynDialedConn, id: uintID(2)}},
   557  				},
   558  				done: []task{
   559  					&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(3)}},
   560  				},
   561  				new: []task{
   562  					&waitExpireTask{Duration: 14 * time.Second},
   563  				},
   564  			},
   565  			// Still waiting for node 3's entry to expire in the cache.
   566  			{
   567  				peers: []*Peer{
   568  					{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
   569  					{rw: &conn{flags: dynDialedConn, id: uintID(2)}},
   570  				},
   571  			},
   572  			// The cache entry for node 3 has expired and is retried.
   573  			{
   574  				peers: []*Peer{
   575  					{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
   576  					{rw: &conn{flags: dynDialedConn, id: uintID(2)}},
   577  				},
   578  				new: []task{
   579  					&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(3)}},
   580  				},
   581  			},
   582  		},
   583  	})
   584  }
   585  
   586  func TestDialResolve(t *testing.T) {
   587  	resolved := discover.NewNode(uintID(1), net.IP{127, 0, 55, 234}, 3333, 4444)
   588  	table := &resolveMock{answer: resolved}
   589  	state := newDialState(nil, nil, table, 0, nil)
   590  
   591  	// Check that the task is generated with an incomplete ID.
   592  	dest := discover.NewNode(uintID(1), nil, 0, 0)
   593  	state.addStatic(dest)
   594  	tasks := state.newTasks(0, nil, time.Time{})
   595  	if !reflect.DeepEqual(tasks, []task{&dialTask{flags: staticDialedConn, dest: dest}}) {
   596  		t.Fatalf("expected dial task, got %#v", tasks)
   597  	}
   598  
   599  	// Now run the task, it should resolve the ID once.
   600  	config := Config{Dialer: TCPDialer{&net.Dialer{Deadline: time.Now().Add(-5 * time.Minute)}}}
   601  	srv := &Server{ntab: table, Config: config}
   602  	tasks[0].Do(srv)
   603  	if !reflect.DeepEqual(table.resolveCalls, []discover.NodeID{dest.ID}) {
   604  		t.Fatalf("wrong resolve calls, got %v", table.resolveCalls)
   605  	}
   606  
   607  	// Report it as done to the dialer, which should update the static node record.
   608  	state.taskDone(tasks[0], time.Now())
   609  	if state.static[uintID(1)].dest != resolved {
   610  		t.Fatalf("state.dest not updated")
   611  	}
   612  }
   613  
   614  // compares task lists but doesn't care about the order.
   615  func sametasks(a, b []task) bool {
   616  	if len(a) != len(b) {
   617  		return false
   618  	}
   619  next:
   620  	for _, ta := range a {
   621  		for _, tb := range b {
   622  			if reflect.DeepEqual(ta, tb) {
   623  				continue next
   624  			}
   625  		}
   626  		return false
   627  	}
   628  	return true
   629  }
   630  
   631  func uintID(i uint32) discover.NodeID {
   632  	var id discover.NodeID
   633  	binary.BigEndian.PutUint32(id[:], i)
   634  	return id
   635  }
   636  
   637  // implements discoverTable for TestDialResolve
   638  type resolveMock struct {
   639  	resolveCalls []discover.NodeID
   640  	answer       *discover.Node
   641  }
   642  
   643  func (t *resolveMock) Resolve(id discover.NodeID) *discover.Node {
   644  	t.resolveCalls = append(t.resolveCalls, id)
   645  	return t.answer
   646  }
   647  
   648  func (t *resolveMock) Self() *discover.Node                     { return new(discover.Node) }
   649  func (t *resolveMock) Close()                                   {}
   650  func (t *resolveMock) Bootstrap([]*discover.Node)               {}
   651  func (t *resolveMock) Lookup(discover.NodeID) []*discover.Node  { return nil }
   652  func (t *resolveMock) ReadRandomNodes(buf []*discover.Node) int { return 0 }