validation: refactor: remove ConnectTrace #34708

pull stickies-v wants to merge 2 commits into bitcoin:master from stickies-v:2026-03/connecttrace-rvalue changing 2 files +15 −51
  1. stickies-v commented at 5:21 am on March 2, 2026: contributor

    The sentinel pattern in ConnectTrace has been unnecessary since conflicted transaction tracking was removed in 5613f9842b4000fed088b8cf7b99674c328d15e1. Without that tracking ConnectTrace is a trivial wrapper around std::vector, so it seems better to just replace it with the vector directly.

    Also modernize/update naming along the way, renaming PerBlockConnectTrace to ConnectedBlock

    Refactor, no behaviour change.

  2. DrahtBot added the label Validation on Mar 2, 2026
  3. DrahtBot commented at 5:22 am on March 2, 2026: contributor

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

    Reviews

    See the guideline for information on the review process.

    Type Reviewers
    ACK HowHsu, sedited, w0xlt

    If your review is incorrectly listed, please copy-paste <!–meta-tag:bot-skip–> into the comment that the bot should ignore.

    Conflicts

    Reviewers, this pull request conflicts with the following ones:

    • #34704 (validation: Explicitly move blocks to validation signals by sedited)

    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.

  4. in src/validation.cpp:3000 in 6d0e221202 outdated
    2994@@ -2995,9 +2995,6 @@ struct PerBlockConnectTrace {
    2995 /**
    2996  * Used to track blocks whose transactions were applied to the UTXO state as a
    2997  * part of a single ActivateBestChainStep call.
    2998- *
    2999- * This class is single-use, once you call GetBlocksConnected() you have to throw
    3000- * it away and make a new one.
    


    sedited commented at 7:05 am on March 2, 2026:
    I think I’d keep the comment. It is not immediately obvious that the class is single use if you just skim its member function declarations.
  5. in src/validation.cpp:3024 in 6d0e221202
    3021         // one waiting for the transactions from the next block. We pop
    3022         // the last entry here to make sure the list we return is sane.
    3023         assert(!blocksConnected.back().pindex);
    3024         blocksConnected.pop_back();
    3025-        return blocksConnected;
    3026+        return std::move(blocksConnected);
    


    ajtowns commented at 7:16 am on March 2, 2026:

    Would this be better as:

    0    assert(!blocksConnected.empty());  // cannot call GetBlocksConnected() twice
    1    assert(!blocksConnected.back().pindex);
    2    blocksConnected.pop_back();
    3
    4    std::vector<PerBlockConnectTrace> result;
    5    std::swap(result, blocksConnected);
    6    return result;
    

    so that blocksConnected is left in a well-defined empty state?


    stickies-v commented at 9:11 am on March 3, 2026:

    Thanks for this suggestion. I now agree this would have been a better implementation for GetBlocksConnected() &&. I haven’t implemented many rvalue ref-qualified methods, so this has been a good learning experience, appreciate the help.

    Resolving since the class no longer exists in HEAD.

  6. in src/validation.cpp:3427 in 6d0e221202
    3423@@ -3426,7 +3424,7 @@ bool Chainstate::ActivateBestChain(BlockValidationState& state, std::shared_ptr<
    3424                 }
    3425                 pindexNewTip = m_chain.Tip();
    3426 
    3427-                for (const PerBlockConnectTrace& trace : connectTrace.GetBlocksConnected()) {
    3428+                for (const PerBlockConnectTrace& trace : std::move(connectTrace).GetBlocksConnected()) {
    


    ajtowns commented at 7:34 am on March 2, 2026:

    Not really sure that does much – std::move() doesn’t really enforce anything much at compile-time (linters sometimes enforce things, eg clang-tidy’s bugprone-use-after-move). For example this change compiles fine:

    0--- a/src/validation.cpp
    1+++ b/src/validation.cpp
    2@@ -3424,6 +3424,7 @@ bool Chainstate::ActivateBestChain(BlockValidationState& state, std::shared_ptr<
    3                 }
    4                 pindexNewTip = m_chain.Tip();
    5
    6+                auto x = std::move(connectTrace).GetBlocksConnected();
    7                 for (const PerBlockConnectTrace& trace : std::move(connectTrace).GetBlocksConnected()) {
    8                     assert(trace.pblock && trace.pindex);
    9                     if (m_chainman.m_options.signals) {```
    

    Previously reusing GetBlocksConnected() would fire an assertion as the extra block has been removed; but I think with this PR it just invokes UB as blocksConnected is no longer in a valid state. So this largely seems worse than the existing code to me.

    I think changing the name to be ConsumeBlocksConnected() without touching the semantics would probably work better than messing with std::move, but given the existing comment and assertion I don’t think changes here add much value at all.

  7. validation: remove sentinel block from ConnectTrace
    The sentinel pattern was necessary to collect conflicted transactions
    before their associated block was recorded, but that tracking was
    removed in 5613f9842b4000fed088b8cf7b99674c328d15e1.
    b83de7f28e
  8. stickies-v force-pushed on Mar 3, 2026
  9. stickies-v renamed this:
    validation: refactor: enforce single-use ConnectTrace consumption
    validation: refactor: remove ConnectTrace
    on Mar 3, 2026
  10. stickies-v commented at 9:07 am on March 3, 2026: contributor

    Thanks for the review and feedback, @sedited and @ajtowns. I’d forgotten that it’s generally discouraged, but not illegal or even UB to access an object after it has been moved, making my suggested interface much less clear and robust than I thought it was.

    I’ve now overhauled the approach. As I digged into its usage a bit more, it has become clear that the sentinel block is no longer necessary, allowing us to (b83de7f28e71f0b10a999327395198fbcb02f44b) make ConnectTrace reusable without overhead. But since basically the class has now become a trivial wrapper around its vector, I think it makes more sense to just remove (e80ace13ad94feb6efe1d50aaaceeb6c2a306991) it entirely, which is the approach I’ve taken here.

    Updated OP and title with the new approach.

  11. in src/validation.cpp:2994 in e80ace13ad
    3027-    }
    3028+struct ConnectedBlock {
    3029+    CBlockIndex* const pindex;
    3030+    const std::shared_ptr<const CBlock> pblock;
    3031+    ConnectedBlock(CBlockIndex* pindex, std::shared_ptr<const CBlock> pblock)
    3032+        : pindex{Assert(pindex)}, pblock{Assert(std::move(pblock))} {}
    


    w0xlt commented at 7:30 pm on March 3, 2026:

    The ConnectedBlock can be simplified.

    const members suppress move semantics, so vector reallocation will copy the shared_ptr instead of moving it.

    The Assert() null-checks were useful when the sentinel pattern could produce empty entries, but with the sentinel gone ConnectTip only reaches the emplace_back after successful connection where both pointers are known-valid.

    0    CBlockIndex* pindex;
    1    std::shared_ptr<const CBlock> pblock;
    

    stickies-v commented at 3:43 am on March 4, 2026:
    Thanks, I agree the correct-by-construction guarantees are a bit overkill here, so I’ve adopted your suggestion (but kept the Assert in ConnectTip() since this relies on another ActivateBestChainStep’s implementation).
  12. w0xlt commented at 7:30 pm on March 3, 2026: contributor
    ACK e80ace13ad94feb6efe1d50aaaceeb6c2a306991
  13. stickies-v force-pushed on Mar 4, 2026
  14. stickies-v commented at 3:45 am on March 4, 2026: contributor
    Force-pushed to remove the correct-by-construction commit since it’s overkill and suppresses ConnectedBlock’s move semantics, addressing #34708 (review).
  15. w0xlt commented at 4:06 pm on March 4, 2026: contributor
    reACK 5d558f024603ae5f4ed4231634394b7794b95eca
  16. in src/validation.cpp:2991 in b83de7f28e
    2989@@ -2990,39 +2990,24 @@ bool Chainstate::DisconnectTip(BlockValidationState& state, DisconnectedBlockTra
    2990 struct PerBlockConnectTrace {
    2991     CBlockIndex* pindex = nullptr;
    


    sedited commented at 5:08 pm on March 4, 2026:
    Could this be made const and not defaulted to nullptr?

    stickies-v commented at 2:24 am on March 5, 2026:
    Sure, pushed!
  17. sedited commented at 5:16 pm on March 4, 2026: contributor
    Nice, Approach ACK
  18. validation: remove ConnectTrace wrapper class
    Replace ConnectTrace with a plain std::vector<ConnectedBlock>, and
    rename PerBlockConnectTrace to ConnectedBlock and connectTrace to
    connected_blocks.
    2f8f2e9001
  19. stickies-v force-pushed on Mar 5, 2026
  20. in src/validation.cpp:2991 in 2f8f2e9001
    2986@@ -2987,57 +2987,22 @@ bool Chainstate::DisconnectTip(BlockValidationState& state, DisconnectedBlockTra
    2987     return true;
    2988 }
    2989 
    2990-struct PerBlockConnectTrace {
    2991-    CBlockIndex* pindex = nullptr;
    2992+struct ConnectedBlock {
    2993+    const CBlockIndex* pindex;
    


    maflcko commented at 7:39 am on March 5, 2026:
    0    CBlockIndex& index;
    

    nit: Haven’t tried, but since you are touching all lines, it could make sense to use a reference or std::refernece_wrapper, as nullptr is never used and not allowed anyway?


    stickies-v commented at 4:25 pm on March 5, 2026:

    I considered this but decided not to, because we can’t use a similar pattern for pblock and it doesn’t simplify things - just moves the asserts to different places. I don’t really have a preference either way, if you feel like the reference is better here I’m happy to update it.

     0diff --git a/src/validation.cpp b/src/validation.cpp
     1index 83c16867fe..873c269335 100644
     2--- a/src/validation.cpp
     3+++ b/src/validation.cpp
     4@@ -2988,7 +2988,7 @@ bool Chainstate::DisconnectTip(BlockValidationState& state, DisconnectedBlockTra
     5 }
     6 
     7 struct ConnectedBlock {
     8-    const CBlockIndex* pindex;
     9+    CBlockIndex& pindex;
    10     std::shared_ptr<const CBlock> pblock;
    11 };
    12 
    13@@ -3099,7 +3099,7 @@ bool Chainstate::ConnectTip(
    14     Chainstate& current_cs{m_chainman.CurrentChainstate()};
    15     m_chainman.MaybeValidateSnapshot(*this, current_cs);
    16 
    17-    connected_blocks.emplace_back(pindexNew, std::move(block_to_connect));
    18+    connected_blocks.emplace_back(*Assert(pindexNew), std::move(block_to_connect));
    19     return true;
    20 }
    21 
    22@@ -3393,7 +3393,7 @@ bool Chainstate::ActivateBestChain(BlockValidationState& state, std::shared_ptr<
    23 
    24                 for (const auto& [index, block] : connected_blocks) {
    25                     if (m_chainman.m_options.signals) {
    26-                        m_chainman.m_options.signals->BlockConnected(chainstate_role, Assert(block), Assert(index));
    27+                        m_chainman.m_options.signals->BlockConnected(chainstate_role, Assert(block), &index);
    28                     }
    29                 }
    30 
    
  21. fanquake added this to the milestone 32.0 on Mar 5, 2026
  22. HowHsu commented at 4:06 am on March 6, 2026: none
    ACK 2f8f2e900118c98699db91075bd1b72e6460c6b1
  23. DrahtBot requested review from w0xlt on Mar 6, 2026
  24. DrahtBot requested review from sedited on Mar 6, 2026
  25. sedited approved
  26. sedited commented at 7:44 am on March 6, 2026: contributor
    ACK 2f8f2e900118c98699db91075bd1b72e6460c6b1

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-03-09 09:13 UTC

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