@@ -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+
466569type setupTransport struct {
467570 pubkey * ecdsa.PublicKey
468571 encHandshakeErr error
0 commit comments