From 24e07391771927a6b2a0047165d017a65ebe0b86 Mon Sep 17 00:00:00 2001 From: Csaba Kiraly Date: Mon, 20 Apr 2026 10:14:20 +0000 Subject: [PATCH 1/9] cmd/devp2p/v5test: test FINDNODE wrong-ip challenge --- cmd/devp2p/internal/v5test/discv5tests.go | 25 +++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/cmd/devp2p/internal/v5test/discv5tests.go b/cmd/devp2p/internal/v5test/discv5tests.go index 4dc2507693..ec10db6ba4 100644 --- a/cmd/devp2p/internal/v5test/discv5tests.go +++ b/cmd/devp2p/internal/v5test/discv5tests.go @@ -54,6 +54,7 @@ func (s *Suite) AllTests() []utesting.Test { {Name: "PingMultiIP", Fn: s.TestPingMultiIP}, {Name: "HandshakeResend", Fn: s.TestHandshakeResend}, {Name: "TalkRequest", Fn: s.TestTalkRequest}, + {Name: "FindnodeWrongIP", Fn: s.TestFindnodeWrongIP}, {Name: "FindnodeZeroDistance", Fn: s.TestFindnodeZeroDistance}, {Name: "FindnodeResults", Fn: s.TestFindnodeResults}, } @@ -232,6 +233,30 @@ and expects an empty TALKRESP response.`) } } +func (s *Suite) TestFindnodeWrongIP(t *utesting.T) { + t.Log(`This test establishes a session on one IP, then sends FINDNODE from another IP. +The remote node should challenge the second endpoint with WHOAREYOU instead of returning NODES.`) + + conn, l1, l2 := s.listen2(t) + defer conn.close() + + ping := &v5wire.Ping{ReqID: conn.nextReqID()} + if resp := conn.reqresp(l1, ping); resp.Kind() != v5wire.PongMsg { + t.Fatal("expected PONG, got", resp) + } + + req := &v5wire.Findnode{ReqID: conn.nextReqID(), Distances: []uint{0}} + conn.write(l2, req, nil) + switch resp := conn.read(l2).(type) { + case *v5wire.Whoareyou: + t.Log("got WHOAREYOU for FINDNODE on wrong IP as expected") + case *v5wire.Nodes: + t.Fatalf("unexpected NODES response on wrong IP: %+v", resp) + default: + t.Fatal("expected WHOAREYOU, got", resp) + } +} + func (s *Suite) TestFindnodeZeroDistance(t *utesting.T) { t.Log(`This test checks that the remote node returns itself for FINDNODE with distance zero.`) From e7e81cfce2a14a9939c09ccfdeaaf1fec9fdfd13 Mon Sep 17 00:00:00 2001 From: Csaba Kiraly Date: Mon, 20 Apr 2026 10:20:59 +0000 Subject: [PATCH 2/9] cmd/devp2p/v5test: test FINDNODE handshake gating --- cmd/devp2p/internal/v5test/discv5tests.go | 29 +++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/cmd/devp2p/internal/v5test/discv5tests.go b/cmd/devp2p/internal/v5test/discv5tests.go index ec10db6ba4..9bd390d4ad 100644 --- a/cmd/devp2p/internal/v5test/discv5tests.go +++ b/cmd/devp2p/internal/v5test/discv5tests.go @@ -55,6 +55,7 @@ func (s *Suite) AllTests() []utesting.Test { {Name: "HandshakeResend", Fn: s.TestHandshakeResend}, {Name: "TalkRequest", Fn: s.TestTalkRequest}, {Name: "FindnodeWrongIP", Fn: s.TestFindnodeWrongIP}, + {Name: "FindnodeHandshake", Fn: s.TestFindnodeHandshake}, {Name: "FindnodeZeroDistance", Fn: s.TestFindnodeZeroDistance}, {Name: "FindnodeResults", Fn: s.TestFindnodeResults}, } @@ -257,6 +258,34 @@ The remote node should challenge the second endpoint with WHOAREYOU instead of r } } +func (s *Suite) TestFindnodeHandshake(t *utesting.T) { + conn, l1 := s.listen1(t) + defer conn.close() + + req := &v5wire.Findnode{ReqID: conn.nextReqID(), Distances: []uint{0}} + nonce := conn.write(l1, req, nil) + + challenge, ok := conn.read(l1).(*v5wire.Whoareyou) + if !ok { + t.Fatal("expected WHOAREYOU before NODES") + } + if challenge.Nonce != nonce { + t.Fatalf("wrong nonce %x in WHOAREYOU (want %x)", challenge.Nonce[:], nonce[:]) + } + + challenge.Node = conn.remote + conn.write(l1, req, challenge) + + resp := conn.read(l1) + nodes, ok := resp.(*v5wire.Nodes) + if !ok { + t.Fatal("expected NODES after completing handshake, got", resp) + } + if !bytes.Equal(nodes.ReqID, req.ReqID) { + t.Fatalf("wrong request ID %x in NODES, want %x", nodes.ReqID, req.ReqID) + } +} + func (s *Suite) TestFindnodeZeroDistance(t *utesting.T) { t.Log(`This test checks that the remote node returns itself for FINDNODE with distance zero.`) From ba64f9eb881d7986788e045a42a14e62cc109e3e Mon Sep 17 00:00:00 2001 From: Csaba Kiraly Date: Mon, 20 Apr 2026 10:28:05 +0000 Subject: [PATCH 3/9] cmd/devp2p/v5test: relax findnode handshake test --- cmd/devp2p/internal/v5test/discv5tests.go | 34 ++++++++++++++++------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/cmd/devp2p/internal/v5test/discv5tests.go b/cmd/devp2p/internal/v5test/discv5tests.go index 9bd390d4ad..233fdc9056 100644 --- a/cmd/devp2p/internal/v5test/discv5tests.go +++ b/cmd/devp2p/internal/v5test/discv5tests.go @@ -259,30 +259,44 @@ The remote node should challenge the second endpoint with WHOAREYOU instead of r } func (s *Suite) TestFindnodeHandshake(t *utesting.T) { + t.Log(`This test checks that the remote answers a FINDNODE request only after completing the WHOAREYOU handshake.`) + conn, l1 := s.listen1(t) defer conn.close() req := &v5wire.Findnode{ReqID: conn.nextReqID(), Distances: []uint{0}} nonce := conn.write(l1, req, nil) - challenge, ok := conn.read(l1).(*v5wire.Whoareyou) + resp, from := conn.readFrom(l1) + challenge, ok := resp.(*v5wire.Whoareyou) if !ok { - t.Fatal("expected WHOAREYOU before NODES") + t.Fatalf("expected WHOAREYOU before NODES, got %T (%v) from %v", resp, resp, from) } if challenge.Nonce != nonce { t.Fatalf("wrong nonce %x in WHOAREYOU (want %x)", challenge.Nonce[:], nonce[:]) } challenge.Node = conn.remote - conn.write(l1, req, challenge) + conn.writeTo(l1, req, challenge, from) - resp := conn.read(l1) - nodes, ok := resp.(*v5wire.Nodes) - if !ok { - t.Fatal("expected NODES after completing handshake, got", resp) - } - if !bytes.Equal(nodes.ReqID, req.ReqID) { - t.Fatalf("wrong request ID %x in NODES, want %x", nodes.ReqID, req.ReqID) + for { + resp, from := conn.readFrom(l1) + switch resp := resp.(type) { + case *v5wire.Ping: + conn.writeTo(l1, &v5wire.Pong{ + ReqID: resp.ReqID, + ENRSeq: conn.localNode.Seq(), + ToIP: from.IP, + ToPort: uint16(from.Port), + }, nil, from) + case *v5wire.Nodes: + if !bytes.Equal(resp.ReqID, req.ReqID) { + t.Fatalf("wrong request ID %x in NODES, want %x", resp.ReqID, req.ReqID) + } + return + default: + t.Fatalf("expected NODES after completing handshake, got %T (%v) from %v", resp, resp, from) + } } } From f8155b9d2a4c25191f8c0bebb7c086ac8a265bfe Mon Sep 17 00:00:00 2001 From: Csaba Kiraly Date: Mon, 20 Apr 2026 10:37:07 +0000 Subject: [PATCH 4/9] cmd/devp2p/v5test: test unsolicited NODES hygiene --- cmd/devp2p/internal/v5test/discv5tests.go | 34 +++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/cmd/devp2p/internal/v5test/discv5tests.go b/cmd/devp2p/internal/v5test/discv5tests.go index 233fdc9056..9644880a9a 100644 --- a/cmd/devp2p/internal/v5test/discv5tests.go +++ b/cmd/devp2p/internal/v5test/discv5tests.go @@ -26,6 +26,7 @@ import ( "github.com/ethereum/go-ethereum/internal/utesting" "github.com/ethereum/go-ethereum/p2p/discover/v5wire" "github.com/ethereum/go-ethereum/p2p/enode" + "github.com/ethereum/go-ethereum/p2p/enr" "github.com/ethereum/go-ethereum/p2p/netutil" ) @@ -58,6 +59,7 @@ func (s *Suite) AllTests() []utesting.Test { {Name: "FindnodeHandshake", Fn: s.TestFindnodeHandshake}, {Name: "FindnodeZeroDistance", Fn: s.TestFindnodeZeroDistance}, {Name: "FindnodeResults", Fn: s.TestFindnodeResults}, + {Name: "UnsolicitedNodes", Fn: s.TestUnsolicitedNodes}, } } @@ -318,6 +320,38 @@ func (s *Suite) TestFindnodeZeroDistance(t *utesting.T) { } } +func (s *Suite) TestUnsolicitedNodes(t *utesting.T) { + conn, l1 := s.listen1(t) + defer conn.close() + + // Establish session so the unsolicited packet is well-formed and authenticated. + ping := &v5wire.Ping{ReqID: conn.nextReqID()} + if resp := conn.reqresp(l1, ping); resp.Kind() != v5wire.PongMsg { + t.Fatal("expected PONG, got", resp) + } + + fakeConn, fakeL := s.listen1(t) + defer fakeConn.close() + fakeConn.setEndpoint(fakeL) + + unsolicited := &v5wire.Nodes{ + ReqID: conn.nextReqID(), + RespCount: 1, + Nodes: []*enr.Record{fakeConn.localNode.Node().Record()}, + } + conn.write(l1, unsolicited, nil) + + results, err := conn.findnode(l1, []uint{uint(enode.LogDist(fakeConn.localNode.ID(), s.Dest.ID()))}) + if err != nil { + t.Fatal(err) + } + for _, n := range results { + if n.ID() == fakeConn.localNode.ID() { + t.Fatal("FINDNODE result contains node from unsolicited NODES response") + } + } +} + func (s *Suite) TestFindnodeResults(t *utesting.T) { t.Log(`This test pings the node under test from multiple other endpoints and node identities (the 'bystanders'). After waiting for them to be accepted into the remote table, the test checks From 0458f0c94b2b87ea181f0c390a41184858c26666 Mon Sep 17 00:00:00 2001 From: Csaba Kiraly Date: Mon, 20 Apr 2026 10:45:06 +0000 Subject: [PATCH 5/9] cmd/devp2p/v5test: harden unsolicited NODES hygiene test --- cmd/devp2p/internal/v5test/discv5tests.go | 34 +++++++++++++++++++---- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/cmd/devp2p/internal/v5test/discv5tests.go b/cmd/devp2p/internal/v5test/discv5tests.go index 9644880a9a..4397c82d02 100644 --- a/cmd/devp2p/internal/v5test/discv5tests.go +++ b/cmd/devp2p/internal/v5test/discv5tests.go @@ -321,6 +321,9 @@ func (s *Suite) TestFindnodeZeroDistance(t *utesting.T) { } func (s *Suite) TestUnsolicitedNodes(t *utesting.T) { + t.Log(`This test sends an unsolicited NODES response advertising a fake node. +The remote node should neither contact the injected node nor return it from later FINDNODE queries.`) + conn, l1 := s.listen1(t) defer conn.close() @@ -339,15 +342,36 @@ func (s *Suite) TestUnsolicitedNodes(t *utesting.T) { RespCount: 1, Nodes: []*enr.Record{fakeConn.localNode.Node().Record()}, } + t.Log("sending unsolicited NODES response with injected node") conn.write(l1, unsolicited, nil) - results, err := conn.findnode(l1, []uint{uint(enode.LogDist(fakeConn.localNode.ID(), s.Dest.ID()))}) - if err != nil { + const contactWindow = 500 * time.Millisecond + buf := make([]byte, 1280) + if err := fakeL.SetReadDeadline(time.Now().Add(contactWindow)); err != nil { t.Fatal(err) } - for _, n := range results { - if n.ID() == fakeConn.localNode.ID() { - t.Fatal("FINDNODE result contains node from unsolicited NODES response") + if n, from, err := fakeL.ReadFrom(buf); err == nil { + t.Fatalf("remote contacted injected node after unsolicited NODES: %d bytes from %v", n, from) + } else if !netutil.IsTimeout(err) { + t.Fatalf("waiting for unexpected contact failed: %v", err) + } + + dist := uint(enode.LogDist(fakeConn.localNode.ID(), s.Dest.ID())) + const maxAttempts = 3 + const retryInterval = 200 * time.Millisecond + for attempt := 1; attempt <= maxAttempts; attempt++ { + results, err := conn.findnode(l1, []uint{dist}) + if err != nil { + t.Fatal(err) + } + for _, n := range results { + if n.ID() == fakeConn.localNode.ID() { + t.Fatalf("attempt %d: FINDNODE result contains node from unsolicited NODES response", attempt) + } + } + t.Logf("attempt %d: injected node not returned in FINDNODE results", attempt) + if attempt < maxAttempts { + time.Sleep(retryInterval) } } } From 6241de1e922cece23c19102845cbd1563b092bb6 Mon Sep 17 00:00:00 2001 From: Csaba Kiraly Date: Mon, 20 Apr 2026 12:54:31 +0000 Subject: [PATCH 6/9] cmd/devp2p/v5test: document new discv5 tests --- cmd/devp2p/internal/v5test/discv5tests.go | 44 +++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/cmd/devp2p/internal/v5test/discv5tests.go b/cmd/devp2p/internal/v5test/discv5tests.go index 4397c82d02..3183472d7b 100644 --- a/cmd/devp2p/internal/v5test/discv5tests.go +++ b/cmd/devp2p/internal/v5test/discv5tests.go @@ -236,6 +236,20 @@ and expects an empty TALKRESP response.`) } } +// TestFindnodeWrongIP establishes a session on one IP, then sends FINDNODE from a +// different IP and expects the remote node to restart the handshake with WHOAREYOU. +// +// Why it exists: +// This test checks that discv5 sessions are tied to the UDP endpoint, not just the peer +// identity. Reusing a previously established session from a different source IP/port +// should fail decryption/authentication and trigger a fresh challenge instead of +// producing a valid NODES response. It extends the existing wrong-IP coverage from PING +// to FINDNODE. +// +// Relevant spec: +// The discv5 session model binds handshake state and session secrets to a specific UDP +// endpoint. When a peer switches endpoints, recipients should refuse to decrypt messages +// from the new endpoint and answer with WHOAREYOU so the session is re-established. func (s *Suite) TestFindnodeWrongIP(t *utesting.T) { t.Log(`This test establishes a session on one IP, then sends FINDNODE from another IP. The remote node should challenge the second endpoint with WHOAREYOU instead of returning NODES.`) @@ -260,6 +274,21 @@ The remote node should challenge the second endpoint with WHOAREYOU instead of r } } +// TestFindnodeHandshake sends FINDNODE without an existing session and verifies +// that the remote node answers only after the WHOAREYOU handshake completes. +// +// Why it exists: +// This test makes FINDNODE's handshake gating explicit. Without an existing session, the +// recipient should not answer the initial request directly with NODES. It must first send +// WHOAREYOU, require the requester to resend the same FINDNODE as a handshake packet, and +// only then return the response. The test also checks that the challenge nonce matches the +// original request and that the eventual NODES reply carries the right request ID. +// +// Relevant spec: +// In discv5, when the recipient has no valid session or cannot decrypt/authenticate an +// incoming request, it must respond with WHOAREYOU. The requester then resends the same +// request as a handshake packet, and only after the handshake succeeds should the +// recipient answer the request. func (s *Suite) TestFindnodeHandshake(t *utesting.T) { t.Log(`This test checks that the remote answers a FINDNODE request only after completing the WHOAREYOU handshake.`) @@ -320,6 +349,21 @@ func (s *Suite) TestFindnodeZeroDistance(t *utesting.T) { } } +// TestUnsolicitedNodes sends an unsolicited authenticated NODES packet and checks +// that the advertised node is neither contacted nor returned by later FINDNODE queries. +// +// Why it exists: +// This test checks that a peer cannot inject arbitrary ENRs into the remote node's view +// of the network by sending unsolicited NODES. Even when the packet is authenticated and +// well-formed, accepting it as useful routing data would allow table pollution. The test +// therefore looks for two concrete bad outcomes: the remote node contacting the injected +// fake node, or the fake node later appearing in FINDNODE results. +// +// Relevant spec: +// In discv5, NODES is defined as the response to FINDNODE or TOPICQUERY, not as a +// free-standing advertisement. Handling of NODES is therefore request/response-oriented, +// keyed to an earlier query and request ID. Ignoring unsolicited NODES is the hygiene +// behavior implied by that response model. func (s *Suite) TestUnsolicitedNodes(t *utesting.T) { t.Log(`This test sends an unsolicited NODES response advertising a fake node. The remote node should neither contact the injected node nor return it from later FINDNODE queries.`) From 3fa594333c37c7b50dd02b6dcce1cc4318ae7409 Mon Sep 17 00:00:00 2001 From: Csaba Kiraly Date: Mon, 20 Apr 2026 22:10:17 +0000 Subject: [PATCH 7/9] Revert "cmd/devp2p/v5test: document new discv5 tests" This reverts commit 6241de1e922cece23c19102845cbd1563b092bb6. --- cmd/devp2p/internal/v5test/discv5tests.go | 44 ----------------------- 1 file changed, 44 deletions(-) diff --git a/cmd/devp2p/internal/v5test/discv5tests.go b/cmd/devp2p/internal/v5test/discv5tests.go index 3183472d7b..4397c82d02 100644 --- a/cmd/devp2p/internal/v5test/discv5tests.go +++ b/cmd/devp2p/internal/v5test/discv5tests.go @@ -236,20 +236,6 @@ and expects an empty TALKRESP response.`) } } -// TestFindnodeWrongIP establishes a session on one IP, then sends FINDNODE from a -// different IP and expects the remote node to restart the handshake with WHOAREYOU. -// -// Why it exists: -// This test checks that discv5 sessions are tied to the UDP endpoint, not just the peer -// identity. Reusing a previously established session from a different source IP/port -// should fail decryption/authentication and trigger a fresh challenge instead of -// producing a valid NODES response. It extends the existing wrong-IP coverage from PING -// to FINDNODE. -// -// Relevant spec: -// The discv5 session model binds handshake state and session secrets to a specific UDP -// endpoint. When a peer switches endpoints, recipients should refuse to decrypt messages -// from the new endpoint and answer with WHOAREYOU so the session is re-established. func (s *Suite) TestFindnodeWrongIP(t *utesting.T) { t.Log(`This test establishes a session on one IP, then sends FINDNODE from another IP. The remote node should challenge the second endpoint with WHOAREYOU instead of returning NODES.`) @@ -274,21 +260,6 @@ The remote node should challenge the second endpoint with WHOAREYOU instead of r } } -// TestFindnodeHandshake sends FINDNODE without an existing session and verifies -// that the remote node answers only after the WHOAREYOU handshake completes. -// -// Why it exists: -// This test makes FINDNODE's handshake gating explicit. Without an existing session, the -// recipient should not answer the initial request directly with NODES. It must first send -// WHOAREYOU, require the requester to resend the same FINDNODE as a handshake packet, and -// only then return the response. The test also checks that the challenge nonce matches the -// original request and that the eventual NODES reply carries the right request ID. -// -// Relevant spec: -// In discv5, when the recipient has no valid session or cannot decrypt/authenticate an -// incoming request, it must respond with WHOAREYOU. The requester then resends the same -// request as a handshake packet, and only after the handshake succeeds should the -// recipient answer the request. func (s *Suite) TestFindnodeHandshake(t *utesting.T) { t.Log(`This test checks that the remote answers a FINDNODE request only after completing the WHOAREYOU handshake.`) @@ -349,21 +320,6 @@ func (s *Suite) TestFindnodeZeroDistance(t *utesting.T) { } } -// TestUnsolicitedNodes sends an unsolicited authenticated NODES packet and checks -// that the advertised node is neither contacted nor returned by later FINDNODE queries. -// -// Why it exists: -// This test checks that a peer cannot inject arbitrary ENRs into the remote node's view -// of the network by sending unsolicited NODES. Even when the packet is authenticated and -// well-formed, accepting it as useful routing data would allow table pollution. The test -// therefore looks for two concrete bad outcomes: the remote node contacting the injected -// fake node, or the fake node later appearing in FINDNODE results. -// -// Relevant spec: -// In discv5, NODES is defined as the response to FINDNODE or TOPICQUERY, not as a -// free-standing advertisement. Handling of NODES is therefore request/response-oriented, -// keyed to an earlier query and request ID. Ignoring unsolicited NODES is the hygiene -// behavior implied by that response model. func (s *Suite) TestUnsolicitedNodes(t *utesting.T) { t.Log(`This test sends an unsolicited NODES response advertising a fake node. The remote node should neither contact the injected node nor return it from later FINDNODE queries.`) From 13d5001c60ba7dbb6dd194cf5ebe703604583572 Mon Sep 17 00:00:00 2001 From: Csaba Kiraly Date: Tue, 21 Apr 2026 08:48:14 +0000 Subject: [PATCH 8/9] cmd/devp2p/v5test: remove unsolicited NODES sleep --- cmd/devp2p/internal/v5test/discv5tests.go | 30 +++++++++++++---------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/cmd/devp2p/internal/v5test/discv5tests.go b/cmd/devp2p/internal/v5test/discv5tests.go index 4397c82d02..e5760603ad 100644 --- a/cmd/devp2p/internal/v5test/discv5tests.go +++ b/cmd/devp2p/internal/v5test/discv5tests.go @@ -345,34 +345,38 @@ The remote node should neither contact the injected node nor return it from late t.Log("sending unsolicited NODES response with injected node") conn.write(l1, unsolicited, nil) - const contactWindow = 500 * time.Millisecond - buf := make([]byte, 1280) - if err := fakeL.SetReadDeadline(time.Now().Add(contactWindow)); err != nil { - t.Fatal(err) - } - if n, from, err := fakeL.ReadFrom(buf); err == nil { - t.Fatalf("remote contacted injected node after unsolicited NODES: %d bytes from %v", n, from) - } else if !netutil.IsTimeout(err) { - t.Fatalf("waiting for unexpected contact failed: %v", err) + checkNoContact := func(phase string) { + buf := make([]byte, 1280) + if err := fakeL.SetReadDeadline(time.Now()); err != nil { + t.Fatal(err) + } + for { + n, from, err := fakeL.ReadFrom(buf) + if err == nil { + t.Fatalf("%s: remote contacted injected node: %d bytes from %v", phase, n, from) + } + if netutil.IsTimeout(err) { + return + } + t.Fatalf("%s: checking for unexpected contact failed: %v", phase, err) + } } + checkNoContact("after unsolicited NODES") dist := uint(enode.LogDist(fakeConn.localNode.ID(), s.Dest.ID())) const maxAttempts = 3 - const retryInterval = 200 * time.Millisecond for attempt := 1; attempt <= maxAttempts; attempt++ { results, err := conn.findnode(l1, []uint{dist}) if err != nil { t.Fatal(err) } + checkNoContact("during FINDNODE probes") for _, n := range results { if n.ID() == fakeConn.localNode.ID() { t.Fatalf("attempt %d: FINDNODE result contains node from unsolicited NODES response", attempt) } } t.Logf("attempt %d: injected node not returned in FINDNODE results", attempt) - if attempt < maxAttempts { - time.Sleep(retryInterval) - } } } From 50013f189b03484aba39275631ffadcf58542d14 Mon Sep 17 00:00:00 2001 From: Csaba Kiraly Date: Tue, 21 Apr 2026 11:53:06 +0000 Subject: [PATCH 9/9] cmd/devp2p/v5test: isolate unsolicited NODES probes --- cmd/devp2p/internal/v5test/discv5tests.go | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/cmd/devp2p/internal/v5test/discv5tests.go b/cmd/devp2p/internal/v5test/discv5tests.go index e5760603ad..84581a9a77 100644 --- a/cmd/devp2p/internal/v5test/discv5tests.go +++ b/cmd/devp2p/internal/v5test/discv5tests.go @@ -324,12 +324,13 @@ func (s *Suite) TestUnsolicitedNodes(t *utesting.T) { t.Log(`This test sends an unsolicited NODES response advertising a fake node. The remote node should neither contact the injected node nor return it from later FINDNODE queries.`) - conn, l1 := s.listen1(t) - defer conn.close() + injectConn, injectL := s.listen1(t) + defer injectConn.close() - // Establish session so the unsolicited packet is well-formed and authenticated. - ping := &v5wire.Ping{ReqID: conn.nextReqID()} - if resp := conn.reqresp(l1, ping); resp.Kind() != v5wire.PongMsg { + // Establish a session for the injection path so the unsolicited packet is + // well-formed and authenticated. + ping := &v5wire.Ping{ReqID: injectConn.nextReqID()} + if resp := injectConn.reqresp(injectL, ping); resp.Kind() != v5wire.PongMsg { t.Fatal("expected PONG, got", resp) } @@ -338,12 +339,12 @@ The remote node should neither contact the injected node nor return it from late fakeConn.setEndpoint(fakeL) unsolicited := &v5wire.Nodes{ - ReqID: conn.nextReqID(), + ReqID: injectConn.nextReqID(), RespCount: 1, Nodes: []*enr.Record{fakeConn.localNode.Node().Record()}, } t.Log("sending unsolicited NODES response with injected node") - conn.write(l1, unsolicited, nil) + injectConn.write(injectL, unsolicited, nil) checkNoContact := func(phase string) { buf := make([]byte, 1280) @@ -363,10 +364,13 @@ The remote node should neither contact the injected node nor return it from late } checkNoContact("after unsolicited NODES") + probeConn, probeL := s.listen1(t) + defer probeConn.close() + dist := uint(enode.LogDist(fakeConn.localNode.ID(), s.Dest.ID())) const maxAttempts = 3 for attempt := 1; attempt <= maxAttempts; attempt++ { - results, err := conn.findnode(l1, []uint{dist}) + results, err := probeConn.findnode(probeL, []uint{dist}) if err != nil { t.Fatal(err) }