p2p/discover: avoid dropping unverified nodes when table is almost empty (#21396)
This change improves discovery behavior in small networks. Very small networks would often fail to bootstrap because all member nodes were dropping table content due to findnode failure. The check is now changed to avoid dropping nodes on findnode failure when their bucket is almost empty. It also relaxes the liveness check requirement for FINDNODE/v4 response nodes, returning unverified nodes as results when there aren't any verified nodes yet. The "findnode failed" log now reports whether the node was dropped instead of the number of results. The value of the "results" was always zero by definition. Co-authored-by: Felix Lange <fjl@twurst.com>
This commit is contained in:
@ -22,6 +22,7 @@ import (
|
||||
crand "crypto/rand"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/rand"
|
||||
"net"
|
||||
@ -277,7 +278,7 @@ func TestUDPv4_findnode(t *testing.T) {
|
||||
test.table.db.UpdateLastPongReceived(remoteID, test.remoteaddr.IP, time.Now())
|
||||
|
||||
// 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})
|
||||
waitNeighbors := func(want []*node) {
|
||||
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.
|
||||
type dgramPipe struct {
|
||||
mu *sync.Mutex
|
||||
|
Reference in New Issue
Block a user