Inside Bitcoin
Opening the selected file…
Opening the selected file…
The code behind the network, with room to understand it.
Release published 2026-10-28 · Snapshot fetched 2026-11-01. Latest stable release at fetch time; development branches and Git history are excluded. Only the selected code window and visible folders load here.
251strprintf("Corrupt AddrMan serialization: nNew=%d, should be in [0, %d]",252nNew,253ADDRMAN_NEW_BUCKET_COUNT * ADDRMAN_BUCKET_SIZE));254}256if (nTried > ADDRMAN_TRIED_BUCKET_COUNT * ADDRMAN_BUCKET_SIZE || nTried < 0) {257throw std::ios_base::failure(258strprintf("Corrupt AddrMan serialization: nTried=%d, should be in [0, %d]",259nTried,260ADDRMAN_TRIED_BUCKET_COUNT * ADDRMAN_BUCKET_SIZE));261}263// Deserialize entries from the new table.264for (int n = 0; n < nNew; n++) {265AddrInfo& info = mapInfo[n];266s >> info;267mapAddr[info] = n;268info.nRandomPos = vRandom.size();269vRandom.push_back(n);270m_network_counts[info.GetNetwork()].n_new++;271}272nIdCount = nNew;274// Deserialize entries from the tried table.275int nLost = 0;276for (int n = 0; n < nTried; n++) {277AddrInfo info;278s >> info;279int nKBucket = info.GetTriedBucket(nKey, m_netgroupman);280int nKBucketPos = info.GetBucketPosition(nKey, false, nKBucket);281if (info.IsValid()282&& vvTried[nKBucket][nKBucketPos] == -1) {283info.nRandomPos = vRandom.size();284info.fInTried = true;285vRandom.push_back(nIdCount);286mapInfo[nIdCount] = info;287mapAddr[info] = nIdCount;288vvTried[nKBucket][nKBucketPos] = nIdCount;289nIdCount++;290m_network_counts[info.GetNetwork()].n_tried++;291} else {292nLost++;293}294}295nTried -= nLost;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.300std::vector<std::pair<int, int>> bucket_entries;302for (int bucket = 0; bucket < nUBuckets; ++bucket) {303int num_entries{0};304s >> num_entries;305for (int n = 0; n < num_entries; ++n) {306int entry_index{0};307s >> entry_index;308if (entry_index >= 0 && entry_index < nNew) {309bucket_entries.emplace_back(bucket, entry_index);310}311}312}314// If the bucket count and asmap version haven't changed, then attempt315// to restore the entries to the buckets/positions they were in before316// serialization.317uint256 supplied_asmap_version{m_netgroupman.GetAsmapVersion()};318uint256 serialized_asmap_version;319if (format >= Format::V2_ASMAP) {320s >> serialized_asmap_version;321}322const bool restore_bucketing{nUBuckets == ADDRMAN_NEW_BUCKET_COUNT &&323serialized_asmap_version == supplied_asmap_version};325if (!restore_bucketing) {326LogDebug(BCLog::ADDRMAN, "Bucketing method was updated, re-bucketing addrman entries from disk\n");327}329for (auto bucket_entry : bucket_entries) {330int bucket{bucket_entry.first};331const int entry_index{bucket_entry.second};332AddrInfo& info = mapInfo[entry_index];334// Don't store the entry in the new bucket if it's not a valid address for our addrman335if (!info.IsValid()) continue;337// The entry shouldn't appear in more than338// ADDRMAN_NEW_BUCKETS_PER_ADDRESS. If it has already, just skip339// this bucket_entry.340if (info.nRefCount >= ADDRMAN_NEW_BUCKETS_PER_ADDRESS) continue;342int bucket_position = info.GetBucketPosition(nKey, true, bucket);343if (restore_bucketing && vvNew[bucket][bucket_position] == -1) {344// Bucketing has not changed, using existing bucket positions for the new table345vvNew[bucket][bucket_position] = entry_index;346++info.nRefCount;347} 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.350bucket = info.GetNewBucket(nKey, m_netgroupman);351bucket_position = info.GetBucketPosition(nKey, true, bucket);352if (vvNew[bucket][bucket_position] == -1) {353vvNew[bucket][bucket_position] = entry_index;354++info.nRefCount;355}356}357}359// Prune new entries with refcount 0 (as a result of collisions or invalid address).360int nLostUnk = 0;361for (auto it = mapInfo.cbegin(); it != mapInfo.cend(); ) {362if (it->second.fInTried == false && it->second.nRefCount == 0) {363const auto itCopy = it++;364Delete(itCopy->first);365++nLostUnk;366} else {367++it;368}369}370if (nLost + nLostUnk > 0) {371LogDebug(BCLog::ADDRMAN, "addrman lost %i new and %i tried addresses due to collisions or invalid addresses\n", nLostUnk, nLost);372}374const int check_code{CheckAddrman()};375if (check_code != 0) {376throw std::ios_base::failure(strprintf(377"Corrupt data. Consistency check failed with code %s",378check_code));379}380}382AddrInfo* AddrManImpl::Find(const CService& addr, nid_type* pnId)383{384AssertLockHeld(cs);386const auto it = mapAddr.find(addr);387if (it == mapAddr.end())388return nullptr;389if (pnId)390*pnId = (*it).second;391const auto it2 = mapInfo.find((*it).second);392if (it2 != mapInfo.end())393return &(*it2).second;394return nullptr;395}397AddrInfo* AddrManImpl::Create(const CAddress& addr, const CNetAddr& addrSource, nid_type* pnId)398{399AssertLockHeld(cs);401nid_type nId = nIdCount++;402mapInfo[nId] = AddrInfo(addr, addrSource);403mapAddr[addr] = nId;404mapInfo[nId].nRandomPos = vRandom.size();405vRandom.push_back(nId);406nNew++;407m_network_counts[addr.GetNetwork()].n_new++;408if (pnId)409*pnId = nId;410return &mapInfo[nId];411}413void AddrManImpl::SwapRandom(unsigned int nRndPos1, unsigned int nRndPos2) const414{415AssertLockHeld(cs);417if (nRndPos1 == nRndPos2)418return;420assert(nRndPos1 < vRandom.size() && nRndPos2 < vRandom.size());422nid_type nId1 = vRandom[nRndPos1];423nid_type nId2 = vRandom[nRndPos2];425const auto it_1{mapInfo.find(nId1)};426const auto it_2{mapInfo.find(nId2)};427assert(it_1 != mapInfo.end());428assert(it_2 != mapInfo.end());430it_1->second.nRandomPos = nRndPos2;431it_2->second.nRandomPos = nRndPos1;433vRandom[nRndPos1] = nId2;434vRandom[nRndPos2] = nId1;435}437void AddrManImpl::Delete(nid_type nId)438{439AssertLockHeld(cs);441assert(mapInfo.contains(nId));442AddrInfo& info = mapInfo[nId];443assert(!info.fInTried);444assert(info.nRefCount == 0);446SwapRandom(info.nRandomPos, vRandom.size() - 1);447m_network_counts[info.GetNetwork()].n_new--;448vRandom.pop_back();449mapAddr.erase(info);450mapInfo.erase(nId);451nNew--;452}454void AddrManImpl::ClearNew(int nUBucket, int nUBucketPos)455{456AssertLockHeld(cs);458// if there is an entry in the specified bucket, delete it.459if (vvNew[nUBucket][nUBucketPos] != -1) {460nid_type nIdDelete = vvNew[nUBucket][nUBucketPos];461AddrInfo& infoDelete = mapInfo[nIdDelete];462assert(infoDelete.nRefCount > 0);463infoDelete.nRefCount--;464vvNew[nUBucket][nUBucketPos] = -1;465LogDebug(BCLog::ADDRMAN, "Removed %s from new[%i][%i]\n", infoDelete.ToStringAddrPort(), nUBucket, nUBucketPos);466if (infoDelete.nRefCount == 0) {467Delete(nIdDelete);468}469}470}472void AddrManImpl::MakeTried(AddrInfo& info, nid_type nId)473{474AssertLockHeld(cs);476// remove the entry from all new buckets477const int start_bucket{info.GetNewBucket(nKey, m_netgroupman)};478for (int n = 0; n < ADDRMAN_NEW_BUCKET_COUNT; ++n) {479const int bucket{(start_bucket + n) % ADDRMAN_NEW_BUCKET_COUNT};480const int pos{info.GetBucketPosition(nKey, true, bucket)};481if (vvNew[bucket][pos] == nId) {482vvNew[bucket][pos] = -1;483info.nRefCount--;484if (info.nRefCount == 0) break;485}486}487nNew--;488m_network_counts[info.GetNetwork()].n_new--;490assert(info.nRefCount == 0);492// which tried bucket to move the entry to493int nKBucket = info.GetTriedBucket(nKey, m_netgroupman);494int nKBucketPos = info.GetBucketPosition(nKey, false, nKBucket);496// first make space to add it (the existing tried entry there is moved to new, deleting whatever is there).497if (vvTried[nKBucket][nKBucketPos] != -1) {498// find an item to evict499nid_type nIdEvict = vvTried[nKBucket][nKBucketPos];500assert(mapInfo.contains(nIdEvict));