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