From 12224c7f5924720767d73f06ed4571dc3ce2f092 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Tue, 27 Jan 2015 14:33:26 +0100 Subject: p2p/discover: new package implementing the Node Discovery Protocol --- p2p/discover/udp_test.go | 156 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 156 insertions(+) create mode 100644 p2p/discover/udp_test.go (limited to 'p2p/discover/udp_test.go') diff --git a/p2p/discover/udp_test.go b/p2p/discover/udp_test.go new file mode 100644 index 000000000..f2ab2b661 --- /dev/null +++ b/p2p/discover/udp_test.go @@ -0,0 +1,156 @@ +package discover + +import ( + logpkg "log" + "net" + "os" + "testing" + "time" + + "github.com/ethereum/go-ethereum/logger" +) + +func init() { + logger.AddLogSystem(logger.NewStdLogSystem(os.Stdout, logpkg.LstdFlags, logger.DebugLevel)) +} + +func TestUDP_ping(t *testing.T) { + t.Parallel() + + n1, _ := ListenUDP(newkey(), "127.0.0.1:0") + n2, _ := ListenUDP(newkey(), "127.0.0.1:0") + defer n1.net.close() + defer n2.net.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") + n2, _ := ListenUDP(newkey(), "127.0.0.1:0") + defer n1.net.close() + defer n2.net.close() + + entry := &Node{ID: NodeID{1}, Addr: &net.UDPAddr{IP: net.IP{1, 2, 3, 4}, Port: 15}} + n2.add([]*Node{entry}) + + target := randomID(n1.self.ID, 100) + result, _ := n1.net.findnode(n2.self, target) + if len(result) != 1 { + t.Fatalf("wrong number of results: got %d, want 1", len(result)) + } + if result[0].ID != entry.ID { + t.Errorf("wrong result: got %v, want %v", result[0], entry) + } +} + +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") + defer n1.net.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") + n2, _ := ListenUDP(newkey(), "127.0.0.1:0") + udp2 := n2.net.(*udp) + defer n1.net.close() + defer n2.net.close() + + nodes := make([]*Node, bucketSize) + for i := range nodes { + nodes[i] = &Node{ + Addr: &net.UDPAddr{IP: net.IP{1, 2, 3, 4}, Port: 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) + go func() { + ns, err := n1.net.findnode(n2.self, n1.self.ID) + if err != nil { + t.Error("findnode error:", err) + } + 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. + result := <-resultC + if hasDuplicates(result) { + t.Error("result slice contains duplicates") + } + if len(result) != len(nodes) { + t.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) { + t.Errorf("wrong number of matching nodes: got %d, want %d", len(matched), len(nodes)) + } +} -- cgit v1.2.3 From 739066ec56393e63b93531787746fb8ba5f1df15 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Thu, 5 Feb 2015 03:07:18 +0100 Subject: p2p/discover: add some helper functions --- p2p/discover/udp_test.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'p2p/discover/udp_test.go') diff --git a/p2p/discover/udp_test.go b/p2p/discover/udp_test.go index f2ab2b661..8ed6ec9c0 100644 --- a/p2p/discover/udp_test.go +++ b/p2p/discover/udp_test.go @@ -19,8 +19,8 @@ func TestUDP_ping(t *testing.T) { n1, _ := ListenUDP(newkey(), "127.0.0.1:0") n2, _ := ListenUDP(newkey(), "127.0.0.1:0") - defer n1.net.close() - defer n2.net.close() + defer n1.Close() + defer n2.Close() if err := n1.net.ping(n2.self); err != nil { t.Fatalf("ping error: %v", err) @@ -49,8 +49,8 @@ func TestUDP_findnode(t *testing.T) { n1, _ := ListenUDP(newkey(), "127.0.0.1:0") n2, _ := ListenUDP(newkey(), "127.0.0.1:0") - defer n1.net.close() - defer n2.net.close() + defer n1.Close() + defer n2.Close() entry := &Node{ID: NodeID{1}, Addr: &net.UDPAddr{IP: net.IP{1, 2, 3, 4}, Port: 15}} n2.add([]*Node{entry}) @@ -77,7 +77,7 @@ func TestUDP_replytimeout(t *testing.T) { defer fd.Close() n1, _ := ListenUDP(newkey(), "127.0.0.1:0") - defer n1.net.close() + defer n1.Close() n2 := n1.bumpOrAdd(randomID(n1.self.ID, 10), fd.LocalAddr().(*net.UDPAddr)) if err := n1.net.ping(n2); err != errTimeout { @@ -97,8 +97,8 @@ func TestUDP_findnodeMultiReply(t *testing.T) { n1, _ := ListenUDP(newkey(), "127.0.0.1:0") n2, _ := ListenUDP(newkey(), "127.0.0.1:0") udp2 := n2.net.(*udp) - defer n1.net.close() - defer n2.net.close() + defer n1.Close() + defer n2.Close() nodes := make([]*Node, bucketSize) for i := range nodes { -- cgit v1.2.3 From 8564eb9f7efcc7a0e639f6a45d7e67037fedac5f Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Fri, 6 Feb 2015 14:40:53 +0100 Subject: p2p/discover: add node URL functions, distinguish TCP/UDP ports The discovery RPC protocol does not yet distinguish TCP and UDP ports. But it can't hurt to do so in our internal model. --- p2p/discover/udp_test.go | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'p2p/discover/udp_test.go') diff --git a/p2p/discover/udp_test.go b/p2p/discover/udp_test.go index 8ed6ec9c0..94680d6fc 100644 --- a/p2p/discover/udp_test.go +++ b/p2p/discover/udp_test.go @@ -4,6 +4,7 @@ import ( logpkg "log" "net" "os" + "reflect" "testing" "time" @@ -11,7 +12,7 @@ import ( ) func init() { - logger.AddLogSystem(logger.NewStdLogSystem(os.Stdout, logpkg.LstdFlags, logger.DebugLevel)) + logger.AddLogSystem(logger.NewStdLogSystem(os.Stdout, logpkg.LstdFlags, logger.ErrorLevel)) } func TestUDP_ping(t *testing.T) { @@ -52,7 +53,7 @@ func TestUDP_findnode(t *testing.T) { defer n1.Close() defer n2.Close() - entry := &Node{ID: NodeID{1}, Addr: &net.UDPAddr{IP: net.IP{1, 2, 3, 4}, Port: 15}} + entry := MustParseNode("enode://9d8a19597e312ef32d76e6b4903bb43d7bcd892d17b769d30b404bd3a4c2dca6c86184b17d0fdeeafe3b01e0e912d990ddc853db3f325d5419f31446543c30be@127.0.0.1:54194") n2.add([]*Node{entry}) target := randomID(n1.self.ID, 100) @@ -60,7 +61,7 @@ func TestUDP_findnode(t *testing.T) { if len(result) != 1 { t.Fatalf("wrong number of results: got %d, want 1", len(result)) } - if result[0].ID != entry.ID { + if !reflect.DeepEqual(result[0], entry) { t.Errorf("wrong result: got %v, want %v", result[0], entry) } } @@ -103,8 +104,10 @@ func TestUDP_findnodeMultiReply(t *testing.T) { nodes := make([]*Node, bucketSize) for i := range nodes { nodes[i] = &Node{ - Addr: &net.UDPAddr{IP: net.IP{1, 2, 3, 4}, Port: i + 1}, - ID: randomID(n2.self.ID, i+1), + IP: net.IP{1, 2, 3, 4}, + DiscPort: i + 1, + TCPPort: i + 1, + ID: randomID(n2.self.ID, i+1), } } -- cgit v1.2.3 From 9915d3c3be831fa2c95ac277459945d969bd7b06 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Mon, 9 Feb 2015 11:02:32 +0100 Subject: p2p/discover: deflake UDP tests --- p2p/discover/udp_test.go | 162 +++++++++++++++++++++++++++++++---------------- 1 file changed, 107 insertions(+), 55 deletions(-) (limited to 'p2p/discover/udp_test.go') diff --git a/p2p/discover/udp_test.go b/p2p/discover/udp_test.go index 94680d6fc..740d0a5d9 100644 --- a/p2p/discover/udp_test.go +++ b/p2p/discover/udp_test.go @@ -1,10 +1,10 @@ package discover import ( + "fmt" logpkg "log" "net" "os" - "reflect" "testing" "time" @@ -53,16 +53,37 @@ func TestUDP_findnode(t *testing.T) { defer n1.Close() defer n2.Close() - entry := MustParseNode("enode://9d8a19597e312ef32d76e6b4903bb43d7bcd892d17b769d30b404bd3a4c2dca6c86184b17d0fdeeafe3b01e0e912d990ddc853db3f325d5419f31446543c30be@127.0.0.1:54194") - n2.add([]*Node{entry}) - + // 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) - result, _ := n1.net.findnode(n2.self, target) - if len(result) != 1 { - t.Fatalf("wrong number of results: got %d, want 1", len(result)) + 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), + }}) } - if !reflect.DeepEqual(result[0], entry) { - t.Errorf("wrong result: got %v, want %v", result[0], entry) + 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) } } @@ -101,59 +122,90 @@ func TestUDP_findnodeMultiReply(t *testing.T) { defer n1.Close() defer n2.Close() - 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), + 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()), + }) } - } - // ask N2 for neighbors. it will send an empty reply back. - // the request will wait for up to bucketSize replies. - resultC := make(chan []*Node) - go func() { - ns, err := n1.net.findnode(n2.self, n1.self.ID) - if err != nil { - t.Error("findnode error:", err) + // 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 } - 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) + if hasDuplicates(result) { + return fmt.Errorf("result slice contains duplicates") } - udp2.send(n1.self, neighborsPacket, neighbors{ - Nodes: nodes[off:end], - Expiration: uint64(time.Now().Add(10 * time.Second).Unix()), - }) + 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) } +} - // check that they are all returned. we cannot just check for - // equality because they might not be returned in the order they - // were sent. - result := <-resultC - if hasDuplicates(result) { - t.Error("result slice contains duplicates") - } - if len(result) != len(nodes) { - t.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 - } +// 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 len(matched) != len(nodes) { - t.Errorf("wrong number of matching nodes: got %d, want %d", len(matched), len(nodes)) + if errcount == n { + return fmt.Errorf("failed on all %d iterations:%s", n, errors) } + return nil } -- cgit v1.2.3 From 170eb3ac684231dc2ddebc34e7006e0f2b5fc0c1 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Fri, 13 Feb 2015 11:38:34 +0100 Subject: p2p/discover: map listening port using configured mechanism --- p2p/discover/udp_test.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'p2p/discover/udp_test.go') diff --git a/p2p/discover/udp_test.go b/p2p/discover/udp_test.go index 740d0a5d9..0a8ff6358 100644 --- a/p2p/discover/udp_test.go +++ b/p2p/discover/udp_test.go @@ -18,8 +18,8 @@ func init() { func TestUDP_ping(t *testing.T) { t.Parallel() - n1, _ := ListenUDP(newkey(), "127.0.0.1:0") - n2, _ := ListenUDP(newkey(), "127.0.0.1:0") + n1, _ := ListenUDP(newkey(), "127.0.0.1:0", nil) + n2, _ := ListenUDP(newkey(), "127.0.0.1:0", nil) defer n1.Close() defer n2.Close() @@ -48,8 +48,8 @@ func find(tab *Table, id NodeID) *Node { func TestUDP_findnode(t *testing.T) { t.Parallel() - n1, _ := ListenUDP(newkey(), "127.0.0.1:0") - n2, _ := ListenUDP(newkey(), "127.0.0.1:0") + n1, _ := ListenUDP(newkey(), "127.0.0.1:0", nil) + n2, _ := ListenUDP(newkey(), "127.0.0.1:0", nil) defer n1.Close() defer n2.Close() @@ -98,7 +98,7 @@ func TestUDP_replytimeout(t *testing.T) { } defer fd.Close() - n1, _ := ListenUDP(newkey(), "127.0.0.1:0") + n1, _ := ListenUDP(newkey(), "127.0.0.1:0", nil) defer n1.Close() n2 := n1.bumpOrAdd(randomID(n1.self.ID, 10), fd.LocalAddr().(*net.UDPAddr)) @@ -116,8 +116,8 @@ func TestUDP_replytimeout(t *testing.T) { func TestUDP_findnodeMultiReply(t *testing.T) { t.Parallel() - n1, _ := ListenUDP(newkey(), "127.0.0.1:0") - n2, _ := ListenUDP(newkey(), "127.0.0.1:0") + 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() -- cgit v1.2.3