refactor: Move calculation logic out from CheckSequenceLocksAtTip() #23897

pull hebasto wants to merge 2 commits into bitcoin:master from hebasto:211229-lockpoints changing 3 files +129 −88
  1. hebasto commented at 5:58 pm on December 29, 2021: member

    This PR is follow up for bitcoin/bitcoin#22677 and bitcoin/bitcoin#23683.

    On master (013daed9acca1b723f599d63ab36b9c2a5c60e5f) it is not obvious that CheckSequenceLocksAtTip() function can modify its LockPoints* lp parameter which leads to #22677 (review).

    This PR:

    • separates the lockpoint calculate logic from CheckSequenceLocksAtTip() function into a new CalculateLockPointsAtTip() one
    • cleans up the CheckSequenceLocksAtTip() function interface
    • makes code easier to reason about (hopefully)
  2. DrahtBot added the label Mempool on Dec 29, 2021
  3. DrahtBot added the label Refactoring on Dec 29, 2021
  4. DrahtBot added the label Validation on Dec 29, 2021
  5. hebasto force-pushed on Dec 29, 2021
  6. hebasto marked this as ready for review on Dec 29, 2021
  7. hebasto force-pushed on Jan 3, 2022
  8. hebasto commented at 2:47 pm on January 3, 2022: member
    Rebased 154307c10b0d3c0e7dfbe463de7d040b2eb97c74 -> c4476a5ea787e3722d8c1e5fed5f0c6e2a554327 (pr23897.01 -> pr23897.02) on top of the merged #23683.
  9. DrahtBot commented at 3:03 pm on January 3, 2022: 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 stickies-v, achow101
    Stale ACK glozow, aureleoules

    If your review is incorrectly listed, please react with 👎 to this comment and the bot will ignore it on the next update.

    Conflicts

    No conflicts as of last run.

  10. DrahtBot added the label Needs rebase on Jan 19, 2022
  11. hebasto force-pushed on Jan 19, 2022
  12. hebasto commented at 9:53 pm on January 19, 2022: member
    Rebased c4476a5ea787e3722d8c1e5fed5f0c6e2a554327 -> 220475bdbca0598c6f5158e870b89ff9503a7258 (pr23897.02 -> pr23897.03) on top of the merged #23976.
  13. DrahtBot removed the label Needs rebase on Jan 19, 2022
  14. in src/validation.cpp:219 in 220475bdbc outdated
    210@@ -211,14 +211,75 @@ bool CheckFinalTx(const CBlockIndex* active_chain_tip, const CTransaction &tx, i
    211     return IsFinalTx(tx, nBlockHeight, nBlockTime);
    212 }
    213 
    214+bool CalculateLockPoints(
    215+    CBlockIndex* tip,
    216+    const CCoinsView& coins_view,
    217+    const CTransaction& tx,
    218+    int flags,
    219+    LockPoints* lp)
    


    glozow commented at 12:08 pm on January 26, 2022:
    It would be nice to remove the raw pointers while we’re changing the interface anyway. I think a better interface is to have CalculateLockPoints() return a std::optional<LockPoints> which encodes the same information as the bool.

    hebasto commented at 4:06 pm on April 19, 2022:
    Thanks! Updated.
  15. in src/validation.cpp:240 in 220475bdbc outdated
    235+    prevheights.resize(tx.vin.size());
    236+    for (size_t txinIndex = 0; txinIndex < tx.vin.size(); ++txinIndex) {
    237+        const CTxIn& txin = tx.vin[txinIndex];
    238+        Coin coin;
    239+        if (!coins_view.GetCoin(txin.prevout, coin)) {
    240+            return error("%s: Missing input", __func__);
    


    glozow commented at 12:09 pm on January 26, 2022:
    Can be replaced with a LogPrintf and return std::nullopt. Also seems like this log could be improved by adding the transaction id and/or prevout that is missing.

    hebasto commented at 4:07 pm on April 19, 2022:
    Thanks! Updated.
  16. in src/validation.cpp:279 in 220475bdbc outdated
    279-                        const CCoinsView& coins_view,
    280-                        const CTransaction& tx,
    281-                        int flags,
    282-                        LockPoints* lp,
    283-                        bool useExistingLockPoints)
    284+                        const LockPoints* lp)
    


    glozow commented at 12:10 pm on January 26, 2022:
    Same here, would be nice to remove the raw pointer and make this parameter a const LockPoints& lp. It doesn’t make sense to allow a nullptr lp to be passed in anymore, since we can’t check sequence locks on empty lockpoints anyway.

    hebasto commented at 4:07 pm on April 19, 2022:
    Thanks! Updated.
  17. in src/validation.cpp:380 in 220475bdbc outdated
    385-            // If CheckSequenceLocks fails, remove the tx and don't depend on the LockPoints.
    386-            return true;
    387-        } else if (!validLP) {
    388-            // If CheckSequenceLocks succeeded, it also updated the LockPoints.
    389-            // Now update the mempool entry lockpoints as well.
    390+        if (!validLP) {
    


    glozow commented at 12:15 pm on January 26, 2022:

    Note that we do have a bit of a behavior change here: we now update the mempool entry LockPoints even if we’re going to remove the entry later. I’m not sure if this is desirable.

    I suggest adding documentation as well, e.g. “If the cached LockPoints are now invalid, recalculate them and update the mempool entry before calling CheckSequenceLocks().


    hebasto commented at 4:09 pm on April 19, 2022:

    Note that we do have a bit of a behavior change here…

    Thank you!

    The current behavior has been preserved in the recent update.

  18. glozow commented at 12:20 pm on January 26, 2022: member
    Concept ACK, I agree that calculating lockpoints and checking them against the current chain should be separate methods and it was definitely unexpected that CheckSequenceLocks() was mutating the lockpoints passed in. There is a slight behavior change to MaybeUpdateMempoolForReorg() here.
  19. DrahtBot added the label Needs rebase on Mar 14, 2022
  20. hebasto force-pushed on Apr 19, 2022
  21. hebasto renamed this:
    refactor: Separate lockpoint calculate logic from CheckSequenceLocks function
    refactor: Move calculation logic out from `CheckSequenceLocksAtTip()`
    on Apr 19, 2022
  22. hebasto commented at 4:06 pm on April 19, 2022: member

    Updated 220475bdbca0598c6f5158e870b89ff9503a7258 -> aa8f68b2352dd37cae59867276b38127d7041685 (pr23897.03 -> pr23897.04):

    • rebased
    • addressed @glozow’s comments

    Also the PR description has been updated.

  23. DrahtBot removed the label Needs rebase on Apr 19, 2022
  24. DrahtBot added the label Needs rebase on May 6, 2022
  25. hebasto force-pushed on May 28, 2022
  26. hebasto commented at 12:35 pm on May 28, 2022: member
    Rebased aa8f68b2352dd37cae59867276b38127d7041685 -> 5a6ce8cca31b3105cb8e96cd3e5e6c0da0fb3479 (pr23897.04 -> pr23897.05) due to conflict with #24804.
  27. DrahtBot removed the label Needs rebase on May 28, 2022
  28. hebasto requested review from glozow on Jul 20, 2022
  29. in src/validation.h:304 in fc31aa12b9 outdated
    299+ *                              coins, or a CCoinsViewMemPool that is connected to the
    300+ *                              mempool and chainstate UTXO set. In the latter case, the caller is
    301+ *                              responsible for holding the appropriate locks to ensure that
    302+ *                              calls to GetCoin() return correct coins.
    303+ *
    304+ * @returns The resulting height and time calculated and the hash of the block needed for calculation.
    


    glozow commented at 9:18 am on July 21, 2022:

    in fc31aa12b9599f3a23c22fd65b3eaa73dbb05d81:

    Should document that this returns std::nullopt if there is an error, i.e., if inputs cannot be found in the coins_view


    hebasto commented at 10:21 am on July 21, 2022:
    Thanks! Updated.
  30. in src/validation.cpp:214 in 5a6ce8cca3 outdated
    184@@ -185,11 +185,67 @@ bool CheckFinalTxAtTip(const CBlockIndex* active_chain_tip, const CTransaction&
    185     return IsFinalTx(tx, nBlockHeight, nBlockTime);
    186 }
    187 
    188+std::optional<LockPoints> CalculateLockPointsAtTip(CBlockIndex* tip,
    189+                                                   const CCoinsView& coins_view,
    190+                                                   const CTransaction& tx)
    191+{
    192+    assert(tip);
    


    glozow commented at 9:48 am on July 21, 2022:
    To me, this suggests tip should be a reference instead of a pointer?

    hebasto commented at 10:15 am on July 21, 2022:
    CChain::Tip() is been passed as an argument, and it could be a nullptr.

    glozow commented at 10:29 am on July 21, 2022:
    Sorry I should have said the parameter tip, as in, if a function expects a non-nullptr tip, then perhaps it should take a reference instead of a pointer. But I think I get what you mean, that CChain::Tip() returns a pointer and that’s passed around pretty frequently in the codebase.
  31. in src/validation.h:314 in 5a6ce8cca3 outdated
    309+
    310+/**
    311+ * Check if transaction will be BIP68 final in the next block to be created on top of tip.
    312+ * @param[in]   tip             Chain tip to check tx sequence locks against. For example,
    313+ *                              the tip of the current active chain.
    314+ * @param[in]   lp              Relative locktime constraints (BIP68).
    


    glozow commented at 9:54 am on July 21, 2022:

    in 5a6ce8cca31b3105cb8e96cd3e5e6c0da0fb3479

    0 * [@param](/bitcoin-bitcoin/contributor/param/)[in]   lp              LockPoints containing the height and time at which this transaction is final.
    

    hebasto commented at 10:21 am on July 21, 2022:
    Thanks! Updated.
  32. glozow commented at 9:56 am on July 21, 2022: member

    code review ACK 5a6ce8cca31b3105cb8e96cd3e5e6c0da0fb3479

    This looks correct to me, a few suggestions to make things clearer. Probably at least 1 other person should take a look as well.

  33. glozow requested review from maflcko on Jul 21, 2022
  34. hebasto force-pushed on Jul 21, 2022
  35. hebasto commented at 10:20 am on July 21, 2022: member

    Updated 5a6ce8cca31b3105cb8e96cd3e5e6c0da0fb3479 -> 803869954e0e3bc32279dac71cf40a0614b79566 (pr23897.05 -> pr23897.06, diff):

  36. maflcko commented at 10:28 am on July 21, 2022: member

    Not sure.

    • The functions are only ever called together, so it seems odd to pull them apart? No objection if you want to move-only some stuff out of a function and then call the newly created function in place.
    • I think the previous changes/docs made it clear that lp may be modified? Otherwise, an inline comment seems fine as well?
  37. glozow commented at 10:32 am on July 21, 2022: member

    reACK 803869954e0e3bc32279dac71cf40a0614b79566

    Only changes are doc updates, for #23897 (review) and #23897 (review)

  38. glozow commented at 10:52 am on July 21, 2022: member

    No objection if you want to move-only some stuff out of a function and then call the newly created function in place.

    fwiw this is >50% move-only.

  39. DrahtBot added the label Needs rebase on Aug 12, 2022
  40. hebasto force-pushed on Aug 16, 2022
  41. hebasto commented at 12:21 pm on August 16, 2022: member
    Rebased 803869954e0e3bc32279dac71cf40a0614b79566 -> 32e3fc173410641569bc4df805d5431cf0013d74 (pr23897.06 -> pr23897.07) due to the conflict with #25677.
  42. DrahtBot removed the label Needs rebase on Aug 16, 2022
  43. DrahtBot added the label Needs rebase on Oct 10, 2022
  44. hebasto force-pushed on Oct 10, 2022
  45. hebasto commented at 12:40 pm on October 10, 2022: member
    Rebased 32e3fc173410641569bc4df805d5431cf0013d74 -> bebaddb0767b9c52507e8d50a18510b592c14778 (pr23897.07 -> pr23897.08) due to the conflict with #25073.
  46. DrahtBot removed the label Needs rebase on Oct 10, 2022
  47. in src/validation.cpp:210 in bebaddb076 outdated
    205+        } else {
    206+            prevheights[txinIndex] = coin.nHeight;
    207+        }
    208+    }
    209+
    210+    const std::pair<int, int64_t> lock_pair{CalculateSequenceLocks(tx, STANDARD_LOCKTIME_VERIFY_FLAGS, prevheights, index)};
    


    aureleoules commented at 2:24 pm on October 11, 2022:

    nit in ed9db580c764cf26fb534be54684c46e79300a38

    0    const auto [minHeight, minTime] = CalculateSequenceLocks(tx, STANDARD_LOCKTIME_VERIFY_FLAGS, prevheights, index);
    

    hebasto commented at 12:06 pm on October 14, 2022:
    Thanks!.Updated.
  48. in src/validation.cpp:204 in ed9db580c7 outdated
    199+            LogPrintf("ERROR: %s: Missing input %d in transaction \'%s\'\n", __func__, txinIndex, tx.GetHash().GetHex());
    200+            return std::nullopt;
    201+        }
    202+        if (coin.nHeight == MEMPOOL_HEIGHT) {
    203+            // Assume all mempool transaction confirm in the next block
    204+            prevheights[txinIndex] = tip->nHeight + 1;
    


    aureleoules commented at 3:17 pm on October 11, 2022:

    nit in ed9db580c764cf26fb534be54684c46e79300a38: Avoids dereferencing multiple times:

     0diff --git a/src/validation.cpp b/src/validation.cpp
     1index a25e817a5..0fa8dc30c 100644
     2--- a/src/validation.cpp
     3+++ b/src/validation.cpp
     4@@ -201,7 +201,7 @@ std::optional<LockPoints> CalculateLockPointsAtTip(CBlockIndex* tip,
     5         }
     6         if (coin.nHeight == MEMPOOL_HEIGHT) {
     7             // Assume all mempool transaction confirm in the next block
     8-            prevheights[txinIndex] = tip->nHeight + 1;
     9+            prevheights[txinIndex] = index.nHeight;
    10         } else {
    11             prevheights[txinIndex] = coin.nHeight;
    12         }
    13@@ -222,7 +222,7 @@ std::optional<LockPoints> CalculateLockPointsAtTip(CBlockIndex* tip,
    14     int max_input_height{0};
    15     for (const int height : prevheights) {
    16         // Can ignore mempool inputs since we'll fail if they had non-zero locks
    17-        if (height != tip->nHeight + 1) {
    18+        if (height != index.nHeight) {
    19             max_input_height = std::max(max_input_height, height);
    20         }
    21     }
    

    hebasto commented at 12:06 pm on October 14, 2022:
    Thanks!.Updated.
  49. aureleoules approved
  50. aureleoules commented at 4:20 pm on October 11, 2022: member
    ACK bebaddb0767b9c52507e8d50a18510b592c14778 The code is now much clearer and easier to understand and the behavior doesn’t seem to have changed.
  51. hebasto force-pushed on Oct 14, 2022
  52. hebasto commented at 12:06 pm on October 14, 2022: member

    Updated bebaddb0767b9c52507e8d50a18510b592c14778 -> ed2d714bd1130291a2f781e644f787d421cdf26e (pr23897.08 -> pr23897.09, diff):

  53. aureleoules approved
  54. aureleoules commented at 12:59 pm on October 14, 2022: member
    reACK ed2d714bd1130291a2f781e644f787d421cdf26e
  55. in src/validation.cpp:194 in ed2d714bd1 outdated
    189+    // Thus if we want to know if a transaction can be part of the
    190+    // *next* block, we need to use one more than active_chainstate.m_chain.Height()
    191+    index.nHeight = tip->nHeight + 1;
    192+
    193+    std::vector<int> prevheights;
    194+    prevheights.resize(tx.vin.size());
    


    stickies-v commented at 2:00 pm on October 24, 2022:

    I’d avoid using resize() since there’s no need to default-initialize everything. reserve() seems more appropriate:

    0    std::vector<int> prevheights;
    1    prevheights.reserve(tx.vin.size());
    

    hebasto commented at 9:48 am on November 30, 2022:
    Going to leave it as this because the suggested diff won’t work.
  56. in src/validation.cpp:185 in ed2d714bd1 outdated
    180+                                                   const CCoinsView& coins_view,
    181+                                                   const CTransaction& tx)
    182+{
    183+    assert(tip);
    184+
    185+    CBlockIndex index;
    


    stickies-v commented at 6:22 pm on November 3, 2022:

    nit: would this be a more helpful name?

    0    CBlockIndex next_tip;
    

    hebasto commented at 9:46 am on November 30, 2022:
    Thanks! Updated.
  57. in src/validation.cpp:214 in ed2d714bd1 outdated
    175@@ -176,11 +176,67 @@ bool CheckFinalTxAtTip(const CBlockIndex& active_chain_tip, const CTransaction&
    176     return IsFinalTx(tx, nBlockHeight, nBlockTime);
    177 }
    178 
    179+std::optional<LockPoints> CalculateLockPointsAtTip(CBlockIndex* tip,
    180+                                                   const CCoinsView& coins_view,
    181+                                                   const CTransaction& tx)
    182+{
    183+    assert(tip);
    


    stickies-v commented at 10:08 am on November 4, 2022:
    Would it make sense to pass a CBlockIndex& instead and avoid checking for nullptr here? If this fn requires a non-nullptr CBlockIndex, I think the fn signature should reflect that and the callsite to take responsibility.

    hebasto commented at 9:46 am on November 30, 2022:
    Going to leave it as this to keep this PR in more reviewable state.
  58. in src/validation.h:281 in ed2d714bd1 outdated
    275+ * @returns The resulting height and time calculated and the hash of the block needed for
    276+ *          calculation, or std::nullopt if there is an error.
    277+ */
    278+std::optional<LockPoints> CalculateLockPointsAtTip(CBlockIndex* tip,
    279+                                                   const CCoinsView& coins_view,
    280+                                                   const CTransaction& tx);
    


    stickies-v commented at 10:12 am on November 4, 2022:
    nit: missing doxygen for tx param

    hebasto commented at 9:45 am on November 30, 2022:
    Thanks! Updated.
  59. stickies-v commented at 10:14 am on November 4, 2022: contributor
    Concept ACK. Hooray for clearer function intents and effects!
  60. in src/validation.cpp:222 in ed2d714bd1 outdated
    183+    assert(tip);
    184+
    185+    CBlockIndex index;
    186+    index.pprev = tip;
    187+    // When SequenceLocks() is called within ConnectBlock(), the height
    188+    // of the block *being* evaluated is what is used.
    


    stickies-v commented at 3:29 pm on November 4, 2022:
    This documentation seems out of date now

    stickies-v commented at 4:48 pm on November 29, 2022:
    @hebasto I believe my comment was aimed at that I couldn’t find any connection between SequenceLocks() and ConnectBlock() and this function - but maybe I just missed it? I also don’t really understand what the documentation means, but maybe that’s just my lack of context.

    hebasto commented at 1:30 pm on January 31, 2023:

    I couldn’t find any connection between SequenceLocks() and ConnectBlock() and this function - but maybe I just missed it?

    https://github.com/bitcoin/bitcoin/blob/357d750cab5221695d718e6d3e8ce63fa2b5ab3a/src/validation.cpp#L2257

  61. in src/validation.cpp:203 in ed2d714bd1 outdated
    202+        if (coin.nHeight == MEMPOOL_HEIGHT) {
    203+            // Assume all mempool transaction confirm in the next block
    204+            prevheights[txinIndex] = index.nHeight;
    205+        } else {
    206+            prevheights[txinIndex] = coin.nHeight;
    207+        }
    


    stickies-v commented at 4:42 pm on November 4, 2022:
    For readability, I think this chunk of code could be separated out into a CalculatePrevHeights function?

    hebasto commented at 5:02 pm on November 29, 2022:
    Thanks! Updated.
  62. in src/validation.cpp:239 in ed2d714bd1 outdated
    238 bool CheckSequenceLocksAtTip(CBlockIndex* tip,
    239-                        const CCoinsView& coins_view,
    240-                        const CTransaction& tx,
    241-                        LockPoints* lp,
    242-                        bool useExistingLockPoints)
    243+                             const LockPoints& lp)
    


    stickies-v commented at 4:47 pm on November 4, 2022:

    nit: I think overly abbreviated variable names should be avoided for readability

    0                             const LockPoints& lock_points)
    

    hebasto commented at 5:02 pm on November 29, 2022:
    Thanks! Updated.
  63. in src/validation.cpp:199 in ed2d714bd1 outdated
    194+    prevheights.resize(tx.vin.size());
    195+    for (size_t txinIndex = 0; txinIndex < tx.vin.size(); ++txinIndex) {
    196+        const CTxIn& txin = tx.vin[txinIndex];
    197+        Coin coin;
    198+        if (!coins_view.GetCoin(txin.prevout, coin)) {
    199+            LogPrintf("ERROR: %s: Missing input %d in transaction \'%s\'\n", __func__, txinIndex, tx.GetHash().GetHex());
    


    stickies-v commented at 11:55 am on November 10, 2022:
    Perhaps you could Assume(false) here to ensure this is visible in debug builds? I don’t think we ever expect this path to be reached?

    hebasto commented at 5:01 pm on November 29, 2022:
    This change could be considered as a behavior change, so leaving it for now.
  64. in src/validation.cpp:353 in ed2d714bd1 outdated
    335@@ -334,20 +336,23 @@ void Chainstate::MaybeUpdateMempoolForReorg(
    336 
    337         // The transaction must be final.
    


    stickies-v commented at 12:44 pm on November 10, 2022:

    While refactoring this function, would it be appropriate to eliminate this behemoth filter_final_and_mature lambda and turn it into a member function? It’s a very straightforward carve-out and I think that massively helps with readability. Example very quick diff below (some lock warnings but compiles otherwise). In addition to carve-out, it also flips the true/false behaviour for an (imo) more intuitive behaviour and naming and thus readability.

      0diff --git a/src/validation.cpp b/src/validation.cpp
      1index 0986147b7..1657323bd 100644
      2--- a/src/validation.cpp
      3+++ b/src/validation.cpp
      4@@ -323,60 +323,60 @@ void Chainstate::MaybeUpdateMempoolForReorg(
      5     // the disconnectpool that were added back and cleans up the mempool state.
      6     m_mempool->UpdateTransactionsFromBlock(vHashUpdate);
      7 
      8-    // Predicate to use for filtering transactions in removeForReorg.
      9-    // Checks whether the transaction is still final and, if it spends a coinbase output, mature.
     10-    // Also updates valid entries' cached LockPoints if needed.
     11-    // If false, the tx is still valid and its lockpoints are updated.
     12-    // If true, the tx would be invalid in the next block; remove this entry and all of its descendants.
     13-    const auto filter_final_and_mature = [this](CTxMemPool::txiter it)
     14-        EXCLUSIVE_LOCKS_REQUIRED(m_mempool->cs, ::cs_main) {
     15-        AssertLockHeld(m_mempool->cs);
     16-        AssertLockHeld(::cs_main);
     17-        const CTransaction& tx = it->GetTx();
     18+    // We also need to remove any now-immature transactions
     19+    m_mempool->removeForReorg(m_chain, [this](CTxMemPool::txiter it){ return !this->IsTxFinalAndMature(it); });
     20+    // Re-limit mempool size, in case we added any transactions
     21+    LimitMempoolSize(*m_mempool, this->CoinsTip());
     22+}
     23 
     24-        // The transaction must be final.
     25-        if (!CheckFinalTxAtTip(*Assert(m_chain.Tip()), tx)) return true;
     26+// Predicate to use for filtering transactions in removeForReorg.
     27+// Checks whether the transaction is still final and, if it spends a coinbase output, mature.
     28+// Also updates valid entries' cached LockPoints if needed.
     29+// If true, the tx is still valid and its lockpoints are updated.
     30+// If false, the tx would be invalid in the next block; remove this entry and all of its descendants.
     31+bool Chainstate::IsTxFinalAndMature(CTxMemPool::txiter it) EXCLUSIVE_LOCKS_REQUIRED(m_mempool->cs, ::cs_main)
     32+{
     33+    AssertLockHeld(m_mempool->cs);
     34+    AssertLockHeld(::cs_main);
     35+    const CTransaction& tx = it->GetTx();
     36 
     37-        const LockPoints& lp = it->GetLockPoints();
     38-        // CheckSequenceLocksAtTip checks if the transaction will be final in the next block to be
     39-        // created on top of the new chain.
     40-        if (TestLockPointValidity(m_chain, lp)) {
     41-            if (!CheckSequenceLocksAtTip(m_chain.Tip(), lp)) {
     42-                return true;
     43-            }
     44+    // The transaction must be final.
     45+    if (!CheckFinalTxAtTip(*Assert(m_chain.Tip()), tx)) return true;
     46+
     47+    const LockPoints& lp = it->GetLockPoints();
     48+    // CheckSequenceLocksAtTip checks if the transaction will be final in the next block to be
     49+    // created on top of the new chain.
     50+    if (TestLockPointValidity(m_chain, lp)) {
     51+        if (!CheckSequenceLocksAtTip(m_chain.Tip(), lp)) {
     52+            return false;
     53+        }
     54+    } else {
     55+        const CCoinsViewMemPool view_mempool{&CoinsTip(), *m_mempool};
     56+        const std::optional<LockPoints> new_lock_points{CalculateLockPointsAtTip(m_chain.Tip(), view_mempool, tx)};
     57+        if (new_lock_points.has_value() && CheckSequenceLocksAtTip(m_chain.Tip(), *new_lock_points)) {
     58+            // Now update the mempool entry lockpoints as well.
     59+            m_mempool->mapTx.modify(it, [&new_lock_points](CTxMemPoolEntry& e) { e.UpdateLockPoints(*new_lock_points); });
     60         } else {
     61-            const CCoinsViewMemPool view_mempool{&CoinsTip(), *m_mempool};
     62-            const std::optional<LockPoints> new_lock_points{CalculateLockPointsAtTip(m_chain.Tip(), view_mempool, tx)};
     63-            if (new_lock_points.has_value() && CheckSequenceLocksAtTip(m_chain.Tip(), *new_lock_points)) {
     64-                // Now update the mempool entry lockpoints as well.
     65-                m_mempool->mapTx.modify(it, [&new_lock_points](CTxMemPoolEntry& e) { e.UpdateLockPoints(*new_lock_points); });
     66-            } else {
     67-                return true;
     68-            }
     69+            return false;
     70         }
     71+    }
     72 
     73-        // If the transaction spends any coinbase outputs, it must be mature.
     74-        if (it->GetSpendsCoinbase()) {
     75-            for (const CTxIn& txin : tx.vin) {
     76-                auto it2 = m_mempool->mapTx.find(txin.prevout.hash);
     77-                if (it2 != m_mempool->mapTx.end())
     78-                    continue;
     79-                const Coin& coin{CoinsTip().AccessCoin(txin.prevout)};
     80-                assert(!coin.IsSpent());
     81-                const auto mempool_spend_height{m_chain.Tip()->nHeight + 1};
     82-                if (coin.IsCoinBase() && mempool_spend_height - coin.nHeight < COINBASE_MATURITY) {
     83-                    return true;
     84-                }
     85+    // If the transaction spends any coinbase outputs, it must be mature.
     86+    if (it->GetSpendsCoinbase()) {
     87+        for (const CTxIn& txin : tx.vin) {
     88+            auto it2 = m_mempool->mapTx.find(txin.prevout.hash);
     89+            if (it2 != m_mempool->mapTx.end())
     90+                continue;
     91+            const Coin& coin{CoinsTip().AccessCoin(txin.prevout)};
     92+            assert(!coin.IsSpent());
     93+            const auto mempool_spend_height{m_chain.Tip()->nHeight + 1};
     94+            if (coin.IsCoinBase() && mempool_spend_height - coin.nHeight < COINBASE_MATURITY) {
     95+                return false;
     96             }
     97         }
     98-        // Transaction is still valid and cached LockPoints are updated.
     99-        return false;
    100-    };
    101-
    102-    // We also need to remove any now-immature transactions
    103-    m_mempool->removeForReorg(m_chain, filter_final_and_mature);
    104-    // Re-limit mempool size, in case we added any transactions
    105-    LimitMempoolSize(*m_mempool, this->CoinsTip());
    106+    }
    107+    // Transaction is still valid and cached LockPoints are updated.
    108+    return true;
    109 }
    110 
    111 /**
    112diff --git a/src/validation.h b/src/validation.h
    113index bd6f710d1..52f1ee66f 100644
    114--- a/src/validation.h
    115+++ b/src/validation.h
    116@@ -291,6 +291,7 @@ std::optional<LockPoints> CalculateLockPointsAtTip(CBlockIndex* tip,
    117  * passed in for evaluation.
    118  * The LockPoints should not be considered valid if CheckSequenceLocksAtTip returns false.
    119  */
    120+
    121 bool CheckSequenceLocksAtTip(CBlockIndex* tip,
    122                              const LockPoints& lp);
    123 
    124@@ -770,6 +771,8 @@ private:
    125         DisconnectedBlockTransactions& disconnectpool,
    126         bool fAddToMempool) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_mempool->cs);
    127 
    128+    bool IsTxFinalAndMature(CTxMemPool::txiter it) EXCLUSIVE_LOCKS_REQUIRED(m_mempool->cs, ::cs_main);
    129+
    130     /** Check warning conditions and do some notifications on new chain tip set. */
    131     void UpdateTip(const CBlockIndex* pindexNew)
    132         EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
    

    hebasto commented at 5:00 pm on November 29, 2022:
    Going to leave it as this to keep this PR in more reviewable state.
  65. in src/validation.cpp:835 in ed2d714bd1 outdated
    831@@ -827,7 +832,7 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
    832     }
    833 
    834     entry.reset(new CTxMemPoolEntry(ptx, ws.m_base_fees, nAcceptTime, m_active_chainstate.m_chain.Height(),
    835-            fSpendsCoinbase, nSigOpsCost, lp));
    836+            fSpendsCoinbase, nSigOpsCost, *lock_points));
    


    stickies-v commented at 3:50 pm on November 10, 2022:

    Since we’re doing the .has_value() check at quite a distance from here, I think it’s safer to use .value() for robustness with future refactoring.

    0            fSpendsCoinbase, nSigOpsCost, lock_points.value()));
    

    hebasto commented at 4:58 pm on November 29, 2022:
    Thanks! Updated.
  66. in src/validation.h:277 in ed2d714bd1 outdated
    287+ *                              transaction is final.
    288  * Simulates calling SequenceLocks() with data from the tip passed in.
    289  * Optionally stores in LockPoints the resulting height and time calculated and the hash
    290  * of the block needed for calculation or skips the calculation and uses the LockPoints
    291  * passed in for evaluation.
    292  * The LockPoints should not be considered valid if CheckSequenceLocksAtTip returns false.
    


    stickies-v commented at 4:01 pm on November 10, 2022:
    This documentation now seems out of date

    stickies-v commented at 4:34 pm on November 29, 2022:

    @hebasto I think we don’t modify LockPoints anymore?

    0 * Optionally stores in LockPoints the resulting height and time calculated and the hash
    1 * of the block needed for calculation or skips the calculation and uses the LockPoints
    2 * passed in for evaluation.
    

    hebasto commented at 4:57 pm on November 29, 2022:
    Thanks! Updated.
  67. in src/validation.cpp:244 in ed2d714bd1 outdated
    223+    for (const int height : prevheights) {
    224+        // Can ignore mempool inputs since we'll fail if they had non-zero locks
    225+        if (height != index.nHeight) {
    226+            max_input_height = std::max(max_input_height, height);
    227+        }
    228+    }
    


    stickies-v commented at 4:51 pm on November 10, 2022:

    Alternative approach using replace_if and max_element

    0    // Set heights of mempool inputs to 0 since we'll fail if they had non-zero locks
    1    std::replace_if(prevheights.begin(), prevheights.end(), [&index](const int height){ return height == index.nHeight; }, 0);
    2    const auto max_input_height{*std::max_element(prevheights.begin(), prevheights.end())};
    

    hebasto commented at 4:57 pm on November 29, 2022:
    Going to leave it as this to keep this PR in more reviewable state.
  68. stickies-v commented at 5:03 pm on November 10, 2022: contributor

    Approach ACK ed2d714bd

    I think this is already a great step in the right direction, but imo there are a bunch more refactoring maintainability improvements in the touched functions that we can make. Quite a few of my comments are suggestions in that direction - I realize some may be out of scope. Even though I’d like to see them incorporated, I’m not very fussed about necessarily doing them in this PR - I’m happy working on a follow-up too.

  69. hebasto force-pushed on Nov 29, 2022
  70. hebasto commented at 4:55 pm on November 29, 2022: member

    Updated ed2d714bd1130291a2f781e644f787d421cdf26e -> 1636bc465fd96b9561de3dd21bed7f989f409de1 (pr23897.09 -> pr23897.10, diff):

  71. in src/validation.cpp:184 in 1636bc465f outdated
    175@@ -176,11 +176,79 @@ bool CheckFinalTxAtTip(const CBlockIndex& active_chain_tip, const CTransaction&
    176     return IsFinalTx(tx, nBlockHeight, nBlockTime);
    177 }
    178 
    179+namespace {
    180+std::optional<std::vector<int>> CalculatePrevHeights(
    


    stickies-v commented at 12:46 pm on December 2, 2022:
    I think this function would benefit from a docstring

    hebasto commented at 4:17 pm on December 3, 2022:
    Thanks! Updated.
  72. in src/validation.cpp:181 in 1636bc465f outdated
    175@@ -176,11 +176,79 @@ bool CheckFinalTxAtTip(const CBlockIndex& active_chain_tip, const CTransaction&
    176     return IsFinalTx(tx, nBlockHeight, nBlockTime);
    177 }
    178 
    179+namespace {
    180+std::optional<std::vector<int>> CalculatePrevHeights(
    181+    const CBlockIndex next_tip,
    


    stickies-v commented at 12:49 pm on December 2, 2022:

    any reason not to pass a reference here?

    0    const CBlockIndex& next_tip,
    

    hebasto commented at 4:17 pm on December 3, 2022:
    Thanks! Updated.
  73. in src/validation.cpp:224 in 1636bc465f outdated
    214+    // of the block *being* evaluated is what is used.
    215+    // Thus if we want to know if a transaction can be part of the
    216+    // *next* block, we need to use one more than active_chainstate.m_chain.Height()
    217+    next_tip.nHeight = tip->nHeight + 1;
    218+
    219+    auto prev_heights = CalculatePrevHeights(next_tip, coins_view, tx);
    


    stickies-v commented at 12:51 pm on December 2, 2022:

    nit

    0    auto prev_heights{CalculatePrevHeights(next_tip, coins_view, tx)};
    

    hebasto commented at 2:52 pm on December 3, 2022:

    https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Res-auto:

    Exception Avoid auto for initializer lists and in cases where you know exactly which type you want and where an initializer might require conversion.


    stickies-v commented at 4:16 pm on January 17, 2023:
    Correct me if I’m wrong, but I don’t think this applies here. The recommendation you reference is if the type is not specified, e.g. auto i{1}. In this case, CalculatePrevHeights() has a well defined return type, so list initialization seems appropriate.

    hebasto commented at 1:36 pm on January 31, 2023:
    Thanks! Updated.
  74. in src/validation.cpp:196 in 1636bc465f outdated
    187+    for (size_t i = 0; i < tx.vin.size(); ++i) {
    188+        const CTxIn& txin = tx.vin[i];
    189+        Coin coin;
    190+        if (!coins.GetCoin(txin.prevout, coin)) {
    191+            LogPrintf("ERROR: %s: Missing input %d in transaction \'%s\'\n", __func__, i, tx.GetHash().GetHex());
    192+            return std::nullopt;
    


    stickies-v commented at 12:57 pm on December 2, 2022:
    util::Result would be a good candidate here too, so you can propagate the error message back

    hebasto commented at 3:32 pm on December 3, 2022:

    Why? The error message is being logged, and it is not exposed to a user in any other way.

    https://github.com/bitcoin/bitcoin/blob/cac29f5cd66f005ce65edd697990d495a904a1f1/src/util/result.h#L22-L26

    If I missed your point, what are benefits of propagating the error message back?


    stickies-v commented at 4:22 pm on January 17, 2023:
    Same reason we want to let the callsite decide how to handle what to do when the calculation failed: with util::Result, the callsite can also decide if they want to log the error or not. In this case, there is only one callsite so it doesn’t matter much. Can always refactor later on if necessary. Happy to mark as resolved, just a thought i had.
  75. hebasto force-pushed on Dec 3, 2022
  76. hebasto commented at 4:16 pm on December 3, 2022: member

    Updated 1636bc465fd96b9561de3dd21bed7f989f409de1 -> 63d6b9a9f067b81fed6029099a4643996e8d274b (pr23897.10 -> pr23897.11):

  77. hebasto marked this as a draft on Dec 3, 2022
  78. hebasto force-pushed on Dec 3, 2022
  79. hebasto force-pushed on Dec 3, 2022
  80. hebasto marked this as ready for review on Dec 3, 2022
  81. in src/validation.h:251 in 68c0f16fa2 outdated
    244@@ -245,7 +245,9 @@ PackageMempoolAcceptResult ProcessNewPackage(Chainstate& active_chainstate, CTxM
    245 bool CheckFinalTxAtTip(const CBlockIndex& active_chain_tip, const CTransaction& tx) EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
    246 
    247 /**
    248- * Check if transaction will be BIP68 final in the next block to be created on top of tip.
    249+ * Calculate LockPoints required to check if transaction will be BIP68 final in the next block
    250+ * to be created on top of tip.
    251+ *
    252  * @param[in]   tip             Chain tip to check tx sequence locks against. For example,
    


    stickies-v commented at 4:44 pm on January 17, 2023:
    nit: “check tx sequence locks” I think should be updated to reflect that this is a pure calculation function, not a checking function?

    stickies-v commented at 11:42 am on January 30, 2023:
    E.g. “Chain tip to check tx sequence locks against.” -> “Chain tip for which tx sequence locks are calculated.”

    hebasto commented at 1:36 pm on January 31, 2023:
    Thanks! Updated.
  82. stickies-v approved
  83. stickies-v commented at 6:22 pm on January 18, 2023: contributor

    ACK 68c0f16fa2a41d6a25361f0bfa0b74ac5c8c43e2

    A couple of improvements for follow-up that I think would further benefit the readability of this code, but I agree with @hebasto’s assessment that they may reduce the reviewability of this PR and they are not blocking imo (so comments can be marked resolved, keeping track here):

  84. refactor: Add `CalculateLockPointsAtTip()` function 3bc434f459
  85. refactor: Move calculation logic out from `CheckSequenceLocksAtTip()` 75db62ba4c
  86. hebasto commented at 1:35 pm on January 31, 2023: member

    Updated 68c0f16fa2a41d6a25361f0bfa0b74ac5c8c43e2 -> 75db62ba4cae048e742ca02dc6a52b3b3d6727de (pr23897.12 -> pr23897.13, diff).

    Addressed some of @stickies-v’s comments:

  87. hebasto force-pushed on Jan 31, 2023
  88. stickies-v approved
  89. stickies-v commented at 3:09 pm on January 31, 2023: contributor
    re-ACK 75db62b
  90. achow101 commented at 3:35 pm on February 28, 2023: member
    ACK 75db62ba4cae048e742ca02dc6a52b3b3d6727de
  91. achow101 requested review from glozow on Feb 28, 2023
  92. DrahtBot requested review from aureleoules on Feb 28, 2023
  93. glozow commented at 4:48 pm on February 28, 2023: member
    code LGTM
  94. DrahtBot requested review from glozow on Feb 28, 2023
  95. glozow merged this on Feb 28, 2023
  96. glozow closed this on Feb 28, 2023

  97. hebasto deleted the branch on Feb 28, 2023
  98. sidhujag referenced this in commit 8978946b4c on Mar 1, 2023
  99. bitcoin locked this on Feb 28, 2024

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: 2024-12-26 12:12 UTC

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