mirror of https://github.com/ethereum/go-ethereum
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
211 lines
5.2 KiB
211 lines
5.2 KiB
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")
|
|
n2, _ := ListenUDP(newkey(), "127.0.0.1:0")
|
|
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")
|
|
n2, _ := ListenUDP(newkey(), "127.0.0.1:0")
|
|
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")
|
|
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")
|
|
n2, _ := ListenUDP(newkey(), "127.0.0.1:0")
|
|
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
|
|
}
|
|
|