cmpctblock: Improve logging of `cmpctblock` message reconstruction statistics [part of prefill series] #35724

pull davidgumberg wants to merge 3 commits into bitcoin:master from davidgumberg:2026-07-14-cb-logging changing 4 files +109 −14
  1. davidgumberg commented at 9:33 PM on July 14, 2026: contributor

    Split from #35558, this makes it easier to observe compact block reconstruction performance by:

    • At debuglevel=trace logging all missing TX's in a CMPCTBLOCK, and all missing TX's requested from us.
    • Logging the sizes and counts of tx's used from the prefill, mempool, and extrapool.
    • Logging the sizes and sources of redundant transactions in the prefill section of received CMPCTBLOCKs.
  2. DrahtBot renamed this:
    cmpctblock: Improve logging of `cmpctblock` message reconstruction statistics [part of prefill series]
    cmpctblock: Improve logging of `cmpctblock` message reconstruction statistics [part of prefill series]
    on Jul 14, 2026
  3. DrahtBot commented at 9:33 PM on July 14, 2026: contributor

    <!--e57a25ab6845829454e8d69fc972939a-->

    The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

    <!--006a51241073e994b41acfe9ec718e94-->

    Code Coverage & Benchmarks

    For details see: https://corecheck.dev/bitcoin/bitcoin/pulls/35724.

    <!--021abf342d371248e50ceaed478a90ca-->

    Reviews

    See the guideline and AI policy for information on the review process. A summary of reviews will appear here.

    <!--174a7506f384e20aa4161008e828411d-->

    Conflicts

    Reviewers, this pull request conflicts with the following ones:

    • #35558 (p2p: Prefill compact blocks by davidgumberg)

    If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first.

    <!--5faf32d7da4f0f540f40219e4f7537a3-->

  4. in src/blockencodings.cpp:164 in 35b79db9fd
     159 | @@ -161,15 +160,14 @@ ReadStatus PartiallyDownloadedBlock::InitData(const CBlockHeaderAndShortTxIDs& c
     160 |                  if (txn_available[idit->second] &&
     161 |                          txn_available[idit->second]->GetWitnessHash() != extra_txn[i].second->GetWitnessHash()) {
     162 |                      txn_available[idit->second].reset();
     163 | -                    mempool_count--;
    


    w0xlt commented at 9:46 PM on July 15, 2026:

    If I understand correctly, removing this decrement assumes that the existing candidate came from the extrapool. However, it may have been inserted during the earlier mempool scan. Would it make sense to track the candidate’s source and decrement the corresponding counter?


    l0rinc commented at 9:52 PM on July 15, 2026:

    Seems tangentially related to https://github.com/bitcoin/bitcoin/pull/35727


    davidgumberg commented at 6:27 PM on July 16, 2026:

    yeah, thanks for catching @w0xlt, I'll fix this after #35727, as that will make this change easier to make correct, marking this pr as draft for now


    davidgumberg commented at 5:26 PM on August 25, 2026:

    marking as resolvesed since #35727 was merged and I've updated this PR based on that.

  5. davidgumberg marked this as a draft on Jul 16, 2026
  6. DrahtBot added the label Needs rebase on Jul 21, 2026
  7. davidgumberg force-pushed on Aug 4, 2026
  8. davidgumberg marked this as ready for review on Aug 4, 2026
  9. davidgumberg commented at 10:45 PM on August 4, 2026: contributor

    Reworked after #35727 merged, this is ready for review.

  10. DrahtBot removed the label Needs rebase on Aug 4, 2026
  11. in src/net_processing.cpp:2853 in 43bc34af72
    2847 | @@ -2848,6 +2848,11 @@ void PeerManagerImpl::SendBlockTransactions(CNode& pfrom, Peer& peer, const CBlo
    2848 |          uint32_t tx_requested_size{0};
    2849 |          for (const auto& tx : resp.txn) tx_requested_size += tx->ComputeTotalSize();
    2850 |          LogDebug(BCLog::CMPCTBLOCK, "%s sent us a GETBLOCKTXN for block %s, sending a BLOCKTXN with %u txns. (%u bytes)", pfrom.LogPeer(), block.GetHash().ToString(), resp.txn.size(), tx_requested_size);
    2851 | +        if (util::log::ShouldTraceLog(BCLog::CMPCTBLOCK)) {
    2852 | +            for(const auto& txn : resp.txn) {
    2853 | +                LogDebug(BCLog::CMPCTBLOCK, "    - txid: %s", txn->GetHash().ToString());
    


    w0xlt commented at 11:22 PM on August 19, 2026:

    The code has contradictory logging levels:

      if (ShouldTraceLog(...)) {
          LogDebug(...);
      }
    

    Suggestion:

    diff --git a/src/net_processing.cpp b/src/net_processing.cpp
    index 1512a10c58..0117616314 100644
    --- a/src/net_processing.cpp
    +++ b/src/net_processing.cpp
    @@ -2850,7 +2850,7 @@ void PeerManagerImpl::SendBlockTransactions(CNode& pfrom, Peer& peer, const CBlo
             LogDebug(BCLog::CMPCTBLOCK, "%s sent us a GETBLOCKTXN for block %s, sending a BLOCKTXN with %u txns. (%u bytes)", pfrom.LogPeer(), block.GetHash().ToString(), resp.txn.size(), tx_requested_size);
             if (util::log::ShouldTraceLog(BCLog::CMPCTBLOCK)) {
                 for(const auto& txn : resp.txn) {
    -                LogDebug(BCLog::CMPCTBLOCK, "    - txid: %s", txn->GetHash().ToString());
    +                LogTrace(BCLog::CMPCTBLOCK, "    - txid: %s", txn->GetHash().ToString());
                 }
             }
         }
    

    0xB10C commented at 10:29 AM on August 21, 2026:

    Good catch @w0xlt. In addition to that, I fear the current log will be hard to analyze automatically or even grep for. It might make sense to include slightly more information than - txid: 4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b in the line, similar to the reconstruction logging you changed above. How about:

    "Sending {txid} to {peer} in BLOCKTXN"?


    0xB10C commented at 10:30 AM on August 21, 2026:

    Alternatively, does it make sense to put all txids, maybe comma separated, into a single line?


    davidgumberg commented at 12:53 AM on August 28, 2026:

    Thanks, I've made it one log line instead.

  12. in src/blockencodings.h:143 in 43bc34af72
     138 | +    size_t prefilled_size = 0, mempool_size = 0, extra_size = 0;
     139 | +
     140 | +    // Either it was already present in our mempool...
     141 | +    size_t redundant_prefilled_mp_count = 0, redundant_prefilled_mp_size = 0;
     142 | +    // or maybe it was present in our extrapool...
     143 | +    size_t redundant_prefilled_ep_count = 0, redundant_prefilled_ep_size = 0;
    


    0xB10C commented at 11:01 AM on August 21, 2026:

    nit: tiny preference for redundant_prefilled_mempool_count instead of redundant_prefilled_mp_count (and similar) here for readability. Not sure if saving the 5 characters is worth it.


    davidgumberg commented at 12:53 AM on August 28, 2026:

    Thanks, fixed

  13. in src/blockencodings.cpp:182 in 78715aab94
     180 | +            switch (tx_source[i]) {
     181 | +                case TxSource::MEMPOOL:
     182 | +                    mempool_count++;
     183 | +                    break;
     184 | +                case TxSource::EXTRA:
     185 | +                    extra_count++;
    


    andrewtoth commented at 6:49 PM on August 23, 2026:

    I'm not sure I agree with the approach of 78715aab9475b25bb10c82d2e8c1c827560b76ed. Incrementing the counts inline is very cheap. I can't see it be worth the code complexity to move these out. The next commit where the size of transactions are computed only for debug logging would make sense to do.


    davidgumberg commented at 10:42 PM on August 25, 2026:

    I moved the count inc/dec out because I thought it made the code easier to read, not for performance.

    The problem is tracking the states, e.g. when decrementing because of a collision, we will have to check whether the tx_source is mempool or extra. it is definitely possible to do it inline, but IMO it's logic that is unrelated to compactblock reconstruction right in the middle of critical and subtle code, it's nice to scope all the logging code, and even though it's ~7 lines longer as-is, I think it makes it easier to read the function.

    I could shorten the current thing to something like

    if (util::log::ShouldDebugLog(BCLog::CMPCTBLOCK)) {
        for (size_t i = 0; i < txn_available.size(); i++) {
            if (tx_source[i] == TxSource::MEMPOOL) ++mempool_count;
            else if (tx_source[i] == TxSource::EXTRA) ++extra_count;
        }
    }
    

    but I think it is clearer as a switch.

  14. in src/blockencodings.cpp:153 in 78715aab94
     149 | @@ -149,8 +150,7 @@ ReadStatus PartiallyDownloadedBlock::InitData(const CBlockHeaderAndShortTxIDs& c
     150 |              if (tx_source[idit->second] == TxSource::NONE) {
     151 |                  txn_available[idit->second] = extra_txn[i].second;
     152 |                  tx_source[idit->second] = TxSource::EXTRA;
     153 | -                mempool_count++;
     154 | -                extra_count++;
     155 | +                available_count++;
    


    andrewtoth commented at 6:50 PM on August 23, 2026:

    nit: prefer prefixing ++ and --.


    davidgumberg commented at 12:53 AM on August 28, 2026:

    Thanks, fixed

  15. in src/blockencodings.h:138 in 43bc34af72 outdated
     132 | @@ -133,7 +133,16 @@ class CBlockHeaderAndShortTxIDs {
     133 |  class PartiallyDownloadedBlock {
     134 |  protected:
     135 |      std::vector<CTransactionRef> txn_available;
     136 | +
     137 |      size_t prefilled_count = 0, mempool_count = 0, extra_count = 0;
     138 | +    size_t prefilled_size = 0, mempool_size = 0, extra_size = 0;
    


    andrewtoth commented at 6:53 PM on August 23, 2026:

    We should have a comment here that these will only be set if debug logging is enabled.


    davidgumberg commented at 12:52 AM on August 28, 2026:

    Thanks, fixed.

  16. in src/blockencodings.cpp:73 in 43bc34af72 outdated
      69 | @@ -69,6 +70,22 @@ ReadStatus PartiallyDownloadedBlock::InitData(const CBlockHeaderAndShortTxIDs& c
      70 |      header = cmpctblock.header;
      71 |      txn_available.resize(cmpctblock.BlockTxCount());
      72 |  
      73 | +    std::vector<Wtxid> extra_wtxids{};
    


    andrewtoth commented at 7:59 PM on August 23, 2026:

    I'm not sure this is the right data structure here. I think we can do this more cleanly with a std::map<Wtxid, size_t> and populate it below in the for (size_t i = 0; i < cmpctblock.prefilledtxn.size(); i++) { loop. Instead of calling pool->exists for each prefill (which takes the pool lock each time), we can take call GetIter after we take the pool lock later. This lets us avoid sorting the vector as well.

    <details><summary>A quick example of how it could look</summary>

    diff --git a/src/blockencodings.cpp b/src/blockencodings.cpp
    index e2522f8781..7765353da1 100644
    --- a/src/blockencodings.cpp
    +++ b/src/blockencodings.cpp
    @@ -16,6 +16,7 @@
     #include <util/log.h>
     #include <validation.h>
     
    +#include <map>
     #include <unordered_map>
     
     CBlockHeaderAndShortTxIDs::CBlockHeaderAndShortTxIDs(const CBlock& block, uint64_t nonce)
    @@ -70,21 +71,9 @@ ReadStatus PartiallyDownloadedBlock::InitData(const CBlockHeaderAndShortTxIDs& c
         header = cmpctblock.header;
         txn_available.resize(cmpctblock.BlockTxCount());
     
    -    std::vector<Wtxid> extra_wtxids{};
    -
    -    bool debug_log = util::log::ShouldDebugLog(BCLog::CMPCTBLOCK);
    -
    -    if (debug_log) {
    -        prefilled_count = cmpctblock.prefilledtxn.size();
    -
    -        // A sorted vec of extra_txn's for cheaply checking if prefills
    -        // were redundant with the extrapool.
    -        for (const auto& [id, tx] : extra_txn) {
    -            extra_wtxids.push_back(id);
    -        }
    -        std::sort(extra_wtxids.begin(), extra_wtxids.end());
    -    }
    -
    +    const bool debug_log{util::log::ShouldDebugLog(BCLog::CMPCTBLOCK)};
    +    // Prefills not already in the mempool, used to check extra-pool redundancy.
    +    std::map<Wtxid, size_t> leftover_prefills;
     
         int32_t lastprefilledindex = -1;
         for (size_t i = 0; i < cmpctblock.prefilledtxn.size(); i++) {
    @@ -102,20 +91,14 @@ ReadStatus PartiallyDownloadedBlock::InitData(const CBlockHeaderAndShortTxIDs& c
             }
     
             if (debug_log) {
    -            size_t tx_size = cmpctblock.prefilledtxn[i].tx->ComputeTotalSize();
    +            const CTransactionRef& tx{cmpctblock.prefilledtxn[i].tx};
    +            const size_t tx_size{tx->ComputeTotalSize()};
                 prefilled_size += tx_size;
    -
    -            auto tx_wtxid =  cmpctblock.prefilledtxn[i].tx->GetWitnessHash();
    -            if (pool->exists(tx_wtxid)) {
    -                redundant_prefilled_mp_count++;
    -                redundant_prefilled_mp_size += tx_size;
    -            } else if (std::binary_search(extra_wtxids.begin(), extra_wtxids.end(), tx_wtxid)) {
    -                redundant_prefilled_ep_count++;
    -                redundant_prefilled_ep_size += tx_size;
    -            }
    +            leftover_prefills.emplace(tx->GetWitnessHash(), tx_size);
             }
             txn_available[lastprefilledindex] = cmpctblock.prefilledtxn[i].tx;
         }
    +    if (debug_log) prefilled_count = cmpctblock.prefilledtxn.size();
     
         // Calculate map of txids -> positions and check mempool to see what we have (or don't)
         // Because well-formed cmpctblock messages will have a (relatively) uniform distribution
    @@ -148,6 +131,17 @@ ReadStatus PartiallyDownloadedBlock::InitData(const CBlockHeaderAndShortTxIDs& c
         size_t available_count = 0;
         {
         LOCK(pool->cs);
    +    if (debug_log) {
    +        for (auto it{leftover_prefills.begin()}; it != leftover_prefills.end();) {
    +            if (pool->GetIter(it->first)) {
    +                ++redundant_prefilled_mp_count;
    +                redundant_prefilled_mp_size += it->second;
    +                it = leftover_prefills.erase(it);
    +            } else {
    +                ++it;
    +            }
    +        }
    +    }
         for (const auto& [wtxid, txit] : pool->txns_randomized) {
             uint64_t shortid = cmpctblock.GetShortID(wtxid);
             std::unordered_map<uint64_t, uint16_t>::iterator idit = shorttxids.find(shortid);
    @@ -201,7 +195,16 @@ ReadStatus PartiallyDownloadedBlock::InitData(const CBlockHeaderAndShortTxIDs& c
                 break;
         }
     
    -    if (util::log::ShouldDebugLog(BCLog::CMPCTBLOCK)) {
    +    if (debug_log) {
    +        for (const auto& [id, tx] : extra_txn) {
    +            if (!tx) continue;
    +            const auto it{leftover_prefills.find(id)};
    +            if (it == leftover_prefills.end()) continue;
    +            ++redundant_prefilled_ep_count;
    +            redundant_prefilled_ep_size += it->second;
    +            leftover_prefills.erase(it);
    +        }
    +
             Assume(txn_available.size() == tx_source.size());
             for (size_t i = 0; i < txn_available.size(); i++) {
                 switch (tx_source[i]) {
    

    </details>

  17. cmpctblock: log: debuglevel=trace print TXID's of all missing tx'es.
    It doesn't make that much sense to log here only when there's a few
    transactions, since either a user is interested in what tx'es caused
    reconstruction to fail or they aren't, so log all txid's and this a
    trace-level log message.
    83e2cc4fce
  18. cmpctblock: log: Log extrapool separately from mempool
    Previously, in the log message and in the `InitData()` logic, the
    mempool was treated as a superset that includes the extra pool, it makes
    more sense to treat them as separate pools.
    
    This commit also separates the reconstruction critical logic of the
    found transaction count from the logging specific counting of tx
    sources.
    41e7b93723
  19. cmpctblock: log: Print sizes of all tx types and prefill redundancies
    At block reconstruction time, log the counts and sizes of prefilled
    transactions, transactions pulled from the mempool, transactions from
    the extrapool, and missing transactions that were acquired via
    `GETBLOCKTXN`.
    
    Also log the count and size of prefilled transactions that were
    redundant and their source (mempool or extrapool).
    a55e3bb55a
  20. davidgumberg force-pushed on Aug 28, 2026

github-metadata-mirror

This is a metadata mirror of the GitHub repository bitcoin/bitcoin. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2026-08-31 19:51 UTC

This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me