Inside Bitcoin
Opening the selected file…
Inside Bitcoin
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.
4501pfrom.fDisconnect = true;4502return;4503}4505// We might have announced the currently-being-connected tip using a4506// compact block, which resulted in the peer sending a getblocks4507// request, which we would otherwise respond to without the new block.4508// To avoid this situation we simply verify that we are on our best4509// known chain now. This is super overkill, but we handle it better4510// for getheaders requests, and there are no known nodes which support4511// compact blocks but still use getblocks to request blocks.4512{4513std::shared_ptr<const CBlock> a_recent_block;4514{4515LOCK(m_most_recent_block_mutex);4516a_recent_block = m_most_recent_block;4517}4518BlockValidationState state;4519if (!m_chainman.ActiveChainstate().ActivateBestChain(state, a_recent_block)) {4520LogDebug(BCLog::NET, "failed to activate chain (%s)\n", state.ToString());4521}4522}4524LOCK(cs_main);4526// Find the last block the caller has in the main chain4527const CBlockIndex* pindex = m_chainman.ActiveChainstate().FindForkInGlobalIndex(locator);4529// Send the rest of the chain4530if (pindex)4531pindex = m_chainman.ActiveChain().Next(*pindex);4532int nLimit = 500;4533LogDebug(BCLog::NET, "getblocks %d to %s limit %d from peer=%d\n", (pindex ? pindex->nHeight : -1), hashStop.IsNull() ? "end" : hashStop.ToString(), nLimit, pfrom.GetId());4534for (; pindex; pindex = m_chainman.ActiveChain().Next(*pindex))4535{4536if (pindex->GetBlockHash() == hashStop)4537{4538LogDebug(BCLog::NET, " getblocks stopping at %d %s", pindex->nHeight, pindex->GetBlockHash().ToString());4539break;4540}4541// If pruning, don't inv blocks unless we have on disk and are likely to still have4542// for some reasonable time window (1 hour) that block relay might require.4543const int nPrunedBlocksLikelyToHave = MIN_BLOCKS_TO_KEEP - 3600 / m_chainparams.GetConsensus().nPowTargetSpacing;4544if (m_chainman.m_blockman.IsPruneMode() && (!(pindex->nStatus & BLOCK_HAVE_DATA) || pindex->nHeight <= m_chainman.ActiveChain().Tip()->nHeight - nPrunedBlocksLikelyToHave)) {4545LogDebug(BCLog::NET, " getblocks stopping, pruned or too old block at %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString());4546break;4547}4548WITH_LOCK(peer.m_block_inv_mutex, peer.m_blocks_for_inv_relay.push_back(pindex->GetBlockHash()));4549if (--nLimit <= 0) {4550// When this block is requested, we'll send an inv that'll4551// trigger the peer to getblocks the next batch of inventory.4552LogDebug(BCLog::NET, " getblocks stopping at limit %d %s", pindex->nHeight, pindex->GetBlockHash().ToString());4553WITH_LOCK(peer.m_block_inv_mutex, {peer.m_continuation_block = pindex->GetBlockHash();});4554break;4555}4556}4557return;4558}4560if (msg_type == NetMsgType::GETBLOCKTXN) {4561BlockTransactionsRequest req;4562vRecv >> req;4564// No legitimate reason to send indexes empty4565if (req.indexes.empty()) {4566LogDebug(BCLog::NET, "getblocktxn received with no transaction indexes, %s", pfrom.DisconnectMsg());4567pfrom.fDisconnect = true;4568return;4569}4571// Verify differential encoding invariant: indexes must be strictly increasing4572// DifferenceFormatter should guarantee this property during deserialization4573for (size_t i = 1; i < req.indexes.size(); ++i) {4574Assume(req.indexes[i] > req.indexes[i-1]);4575}4577std::shared_ptr<const CBlock> recent_block;4578{4579LOCK(m_most_recent_block_mutex);4580if (m_most_recent_block_hash == req.blockhash)4581recent_block = m_most_recent_block;4582// Unlock m_most_recent_block_mutex to avoid cs_main lock inversion4583}4584if (recent_block) {4585SendBlockTransactions(pfrom, peer, *recent_block, req);4586return;4587}4589FlatFilePos block_pos{};4590{4591LOCK(cs_main);4593const CBlockIndex* pindex = m_chainman.m_blockman.LookupBlockIndex(req.blockhash);4594if (!pindex || !(pindex->nStatus & BLOCK_HAVE_DATA)) {4595LogDebug(BCLog::NET, "Peer %d sent us a getblocktxn for a block we don't have\n", pfrom.GetId());4596return;4597}4599if (pindex->nHeight >= m_chainman.ActiveChain().Height() - MAX_BLOCKTXN_DEPTH) {4600block_pos = pindex->GetBlockPos();4601}4602}4604if (!block_pos.IsNull()) {4605CBlock block;4606const bool ret{m_chainman.m_blockman.ReadBlock(block, block_pos, req.blockhash)};4607// If height is above MAX_BLOCKTXN_DEPTH then this block cannot get4608// pruned after we release cs_main above, so this read should never fail.4609assert(ret);4611SendBlockTransactions(pfrom, peer, block, req);4612return;4613}4615// If an older block is requested (should never happen in practice,4616// but can happen in tests) send a block response instead of a4617// blocktxn response. Sending a full block response instead of a4618// small blocktxn response is preferable in the case where a peer4619// might maliciously send lots of getblocktxn requests to trigger4620// expensive disk reads, because it will require the peer to4621// actually receive all the data read from disk over the network.4622LogDebug(BCLog::NET, "Peer %d sent us a getblocktxn for a block > %i deep\n", pfrom.GetId(), MAX_BLOCKTXN_DEPTH);4623CInv inv{MSG_WITNESS_BLOCK, req.blockhash};4624WITH_LOCK(peer.m_getdata_requests_mutex, peer.m_getdata_requests.push_back(inv));4625// The message processing loop will go around again (without pausing) and we'll respond then4626return;4627}4629if (msg_type == NetMsgType::GETHEADERS) {4630CBlockLocator locator;4631uint256 hashStop;4632vRecv >> locator >> hashStop;4634if (locator.vHave.size() > MAX_LOCATOR_SZ) {4635LogDebug(BCLog::NET, "getheaders locator size %lld > %d, %s", locator.vHave.size(), MAX_LOCATOR_SZ, pfrom.DisconnectMsg());4636pfrom.fDisconnect = true;4637return;4638}4640if (m_chainman.m_blockman.LoadingBlocks()) {4641LogDebug(BCLog::NET, "Ignoring getheaders from peer=%d while importing/reindexing\n", pfrom.GetId());4642return;4643}4645LOCK(cs_main);4647// Don't serve headers from our active chain until our chainwork is at least4648// the minimum chain work. This prevents us from starting a low-work headers4649// sync that will inevitably be aborted by our peer.4650if (m_chainman.ActiveTip() == nullptr ||4651(m_chainman.ActiveTip()->nChainWork < m_chainman.MinimumChainWork() && !pfrom.HasPermission(NetPermissionFlags::Download))) {4652LogDebug(BCLog::NET, "Ignoring getheaders from peer=%d because active chain has too little work; sending empty response\n", pfrom.GetId());4653// Just respond with an empty headers message, to tell the peer to4654// go away but not treat us as unresponsive.4655MakeAndPushMessage(pfrom, NetMsgType::HEADERS, std::vector<CBlockHeader>());4656return;4657}4659CNodeState *nodestate = State(pfrom.GetId());4660const CBlockIndex* pindex = nullptr;4661if (locator.IsNull())4662{4663// If locator is null, return the hashStop block4664pindex = m_chainman.m_blockman.LookupBlockIndex(hashStop);4665if (!pindex) {4666return;4667}4668if (!BlockRequestAllowed(*pindex)) {4669LogDebug(BCLog::NET, "%s: ignoring request from peer=%i for old block header that isn't in the main chain\n", __func__, pfrom.GetId());4670return;4671}4672}4673else4674{4675// Find the last block the caller has in the main chain4676pindex = m_chainman.ActiveChainstate().FindForkInGlobalIndex(locator);4677if (pindex)4678pindex = m_chainman.ActiveChain().Next(*pindex);4679}4681// we must use CBlocks, as CBlockHeaders won't include the 0x00 nTx count at the end4682std::vector<CBlock> vHeaders;4683int nLimit = m_opts.max_headers_result;4684LogDebug(BCLog::NET, "getheaders %d to %s from peer=%d\n", (pindex ? pindex->nHeight : -1), hashStop.IsNull() ? "end" : hashStop.ToString(), pfrom.GetId());4685for (; pindex; pindex = m_chainman.ActiveChain().Next(*pindex))4686{4687vHeaders.emplace_back(pindex->GetBlockHeader());4688if (--nLimit <= 0 || pindex->GetBlockHash() == hashStop)4689break;4690}4691// pindex can be nullptr either if we sent m_chainman.ActiveChain().Tip() OR4692// if our peer has m_chainman.ActiveChain().Tip() (and thus we are sending an empty4693// headers message). In both cases it's safe to update4694// pindexBestHeaderSent to be our tip.4695//4696// It is important that we simply reset the BestHeaderSent value here,4697// and not max(BestHeaderSent, newHeaderSent). We might have announced4698// the currently-being-connected tip using a compact block, which4699// resulted in the peer sending a headers request, which we respond to4700// without the new block. By resetting the BestHeaderSent, we ensure we4701// will re-announce the new block via headers (or compact blocks again)4702// in the SendMessages logic.4703nodestate->pindexBestHeaderSent = pindex ? pindex : m_chainman.ActiveChain().Tip();4704MakeAndPushMessage(pfrom, NetMsgType::HEADERS, TX_WITH_WITNESS(vHeaders));4705return;4706}4708if (msg_type == NetMsgType::TX) {4709if (RejectIncomingTxs(pfrom)) {4710LogDebug(BCLog::NET, "transaction sent in violation of protocol, %s", pfrom.DisconnectMsg());4711pfrom.fDisconnect = true;4712return;4713}4715// Stop processing the transaction early if we are still in IBD since we don't4716// have enough information to validate it yet. Sending unsolicited transactions4717// is not considered a protocol violation, so don't punish the peer.4718if (m_chainman.IsInitialBlockDownload()) return;4720CTransactionRef ptx;4721vRecv >> TX_WITH_WITNESS(ptx);4723const Txid& txid = ptx->GetHash();4724const Wtxid& wtxid = ptx->GetWitnessHash();4726const uint256& hash = peer.m_wtxid_relay ? wtxid.ToUint256() : txid.ToUint256();4727AddKnownTx(peer, hash);4729if (m_tx_for_private_broadcast.MarkResolved(ptx)) {4730LogDebug(BCLog::PRIVBROADCAST, "Received our privately broadcast transaction (txid=%s) from the "4731"network from %s",4732txid.ToString(), pfrom.LogPeer());4733}4735LOCK2(cs_main, m_tx_download_mutex);4737const auto& [should_validate, package_to_validate] = m_txdownloadman.ReceivedTx(pfrom.GetId(), ptx);4738if (!should_validate) {4739if (pfrom.HasPermission(NetPermissionFlags::ForceRelay)) {4740// Always relay transactions received from peers with forcerelay4741// permission, even if they were already in the mempool, allowing4742// the node to function as a gateway for nodes hidden behind it.4743if (!m_mempool.exists(txid)) {4744LogInfo("Not relaying non-mempool transaction %s (wtxid=%s) from forcerelay peer=%d\n",4745txid.ToString(), wtxid.ToString(), pfrom.GetId());4746} else {4747LogInfo("Force relaying tx %s (wtxid=%s) from peer=%d\n",4748txid.ToString(), wtxid.ToString(), pfrom.GetId());4749InitiateTxBroadcastToAll(wtxid);4750}