github.com/c2s/go-ethereum@v1.9.7/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  	"strings"
    24  	"testing"
    25  	"time"
    26  
    27  	"github.com/davecgh/go-spew/spew"
    28  	"github.com/ethereum/go-ethereum/internal/testlog"
    29  	"github.com/ethereum/go-ethereum/log"
    30  	"github.com/ethereum/go-ethereum/p2p/enode"
    31  	"github.com/ethereum/go-ethereum/p2p/enr"
    32  	"github.com/ethereum/go-ethereum/p2p/netutil"
    33  )
    34  
    35  func init() {
    36  	spew.Config.Indent = "\t"
    37  }
    38  
    39  type dialtest struct {
    40  	init   *dialstate // state before and after the test.
    41  	rounds []round
    42  }
    43  
    44  type round struct {
    45  	peers []*Peer // current peer set
    46  	done  []task  // tasks that got done this round
    47  	new   []task  // the result must match this one
    48  }
    49  
    50  func runDialTest(t *testing.T, test dialtest) {
    51  	var (
    52  		vtime   time.Time
    53  		running int
    54  	)
    55  	pm := func(ps []*Peer) map[enode.ID]*Peer {
    56  		m := make(map[enode.ID]*Peer)
    57  		for _, p := range ps {
    58  			m[p.ID()] = p
    59  		}
    60  		return m
    61  	}
    62  	for i, round := range test.rounds {
    63  		for _, task := range round.done {
    64  			running--
    65  			if running < 0 {
    66  				panic("running task counter underflow")
    67  			}
    68  			test.init.taskDone(task, vtime)
    69  		}
    70  
    71  		new := test.init.newTasks(running, pm(round.peers), vtime)
    72  		if !sametasks(new, round.new) {
    73  			t.Errorf("ERROR round %d: got %v\nwant %v\nstate: %v\nrunning: %v",
    74  				i, spew.Sdump(new), spew.Sdump(round.new), spew.Sdump(test.init), spew.Sdump(running))
    75  		}
    76  		t.Logf("round %d (running %d) new tasks: %s", i, running, strings.TrimSpace(spew.Sdump(new)))
    77  
    78  		// Time advances by 16 seconds on every round.
    79  		vtime = vtime.Add(16 * time.Second)
    80  		running += len(new)
    81  	}
    82  }
    83  
    84  // This test checks that dynamic dials are launched from discovery results.
    85  func TestDialStateDynDial(t *testing.T) {
    86  	config := &Config{Logger: testlog.Logger(t, log.LvlTrace)}
    87  	runDialTest(t, dialtest{
    88  		init: newDialState(enode.ID{}, 5, config),
    89  		rounds: []round{
    90  			// A discovery query is launched.
    91  			{
    92  				peers: []*Peer{
    93  					{rw: &conn{flags: staticDialedConn, node: newNode(uintID(0), nil)}},
    94  					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(1), nil)}},
    95  					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(2), nil)}},
    96  				},
    97  				new: []task{
    98  					&discoverTask{want: 3},
    99  				},
   100  			},
   101  			// Dynamic dials are launched when it completes.
   102  			{
   103  				peers: []*Peer{
   104  					{rw: &conn{flags: staticDialedConn, node: newNode(uintID(0), nil)}},
   105  					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(1), nil)}},
   106  					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(2), nil)}},
   107  				},
   108  				done: []task{
   109  					&discoverTask{results: []*enode.Node{
   110  						newNode(uintID(2), nil), // this one is already connected and not dialed.
   111  						newNode(uintID(3), nil),
   112  						newNode(uintID(4), nil),
   113  						newNode(uintID(5), nil),
   114  						newNode(uintID(6), nil), // these are not tried because max dyn dials is 5
   115  						newNode(uintID(7), nil), // ...
   116  					}},
   117  				},
   118  				new: []task{
   119  					&dialTask{flags: dynDialedConn, dest: newNode(uintID(3), nil)},
   120  					&dialTask{flags: dynDialedConn, dest: newNode(uintID(4), nil)},
   121  					&dialTask{flags: dynDialedConn, dest: newNode(uintID(5), nil)},
   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, node: newNode(uintID(0), nil)}},
   129  					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(1), nil)}},
   130  					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(2), nil)}},
   131  					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(3), nil)}},
   132  					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(4), nil)}},
   133  				},
   134  				done: []task{
   135  					&dialTask{flags: dynDialedConn, dest: newNode(uintID(3), nil)},
   136  					&dialTask{flags: dynDialedConn, dest: newNode(uintID(4), nil)},
   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, node: newNode(uintID(0), nil)}},
   144  					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(1), nil)}},
   145  					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(2), nil)}},
   146  					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(3), nil)}},
   147  					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(4), nil)}},
   148  					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(5), nil)}},
   149  				},
   150  				done: []task{
   151  					&dialTask{flags: dynDialedConn, dest: newNode(uintID(5), nil)},
   152  				},
   153  				new: []task{
   154  					&waitExpireTask{Duration: 19 * 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, node: newNode(uintID(0), nil)}},
   162  					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(1), nil)}},
   163  					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(3), nil)}},
   164  					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(4), nil)}},
   165  					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(5), nil)}},
   166  				},
   167  				new: []task{
   168  					&dialTask{flags: dynDialedConn, dest: newNode(uintID(6), nil)},
   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, node: newNode(uintID(0), nil)}},
   177  					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(1), nil)}},
   178  					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(5), nil)}},
   179  				},
   180  				done: []task{
   181  					&dialTask{flags: dynDialedConn, dest: newNode(uintID(6), nil)},
   182  				},
   183  				new: []task{
   184  					&dialTask{flags: dynDialedConn, dest: newNode(uintID(7), nil)},
   185  					&discoverTask{want: 2},
   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, node: newNode(uintID(0), nil)}},
   194  					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(1), nil)}},
   195  					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(5), nil)}},
   196  					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(7), nil)}},
   197  				},
   198  				done: []task{
   199  					&dialTask{flags: dynDialedConn, dest: newNode(uintID(7), nil)},
   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, node: newNode(uintID(0), nil)}},
   207  					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(1), nil)}},
   208  					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(5), nil)}},
   209  					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(7), nil)}},
   210  				},
   211  				done: []task{
   212  					&discoverTask{},
   213  				},
   214  				new: []task{
   215  					&discoverTask{want: 2},
   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  	config := &Config{
   225  		BootstrapNodes: []*enode.Node{
   226  			newNode(uintID(1), nil),
   227  			newNode(uintID(2), nil),
   228  			newNode(uintID(3), nil),
   229  		},
   230  		Logger: testlog.Logger(t, log.LvlTrace),
   231  	}
   232  	runDialTest(t, dialtest{
   233  		init: newDialState(enode.ID{}, 5, config),
   234  		rounds: []round{
   235  			{
   236  				new: []task{
   237  					&discoverTask{want: 5},
   238  				},
   239  			},
   240  			{
   241  				done: []task{
   242  					&discoverTask{
   243  						results: []*enode.Node{
   244  							newNode(uintID(4), nil),
   245  							newNode(uintID(5), nil),
   246  						},
   247  					},
   248  				},
   249  				new: []task{
   250  					&dialTask{flags: dynDialedConn, dest: newNode(uintID(4), nil)},
   251  					&dialTask{flags: dynDialedConn, dest: newNode(uintID(5), nil)},
   252  					&discoverTask{want: 3},
   253  				},
   254  			},
   255  			// No dials succeed, bootnodes still pending fallback interval
   256  			{},
   257  			// 1 bootnode attempted as fallback interval was reached
   258  			{
   259  				done: []task{
   260  					&dialTask{flags: dynDialedConn, dest: newNode(uintID(4), nil)},
   261  					&dialTask{flags: dynDialedConn, dest: newNode(uintID(5), nil)},
   262  				},
   263  				new: []task{
   264  					&dialTask{flags: dynDialedConn, dest: newNode(uintID(1), nil)},
   265  				},
   266  			},
   267  			// No dials succeed, 2nd bootnode is attempted
   268  			{
   269  				done: []task{
   270  					&dialTask{flags: dynDialedConn, dest: newNode(uintID(1), nil)},
   271  				},
   272  				new: []task{
   273  					&dialTask{flags: dynDialedConn, dest: newNode(uintID(2), nil)},
   274  				},
   275  			},
   276  			// No dials succeed, 3rd bootnode is attempted
   277  			{
   278  				done: []task{
   279  					&dialTask{flags: dynDialedConn, dest: newNode(uintID(2), nil)},
   280  				},
   281  				new: []task{
   282  					&dialTask{flags: dynDialedConn, dest: newNode(uintID(3), nil)},
   283  				},
   284  			},
   285  			// No dials succeed, 1st bootnode is attempted again, expired random nodes retried
   286  			{
   287  				done: []task{
   288  					&dialTask{flags: dynDialedConn, dest: newNode(uintID(3), nil)},
   289  					&discoverTask{results: []*enode.Node{
   290  						newNode(uintID(6), nil),
   291  					}},
   292  				},
   293  				new: []task{
   294  					&dialTask{flags: dynDialedConn, dest: newNode(uintID(6), nil)},
   295  					&discoverTask{want: 4},
   296  				},
   297  			},
   298  			// Random dial succeeds, no more bootnodes are attempted
   299  			{
   300  				peers: []*Peer{
   301  					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(6), nil)}},
   302  				},
   303  			},
   304  		},
   305  	})
   306  }
   307  
   308  func newNode(id enode.ID, ip net.IP) *enode.Node {
   309  	var r enr.Record
   310  	if ip != nil {
   311  		r.Set(enr.IP(ip))
   312  	}
   313  	return enode.SignNull(&r, id)
   314  }
   315  
   316  // // This test checks that candidates that do not match the netrestrict list are not dialed.
   317  func TestDialStateNetRestrict(t *testing.T) {
   318  	// This table always returns the same random nodes
   319  	// in the order given below.
   320  	nodes := []*enode.Node{
   321  		newNode(uintID(1), net.ParseIP("127.0.0.1")),
   322  		newNode(uintID(2), net.ParseIP("127.0.0.2")),
   323  		newNode(uintID(3), net.ParseIP("127.0.0.3")),
   324  		newNode(uintID(4), net.ParseIP("127.0.0.4")),
   325  		newNode(uintID(5), net.ParseIP("127.0.2.5")),
   326  		newNode(uintID(6), net.ParseIP("127.0.2.6")),
   327  		newNode(uintID(7), net.ParseIP("127.0.2.7")),
   328  		newNode(uintID(8), net.ParseIP("127.0.2.8")),
   329  	}
   330  	restrict := new(netutil.Netlist)
   331  	restrict.Add("127.0.2.0/24")
   332  
   333  	runDialTest(t, dialtest{
   334  		init: newDialState(enode.ID{}, 10, &Config{NetRestrict: restrict}),
   335  		rounds: []round{
   336  			{
   337  				new: []task{
   338  					&discoverTask{want: 10},
   339  				},
   340  			},
   341  			{
   342  				done: []task{
   343  					&discoverTask{results: nodes},
   344  				},
   345  				new: []task{
   346  					&dialTask{flags: dynDialedConn, dest: nodes[4]},
   347  					&dialTask{flags: dynDialedConn, dest: nodes[5]},
   348  					&dialTask{flags: dynDialedConn, dest: nodes[6]},
   349  					&dialTask{flags: dynDialedConn, dest: nodes[7]},
   350  					&discoverTask{want: 6},
   351  				},
   352  			},
   353  		},
   354  	})
   355  }
   356  
   357  // This test checks that static dials are launched.
   358  func TestDialStateStaticDial(t *testing.T) {
   359  	config := &Config{
   360  		StaticNodes: []*enode.Node{
   361  			newNode(uintID(1), nil),
   362  			newNode(uintID(2), nil),
   363  			newNode(uintID(3), nil),
   364  			newNode(uintID(4), nil),
   365  			newNode(uintID(5), nil),
   366  		},
   367  		Logger: testlog.Logger(t, log.LvlTrace),
   368  	}
   369  	runDialTest(t, dialtest{
   370  		init: newDialState(enode.ID{}, 0, config),
   371  		rounds: []round{
   372  			// Static dials are launched for the nodes that
   373  			// aren't yet connected.
   374  			{
   375  				peers: []*Peer{
   376  					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(1), nil)}},
   377  					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(2), nil)}},
   378  				},
   379  				new: []task{
   380  					&dialTask{flags: staticDialedConn, dest: newNode(uintID(3), nil)},
   381  					&dialTask{flags: staticDialedConn, dest: newNode(uintID(4), nil)},
   382  					&dialTask{flags: staticDialedConn, dest: newNode(uintID(5), nil)},
   383  				},
   384  			},
   385  			// No new tasks are launched in this round because all static
   386  			// nodes are either connected or still being dialed.
   387  			{
   388  				peers: []*Peer{
   389  					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(1), nil)}},
   390  					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(2), nil)}},
   391  					{rw: &conn{flags: staticDialedConn, node: newNode(uintID(3), nil)}},
   392  				},
   393  				done: []task{
   394  					&dialTask{flags: staticDialedConn, dest: newNode(uintID(3), nil)},
   395  				},
   396  			},
   397  			// No new dial tasks are launched because all static
   398  			// nodes are now connected.
   399  			{
   400  				peers: []*Peer{
   401  					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(1), nil)}},
   402  					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(2), nil)}},
   403  					{rw: &conn{flags: staticDialedConn, node: newNode(uintID(3), nil)}},
   404  					{rw: &conn{flags: staticDialedConn, node: newNode(uintID(4), nil)}},
   405  					{rw: &conn{flags: staticDialedConn, node: newNode(uintID(5), nil)}},
   406  				},
   407  				done: []task{
   408  					&dialTask{flags: staticDialedConn, dest: newNode(uintID(4), nil)},
   409  					&dialTask{flags: staticDialedConn, dest: newNode(uintID(5), nil)},
   410  				},
   411  				new: []task{
   412  					&waitExpireTask{Duration: 19 * time.Second},
   413  				},
   414  			},
   415  			// Wait a round for dial history to expire, no new tasks should spawn.
   416  			{
   417  				peers: []*Peer{
   418  					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(1), nil)}},
   419  					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(2), nil)}},
   420  					{rw: &conn{flags: staticDialedConn, node: newNode(uintID(3), nil)}},
   421  					{rw: &conn{flags: staticDialedConn, node: newNode(uintID(4), nil)}},
   422  					{rw: &conn{flags: staticDialedConn, node: newNode(uintID(5), nil)}},
   423  				},
   424  			},
   425  			// If a static node is dropped, it should be immediately redialed,
   426  			// irrespective whether it was originally static or dynamic.
   427  			{
   428  				done: []task{
   429  					&waitExpireTask{Duration: 19 * time.Second},
   430  				},
   431  				peers: []*Peer{
   432  					{rw: &conn{flags: dynDialedConn, node: newNode(uintID(1), nil)}},
   433  					{rw: &conn{flags: staticDialedConn, node: newNode(uintID(3), nil)}},
   434  					{rw: &conn{flags: staticDialedConn, node: newNode(uintID(5), nil)}},
   435  				},
   436  				new: []task{
   437  					&dialTask{flags: staticDialedConn, dest: newNode(uintID(2), nil)},
   438  				},
   439  			},
   440  		},
   441  	})
   442  }
   443  
   444  // This test checks that past dials are not retried for some time.
   445  func TestDialStateCache(t *testing.T) {
   446  	config := &Config{
   447  		StaticNodes: []*enode.Node{
   448  			newNode(uintID(1), nil),
   449  			newNode(uintID(2), nil),
   450  			newNode(uintID(3), nil),
   451  		},
   452  		Logger: testlog.Logger(t, log.LvlTrace),
   453  	}
   454  	runDialTest(t, dialtest{
   455  		init: newDialState(enode.ID{}, 0, config),
   456  		rounds: []round{
   457  			// Static dials are launched for the nodes that
   458  			// aren't yet connected.
   459  			{
   460  				peers: nil,
   461  				new: []task{
   462  					&dialTask{flags: staticDialedConn, dest: newNode(uintID(1), nil)},
   463  					&dialTask{flags: staticDialedConn, dest: newNode(uintID(2), nil)},
   464  					&dialTask{flags: staticDialedConn, dest: newNode(uintID(3), nil)},
   465  				},
   466  			},
   467  			// No new tasks are launched in this round because all static
   468  			// nodes are either connected or still being dialed.
   469  			{
   470  				peers: []*Peer{
   471  					{rw: &conn{flags: staticDialedConn, node: newNode(uintID(1), nil)}},
   472  					{rw: &conn{flags: staticDialedConn, node: newNode(uintID(2), nil)}},
   473  				},
   474  				done: []task{
   475  					&dialTask{flags: staticDialedConn, dest: newNode(uintID(1), nil)},
   476  					&dialTask{flags: staticDialedConn, dest: newNode(uintID(2), nil)},
   477  				},
   478  			},
   479  			// A salvage task is launched to wait for node 3's history
   480  			// entry to expire.
   481  			{
   482  				peers: []*Peer{
   483  					{rw: &conn{flags: staticDialedConn, node: newNode(uintID(1), nil)}},
   484  					{rw: &conn{flags: staticDialedConn, node: newNode(uintID(2), nil)}},
   485  				},
   486  				done: []task{
   487  					&dialTask{flags: staticDialedConn, dest: newNode(uintID(3), nil)},
   488  				},
   489  				new: []task{
   490  					&waitExpireTask{Duration: 19 * time.Second},
   491  				},
   492  			},
   493  			// Still waiting for node 3's entry to expire in the cache.
   494  			{
   495  				peers: []*Peer{
   496  					{rw: &conn{flags: staticDialedConn, node: newNode(uintID(1), nil)}},
   497  					{rw: &conn{flags: staticDialedConn, node: newNode(uintID(2), nil)}},
   498  				},
   499  			},
   500  			{
   501  				peers: []*Peer{
   502  					{rw: &conn{flags: staticDialedConn, node: newNode(uintID(1), nil)}},
   503  					{rw: &conn{flags: staticDialedConn, node: newNode(uintID(2), nil)}},
   504  				},
   505  			},
   506  			// The cache entry for node 3 has expired and is retried.
   507  			{
   508  				done: []task{
   509  					&waitExpireTask{Duration: 19 * time.Second},
   510  				},
   511  				peers: []*Peer{
   512  					{rw: &conn{flags: staticDialedConn, node: newNode(uintID(1), nil)}},
   513  					{rw: &conn{flags: staticDialedConn, node: newNode(uintID(2), nil)}},
   514  				},
   515  				new: []task{
   516  					&dialTask{flags: staticDialedConn, dest: newNode(uintID(3), nil)},
   517  				},
   518  			},
   519  		},
   520  	})
   521  }
   522  
   523  func TestDialResolve(t *testing.T) {
   524  	config := &Config{
   525  		Logger: testlog.Logger(t, log.LvlTrace),
   526  		Dialer: TCPDialer{&net.Dialer{Deadline: time.Now().Add(-5 * time.Minute)}},
   527  	}
   528  	resolved := newNode(uintID(1), net.IP{127, 0, 55, 234})
   529  	resolver := &resolveMock{answer: resolved}
   530  	state := newDialState(enode.ID{}, 0, config)
   531  
   532  	// Check that the task is generated with an incomplete ID.
   533  	dest := newNode(uintID(1), nil)
   534  	state.addStatic(dest)
   535  	tasks := state.newTasks(0, nil, time.Time{})
   536  	if !reflect.DeepEqual(tasks, []task{&dialTask{flags: staticDialedConn, dest: dest}}) {
   537  		t.Fatalf("expected dial task, got %#v", tasks)
   538  	}
   539  
   540  	// Now run the task, it should resolve the ID once.
   541  	srv := &Server{
   542  		Config:             *config,
   543  		log:                config.Logger,
   544  		staticNodeResolver: resolver,
   545  	}
   546  	tasks[0].Do(srv)
   547  	if !reflect.DeepEqual(resolver.calls, []*enode.Node{dest}) {
   548  		t.Fatalf("wrong resolve calls, got %v", resolver.calls)
   549  	}
   550  
   551  	// Report it as done to the dialer, which should update the static node record.
   552  	state.taskDone(tasks[0], time.Now())
   553  	if state.static[uintID(1)].dest != resolved {
   554  		t.Fatalf("state.dest not updated")
   555  	}
   556  }
   557  
   558  // compares task lists but doesn't care about the order.
   559  func sametasks(a, b []task) bool {
   560  	if len(a) != len(b) {
   561  		return false
   562  	}
   563  next:
   564  	for _, ta := range a {
   565  		for _, tb := range b {
   566  			if reflect.DeepEqual(ta, tb) {
   567  				continue next
   568  			}
   569  		}
   570  		return false
   571  	}
   572  	return true
   573  }
   574  
   575  func uintID(i uint32) enode.ID {
   576  	var id enode.ID
   577  	binary.BigEndian.PutUint32(id[:], i)
   578  	return id
   579  }
   580  
   581  // for TestDialResolve
   582  type resolveMock struct {
   583  	calls  []*enode.Node
   584  	answer *enode.Node
   585  }
   586  
   587  func (t *resolveMock) Resolve(n *enode.Node) *enode.Node {
   588  	t.calls = append(t.calls, n)
   589  	return t.answer
   590  }