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.
1// Copyright (c) 2016-present The Bitcoin Core developers2// Distributed under the MIT software license, see the accompanying3// file COPYING or http://www.opensource.org/licenses/mit-license.php.5#include <blockencodings.h>6#include <chainparams.h>7#include <common/system.h>8#include <consensus/consensus.h>9#include <consensus/validation.h>10#include <crypto/sha256.h>11#include <crypto/siphash.h>12#include <random.h>13#include <streams.h>14#include <txmempool.h>15#include <util/log.h>16#include <validation.h>18#include <unordered_map>20CBlockHeaderAndShortTxIDs::CBlockHeaderAndShortTxIDs(const CBlock& block, uint64_t nonce)21: nonce(nonce),22shorttxids(block.vtx.size() - 1),23prefilledtxn(1),24header(block)25{26FillShortTxIDSelector();27// TODO: Use our mempool prior to block acceptance to predictively fill more than just the coinbase28prefilledtxn[0] = {0, block.vtx[0]};29for (size_t i = 1; i < block.vtx.size(); i++) {30const CTransaction& tx = *block.vtx[i];31shorttxids[i - 1] = GetShortID(tx.GetWitnessHash());32}33}35void CBlockHeaderAndShortTxIDs::FillShortTxIDSelector() const36{37DataStream stream{};38stream << header << nonce;39CSHA256 hasher;40hasher.Write((unsigned char*)&(*stream.begin()), stream.end() - stream.begin());41uint256 shorttxidhash;42hasher.Finalize(shorttxidhash.begin());43m_hasher.emplace(shorttxidhash.GetUint64(0), shorttxidhash.GetUint64(1));44}46uint64_t CBlockHeaderAndShortTxIDs::GetShortID(const Wtxid& wtxid) const47{48static_assert(SHORTTXIDS_LENGTH == 6, "shorttxids calculation assumes 6-byte shorttxids");49return (*Assert(m_hasher))(wtxid.ToUint256()) & 0xffffffffffffL;50}52/* Reconstructing a compact block is in the hot-path for block relay,53* so we want to do it as quickly as possible. Because this often54* involves iterating over the entire mempool, we put all the data we55* need (ie the wtxid and a reference to the actual transaction data)56* in a vector and iterate over the vector directly. This allows optimal57* CPU caching behaviour, at a cost of only 40 bytes per transaction.58*/59ReadStatus PartiallyDownloadedBlock::InitData(const CBlockHeaderAndShortTxIDs& cmpctblock, const std::vector<std::pair<Wtxid, CTransactionRef>>& extra_txn)60{61LogDebug(BCLog::CMPCTBLOCK, "Initializing PartiallyDownloadedBlock for block %s using a cmpctblock of %u bytes\n", cmpctblock.header.GetHash().ToString(), GetSerializeSize(cmpctblock));62if (cmpctblock.header.IsNull() || (cmpctblock.shorttxids.empty() && cmpctblock.prefilledtxn.empty()))63return READ_STATUS_INVALID;64if (cmpctblock.shorttxids.size() + cmpctblock.prefilledtxn.size() > MAX_BLOCK_WEIGHT / MIN_SERIALIZABLE_TRANSACTION_WEIGHT)65return READ_STATUS_INVALID;67if (!header.IsNull() || !txn_available.empty()) return READ_STATUS_INVALID;69header = cmpctblock.header;70txn_available.resize(cmpctblock.BlockTxCount());72int32_t lastprefilledindex = -1;73for (size_t i = 0; i < cmpctblock.prefilledtxn.size(); i++) {74if (cmpctblock.prefilledtxn[i].tx->IsNull())75return READ_STATUS_INVALID;77lastprefilledindex += cmpctblock.prefilledtxn[i].index + 1; //index is a uint16_t, so can't overflow here78if (lastprefilledindex > std::numeric_limits<uint16_t>::max())79return READ_STATUS_INVALID;80if ((uint32_t)lastprefilledindex > cmpctblock.shorttxids.size() + i) {81// If we are inserting a tx at an index greater than our full list of shorttxids82// plus the number of prefilled txn we've inserted, then we have txn for which we83// have neither a prefilled txn or a shorttxid!84return READ_STATUS_INVALID;85}86txn_available[lastprefilledindex] = cmpctblock.prefilledtxn[i].tx;87}88prefilled_count = cmpctblock.prefilledtxn.size();90// Calculate map of txids -> positions and check mempool to see what we have (or don't)91// Because well-formed cmpctblock messages will have a (relatively) uniform distribution92// of short IDs, any highly-uneven distribution of elements can be safely treated as a93// READ_STATUS_FAILED.94std::unordered_map<uint64_t, uint16_t> shorttxids(cmpctblock.shorttxids.size());95uint16_t index_offset = 0;96for (size_t i = 0; i < cmpctblock.shorttxids.size(); i++) {97while (txn_available[i + index_offset])98index_offset++;99shorttxids[cmpctblock.shorttxids[i]] = i + index_offset;100// To determine the chance that the number of entries in a bucket exceeds N,101// we use the fact that the number of elements in a single bucket is102// binomially distributed (with n = the number of shorttxids S, and p =103// 1 / the number of buckets), that in the worst case the number of buckets is104// equal to S (due to std::unordered_map having a default load factor of 1.0),105// and that the chance for any bucket to exceed N elements is at most106// buckets * (the chance that any given bucket is above N elements).107// Thus: P(max_elements_per_bucket > N) <= S * (1 - cdf(binomial(n=S,p=1/S), N)).108// If we assume blocks of up to 16000, allowing 12 elements per bucket should109// only fail once per ~1 million block transfers (per peer and connection).110if (shorttxids.bucket_size(shorttxids.bucket(cmpctblock.shorttxids[i])) > 12)111return READ_STATUS_FAILED;112}113if (shorttxids.size() != cmpctblock.shorttxids.size())114return READ_STATUS_FAILED; // Short ID collision116enum class TxSource : uint8_t { NONE, MEMPOOL, EXTRA, COLLIDED };117std::vector<TxSource> tx_source(txn_available.size(), TxSource::NONE);118{119LOCK(pool->cs);120for (const auto& [wtxid, txit] : pool->txns_randomized) {121uint64_t shortid = cmpctblock.GetShortID(wtxid);122std::unordered_map<uint64_t, uint16_t>::iterator idit = shorttxids.find(shortid);123if (idit != shorttxids.end()) {124if (tx_source[idit->second] == TxSource::NONE) {125txn_available[idit->second] = txit->GetSharedTx();126tx_source[idit->second] = TxSource::MEMPOOL;127mempool_count++;128} else if (tx_source[idit->second] != TxSource::COLLIDED) {129// If we find two mempool txn that match the short id, just request it.130// This should be rare enough that the extra bandwidth doesn't matter,131// but eating a round-trip due to FillBlock failure would be annoying132txn_available[idit->second].reset();133mempool_count--;134tx_source[idit->second] = TxSource::COLLIDED;135}136}137// Though ideally we'd continue scanning for the two-txn-match-shortid case,138// the performance win of an early exit here is too good to pass up and worth139// the extra risk.140if (mempool_count == shorttxids.size())141break;142}143}145for (size_t i = 0; i < extra_txn.size(); i++) {146uint64_t shortid = cmpctblock.GetShortID(extra_txn[i].first);147std::unordered_map<uint64_t, uint16_t>::iterator idit = shorttxids.find(shortid);148if (idit != shorttxids.end()) {149if (tx_source[idit->second] == TxSource::NONE) {150txn_available[idit->second] = extra_txn[i].second;151tx_source[idit->second] = TxSource::EXTRA;152mempool_count++;153extra_count++;154} else if (tx_source[idit->second] != TxSource::COLLIDED &&155txn_available[idit->second]->GetWitnessHash() != extra_txn[i].second->GetWitnessHash()) {156// If we find two mempool/extra txn that match the short id, just157// request it.158// This should be rare enough that the extra bandwidth doesn't matter,159// but eating a round-trip due to FillBlock failure would be annoying160// Note that we don't want duplication between extra_txn and mempool to161// trigger this case, so we compare witness hashes first162txn_available[idit->second].reset();163mempool_count--;164extra_count -= (tx_source[idit->second] == TxSource::EXTRA);165tx_source[idit->second] = TxSource::COLLIDED;166}167}168// Though ideally we'd continue scanning for the two-txn-match-shortid case,169// the performance win of an early exit here is too good to pass up and worth170// the extra risk.171if (mempool_count == shorttxids.size())172break;173}175LogDebug(BCLog::CMPCTBLOCK, "Initialized PartiallyDownloadedBlock for block %s using a cmpctblock of %u bytes\n", cmpctblock.header.GetHash().ToString(), GetSerializeSize(cmpctblock));177return READ_STATUS_OK;178}180bool PartiallyDownloadedBlock::IsTxAvailable(size_t index) const181{182if (header.IsNull()) return false;184assert(index < txn_available.size());185return txn_available[index] != nullptr;186}188ReadStatus PartiallyDownloadedBlock::FillBlock(CBlock& block, const std::vector<CTransactionRef>& vtx_missing, bool segwit_active)189{190if (header.IsNull()) return READ_STATUS_INVALID;192block = header;193block.vtx.resize(txn_available.size());195size_t tx_missing_offset = 0;196for (size_t i = 0; i < txn_available.size(); i++) {197if (!txn_available[i]) {198if (tx_missing_offset >= vtx_missing.size()) {199return READ_STATUS_INVALID;200}201block.vtx[i] = vtx_missing[tx_missing_offset++];202} else {203block.vtx[i] = std::move(txn_available[i]);204}205}207// Make sure we can't call FillBlock again.208header.SetNull();209txn_available.clear();211if (vtx_missing.size() != tx_missing_offset) {212return READ_STATUS_INVALID;213}215// Check for possible mutations early now that we have a seemingly good block216IsBlockMutatedFn check_mutated{m_check_block_mutated_mock ? m_check_block_mutated_mock : IsBlockMutated};217if (check_mutated(/*block=*/block, /*check_witness_root=*/segwit_active)) {218return READ_STATUS_FAILED; // Possible Short ID collision219}221if (util::log::ShouldDebugLog(BCLog::CMPCTBLOCK)) {222const uint256 hash{block.GetHash()};223uint32_t tx_missing_size{0};224for (const auto& tx : vtx_missing) tx_missing_size += tx->ComputeTotalSize();225LogDebug(BCLog::CMPCTBLOCK, "Successfully reconstructed block %s with %u txn prefilled, %u txn from mempool (incl at least %u from extra pool) and %u txn (%u bytes) requested\n", hash.ToString(), prefilled_count, mempool_count, extra_count, vtx_missing.size(), tx_missing_size);226if (vtx_missing.size() < 5) {227for (const auto& tx : vtx_missing) {228LogDebug(BCLog::CMPCTBLOCK, "Reconstructed block %s required tx %s\n", hash.ToString(), tx->GetHash().ToString());229}230}231}233return READ_STATUS_OK;234}