From 9fdca91bb75fb6101822868ecd1710f2664290a2 Mon Sep 17 00:00:00 2001 From: Daniel Liu <139250065@qq.com> Date: Thu, 4 Jun 2026 19:57:27 +0800 Subject: [PATCH] fix(p2p/discover): return nil when v4 resolve cannot refresh node Restore Resolve failure semantics to return nil so dial backoff can detect unresolved nodes correctly.\n\nAdd a regression test covering unresolved ENR refresh paths. --- cmd/devp2p/discv4cmd.go | 7 ++++++- p2p/discover/v4_udp.go | 6 +++--- p2p/discover/v4_udp_test.go | 35 +++++++++++++++++++++++++++++++++++ 3 files changed, 44 insertions(+), 4 deletions(-) diff --git a/cmd/devp2p/discv4cmd.go b/cmd/devp2p/discv4cmd.go index 74e1c35fb4a..d14755e9e68 100644 --- a/cmd/devp2p/discv4cmd.go +++ b/cmd/devp2p/discv4cmd.go @@ -289,7 +289,12 @@ func discv4Resolve(ctx *cli.Context) error { disc := startV4(ctx) defer disc.Close() - fmt.Println(disc.Resolve(n).String()) + resolved := disc.Resolve(n) + if resolved == nil { + fmt.Println("unresolved") + return fmt.Errorf("could not resolve node %s", n.ID()) + } + fmt.Println(resolved.String()) return nil } diff --git a/p2p/discover/v4_udp.go b/p2p/discover/v4_udp.go index 4ee4f3c2c74..ebf2121a3ff 100644 --- a/p2p/discover/v4_udp.go +++ b/p2p/discover/v4_udp.go @@ -175,7 +175,7 @@ func (t *UDPv4) Close() { } // Resolve searches for a specific node with the given ID and tries to get the most recent -// version of the node record for it. It returns n if the node could not be resolved. +// version of the node record for it. It returns nil if the node could not be resolved. func (t *UDPv4) Resolve(n *enode.Node) *enode.Node { // Try asking directly. This works if the node is still responding on the endpoint we have. if rn, err := t.RequestENR(n); err == nil { @@ -191,7 +191,7 @@ func (t *UDPv4) Resolve(n *enode.Node) *enode.Node { // Otherwise perform a network lookup. var key enode.Secp256k1 if n.Load(&key) != nil { - return n // no secp256k1 key + return nil // no secp256k1 key } result := t.LookupPubkey((*ecdsa.PublicKey)(&key)) for _, rn := range result { @@ -201,7 +201,7 @@ func (t *UDPv4) Resolve(n *enode.Node) *enode.Node { } } } - return n + return nil } func (t *UDPv4) ourEndpoint() v4wire.Endpoint { diff --git a/p2p/discover/v4_udp_test.go b/p2p/discover/v4_udp_test.go index 9b4dadddb8e..86c665eff2d 100644 --- a/p2p/discover/v4_udp_test.go +++ b/p2p/discover/v4_udp_test.go @@ -184,6 +184,41 @@ func TestUDPv4_pingTimeout(t *testing.T) { } } +func TestUDPv4_resolveReturnsNilWhenUnresolved(t *testing.T) { + test := newUDPTest(t) + defer test.close() + + var r enr.Record + ip := net.IP{127, 0, 0, 1} + id := enode.ID{9} + r.Set(enr.IP(ip)) + r.Set(enr.UDP(30303)) + n := enode.SignNull(&r, id) + + // Avoid triggering the extra bonding ping path in requestENR. + test.udp.db.UpdateLastPingReceived(id, ip, time.Now()) + + if resolved := test.udp.Resolve(n); resolved != nil { + t.Fatalf("expected nil for unresolved node, got: %v", resolved) + } +} + +func TestUDPv4_resolveReturnsNilWhenLookupMisses(t *testing.T) { + test := newUDPTest(t) + defer test.close() + + key := newkey() + ip := net.IP{127, 0, 0, 1} + n := enode.NewV4(&key.PublicKey, ip, 30303, 30303) + + // Avoid triggering the extra bonding ping path in requestENR. + test.udp.db.UpdateLastPingReceived(n.ID(), ip, time.Now()) + + if resolved := test.udp.Resolve(n); resolved != nil { + t.Fatalf("expected nil for unresolved node with secp256k1 key, got: %v", resolved) + } +} + type testPacket byte func (req testPacket) Kind() byte { return byte(req) }