Skip to content

Commit 06329eb

Browse files
committed
Merge bitcoin/bitcoin#29436: net: call Select with reachable networks in ThreadOpenConnections
e4e3b44 net: call `Select` with reachable networks in `ThreadOpenConnections` (brunoerg) 829becd addrman: change `Select` to support multiple networks (brunoerg) f698636 net: add `All()` in `ReachableNets` (brunoerg) Pull request description: This PR changes addrman's `Select` to support multiple networks and change `ThreadOpenConnections` to call it with reachable networks. It can avoid unnecessary `Select` calls and avoid exceeding the max number of tries (100), especially when turning a clearnet + Tor/I2P/CJDNS node to Tor/I2P/CJDNS. Compared to #29330, this approach is "less aggresive". It does not add a new init flag and does not impact address relay. I did an experiment of calling `Select` without passing a network until it finds an address from a network that compose 20% ~ 25% of the addrman (limited to 100 tries). ![Screenshot 2024-02-14 at 14 37 58](https://github.com/bitcoin/bitcoin/assets/19480819/7b6863a5-d7a6-40b6-87d5-01667c2de66a) ACKs for top commit: achow101: ACK e4e3b44 vasild: ACK e4e3b44 naumenkogs: ACK e4e3b44 Tree-SHA512: e8466b72b85bbc2ad8bfb14471eb27d2c50d4e84218f5ede2c15a6fa3653af61b488cde492dbd398f7502bd847e95bfee1abb7e01092daba2236d3ce3d6d2268
2 parents e983ed4 + e4e3b44 commit 06329eb

File tree

8 files changed

+72
-43
lines changed

8 files changed

+72
-43
lines changed

src/addrman.cpp

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -710,7 +710,7 @@ void AddrManImpl::Attempt_(const CService& addr, bool fCountFailure, NodeSeconds
710710
}
711711
}
712712

713-
std::pair<CAddress, NodeSeconds> AddrManImpl::Select_(bool new_only, std::optional<Network> network) const
713+
std::pair<CAddress, NodeSeconds> AddrManImpl::Select_(bool new_only, const std::unordered_set<Network>& networks) const
714714
{
715715
AssertLockHeld(cs);
716716

@@ -719,13 +719,18 @@ std::pair<CAddress, NodeSeconds> AddrManImpl::Select_(bool new_only, std::option
719719
size_t new_count = nNew;
720720
size_t tried_count = nTried;
721721

722-
if (network.has_value()) {
723-
auto it = m_network_counts.find(*network);
724-
if (it == m_network_counts.end()) return {};
725-
726-
auto counts = it->second;
727-
new_count = counts.n_new;
728-
tried_count = counts.n_tried;
722+
if (!networks.empty()) {
723+
new_count = 0;
724+
tried_count = 0;
725+
for (auto& network : networks) {
726+
auto it = m_network_counts.find(network);
727+
if (it == m_network_counts.end()) {
728+
continue;
729+
}
730+
auto counts = it->second;
731+
new_count += counts.n_new;
732+
tried_count += counts.n_tried;
733+
}
729734
}
730735

731736
if (new_only && new_count == 0) return {};
@@ -758,9 +763,9 @@ std::pair<CAddress, NodeSeconds> AddrManImpl::Select_(bool new_only, std::option
758763
position = (initial_position + i) % ADDRMAN_BUCKET_SIZE;
759764
node_id = GetEntry(search_tried, bucket, position);
760765
if (node_id != -1) {
761-
if (network.has_value()) {
766+
if (!networks.empty()) {
762767
const auto it{mapInfo.find(node_id)};
763-
if (Assume(it != mapInfo.end()) && it->second.GetNetwork() == *network) break;
768+
if (Assume(it != mapInfo.end()) && networks.contains(it->second.GetNetwork())) break;
764769
} else {
765770
break;
766771
}
@@ -1208,11 +1213,11 @@ std::pair<CAddress, NodeSeconds> AddrManImpl::SelectTriedCollision()
12081213
return ret;
12091214
}
12101215

1211-
std::pair<CAddress, NodeSeconds> AddrManImpl::Select(bool new_only, std::optional<Network> network) const
1216+
std::pair<CAddress, NodeSeconds> AddrManImpl::Select(bool new_only, const std::unordered_set<Network>& networks) const
12121217
{
12131218
LOCK(cs);
12141219
Check();
1215-
auto addrRet = Select_(new_only, network);
1220+
auto addrRet = Select_(new_only, networks);
12161221
Check();
12171222
return addrRet;
12181223
}
@@ -1315,9 +1320,9 @@ std::pair<CAddress, NodeSeconds> AddrMan::SelectTriedCollision()
13151320
return m_impl->SelectTriedCollision();
13161321
}
13171322

1318-
std::pair<CAddress, NodeSeconds> AddrMan::Select(bool new_only, std::optional<Network> network) const
1323+
std::pair<CAddress, NodeSeconds> AddrMan::Select(bool new_only, const std::unordered_set<Network>& networks) const
13191324
{
1320-
return m_impl->Select(new_only, network);
1325+
return m_impl->Select(new_only, networks);
13211326
}
13221327

13231328
std::vector<CAddress> AddrMan::GetAddr(size_t max_addresses, size_t max_pct, std::optional<Network> network, const bool filtered) const

src/addrman.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include <cstdint>
1616
#include <memory>
1717
#include <optional>
18+
#include <unordered_set>
1819
#include <utility>
1920
#include <vector>
2021

@@ -154,12 +155,12 @@ class AddrMan
154155
* an address from the new table or an empty pair. Passing `false` will return an
155156
* empty pair or an address from either the new or tried table (it does not
156157
* guarantee a tried entry).
157-
* @param[in] network Select only addresses of this network (nullopt = all). Passing a network may
158+
* @param[in] networks Select only addresses of these networks (empty = all). Passing networks may
158159
* slow down the search.
159160
* @return CAddress The record for the selected peer.
160161
* seconds The last time we attempted to connect to that peer.
161162
*/
162-
std::pair<CAddress, NodeSeconds> Select(bool new_only = false, std::optional<Network> network = std::nullopt) const;
163+
std::pair<CAddress, NodeSeconds> Select(bool new_only = false, const std::unordered_set<Network>& networks = {}) const;
163164

164165
/**
165166
* Return all or many randomly selected addresses, optionally by network.

src/addrman_impl.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ class AddrManImpl
125125

126126
std::pair<CAddress, NodeSeconds> SelectTriedCollision() EXCLUSIVE_LOCKS_REQUIRED(!cs);
127127

128-
std::pair<CAddress, NodeSeconds> Select(bool new_only, std::optional<Network> network) const
128+
std::pair<CAddress, NodeSeconds> Select(bool new_only, const std::unordered_set<Network>& networks) const
129129
EXCLUSIVE_LOCKS_REQUIRED(!cs);
130130

131131
std::vector<CAddress> GetAddr(size_t max_addresses, size_t max_pct, std::optional<Network> network, const bool filtered = true) const
@@ -252,7 +252,7 @@ class AddrManImpl
252252

253253
void Attempt_(const CService& addr, bool fCountFailure, NodeSeconds time) EXCLUSIVE_LOCKS_REQUIRED(cs);
254254

255-
std::pair<CAddress, NodeSeconds> Select_(bool new_only, std::optional<Network> network) const EXCLUSIVE_LOCKS_REQUIRED(cs);
255+
std::pair<CAddress, NodeSeconds> Select_(bool new_only, const std::unordered_set<Network>& networks) const EXCLUSIVE_LOCKS_REQUIRED(cs);
256256

257257
/** Helper to generalize looking up an addrman entry from either table.
258258
*

src/bench/addrman.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ static void AddrManSelectByNetwork(benchmark::Bench& bench)
133133
FillAddrMan(addrman);
134134

135135
bench.run([&] {
136-
(void)addrman.Select(/*new_only=*/false, NET_I2P);
136+
(void)addrman.Select(/*new_only=*/false, {NET_I2P});
137137
});
138138
}
139139

src/net.cpp

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2693,6 +2693,8 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect, Spa
26932693

26942694
const auto current_time{NodeClock::now()};
26952695
int nTries = 0;
2696+
const auto reachable_nets{g_reachable_nets.All()};
2697+
26962698
while (!interruptNet)
26972699
{
26982700
if (anchor && !m_anchors.empty()) {
@@ -2724,7 +2726,7 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect, Spa
27242726
if (!addr.IsValid()) {
27252727
// No tried table collisions. Select a new table address
27262728
// for our feeler.
2727-
std::tie(addr, addr_last_try) = addrman.Select(true);
2729+
std::tie(addr, addr_last_try) = addrman.Select(true, reachable_nets);
27282730
} else if (AlreadyConnectedToAddress(addr)) {
27292731
// If test-before-evict logic would have us connect to a
27302732
// peer that we're already connected to, just mark that
@@ -2733,14 +2735,16 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect, Spa
27332735
// a currently-connected peer.
27342736
addrman.Good(addr);
27352737
// Select a new table address for our feeler instead.
2736-
std::tie(addr, addr_last_try) = addrman.Select(true);
2738+
std::tie(addr, addr_last_try) = addrman.Select(true, reachable_nets);
27372739
}
27382740
} else {
27392741
// Not a feeler
27402742
// If preferred_net has a value set, pick an extra outbound
27412743
// peer from that network. The eviction logic in net_processing
27422744
// ensures that a peer from another network will be evicted.
2743-
std::tie(addr, addr_last_try) = addrman.Select(false, preferred_net);
2745+
std::tie(addr, addr_last_try) = preferred_net.has_value()
2746+
? addrman.Select(false, {*preferred_net})
2747+
: addrman.Select(false, reachable_nets);
27442748
}
27452749

27462750
// Require outbound IPv4/IPv6 connections, other than feelers, to be to distinct network groups

src/netbase.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,13 @@ class ReachableNets {
134134
return Contains(addr.GetNetwork());
135135
}
136136

137+
[[nodiscard]] std::unordered_set<Network> All() const EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
138+
{
139+
AssertLockNotHeld(m_mutex);
140+
LOCK(m_mutex);
141+
return m_reachable;
142+
}
143+
137144
private:
138145
mutable Mutex m_mutex;
139146

src/test/addrman_tests.cpp

Lines changed: 24 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -196,55 +196,59 @@ BOOST_AUTO_TEST_CASE(addrman_select)
196196
BOOST_AUTO_TEST_CASE(addrman_select_by_network)
197197
{
198198
auto addrman = std::make_unique<AddrMan>(EMPTY_NETGROUPMAN, DETERMINISTIC, GetCheckRatio(m_node));
199-
BOOST_CHECK(!addrman->Select(/*new_only=*/true, NET_IPV4).first.IsValid());
200-
BOOST_CHECK(!addrman->Select(/*new_only=*/false, NET_IPV4).first.IsValid());
199+
BOOST_CHECK(!addrman->Select(/*new_only=*/true, {NET_IPV4}).first.IsValid());
200+
BOOST_CHECK(!addrman->Select(/*new_only=*/false, {NET_IPV4}).first.IsValid());
201201

202202
// add ipv4 address to the new table
203203
CNetAddr source = ResolveIP("252.2.2.2");
204204
CService addr1 = ResolveService("250.1.1.1", 8333);
205205
BOOST_CHECK(addrman->Add({CAddress(addr1, NODE_NONE)}, source));
206206

207-
BOOST_CHECK(addrman->Select(/*new_only=*/true, NET_IPV4).first == addr1);
208-
BOOST_CHECK(addrman->Select(/*new_only=*/false, NET_IPV4).first == addr1);
209-
BOOST_CHECK(!addrman->Select(/*new_only=*/false, NET_IPV6).first.IsValid());
210-
BOOST_CHECK(!addrman->Select(/*new_only=*/false, NET_ONION).first.IsValid());
211-
BOOST_CHECK(!addrman->Select(/*new_only=*/false, NET_I2P).first.IsValid());
212-
BOOST_CHECK(!addrman->Select(/*new_only=*/false, NET_CJDNS).first.IsValid());
213-
BOOST_CHECK(!addrman->Select(/*new_only=*/true, NET_CJDNS).first.IsValid());
207+
BOOST_CHECK(addrman->Select(/*new_only=*/true, {NET_IPV4}).first == addr1);
208+
BOOST_CHECK(addrman->Select(/*new_only=*/false, {NET_IPV4}).first == addr1);
209+
BOOST_CHECK(!addrman->Select(/*new_only=*/false, {NET_IPV6}).first.IsValid());
210+
BOOST_CHECK(!addrman->Select(/*new_only=*/false, {NET_ONION}).first.IsValid());
211+
BOOST_CHECK(!addrman->Select(/*new_only=*/false, {NET_I2P}).first.IsValid());
212+
BOOST_CHECK(!addrman->Select(/*new_only=*/false, {NET_CJDNS}).first.IsValid());
213+
BOOST_CHECK(!addrman->Select(/*new_only=*/true, {NET_CJDNS}).first.IsValid());
214214
BOOST_CHECK(addrman->Select(/*new_only=*/false).first == addr1);
215215

216216
// add I2P address to the new table
217217
CAddress i2p_addr;
218218
i2p_addr.SetSpecial("udhdrtrcetjm5sxzskjyr5ztpeszydbh4dpl3pl4utgqqw2v4jna.b32.i2p");
219219
BOOST_CHECK(addrman->Add({i2p_addr}, source));
220220

221-
BOOST_CHECK(addrman->Select(/*new_only=*/true, NET_I2P).first == i2p_addr);
222-
BOOST_CHECK(addrman->Select(/*new_only=*/false, NET_I2P).first == i2p_addr);
223-
BOOST_CHECK(addrman->Select(/*new_only=*/false, NET_IPV4).first == addr1);
224-
BOOST_CHECK(!addrman->Select(/*new_only=*/false, NET_IPV6).first.IsValid());
225-
BOOST_CHECK(!addrman->Select(/*new_only=*/false, NET_ONION).first.IsValid());
226-
BOOST_CHECK(!addrman->Select(/*new_only=*/false, NET_CJDNS).first.IsValid());
221+
BOOST_CHECK(addrman->Select(/*new_only=*/true, {NET_I2P}).first == i2p_addr);
222+
BOOST_CHECK(addrman->Select(/*new_only=*/false, {NET_I2P}).first == i2p_addr);
223+
BOOST_CHECK(addrman->Select(/*new_only=*/false, {NET_IPV4}).first == addr1);
224+
std::unordered_set<Network> nets_with_entries = {NET_IPV4, NET_I2P};
225+
BOOST_CHECK(addrman->Select(/*new_only=*/false, nets_with_entries).first.IsValid());
226+
BOOST_CHECK(!addrman->Select(/*new_only=*/false, {NET_IPV6}).first.IsValid());
227+
BOOST_CHECK(!addrman->Select(/*new_only=*/false, {NET_ONION}).first.IsValid());
228+
BOOST_CHECK(!addrman->Select(/*new_only=*/false, {NET_CJDNS}).first.IsValid());
229+
std::unordered_set<Network> nets_without_entries = {NET_IPV6, NET_ONION, NET_CJDNS};
230+
BOOST_CHECK(!addrman->Select(/*new_only=*/false, nets_without_entries).first.IsValid());
227231

228232
// bump I2P address to tried table
229233
BOOST_CHECK(addrman->Good(i2p_addr));
230234

231-
BOOST_CHECK(!addrman->Select(/*new_only=*/true, NET_I2P).first.IsValid());
232-
BOOST_CHECK(addrman->Select(/*new_only=*/false, NET_I2P).first == i2p_addr);
235+
BOOST_CHECK(!addrman->Select(/*new_only=*/true, {NET_I2P}).first.IsValid());
236+
BOOST_CHECK(addrman->Select(/*new_only=*/false, {NET_I2P}).first == i2p_addr);
233237

234238
// add another I2P address to the new table
235239
CAddress i2p_addr2;
236240
i2p_addr2.SetSpecial("c4gfnttsuwqomiygupdqqqyy5y5emnk5c73hrfvatri67prd7vyq.b32.i2p");
237241
BOOST_CHECK(addrman->Add({i2p_addr2}, source));
238242

239-
BOOST_CHECK(addrman->Select(/*new_only=*/true, NET_I2P).first == i2p_addr2);
243+
BOOST_CHECK(addrman->Select(/*new_only=*/true, {NET_I2P}).first == i2p_addr2);
240244

241245
// ensure that both new and tried table are selected from
242246
bool new_selected{false};
243247
bool tried_selected{false};
244248
int counter = 256;
245249

246250
while (--counter > 0 && (!new_selected || !tried_selected)) {
247-
const CAddress selected{addrman->Select(/*new_only=*/false, NET_I2P).first};
251+
const CAddress selected{addrman->Select(/*new_only=*/false, {NET_I2P}).first};
248252
BOOST_REQUIRE(selected == i2p_addr || selected == i2p_addr2);
249253
if (selected == i2p_addr) {
250254
tried_selected = true;
@@ -277,7 +281,7 @@ BOOST_AUTO_TEST_CASE(addrman_select_special)
277281
// since the only ipv4 address is on the new table, ensure that the new
278282
// table gets selected even if new_only is false. if the table was being
279283
// selected at random, this test will sporadically fail
280-
BOOST_CHECK(addrman->Select(/*new_only=*/false, NET_IPV4).first == addr1);
284+
BOOST_CHECK(addrman->Select(/*new_only=*/false, {NET_IPV4}).first == addr1);
281285
}
282286

283287
BOOST_AUTO_TEST_CASE(addrman_new_collisions)

src/test/fuzz/addrman.cpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,15 @@ FUZZ_TARGET(addrman, .init = initialize_addrman)
285285
auto max_pct = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096);
286286
auto filtered = fuzzed_data_provider.ConsumeBool();
287287
(void)const_addr_man.GetAddr(max_addresses, max_pct, network, filtered);
288-
(void)const_addr_man.Select(fuzzed_data_provider.ConsumeBool(), network);
288+
289+
std::unordered_set<Network> nets;
290+
for (const auto& net : ALL_NETWORKS) {
291+
if (fuzzed_data_provider.ConsumeBool()) {
292+
nets.insert(net);
293+
}
294+
}
295+
(void)const_addr_man.Select(fuzzed_data_provider.ConsumeBool(), nets);
296+
289297
std::optional<bool> in_new;
290298
if (fuzzed_data_provider.ConsumeBool()) {
291299
in_new = fuzzed_data_provider.ConsumeBool();

0 commit comments

Comments
 (0)