212 lines
		
	
	
		
			5.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			212 lines
		
	
	
		
			5.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package discover
 | 
						|
 | 
						|
import (
 | 
						|
	"fmt"
 | 
						|
	logpkg "log"
 | 
						|
	"net"
 | 
						|
	"os"
 | 
						|
	"testing"
 | 
						|
	"time"
 | 
						|
 | 
						|
	"github.com/ethereum/go-ethereum/logger"
 | 
						|
)
 | 
						|
 | 
						|
func init() {
 | 
						|
	logger.AddLogSystem(logger.NewStdLogSystem(os.Stdout, logpkg.LstdFlags, logger.ErrorLevel))
 | 
						|
}
 | 
						|
 | 
						|
func TestUDP_ping(t *testing.T) {
 | 
						|
	t.Parallel()
 | 
						|
 | 
						|
	n1, _ := ListenUDP(newkey(), "127.0.0.1:0", nil)
 | 
						|
	n2, _ := ListenUDP(newkey(), "127.0.0.1:0", nil)
 | 
						|
	defer n1.Close()
 | 
						|
	defer n2.Close()
 | 
						|
 | 
						|
	if err := n1.net.ping(n2.self); err != nil {
 | 
						|
		t.Fatalf("ping error: %v", err)
 | 
						|
	}
 | 
						|
	if find(n2, n1.self.ID) == nil {
 | 
						|
		t.Errorf("node 2 does not contain id of node 1")
 | 
						|
	}
 | 
						|
	if e := find(n1, n2.self.ID); e != nil {
 | 
						|
		t.Errorf("node 1 does contains id of node 2: %v", e)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func find(tab *Table, id NodeID) *Node {
 | 
						|
	for _, b := range tab.buckets {
 | 
						|
		for _, e := range b.entries {
 | 
						|
			if e.ID == id {
 | 
						|
				return e
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func TestUDP_findnode(t *testing.T) {
 | 
						|
	t.Parallel()
 | 
						|
 | 
						|
	n1, _ := ListenUDP(newkey(), "127.0.0.1:0", nil)
 | 
						|
	n2, _ := ListenUDP(newkey(), "127.0.0.1:0", nil)
 | 
						|
	defer n1.Close()
 | 
						|
	defer n2.Close()
 | 
						|
 | 
						|
	// put a few nodes into n2. the exact distribution shouldn't
 | 
						|
	// matter much, altough we need to take care not to overflow
 | 
						|
	// any bucket.
 | 
						|
	target := randomID(n1.self.ID, 100)
 | 
						|
	nodes := &nodesByDistance{target: target}
 | 
						|
	for i := 0; i < bucketSize; i++ {
 | 
						|
		n2.add([]*Node{&Node{
 | 
						|
			IP:       net.IP{1, 2, 3, byte(i)},
 | 
						|
			DiscPort: i + 2,
 | 
						|
			TCPPort:  i + 2,
 | 
						|
			ID:       randomID(n2.self.ID, i+2),
 | 
						|
		}})
 | 
						|
	}
 | 
						|
	n2.add(nodes.entries)
 | 
						|
	n2.bumpOrAdd(n1.self.ID, &net.UDPAddr{IP: n1.self.IP, Port: n1.self.DiscPort})
 | 
						|
	expected := n2.closest(target, bucketSize)
 | 
						|
 | 
						|
	err := runUDP(10, func() error {
 | 
						|
		result, _ := n1.net.findnode(n2.self, target)
 | 
						|
		if len(result) != bucketSize {
 | 
						|
			return fmt.Errorf("wrong number of results: got %d, want %d", len(result), bucketSize)
 | 
						|
		}
 | 
						|
		for i := range result {
 | 
						|
			if result[i].ID != expected.entries[i].ID {
 | 
						|
				return fmt.Errorf("result mismatch at %d:\n  got:  %v\n  want: %v", i, result[i], expected.entries[i])
 | 
						|
			}
 | 
						|
		}
 | 
						|
		return nil
 | 
						|
	})
 | 
						|
	if err != nil {
 | 
						|
		t.Error(err)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestUDP_replytimeout(t *testing.T) {
 | 
						|
	t.Parallel()
 | 
						|
 | 
						|
	// reserve a port so we don't talk to an existing service by accident
 | 
						|
	addr, _ := net.ResolveUDPAddr("udp", "127.0.0.1:0")
 | 
						|
	fd, err := net.ListenUDP("udp", addr)
 | 
						|
	if err != nil {
 | 
						|
		t.Fatal(err)
 | 
						|
	}
 | 
						|
	defer fd.Close()
 | 
						|
 | 
						|
	n1, _ := ListenUDP(newkey(), "127.0.0.1:0", nil)
 | 
						|
	defer n1.Close()
 | 
						|
	n2 := n1.bumpOrAdd(randomID(n1.self.ID, 10), fd.LocalAddr().(*net.UDPAddr))
 | 
						|
 | 
						|
	if err := n1.net.ping(n2); err != errTimeout {
 | 
						|
		t.Error("expected timeout error, got", err)
 | 
						|
	}
 | 
						|
 | 
						|
	if result, err := n1.net.findnode(n2, n1.self.ID); err != errTimeout {
 | 
						|
		t.Error("expected timeout error, got", err)
 | 
						|
	} else if len(result) > 0 {
 | 
						|
		t.Error("expected empty result, got", result)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestUDP_findnodeMultiReply(t *testing.T) {
 | 
						|
	t.Parallel()
 | 
						|
 | 
						|
	n1, _ := ListenUDP(newkey(), "127.0.0.1:0", nil)
 | 
						|
	n2, _ := ListenUDP(newkey(), "127.0.0.1:0", nil)
 | 
						|
	udp2 := n2.net.(*udp)
 | 
						|
	defer n1.Close()
 | 
						|
	defer n2.Close()
 | 
						|
 | 
						|
	err := runUDP(10, func() error {
 | 
						|
		nodes := make([]*Node, bucketSize)
 | 
						|
		for i := range nodes {
 | 
						|
			nodes[i] = &Node{
 | 
						|
				IP:       net.IP{1, 2, 3, 4},
 | 
						|
				DiscPort: i + 1,
 | 
						|
				TCPPort:  i + 1,
 | 
						|
				ID:       randomID(n2.self.ID, i+1),
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		// ask N2 for neighbors. it will send an empty reply back.
 | 
						|
		// the request will wait for up to bucketSize replies.
 | 
						|
		resultc := make(chan []*Node)
 | 
						|
		errc := make(chan error)
 | 
						|
		go func() {
 | 
						|
			ns, err := n1.net.findnode(n2.self, n1.self.ID)
 | 
						|
			if err != nil {
 | 
						|
				errc <- err
 | 
						|
			} else {
 | 
						|
				resultc <- ns
 | 
						|
			}
 | 
						|
		}()
 | 
						|
 | 
						|
		// send a few more neighbors packets to N1.
 | 
						|
		// it should collect those.
 | 
						|
		for end := 0; end < len(nodes); {
 | 
						|
			off := end
 | 
						|
			if end = end + 5; end > len(nodes) {
 | 
						|
				end = len(nodes)
 | 
						|
			}
 | 
						|
			udp2.send(n1.self, neighborsPacket, neighbors{
 | 
						|
				Nodes:      nodes[off:end],
 | 
						|
				Expiration: uint64(time.Now().Add(10 * time.Second).Unix()),
 | 
						|
			})
 | 
						|
		}
 | 
						|
 | 
						|
		// check that they are all returned. we cannot just check for
 | 
						|
		// equality because they might not be returned in the order they
 | 
						|
		// were sent.
 | 
						|
		var result []*Node
 | 
						|
		select {
 | 
						|
		case result = <-resultc:
 | 
						|
		case err := <-errc:
 | 
						|
			return err
 | 
						|
		}
 | 
						|
		if hasDuplicates(result) {
 | 
						|
			return fmt.Errorf("result slice contains duplicates")
 | 
						|
		}
 | 
						|
		if len(result) != len(nodes) {
 | 
						|
			return fmt.Errorf("wrong number of nodes returned: got %d, want %d", len(result), len(nodes))
 | 
						|
		}
 | 
						|
		matched := make(map[NodeID]bool)
 | 
						|
		for _, n := range result {
 | 
						|
			for _, expn := range nodes {
 | 
						|
				if n.ID == expn.ID { // && bytes.Equal(n.Addr.IP, expn.Addr.IP) && n.Addr.Port == expn.Addr.Port {
 | 
						|
					matched[n.ID] = true
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
		if len(matched) != len(nodes) {
 | 
						|
			return fmt.Errorf("wrong number of matching nodes: got %d, want %d", len(matched), len(nodes))
 | 
						|
		}
 | 
						|
		return nil
 | 
						|
	})
 | 
						|
	if err != nil {
 | 
						|
		t.Error(err)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// runUDP runs a test n times and returns an error if the test failed
 | 
						|
// in all n runs. This is necessary because UDP is unreliable even for
 | 
						|
// connections on the local machine, causing test failures.
 | 
						|
func runUDP(n int, test func() error) error {
 | 
						|
	errcount := 0
 | 
						|
	errors := ""
 | 
						|
	for i := 0; i < n; i++ {
 | 
						|
		if err := test(); err != nil {
 | 
						|
			errors += fmt.Sprintf("\n#%d: %v", i, err)
 | 
						|
			errcount++
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if errcount == n {
 | 
						|
		return fmt.Errorf("failed on all %d iterations:%s", n, errors)
 | 
						|
	}
 | 
						|
	return nil
 | 
						|
}
 |