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.
501AddrInfo& infoOld = mapInfo[nIdEvict];503// Remove the to-be-evicted item from the tried set.504infoOld.fInTried = false;505vvTried[nKBucket][nKBucketPos] = -1;506nTried--;507m_network_counts[infoOld.GetNetwork()].n_tried--;509// find which new bucket it belongs to510int nUBucket = infoOld.GetNewBucket(nKey, m_netgroupman);511int nUBucketPos = infoOld.GetBucketPosition(nKey, true, nUBucket);512ClearNew(nUBucket, nUBucketPos);513assert(vvNew[nUBucket][nUBucketPos] == -1);515// Enter it into the new set again.516infoOld.nRefCount = 1;517vvNew[nUBucket][nUBucketPos] = nIdEvict;518nNew++;519m_network_counts[infoOld.GetNetwork()].n_new++;520LogDebug(BCLog::ADDRMAN, "Moved %s from tried[%i][%i] to new[%i][%i] to make space\n",521infoOld.ToStringAddrPort(), nKBucket, nKBucketPos, nUBucket, nUBucketPos);522}523assert(vvTried[nKBucket][nKBucketPos] == -1);525vvTried[nKBucket][nKBucketPos] = nId;526nTried++;527info.fInTried = true;528m_network_counts[info.GetNetwork()].n_tried++;529}531bool AddrManImpl::AddSingle(const CAddress& addr, const CNetAddr& source, std::chrono::seconds time_penalty)532{533AssertLockHeld(cs);535if (!addr.IsRoutable())536return false;538nid_type nId;539AddrInfo* pinfo = Find(addr, &nId);541// Do not set a penalty for a source's self-announcement542if (addr == source) {543time_penalty = 0s;544}546if (pinfo) {547// periodically update nTime548const bool currently_online{NodeClock::now() - addr.nTime < 24h};549const auto update_interval{currently_online ? 1h : 24h};550if (pinfo->nTime < addr.nTime - update_interval - time_penalty) {551pinfo->nTime = std::max(NodeSeconds{0s}, addr.nTime - time_penalty);552}554// add services555pinfo->nServices = ServiceFlags(pinfo->nServices | addr.nServices);557// do not update if no new information is present558if (addr.nTime <= pinfo->nTime) {559return false;560}562// do not update if the entry was already in the "tried" table563if (pinfo->fInTried)564return false;566// do not update if the max reference count is reached567if (pinfo->nRefCount == ADDRMAN_NEW_BUCKETS_PER_ADDRESS)568return false;570// stochastic test: previous nRefCount == N: 2^N times harder to increase it571if (pinfo->nRefCount > 0) {572const int nFactor{1 << pinfo->nRefCount};573if (insecure_rand.randrange(nFactor) != 0) return false;574}575} else {576pinfo = Create(addr, source, &nId);577pinfo->nTime = std::max(NodeSeconds{0s}, pinfo->nTime - time_penalty);578}580int nUBucket = pinfo->GetNewBucket(nKey, source, m_netgroupman);581int nUBucketPos = pinfo->GetBucketPosition(nKey, true, nUBucket);582bool fInsert = vvNew[nUBucket][nUBucketPos] == -1;583if (vvNew[nUBucket][nUBucketPos] != nId) {584if (!fInsert) {585AddrInfo& infoExisting = mapInfo[vvNew[nUBucket][nUBucketPos]];586if (infoExisting.IsTerrible() || (infoExisting.nRefCount > 1 && pinfo->nRefCount == 0)) {587// Overwrite the existing new table entry.588fInsert = true;589}590}591if (fInsert) {592ClearNew(nUBucket, nUBucketPos);593pinfo->nRefCount++;594vvNew[nUBucket][nUBucketPos] = nId;595const auto mapped_as{m_netgroupman.GetMappedAS(addr)};596LogDebug(BCLog::ADDRMAN, "Added %s%s to new[%i][%i]\n",597addr.ToStringAddrPort(), (mapped_as ? strprintf(" mapped to AS%i", mapped_as) : ""), nUBucket, nUBucketPos);598} else {599if (pinfo->nRefCount == 0) {600Delete(nId);601}602}603}604return fInsert;605}607bool AddrManImpl::Good_(const CService& addr, bool test_before_evict, NodeSeconds time)608{609AssertLockHeld(cs);611nid_type nId;613m_last_good = time;615AddrInfo* pinfo = Find(addr, &nId);617// if not found, bail out618if (!pinfo) return false;620AddrInfo& info = *pinfo;622// update info623info.m_last_success = time;624info.m_last_try = time;625info.nAttempts = 0;626// nTime is not updated here, to avoid leaking information about627// currently-connected peers.629// if it is already in the tried set, don't do anything else630if (info.fInTried) return false;632// if it is not in new, something bad happened633if (!Assume(info.nRefCount > 0)) return false;636// which tried bucket to move the entry to637int tried_bucket = info.GetTriedBucket(nKey, m_netgroupman);638int tried_bucket_pos = info.GetBucketPosition(nKey, false, tried_bucket);640// Will moving this address into tried evict another entry?641if (test_before_evict && (vvTried[tried_bucket][tried_bucket_pos] != -1)) {642if (m_tried_collisions.size() < ADDRMAN_SET_TRIED_COLLISION_SIZE) {643m_tried_collisions.insert(nId);644}645// Output the entry we'd be colliding with, for debugging purposes646auto colliding_entry = mapInfo.find(vvTried[tried_bucket][tried_bucket_pos]);647LogDebug(BCLog::ADDRMAN, "Collision with %s while attempting to move %s to tried table. Collisions=%d",648colliding_entry != mapInfo.end() ? colliding_entry->second.ToStringAddrPort() : "<unknown-addr>",649addr.ToStringAddrPort(),650m_tried_collisions.size());651return false;652} else {653// move nId to the tried tables654MakeTried(info, nId);655const auto mapped_as{m_netgroupman.GetMappedAS(addr)};656LogDebug(BCLog::ADDRMAN, "Moved %s%s to tried[%i][%i]\n",657addr.ToStringAddrPort(), (mapped_as ? strprintf(" mapped to AS%i", mapped_as) : ""), tried_bucket, tried_bucket_pos);658return true;659}660}662bool AddrManImpl::Add_(const std::vector<CAddress>& vAddr, const CNetAddr& source, std::chrono::seconds time_penalty)663{664int added{0};665for (std::vector<CAddress>::const_iterator it = vAddr.begin(); it != vAddr.end(); it++) {666added += AddSingle(*it, source, time_penalty) ? 1 : 0;667}668if (added > 0) {669LogDebug(BCLog::ADDRMAN, "Added %i addresses (of %i) from %s: %i tried, %i new\n", added, vAddr.size(), source.ToStringAddr(), nTried, nNew);670}671return added > 0;672}674void AddrManImpl::Attempt_(const CService& addr, bool fCountFailure, NodeSeconds time)675{676AssertLockHeld(cs);678AddrInfo* pinfo = Find(addr);680// if not found, bail out681if (!pinfo)682return;684AddrInfo& info = *pinfo;686// update info687info.m_last_try = time;688if (fCountFailure && info.m_last_count_attempt < m_last_good) {689info.m_last_count_attempt = time;690info.nAttempts++;691}692}694std::pair<CAddress, NodeSeconds> AddrManImpl::Select_(bool new_only, const std::unordered_set<Network>& networks) const695{696AssertLockHeld(cs);698if (vRandom.empty()) return {};700size_t new_count = nNew;701size_t tried_count = nTried;703if (!networks.empty()) {704new_count = 0;705tried_count = 0;706for (auto& network : networks) {707auto it = m_network_counts.find(network);708if (it == m_network_counts.end()) {709continue;710}711auto counts = it->second;712new_count += counts.n_new;713tried_count += counts.n_tried;714}715}717if (new_only && new_count == 0) return {};718if (new_count + tried_count == 0) return {};720// Decide if we are going to search the new or tried table721// If either option is viable, use a 50% chance to choose722bool search_tried;723if (new_only || tried_count == 0) {724search_tried = false;725} else if (new_count == 0) {726search_tried = true;727} else {728search_tried = insecure_rand.randbool();729}731const int bucket_count{search_tried ? ADDRMAN_TRIED_BUCKET_COUNT : ADDRMAN_NEW_BUCKET_COUNT};733// Loop through the addrman table until we find an appropriate entry734double chance_factor = 1.0;735while (1) {736// Pick a bucket, and an initial position in that bucket.737int bucket = insecure_rand.randrange(bucket_count);738int initial_position = insecure_rand.randrange(ADDRMAN_BUCKET_SIZE);740// Iterate over the positions of that bucket, starting at the initial one,741// and looping around.742int i, position;743nid_type node_id;744for (i = 0; i < ADDRMAN_BUCKET_SIZE; ++i) {745position = (initial_position + i) % ADDRMAN_BUCKET_SIZE;746node_id = GetEntry(search_tried, bucket, position);747if (node_id != -1) {748if (!networks.empty()) {749const auto it{mapInfo.find(node_id)};750if (Assume(it != mapInfo.end()) && networks.contains(it->second.GetNetwork())) break;