Skip to content

Commit 3b42dbf

Browse files
test: Not redialing connections if in pending inbound state
1 parent db9f977 commit 3b42dbf

File tree

2 files changed

+179
-0
lines changed

2 files changed

+179
-0
lines changed

p2p/dial_test.go

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -423,6 +423,82 @@ func TestDialSchedDNSHostname(t *testing.T) {
423423
})
424424
}
425425

426+
// This test checks that nodes with pending inbound connections are not dialed.
427+
func TestDialSchedPendingInbound(t *testing.T) {
428+
t.Parallel()
429+
430+
config := dialConfig{
431+
maxActiveDials: 5,
432+
maxDialPeers: 4,
433+
}
434+
runDialTest(t, config, []dialTestRound{
435+
// 2 peers are connected, leaving 2 dial slots.
436+
// Node 0x03 has a pending inbound connection.
437+
// Discovered nodes 0x03, 0x04, 0x05 but only 0x04 and 0x05 should be dialed.
438+
{
439+
peersAdded: []*conn{
440+
{flags: dynDialedConn, node: newNode(uintID(0x01), "127.0.0.1:30303")},
441+
{flags: dynDialedConn, node: newNode(uintID(0x02), "127.0.0.2:30303")},
442+
},
443+
update: func(d *dialScheduler) {
444+
d.inboundPending(uintID(0x03))
445+
},
446+
discovered: []*enode.Node{
447+
newNode(uintID(0x03), "127.0.0.3:30303"), // not dialed because pending inbound
448+
newNode(uintID(0x04), "127.0.0.4:30303"),
449+
newNode(uintID(0x05), "127.0.0.5:30303"),
450+
},
451+
wantNewDials: []*enode.Node{
452+
newNode(uintID(0x04), "127.0.0.4:30303"),
453+
newNode(uintID(0x05), "127.0.0.5:30303"),
454+
},
455+
},
456+
// Pending inbound connection for 0x03 completes successfully.
457+
// Node 0x03 becomes a connected peer.
458+
// One dial slot remains, node 0x06 is dialed.
459+
{
460+
update: func(d *dialScheduler) {
461+
// Pending inbound completes
462+
d.inboundCompleted(uintID(0x03))
463+
},
464+
peersAdded: []*conn{
465+
{flags: inboundConn, node: newNode(uintID(0x03), "127.0.0.3:30303")},
466+
},
467+
succeeded: []enode.ID{
468+
uintID(0x04),
469+
},
470+
failed: []enode.ID{
471+
uintID(0x05),
472+
},
473+
discovered: []*enode.Node{
474+
newNode(uintID(0x03), "127.0.0.3:30303"), // not dialed, now connected
475+
newNode(uintID(0x06), "127.0.0.6:30303"),
476+
},
477+
wantNewDials: []*enode.Node{
478+
newNode(uintID(0x06), "127.0.0.6:30303"),
479+
},
480+
},
481+
// Inbound peer 0x03 disconnects.
482+
// Another pending inbound starts for 0x07.
483+
// Only 0x03 should be dialed, not 0x07.
484+
{
485+
peersRemoved: []enode.ID{
486+
uintID(0x03),
487+
},
488+
update: func(d *dialScheduler) {
489+
d.inboundPending(uintID(0x07))
490+
},
491+
discovered: []*enode.Node{
492+
newNode(uintID(0x03), "127.0.0.3:30303"),
493+
newNode(uintID(0x07), "127.0.0.7:30303"), // not dialed because pending inbound
494+
},
495+
wantNewDials: []*enode.Node{
496+
newNode(uintID(0x03), "127.0.0.3:30303"),
497+
},
498+
},
499+
})
500+
}
501+
426502
// -------
427503
// Code below here is the framework for the tests above.
428504

p2p/server_test.go

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -463,6 +463,109 @@ func TestServerSetupConn(t *testing.T) {
463463
}
464464
}
465465

466+
// TestServerPendingInboundRejection checks that duplicate inbound connections
467+
// are rejected when there's already a pending inbound connection from the same peer.
468+
func TestServerPendingInboundRejection(t *testing.T) {
469+
trustedNode := newkey()
470+
srv := &Server{
471+
Config: Config{
472+
PrivateKey: newkey(),
473+
MaxPeers: 10,
474+
NoDial: true,
475+
NoDiscovery: true,
476+
Logger: testlog.Logger(t, log.LvlTrace),
477+
},
478+
}
479+
if err := srv.Start(); err != nil {
480+
t.Fatalf("could not start: %v", err)
481+
}
482+
defer srv.Stop()
483+
484+
newconn := func(id enode.ID) *conn {
485+
fd, _ := net.Pipe()
486+
tx := newTestTransport(&trustedNode.PublicKey, fd, nil)
487+
node := enode.SignNull(new(enr.Record), id)
488+
return &conn{fd: fd, transport: tx, flags: inboundConn, node: node, cont: make(chan error)}
489+
}
490+
491+
// Create two connections from the same peer
492+
peerID := randomID()
493+
c1 := newconn(peerID)
494+
c2 := newconn(peerID)
495+
496+
// First connection enters pendingInbound
497+
err1 := srv.checkpoint(c1, srv.checkpointPostHandshake)
498+
if err1 != nil {
499+
t.Fatalf("first connection failed unexpectedly: %v", err1)
500+
}
501+
502+
// Second connection should be rejected (duplicate pending inbound)
503+
err2 := srv.checkpoint(c2, srv.checkpointPostHandshake)
504+
if err2 != DiscAlreadyConnected {
505+
t.Errorf("expected DiscAlreadyConnected for duplicate pending inbound, got: %v", err2)
506+
}
507+
508+
t.Logf("✅ First connection accepted, second rejected with: %v", err2)
509+
}
510+
511+
// TestServerPendingInboundCleanup checks that pending inbound state is properly
512+
// cleaned up when a connection fails or completes.
513+
func TestServerPendingInboundCleanup(t *testing.T) {
514+
// Track when peers connect
515+
connected := make(chan *Peer, 2)
516+
remid := &newkey().PublicKey
517+
518+
srv := startTestServer(t, remid, func(p *Peer) {
519+
connected <- p
520+
})
521+
defer close(connected)
522+
defer srv.Stop()
523+
524+
// First connection attempt - will succeed
525+
conn1, err := net.DialTimeout("tcp", srv.ListenAddr, 5*time.Second)
526+
if err != nil {
527+
t.Fatalf("could not dial: %v", err)
528+
}
529+
defer conn1.Close()
530+
531+
// Wait for peer to connect
532+
select {
533+
case peer := <-connected:
534+
t.Logf("First connection succeeded, peer: %s", peer.ID())
535+
536+
// Disconnect the peer to clean up pendingInbound
537+
peer.Disconnect(DiscRequested)
538+
time.Sleep(100 * time.Millisecond)
539+
540+
// Verify peer is gone
541+
if srv.PeerCount() != 0 {
542+
t.Errorf("expected 0 peers after disconnect, got %d", srv.PeerCount())
543+
}
544+
545+
case <-time.After(2 * time.Second):
546+
t.Fatal("first connection did not complete within timeout")
547+
}
548+
549+
// Second connection attempt from same peer - should succeed now
550+
// because pendingInbound was cleaned up
551+
conn2, err := net.DialTimeout("tcp", srv.ListenAddr, 5*time.Second)
552+
if err != nil {
553+
t.Fatalf("could not dial second time: %v", err)
554+
}
555+
defer conn2.Close()
556+
557+
select {
558+
case peer := <-connected:
559+
t.Logf("Second connection succeeded after cleanup, peer: %s", peer.ID())
560+
if peer.ID() != enode.PubkeyToIDV4(remid) {
561+
t.Errorf("peer has wrong id")
562+
}
563+
564+
case <-time.After(2 * time.Second):
565+
t.Error("second connection did not complete within timeout - pendingInbound may not have been cleaned up")
566+
}
567+
}
568+
466569
type setupTransport struct {
467570
pubkey *ecdsa.PublicKey
468571
encHandshakeErr error

0 commit comments

Comments
 (0)