@@ -197,10 +197,181 @@ void CAddrMan::Serialize(Stream& s_) const
197197 s << asmap_checksum;
198198}
199199
200+ template <typename Stream>
201+ void CAddrMan::Unserialize (Stream& s_)
202+ {
203+ LOCK (cs);
204+
205+ assert (vRandom.empty ());
206+
207+ Format format;
208+ s_ >> Using<CustomUintFormatter<1 >>(format);
209+
210+ int stream_version = s_.GetVersion ();
211+ if (format >= Format::V3_BIP155) {
212+ // Add ADDRV2_FORMAT to the version so that the CNetAddr and CAddress
213+ // unserialize methods know that an address in addrv2 format is coming.
214+ stream_version |= ADDRV2_FORMAT;
215+ }
216+
217+ OverrideStream<Stream> s (&s_, s_.GetType (), stream_version);
218+
219+ uint8_t compat;
220+ s >> compat;
221+ const uint8_t lowest_compatible = compat - INCOMPATIBILITY_BASE;
222+ if (lowest_compatible > FILE_FORMAT) {
223+ throw std::ios_base::failure (strprintf (
224+ " Unsupported format of addrman database: %u. It is compatible with formats >=%u, "
225+ " but the maximum supported by this version of %s is %u." ,
226+ format, lowest_compatible, PACKAGE_NAME, static_cast <uint8_t >(FILE_FORMAT)));
227+ }
228+
229+ s >> nKey;
230+ s >> nNew;
231+ s >> nTried;
232+ int nUBuckets = 0 ;
233+ s >> nUBuckets;
234+ if (format >= Format::V1_DETERMINISTIC) {
235+ nUBuckets ^= (1 << 30 );
236+ }
237+
238+ if (nNew > ADDRMAN_NEW_BUCKET_COUNT * ADDRMAN_BUCKET_SIZE || nNew < 0 ) {
239+ throw std::ios_base::failure (
240+ strprintf (" Corrupt CAddrMan serialization: nNew=%d, should be in [0, %u]" ,
241+ nNew,
242+ ADDRMAN_NEW_BUCKET_COUNT * ADDRMAN_BUCKET_SIZE));
243+ }
244+
245+ if (nTried > ADDRMAN_TRIED_BUCKET_COUNT * ADDRMAN_BUCKET_SIZE || nTried < 0 ) {
246+ throw std::ios_base::failure (
247+ strprintf (" Corrupt CAddrMan serialization: nTried=%d, should be in [0, %u]" ,
248+ nTried,
249+ ADDRMAN_TRIED_BUCKET_COUNT * ADDRMAN_BUCKET_SIZE));
250+ }
251+
252+ // Deserialize entries from the new table.
253+ for (int n = 0 ; n < nNew; n++) {
254+ CAddrInfo &info = mapInfo[n];
255+ s >> info;
256+ mapAddr[info] = n;
257+ info.nRandomPos = vRandom.size ();
258+ vRandom.push_back (n);
259+ }
260+ nIdCount = nNew;
261+
262+ // Deserialize entries from the tried table.
263+ int nLost = 0 ;
264+ for (int n = 0 ; n < nTried; n++) {
265+ CAddrInfo info;
266+ s >> info;
267+ int nKBucket = info.GetTriedBucket (nKey, m_asmap);
268+ int nKBucketPos = info.GetBucketPosition (nKey, false , nKBucket);
269+ if (info.IsValid ()
270+ && vvTried[nKBucket][nKBucketPos] == -1 ) {
271+ info.nRandomPos = vRandom.size ();
272+ info.fInTried = true ;
273+ vRandom.push_back (nIdCount);
274+ mapInfo[nIdCount] = info;
275+ mapAddr[info] = nIdCount;
276+ vvTried[nKBucket][nKBucketPos] = nIdCount;
277+ nIdCount++;
278+ } else {
279+ nLost++;
280+ }
281+ }
282+ nTried -= nLost;
283+
284+ // Store positions in the new table buckets to apply later (if possible).
285+ // An entry may appear in up to ADDRMAN_NEW_BUCKETS_PER_ADDRESS buckets,
286+ // so we store all bucket-entry_index pairs to iterate through later.
287+ std::vector<std::pair<int , int >> bucket_entries;
288+
289+ for (int bucket = 0 ; bucket < nUBuckets; ++bucket) {
290+ int num_entries{0 };
291+ s >> num_entries;
292+ for (int n = 0 ; n < num_entries; ++n) {
293+ int entry_index{0 };
294+ s >> entry_index;
295+ if (entry_index >= 0 && entry_index < nNew) {
296+ bucket_entries.emplace_back (bucket, entry_index);
297+ }
298+ }
299+ }
300+
301+ // If the bucket count and asmap checksum haven't changed, then attempt
302+ // to restore the entries to the buckets/positions they were in before
303+ // serialization.
304+ uint256 supplied_asmap_checksum;
305+ if (m_asmap.size () != 0 ) {
306+ supplied_asmap_checksum = SerializeHash (m_asmap);
307+ }
308+ uint256 serialized_asmap_checksum;
309+ if (format >= Format::V2_ASMAP) {
310+ s >> serialized_asmap_checksum;
311+ }
312+ const bool restore_bucketing{nUBuckets == ADDRMAN_NEW_BUCKET_COUNT &&
313+ serialized_asmap_checksum == supplied_asmap_checksum};
314+
315+ if (!restore_bucketing) {
316+ LogPrint (BCLog::ADDRMAN, " Bucketing method was updated, re-bucketing addrman entries from disk\n " );
317+ }
318+
319+ for (auto bucket_entry : bucket_entries) {
320+ int bucket{bucket_entry.first };
321+ const int entry_index{bucket_entry.second };
322+ CAddrInfo& info = mapInfo[entry_index];
323+
324+ // Don't store the entry in the new bucket if it's not a valid address for our addrman
325+ if (!info.IsValid ()) continue ;
326+
327+ // The entry shouldn't appear in more than
328+ // ADDRMAN_NEW_BUCKETS_PER_ADDRESS. If it has already, just skip
329+ // this bucket_entry.
330+ if (info.nRefCount >= ADDRMAN_NEW_BUCKETS_PER_ADDRESS) continue ;
331+
332+ int bucket_position = info.GetBucketPosition (nKey, true , bucket);
333+ if (restore_bucketing && vvNew[bucket][bucket_position] == -1 ) {
334+ // Bucketing has not changed, using existing bucket positions for the new table
335+ vvNew[bucket][bucket_position] = entry_index;
336+ ++info.nRefCount ;
337+ } else {
338+ // In case the new table data cannot be used (bucket count wrong or new asmap),
339+ // try to give them a reference based on their primary source address.
340+ bucket = info.GetNewBucket (nKey, m_asmap);
341+ bucket_position = info.GetBucketPosition (nKey, true , bucket);
342+ if (vvNew[bucket][bucket_position] == -1 ) {
343+ vvNew[bucket][bucket_position] = entry_index;
344+ ++info.nRefCount ;
345+ }
346+ }
347+ }
348+
349+ // Prune new entries with refcount 0 (as a result of collisions or invalid address).
350+ int nLostUnk = 0 ;
351+ for (auto it = mapInfo.cbegin (); it != mapInfo.cend (); ) {
352+ if (it->second .fInTried == false && it->second .nRefCount == 0 ) {
353+ const auto itCopy = it++;
354+ Delete (itCopy->first );
355+ ++nLostUnk;
356+ } else {
357+ ++it;
358+ }
359+ }
360+ if (nLost + nLostUnk > 0 ) {
361+ LogPrint (BCLog::ADDRMAN, " addrman lost %i new and %i tried addresses due to collisions or invalid addresses\n " , nLostUnk, nLost);
362+ }
363+
364+ Check ();
365+ }
366+
200367// explicit instantiation
201368template void CAddrMan::Serialize (CHashWriter& s) const ;
202369template void CAddrMan::Serialize (CAutoFile& s) const ;
203370template void CAddrMan::Serialize (CDataStream& s) const ;
371+ template void CAddrMan::Unserialize (CAutoFile& s);
372+ template void CAddrMan::Unserialize (CHashVerifier<CAutoFile>& s);
373+ template void CAddrMan::Unserialize (CDataStream& s);
374+ template void CAddrMan::Unserialize (CHashVerifier<CDataStream>& s);
204375
205376CAddrInfo* CAddrMan::Find (const CNetAddr& addr, int * pnId)
206377{
0 commit comments