diff --git a/p2p/discover/v4_udp.go b/p2p/discover/v4_udp.go index ae8cbec3e2..28702f0fc6 100644 --- a/p2p/discover/v4_udp.go +++ b/p2p/discover/v4_udp.go @@ -355,7 +355,10 @@ func (t *UDPv4) findnode(toid enode.ID, toAddrPort netip.AddrPort, target v4wire // RequestENR sends ENRRequest to the given node and waits for a response. func (t *UDPv4) RequestENR(n *enode.Node) (*enode.Node, error) { - addr, _ := n.UDPEndpoint() + addr, ok := n.UDPEndpoint() + if !ok { + return nil, errNoUDPEndpoint + } t.ensureBond(n.ID(), addr) req := &v4wire.ENRRequest{ diff --git a/p2p/discover/v4_udp_test.go b/p2p/discover/v4_udp_test.go index 287f0c34fa..a4136c76d3 100644 --- a/p2p/discover/v4_udp_test.go +++ b/p2p/discover/v4_udp_test.go @@ -158,6 +158,23 @@ func TestUDPv4_pingTimeout(t *testing.T) { } } +// TestUDPv4_RequestENRNoUDPEndpoint verifies that RequestENR returns a clean +// errNoUDPEndpoint error for a node without a usable UDP endpoint, instead of +// silently sending a packet to the zero address and waiting for a timeout. +// This mirrors the existing guards in ping/Ping/findnode. +func TestUDPv4_RequestENRNoUDPEndpoint(t *testing.T) { + t.Parallel() + test := newUDPTest(t) + defer test.close() + + key := newkey() + // UDP port 0 makes UDPEndpoint return ok=false. + node := enode.NewV4(&key.PublicKey, net.ParseIP("1.2.3.4"), 2222, 0) + if _, err := test.udp.RequestENR(node); err != errNoUDPEndpoint { + t.Errorf("expected errNoUDPEndpoint, got %v", err) + } +} + type testPacket byte func (req testPacket) Kind() byte { return byte(req) }