@@ -1546,13 +1546,33 @@ void V2Transport::MarkBytesSent(size_t bytes_sent) noexcept
1546
1546
1547
1547
m_send_pos += bytes_sent;
1548
1548
Assume (m_send_pos <= m_send_buffer.size ());
1549
+ if (m_send_pos >= CMessageHeader::HEADER_SIZE) {
1550
+ m_sent_v1_header_worth = true ;
1551
+ }
1549
1552
// Wipe the buffer when everything is sent.
1550
1553
if (m_send_pos == m_send_buffer.size ()) {
1551
1554
m_send_pos = 0 ;
1552
1555
ClearShrink (m_send_buffer);
1553
1556
}
1554
1557
}
1555
1558
1559
+ bool V2Transport::ShouldReconnectV1 () const noexcept
1560
+ {
1561
+ AssertLockNotHeld (m_send_mutex);
1562
+ AssertLockNotHeld (m_recv_mutex);
1563
+ // Only outgoing connections need reconnection.
1564
+ if (!m_initiating) return false ;
1565
+
1566
+ LOCK (m_recv_mutex);
1567
+ // We only reconnect in the very first state and when the receive buffer is empty. Together
1568
+ // these conditions imply nothing has been received so far.
1569
+ if (m_recv_state != RecvState::KEY) return false ;
1570
+ if (!m_recv_buffer.empty ()) return false ;
1571
+ // Check if we've sent enough for the other side to disconnect us (if it was V1).
1572
+ LOCK (m_send_mutex);
1573
+ return m_sent_v1_header_worth;
1574
+ }
1575
+
1556
1576
size_t V2Transport::GetSendMemoryUsage () const noexcept
1557
1577
{
1558
1578
AssertLockNotHeld (m_send_mutex);
@@ -1868,6 +1888,13 @@ bool CConnman::AddConnection(const std::string& address, ConnectionType conn_typ
1868
1888
1869
1889
void CConnman::DisconnectNodes ()
1870
1890
{
1891
+ AssertLockNotHeld (m_nodes_mutex);
1892
+ AssertLockNotHeld (m_reconnections_mutex);
1893
+
1894
+ // Use a temporary variable to accumulate desired reconnections, so we don't need
1895
+ // m_reconnections_mutex while holding m_nodes_mutex.
1896
+ decltype (m_reconnections) reconnections_to_add;
1897
+
1871
1898
{
1872
1899
LOCK (m_nodes_mutex);
1873
1900
@@ -1890,6 +1917,19 @@ void CConnman::DisconnectNodes()
1890
1917
// remove from m_nodes
1891
1918
m_nodes.erase (remove (m_nodes.begin (), m_nodes.end (), pnode), m_nodes.end ());
1892
1919
1920
+ // Add to reconnection list if appropriate. We don't reconnect right here, because
1921
+ // the creation of a connection is a blocking operation (up to several seconds),
1922
+ // and we don't want to hold up the socket handler thread for that long.
1923
+ if (pnode->m_transport ->ShouldReconnectV1 ()) {
1924
+ reconnections_to_add.push_back ({
1925
+ .addr_connect = pnode->addr ,
1926
+ .grant = std::move (pnode->grantOutbound ),
1927
+ .destination = pnode->m_dest ,
1928
+ .conn_type = pnode->m_conn_type ,
1929
+ .use_v2transport = false });
1930
+ LogPrint (BCLog::NET, " retrying with v1 transport protocol for peer=%d\n " , pnode->GetId ());
1931
+ }
1932
+
1893
1933
// release outbound grant (if any)
1894
1934
pnode->grantOutbound .Release ();
1895
1935
@@ -1917,6 +1957,11 @@ void CConnman::DisconnectNodes()
1917
1957
}
1918
1958
}
1919
1959
}
1960
+ {
1961
+ // Move entries from reconnections_to_add to m_reconnections.
1962
+ LOCK (m_reconnections_mutex);
1963
+ m_reconnections.splice (m_reconnections.end (), std::move (reconnections_to_add));
1964
+ }
1920
1965
}
1921
1966
1922
1967
void CConnman::NotifyNumConnectionsChanged ()
@@ -2389,6 +2434,7 @@ bool CConnman::MaybePickPreferredNetwork(std::optional<Network>& network)
2389
2434
void CConnman::ThreadOpenConnections (const std::vector<std::string> connect)
2390
2435
{
2391
2436
AssertLockNotHeld (m_unused_i2p_sessions_mutex);
2437
+ AssertLockNotHeld (m_reconnections_mutex);
2392
2438
FastRandomContext rng;
2393
2439
// Connect to specific addresses
2394
2440
if (!connect.empty ())
@@ -2432,6 +2478,8 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
2432
2478
if (!interruptNet.sleep_for (std::chrono::milliseconds (500 )))
2433
2479
return ;
2434
2480
2481
+ PerformReconnections ();
2482
+
2435
2483
CSemaphoreGrant grant (*semOutbound);
2436
2484
if (interruptNet)
2437
2485
return ;
@@ -2778,6 +2826,7 @@ std::vector<AddedNodeInfo> CConnman::GetAddedNodeInfo() const
2778
2826
void CConnman::ThreadOpenAddedConnections ()
2779
2827
{
2780
2828
AssertLockNotHeld (m_unused_i2p_sessions_mutex);
2829
+ AssertLockNotHeld (m_reconnections_mutex);
2781
2830
while (true )
2782
2831
{
2783
2832
CSemaphoreGrant grant (*semAddnode);
@@ -2800,6 +2849,8 @@ void CConnman::ThreadOpenAddedConnections()
2800
2849
// Retry every 60 seconds if a connection was attempted, otherwise two seconds
2801
2850
if (!interruptNet.sleep_for (std::chrono::seconds (tried ? 60 : 2 )))
2802
2851
return ;
2852
+ // See if any reconnections are desired.
2853
+ PerformReconnections ();
2803
2854
}
2804
2855
}
2805
2856
@@ -3613,6 +3664,7 @@ CNode::CNode(NodeId idIn,
3613
3664
addr{addrIn},
3614
3665
addrBind{addrBindIn},
3615
3666
m_addr_name{addrNameIn.empty () ? addr.ToStringAddrPort () : addrNameIn},
3667
+ m_dest (addrNameIn),
3616
3668
m_inbound_onion{inbound_onion},
3617
3669
m_prefer_evict{node_opts.prefer_evict },
3618
3670
nKeyedNetGroup{nKeyedNetGroupIn},
@@ -3743,6 +3795,33 @@ uint64_t CConnman::CalculateKeyedNetGroup(const CAddress& address) const
3743
3795
return GetDeterministicRandomizer (RANDOMIZER_ID_NETGROUP).Write (vchNetGroup).Finalize ();
3744
3796
}
3745
3797
3798
+ void CConnman::PerformReconnections ()
3799
+ {
3800
+ AssertLockNotHeld (m_reconnections_mutex);
3801
+ AssertLockNotHeld (m_unused_i2p_sessions_mutex);
3802
+ while (true ) {
3803
+ // Move first element of m_reconnections to todo (avoiding an allocation inside the lock).
3804
+ decltype (m_reconnections) todo;
3805
+ {
3806
+ LOCK (m_reconnections_mutex);
3807
+ if (m_reconnections.empty ()) break ;
3808
+ todo.splice (todo.end (), m_reconnections, m_reconnections.begin ());
3809
+ }
3810
+
3811
+ auto & item = *todo.begin ();
3812
+ OpenNetworkConnection (item.addr_connect ,
3813
+ // We only reconnect if the first attempt to connect succeeded at
3814
+ // connection time, but then failed after the CNode object was
3815
+ // created. Since we already know connecting is possible, do not
3816
+ // count failure to reconnect.
3817
+ /* fCountFailure=*/ false ,
3818
+ std::move (item.grant ),
3819
+ item.destination .empty () ? nullptr : item.destination .c_str (),
3820
+ item.conn_type ,
3821
+ item.use_v2transport );
3822
+ }
3823
+ }
3824
+
3746
3825
// Dump binary message to file, with timestamp.
3747
3826
static void CaptureMessageToFile (const CAddress& addr,
3748
3827
const std::string& msg_type,
0 commit comments