|
@@ -22,6 +22,7 @@ import (
|
|
|
crand "crypto/rand"
|
|
crand "crypto/rand"
|
|
|
"encoding/binary"
|
|
"encoding/binary"
|
|
|
"errors"
|
|
"errors"
|
|
|
|
|
+ "fmt"
|
|
|
"io"
|
|
"io"
|
|
|
"math/rand"
|
|
"math/rand"
|
|
|
"net"
|
|
"net"
|
|
@@ -277,7 +278,7 @@ func TestUDPv4_findnode(t *testing.T) {
|
|
|
test.table.db.UpdateLastPongReceived(remoteID, test.remoteaddr.IP, time.Now())
|
|
test.table.db.UpdateLastPongReceived(remoteID, test.remoteaddr.IP, time.Now())
|
|
|
|
|
|
|
|
// check that closest neighbors are returned.
|
|
// check that closest neighbors are returned.
|
|
|
- expected := test.table.closest(testTarget.ID(), bucketSize, true)
|
|
|
|
|
|
|
+ expected := test.table.findnodeByID(testTarget.ID(), bucketSize, true)
|
|
|
test.packetIn(nil, &v4wire.Findnode{Target: testTarget, Expiration: futureExp})
|
|
test.packetIn(nil, &v4wire.Findnode{Target: testTarget, Expiration: futureExp})
|
|
|
waitNeighbors := func(want []*node) {
|
|
waitNeighbors := func(want []*node) {
|
|
|
test.waitPacketOut(func(p *v4wire.Neighbors, to *net.UDPAddr, hash []byte) {
|
|
test.waitPacketOut(func(p *v4wire.Neighbors, to *net.UDPAddr, hash []byte) {
|
|
@@ -493,6 +494,91 @@ func TestUDPv4_EIP868(t *testing.T) {
|
|
|
})
|
|
})
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+// This test verifies that a small network of nodes can boot up into a healthy state.
|
|
|
|
|
+func TestUDPv4_smallNetConvergence(t *testing.T) {
|
|
|
|
|
+ t.Parallel()
|
|
|
|
|
+
|
|
|
|
|
+ // Start the network.
|
|
|
|
|
+ nodes := make([]*UDPv4, 4)
|
|
|
|
|
+ for i := range nodes {
|
|
|
|
|
+ var cfg Config
|
|
|
|
|
+ if i > 0 {
|
|
|
|
|
+ bn := nodes[0].Self()
|
|
|
|
|
+ cfg.Bootnodes = []*enode.Node{bn}
|
|
|
|
|
+ }
|
|
|
|
|
+ nodes[i] = startLocalhostV4(t, cfg)
|
|
|
|
|
+ defer nodes[i].Close()
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Run through the iterator on all nodes until
|
|
|
|
|
+ // they have all found each other.
|
|
|
|
|
+ status := make(chan error, len(nodes))
|
|
|
|
|
+ for i := range nodes {
|
|
|
|
|
+ node := nodes[i]
|
|
|
|
|
+ go func() {
|
|
|
|
|
+ found := make(map[enode.ID]bool, len(nodes))
|
|
|
|
|
+ it := node.RandomNodes()
|
|
|
|
|
+ for it.Next() {
|
|
|
|
|
+ found[it.Node().ID()] = true
|
|
|
|
|
+ if len(found) == len(nodes) {
|
|
|
|
|
+ status <- nil
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ status <- fmt.Errorf("node %s didn't find all nodes", node.Self().ID().TerminalString())
|
|
|
|
|
+ }()
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Wait for all status reports.
|
|
|
|
|
+ timeout := time.NewTimer(30 * time.Second)
|
|
|
|
|
+ defer timeout.Stop()
|
|
|
|
|
+ for received := 0; received < len(nodes); {
|
|
|
|
|
+ select {
|
|
|
|
|
+ case <-timeout.C:
|
|
|
|
|
+ for _, node := range nodes {
|
|
|
|
|
+ node.Close()
|
|
|
|
|
+ }
|
|
|
|
|
+ case err := <-status:
|
|
|
|
|
+ received++
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ t.Error("ERROR:", err)
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+func startLocalhostV4(t *testing.T, cfg Config) *UDPv4 {
|
|
|
|
|
+ t.Helper()
|
|
|
|
|
+
|
|
|
|
|
+ cfg.PrivateKey = newkey()
|
|
|
|
|
+ db, _ := enode.OpenDB("")
|
|
|
|
|
+ ln := enode.NewLocalNode(db, cfg.PrivateKey)
|
|
|
|
|
+
|
|
|
|
|
+ // Prefix logs with node ID.
|
|
|
|
|
+ lprefix := fmt.Sprintf("(%s)", ln.ID().TerminalString())
|
|
|
|
|
+ lfmt := log.TerminalFormat(false)
|
|
|
|
|
+ cfg.Log = testlog.Logger(t, log.LvlTrace)
|
|
|
|
|
+ cfg.Log.SetHandler(log.FuncHandler(func(r *log.Record) error {
|
|
|
|
|
+ t.Logf("%s %s", lprefix, lfmt.Format(r))
|
|
|
|
|
+ return nil
|
|
|
|
|
+ }))
|
|
|
|
|
+
|
|
|
|
|
+ // Listen.
|
|
|
|
|
+ socket, err := net.ListenUDP("udp4", &net.UDPAddr{IP: net.IP{127, 0, 0, 1}})
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ t.Fatal(err)
|
|
|
|
|
+ }
|
|
|
|
|
+ realaddr := socket.LocalAddr().(*net.UDPAddr)
|
|
|
|
|
+ ln.SetStaticIP(realaddr.IP)
|
|
|
|
|
+ ln.SetFallbackUDP(realaddr.Port)
|
|
|
|
|
+ udp, err := ListenV4(socket, ln, cfg)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ t.Fatal(err)
|
|
|
|
|
+ }
|
|
|
|
|
+ return udp
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
// dgramPipe is a fake UDP socket. It queues all sent datagrams.
|
|
// dgramPipe is a fake UDP socket. It queues all sent datagrams.
|
|
|
type dgramPipe struct {
|
|
type dgramPipe struct {
|
|
|
mu *sync.Mutex
|
|
mu *sync.Mutex
|