Skip to content

Commit 25c3355

Browse files
Merge #6365: backport: merge bitcoin#22778, bitcoin#25156, bitcoin#26497, bitcoin#27213, bitcoin#28189, bitcoin#28155, bitcoin#28895, partial bitcoin#26396 (networking backports: part 9)
09504bd merge bitcoin#28895: do not make automatic outbound connections to addnode peers (Kittywhiskers Van Gogh) 6cf206c merge bitcoin#28155: improves addnode / m_added_nodes logic (Kittywhiskers Van Gogh) 11d654a merge bitcoin#28189: diversify network outbounds release note (Kittywhiskers Van Gogh) 5dc52b3 merge bitcoin#27213: Diversify automatic outbound connections with respect to networks (Kittywhiskers Van Gogh) 291305b merge bitcoin#26497: Make ConsumeNetAddr always produce valid onion addresses (Kittywhiskers Van Gogh) 6a37934 partial bitcoin#26396: Avoid SetTxRelay for feeler connections (Kittywhiskers Van Gogh) 221a78e merge bitcoin#25156: Introduce PeerManagerImpl::RejectIncomingTxs (Kittywhiskers Van Gogh) cc694c2 merge bitcoin#22778: Reduce resource usage for inbound block-relay-only connections (Kittywhiskers Van Gogh) 6e6de54 net: use `Peer::m_can_relay_tx` when we mean `m_tx_relay != nullptr` (Kittywhiskers Van Gogh) 77526d2 net: rename `Peer::m_block_relay_only` to `Peer::m_can_tx_relay` (Kittywhiskers Van Gogh) Pull request description: ## Additional Information * Dependency for #6333 * When backporting [bitcoin#22778](bitcoin#22778), the `m_tx_inventory_known_filter.insert()` call in `queueAndMaybePushInv()` had to be moved out as `EXCLUSIVE_LOCKS_REQUIRED` does not seem to work with lambda parameters list (though it does work with capture list, [source](https://github.com/dashpay/dash/blob/4b5e39290c8f1c78305e597312408c84e2b06b64/src/net_processing.cpp#L5781)), see error below <details> <summary>Compiler error:</summary> ``` net_processing.cpp:5895:21: note: found near match 'tx_relay->m_tx_inventory_mutex' net_processing.cpp:5955:21: error: calling function 'operator()' requires holding mutex 'queueAndMaybePushInv.m_tx_inventory_mutex' exclusively [-Werror,-Wthread-safety-precise] queueAndMaybePushInv(tx_relay, CInv(nInvType, hash)); ^ net_processing.cpp:5955:21: note: found near match 'tx_relay->m_tx_inventory_mutex' net_processing.cpp:5977:17: error: calling function 'operator()' requires holding mutex 'queueAndMaybePushInv.m_tx_inventory_mutex' exclusively [-Werror,-Wthread-safety-precise] queueAndMaybePushInv(inv_relay, inv); ^ ``` </details> * Attempting to remove the `EXCLUSIVE_LOCKS_REQUIRED` or the `AssertLockHeld` are not options due to stricter thread sanitization checks being applied since [dash#6319](#6319) (and in general, removing annotations being inadvisable regardless) * We cannot simply lock `peer->GetInvRelay()->m_tx_inventory_mutex` as a) the caller already has a copy of the relay pointer (which means that `m_tx_relay_mutex` was already held) that b) they used to lock `m_tx_inventory_mutex` (which we were already asserting) so c) we cannot simply re-lock `m_tx_inventory_mutex` as it's already already held, just through a less circuitous path. * The reason locking is mentioned instead of asserting is that the compiler treats `peer->GetInvRelay()->m_tx_inventory_mutex` and `tx_relay->m_tx_inventory_mutex` as separate locks (despite being different ways of accessing the same thing) and would complain similarly to the error above if attempting to assert the former while holding the latter. * As `m_tx_relay` is always initialized for Dash, to mimic the behaviour _expected_ upstream, in [bitcoin#22778](bitcoin#22778), `SetTxRelay()` will _allow_ `GetTxRelay()` to return `m_can_tx_relay` (while Dash code that demands unconditional access can use `GetInvRelay()` instead) with the lack of `SetTxRelay()` resulting in `GetTxRelay()` feigning the absence of `m_can_tx_relay`. This allows us to retain the same style of checks used upstream instead of using proxies like `!CNode::IsBlockOnlyConn()` to determined if we _should_ use `m_tx_relay`. * Speaking of proxies, being a block-only connection is now no longer the only reason not to relay transactions In preparation for [bitcoin#26396](bitcoin#26396), proxy usage of `!CNode::IsBlockOnlyConn()` and `!Peer::m_block_relay_only` was replaced with the more explicit `Peer::m_can_tx_relay` before being used to gate the result of `GetTxRelay()`, to help it mimic upstream semantics. ## Breaking Changes None expected ## Checklist - [x] I have performed a self-review of my own code - [x] I have commented my code, particularly in hard-to-understand areas **(note: N/A)** - [x] I have added or updated relevant unit/integration/functional/e2e tests - [x] I have made corresponding changes to the documentation - [x] I have assigned this pull request to a milestone _(for repository code-owners and collaborators only)_ ACKs for top commit: UdjinM6: utACK 09504bd Tree-SHA512: f4f36f12f749b697dd4ad5521ed15f862c93ed4492a047759554aa80a3ce00dbd1bdc0242f7a4468f41f25925d5b79c8ab774d8489317437b1983f0a1277eecb
2 parents 565f2db + 09504bd commit 25c3355

24 files changed

+655
-188
lines changed

doc/release-notes-6365.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
P2P and network changes
2+
------
3+
4+
- Nodes with multiple reachable networks will actively try to have at least one
5+
outbound connection to each network. This improves individual resistance to
6+
eclipse attacks and network level resistance to partition attacks. Users no
7+
longer need to perform active measures to ensure being connected to multiple
8+
enabled networks.

src/Makefile.test.include

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ BITCOIN_TESTS =\
134134
test/minisketch_tests.cpp \
135135
test/miner_tests.cpp \
136136
test/multisig_tests.cpp \
137+
test/net_peer_connection_tests.cpp \
137138
test/net_peer_eviction_tests.cpp \
138139
test/net_tests.cpp \
139140
test/netbase_tests.cpp \

src/Makefile.test_fuzz.include

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,16 @@ TEST_FUZZ_H = \
1111
test/fuzz/fuzz.h \
1212
test/fuzz/FuzzedDataProvider.h \
1313
test/fuzz/util.h \
14-
test/util/mining.h
14+
test/util/mining.h \
15+
test/fuzz/util/net.h
1516

1617
libtest_fuzz_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(MINIUPNPC_CPPFLAGS) $(EVENT_CFLAGS) $(EVENT_PTHREADS_CFLAGS)
1718
libtest_fuzz_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
1819
libtest_fuzz_a_SOURCES = \
1920
test/fuzz/fuzz.cpp \
2021
test/util/mining.cpp \
2122
test/fuzz/util.cpp \
23+
test/fuzz/util/net.cpp \
2224
$(TEST_FUZZ_H)
2325

2426
LIBTEST_FUZZ += $(LIBBITCOIN_SERVER)

src/net.cpp

Lines changed: 112 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,9 @@ static constexpr std::chrono::seconds MAX_UPLOAD_TIMEFRAME{60 * 60 * 24};
107107
// A random time period (0 to 1 seconds) is added to feeler connections to prevent synchronization.
108108
static constexpr auto FEELER_SLEEP_WINDOW{1s};
109109

110+
/** Frequency to attempt extra connections to reachable networks we're not connected to yet **/
111+
static constexpr auto EXTRA_NETWORK_PEER_INTERVAL{5min};
112+
110113
/** Used to pass flags to the Bind() function */
111114
enum BindFlags {
112115
BF_NONE = 0,
@@ -520,21 +523,25 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
520523
const uint16_t default_port{pszDest != nullptr ? Params().GetDefaultPort(pszDest) :
521524
Params().GetDefaultPort()};
522525
if (pszDest) {
523-
const std::vector<CService> resolved{Lookup(pszDest, default_port, fNameLookup && !HaveNameProxy(), 256)};
526+
std::vector<CService> resolved{Lookup(pszDest, default_port, fNameLookup && !HaveNameProxy(), 256)};
524527
if (!resolved.empty()) {
525-
const CService& rnd{resolved[GetRand(resolved.size())]};
526-
addrConnect = CAddress{MaybeFlipIPv6toCJDNS(rnd), NODE_NONE};
527-
if (!addrConnect.IsValid()) {
528-
LogPrint(BCLog::NET, "Resolver returned invalid address %s for %s\n", addrConnect.ToStringAddrPort(), pszDest);
529-
return nullptr;
530-
}
531-
// It is possible that we already have a connection to the IP/port pszDest resolved to.
532-
// In that case, drop the connection that was just created.
533-
LOCK(m_nodes_mutex);
534-
CNode* pnode = FindNode(static_cast<CService>(addrConnect));
535-
if (pnode) {
536-
LogPrintf("Failed to open new connection, already connected\n");
537-
return nullptr;
528+
Shuffle(resolved.begin(), resolved.end(), FastRandomContext());
529+
// If the connection is made by name, it can be the case that the name resolves to more than one address.
530+
// We don't want to connect any more of them if we are already connected to one
531+
for (const auto& r : resolved) {
532+
addrConnect = CAddress{MaybeFlipIPv6toCJDNS(r), NODE_NONE};
533+
if (!addrConnect.IsValid()) {
534+
LogPrint(BCLog::NET, "Resolver returned invalid address %s for %s\n", addrConnect.ToStringAddrPort(), pszDest);
535+
return nullptr;
536+
}
537+
// It is possible that we already have a connection to the IP/port pszDest resolved to.
538+
// In that case, drop the connection that was just created.
539+
LOCK(m_nodes_mutex);
540+
CNode* pnode = FindNode(static_cast<CService>(addrConnect));
541+
if (pnode) {
542+
LogPrintf("Not opening a connection to %s, already connected to %s\n", pszDest, addrConnect.ToStringAddrPort());
543+
return nullptr;
544+
}
538545
}
539546
}
540547
}
@@ -2252,6 +2259,9 @@ void CConnman::DisconnectNodes()
22522259
// close socket and cleanup
22532260
pnode->CloseSocketDisconnect(this);
22542261

2262+
// update connection count by network
2263+
if (pnode->IsManualOrFullOutboundConn()) --m_network_conn_counts[pnode->addr.GetNetwork()];
2264+
22552265
// hold in disconnected pool until all refs are released
22562266
pnode->Release();
22572267
m_nodes_disconnected.push_back(pnode);
@@ -3182,6 +3192,28 @@ std::unordered_set<Network> CConnman::GetReachableEmptyNetworks() const
31823192
return networks;
31833193
}
31843194

3195+
bool CConnman::MultipleManualOrFullOutboundConns(Network net) const
3196+
{
3197+
AssertLockHeld(m_nodes_mutex);
3198+
return m_network_conn_counts[net] > 1;
3199+
}
3200+
3201+
bool CConnman::MaybePickPreferredNetwork(std::optional<Network>& network)
3202+
{
3203+
std::array<Network, 5> nets{NET_IPV4, NET_IPV6, NET_ONION, NET_I2P, NET_CJDNS};
3204+
Shuffle(nets.begin(), nets.end(), FastRandomContext());
3205+
3206+
LOCK(m_nodes_mutex);
3207+
for (const auto net : nets) {
3208+
if (IsReachable(net) && m_network_conn_counts[net] == 0 && addrman.Size(net) != 0) {
3209+
network = net;
3210+
return true;
3211+
}
3212+
}
3213+
3214+
return false;
3215+
}
3216+
31853217
void CConnman::ThreadOpenConnections(const std::vector<std::string> connect, CDeterministicMNManager& dmnman)
31863218
{
31873219
AssertLockNotHeld(m_unused_i2p_sessions_mutex);
@@ -3217,6 +3249,7 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect, CDe
32173249
// Minimum time before next feeler connection (in microseconds).
32183250
auto next_feeler = GetExponentialRand(start, FEELER_INTERVAL);
32193251
auto next_extra_block_relay = GetExponentialRand(start, EXTRA_BLOCK_RELAY_ONLY_PEER_INTERVAL);
3252+
auto next_extra_network_peer{GetExponentialRand(start, EXTRA_NETWORK_PEER_INTERVAL)};
32203253
const bool dnsseed = gArgs.GetBoolArg("-dnsseed", DEFAULT_DNSSEED);
32213254
bool add_fixed_seeds = gArgs.GetBoolArg("-fixedseeds", DEFAULT_FIXEDSEEDS);
32223255
const bool use_seednodes{gArgs.IsArgSet("-seednode")};
@@ -3345,6 +3378,7 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect, CDe
33453378
auto now = GetTime<std::chrono::microseconds>();
33463379
bool anchor = false;
33473380
bool fFeeler = false;
3381+
std::optional<Network> preferred_net;
33483382
bool onion_only = false;
33493383

33503384
// Determine what type of connection to open. Opening
@@ -3395,6 +3429,17 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect, CDe
33953429
next_feeler = GetExponentialRand(now, FEELER_INTERVAL);
33963430
conn_type = ConnectionType::FEELER;
33973431
fFeeler = true;
3432+
} else if (nOutboundFullRelay == m_max_outbound_full_relay &&
3433+
m_max_outbound_full_relay == MAX_OUTBOUND_FULL_RELAY_CONNECTIONS &&
3434+
now > next_extra_network_peer &&
3435+
MaybePickPreferredNetwork(preferred_net)) {
3436+
// Full outbound connection management: Attempt to get at least one
3437+
// outbound peer from each reachable network by making extra connections
3438+
// and then protecting "only" peers from a network during outbound eviction.
3439+
// This is not attempted if the user changed -maxconnections to a value
3440+
// so low that less than MAX_OUTBOUND_FULL_RELAY_CONNECTIONS are made,
3441+
// to prevent interactions with otherwise protected outbound peers.
3442+
next_extra_network_peer = GetExponentialRand(now, EXTRA_NETWORK_PEER_INTERVAL);
33983443
} else if (nOutboundOnionRelay < m_max_outbound_onion && IsReachable(Network::NET_ONION)) {
33993444
onion_only = true;
34003445
} else {
@@ -3452,7 +3497,10 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect, CDe
34523497
}
34533498
} else {
34543499
// Not a feeler
3455-
std::tie(addr, addr_last_try) = addrman.Select();
3500+
// If preferred_net has a value set, pick an extra outbound
3501+
// peer from that network. The eviction logic in net_processing
3502+
// ensures that a peer from another network will be evicted.
3503+
std::tie(addr, addr_last_try) = addrman.Select(false, preferred_net);
34563504
}
34573505

34583506
auto dmn = mnList.GetMNByService(addr);
@@ -3503,6 +3551,17 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect, CDe
35033551
continue;
35043552
}
35053553

3554+
// Do not make automatic outbound connections to addnode peers, to
3555+
// not use our limited outbound slots for them and to ensure
3556+
// addnode connections benefit from their intended protections.
3557+
if (AddedNodesContain(addr)) {
3558+
LogPrint(BCLog::NET, "Not making automatic %s%s connection to %s peer selected for manual (addnode) connection%s\n",
3559+
preferred_net.has_value() ? "network-specific " : "",
3560+
ConnectionTypeAsString(conn_type), GetNetworkName(addr.GetNetwork()),
3561+
fLogIPs ? strprintf(": %s", addr.ToStringAddrPort()) : "");
3562+
continue;
3563+
}
3564+
35063565
addrConnect = addr;
35073566
break;
35083567
}
@@ -3519,6 +3578,9 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect, CDe
35193578
LogPrint(BCLog::NET, "Making feeler connection\n");
35203579
}
35213580
}
3581+
3582+
if (preferred_net != std::nullopt) LogPrint(BCLog::NET, "Making network specific connection to %s on %s.\n", addrConnect.ToStringAddrPort(), GetNetworkName(preferred_net.value()));
3583+
35223584
// Record addrman failure attempts when node has at least 2 persistent outbound connections to peers with
35233585
// different netgroups in ipv4/ipv6 networks + all peers in Tor/I2P/CJDNS networks.
35243586
// Don't record addrman failure attempts when node is offline. This can be identified since all local
@@ -3544,7 +3606,7 @@ std::vector<CAddress> CConnman::GetCurrentBlockRelayOnlyConns() const
35443606
return ret;
35453607
}
35463608

3547-
std::vector<AddedNodeInfo> CConnman::GetAddedNodeInfo() const
3609+
std::vector<AddedNodeInfo> CConnman::GetAddedNodeInfo(bool include_connected) const
35483610
{
35493611
std::vector<AddedNodeInfo> ret;
35503612

@@ -3579,6 +3641,9 @@ std::vector<AddedNodeInfo> CConnman::GetAddedNodeInfo() const
35793641
// strAddNode is an IP:port
35803642
auto it = mapConnected.find(service);
35813643
if (it != mapConnected.end()) {
3644+
if (!include_connected) {
3645+
continue;
3646+
}
35823647
addedNode.resolvedAddress = service;
35833648
addedNode.fConnected = true;
35843649
addedNode.fInbound = it->second;
@@ -3587,6 +3652,9 @@ std::vector<AddedNodeInfo> CConnman::GetAddedNodeInfo() const
35873652
// strAddNode is a name
35883653
auto it = mapConnectedByName.find(addr.m_added_node);
35893654
if (it != mapConnectedByName.end()) {
3655+
if (!include_connected) {
3656+
continue;
3657+
}
35903658
addedNode.resolvedAddress = it->second.second;
35913659
addedNode.fConnected = true;
35923660
addedNode.fInbound = it->second.first;
@@ -3605,21 +3673,19 @@ void CConnman::ThreadOpenAddedConnections()
36053673
while (true)
36063674
{
36073675
CSemaphoreGrant grant(*semAddnode);
3608-
std::vector<AddedNodeInfo> vInfo = GetAddedNodeInfo();
3676+
std::vector<AddedNodeInfo> vInfo = GetAddedNodeInfo(/*include_connected=*/false);
36093677
bool tried = false;
36103678
for (const AddedNodeInfo& info : vInfo) {
3611-
if (!info.fConnected) {
3612-
if (!grant) {
3613-
// If we've used up our semaphore and need a new one, let's not wait here since while we are waiting
3614-
// the addednodeinfo state might change.
3615-
break;
3616-
}
3617-
tried = true;
3618-
CAddress addr(CService(), NODE_NONE);
3619-
OpenNetworkConnection(addr, false, std::move(grant), info.m_params.m_added_node.c_str(), ConnectionType::MANUAL, info.m_params.m_use_v2transport);
3620-
if (!interruptNet.sleep_for(std::chrono::milliseconds(500))) return;
3621-
grant = CSemaphoreGrant(*semAddnode, /*fTry=*/true);
3679+
if (!grant) {
3680+
// If we've used up our semaphore and need a new one, let's not wait here since while we are waiting
3681+
// the addednodeinfo state might change.
3682+
break;
36223683
}
3684+
tried = true;
3685+
CAddress addr(CService(), NODE_NONE);
3686+
OpenNetworkConnection(addr, false, std::move(grant), info.m_params.m_added_node.c_str(), ConnectionType::MANUAL, info.m_params.m_use_v2transport);
3687+
if (!interruptNet.sleep_for(std::chrono::milliseconds(500))) return;
3688+
grant = CSemaphoreGrant(*semAddnode, /*fTry=*/true);
36233689
}
36243690
// See if any reconnections are desired.
36253691
PerformReconnections();
@@ -3881,6 +3947,9 @@ void CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFai
38813947
{
38823948
LOCK(m_nodes_mutex);
38833949
m_nodes.push_back(pnode);
3950+
3951+
// update connection count by network
3952+
if (pnode->IsManualOrFullOutboundConn()) ++m_network_conn_counts[pnode->addr.GetNetwork()];
38843953
}
38853954
{
38863955
if (m_edge_trig_events) {
@@ -4502,9 +4571,12 @@ std::vector<CAddress> CConnman::GetAddresses(CNode& requestor, size_t max_addres
45024571

45034572
bool CConnman::AddNode(const AddedNodeParams& add)
45044573
{
4574+
const CService resolved(LookupNumeric(add.m_added_node, Params().GetDefaultPort(add.m_added_node)));
4575+
const bool resolved_is_valid{resolved.IsValid()};
4576+
45054577
LOCK(m_added_nodes_mutex);
45064578
for (const auto& it : m_added_node_params) {
4507-
if (add.m_added_node == it.m_added_node) return false;
4579+
if (add.m_added_node == it.m_added_node || (resolved_is_valid && resolved == LookupNumeric(it.m_added_node, Params().GetDefaultPort(it.m_added_node)))) return false;
45084580
}
45094581

45104582
m_added_node_params.push_back(add);
@@ -4523,6 +4595,17 @@ bool CConnman::RemoveAddedNode(const std::string& strNode)
45234595
return false;
45244596
}
45254597

4598+
bool CConnman::AddedNodesContain(const CAddress& addr) const
4599+
{
4600+
AssertLockNotHeld(m_added_nodes_mutex);
4601+
const std::string addr_str{addr.ToStringAddr()};
4602+
const std::string addr_port_str{addr.ToStringAddrPort()};
4603+
LOCK(m_added_nodes_mutex);
4604+
return (m_added_node_params.size() < 24 // bound the query to a reasonable limit
4605+
&& std::any_of(m_added_node_params.cbegin(), m_added_node_params.cend(),
4606+
[&](const auto& p) { return p.m_added_node == addr_str || p.m_added_node == addr_port_str; }));
4607+
}
4608+
45264609
bool CConnman::AddPendingMasternode(const uint256& proTxHash)
45274610
{
45284611
LOCK(cs_vPendingMasternodes);

src/net.h

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -889,6 +889,22 @@ class CNode
889889
return m_conn_type == ConnectionType::MANUAL;
890890
}
891891

892+
bool IsManualOrFullOutboundConn() const
893+
{
894+
switch (m_conn_type) {
895+
case ConnectionType::INBOUND:
896+
case ConnectionType::FEELER:
897+
case ConnectionType::BLOCK_RELAY:
898+
case ConnectionType::ADDR_FETCH:
899+
return false;
900+
case ConnectionType::OUTBOUND_FULL_RELAY:
901+
case ConnectionType::MANUAL:
902+
return true;
903+
} // no default case, so the compiler can warn about missing cases
904+
905+
assert(false);
906+
}
907+
892908
bool IsBlockOnlyConn() const {
893909
return m_conn_type == ConnectionType::BLOCK_RELAY;
894910
}
@@ -929,10 +945,8 @@ class CNode
929945
/** Whether this peer provides all services that we want. Used for eviction decisions */
930946
std::atomic_bool m_has_all_wanted_services{false};
931947

932-
/** Whether we should relay transactions to this peer (their version
933-
* message did not include fRelay=false and this is not a block-relay-only
934-
* connection). This only changes from false to true. It will never change
935-
* back to false. Used only in inbound eviction logic. */
948+
/** Whether we should relay transactions to this peer. This only changes
949+
* from false to true. It will never change back to false. */
936950
std::atomic_bool m_relays_txs{false};
937951

938952
/** Whether this peer has loaded a bloom filter. Used only in inbound
@@ -1283,6 +1297,9 @@ friend class CNode;
12831297
EXCLUSIVE_LOCKS_REQUIRED(!m_unused_i2p_sessions_mutex, !mutexMsgProc);
12841298
bool CheckIncomingNonce(uint64_t nonce);
12851299

1300+
// alias for thread safety annotations only, not defined
1301+
RecursiveMutex& GetNodesMutex() const LOCK_RETURNED(m_nodes_mutex);
1302+
12861303
struct CFullyConnectedOnly {
12871304
bool operator() (const CNode* pnode) const {
12881305
return NodeFullyConnected(pnode);
@@ -1464,7 +1481,8 @@ friend class CNode;
14641481

14651482
bool AddNode(const AddedNodeParams& add) EXCLUSIVE_LOCKS_REQUIRED(!m_added_nodes_mutex);
14661483
bool RemoveAddedNode(const std::string& node) EXCLUSIVE_LOCKS_REQUIRED(!m_added_nodes_mutex);
1467-
std::vector<AddedNodeInfo> GetAddedNodeInfo() const EXCLUSIVE_LOCKS_REQUIRED(!m_added_nodes_mutex);
1484+
bool AddedNodesContain(const CAddress& addr) const EXCLUSIVE_LOCKS_REQUIRED(!m_added_nodes_mutex);
1485+
std::vector<AddedNodeInfo> GetAddedNodeInfo(bool include_connected) const EXCLUSIVE_LOCKS_REQUIRED(!m_added_nodes_mutex);
14681486

14691487
/**
14701488
* Attempts to open a connection. Currently only used from tests.
@@ -1536,6 +1554,8 @@ friend class CNode;
15361554
/** Return true if we should disconnect the peer for failing an inactivity check. */
15371555
bool ShouldRunInactivityChecks(const CNode& node, std::chrono::seconds now) const;
15381556

1557+
bool MultipleManualOrFullOutboundConns(Network net) const EXCLUSIVE_LOCKS_REQUIRED(m_nodes_mutex);
1558+
15391559
/**
15401560
* RAII helper to atomically create a copy of `m_nodes` and add a reference
15411561
* to each of the nodes. The nodes are released when this object is destroyed.
@@ -1736,6 +1756,18 @@ friend class CNode;
17361756
*/
17371757
std::vector<CAddress> GetCurrentBlockRelayOnlyConns() const;
17381758

1759+
/**
1760+
* Search for a "preferred" network, a reachable network to which we
1761+
* currently don't have any OUTBOUND_FULL_RELAY or MANUAL connections.
1762+
* There needs to be at least one address in AddrMan for a preferred
1763+
* network to be picked.
1764+
*
1765+
* @param[out] network Preferred network, if found.
1766+
*
1767+
* @return bool Whether a preferred network was found.
1768+
*/
1769+
bool MaybePickPreferredNetwork(std::optional<Network>& network);
1770+
17391771
// Whether the node should be passed out in ForEach* callbacks
17401772
static bool NodeFullyConnected(const CNode* pnode);
17411773

@@ -1778,6 +1810,9 @@ friend class CNode;
17781810
std::atomic<NodeId> nLastNodeId{0};
17791811
unsigned int nPrevNodeCount{0};
17801812

1813+
// Stores number of full-tx connections (outbound and manual) per network
1814+
std::array<unsigned int, Network::NET_MAX> m_network_conn_counts GUARDED_BY(m_nodes_mutex) = {};
1815+
17811816
std::vector<uint256> vPendingMasternodes;
17821817
mutable RecursiveMutex cs_vPendingMasternodes;
17831818
std::map<std::pair<Consensus::LLMQType, uint256>, std::set<uint256>> masternodeQuorumNodes GUARDED_BY(cs_vPendingMasternodes);

0 commit comments

Comments
 (0)