Fixes #27843.
CreateNodeFromAcceptedSocket() enforces the inbound connection limit by
calling AttemptToEvictConnection() when the inbound slots are full. The
selected peer was previously only marked fDisconnect, leaving socket
closure and removal from m_nodes to ThreadSocketHandler().
That works for TCP accepts, which run in the socket handler loop, but I2P
accepts run in ThreadI2PAcceptIncoming(). During an I2P inbound burst,
the accept thread can add replacement peers before the socket handler
drains the evicted ones, temporarily exceeding -maxconnections.
This change removes an evicted inbound peer from m_nodes, closes its
socket, and queues it for deferred deletion immediately from
AttemptToEvictConnection(). m_nodes_disconnected is now guarded by
m_nodes_mutex, since the eviction path can append to it outside the
socket handler thread.
A regression test fills the inbound slots, accepts one more inbound
connection, and checks that the replacement does not grow m_nodes.
Test
cmake --build build --target test_bitcoin
build/bin/test_bitcoin --run_test=net_peer_connection_tests/inbound_eviction_removes_node_immediately
build/bin/test_bitcoin --run_test=net_peer_connection_tests
build/bin/test_bitcoin --run_test=net_peer_eviction_tests
build/bin/test_bitcoin --run_test=i2p_tests