wallet: fix coin selection tracing to return -1 when no change pos #29272

pull remyers wants to merge 2 commits into bitcoin:master from remyers:cs-usdt-fixup changing 2 files +40 −5
  1. remyers commented at 7:56 am on January 18, 2024: contributor

    This is a bugfix for from when optional was introduced for change_pos in the wallet. When optional change_pos is unset, we should return -1 and not 0.

    I added two new checks to the test/functional/interface_usdt_coinselection.py which adds coverage for the situations when normal_create_tx_internal and aps_create_tx_internal events occur with no change.

    You can reproduce this bug using the coin-selection-simulation scripts as described in [issue #16](https://github.com/achow101/coin-selection-simulation/issues/16). You can also run the interface_usdt_coinselection.py test without the changes to wallet/spend.cpp.

  2. DrahtBot commented at 7:56 am on January 18, 2024: contributor

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

    Code Coverage

    For detailed information about the code coverage, see the test coverage report.

    Reviews

    See the guideline for information on the review process.

    Type Reviewers
    ACK murchandamus, 0xB10C, achow101

    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:

    • #26593 (tracing: Only prepare tracepoint arguments when actually tracing by 0xB10C)

    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 Jan 18, 2024
  4. remyers commented at 7:59 am on January 18, 2024: contributor
    You might want to take a look at this @murchandamus. I discovered the problem while trying to repo your coingrinder results with the coin-selection-simulation scripts.
  5. in src/wallet/spend.cpp:1358 in cc1bd8fcff outdated
    1354@@ -1355,7 +1355,7 @@ util::Result<CreatedTransactionResult> CreateTransaction(
    1355         // if fee of this alternative one is within the range of the max fee, we use this one
    1356         const bool use_aps{txr_grouped.has_value() ? (txr_grouped->fee <= txr_ungrouped.fee + wallet.m_max_aps_fee) : false};
    1357         TRACE5(coin_selection, aps_create_tx_internal, wallet.GetName().c_str(), use_aps, txr_grouped.has_value(),
    1358-               txr_grouped.has_value() ? txr_grouped->fee : 0, txr_grouped.has_value() && txr_grouped->change_pos.has_value() ? *txr_grouped->change_pos : 0);
    1359+               txr_grouped.has_value() ? txr_grouped->fee : 0, txr_grouped.has_value() && txr_grouped->change_pos.has_value() ? *txr_grouped->change_pos : -1);
    


    0xB10C commented at 8:37 am on January 18, 2024:
    nit: I think this tracepoint would be easier to reason about if it was one line per argument. Not sure if this PR is the right place to reformat it.

    remyers commented at 11:27 am on January 20, 2024:
    This seems like a good practice and is consistent with other TRACEx uses in the code where x > 1. This would also makes it easier to check the correct number of parameters is used if PR #26593 is adopted. Fixed in 350606d.
  6. DrahtBot added the label CI failed on Jan 18, 2024
  7. DrahtBot commented at 8:42 am on January 18, 2024: contributor

    🚧 At least one of the CI tasks failed. Make sure to run all tests locally, according to the documentation.

    Possibly this is due to a silent merge conflict (the changes in this pull request being incompatible with the current code in the target branch). If so, make sure to rebase on the latest commit of the target branch.

    Leave a comment here, if you need help tracking down a confusing failure.

    Debug: https://github.com/bitcoin/bitcoin/runs/20605138868

  8. in src/wallet/spend.cpp:1340 in cc1bd8fcff outdated
    1336@@ -1337,7 +1337,7 @@ util::Result<CreatedTransactionResult> CreateTransaction(
    1337 
    1338     auto res = CreateTransactionInternal(wallet, vecSend, change_pos, coin_control, sign);
    1339     TRACE4(coin_selection, normal_create_tx_internal, wallet.GetName().c_str(), bool(res),
    1340-           res ? res->fee : 0, res && res->change_pos.has_value() ? *res->change_pos : 0);
    1341+           res ? res->fee : 0, res && res->change_pos.has_value() ? *res->change_pos : -1);
    


    0xB10C commented at 8:48 am on January 18, 2024:

    CI is unhappy here:

    0(mocktwallet/spend.cpp:1339:5: runtime error: implicit conversion from type 'int' of value -1 (32-bit, signed) to type 'unsigned int' changed the value to 4294967295 (32-bit, unsigned)
    1    [#0](/bitcoin-bitcoin/0/) 0x5589c92846e6 in wallet::CreateTransaction(wallet::CWallet&, std::vector<wallet::CRecipient, std::allocator<wallet::CRecipient>> const&, std::optional<unsigned int>, wallet::CCoinControl const&, bool) src/wallet/spend.cpp:1339:5
    2    [#1](/bitcoin-bitcoin/1/) 0x5589c8d8617d in wallet::spend_tests::wallet_duplicated_preset_inputs_test::test_method() src/wallet/test/spend_tests.cpp:100:53
    3   ...
    4
    5SUMMARY: UndefinedBehaviorSanitizer: implicit-integer-sign-change wallet/spend.cpp:1339:5 in 
    

    remyers commented at 10:43 am on January 18, 2024:
    I think wrapping the trace parameter in static_cast<int32_t>() is the correct way to resolve this because change_pos is otherwise a std::optional<unsigned int>. In tracing.md change_pos is defined as int32 so it should always be returned as a signed 32-bit int.

    remyers commented at 11:24 am on January 18, 2024:
    .. or maybe static_cast is overkill, a cast with int32_t() seems to be what’s recommended in developer-notes.md .

    remyers commented at 10:37 pm on January 18, 2024:
    I had my casting on the wrong value - we need to cast the unsigned optional value. Fixed in e701982.
  9. murchandamus commented at 9:51 pm on January 18, 2024: contributor
    Returning -1 if there is no change seems reasonable to me, but it looks like it’s cast to an unsigned int somewhere else in the stack. I’m not familiar with the tracing framework interna, so it’s not obvious to me how to fix this.
  10. achow101 commented at 10:03 pm on January 18, 2024: member
    ACK 8c6d266aa61ffc409b50ece4e649f7a74ec7acb7
  11. remyers force-pushed on Jan 18, 2024
  12. DrahtBot removed the label CI failed on Jan 18, 2024
  13. in test/functional/interface_usdt_coinselection.py:218 in e701982c74 outdated
    213+        # 5. aps_create_tx_internal (type 4)
    214+        wallet.sendtoaddress(address=wallet.getnewaddress(), amount=10, subtractfeefromamount=True, avoid_reuse=False)
    215+        events = self.get_tracepoints([1, 2, 3, 1, 4])
    216+        success, use_aps, algo, waste, change_pos = self.determine_selection_from_usdt(events)
    217+        assert_equal(success, True)
    218+        assert_equal(change_pos, -1)
    


    murchandamus commented at 3:45 pm on January 19, 2024:

    There were a couple transactions before, each sending 10 BTC to the wallet itself. So the wallet should still have a balance of 50 – fees BTC, and presumably 3 UTXOs of 10 BTC, 10 BTC, and 30 - fees BTC. I‘m not sure why APS (avoid partial spend) would make a difference here. APS just means that we’ll force use of all coins associated with the same scriptpubkey at once rather than a single UTXO if there is address reuse, but it seems to me that funds are always sent to new addresses here.

    It seems to me that this test works because BnB is disabled due subtractfeefromamount being active, Knapsack finding the single input solution and Knapsack being run before SRD and therefore Knapsack’s result being preferred, but it’s not obvious why this transaction would generally end up not having any change if e.g. we change how we pick the input set from the various coin selection algorithm results. It might be more future proof to get the current balance, and then send the entire balance while setting subtractfeefromamount.

    Usually I would tend to suggest sendall to make a changeless transaction, but sendall doesn’t use coinselection which is what we are actually trying to test here.

    So maybe, these two test-cases could just be folded into one and the distinction for APS could be dropped, or the first should perhaps also spend the entire balance.


    remyers commented at 4:10 pm on January 19, 2024:
    Thanks for the explanation; although the next test after this one does what you suggest, it doesn’t trigger the fall-through APS path. I’ll see if I can rework this test to better match a real APS situation.

    remyers commented at 11:01 am on January 20, 2024:
    Fixed in 39d8621. I also updated the description to make it clearer that the APS path is tested due to APS being initially disabled.
  14. murchandamus approved
  15. murchandamus commented at 4:04 pm on January 19, 2024: contributor
    tACK with nit: e701982c741edd2182690ddf4628e4c079a5185e
  16. DrahtBot requested review from achow101 on Jan 19, 2024
  17. remyers force-pushed on Jan 20, 2024
  18. DrahtBot added the label CI failed on Jan 20, 2024
  19. DrahtBot commented at 11:46 am on January 20, 2024: contributor

    🚧 At least one of the CI tasks failed. Make sure to run all tests locally, according to the documentation.

    Possibly this is due to a silent merge conflict (the changes in this pull request being incompatible with the current code in the target branch). If so, make sure to rebase on the latest commit of the target branch.

    Leave a comment here, if you need help tracking down a confusing failure.

    Debug: https://github.com/bitcoin/bitcoin/runs/20685191675

  20. DrahtBot removed the label CI failed on Jan 20, 2024
  21. murchandamus commented at 1:50 pm on January 20, 2024: contributor

    ACK, 350606d6093563e93b8faa059af92e19f9d439e5. The final code LGTM.

    Nit: It seems to me that the first three commits are iterating on the same conceptual change. I would prefer if those three commits were squashed into one so we immediately arrive at the final version. The refactor of the trace output should stay a separate commit, though.

  22. wallet: fix coin selection tracing to return -1 when no change pos 2d58629ee6
  23. Move TRACEx parameters to seperate lines d55fdb1a49
  24. remyers force-pushed on Jan 20, 2024
  25. furszy commented at 2:08 pm on January 20, 2024: member

    It seems to me that the first three commits are iterating on the same conceptual change.

    Also, to keep a clean commits history, each commit should pass the CI tasks independently. And the first one alone is not passing.

  26. remyers commented at 2:09 pm on January 20, 2024: contributor

    Nit: It seems to me that the first three commits are iterating on the same conceptual change. I would prefer if those three commits were squashed into one so we immediately arrive at the final version. The refactor of the trace output should stay a separate commit, though.

    Good point, I meant to do that; squashed in d55fdb1.

  27. murchandamus commented at 2:35 pm on January 20, 2024: contributor
    ACK d55fdb1a495190e213b1b5127f5d91e4a409765e
  28. 0xB10C commented at 11:08 pm on January 20, 2024: contributor

    ACK d55fdb1a495190e213b1b5127f5d91e4a409765e

    The int32_t cast and the tracing related changes look good to me. Thanks for reformatting the tracepoints! I’m not familiar enough with coin selection to comment on the test changes.

  29. achow101 commented at 7:20 pm on January 23, 2024: member
    ACK d55fdb1a495190e213b1b5127f5d91e4a409765e
  30. DrahtBot removed review request from achow101 on Jan 23, 2024
  31. achow101 merged this on Jan 23, 2024
  32. achow101 closed this on Jan 23, 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-21 15:12 UTC

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