wallet: when a block is disconnected, update transactions that are no longer conflicted #27145

pull ishaanam wants to merge 3 commits into bitcoin:master from ishaanam:mark_not_conflicted_inactive changing 5 files +212 −57
  1. ishaanam commented at 7:00 pm on February 22, 2023: contributor

    This implements a fix for #7315. Previously when a block was disconnected any transactions that were conflicting with transactions mined in that block were not updated to be marked as inactive. The fix implemented here is described on the Bitcoin DevWiki. A test which tested the previous behavior has also been updated.

    Second attempt at #17543

  2. DrahtBot commented at 7:00 pm on February 22, 2023: 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 rajarshimaitra, achow101, glozow, furszy
    Concept ACK ryanofsky

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

    Conflicts

    Reviewers, this pull request conflicts with the following ones:

    • #27307 (wallet: track mempool conflicts with wallet transactions by ishaanam)
    • #27286 (wallet: Keep track of the wallet’s own transaction outputs in memory by achow101)

    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.

  3. DrahtBot added the label Wallet on Feb 22, 2023
  4. ryanofsky commented at 5:19 pm on March 1, 2023: contributor

    Concept ACK on refreshing state of conflicted transactions in the wallet when a block is disconnected. I think there is a potential problem with the implementation though because it isn’t recursively marking conflicted transactions as unconfirmed, only marking the top level transactions without recursing.

    I think a good approach could be to steal from the previous implementation of this idea in commits b8b4592d985fa36dd0dddfd0e2bc6daf8df6e79c and 3c8bd6814ca88715d3d56994b10dc2c5f3e38e2d from #17543, which did recurse. That approach was also nice because it reused the existing MarkConflicted function in order to avoid code duplication. Commit ec5a89a26fa17e68b3e5928730e7d7c3e0370729 from that PR also added some more test coverage which could be used here.

  5. ishaanam force-pushed on Mar 5, 2023
  6. ishaanam force-pushed on Mar 5, 2023
  7. ishaanam commented at 6:17 am on March 5, 2023: contributor

    @ryanofsky

    I think there is a potential problem with the implementation though because it isn’t recursively marking conflicted transactions as unconfirmed, only marking the top level transactions without recursing.

    Yes, I had forgotten about this, thanks for pointing it out!

    I think a good approach could be to steal from the previous implementation of this idea in commits https://github.com/bitcoin/bitcoin/commit/b8b4592d985fa36dd0dddfd0e2bc6daf8df6e79c and https://github.com/bitcoin/bitcoin/commit/3c8bd6814ca88715d3d56994b10dc2c5f3e38e2d from #17543, which did recurse.

    I have used ideas from that PR so that the transaction state updating will recurse.

    Commit https://github.com/bitcoin/bitcoin/commit/ec5a89a26fa17e68b3e5928730e7d7c3e0370729 from that PR also added some more test coverage which could be used here.

    I have cherry-picked that commit to this branch with a few adjustments.

  8. achow101 added this to the milestone 25.0 on Mar 23, 2023
  9. in test/functional/wallet_conflicts.py:6 in 77a2e522d8 outdated
    0@@ -0,0 +1,113 @@
    1+#!/usr/bin/env python3
    2+# Copyright (c) 2023 The Bitcoin Core developers
    3+# Distributed under the MIT software license, see the accompanying
    4+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
    5+
    6+"""Test conflicts tracking with multilple txn conflicting a conflicted tx."""
    


    glozow commented at 12:58 pm on April 6, 2023:

    in 77a2e522d845eda270b2c03fd7e0a16bc4c00fbb:

    I think this comment is a little bit hard to follow. Something more descriptive might be

    0"""
    1Test that wallet correctly tracks transactions that have been conflicted by blocks, particularly during reorgs.
    2"""
    

    ishaanam commented at 4:54 am on April 8, 2023:
    I’ve updated it to this, this is much more clear.
  10. in test/functional/wallet_conflicts.py:88 in 77a2e522d8 outdated
    83+        child_conflicted = self.nodes[0].gettransaction(child_conflicted_txid)
    84+        conflicting = self.nodes[0].gettransaction(conflicting_txid_A)
    85+        # Conflicted tx should have confirmations set to the confirmations of the most conflicting tx
    86+        assert_equal(conflicted["confirmations"], -conflicting["confirmations"])
    87+        # Child should inherit conflicted state from parent
    88+        assert_equal(child_conflicted["confirmations"], -conflicting["confirmations"])
    


    glozow commented at 1:17 pm on April 6, 2023:
    We should also test who “wins” in this conflict contest and what the values are, i.e. conflicting A has 8 confirmations and conflicting B has 4 confirmations.

    ishaanam commented at 4:54 am on April 8, 2023:
    I’ve added a check for this.
  11. in test/functional/wallet_conflicts.py:41 in 77a2e522d8 outdated
    36+        self.log.info("Create a transaction with conflicted transactions spending outpoints A & B")
    37+        nA = next(tx_out["vout"] for tx_out in self.nodes[0].gettransaction(txid_conflict_from_1)["details"] if tx_out["amount"] == Decimal("10"))
    38+        nB = next(tx_out["vout"] for tx_out in self.nodes[0].gettransaction(txid_conflict_from_2)["details"] if tx_out["amount"] == Decimal("10"))
    39+        inputs_conflicted_tx = []
    40+        inputs_conflicted_tx.append({"txid": txid_conflict_from_1, "vout": nA})
    41+        inputs_conflicted_tx.append({"txid": txid_conflict_from_2, "vout": nB})
    


    glozow commented at 1:19 pm on April 6, 2023:

    nit in 77a2e522d845eda270b2c03fd7e0a16bc4c00fbb, I think this is clearer (same with the other arrays of inputs and outputs)

    0        inputs_conflicted_tx = [{"txid": txid_conflict_from_1, "vout": nA}, {"txid": txid_conflict_from_2, "vout": nB}]
    

    ishaanam commented at 4:55 am on April 8, 2023:
    Done
  12. in test/functional/wallet_conflicts.py:56 in 77a2e522d8 outdated
    51+        outputs_conflicting_tx_B = {}
    52+        outputs_conflicting_tx_B[self.nodes[0].getnewaddress()] = Decimal("9.99998")
    53+
    54+        conflicted = self.nodes[0].signrawtransactionwithwallet(self.nodes[0].createrawtransaction(inputs_conflicted_tx, outputs_conflicted_tx))
    55+        conflicting_tx_A = self.nodes[0].signrawtransactionwithwallet(self.nodes[0].createrawtransaction(inputs_conflicting_tx_A, outputs_conflicting_tx_A))
    56+        conflicting_tx_B = self.nodes[0].signrawtransactionwithwallet(self.nodes[0].createrawtransaction(inputs_conflicting_tx_B, outputs_conflicting_tx_B))
    


    glozow commented at 1:33 pm on April 6, 2023:

    Sorry to bikeshed, but I think the naming is a bit hard to follow (“conflicted” vs “conflicting” don’t immediately make sense to me, especially since both sets of transactions get conflicted at some point during the test). Might I suggest names that indicate which input(s) they spend, which node broadcasts them, and what “generation” they are? For example

    tx_AB_0_parent instead of conflicted tx_A_1 instead of conflicting_tx_A tx_B_1 instead of conflicting_tx_B

    C instead of nA for the 19.99998BTC output, because nA is already the name of the 10BTC output from one of the funding transactions. tx_C_0_child instead of child_conflicted


    ishaanam commented at 4:55 am on April 8, 2023:
    I agree, the naming here was a bit confusing. It should be fixed now.
  13. in test/functional/wallet_conflicts.py:95 in 77a2e522d8 outdated
    90+        # Node2 chain without conflicts
    91+        self.generate(self.nodes[2], 15, sync_fun=self.no_op)
    92+
    93+        # Connect node0 and node2 and wait reorg
    94+        self.connect_nodes(0, 2)
    95+        self.sync_blocks([self.nodes[0], self.nodes[2]])
    


    glozow commented at 1:34 pm on April 6, 2023:
    0        self.sync_blocks()
    

    ishaanam commented at 4:56 am on April 8, 2023:
    Done both here and one other place.
  14. in src/wallet/wallet.cpp:1469 in 49f8af533f outdated
    1464@@ -1465,8 +1465,32 @@ void CWallet::blockDisconnected(const interfaces::BlockInfo& block)
    1465     // future with a stickier abandoned state or even removing abandontransaction call.
    1466     m_last_block_processed_height = block.height - 1;
    1467     m_last_block_processed = *Assert(block.prev_hash);
    1468+
    1469+    const int& disconnect_height = block.height;
    


    glozow commented at 1:39 pm on April 6, 2023:

    in commit 49f8af533f2e6370f4ae5220f5c706e30b90d06c

    no need for const reference to integer


    ishaanam commented at 4:56 am on April 8, 2023:
    Fixed
  15. glozow commented at 1:55 pm on April 6, 2023: member

    Concept ACK

    There is a missing dash in “Co-authored-by:” in the commit messages

  16. ishaanam force-pushed on Apr 8, 2023
  17. ishaanam commented at 4:59 am on April 8, 2023: contributor
    Thanks for the review @glozow, I have addressed your comments and also made a modification to RecursiveUpdateTxState which will allow it to be more extensible.
  18. in src/wallet/wallet.cpp:1330 in c3fe92175b outdated
    1349+
    1350+    RecursiveUpdateTxState(hashTx, get_updated_state);
    1351+
    1352+}
    1353+
    1354+void CWallet::RecursiveUpdateTxState(const uint256& tx_hash, const GetUpdatedStateFn& get_updated_state) {
    


    achow101 commented at 4:09 pm on April 11, 2023:

    In c3fe92175b6ac709e4fdfd4766253c75d7ffd184 “wallet: introduce generic recursive tx state updating function”

    This could also be used by AbandonTransaction.


    ishaanam commented at 6:37 pm on April 11, 2023:
    Done
  19. in src/wallet/wallet.cpp:1373 in c3fe92175b outdated
    1373-            // Mark transaction as conflicted with this block.
    1374-            wtx.m_state = TxStateConflicted{hashBlock, conflicting_height};
    1375+        if (get_updated_state(wtx)) {
    1376             wtx.MarkDirty();
    1377             batch.WriteTx(wtx);
    1378             // Iterate over all its outputs, and mark transactions in the wallet that spend them conflicted too
    


    achow101 commented at 4:09 pm on April 11, 2023:

    In c3fe92175b6ac709e4fdfd4766253c75d7ffd184 “wallet: introduce generic recursive tx state updating function”

    The comments should be updated to avoid mentioning specific transaction states.


    ishaanam commented at 6:38 pm on April 11, 2023:
    Right, I’ve made them more generic now.
  20. ishaanam force-pushed on Apr 11, 2023
  21. in src/wallet/wallet.cpp:1330 in 06d7eb7157 outdated
    1326+
    1327+    RecursiveUpdateTxState(hashTx, get_updated_state);
    1328+
    1329+}
    1330+
    1331+void CWallet::RecursiveUpdateTxState(const uint256& tx_hash, const GetUpdatedStateFn& get_updated_state) {
    


    furszy commented at 6:37 pm on April 11, 2023:
    The GetUpdatedStateFn function isn’t really a getter, what about calling it TryUpdateState?.

    ishaanam commented at 11:47 pm on April 11, 2023:
    Yes, I’ve changed GetUpdatedStateFn to TryUpdatingStateFn, and get_updated_state to try_updating_state.
  22. in src/wallet/wallet.cpp:1317 in 06d7eb7157 outdated
    1312@@ -1336,13 +1313,29 @@ void CWallet::MarkConflicted(const uint256& hashBlock, int conflicting_height, c
    1313     if (conflictconfirms >= 0)
    1314         return;
    1315 
    1316-    // Do not flush the wallet here for performance reasons
    1317+    auto get_updated_state = [&](CWalletTx& wtx) {
    1318+        LOCK(cs_wallet);
    


    furszy commented at 6:41 pm on April 11, 2023:

    No need to re-acquire cs_wallet here (RecursiveUpdateTxState already locks cs_wallet) . Can add the exclusive lock annotation instead, e.g:

    0const auto& try_update_state = [&](CWalletTx& wtx) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) {
    1    if (conflictconfirms >= GetTxDepthInMainChain(wtx)) return false;
    2    
    3    wtx.m_state = TxStateConflicted{hashBlock, conflicting_height};
    4    return true;
    5};
    

    (same for the others)


    ishaanam commented at 11:48 pm on April 11, 2023:
    I’ve implemented this, it also fixes the CI error.
  23. ishaanam force-pushed on Apr 11, 2023
  24. in src/wallet/wallet.cpp:1464 in fadc353c67 outdated
    1459+            for (TxSpends::const_iterator _it = range.first; _it != range.second; ++_it) {
    1460+                CWalletTx& wtx = mapWallet.find(_it->second)->second;
    1461+
    1462+                if (!wtx.isConflicted()) continue;
    1463+
    1464+                auto try_updating_state = [&](CWalletTx& wtx) {
    


    furszy commented at 1:22 am on April 12, 2023:
    I would try to not provide the context by reference to the function. The lambda function’s wtx shadows the function’s wtx. Could just provide disconnect_height.

    ishaanam commented at 6:22 am on April 14, 2023:

    The lambda function’s wtx shadows the function’s wtx.

    I have renamed the lambda function’s wtx.

    Could just provide disconnect_height.

    By this do you mean providing the disconnect_height of the block to RecursiveUpdateTxState or providing the wallet tx’s conflicting_block_height to try_updating_state instead of the whole wallet tx by reference?

  25. furszy commented at 9:13 pm on April 12, 2023: member
    Haven’t finished checking the latest changes but, if you like it, https://github.com/furszy/bitcoin-core/commit/8bb14c1eaf09b0bca9efa78e1a7a7a00c61c0a29 is all yours. During the review, the functional test wasn’t clear enough for me so I spent a bit of time making it more friendly.
  26. achow101 removed this from the milestone 25.0 on Apr 13, 2023
  27. in src/wallet/wallet.cpp:1331 in f998cdbc68 outdated
    1325+    RecursiveUpdateTxState(hashTx, try_updating_state);
    1326+
    1327+}
    1328+
    1329+void CWallet::RecursiveUpdateTxState(const uint256& tx_hash, const TryUpdatingStateFn& try_updating_state) {
    1330+    LOCK(cs_wallet);
    


    furszy commented at 7:39 pm on April 13, 2023:
    In f998cdbc: This lock isn’t needed. All callers have cs_wallet acquired. Could add the lock required annotation instead.

    ishaanam commented at 6:14 am on April 14, 2023:
    Done
  28. in src/wallet/wallet.cpp:1287 in f998cdbc68 outdated
    1296-            // If the orig tx was not in block/mempool, none of its spends can be in mempool
    1297             assert(!wtx.InMempool());
    1298             wtx.m_state = TxStateInactive{/*abandoned=*/true};
    1299-            wtx.MarkDirty();
    1300-            batch.WriteTx(wtx);
    1301             NotifyTransactionChanged(wtx.GetHash(), CT_UPDATED);
    


    furszy commented at 7:45 pm on April 13, 2023:

    In f998cdbc:

    Would be good to move this NotifyTransactionChanged to RecursiveUpdateTxState too. So MarkConflicted and the BlockDisconnected txes state changes are notified to the upper layers.


    ishaanam commented at 6:14 am on April 14, 2023:
    I think that this is a good idea, but would be out of the scope of this PR and would warrant more discussion, since then it would be notifying for tx state updates that it previously did not.

    ryanofsky commented at 4:13 pm on April 17, 2023:

    In commit “wallet: introduce generic recursive tx state updating function” (f998cdbc689cc65ff0aca98ce63758c59786c1db)

    I think that this is a good idea, but would out of the scope of this PR and would warrant more discussion, since then it would be notifying for tx state updates that it previously did not.

    Agree we should avoid changing behavior, but I think the current commit might also be changing behavior by notifying before calling MarkDirty and WriteTx instead of after.

    I think the best thing to do would be to change TryUpdatingStateFn to return an enum instead of a bool like:

    0enum class TxUpdate { UNCHANGED, CHANGED, NOTIFY_CHANGED };
    1using TryUpdatingStateFn = std::function<TxUpdate(CWalletTx& wtx)>;
    

    Returning CHANGED/UNCHANGED should make the code clearer anyway because it’s less obvious what true and false mean.


    ishaanam commented at 9:42 pm on April 18, 2023:
    I have implemented this suggestion, I agree that it is much clearer now.
  29. ishaanam force-pushed on Apr 14, 2023
  30. ishaanam commented at 6:25 am on April 14, 2023: contributor

    Thanks for the review @achow101 and @furszy!

    Haven’t finished checking the latest changes but, if you like it, https://github.com/furszy/bitcoin-core/commit/8bb14c1eaf09b0bca9efa78e1a7a7a00c61c0a29 is all yours. During the review, the functional test wasn’t clear enough for me so I spent a bit of time making it more friendly.

    Thanks, I’ll use these improvements here once I review them. Edit: I’ve added the suggested changes to the functional tests.

  31. ishaanam force-pushed on Apr 14, 2023
  32. in src/wallet/wallet.cpp:1292 in f998cdbc68 outdated
    1288-        CWalletTx& wtx = it->second;
    1289+    auto try_updating_state = [&](CWalletTx& wtx) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) {
    1290         int currentconfirm = GetTxDepthInMainChain(wtx);
    1291-        // If the orig tx was not in block, none of its spends can be
    1292         assert(currentconfirm <= 0);
    1293-        // if (currentconfirm < 0) {Tx and spends are already conflicted, no need to abandon}
    


    ryanofsky commented at 6:47 pm on April 17, 2023:

    In commit “wallet: introduce generic recursive tx state updating function” (f998cdbc689cc65ff0aca98ce63758c59786c1db)

    I think as long as this code is still calling GetTxDepthInMainChain, it would be better to keep the 3 comments that were here instead of deleting them, because they explain why the asserts are valid and why conflicted transactions are ignored


    ishaanam commented at 9:43 pm on April 18, 2023:
    I implemented your next suggestion about simplifying the code in the lambda, so the comments are a bit different now but I did add them back.
  33. in src/wallet/wallet.cpp:1281 in f998cdbc68 outdated
    1285-        done.insert(now);
    1286-        auto it = mapWallet.find(now);
    1287-        assert(it != mapWallet.end());
    1288-        CWalletTx& wtx = it->second;
    1289+    auto try_updating_state = [&](CWalletTx& wtx) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) {
    1290         int currentconfirm = GetTxDepthInMainChain(wtx);
    


    ryanofsky commented at 6:58 pm on April 17, 2023:

    In commit “wallet: introduce generic recursive tx state updating function” (f998cdbc689cc65ff0aca98ce63758c59786c1db)

    It seems like the code in this lambda would be simpler it avoided calling GetTxDepthInMainChain. The following should be equivalent:

    0// If the orig tx was not in block/mempool, none of its spends can be.
    1assert(!wtx.isConfirmed());
    2assert(!wtx.InMempool());
    3// If already conflicted or abandoned, no need to set abandoned.
    4if (!wtx.isConflicted() && !wtx.isAbandoned()) {
    5    wtx.m_state = TxStateInactive{/*abandoned=*/true};
    6    ...
    7}
    

    Free to ignore this suggestion if you think it would complicate things, though. The cleanup is orthogonal and could be done separately.


    ishaanam commented at 9:44 pm on April 18, 2023:
    This is simpler and it is a small change so I’ve implemented it in this commit.
  34. in src/wallet/wallet.cpp:1334 in f998cdbc68 outdated
    1312@@ -1336,13 +1313,27 @@ void CWallet::MarkConflicted(const uint256& hashBlock, int conflicting_height, c
    1313     if (conflictconfirms >= 0)
    1314         return;
    1315 
    1316-    // Do not flush the wallet here for performance reasons
    


    ryanofsky commented at 7:04 pm on April 17, 2023:

    In commit “wallet: introduce generic recursive tx state updating function” (f998cdbc689cc65ff0aca98ce63758c59786c1db)

    This also seems like a useful comment. Would keep instead of deleting


    ishaanam commented at 9:45 pm on April 18, 2023:
    I have added it back.
  35. in src/wallet/wallet.cpp:1321 in f998cdbc68 outdated
    1320+            return true;
    1321+        }
    1322+        return false;
    1323+    };
    1324+
    1325+    RecursiveUpdateTxState(hashTx, try_updating_state);
    


    ryanofsky commented at 7:11 pm on April 17, 2023:

    In commit “wallet: introduce generic recursive tx state updating function” (f998cdbc689cc65ff0aca98ce63758c59786c1db)

    This code pretty opaque, would suggest copying the previous comment here “Iterate over all its outputs, and mark transactions in the wallet that spend them conflicted too” or something equivalent.


    ishaanam commented at 9:45 pm on April 18, 2023:
    Done
  36. in src/wallet/wallet.cpp:1314 in f998cdbc68 outdated
    1312@@ -1336,13 +1313,27 @@ void CWallet::MarkConflicted(const uint256& hashBlock, int conflicting_height, c
    1313     if (conflictconfirms >= 0)
    1314         return;
    1315 
    1316-    // Do not flush the wallet here for performance reasons
    1317+    auto try_updating_state = [&](CWalletTx& wtx) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) {
    1318+        if (conflictconfirms < GetTxDepthInMainChain(wtx)) {
    1319+            wtx.m_state = TxStateConflicted{hashBlock, conflicting_height};
    


    ryanofsky commented at 7:12 pm on April 17, 2023:

    In commit “wallet: introduce generic recursive tx state updating function” (f998cdbc689cc65ff0aca98ce63758c59786c1db)

    Would suggest keeping the previous comment here instead of deleting it. It’s not obvious what the check means or what’s happening in the block:

    0// Block is 'more conflicted' than current confirm; update.
    1// Mark transaction as conflicted with this block.
    

    ishaanam commented at 9:45 pm on April 18, 2023:
    Done
  37. fanquake added this to the milestone 26.0 on Apr 18, 2023
  38. ishaanam force-pushed on Apr 18, 2023
  39. ishaanam force-pushed on Apr 18, 2023
  40. ishaanam force-pushed on Apr 18, 2023
  41. in src/wallet/wallet.cpp:1280 in 26aff02b3f outdated
    1291-        assert(currentconfirm <= 0);
    1292-        // if (currentconfirm < 0) {Tx and spends are already conflicted, no need to abandon}
    1293-        if (currentconfirm == 0 && !wtx.isAbandoned()) {
    1294-            // If the orig tx was not in block/mempool, none of its spends can be in mempool
    1295-            assert(!wtx.InMempool());
    1296+    auto try_updating_state = [&](CWalletTx& wtx) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) {
    


    furszy commented at 11:39 am on April 22, 2023:

    nit: if you have to retouch. Can remove the context ref.

    0    auto try_updating_state = [](CWalletTx& wtx) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) {
    

    ishaanam commented at 3:08 pm on April 22, 2023:
    Done
  42. wallet: introduce generic recursive tx state updating function
    This commit also changed `MarkConflicted` and `AbandonTransaction`
    to use this new function
    
    Co-authored-by: ariard <antoine.riard@gmail.com>
    096487c4dc
  43. ishaanam force-pushed on Apr 22, 2023
  44. ishaanam commented at 3:08 pm on April 22, 2023: contributor
    Rebased to fix failing CI.
  45. in test/functional/wallet_abandonconflict.py:234 in 62cdcd02cd outdated
    232         balance = newbalance
    233 
    234-        # There is currently a minor bug around this and so this test doesn't work.  See Issue #7315
    235-        # Invalidate the block with the double spend and B's 10 BTC output should no longer be available
    236-        # Don't think C's should either
    237+        # Invalidate the block with the double spend. B & C's 10 BTC outputs should no longer be available
    


    furszy commented at 1:15 pm on May 4, 2023:

    Let me see if I’m following this:

    Tx_AB1 spends B, and Tx_ABC2 (the child of Tx_AB1) spends C. So, when the double spend tx gets disconnected, Tx_AB1 state changes to inactive, marking output B and C as spent again.

    Which ends up with the txes stuck now right?, and the user need to manually re-submit them.


    ishaanam commented at 3:39 pm on May 5, 2023:
    Yes, the user would need to manually re-submit them or wait for the wallet to re-submit them automatically.

    furszy commented at 12:52 pm on May 12, 2023:
    good. Not sure if there is any but would be nice to have coverage for the automatic re-submission. Would just need to mock the node time 24hs in the future and see if they get re-submitted.

    ishaanam commented at 2:59 pm on May 14, 2023:
    There are some tests for that here: https://github.com/bitcoin/bitcoin/blob/master/test/functional/wallet_resendwallettransactions.py. Or did you mean that a test for that should be added for this PR specifically?

    furszy commented at 2:19 am on May 26, 2023:
    yeah, great if that is already covered. (sorry for the delayed answer)
  46. furszy approved
  47. furszy commented at 1:25 pm on May 4, 2023: member
    ACK 6f1d3948
  48. wallet, tests: mark unconflicted txs as inactive
    In `blockDisconnected`, for each transaction in the block, look
    for any wallet transactions spending the same inputs. If any of
    these transactions were marked conflicted, they are now marked as
    inactive.
    
    Co-authored-by: ariard <antoine.riard@gmail.com>
    dced203162
  49. Add wallets_conflicts
    Test the case of a tx being conflicted by multiple
    txs with different depths. The conflicted tx is also spent by
    a child tx for which confirmation status is tied to the parent's.
    After a reorg of conflicting txs, the conflicted status should be
    undone properly.
    
    Co-authored-by: furszy <mfurszy@protonmail.com>
    89df7987c2
  50. ishaanam force-pushed on May 14, 2023
  51. ishaanam commented at 2:52 pm on May 14, 2023: contributor
    I’ve fixed a bug which resulted in the conflicted tx not being marked as inactive if the conflicting tx was not in mapTxSpends.
  52. rajarshimaitra commented at 11:27 am on May 22, 2023: contributor

    tACK 89df7987c2f1eea42454c2b0efc31a924fbfd3a8.

    Changes look very clean. We reviewed it in out weekly club and went over the new code addition.

    Ran the tests on the master branch and saw it failing assert_equal(conflicted["confirmations"], 0). In the master conflicting transaction’s state doesn’t update after reorg.

  53. DrahtBot requested review from furszy on May 22, 2023
  54. achow101 requested review from ryanofsky on May 22, 2023
  55. achow101 commented at 5:02 pm on May 25, 2023: member
    ACK 89df7987c2f1eea42454c2b0efc31a924fbfd3a8
  56. furszy commented at 2:15 am on May 26, 2023: member

    I’ve fixed a bug which resulted in the conflicted tx not being marked as inactive if the conflicting tx was not in mapTxSpends.

    Could you expand this?

    Not sure if I follow the latest push, where only the inequality was changed. If mapTxSpends.count(outpoint) == 1, the outpoint was spent only once, so the wallet shouldn’t contain any conflicting tx.

  57. glozow commented at 5:51 pm on May 26, 2023: member
    ACK 89df7987c2f1eea42454c2b0efc31a924fbfd3a8
  58. ishaanam commented at 2:08 am on May 27, 2023: contributor

    If mapTxSpends.count(outpoint) == 1, the outpoint was spent only once, so the wallet shouldn’t contain any conflicting tx.

    This is not necessarily always true, because even though the conflicted wallet transaction must always have its spends added to mapTxSpends, this is not the case for all conflicting transactions.

    For example, let’s say that Alice has a utxo. She creates a transaction, tx1, spending this utxo to Bob. AddToSpends will get called on Bob’s node for this transaction, because Bob is the recipient. Alice then creates a conflicting transaction to tx1, called tx2, which sends the funds back to herself. However, Bob’s node does not call AddToSpends on tx2, because Bob is not the recipient. If tx2 gets mined, but then that block is disconnected from our node, Bob’s mapTxSpends will only show a single spend for Alice’s utxo in mapTxSpends, even though there are technically two.

  59. furszy commented at 3:28 pm on May 27, 2023: member
    Ok nice, great. Thanks for the details.
  60. furszy approved
  61. furszy commented at 3:33 pm on May 27, 2023: member

    Tested ACK 89df7987

    For example, let’s say that Alice has a utxo. She creates a transaction, tx1, spending this utxo to Bob. AddToSpends will get called on Bob’s node for this transaction, because Bob is the recipient. Alice then creates a conflicting transaction to tx1, called tx2, which sends the funds back to herself. However, Bob’s node does not call AddToSpends on tx2, because Bob is not the recipient. If tx2 gets mined, but then that block is disconnected from our node, Bob’s mapTxSpends will only show a single spend for Alice’s utxo in mapTxSpends, even though there are technically two.

    Made a quick unit test and all good. Good finding. Would be good to have a real functional test in the future.

  62. ishaanam commented at 4:08 pm on May 27, 2023: contributor

    Would be good to have a real functional test in the future.

    #27307 tests this behavior, which is how I found this bug.

  63. achow101 merged this on May 27, 2023
  64. achow101 closed this on May 27, 2023

  65. joostjager referenced this in commit 7b403b4b5e on May 28, 2023
  66. sidhujag referenced this in commit 5cada6eaef on May 29, 2023
  67. ishaanam deleted the branch on Nov 30, 2023

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-10-04 22:12 UTC

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