The p2p packages can now be configured to restrict all communication to a certain subset of IP networks. This feature is meant to be used for private networks.
		
			
				
	
	
		
			565 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			565 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Copyright 2015 The go-ethereum Authors
 | |
| // This file is part of the go-ethereum library.
 | |
| //
 | |
| // The go-ethereum library is free software: you can redistribute it and/or modify
 | |
| // it under the terms of the GNU Lesser General Public License as published by
 | |
| // the Free Software Foundation, either version 3 of the License, or
 | |
| // (at your option) any later version.
 | |
| //
 | |
| // The go-ethereum library is distributed in the hope that it will be useful,
 | |
| // but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 | |
| // GNU Lesser General Public License for more details.
 | |
| //
 | |
| // You should have received a copy of the GNU Lesser General Public License
 | |
| // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
 | |
| 
 | |
| package p2p
 | |
| 
 | |
| import (
 | |
| 	"encoding/binary"
 | |
| 	"net"
 | |
| 	"reflect"
 | |
| 	"testing"
 | |
| 	"time"
 | |
| 
 | |
| 	"github.com/davecgh/go-spew/spew"
 | |
| 	"github.com/ethereum/go-ethereum/p2p/discover"
 | |
| 	"github.com/ethereum/go-ethereum/p2p/netutil"
 | |
| )
 | |
| 
 | |
| func init() {
 | |
| 	spew.Config.Indent = "\t"
 | |
| }
 | |
| 
 | |
| type dialtest struct {
 | |
| 	init   *dialstate // state before and after the test.
 | |
| 	rounds []round
 | |
| }
 | |
| 
 | |
| type round struct {
 | |
| 	peers []*Peer // current peer set
 | |
| 	done  []task  // tasks that got done this round
 | |
| 	new   []task  // the result must match this one
 | |
| }
 | |
| 
 | |
| func runDialTest(t *testing.T, test dialtest) {
 | |
| 	var (
 | |
| 		vtime   time.Time
 | |
| 		running int
 | |
| 	)
 | |
| 	pm := func(ps []*Peer) map[discover.NodeID]*Peer {
 | |
| 		m := make(map[discover.NodeID]*Peer)
 | |
| 		for _, p := range ps {
 | |
| 			m[p.rw.id] = p
 | |
| 		}
 | |
| 		return m
 | |
| 	}
 | |
| 	for i, round := range test.rounds {
 | |
| 		for _, task := range round.done {
 | |
| 			running--
 | |
| 			if running < 0 {
 | |
| 				panic("running task counter underflow")
 | |
| 			}
 | |
| 			test.init.taskDone(task, vtime)
 | |
| 		}
 | |
| 
 | |
| 		new := test.init.newTasks(running, pm(round.peers), vtime)
 | |
| 		if !sametasks(new, round.new) {
 | |
| 			t.Errorf("round %d: new tasks mismatch:\ngot %v\nwant %v\nstate: %v\nrunning: %v\n",
 | |
| 				i, spew.Sdump(new), spew.Sdump(round.new), spew.Sdump(test.init), spew.Sdump(running))
 | |
| 		}
 | |
| 
 | |
| 		// Time advances by 16 seconds on every round.
 | |
| 		vtime = vtime.Add(16 * time.Second)
 | |
| 		running += len(new)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| type fakeTable []*discover.Node
 | |
| 
 | |
| func (t fakeTable) Self() *discover.Node                     { return new(discover.Node) }
 | |
| func (t fakeTable) Close()                                   {}
 | |
| func (t fakeTable) Lookup(discover.NodeID) []*discover.Node  { return nil }
 | |
| func (t fakeTable) Resolve(discover.NodeID) *discover.Node   { return nil }
 | |
| func (t fakeTable) ReadRandomNodes(buf []*discover.Node) int { return copy(buf, t) }
 | |
| 
 | |
| // This test checks that dynamic dials are launched from discovery results.
 | |
| func TestDialStateDynDial(t *testing.T) {
 | |
| 	runDialTest(t, dialtest{
 | |
| 		init: newDialState(nil, fakeTable{}, 5, nil),
 | |
| 		rounds: []round{
 | |
| 			// A discovery query is launched.
 | |
| 			{
 | |
| 				peers: []*Peer{
 | |
| 					{rw: &conn{flags: staticDialedConn, id: uintID(0)}},
 | |
| 					{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
 | |
| 					{rw: &conn{flags: dynDialedConn, id: uintID(2)}},
 | |
| 				},
 | |
| 				new: []task{&discoverTask{}},
 | |
| 			},
 | |
| 			// Dynamic dials are launched when it completes.
 | |
| 			{
 | |
| 				peers: []*Peer{
 | |
| 					{rw: &conn{flags: staticDialedConn, id: uintID(0)}},
 | |
| 					{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
 | |
| 					{rw: &conn{flags: dynDialedConn, id: uintID(2)}},
 | |
| 				},
 | |
| 				done: []task{
 | |
| 					&discoverTask{results: []*discover.Node{
 | |
| 						{ID: uintID(2)}, // this one is already connected and not dialed.
 | |
| 						{ID: uintID(3)},
 | |
| 						{ID: uintID(4)},
 | |
| 						{ID: uintID(5)},
 | |
| 						{ID: uintID(6)}, // these are not tried because max dyn dials is 5
 | |
| 						{ID: uintID(7)}, // ...
 | |
| 					}},
 | |
| 				},
 | |
| 				new: []task{
 | |
| 					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(3)}},
 | |
| 					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(4)}},
 | |
| 					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(5)}},
 | |
| 				},
 | |
| 			},
 | |
| 			// Some of the dials complete but no new ones are launched yet because
 | |
| 			// the sum of active dial count and dynamic peer count is == maxDynDials.
 | |
| 			{
 | |
| 				peers: []*Peer{
 | |
| 					{rw: &conn{flags: staticDialedConn, id: uintID(0)}},
 | |
| 					{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
 | |
| 					{rw: &conn{flags: dynDialedConn, id: uintID(2)}},
 | |
| 					{rw: &conn{flags: dynDialedConn, id: uintID(3)}},
 | |
| 					{rw: &conn{flags: dynDialedConn, id: uintID(4)}},
 | |
| 				},
 | |
| 				done: []task{
 | |
| 					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(3)}},
 | |
| 					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(4)}},
 | |
| 				},
 | |
| 			},
 | |
| 			// No new dial tasks are launched in the this round because
 | |
| 			// maxDynDials has been reached.
 | |
| 			{
 | |
| 				peers: []*Peer{
 | |
| 					{rw: &conn{flags: staticDialedConn, id: uintID(0)}},
 | |
| 					{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
 | |
| 					{rw: &conn{flags: dynDialedConn, id: uintID(2)}},
 | |
| 					{rw: &conn{flags: dynDialedConn, id: uintID(3)}},
 | |
| 					{rw: &conn{flags: dynDialedConn, id: uintID(4)}},
 | |
| 					{rw: &conn{flags: dynDialedConn, id: uintID(5)}},
 | |
| 				},
 | |
| 				done: []task{
 | |
| 					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(5)}},
 | |
| 				},
 | |
| 				new: []task{
 | |
| 					&waitExpireTask{Duration: 14 * time.Second},
 | |
| 				},
 | |
| 			},
 | |
| 			// In this round, the peer with id 2 drops off. The query
 | |
| 			// results from last discovery lookup are reused.
 | |
| 			{
 | |
| 				peers: []*Peer{
 | |
| 					{rw: &conn{flags: staticDialedConn, id: uintID(0)}},
 | |
| 					{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
 | |
| 					{rw: &conn{flags: dynDialedConn, id: uintID(3)}},
 | |
| 					{rw: &conn{flags: dynDialedConn, id: uintID(4)}},
 | |
| 					{rw: &conn{flags: dynDialedConn, id: uintID(5)}},
 | |
| 				},
 | |
| 				new: []task{
 | |
| 					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(6)}},
 | |
| 				},
 | |
| 			},
 | |
| 			// More peers (3,4) drop off and dial for ID 6 completes.
 | |
| 			// The last query result from the discovery lookup is reused
 | |
| 			// and a new one is spawned because more candidates are needed.
 | |
| 			{
 | |
| 				peers: []*Peer{
 | |
| 					{rw: &conn{flags: staticDialedConn, id: uintID(0)}},
 | |
| 					{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
 | |
| 					{rw: &conn{flags: dynDialedConn, id: uintID(5)}},
 | |
| 				},
 | |
| 				done: []task{
 | |
| 					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(6)}},
 | |
| 				},
 | |
| 				new: []task{
 | |
| 					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(7)}},
 | |
| 					&discoverTask{},
 | |
| 				},
 | |
| 			},
 | |
| 			// Peer 7 is connected, but there still aren't enough dynamic peers
 | |
| 			// (4 out of 5). However, a discovery is already running, so ensure
 | |
| 			// no new is started.
 | |
| 			{
 | |
| 				peers: []*Peer{
 | |
| 					{rw: &conn{flags: staticDialedConn, id: uintID(0)}},
 | |
| 					{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
 | |
| 					{rw: &conn{flags: dynDialedConn, id: uintID(5)}},
 | |
| 					{rw: &conn{flags: dynDialedConn, id: uintID(7)}},
 | |
| 				},
 | |
| 				done: []task{
 | |
| 					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(7)}},
 | |
| 				},
 | |
| 			},
 | |
| 			// Finish the running node discovery with an empty set. A new lookup
 | |
| 			// should be immediately requested.
 | |
| 			{
 | |
| 				peers: []*Peer{
 | |
| 					{rw: &conn{flags: staticDialedConn, id: uintID(0)}},
 | |
| 					{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
 | |
| 					{rw: &conn{flags: dynDialedConn, id: uintID(5)}},
 | |
| 					{rw: &conn{flags: dynDialedConn, id: uintID(7)}},
 | |
| 				},
 | |
| 				done: []task{
 | |
| 					&discoverTask{},
 | |
| 				},
 | |
| 				new: []task{
 | |
| 					&discoverTask{},
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 	})
 | |
| }
 | |
| 
 | |
| func TestDialStateDynDialFromTable(t *testing.T) {
 | |
| 	// This table always returns the same random nodes
 | |
| 	// in the order given below.
 | |
| 	table := fakeTable{
 | |
| 		{ID: uintID(1)},
 | |
| 		{ID: uintID(2)},
 | |
| 		{ID: uintID(3)},
 | |
| 		{ID: uintID(4)},
 | |
| 		{ID: uintID(5)},
 | |
| 		{ID: uintID(6)},
 | |
| 		{ID: uintID(7)},
 | |
| 		{ID: uintID(8)},
 | |
| 	}
 | |
| 
 | |
| 	runDialTest(t, dialtest{
 | |
| 		init: newDialState(nil, table, 10, nil),
 | |
| 		rounds: []round{
 | |
| 			// 5 out of 8 of the nodes returned by ReadRandomNodes are dialed.
 | |
| 			{
 | |
| 				new: []task{
 | |
| 					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(1)}},
 | |
| 					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(2)}},
 | |
| 					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(3)}},
 | |
| 					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(4)}},
 | |
| 					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(5)}},
 | |
| 					&discoverTask{},
 | |
| 				},
 | |
| 			},
 | |
| 			// Dialing nodes 1,2 succeeds. Dials from the lookup are launched.
 | |
| 			{
 | |
| 				peers: []*Peer{
 | |
| 					{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
 | |
| 					{rw: &conn{flags: dynDialedConn, id: uintID(2)}},
 | |
| 				},
 | |
| 				done: []task{
 | |
| 					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(1)}},
 | |
| 					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(2)}},
 | |
| 					&discoverTask{results: []*discover.Node{
 | |
| 						{ID: uintID(10)},
 | |
| 						{ID: uintID(11)},
 | |
| 						{ID: uintID(12)},
 | |
| 					}},
 | |
| 				},
 | |
| 				new: []task{
 | |
| 					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(10)}},
 | |
| 					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(11)}},
 | |
| 					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(12)}},
 | |
| 					&discoverTask{},
 | |
| 				},
 | |
| 			},
 | |
| 			// Dialing nodes 3,4,5 fails. The dials from the lookup succeed.
 | |
| 			{
 | |
| 				peers: []*Peer{
 | |
| 					{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
 | |
| 					{rw: &conn{flags: dynDialedConn, id: uintID(2)}},
 | |
| 					{rw: &conn{flags: dynDialedConn, id: uintID(10)}},
 | |
| 					{rw: &conn{flags: dynDialedConn, id: uintID(11)}},
 | |
| 					{rw: &conn{flags: dynDialedConn, id: uintID(12)}},
 | |
| 				},
 | |
| 				done: []task{
 | |
| 					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(3)}},
 | |
| 					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(4)}},
 | |
| 					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(5)}},
 | |
| 					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(10)}},
 | |
| 					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(11)}},
 | |
| 					&dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(12)}},
 | |
| 				},
 | |
| 			},
 | |
| 			// Waiting for expiry. No waitExpireTask is launched because the
 | |
| 			// discovery query is still running.
 | |
| 			{
 | |
| 				peers: []*Peer{
 | |
| 					{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
 | |
| 					{rw: &conn{flags: dynDialedConn, id: uintID(2)}},
 | |
| 					{rw: &conn{flags: dynDialedConn, id: uintID(10)}},
 | |
| 					{rw: &conn{flags: dynDialedConn, id: uintID(11)}},
 | |
| 					{rw: &conn{flags: dynDialedConn, id: uintID(12)}},
 | |
| 				},
 | |
| 			},
 | |
| 			// Nodes 3,4 are not tried again because only the first two
 | |
| 			// returned random nodes (nodes 1,2) are tried and they're
 | |
| 			// already connected.
 | |
| 			{
 | |
| 				peers: []*Peer{
 | |
| 					{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
 | |
| 					{rw: &conn{flags: dynDialedConn, id: uintID(2)}},
 | |
| 					{rw: &conn{flags: dynDialedConn, id: uintID(10)}},
 | |
| 					{rw: &conn{flags: dynDialedConn, id: uintID(11)}},
 | |
| 					{rw: &conn{flags: dynDialedConn, id: uintID(12)}},
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 	})
 | |
| }
 | |
| 
 | |
| // This test checks that candidates that do not match the netrestrict list are not dialed.
 | |
| func TestDialStateNetRestrict(t *testing.T) {
 | |
| 	// This table always returns the same random nodes
 | |
| 	// in the order given below.
 | |
| 	table := fakeTable{
 | |
| 		{ID: uintID(1), IP: net.ParseIP("127.0.0.1")},
 | |
| 		{ID: uintID(2), IP: net.ParseIP("127.0.0.2")},
 | |
| 		{ID: uintID(3), IP: net.ParseIP("127.0.0.3")},
 | |
| 		{ID: uintID(4), IP: net.ParseIP("127.0.0.4")},
 | |
| 		{ID: uintID(5), IP: net.ParseIP("127.0.2.5")},
 | |
| 		{ID: uintID(6), IP: net.ParseIP("127.0.2.6")},
 | |
| 		{ID: uintID(7), IP: net.ParseIP("127.0.2.7")},
 | |
| 		{ID: uintID(8), IP: net.ParseIP("127.0.2.8")},
 | |
| 	}
 | |
| 	restrict := new(netutil.Netlist)
 | |
| 	restrict.Add("127.0.2.0/24")
 | |
| 
 | |
| 	runDialTest(t, dialtest{
 | |
| 		init: newDialState(nil, table, 10, restrict),
 | |
| 		rounds: []round{
 | |
| 			{
 | |
| 				new: []task{
 | |
| 					&dialTask{flags: dynDialedConn, dest: table[4]},
 | |
| 					&discoverTask{},
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 	})
 | |
| }
 | |
| 
 | |
| // This test checks that static dials are launched.
 | |
| func TestDialStateStaticDial(t *testing.T) {
 | |
| 	wantStatic := []*discover.Node{
 | |
| 		{ID: uintID(1)},
 | |
| 		{ID: uintID(2)},
 | |
| 		{ID: uintID(3)},
 | |
| 		{ID: uintID(4)},
 | |
| 		{ID: uintID(5)},
 | |
| 	}
 | |
| 
 | |
| 	runDialTest(t, dialtest{
 | |
| 		init: newDialState(wantStatic, fakeTable{}, 0, nil),
 | |
| 		rounds: []round{
 | |
| 			// Static dials are launched for the nodes that
 | |
| 			// aren't yet connected.
 | |
| 			{
 | |
| 				peers: []*Peer{
 | |
| 					{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
 | |
| 					{rw: &conn{flags: dynDialedConn, id: uintID(2)}},
 | |
| 				},
 | |
| 				new: []task{
 | |
| 					&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(3)}},
 | |
| 					&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(4)}},
 | |
| 					&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(5)}},
 | |
| 				},
 | |
| 			},
 | |
| 			// No new tasks are launched in this round because all static
 | |
| 			// nodes are either connected or still being dialed.
 | |
| 			{
 | |
| 				peers: []*Peer{
 | |
| 					{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
 | |
| 					{rw: &conn{flags: dynDialedConn, id: uintID(2)}},
 | |
| 					{rw: &conn{flags: staticDialedConn, id: uintID(3)}},
 | |
| 				},
 | |
| 				done: []task{
 | |
| 					&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(3)}},
 | |
| 				},
 | |
| 			},
 | |
| 			// No new dial tasks are launched because all static
 | |
| 			// nodes are now connected.
 | |
| 			{
 | |
| 				peers: []*Peer{
 | |
| 					{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
 | |
| 					{rw: &conn{flags: dynDialedConn, id: uintID(2)}},
 | |
| 					{rw: &conn{flags: staticDialedConn, id: uintID(3)}},
 | |
| 					{rw: &conn{flags: staticDialedConn, id: uintID(4)}},
 | |
| 					{rw: &conn{flags: staticDialedConn, id: uintID(5)}},
 | |
| 				},
 | |
| 				done: []task{
 | |
| 					&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(4)}},
 | |
| 					&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(5)}},
 | |
| 				},
 | |
| 				new: []task{
 | |
| 					&waitExpireTask{Duration: 14 * time.Second},
 | |
| 				},
 | |
| 			},
 | |
| 			// Wait a round for dial history to expire, no new tasks should spawn.
 | |
| 			{
 | |
| 				peers: []*Peer{
 | |
| 					{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
 | |
| 					{rw: &conn{flags: dynDialedConn, id: uintID(2)}},
 | |
| 					{rw: &conn{flags: staticDialedConn, id: uintID(3)}},
 | |
| 					{rw: &conn{flags: staticDialedConn, id: uintID(4)}},
 | |
| 					{rw: &conn{flags: staticDialedConn, id: uintID(5)}},
 | |
| 				},
 | |
| 			},
 | |
| 			// If a static node is dropped, it should be immediately redialed,
 | |
| 			// irrespective whether it was originally static or dynamic.
 | |
| 			{
 | |
| 				peers: []*Peer{
 | |
| 					{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
 | |
| 					{rw: &conn{flags: staticDialedConn, id: uintID(3)}},
 | |
| 					{rw: &conn{flags: staticDialedConn, id: uintID(5)}},
 | |
| 				},
 | |
| 				new: []task{
 | |
| 					&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(2)}},
 | |
| 					&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(4)}},
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 	})
 | |
| }
 | |
| 
 | |
| // This test checks that past dials are not retried for some time.
 | |
| func TestDialStateCache(t *testing.T) {
 | |
| 	wantStatic := []*discover.Node{
 | |
| 		{ID: uintID(1)},
 | |
| 		{ID: uintID(2)},
 | |
| 		{ID: uintID(3)},
 | |
| 	}
 | |
| 
 | |
| 	runDialTest(t, dialtest{
 | |
| 		init: newDialState(wantStatic, fakeTable{}, 0, nil),
 | |
| 		rounds: []round{
 | |
| 			// Static dials are launched for the nodes that
 | |
| 			// aren't yet connected.
 | |
| 			{
 | |
| 				peers: nil,
 | |
| 				new: []task{
 | |
| 					&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(1)}},
 | |
| 					&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(2)}},
 | |
| 					&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(3)}},
 | |
| 				},
 | |
| 			},
 | |
| 			// No new tasks are launched in this round because all static
 | |
| 			// nodes are either connected or still being dialed.
 | |
| 			{
 | |
| 				peers: []*Peer{
 | |
| 					{rw: &conn{flags: staticDialedConn, id: uintID(1)}},
 | |
| 					{rw: &conn{flags: staticDialedConn, id: uintID(2)}},
 | |
| 				},
 | |
| 				done: []task{
 | |
| 					&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(1)}},
 | |
| 					&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(2)}},
 | |
| 				},
 | |
| 			},
 | |
| 			// A salvage task is launched to wait for node 3's history
 | |
| 			// entry to expire.
 | |
| 			{
 | |
| 				peers: []*Peer{
 | |
| 					{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
 | |
| 					{rw: &conn{flags: dynDialedConn, id: uintID(2)}},
 | |
| 				},
 | |
| 				done: []task{
 | |
| 					&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(3)}},
 | |
| 				},
 | |
| 				new: []task{
 | |
| 					&waitExpireTask{Duration: 14 * time.Second},
 | |
| 				},
 | |
| 			},
 | |
| 			// Still waiting for node 3's entry to expire in the cache.
 | |
| 			{
 | |
| 				peers: []*Peer{
 | |
| 					{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
 | |
| 					{rw: &conn{flags: dynDialedConn, id: uintID(2)}},
 | |
| 				},
 | |
| 			},
 | |
| 			// The cache entry for node 3 has expired and is retried.
 | |
| 			{
 | |
| 				peers: []*Peer{
 | |
| 					{rw: &conn{flags: dynDialedConn, id: uintID(1)}},
 | |
| 					{rw: &conn{flags: dynDialedConn, id: uintID(2)}},
 | |
| 				},
 | |
| 				new: []task{
 | |
| 					&dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(3)}},
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 	})
 | |
| }
 | |
| 
 | |
| func TestDialResolve(t *testing.T) {
 | |
| 	resolved := discover.NewNode(uintID(1), net.IP{127, 0, 55, 234}, 3333, 4444)
 | |
| 	table := &resolveMock{answer: resolved}
 | |
| 	state := newDialState(nil, table, 0, nil)
 | |
| 
 | |
| 	// Check that the task is generated with an incomplete ID.
 | |
| 	dest := discover.NewNode(uintID(1), nil, 0, 0)
 | |
| 	state.addStatic(dest)
 | |
| 	tasks := state.newTasks(0, nil, time.Time{})
 | |
| 	if !reflect.DeepEqual(tasks, []task{&dialTask{flags: staticDialedConn, dest: dest}}) {
 | |
| 		t.Fatalf("expected dial task, got %#v", tasks)
 | |
| 	}
 | |
| 
 | |
| 	// Now run the task, it should resolve the ID once.
 | |
| 	config := Config{Dialer: &net.Dialer{Deadline: time.Now().Add(-5 * time.Minute)}}
 | |
| 	srv := &Server{ntab: table, Config: config}
 | |
| 	tasks[0].Do(srv)
 | |
| 	if !reflect.DeepEqual(table.resolveCalls, []discover.NodeID{dest.ID}) {
 | |
| 		t.Fatalf("wrong resolve calls, got %v", table.resolveCalls)
 | |
| 	}
 | |
| 
 | |
| 	// Report it as done to the dialer, which should update the static node record.
 | |
| 	state.taskDone(tasks[0], time.Now())
 | |
| 	if state.static[uintID(1)].dest != resolved {
 | |
| 		t.Fatalf("state.dest not updated")
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // compares task lists but doesn't care about the order.
 | |
| func sametasks(a, b []task) bool {
 | |
| 	if len(a) != len(b) {
 | |
| 		return false
 | |
| 	}
 | |
| next:
 | |
| 	for _, ta := range a {
 | |
| 		for _, tb := range b {
 | |
| 			if reflect.DeepEqual(ta, tb) {
 | |
| 				continue next
 | |
| 			}
 | |
| 		}
 | |
| 		return false
 | |
| 	}
 | |
| 	return true
 | |
| }
 | |
| 
 | |
| func uintID(i uint32) discover.NodeID {
 | |
| 	var id discover.NodeID
 | |
| 	binary.BigEndian.PutUint32(id[:], i)
 | |
| 	return id
 | |
| }
 | |
| 
 | |
| // implements discoverTable for TestDialResolve
 | |
| type resolveMock struct {
 | |
| 	resolveCalls []discover.NodeID
 | |
| 	answer       *discover.Node
 | |
| }
 | |
| 
 | |
| func (t *resolveMock) Resolve(id discover.NodeID) *discover.Node {
 | |
| 	t.resolveCalls = append(t.resolveCalls, id)
 | |
| 	return t.answer
 | |
| }
 | |
| 
 | |
| func (t *resolveMock) Self() *discover.Node                     { return new(discover.Node) }
 | |
| func (t *resolveMock) Close()                                   {}
 | |
| func (t *resolveMock) Bootstrap([]*discover.Node)               {}
 | |
| func (t *resolveMock) Lookup(discover.NodeID) []*discover.Node  { return nil }
 | |
| func (t *resolveMock) ReadRandomNodes(buf []*discover.Node) int { return 0 }
 |