Branch data Line data Source code
1 : : // Copyright (c) 2012 Pieter Wuille
2 : : // Copyright (c) 2012-present The Bitcoin Core developers
3 : : // Distributed under the MIT software license, see the accompanying
4 : : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
5 : :
6 : : #include <bitcoin-build-config.h> // IWYU pragma: keep
7 : :
8 : : #include <addrman.h>
9 : : #include <addrman_impl.h>
10 : :
11 : : #include <hash.h>
12 : : #include <logging.h>
13 : : #include <logging/timer.h>
14 : : #include <netaddress.h>
15 : : #include <protocol.h>
16 : : #include <random.h>
17 : : #include <serialize.h>
18 : : #include <streams.h>
19 : : #include <tinyformat.h>
20 : : #include <uint256.h>
21 : : #include <util/check.h>
22 : : #include <util/time.h>
23 : :
24 : : #include <cmath>
25 : : #include <optional>
26 : :
27 : :
28 : 10289569 : int AddrInfo::GetTriedBucket(const uint256& nKey, const NetGroupManager& netgroupman) const
29 : : {
30 [ + - + - ]: 10289569 : uint64_t hash1 = (HashWriter{} << nKey << GetKey()).GetCheapHash();
31 [ + - + - : 10289569 : uint64_t hash2 = (HashWriter{} << nKey << netgroupman.GetGroup(*this) << (hash1 % ADDRMAN_TRIED_BUCKETS_PER_GROUP)).GetCheapHash();
+ - ]
32 : 10289569 : return hash2 % ADDRMAN_TRIED_BUCKET_COUNT;
33 : : }
34 : :
35 : 10409513 : int AddrInfo::GetNewBucket(const uint256& nKey, const CNetAddr& src, const NetGroupManager& netgroupman) const
36 : : {
37 : 10409513 : std::vector<unsigned char> vchSourceGroupKey = netgroupman.GetGroup(src);
38 [ + - + - : 20819026 : uint64_t hash1 = (HashWriter{} << nKey << netgroupman.GetGroup(*this) << vchSourceGroupKey).GetCheapHash();
+ - + - +
- ]
39 [ + - + - : 10409513 : uint64_t hash2 = (HashWriter{} << nKey << vchSourceGroupKey << (hash1 % ADDRMAN_NEW_BUCKETS_PER_SOURCE_GROUP)).GetCheapHash();
+ - + - +
- ]
40 : 10409513 : return hash2 % ADDRMAN_NEW_BUCKET_COUNT;
41 : 10409513 : }
42 : :
43 : 29130970 : int AddrInfo::GetBucketPosition(const uint256& nKey, bool fNew, int bucket) const
44 : : {
45 [ + + + - : 39420539 : uint64_t hash1 = (HashWriter{} << nKey << (fNew ? uint8_t{'N'} : uint8_t{'K'}) << bucket << GetKey()).GetCheapHash();
+ - ]
46 : 29130970 : return hash1 % ADDRMAN_BUCKET_SIZE;
47 : : }
48 : :
49 : 2464883 : bool AddrInfo::IsTerrible(NodeSeconds now) const
50 : : {
51 [ + + ]: 2464883 : if (now - m_last_try <= 1min) { // never remove things tried in the last minute
52 : : return false;
53 : : }
54 : :
55 [ + + ]: 1921804 : if (nTime > now + 10min) { // came in a flying DeLorean
56 : : return true;
57 : : }
58 : :
59 [ + + ]: 1055782 : if (now - nTime > ADDRMAN_HORIZON) { // not seen in recent history
60 : : return true;
61 : : }
62 : :
63 [ + + + + ]: 28189 : if (TicksSinceEpoch<std::chrono::seconds>(m_last_success) == 0 && nAttempts >= ADDRMAN_RETRIES) { // tried N times and never a success
64 : : return true;
65 : : }
66 : :
67 [ + + + + ]: 28161 : if (now - m_last_success > ADDRMAN_MIN_FAIL && nAttempts >= ADDRMAN_MAX_FAILURES) { // N successive failures in the last week
68 : 153 : return true;
69 : : }
70 : :
71 : : return false;
72 : : }
73 : :
74 : 3482 : double AddrInfo::GetChance(NodeSeconds now) const
75 : : {
76 : 3482 : double fChance = 1.0;
77 : :
78 : : // deprioritize very recent attempts away
79 [ + + ]: 3482 : if (now - m_last_try < 10min) {
80 : 1877 : fChance *= 0.01;
81 : : }
82 : :
83 : : // deprioritize 66% after each failed attempt, but at most 1/28th to avoid the search taking forever or overly penalizing outages.
84 [ + + ]: 3482 : fChance *= pow(0.66, std::min(nAttempts, 8));
85 : :
86 : 3482 : return fChance;
87 : : }
88 : :
89 : 11524 : AddrManImpl::AddrManImpl(const NetGroupManager& netgroupman, bool deterministic, int32_t consistency_check_ratio)
90 : 11524 : : insecure_rand{deterministic}
91 [ + + ]: 11524 : , nKey{deterministic ? uint256{1} : insecure_rand.rand256()}
92 : 11524 : , m_consistency_check_ratio{consistency_check_ratio}
93 : 34572 : , m_netgroupman{netgroupman}
94 : : {
95 [ + + ]: 11812100 : for (auto& bucket : vvNew) {
96 [ + + ]: 767037440 : for (auto& entry : bucket) {
97 : 755236864 : entry = -1;
98 : : }
99 : : }
100 [ + + ]: 2961668 : for (auto& bucket : vvTried) {
101 [ + + ]: 191759360 : for (auto& entry : bucket) {
102 : 188809216 : entry = -1;
103 : : }
104 : : }
105 : 11524 : }
106 : :
107 : 11524 : AddrManImpl::~AddrManImpl()
108 : : {
109 : 11524 : nKey.SetNull();
110 : 11524 : }
111 : :
112 : : template <typename Stream>
113 : 2158 : void AddrManImpl::Serialize(Stream& s_) const
114 : : {
115 [ + - ]: 2158 : LOCK(cs);
116 : :
117 : : /**
118 : : * Serialized format.
119 : : * * format version byte (@see `Format`)
120 : : * * lowest compatible format version byte. This is used to help old software decide
121 : : * whether to parse the file. For example:
122 : : * * Bitcoin Core version N knows how to parse up to format=3. If a new format=4 is
123 : : * introduced in version N+1 that is compatible with format=3 and it is known that
124 : : * version N will be able to parse it, then version N+1 will write
125 : : * (format=4, lowest_compatible=3) in the first two bytes of the file, and so
126 : : * version N will still try to parse it.
127 : : * * Bitcoin Core version N+2 introduces a new incompatible format=5. It will write
128 : : * (format=5, lowest_compatible=5) and so any versions that do not know how to parse
129 : : * format=5 will not try to read the file.
130 : : * * nKey
131 : : * * nNew
132 : : * * nTried
133 : : * * number of "new" buckets XOR 2**30
134 : : * * all new addresses (total count: nNew)
135 : : * * all tried addresses (total count: nTried)
136 : : * * for each new bucket:
137 : : * * number of elements
138 : : * * for each element: index in the serialized "all new addresses"
139 : : * * asmap version
140 : : *
141 : : * 2**30 is xorred with the number of buckets to make addrman deserializer v0 detect it
142 : : * as incompatible. This is necessary because it did not check the version number on
143 : : * deserialization.
144 : : *
145 : : * vvNew, vvTried, mapInfo, mapAddr and vRandom are never encoded explicitly;
146 : : * they are instead reconstructed from the other information.
147 : : *
148 : : * This format is more complex, but significantly smaller (at most 1.5 MiB), and supports
149 : : * changes to the ADDRMAN_ parameters without breaking the on-disk structure.
150 : : *
151 : : * We don't use SERIALIZE_METHODS since the serialization and deserialization code has
152 : : * very little in common.
153 : : */
154 : :
155 : : // Always serialize in the latest version (FILE_FORMAT).
156 : 2158 : ParamsStream s{s_, CAddress::V2_DISK};
157 : :
158 [ + - ]: 2158 : s << static_cast<uint8_t>(FILE_FORMAT);
159 : :
160 : : // Increment `lowest_compatible` iff a newly introduced format is incompatible with
161 : : // the previous one.
162 : : static constexpr uint8_t lowest_compatible = Format::V4_MULTIPORT;
163 [ + - ]: 2158 : s << static_cast<uint8_t>(INCOMPATIBILITY_BASE + lowest_compatible);
164 : :
165 [ + - ]: 2158 : s << nKey;
166 [ + - ]: 2158 : s << nNew;
167 [ + - ]: 2158 : s << nTried;
168 : :
169 [ + - ]: 2158 : int nUBuckets = ADDRMAN_NEW_BUCKET_COUNT ^ (1 << 30);
170 : 2158 : s << nUBuckets;
171 : 2158 : std::unordered_map<nid_type, int> mapUnkIds;
172 : 2158 : int nIds = 0;
173 [ + + + - ]: 5547935 : for (const auto& entry : mapInfo) {
174 [ + - ]: 5545777 : mapUnkIds[entry.first] = nIds;
175 : 5545777 : const AddrInfo& info = entry.second;
176 [ + + ]: 5545777 : if (info.nRefCount) {
177 [ - + ]: 3198567 : assert(nIds != nNew); // this means nNew was wrong, oh ow
178 : 3198567 : s << info;
179 : 3198567 : nIds++;
180 : : }
181 : : }
182 : 2158 : nIds = 0;
183 [ + + + + ]: 5547935 : for (const auto& entry : mapInfo) {
184 : 5545777 : const AddrInfo& info = entry.second;
185 [ + + ]: 5545777 : if (info.fInTried) {
186 [ - + ]: 2347210 : assert(nIds != nTried); // this means nTried was wrong, oh ow
187 : 2347210 : s << info;
188 : 2347210 : nIds++;
189 : : }
190 : : }
191 [ + + ]: 2211950 : for (int bucket = 0; bucket < ADDRMAN_NEW_BUCKET_COUNT; bucket++) {
192 : : int nSize = 0;
193 [ + + ]: 143636480 : for (int i = 0; i < ADDRMAN_BUCKET_SIZE; i++) {
194 [ + + ]: 141426688 : if (vvNew[bucket][i] != -1)
195 : 3324695 : nSize++;
196 : : }
197 : 143636480 : s << nSize;
198 [ + + ]: 143636480 : for (int i = 0; i < ADDRMAN_BUCKET_SIZE; i++) {
199 [ + + ]: 141426688 : if (vvNew[bucket][i] != -1) {
200 [ + - + - ]: 3324695 : int nIndex = mapUnkIds[vvNew[bucket][i]];
201 : 141426688 : s << nIndex;
202 : : }
203 : : }
204 : : }
205 : : // Store asmap version after bucket entries so that it
206 : : // can be ignored by older clients for backward compatibility.
207 [ + - ]: 4316 : s << m_netgroupman.GetAsmapVersion();
208 [ + - ]: 4316 : }
209 : :
210 : : template <typename Stream>
211 : 3591 : void AddrManImpl::Unserialize(Stream& s_)
212 : : {
213 : 3591 : LOCK(cs);
214 : :
215 [ - + ]: 3591 : assert(vRandom.empty());
216 : :
217 : : Format format;
218 [ + + ]: 3591 : s_ >> Using<CustomUintFormatter<1>>(format);
219 : :
220 [ + + + + ]: 3562 : const auto ser_params = (format >= Format::V3_BIP155 ? CAddress::V2_DISK : CAddress::V1_DISK);
221 [ + + ]: 3562 : ParamsStream s{s_, ser_params};
222 : :
223 : : uint8_t compat;
224 : 3508 : s >> compat;
225 [ + + ]: 3508 : if (compat < INCOMPATIBILITY_BASE) {
226 [ + - + - ]: 292 : throw std::ios_base::failure(strprintf(
227 : : "Corrupted addrman database: The compat value (%u) "
228 : : "is lower than the expected minimum value %u.",
229 : : compat, INCOMPATIBILITY_BASE));
230 : : }
231 : 3362 : const uint8_t lowest_compatible = compat - INCOMPATIBILITY_BASE;
232 [ + + ]: 3362 : if (lowest_compatible > FILE_FORMAT) {
233 : 454 : throw InvalidAddrManVersionError(strprintf(
234 : : "Unsupported format of addrman database: %u. It is compatible with formats >=%u, "
235 : : "but the maximum supported by this version of %s is %u.",
236 [ + - ]: 227 : uint8_t{format}, lowest_compatible, CLIENT_NAME, uint8_t{FILE_FORMAT}));
237 : : }
238 : :
239 [ + + ]: 3135 : s >> nKey;
240 [ + + ]: 3124 : s >> nNew;
241 [ + + ]: 3122 : s >> nTried;
242 [ + + ]: 3121 : int nUBuckets = 0;
243 : 3118 : s >> nUBuckets;
244 [ + + ]: 3118 : if (format >= Format::V1_DETERMINISTIC) {
245 : 2001 : nUBuckets ^= (1 << 30);
246 : : }
247 : :
248 [ + + ]: 3118 : if (nNew > ADDRMAN_NEW_BUCKET_COUNT * ADDRMAN_BUCKET_SIZE || nNew < 0) {
249 [ + - ]: 44 : throw std::ios_base::failure(
250 : 22 : strprintf("Corrupt AddrMan serialization: nNew=%d, should be in [0, %d]",
251 : : nNew,
252 [ + - ]: 22 : ADDRMAN_NEW_BUCKET_COUNT * ADDRMAN_BUCKET_SIZE));
253 : : }
254 : :
255 [ + + ]: 3096 : if (nTried > ADDRMAN_TRIED_BUCKET_COUNT * ADDRMAN_BUCKET_SIZE || nTried < 0) {
256 [ + - ]: 46 : throw std::ios_base::failure(
257 : 23 : strprintf("Corrupt AddrMan serialization: nTried=%d, should be in [0, %d]",
258 : : nTried,
259 [ + - ]: 23 : ADDRMAN_TRIED_BUCKET_COUNT * ADDRMAN_BUCKET_SIZE));
260 : : }
261 : :
262 : : // Deserialize entries from the new table.
263 [ + + ]: 2840603 : for (int n = 0; n < nNew; n++) {
264 [ + - + + ]: 2838136 : AddrInfo& info = mapInfo[n];
265 : 2837530 : s >> info;
266 [ + - ]: 2837530 : mapAddr[info] = n;
267 [ - + ]: 2837530 : info.nRandomPos = vRandom.size();
268 [ + - ]: 2837530 : vRandom.push_back(n);
269 [ + - + - ]: 2837530 : m_network_counts[info.GetNetwork()].n_new++;
270 : : }
271 : 2467 : nIdCount = nNew;
272 : :
273 : : // Deserialize entries from the tried table.
274 : 2467 : int nLost = 0;
275 [ + + ]: 2368501 : for (int n = 0; n < nTried; n++) {
276 [ + - ]: 2366034 : AddrInfo info;
277 : 2365685 : s >> info;
278 [ + - ]: 2365685 : int nKBucket = info.GetTriedBucket(nKey, m_netgroupman);
279 [ + - ]: 2365685 : int nKBucketPos = info.GetBucketPosition(nKey, false, nKBucket);
280 [ + - ]: 2365685 : if (info.IsValid()
281 [ + + + + ]: 2365685 : && vvTried[nKBucket][nKBucketPos] == -1) {
282 [ - + ]: 2331303 : info.nRandomPos = vRandom.size();
283 : 2331303 : info.fInTried = true;
284 [ + - ]: 2331303 : vRandom.push_back(nIdCount);
285 [ + - ]: 2331303 : mapInfo[nIdCount] = info;
286 [ + - ]: 2331303 : mapAddr[info] = nIdCount;
287 : 2331303 : vvTried[nKBucket][nKBucketPos] = nIdCount;
288 : 2331303 : nIdCount++;
289 [ + - + - ]: 2331303 : m_network_counts[info.GetNetwork()].n_tried++;
290 : : } else {
291 : 34382 : nLost++;
292 : : }
293 : : }
294 : 2118 : nTried -= nLost;
295 : :
296 : : // Store positions in the new table buckets to apply later (if possible).
297 : : // An entry may appear in up to ADDRMAN_NEW_BUCKETS_PER_ADDRESS buckets,
298 : : // so we store all bucket-entry_index pairs to iterate through later.
299 : 2118 : std::vector<std::pair<int, int>> bucket_entries;
300 : :
301 [ + + ]: 986116 : for (int bucket = 0; bucket < nUBuckets; ++bucket) {
302 [ + + ]: 984057 : int num_entries{0};
303 : 4425085 : s >> num_entries;
304 [ + + ]: 4425085 : for (int n = 0; n < num_entries; ++n) {
305 [ + + ]: 3441087 : int entry_index{0};
306 : 3441052 : s >> entry_index;
307 [ + + + + ]: 3441052 : if (entry_index >= 0 && entry_index < nNew) {
308 [ + - ]: 3408420 : bucket_entries.emplace_back(bucket, entry_index);
309 : : }
310 : : }
311 : : }
312 : :
313 : : // If the bucket count and asmap version haven't changed, then attempt
314 : : // to restore the entries to the buckets/positions they were in before
315 : : // serialization.
316 [ + - ]: 2059 : uint256 supplied_asmap_version{m_netgroupman.GetAsmapVersion()};
317 : 2059 : uint256 serialized_asmap_version;
318 [ + + ]: 2059 : if (format >= Format::V2_ASMAP) {
319 : 2048 : s >> serialized_asmap_version;
320 : : }
321 [ + + ]: 2048 : const bool restore_bucketing{nUBuckets == ADDRMAN_NEW_BUCKET_COUNT &&
322 [ + + ]: 946 : serialized_asmap_version == supplied_asmap_version};
323 : :
324 : : if (!restore_bucketing) {
325 [ + - - + : 1105 : LogDebug(BCLog::ADDRMAN, "Bucketing method was updated, re-bucketing addrman entries from disk\n");
- - ]
326 : : }
327 : :
328 [ + + ]: 2983183 : for (auto bucket_entry : bucket_entries) {
329 : 2981135 : int bucket{bucket_entry.first};
330 : 2981135 : const int entry_index{bucket_entry.second};
331 [ + - ]: 2981135 : AddrInfo& info = mapInfo[entry_index];
332 : :
333 : : // Don't store the entry in the new bucket if it's not a valid address for our addrman
334 [ + - + + ]: 2981135 : if (!info.IsValid()) continue;
335 : :
336 : : // The entry shouldn't appear in more than
337 : : // ADDRMAN_NEW_BUCKETS_PER_ADDRESS. If it has already, just skip
338 : : // this bucket_entry.
339 [ + + ]: 2973525 : if (info.nRefCount >= ADDRMAN_NEW_BUCKETS_PER_ADDRESS) continue;
340 : :
341 [ + - ]: 2935385 : int bucket_position = info.GetBucketPosition(nKey, true, bucket);
342 [ + + + + ]: 2935385 : if (restore_bucketing && vvNew[bucket][bucket_position] == -1) {
343 : : // Bucketing has not changed, using existing bucket positions for the new table
344 : 2797161 : vvNew[bucket][bucket_position] = entry_index;
345 : 2797161 : ++info.nRefCount;
346 : : } else {
347 : : // In case the new table data cannot be used (bucket count wrong or new asmap),
348 : : // try to give them a reference based on their primary source address.
349 [ + - ]: 138224 : bucket = info.GetNewBucket(nKey, m_netgroupman);
350 [ + - ]: 138224 : bucket_position = info.GetBucketPosition(nKey, true, bucket);
351 [ + + ]: 138224 : if (vvNew[bucket][bucket_position] == -1) {
352 : 1972 : vvNew[bucket][bucket_position] = entry_index;
353 : 1972 : ++info.nRefCount;
354 : : }
355 : : }
356 : : }
357 : :
358 : : // Prune new entries with refcount 0 (as a result of collisions or invalid address).
359 : 2048 : int nLostUnk = 0;
360 [ + + + + ]: 5095631 : for (auto it = mapInfo.cbegin(); it != mapInfo.cend(); ) {
361 [ + + + + ]: 5093583 : if (it->second.fInTried == false && it->second.nRefCount == 0) {
362 [ + - ]: 73122 : const auto itCopy = it++;
363 [ + - ]: 73122 : Delete(itCopy->first);
364 : 73122 : ++nLostUnk;
365 : : } else {
366 : : ++it;
367 : : }
368 : : }
369 [ + + ]: 2048 : if (nLost + nLostUnk > 0) {
370 [ + - - + : 534 : LogDebug(BCLog::ADDRMAN, "addrman lost %i new and %i tried addresses due to collisions or invalid addresses\n", nLostUnk, nLost);
- - ]
371 : : }
372 : :
373 [ + - ]: 2048 : const int check_code{CheckAddrman()};
374 [ + + ]: 2048 : if (check_code != 0) {
375 [ + - + - ]: 372 : throw std::ios_base::failure(strprintf(
376 : : "Corrupt data. Consistency check failed with code %s",
377 : : check_code));
378 : : }
379 [ + - ]: 3980 : }
380 : :
381 : 12992570 : AddrInfo* AddrManImpl::Find(const CService& addr, nid_type* pnId)
382 : : {
383 : 12992570 : AssertLockHeld(cs);
384 : :
385 : 12992570 : const auto it = mapAddr.find(addr);
386 [ + + ]: 12992570 : if (it == mapAddr.end())
387 : : return nullptr;
388 [ + + ]: 4861395 : if (pnId)
389 : 4849179 : *pnId = (*it).second;
390 : 4861395 : const auto it2 = mapInfo.find((*it).second);
391 [ + - ]: 4861395 : if (it2 != mapInfo.end())
392 : 4861395 : return &(*it2).second;
393 : : return nullptr;
394 : : }
395 : :
396 : 7571969 : AddrInfo* AddrManImpl::Create(const CAddress& addr, const CNetAddr& addrSource, nid_type* pnId)
397 : : {
398 : 7571969 : AssertLockHeld(cs);
399 : :
400 : 7571969 : nid_type nId = nIdCount++;
401 [ + - ]: 7571969 : mapInfo[nId] = AddrInfo(addr, addrSource);
402 : 7571969 : mapAddr[addr] = nId;
403 [ - + ]: 7571969 : mapInfo[nId].nRandomPos = vRandom.size();
404 : 7571969 : vRandom.push_back(nId);
405 : 7571969 : nNew++;
406 : 7571969 : m_network_counts[addr.GetNetwork()].n_new++;
407 [ + - ]: 7571969 : if (pnId)
408 : 7571969 : *pnId = nId;
409 : 7571969 : return &mapInfo[nId];
410 : : }
411 : :
412 : 2583612 : void AddrManImpl::SwapRandom(unsigned int nRndPos1, unsigned int nRndPos2) const
413 : : {
414 : 2583612 : AssertLockHeld(cs);
415 : :
416 [ + + ]: 2583612 : if (nRndPos1 == nRndPos2)
417 : : return;
418 : :
419 [ - + + - : 2004738 : assert(nRndPos1 < vRandom.size() && nRndPos2 < vRandom.size());
- + ]
420 : :
421 : 2004738 : nid_type nId1 = vRandom[nRndPos1];
422 : 2004738 : nid_type nId2 = vRandom[nRndPos2];
423 : :
424 : 2004738 : const auto it_1{mapInfo.find(nId1)};
425 : 2004738 : const auto it_2{mapInfo.find(nId2)};
426 [ - + ]: 2004738 : assert(it_1 != mapInfo.end());
427 [ + - ]: 2004738 : assert(it_2 != mapInfo.end());
428 : :
429 : 2004738 : it_1->second.nRandomPos = nRndPos2;
430 : 2004738 : it_2->second.nRandomPos = nRndPos1;
431 : :
432 : 2004738 : vRandom[nRndPos1] = nId2;
433 : 2004738 : vRandom[nRndPos2] = nId1;
434 : : }
435 : :
436 : 2041063 : void AddrManImpl::Delete(nid_type nId)
437 : : {
438 : 2041063 : AssertLockHeld(cs);
439 : :
440 [ - + ]: 2041063 : assert(mapInfo.contains(nId));
441 : 2041063 : AddrInfo& info = mapInfo[nId];
442 [ - + ]: 2041063 : assert(!info.fInTried);
443 [ - + ]: 2041063 : assert(info.nRefCount == 0);
444 : :
445 [ - + ]: 2041063 : SwapRandom(info.nRandomPos, vRandom.size() - 1);
446 : 2041063 : m_network_counts[info.GetNetwork()].n_new--;
447 : 2041063 : vRandom.pop_back();
448 : 2041063 : mapAddr.erase(info);
449 : 2041063 : mapInfo.erase(nId);
450 : 2041063 : nNew--;
451 : 2041063 : }
452 : :
453 : 7235551 : void AddrManImpl::ClearNew(int nUBucket, int nUBucketPos)
454 : : {
455 : 7235551 : AssertLockHeld(cs);
456 : :
457 : : // if there is an entry in the specified bucket, delete it.
458 [ + + ]: 7235551 : if (vvNew[nUBucket][nUBucketPos] != -1) {
459 : 1498231 : nid_type nIdDelete = vvNew[nUBucket][nUBucketPos];
460 : 1498231 : AddrInfo& infoDelete = mapInfo[nIdDelete];
461 [ - + ]: 1498231 : assert(infoDelete.nRefCount > 0);
462 : 1498231 : infoDelete.nRefCount--;
463 : 1498231 : vvNew[nUBucket][nUBucketPos] = -1;
464 [ - + - - ]: 1498231 : LogDebug(BCLog::ADDRMAN, "Removed %s from new[%i][%i]\n", infoDelete.ToStringAddrPort(), nUBucket, nUBucketPos);
465 [ + + ]: 1498231 : if (infoDelete.nRefCount == 0) {
466 : 1430844 : Delete(nIdDelete);
467 : : }
468 : : }
469 : 7235551 : }
470 : :
471 : 2348233 : void AddrManImpl::MakeTried(AddrInfo& info, nid_type nId)
472 : : {
473 : 2348233 : AssertLockHeld(cs);
474 : :
475 : : // remove the entry from all new buckets
476 : 2348233 : const int start_bucket{info.GetNewBucket(nKey, m_netgroupman)};
477 [ + - ]: 5046033 : for (int n = 0; n < ADDRMAN_NEW_BUCKET_COUNT; ++n) {
478 : 5046033 : const int bucket{(start_bucket + n) % ADDRMAN_NEW_BUCKET_COUNT};
479 : 5046033 : const int pos{info.GetBucketPosition(nKey, true, bucket)};
480 [ + + ]: 5046033 : if (vvNew[bucket][pos] == nId) {
481 : 2353986 : vvNew[bucket][pos] = -1;
482 : 2353986 : info.nRefCount--;
483 [ + + ]: 2353986 : if (info.nRefCount == 0) break;
484 : : }
485 : : }
486 : 2348233 : nNew--;
487 : 2348233 : m_network_counts[info.GetNetwork()].n_new--;
488 : :
489 [ - + ]: 2348233 : assert(info.nRefCount == 0);
490 : :
491 : : // which tried bucket to move the entry to
492 : 2348233 : int nKBucket = info.GetTriedBucket(nKey, m_netgroupman);
493 : 2348233 : int nKBucketPos = info.GetBucketPosition(nKey, false, nKBucket);
494 : :
495 : : // first make space to add it (the existing tried entry there is moved to new, deleting whatever is there).
496 [ + + ]: 2348233 : if (vvTried[nKBucket][nKBucketPos] != -1) {
497 : : // find an item to evict
498 : 1184 : nid_type nIdEvict = vvTried[nKBucket][nKBucketPos];
499 [ - + ]: 1184 : assert(mapInfo.contains(nIdEvict));
500 : 1184 : AddrInfo& infoOld = mapInfo[nIdEvict];
501 : :
502 : : // Remove the to-be-evicted item from the tried set.
503 : 1184 : infoOld.fInTried = false;
504 : 1184 : vvTried[nKBucket][nKBucketPos] = -1;
505 : 1184 : nTried--;
506 : 1184 : m_network_counts[infoOld.GetNetwork()].n_tried--;
507 : :
508 : : // find which new bucket it belongs to
509 : 1184 : int nUBucket = infoOld.GetNewBucket(nKey, m_netgroupman);
510 : 1184 : int nUBucketPos = infoOld.GetBucketPosition(nKey, true, nUBucket);
511 : 1184 : ClearNew(nUBucket, nUBucketPos);
512 [ - + ]: 1184 : assert(vvNew[nUBucket][nUBucketPos] == -1);
513 : :
514 : : // Enter it into the new set again.
515 : 1184 : infoOld.nRefCount = 1;
516 : 1184 : vvNew[nUBucket][nUBucketPos] = nIdEvict;
517 : 1184 : nNew++;
518 : 1184 : m_network_counts[infoOld.GetNetwork()].n_new++;
519 [ - + - - ]: 1184 : LogDebug(BCLog::ADDRMAN, "Moved %s from tried[%i][%i] to new[%i][%i] to make space\n",
520 : : infoOld.ToStringAddrPort(), nKBucket, nKBucketPos, nUBucket, nUBucketPos);
521 : : }
522 [ - + ]: 2348233 : assert(vvTried[nKBucket][nKBucketPos] == -1);
523 : :
524 : 2348233 : vvTried[nKBucket][nKBucketPos] = nId;
525 : 2348233 : nTried++;
526 : 2348233 : info.fInTried = true;
527 : 2348233 : m_network_counts[info.GetNetwork()].n_tried++;
528 : 2348233 : }
529 : :
530 : 9109266 : bool AddrManImpl::AddSingle(const CAddress& addr, const CNetAddr& source, std::chrono::seconds time_penalty)
531 : : {
532 : 9109266 : AssertLockHeld(cs);
533 : :
534 [ + + ]: 9109266 : if (!addr.IsRoutable())
535 : : return false;
536 : :
537 : 9008019 : nid_type nId;
538 : 9008019 : AddrInfo* pinfo = Find(addr, &nId);
539 : :
540 : : // Do not set a penalty for a source's self-announcement
541 [ + + ]: 9008019 : if (addr == source) {
542 : 100957 : time_penalty = 0s;
543 : : }
544 : :
545 [ + + ]: 9008019 : if (pinfo) {
546 : : // periodically update nTime
547 : 1436050 : const bool currently_online{NodeClock::now() - addr.nTime < 24h};
548 [ + + ]: 1436050 : const auto update_interval{currently_online ? 1h : 24h};
549 [ + + ]: 1436050 : if (pinfo->nTime < addr.nTime - update_interval - time_penalty) {
550 : 76939 : pinfo->nTime = std::max(NodeSeconds{0s}, addr.nTime - time_penalty);
551 : : }
552 : :
553 : : // add services
554 : 1436050 : pinfo->nServices = ServiceFlags(pinfo->nServices | addr.nServices);
555 : :
556 : : // do not update if no new information is present
557 [ + + ]: 1436050 : if (addr.nTime <= pinfo->nTime) {
558 : : return false;
559 : : }
560 : :
561 : : // do not update if the entry was already in the "tried" table
562 [ + + ]: 1238088 : if (pinfo->fInTried)
563 : : return false;
564 : :
565 : : // do not update if the max reference count is reached
566 [ + + ]: 774576 : if (pinfo->nRefCount == ADDRMAN_NEW_BUCKETS_PER_ADDRESS)
567 : : return false;
568 : :
569 : : // stochastic test: previous nRefCount == N: 2^N times harder to increase it
570 [ + - ]: 773915 : if (pinfo->nRefCount > 0) {
571 : 773915 : const int nFactor{1 << pinfo->nRefCount};
572 [ + + ]: 773915 : if (insecure_rand.randrange(nFactor) != 0) return false;
573 : : }
574 : : } else {
575 : 7571969 : pinfo = Create(addr, source, &nId);
576 : 7571969 : pinfo->nTime = std::max(NodeSeconds{0s}, pinfo->nTime - time_penalty);
577 : : }
578 : :
579 : 7921872 : int nUBucket = pinfo->GetNewBucket(nKey, source, m_netgroupman);
580 : 7921872 : int nUBucketPos = pinfo->GetBucketPosition(nKey, true, nUBucket);
581 : 7921872 : bool fInsert = vvNew[nUBucket][nUBucketPos] == -1;
582 [ + + ]: 7921872 : if (vvNew[nUBucket][nUBucketPos] != nId) {
583 [ + + ]: 7783622 : if (!fInsert) {
584 : 2047356 : AddrInfo& infoExisting = mapInfo[vvNew[nUBucket][nUBucketPos]];
585 [ + + + + : 2047356 : if (infoExisting.IsTerrible() || (infoExisting.nRefCount > 1 && pinfo->nRefCount == 0)) {
+ + ]
586 : : // Overwrite the existing new table entry.
587 : : fInsert = true;
588 : : }
589 : : }
590 [ + + ]: 6285521 : if (fInsert) {
591 : 7234367 : ClearNew(nUBucket, nUBucketPos);
592 : 7234367 : pinfo->nRefCount++;
593 : 7234367 : vvNew[nUBucket][nUBucketPos] = nId;
594 : 7234367 : const auto mapped_as{m_netgroupman.GetMappedAS(addr)};
595 [ - + - - : 7234367 : LogDebug(BCLog::ADDRMAN, "Added %s%s to new[%i][%i]\n",
- - - - ]
596 : : addr.ToStringAddrPort(), (mapped_as ? strprintf(" mapped to AS%i", mapped_as) : ""), nUBucket, nUBucketPos);
597 : : } else {
598 [ + + ]: 549255 : if (pinfo->nRefCount == 0) {
599 : 537097 : Delete(nId);
600 : : }
601 : : }
602 : : }
603 : : return fInsert;
604 : : }
605 : :
606 : 3939280 : bool AddrManImpl::Good_(const CService& addr, bool test_before_evict, NodeSeconds time)
607 : : {
608 : 3939280 : AssertLockHeld(cs);
609 : :
610 : 3939280 : nid_type nId;
611 : :
612 : 3939280 : m_last_good = time;
613 : :
614 : 3939280 : AddrInfo* pinfo = Find(addr, &nId);
615 : :
616 : : // if not found, bail out
617 [ + + ]: 3939280 : if (!pinfo) return false;
618 : :
619 : 3413129 : AddrInfo& info = *pinfo;
620 : :
621 : : // update info
622 : 3413129 : info.m_last_success = time;
623 : 3413129 : info.m_last_try = time;
624 : 3413129 : info.nAttempts = 0;
625 : : // nTime is not updated here, to avoid leaking information about
626 : : // currently-connected peers.
627 : :
628 : : // if it is already in the tried set, don't do anything else
629 [ + + ]: 3413129 : if (info.fInTried) return false;
630 : :
631 : : // if it is not in new, something bad happened
632 [ + - ]: 3216147 : if (!Assume(info.nRefCount > 0)) return false;
633 : :
634 : :
635 : : // which tried bucket to move the entry to
636 : 3216147 : int tried_bucket = info.GetTriedBucket(nKey, m_netgroupman);
637 : 3216147 : int tried_bucket_pos = info.GetBucketPosition(nKey, false, tried_bucket);
638 : :
639 : : // Will moving this address into tried evict another entry?
640 [ + + + + ]: 3216147 : if (test_before_evict && (vvTried[tried_bucket][tried_bucket_pos] != -1)) {
641 [ + + ]: 867914 : if (m_tried_collisions.size() < ADDRMAN_SET_TRIED_COLLISION_SIZE) {
642 : 17131 : m_tried_collisions.insert(nId);
643 : : }
644 : : // Output the entry we'd be colliding with, for debugging purposes
645 : 867914 : auto colliding_entry = mapInfo.find(vvTried[tried_bucket][tried_bucket_pos]);
646 [ - + - - : 867914 : LogDebug(BCLog::ADDRMAN, "Collision with %s while attempting to move %s to tried table. Collisions=%d",
- - - - -
- ]
647 : : colliding_entry != mapInfo.end() ? colliding_entry->second.ToStringAddrPort() : "<unknown-addr>",
648 : : addr.ToStringAddrPort(),
649 : : m_tried_collisions.size());
650 : 867914 : return false;
651 : : } else {
652 : : // move nId to the tried tables
653 : 2348233 : MakeTried(info, nId);
654 : 2348233 : const auto mapped_as{m_netgroupman.GetMappedAS(addr)};
655 [ - + - - : 2348233 : LogDebug(BCLog::ADDRMAN, "Moved %s%s to tried[%i][%i]\n",
- - - - ]
656 : : addr.ToStringAddrPort(), (mapped_as ? strprintf(" mapped to AS%i", mapped_as) : ""), tried_bucket, tried_bucket_pos);
657 : 2348233 : return true;
658 : : }
659 : : }
660 : :
661 : 7146927 : bool AddrManImpl::Add_(const std::vector<CAddress>& vAddr, const CNetAddr& source, std::chrono::seconds time_penalty)
662 : : {
663 : 7146927 : int added{0};
664 [ + + ]: 16256193 : for (std::vector<CAddress>::const_iterator it = vAddr.begin(); it != vAddr.end(); it++) {
665 [ + + ]: 10984165 : added += AddSingle(*it, source, time_penalty) ? 1 : 0;
666 : : }
667 [ + + ]: 7146927 : if (added > 0) {
668 [ - + - - : 5898323 : LogDebug(BCLog::ADDRMAN, "Added %i addresses (of %i) from %s: %i tried, %i new\n", added, vAddr.size(), source.ToStringAddr(), nTried, nNew);
- - ]
669 : : }
670 : 7146927 : return added > 0;
671 : : }
672 : :
673 : 4028 : void AddrManImpl::Attempt_(const CService& addr, bool fCountFailure, NodeSeconds time)
674 : : {
675 : 4028 : AssertLockHeld(cs);
676 : :
677 : 4028 : AddrInfo* pinfo = Find(addr);
678 : :
679 : : // if not found, bail out
680 [ + + ]: 4028 : if (!pinfo)
681 : : return;
682 : :
683 : 2216 : AddrInfo& info = *pinfo;
684 : :
685 : : // update info
686 : 2216 : info.m_last_try = time;
687 [ + + + + ]: 2216 : if (fCountFailure && info.m_last_count_attempt < m_last_good) {
688 : 879 : info.m_last_count_attempt = time;
689 : 879 : info.nAttempts++;
690 : : }
691 : : }
692 : :
693 : 1262 : std::pair<CAddress, NodeSeconds> AddrManImpl::Select_(bool new_only, const std::unordered_set<Network>& networks) const
694 : : {
695 : 1262 : AssertLockHeld(cs);
696 : :
697 [ + + ]: 1262 : if (vRandom.empty()) return {};
698 : :
699 : 861 : size_t new_count = nNew;
700 : 861 : size_t tried_count = nTried;
701 : :
702 [ + + ]: 861 : if (!networks.empty()) {
703 : 150 : new_count = 0;
704 : 150 : tried_count = 0;
705 [ + + ]: 832 : for (auto& network : networks) {
706 : 682 : auto it = m_network_counts.find(network);
707 [ + + ]: 682 : if (it == m_network_counts.end()) {
708 : 370 : continue;
709 : : }
710 : 312 : auto counts = it->second;
711 : 312 : new_count += counts.n_new;
712 : 312 : tried_count += counts.n_tried;
713 : : }
714 : : }
715 : :
716 [ + + ]: 861 : if (new_only && new_count == 0) return {};
717 [ + + ]: 858 : if (new_count + tried_count == 0) return {};
718 : :
719 : : // Decide if we are going to search the new or tried table
720 : : // If either option is viable, use a 50% chance to choose
721 : 848 : bool search_tried;
722 [ + + ]: 848 : if (new_only || tried_count == 0) {
723 : : search_tried = false;
724 [ + + ]: 163 : } else if (new_count == 0) {
725 : : search_tried = true;
726 : : } else {
727 : 137 : search_tried = insecure_rand.randbool();
728 : : }
729 : :
730 [ + + ]: 137 : const int bucket_count{search_tried ? ADDRMAN_TRIED_BUCKET_COUNT : ADDRMAN_NEW_BUCKET_COUNT};
731 : :
732 : : // Loop through the addrman table until we find an appropriate entry
733 : 848 : double chance_factor = 1.0;
734 : 2654265 : while (1) {
735 : : // Pick a bucket, and an initial position in that bucket.
736 : 2654265 : int bucket = insecure_rand.randrange(bucket_count);
737 : 2654265 : int initial_position = insecure_rand.randrange(ADDRMAN_BUCKET_SIZE);
738 : :
739 : : // Iterate over the positions of that bucket, starting at the initial one,
740 : : // and looping around.
741 : 2654265 : int i, position;
742 : 2654265 : nid_type node_id;
743 [ + + ]: 172404391 : for (i = 0; i < ADDRMAN_BUCKET_SIZE; ++i) {
744 : 169753608 : position = (initial_position + i) % ADDRMAN_BUCKET_SIZE;
745 : 169753608 : node_id = GetEntry(search_tried, bucket, position);
746 [ + + ]: 169753608 : if (node_id != -1) {
747 [ + + ]: 11388 : if (!networks.empty()) {
748 : 8481 : const auto it{mapInfo.find(node_id)};
749 [ + - + - : 16962 : if (Assume(it != mapInfo.end()) && networks.contains(it->second.GetNetwork())) break;
+ + ]
750 : : } else {
751 : : break;
752 : : }
753 : : }
754 : : }
755 : :
756 : : // If the bucket is entirely empty, start over with a (likely) different one.
757 [ + + ]: 2654265 : if (i == ADDRMAN_BUCKET_SIZE) continue;
758 : :
759 : : // Find the entry to return.
760 : 3482 : const auto it_found{mapInfo.find(node_id)};
761 [ + - ]: 3482 : assert(it_found != mapInfo.end());
762 : 3482 : const AddrInfo& info{it_found->second};
763 : :
764 : : // With probability GetChance() * chance_factor, return the entry.
765 [ + + ]: 3482 : if (insecure_rand.randbits<30>() < chance_factor * info.GetChance() * (1 << 30)) {
766 [ - + - - : 848 : LogDebug(BCLog::ADDRMAN, "Selected %s from %s\n", info.ToStringAddrPort(), search_tried ? "tried" : "new");
- - ]
767 : 848 : return {info, info.m_last_try};
768 : : }
769 : :
770 : : // Otherwise start over with a (likely) different bucket, and increased chance factor.
771 : 2634 : chance_factor *= 1.2;
772 : : }
773 : : }
774 : :
775 : 169835528 : nid_type AddrManImpl::GetEntry(bool use_tried, size_t bucket, size_t position) const
776 : : {
777 : 169835528 : AssertLockHeld(cs);
778 : :
779 [ + + ]: 169835528 : if (use_tried) {
780 [ + - + - ]: 8099306 : if (Assume(position < ADDRMAN_BUCKET_SIZE) && Assume(bucket < ADDRMAN_TRIED_BUCKET_COUNT)) {
781 : 8099306 : return vvTried[bucket][position];
782 : : }
783 : : } else {
784 [ + - + - ]: 161736222 : if (Assume(position < ADDRMAN_BUCKET_SIZE) && Assume(bucket < ADDRMAN_NEW_BUCKET_COUNT)) {
785 : 161736222 : return vvNew[bucket][position];
786 : : }
787 : : }
788 : :
789 : : return -1;
790 : : }
791 : :
792 : 12092 : std::vector<CAddress> AddrManImpl::GetAddr_(size_t max_addresses, size_t max_pct, std::optional<Network> network, const bool filtered) const
793 : : {
794 : 12092 : AssertLockHeld(cs);
795 [ - + ]: 12092 : Assume(max_pct <= 100);
796 : :
797 [ - + ]: 12092 : size_t nNodes = vRandom.size();
798 [ + + ]: 12092 : if (max_pct != 0) {
799 [ + - ]: 6836 : max_pct = std::min(max_pct, size_t{100});
800 : 6836 : nNodes = max_pct * nNodes / 100;
801 : : }
802 [ + + ]: 12092 : if (max_addresses != 0) {
803 [ + + ]: 13701 : nNodes = std::min(nNodes, max_addresses);
804 : : }
805 : :
806 : : // gather a list of random nodes, skipping those of low quality
807 : 12092 : const auto now{Now<NodeSeconds>()};
808 : 12092 : std::vector<CAddress> addresses;
809 [ + - ]: 12092 : addresses.reserve(nNodes);
810 [ - + + + ]: 554641 : for (unsigned int n = 0; n < vRandom.size(); n++) {
811 [ - + + + ]: 542857 : if (addresses.size() >= nNodes)
812 : : break;
813 : :
814 : 542549 : int nRndPos = insecure_rand.randrange(vRandom.size() - n) + n;
815 [ + - ]: 542549 : SwapRandom(n, nRndPos);
816 : 542549 : const auto it{mapInfo.find(vRandom[n])};
817 [ - + ]: 542549 : assert(it != mapInfo.end());
818 : :
819 [ + + ]: 542549 : const AddrInfo& ai{it->second};
820 : :
821 : : // Filter by network (optional)
822 [ + + + - : 542549 : if (network != std::nullopt && ai.GetNetClass() != network) continue;
+ + ]
823 : :
824 : : // Filter for quality
825 [ + - + + : 417527 : if (ai.IsTerrible(now) && filtered) continue;
+ + ]
826 : :
827 [ + - ]: 338524 : addresses.push_back(ai);
828 : : }
829 [ + - + + : 12092 : LogDebug(BCLog::ADDRMAN, "GetAddr returned %d random addresses\n", addresses.size());
- + + - ]
830 : 12092 : return addresses;
831 : 0 : }
832 : :
833 : 2 : std::vector<std::pair<AddrInfo, AddressPosition>> AddrManImpl::GetEntries_(bool from_tried) const
834 : : {
835 : 2 : AssertLockHeld(cs);
836 : :
837 [ + + ]: 2 : const int bucket_count = from_tried ? ADDRMAN_TRIED_BUCKET_COUNT : ADDRMAN_NEW_BUCKET_COUNT;
838 : 2 : std::vector<std::pair<AddrInfo, AddressPosition>> infos;
839 [ + + ]: 1282 : for (int bucket = 0; bucket < bucket_count; ++bucket) {
840 [ + + ]: 83200 : for (int position = 0; position < ADDRMAN_BUCKET_SIZE; ++position) {
841 [ + - ]: 81920 : nid_type id = GetEntry(from_tried, bucket, position);
842 [ - + ]: 81920 : if (id >= 0) {
843 [ # # ]: 0 : AddrInfo info = mapInfo.at(id);
844 : 0 : AddressPosition location = AddressPosition(
845 : : from_tried,
846 : : /*multiplicity_in=*/from_tried ? 1 : info.nRefCount,
847 : : bucket,
848 [ # # # # ]: 0 : position);
849 [ # # ]: 0 : infos.emplace_back(info, location);
850 : 0 : }
851 : : }
852 : : }
853 : :
854 : 2 : return infos;
855 : 0 : }
856 : :
857 : 4877 : void AddrManImpl::Connected_(const CService& addr, NodeSeconds time)
858 : : {
859 : 4877 : AssertLockHeld(cs);
860 : :
861 : 4877 : AddrInfo* pinfo = Find(addr);
862 : :
863 : : // if not found, bail out
864 [ + + ]: 4877 : if (!pinfo)
865 : : return;
866 : :
867 : 1331 : AddrInfo& info = *pinfo;
868 : :
869 : : // update info
870 : 1331 : const auto update_interval{20min};
871 [ + + ]: 1331 : if (time - info.nTime > update_interval) {
872 : 326 : info.nTime = time;
873 : : }
874 : : }
875 : :
876 : 36366 : void AddrManImpl::SetServices_(const CService& addr, ServiceFlags nServices)
877 : : {
878 : 36366 : AssertLockHeld(cs);
879 : :
880 : 36366 : AddrInfo* pinfo = Find(addr);
881 : :
882 : : // if not found, bail out
883 [ + + ]: 36366 : if (!pinfo)
884 : : return;
885 : :
886 : 8669 : AddrInfo& info = *pinfo;
887 : :
888 : : // update info
889 : 8669 : info.nServices = nServices;
890 : : }
891 : :
892 : 7336 : void AddrManImpl::ResolveCollisions_()
893 : : {
894 : 7336 : AssertLockHeld(cs);
895 : :
896 [ + + ]: 15496 : for (std::set<nid_type>::iterator it = m_tried_collisions.begin(); it != m_tried_collisions.end();) {
897 : 8160 : nid_type id_new = *it;
898 : :
899 : 8160 : bool erase_collision = false;
900 : :
901 : : // If id_new not found in mapInfo remove it from m_tried_collisions
902 [ + + ]: 8160 : if (!mapInfo.contains(id_new)) {
903 : : erase_collision = true;
904 : : } else {
905 : 8030 : AddrInfo& info_new = mapInfo[id_new];
906 : :
907 : : // Which tried bucket to move the entry to.
908 : 8030 : int tried_bucket = info_new.GetTriedBucket(nKey, m_netgroupman);
909 : 8030 : int tried_bucket_pos = info_new.GetBucketPosition(nKey, false, tried_bucket);
910 [ + - ]: 8030 : if (!info_new.IsValid()) { // id_new may no longer map to a valid address
911 : : erase_collision = true;
912 [ + - ]: 8030 : } else if (vvTried[tried_bucket][tried_bucket_pos] != -1) { // The position in the tried bucket is not empty
913 : :
914 : : // Get the to-be-evicted address that is being tested
915 : 8030 : nid_type id_old = vvTried[tried_bucket][tried_bucket_pos];
916 : 8030 : AddrInfo& info_old = mapInfo[id_old];
917 : :
918 : 8030 : const auto current_time{Now<NodeSeconds>()};
919 : :
920 : : // Has successfully connected in last X hours
921 [ + + ]: 8030 : if (current_time - info_old.m_last_success < ADDRMAN_REPLACEMENT) {
922 : : erase_collision = true;
923 [ + + ]: 4748 : } else if (current_time - info_old.m_last_try < ADDRMAN_REPLACEMENT) { // attempted to connect and failed in last X hours
924 : :
925 : : // Give address at least 60 seconds to successfully connect
926 [ + + ]: 477 : if (current_time - info_old.m_last_try > 60s) {
927 [ - + - - : 2 : LogDebug(BCLog::ADDRMAN, "Replacing %s with %s in tried table\n", info_old.ToStringAddrPort(), info_new.ToStringAddrPort());
- - ]
928 : :
929 : : // Replaces an existing address already in the tried table with the new address
930 : 2 : Good_(info_new, false, current_time);
931 : 2 : erase_collision = true;
932 : : }
933 [ + + ]: 4271 : } else if (current_time - info_new.m_last_success > ADDRMAN_TEST_WINDOW) {
934 : : // If the collision hasn't resolved in some reasonable amount of time,
935 : : // just evict the old entry -- we must not be able to
936 : : // connect to it for some reason.
937 [ - + - - : 1182 : LogDebug(BCLog::ADDRMAN, "Unable to test; replacing %s with %s in tried table anyway\n", info_old.ToStringAddrPort(), info_new.ToStringAddrPort());
- - ]
938 : 1182 : Good_(info_new, false, current_time);
939 : 1182 : erase_collision = true;
940 : : }
941 : : } else { // Collision is not actually a collision anymore
942 : 0 : Good_(info_new, false, Now<NodeSeconds>());
943 : 0 : erase_collision = true;
944 : : }
945 : : }
946 : :
947 : 1184 : if (erase_collision) {
948 : 4596 : m_tried_collisions.erase(it++);
949 : : } else {
950 : 3564 : it++;
951 : : }
952 : : }
953 : 7336 : }
954 : :
955 : 38075 : std::pair<CAddress, NodeSeconds> AddrManImpl::SelectTriedCollision_()
956 : : {
957 : 38075 : AssertLockHeld(cs);
958 : :
959 [ + + ]: 38075 : if (m_tried_collisions.size() == 0) return {};
960 : :
961 : 23544 : std::set<nid_type>::iterator it = m_tried_collisions.begin();
962 : :
963 : : // Selects a random element from m_tried_collisions
964 : 23544 : std::advance(it, insecure_rand.randrange(m_tried_collisions.size()));
965 : 23544 : nid_type id_new = *it;
966 : :
967 : : // If id_new not found in mapInfo remove it from m_tried_collisions
968 [ + + ]: 23544 : if (!mapInfo.contains(id_new)) {
969 : 269 : m_tried_collisions.erase(it);
970 : 269 : return {};
971 : : }
972 : :
973 : 23275 : const AddrInfo& newInfo = mapInfo[id_new];
974 : :
975 : : // which tried bucket to move the entry to
976 : 23275 : int tried_bucket = newInfo.GetTriedBucket(nKey, m_netgroupman);
977 : 23275 : int tried_bucket_pos = newInfo.GetBucketPosition(nKey, false, tried_bucket);
978 : :
979 : 23275 : const AddrInfo& info_old = mapInfo[vvTried[tried_bucket][tried_bucket_pos]];
980 : 23275 : return {info_old, info_old.m_last_try};
981 : : }
982 : :
983 : 0 : std::optional<AddressPosition> AddrManImpl::FindAddressEntry_(const CAddress& addr)
984 : : {
985 : 0 : AssertLockHeld(cs);
986 : :
987 : 0 : AddrInfo* addr_info = Find(addr);
988 : :
989 [ # # ]: 0 : if (!addr_info) return std::nullopt;
990 : :
991 [ # # ]: 0 : if(addr_info->fInTried) {
992 : 0 : int bucket{addr_info->GetTriedBucket(nKey, m_netgroupman)};
993 : 0 : return AddressPosition(/*tried_in=*/true,
994 : : /*multiplicity_in=*/1,
995 : : /*bucket_in=*/bucket,
996 : 0 : /*position_in=*/addr_info->GetBucketPosition(nKey, false, bucket));
997 : : } else {
998 : 0 : int bucket{addr_info->GetNewBucket(nKey, m_netgroupman)};
999 : 0 : return AddressPosition(/*tried_in=*/false,
1000 : : /*multiplicity_in=*/addr_info->nRefCount,
1001 : : /*bucket_in=*/bucket,
1002 : 0 : /*position_in=*/addr_info->GetBucketPosition(nKey, true, bucket));
1003 : : }
1004 : : }
1005 : :
1006 : 5141849 : size_t AddrManImpl::Size_(std::optional<Network> net, std::optional<bool> in_new) const
1007 : : {
1008 : 5141849 : AssertLockHeld(cs);
1009 : :
1010 [ + + ]: 5141849 : if (!net.has_value()) {
1011 [ + + ]: 5141727 : if (in_new.has_value()) {
1012 [ + + ]: 50 : return *in_new ? nNew : nTried;
1013 : : } else {
1014 [ - + ]: 5141677 : return vRandom.size();
1015 : : }
1016 : : }
1017 [ + + ]: 122 : if (auto it = m_network_counts.find(*net); it != m_network_counts.end()) {
1018 [ + + ]: 58 : auto net_count = it->second;
1019 [ + + ]: 58 : if (in_new.has_value()) {
1020 [ + + ]: 38 : return *in_new ? net_count.n_new : net_count.n_tried;
1021 : : } else {
1022 : 20 : return net_count.n_new + net_count.n_tried;
1023 : : }
1024 : : }
1025 : : return 0;
1026 : : }
1027 : :
1028 : 32661820 : void AddrManImpl::Check() const
1029 : : {
1030 : 32661820 : AssertLockHeld(cs);
1031 : :
1032 : : // Run consistency checks 1 in m_consistency_check_ratio times if enabled
1033 [ - + ]: 32661820 : if (m_consistency_check_ratio == 0) return;
1034 [ # # ]: 0 : if (insecure_rand.randrange(m_consistency_check_ratio) >= 1) return;
1035 : :
1036 : 0 : const int err{CheckAddrman()};
1037 [ # # ]: 0 : if (err) {
1038 : 0 : LogError("ADDRMAN CONSISTENCY CHECK FAILED!!! err=%i", err);
1039 : 0 : assert(false);
1040 : : }
1041 : : }
1042 : :
1043 : 2048 : int AddrManImpl::CheckAddrman() const
1044 : : {
1045 : 2048 : AssertLockHeld(cs);
1046 : :
1047 [ - + + - : 4096 : LOG_TIME_MILLIS_WITH_CATEGORY_MSG_ONCE(
+ - ]
1048 : : strprintf("new %i, tried %i, total %u", nNew, nTried, vRandom.size()), BCLog::ADDRMAN);
1049 : :
1050 [ - + ]: 2048 : std::unordered_set<nid_type> setTried;
1051 [ - + ]: 2048 : std::unordered_map<nid_type, int> mapNew;
1052 : 2048 : std::unordered_map<Network, NewTriedCount> local_counts;
1053 : :
1054 [ - + + - ]: 2048 : if (vRandom.size() != (size_t)(nTried + nNew))
1055 : : return -7;
1056 : :
1057 [ + + + + ]: 5021661 : for (const auto& entry : mapInfo) {
1058 : 5019796 : nid_type n = entry.first;
1059 : 5019796 : const AddrInfo& info = entry.second;
1060 [ + + ]: 5019796 : if (info.fInTried) {
1061 [ + + ]: 2328462 : if (!TicksSinceEpoch<std::chrono::seconds>(info.m_last_success)) {
1062 : : return -1;
1063 : : }
1064 [ + - ]: 2328444 : if (info.nRefCount)
1065 : : return -2;
1066 [ + - ]: 2328444 : setTried.insert(n);
1067 [ + - + - ]: 2328444 : local_counts[info.GetNetwork()].n_tried++;
1068 : : } else {
1069 [ + - ]: 2691334 : if (info.nRefCount < 0 || info.nRefCount > ADDRMAN_NEW_BUCKETS_PER_ADDRESS)
1070 : : return -3;
1071 [ + - ]: 2691334 : if (!info.nRefCount)
1072 : : return -4;
1073 [ + - ]: 2691334 : mapNew[n] = info.nRefCount;
1074 [ + - + - ]: 2691334 : local_counts[info.GetNetwork()].n_new++;
1075 : : }
1076 [ + - ]: 5019778 : const auto it{mapAddr.find(info)};
1077 [ + + + + ]: 5019778 : if (it == mapAddr.end() || it->second != n) {
1078 : : return -5;
1079 : : }
1080 [ + - - + : 5019754 : if (info.nRandomPos < 0 || (size_t)info.nRandomPos >= vRandom.size() || vRandom[info.nRandomPos] != n)
+ - + - ]
1081 : : return -14;
1082 [ + - ]: 5019754 : if (info.m_last_try < NodeSeconds{0s}) {
1083 : : return -6;
1084 : : }
1085 [ + + ]: 5019754 : if (info.m_last_success < NodeSeconds{0s}) {
1086 : : return -8;
1087 : : }
1088 : : }
1089 : :
1090 [ + - ]: 1865 : if (setTried.size() != (size_t)nTried)
1091 : : return -9;
1092 [ + - ]: 1865 : if (mapNew.size() != (size_t)nNew)
1093 : : return -10;
1094 : :
1095 [ + + ]: 479305 : for (int n = 0; n < ADDRMAN_TRIED_BUCKET_COUNT; n++) {
1096 [ + + ]: 31033600 : for (int i = 0; i < ADDRMAN_BUCKET_SIZE; i++) {
1097 [ + + ]: 30556160 : if (vvTried[n][i] != -1) {
1098 [ + - ]: 2328199 : if (!setTried.contains(vvTried[n][i]))
1099 : : return -11;
1100 : 2328199 : const auto it{mapInfo.find(vvTried[n][i])};
1101 [ + - + - : 2328199 : if (it == mapInfo.end() || it->second.GetTriedBucket(nKey, m_netgroupman) != n) {
+ - ]
1102 : 0 : return -17;
1103 : : }
1104 [ + - + - ]: 2328199 : if (it->second.GetBucketPosition(nKey, false, n) != i) {
1105 : : return -18;
1106 : : }
1107 : 2328199 : setTried.erase(vvTried[n][i]);
1108 : : }
1109 : : }
1110 : : }
1111 : :
1112 [ + + ]: 1911625 : for (int n = 0; n < ADDRMAN_NEW_BUCKET_COUNT; n++) {
1113 [ + + ]: 124134400 : for (int i = 0; i < ADDRMAN_BUCKET_SIZE; i++) {
1114 [ + + ]: 122224640 : if (vvNew[n][i] != -1) {
1115 [ + - ]: 2798703 : if (!mapNew.contains(vvNew[n][i]))
1116 : : return -12;
1117 : 2798703 : const auto it{mapInfo.find(vvNew[n][i])};
1118 [ + - + - : 2798703 : if (it == mapInfo.end() || it->second.GetBucketPosition(nKey, true, n) != i) {
+ - ]
1119 : 0 : return -19;
1120 : : }
1121 [ + - + + ]: 2798703 : if (--mapNew[vvNew[n][i]] == 0)
1122 : 2691039 : mapNew.erase(vvNew[n][i]);
1123 : : }
1124 : : }
1125 : : }
1126 : :
1127 [ + - ]: 1865 : if (setTried.size())
1128 : : return -13;
1129 [ + - ]: 1865 : if (mapNew.size())
1130 : : return -15;
1131 [ + + ]: 3730 : if (nKey.IsNull())
1132 : : return -16;
1133 : :
1134 : : // It's possible that m_network_counts may have all-zero entries that local_counts
1135 : : // doesn't have if addrs from a network were being added and then removed again in the past.
1136 [ + - ]: 1862 : if (m_network_counts.size() < local_counts.size()) {
1137 : : return -20;
1138 : : }
1139 [ + + + - ]: 7668 : for (const auto& [net, count] : m_network_counts) {
1140 [ + - + - : 11240 : if (local_counts[net].n_new != count.n_new || local_counts[net].n_tried != count.n_tried) {
+ - ]
1141 : 0 : return -21;
1142 : : }
1143 : : }
1144 : :
1145 : : return 0;
1146 : 2048 : }
1147 : :
1148 : 5141849 : size_t AddrManImpl::Size(std::optional<Network> net, std::optional<bool> in_new) const
1149 : : {
1150 : 5141849 : LOCK(cs);
1151 [ + - ]: 5141849 : Check();
1152 [ + - ]: 5141849 : auto ret = Size_(net, in_new);
1153 [ + - ]: 5141849 : Check();
1154 [ + - ]: 5141849 : return ret;
1155 : 5141849 : }
1156 : :
1157 : 7146927 : bool AddrManImpl::Add(const std::vector<CAddress>& vAddr, const CNetAddr& source, std::chrono::seconds time_penalty)
1158 : : {
1159 : 7146927 : LOCK(cs);
1160 [ + - ]: 7146927 : Check();
1161 [ + - ]: 7146927 : auto ret = Add_(vAddr, source, time_penalty);
1162 [ + - ]: 7146927 : Check();
1163 [ + - ]: 7146927 : return ret;
1164 : 7146927 : }
1165 : :
1166 : 3938096 : bool AddrManImpl::Good(const CService& addr, NodeSeconds time)
1167 : : {
1168 : 3938096 : LOCK(cs);
1169 [ + - ]: 3938096 : Check();
1170 [ + - ]: 3938096 : auto ret = Good_(addr, /*test_before_evict=*/true, time);
1171 [ + - ]: 3938096 : Check();
1172 [ + - ]: 3938096 : return ret;
1173 : 3938096 : }
1174 : :
1175 : 4028 : void AddrManImpl::Attempt(const CService& addr, bool fCountFailure, NodeSeconds time)
1176 : : {
1177 : 4028 : LOCK(cs);
1178 [ + - ]: 4028 : Check();
1179 [ + - ]: 4028 : Attempt_(addr, fCountFailure, time);
1180 [ + - ]: 4028 : Check();
1181 : 4028 : }
1182 : :
1183 : 7336 : void AddrManImpl::ResolveCollisions()
1184 : : {
1185 : 7336 : LOCK(cs);
1186 [ + - ]: 7336 : Check();
1187 [ + - ]: 7336 : ResolveCollisions_();
1188 [ + - ]: 7336 : Check();
1189 : 7336 : }
1190 : :
1191 : 38075 : std::pair<CAddress, NodeSeconds> AddrManImpl::SelectTriedCollision()
1192 : : {
1193 : 38075 : LOCK(cs);
1194 [ + - ]: 38075 : Check();
1195 [ + - ]: 38075 : auto ret = SelectTriedCollision_();
1196 [ + - ]: 38075 : Check();
1197 [ + - ]: 38075 : return ret;
1198 : 38075 : }
1199 : :
1200 : 1262 : std::pair<CAddress, NodeSeconds> AddrManImpl::Select(bool new_only, const std::unordered_set<Network>& networks) const
1201 : : {
1202 : 1262 : LOCK(cs);
1203 [ + - ]: 1262 : Check();
1204 [ + - ]: 1262 : auto addrRet = Select_(new_only, networks);
1205 [ + - ]: 1262 : Check();
1206 [ + - ]: 1262 : return addrRet;
1207 : 1262 : }
1208 : :
1209 : 12092 : std::vector<CAddress> AddrManImpl::GetAddr(size_t max_addresses, size_t max_pct, std::optional<Network> network, const bool filtered) const
1210 : : {
1211 : 12092 : LOCK(cs);
1212 [ + - ]: 12092 : Check();
1213 [ + - ]: 12092 : auto addresses = GetAddr_(max_addresses, max_pct, network, filtered);
1214 [ + - ]: 12092 : Check();
1215 [ + - ]: 12092 : return addresses;
1216 : 12092 : }
1217 : :
1218 : 2 : std::vector<std::pair<AddrInfo, AddressPosition>> AddrManImpl::GetEntries(bool from_tried) const
1219 : : {
1220 : 2 : LOCK(cs);
1221 [ + - ]: 2 : Check();
1222 [ + - ]: 2 : auto addrInfos = GetEntries_(from_tried);
1223 [ + - ]: 2 : Check();
1224 [ + - ]: 2 : return addrInfos;
1225 : 2 : }
1226 : :
1227 : 4877 : void AddrManImpl::Connected(const CService& addr, NodeSeconds time)
1228 : : {
1229 : 4877 : LOCK(cs);
1230 [ + - ]: 4877 : Check();
1231 [ + - ]: 4877 : Connected_(addr, time);
1232 [ + - ]: 4877 : Check();
1233 : 4877 : }
1234 : :
1235 : 36366 : void AddrManImpl::SetServices(const CService& addr, ServiceFlags nServices)
1236 : : {
1237 : 36366 : LOCK(cs);
1238 [ + - ]: 36366 : Check();
1239 [ + - ]: 36366 : SetServices_(addr, nServices);
1240 [ + - ]: 36366 : Check();
1241 : 36366 : }
1242 : :
1243 : 0 : std::optional<AddressPosition> AddrManImpl::FindAddressEntry(const CAddress& addr)
1244 : : {
1245 : 0 : LOCK(cs);
1246 [ # # ]: 0 : Check();
1247 [ # # ]: 0 : auto entry = FindAddressEntry_(addr);
1248 [ # # ]: 0 : Check();
1249 [ # # ]: 0 : return entry;
1250 : 0 : }
1251 : :
1252 : 11524 : AddrMan::AddrMan(const NetGroupManager& netgroupman, bool deterministic, int32_t consistency_check_ratio)
1253 : 11524 : : m_impl(std::make_unique<AddrManImpl>(netgroupman, deterministic, consistency_check_ratio)) {}
1254 : :
1255 : 11524 : AddrMan::~AddrMan() = default;
1256 : :
1257 : : template <typename Stream>
1258 : 2158 : void AddrMan::Serialize(Stream& s_) const
1259 : : {
1260 : 2158 : m_impl->Serialize<Stream>(s_);
1261 : 2158 : }
1262 : :
1263 : : template <typename Stream>
1264 : 3591 : void AddrMan::Unserialize(Stream& s_)
1265 : : {
1266 : 3591 : m_impl->Unserialize<Stream>(s_);
1267 : 1862 : }
1268 : :
1269 : : // explicit instantiation
1270 : : template void AddrMan::Serialize(HashedSourceWriter<AutoFile>&) const;
1271 : : template void AddrMan::Serialize(DataStream&) const;
1272 : : template void AddrMan::Unserialize(AutoFile&);
1273 : : template void AddrMan::Unserialize(HashVerifier<AutoFile>&);
1274 : : template void AddrMan::Unserialize(DataStream&);
1275 : : template void AddrMan::Unserialize(HashVerifier<DataStream>&);
1276 : :
1277 : 5141849 : size_t AddrMan::Size(std::optional<Network> net, std::optional<bool> in_new) const
1278 : : {
1279 : 5141849 : return m_impl->Size(net, in_new);
1280 : : }
1281 : :
1282 : 7146927 : bool AddrMan::Add(const std::vector<CAddress>& vAddr, const CNetAddr& source, std::chrono::seconds time_penalty)
1283 : : {
1284 : 7146927 : return m_impl->Add(vAddr, source, time_penalty);
1285 : : }
1286 : :
1287 : 3938096 : bool AddrMan::Good(const CService& addr, NodeSeconds time)
1288 : : {
1289 : 3938096 : return m_impl->Good(addr, time);
1290 : : }
1291 : :
1292 : 4028 : void AddrMan::Attempt(const CService& addr, bool fCountFailure, NodeSeconds time)
1293 : : {
1294 : 4028 : m_impl->Attempt(addr, fCountFailure, time);
1295 : 4028 : }
1296 : :
1297 : 7336 : void AddrMan::ResolveCollisions()
1298 : : {
1299 : 7336 : m_impl->ResolveCollisions();
1300 : 7336 : }
1301 : :
1302 : 38075 : std::pair<CAddress, NodeSeconds> AddrMan::SelectTriedCollision()
1303 : : {
1304 : 38075 : return m_impl->SelectTriedCollision();
1305 : : }
1306 : :
1307 : 1262 : std::pair<CAddress, NodeSeconds> AddrMan::Select(bool new_only, const std::unordered_set<Network>& networks) const
1308 : : {
1309 : 1262 : return m_impl->Select(new_only, networks);
1310 : : }
1311 : :
1312 : 12092 : std::vector<CAddress> AddrMan::GetAddr(size_t max_addresses, size_t max_pct, std::optional<Network> network, const bool filtered) const
1313 : : {
1314 : 12092 : return m_impl->GetAddr(max_addresses, max_pct, network, filtered);
1315 : : }
1316 : :
1317 : 2 : std::vector<std::pair<AddrInfo, AddressPosition>> AddrMan::GetEntries(bool use_tried) const
1318 : : {
1319 : 2 : return m_impl->GetEntries(use_tried);
1320 : : }
1321 : :
1322 : 4877 : void AddrMan::Connected(const CService& addr, NodeSeconds time)
1323 : : {
1324 : 4877 : m_impl->Connected(addr, time);
1325 : 4877 : }
1326 : :
1327 : 36366 : void AddrMan::SetServices(const CService& addr, ServiceFlags nServices)
1328 : : {
1329 : 36366 : m_impl->SetServices(addr, nServices);
1330 : 36366 : }
1331 : :
1332 : 0 : std::optional<AddressPosition> AddrMan::FindAddressEntry(const CAddress& addr)
1333 : : {
1334 : 0 : return m_impl->FindAddressEntry(addr);
1335 : : }
|