Refactor BnB tests #29532

pull murchandamus wants to merge 7 commits into bitcoin:master from murchandamus:2024-03-coinselection_tests changing 3 files +198 βˆ’162
  1. murchandamus commented at 10:37 pm on March 1, 2024: contributor

    This PR is splitting off some of the improvements made in #28985 and starts addressing the issues raised in #27754.

    I aim to completely replace coinselector_tests with coinselection_tests. The goal is to generally use coins created per a nominal effective value so we can get away from testing with CoinSelectionParams that are non-representative and effectuate counterintuitive behavior such as feerate = 0 or cost_of_change = 0

  2. DrahtBot commented at 10:37 pm on March 1, 2024: contributor

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

    Code Coverage & Benchmarks

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

    Reviews

    See the guideline for information on the review process.

    Type Reviewers
    Concept ACK jasonribble, ismaelsadeeq

    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.

  3. murchandamus force-pushed on Mar 5, 2024
  4. DrahtBot added the label CI failed on Mar 23, 2024
  5. DrahtBot removed the label CI failed on Mar 28, 2024
  6. murchandamus marked this as ready for review on Apr 9, 2024
  7. murchandamus commented at 12:03 pm on April 9, 2024: contributor
    Pinging @furszy, @achow101, @S3RK, as discussed
  8. DrahtBot added the label CI failed on Apr 19, 2024
  9. DrahtBot removed the label CI failed on Apr 23, 2024
  10. in src/wallet/test/coinselection_tests.cpp:69 in 3dff8f0490 outdated
    64+}
    65+
    66+/** Make multiple coins with given effective values */
    67+static void AddCoins(std::vector<COutput>& utxo_pool, std::vector<CAmount> coins, CFeeRate feerate = default_cs_params.m_effective_feerate)
    68+{
    69+    for (int c : coins) {
    


    achow101 commented at 2:55 pm on April 24, 2024:

    In 3dff8f0490b4b92c4adf229a828063dfda6d3a80 “[test] Create coinselection_tests”

    The elements in coins are CAmount, not int.


    murchandamus commented at 7:06 pm on May 31, 2024:
    Thanks, fixed
  11. in src/wallet/test/coinselection_tests.cpp:78 in 3dff8f0490 outdated
    73+
    74+/** Group available coins into OutputGroups */
    75+inline std::vector<OutputGroup>& GroupCoins(const std::vector<COutput>& available_coins, const CoinSelectionParams& cs_params = default_cs_params, bool subtract_fee_outputs = false)
    76+{
    77+    static std::vector<OutputGroup> static_groups;
    78+    static_groups.clear();
    


    achow101 commented at 2:57 pm on April 24, 2024:

    In 3dff8f0490b4b92c4adf229a828063dfda6d3a80 “[test] Create coinselection_tests”

    There doesn’t seem to be a purpose for having a static variable here. We basically treat it as temporary since it’s cleared every time.


    murchandamus commented at 5:01 pm on June 6, 2024:
    Removed the static keyword and the clear as suggested.
  12. in src/wallet/test/coinselection_tests.cpp:75 in 3dff8f0490 outdated
    70+        utxo_pool.push_back(MakeCoin(c, true, 0, feerate));
    71+    }
    72+}
    73+
    74+/** Group available coins into OutputGroups */
    75+inline std::vector<OutputGroup>& GroupCoins(const std::vector<COutput>& available_coins, const CoinSelectionParams& cs_params = default_cs_params, bool subtract_fee_outputs = false)
    


    achow101 commented at 2:58 pm on April 24, 2024:

    In 3dff8f0490b4b92c4adf229a828063dfda6d3a80 “[test] Create coinselection_tests”

    Since the expected pattern is to always GroupCoins(utxo_pool), it might make sense to make utxo_pool a std::vector<OutputGroup> and combine this function with AddCoins.


    murchandamus commented at 3:57 pm on June 27, 2024:
    Amended all the functions to directly create OutputGroups instead.
  13. in src/wallet/test/coinselection_tests.cpp:111 in e286c6470b outdated
    103@@ -104,5 +104,40 @@ static bool EquivalentResult(const SelectionResult& a, const SelectionResult& b)
    104     return ret.first == a_amts.end() && ret.second == b_amts.end();
    105 }
    106 
    107+static void TestBnBSuccess(std::string test_title, std::vector<COutput>& utxo_pool, const CAmount& selection_target, const std::vector<CAmount>& expected_input_amounts, const CFeeRate& feerate = default_cs_params.m_effective_feerate)
    108+{
    109+    SelectionResult expected_result(CAmount(0), SelectionAlgorithm::BNB);
    110+    CAmount expected_amount = 0;
    111+    for (int input_amount : expected_input_amounts) {
    


    achow101 commented at 2:59 pm on April 24, 2024:

    In e286c6470bbaa1358fecb7c5a7e109583d158780 “[test] Recreate simple BnB success tests”

    The elements of expected_input_amounts are CAmount not int.


    murchandamus commented at 8:24 pm on May 31, 2024:
    Thanks, fixed
  14. in src/wallet/test/coinselection_tests.cpp:120 in e286c6470b outdated
    115+        group.Insert(std::make_shared<COutput>(coin), /*ancestors=*/ 0, /*descendants=*/ 0);
    116+        expected_result.AddInput(group);
    117+    }
    118+
    119+    const auto result = SelectCoinsBnB(GroupCoins(utxo_pool), selection_target, /*cost_of_change=*/ default_cs_params.m_cost_of_change, /*max_weight=*/MAX_STANDARD_TX_WEIGHT);
    120+    BOOST_CHECK_MESSAGE(result, "BnB-Success: " + test_title);
    


    achow101 commented at 3:02 pm on April 24, 2024:

    In e286c6470bbaa1358fecb7c5a7e109583d158780 “[test] Recreate simple BnB success tests”

    The *_MESSAGE functions print out the message on failure, not on success. So the message should be something that better indicates what the failure was, not something that might suggest something was successful.


    murchandamus commented at 2:52 pm on June 27, 2024:
    Made all tests output messages indicate what went wrong in the success case.
  15. in src/wallet/test/coinselection_tests.cpp:56 in 3dff8f0490 outdated
    51+}
    52+
    53+static const CoinSelectionParams default_cs_params = init_default_params();
    54+
    55+/** Make one coin that either has a given effective value (default) or a given amount (`is_eff_value = false`). */
    56+static COutput MakeCoin(const CAmount& amount, bool is_eff_value = true, int nInput = 0, CFeeRate feerate = default_cs_params.m_effective_feerate, int custom_spending_vsize = 68)
    


    furszy commented at 7:23 pm on May 1, 2024:
    In 3dff8f0490: As far as I can see, the nInput arg is always 0 in this PR and also in #28985. Can we remove it?

    murchandamus commented at 7:05 pm on May 31, 2024:
    I have removed nInput from the function parameters of MakeCoin(…).

    murchandamus commented at 5:45 pm on June 27, 2024:
    And then actually also removed it as a variable in the function by assuming that there generally is only one output.
  16. in src/wallet/test/coinselection_tests.cpp:23 in 3dff8f0490 outdated
    18+#include <wallet/test/wallet_test_fixture.h>
    19+#include <wallet/wallet.h>
    20+
    21+#include <algorithm>
    22+#include <boost/test/unit_test.hpp>
    23+#include <random>
    


    furszy commented at 7:28 pm on May 1, 2024:

    In https://github.com/bitcoin/bitcoin/commit/3dff8f0490b4b92c4adf229a828063dfda6d3a80:

    I don’t think we need all this includes. For instance, no wallet is used and <wallet/wallet.h> is included, no fee function is used and wallet/fees.h is included, <random.h> and <random> are included.

    Maybe run IOWY to clean this up.


    murchandamus commented at 7:05 pm on May 31, 2024:

    I was able to remove all except for

    0#include <consensus/amount.h>
    1#include <wallet/coinselection.h>
    2#include <wallet/test/wallet_test_fixture.h>
    3
    4#include <boost/test/unit_test.hpp>
    

    I’m not familiar with IOWY and could not turn up anything with a websearch, what is that?


    furszy commented at 4:10 pm on June 27, 2024:

    I’m not familiar with IOWY and could not turn up anything with a websearch, what is that?

    Upps, the acronym is IWYU from include-what-you-use. I wrote it fast as IOWY “include only what you use”. Should stop using acronyms..


    murchandamus commented at 5:45 pm on June 27, 2024:
    I see, thanks for clarifying. I think I cleared out everything that I don’t use.
  17. in src/wallet/test/coinselection_tests.cpp:123 in e286c6470b outdated
    118+
    119+    const auto result = SelectCoinsBnB(GroupCoins(utxo_pool), selection_target, /*cost_of_change=*/ default_cs_params.m_cost_of_change, /*max_weight=*/MAX_STANDARD_TX_WEIGHT);
    120+    BOOST_CHECK_MESSAGE(result, "BnB-Success: " + test_title);
    121+    BOOST_CHECK(EquivalentResult(expected_result, *result));
    122+    BOOST_CHECK_EQUAL(result->GetSelectedValue(), expected_amount);
    123+    expected_result.Clear();
    


    furszy commented at 1:59 pm on May 3, 2024:

    In e286c6470b:

    No need to clear expected_result. It’s created within this function.


    murchandamus commented at 8:25 pm on May 31, 2024:
    Thanks, fixed
  18. furszy commented at 3:08 pm on May 3, 2024: member

    I’m not completely sure about 584e524eb57444d7192df1049cafde9ccc480406. The commit description says

    Originally these tests verified that at a SelectCoins level that a solution with fewer inputs gets preferred at high feerates, and a solution with more inputs gets preferred at low feerates. This outcome relies on the behavior of BnB, so we move these tests under the umbrella of BnB tests.

    It is true that the outcome relies only on the BnB behavior currently but that might not be true in the future. There could be other algorithm clashing with it.

  19. murchandamus force-pushed on Jun 27, 2024
  20. in src/wallet/test/coinselection_tests.cpp:14 in 71606e7bd1 outdated
     9+#include <boost/test/unit_test.hpp>
    10+
    11+namespace wallet {
    12+BOOST_FIXTURE_TEST_SUITE(coinselection_tests, WalletTestingSetup)
    13+
    14+static int nextLockTime = 0;
    


    murchandamus commented at 4:03 pm on June 27, 2024:
    Should be snake_case.
  21. in src/wallet/test/coinselection_tests.cpp:45 in 71606e7bd1 outdated
    40+
    41+/** Make one coin that either has a given effective value (default) or a given amount (`is_eff_value = false`). */
    42+static OutputGroup MakeCoin(const CAmount& amount, bool is_eff_value = true, CoinSelectionParams cs_params = default_cs_params, int custom_spending_vsize = 68)
    43+{
    44+    // Always assume that we only have one input
    45+    const int nInput = 0;
    


    murchandamus commented at 4:04 pm on June 27, 2024:
    nInput is unused, let’s remove it.
  22. in src/wallet/test/coinselection_tests.cpp:52 in 71606e7bd1 outdated
    47+    tx.vout.resize(nInput + 1);
    48+    CAmount fees = cs_params.m_effective_feerate.GetFee(custom_spending_vsize);
    49+    tx.vout[nInput].nValue = amount + int(is_eff_value) * fees;
    50+    tx.nLockTime = nextLockTime++;        // so all transactions get different hashes
    51+    OutputGroup group(cs_params);
    52+    group.Insert(std::make_shared<COutput>(COutPoint(tx.GetHash(), nInput), tx.vout.at(nInput), /*depth=*/ 1, /*input_bytes=*/ custom_spending_vsize, /*spendable=*/ true, /*solvable=*/ true, /*safe=*/ true, /*time=*/ 0, /*from_me=*/ false, /*fees=*/ fees), /*ancestors=*/0, /*descendants=*/0);
    


    murchandamus commented at 4:05 pm on June 27, 2024:
    Remove spaces after comments describing params.
  23. in src/wallet/test/coinselection_tests.cpp:56 in 71606e7bd1 outdated
    51+    OutputGroup group(cs_params);
    52+    group.Insert(std::make_shared<COutput>(COutPoint(tx.GetHash(), nInput), tx.vout.at(nInput), /*depth=*/ 1, /*input_bytes=*/ custom_spending_vsize, /*spendable=*/ true, /*solvable=*/ true, /*safe=*/ true, /*time=*/ 0, /*from_me=*/ false, /*fees=*/ fees), /*ancestors=*/0, /*descendants=*/0);
    53+    return group;
    54+}
    55+
    56+/** Make multiple coins with given effective values */
    


    murchandamus commented at 4:06 pm on June 27, 2024:
    Outdated comment, it’s an OutputGroup not coin now.
  24. in src/wallet/test/coinselection_tests.cpp:84 in e62bf2d346 outdated
    80@@ -80,5 +81,37 @@ static bool EquivalentResult(const SelectionResult& a, const SelectionResult& b)
    81     return ret.first == a_amts.end() && ret.second == b_amts.end();
    82 }
    83 
    84+static void TestBnBSuccess(std::string test_title, std::vector<OutputGroup>& utxo_pool, const CAmount& selection_target, const std::vector<CAmount>& expected_input_amounts, const CoinSelectionParams cs_params = default_cs_params)
    


    murchandamus commented at 4:07 pm on June 27, 2024:

    Should be a reference.

    0static void TestBnBSuccess(std::string test_title, std::vector<OutputGroup>& utxo_pool, const CAmount& selection_target, const std::vector<CAmount>& expected_input_amounts, const CoinSelectionParams& cs_params = default_cs_params)
    
  25. in src/wallet/test/coinselection_tests.cpp:94 in e62bf2d346 outdated
    89+        OutputGroup group = MakeCoin(input_amount, true, cs_params);
    90+        expected_amount += group.m_value;
    91+        expected_result.AddInput(group);
    92+    }
    93+
    94+    const auto result = SelectCoinsBnB(utxo_pool, selection_target, /*cost_of_change=*/ default_cs_params.m_cost_of_change, /*max_weight=*/MAX_STANDARD_TX_WEIGHT);
    


    murchandamus commented at 4:08 pm on June 27, 2024:
    0    const auto result = SelectCoinsBnB(utxo_pool, selection_target, /*cost_of_change=*/default_cs_params.m_cost_of_change, /*max_weight=*/MAX_STANDARD_TX_WEIGHT);
    
  26. in src/wallet/test/coinselection_tests.cpp:106 in e62bf2d346 outdated
    101+{
    102+    std::vector<OutputGroup> utxo_pool;
    103+    AddCoins(utxo_pool, {1 * CENT, 3 * CENT, 5 * CENT});
    104+
    105+    // Simple success cases
    106+    TestBnBSuccess("Select smallest UTXO", utxo_pool, /*selection_target=*/ 1 * CENT, /*expected_input_amounts=*/ {1 * CENT});
    


    murchandamus commented at 4:08 pm on June 27, 2024:
    Remove spaces after param comments
  27. in src/wallet/test/coinselection_tests.cpp:113 in e62bf2d346 outdated
    108+    TestBnBSuccess("Select biggest UTXO", utxo_pool, /*selection_target=*/ 5 * CENT, /*expected_input_amounts=*/ {5 * CENT});
    109+    TestBnBSuccess("Select two UTXOs", utxo_pool, /*selection_target=*/ 4 * CENT, /*expected_input_amounts=*/ {1 * CENT, 3 * CENT});
    110+    TestBnBSuccess("Select all UTXOs", utxo_pool, /*selection_target=*/ 9 * CENT, /*expected_input_amounts=*/ {1 * CENT, 3 * CENT, 5 * CENT});
    111+
    112+    // BnB finds changeless solution while overshooting by up to cost_of_change
    113+    TestBnBSuccess("Select upper bound", utxo_pool, /*selection_target=*/ 9 * CENT - default_cs_params.m_cost_of_change, /*expected_input_amounts=*/ {1 * CENT, 3 * CENT, 5 * CENT});
    


    murchandamus commented at 4:10 pm on June 27, 2024:

    Should not overselect, so we could do this with 4Γ—CENT

    0    TestBnBSuccess("Select upper bound", utxo_pool, /*selection_target=*/ 4 * CENT - default_cs_params.m_cost_of_change, /*expected_input_amounts=*/ {1 * CENT, 3 * CENT});
    
  28. in src/wallet/test/coinselection_tests.cpp:188 in dc14005ef1 outdated
    120+    AddCoins(low_feerate_pool, {2 * CENT, 3 * CENT, 5 * CENT, 10 * CENT});
    121+    TestBnBSuccess("Select many inputs at low feerates", low_feerate_pool, /*selection_target=*/ 10 * CENT, /*expected_input_amounts=*/ {2 * CENT, 3 * CENT, 5 * CENT});
    122+
    123+    std::vector<OutputGroup> high_feerate_pool; // 25β€―sat/vB (greater than long_term_feerate of 10β€―sat/vB)
    124+    CoinSelectionParams high_feerate_params = init_default_params();
    125+    high_feerate_params.m_effective_feerate = CFeeRate{25'000};
    


    murchandamus commented at 4:11 pm on June 27, 2024:
    0    CoinSelectionParams high_feerate_params = init_default_params();
    1    high_feerate_params.m_effective_feerate = CFeeRate{25'000};
    2    std::vector<OutputGroup> high_feerate_pool; // 25β€―sat/vB (greater than long_term_feerate of 10β€―sat/vB)
    
  29. in src/wallet/test/coinselection_tests.cpp:109 in 77e445976a outdated
    103@@ -104,9 +104,18 @@ static void TestBnBSuccess(std::string test_title, std::vector<OutputGroup>& utx
    104     BOOST_CHECK_MESSAGE(result->GetSelectedValue() == expected_amount, "Selected amount mismatch in BnB-Success: " + test_title);
    105 }
    106 
    107+static void TestBnBFail(std::string test_title, std::vector<OutputGroup>& utxo_pool, const CAmount& selection_target)
    108+{
    109+    BOOST_CHECK_MESSAGE(!SelectCoinsBnB(utxo_pool, selection_target, /*cost_of_change=*/ default_cs_params.m_cost_of_change, /*max_weight=*/MAX_STANDARD_TX_WEIGHT), "BnB-Fail: " + test_title);
    


    murchandamus commented at 4:12 pm on June 27, 2024:
    remove spaces
  30. in src/wallet/test/coinselection_tests.cpp:132 in 77e445976a outdated
    127@@ -119,6 +128,14 @@ BOOST_AUTO_TEST_CASE(bnb_test)
    128     // BnB finds changeless solution while overshooting by up to cost_of_change
    129     TestBnBSuccess("Select upper bound", utxo_pool, /*selection_target=*/ 9 * CENT - default_cs_params.m_cost_of_change, /*expected_input_amounts=*/ {1 * CENT, 3 * CENT, 5 * CENT});
    130 
    131+    // BnB fails to find changeless solution when overshooting by cost_of_change + 1 sat
    132+    TestBnBFail("Overshoot upper bound", utxo_pool, /*selection_target=*/ 9 * CENT - default_cs_params.m_cost_of_change - 1);
    


    murchandamus commented at 4:14 pm on June 27, 2024:
    0    TestBnBFail("Overshoot upper bound", utxo_pool, /*selection_target=*/4 * CENT - default_cs_params.m_cost_of_change - 1);
    
  31. in src/wallet/test/coinselection_tests.cpp:117 in 77e445976a outdated
    112 BOOST_AUTO_TEST_CASE(bnb_test)
    113 {
    114     std::vector<OutputGroup> utxo_pool;
    115+
    116+    // Fail for empty UTXO pool
    117+    TestBnBFail("Empty UTXO pool", utxo_pool, /*selection_target=*/ 1 * CENT);
    


    murchandamus commented at 5:04 pm on June 27, 2024:
    Space
  32. murchandamus force-pushed on Jun 27, 2024
  33. murchandamus force-pushed on Jun 27, 2024
  34. murchandamus commented at 5:36 pm on June 27, 2024: contributor
    Alright, should hopefully be ready to review.
  35. murchandamus commented at 5:44 pm on June 27, 2024: contributor

    I’m not completely sure about 584e524. The commit description says

    Originally these tests verified that at a SelectCoins level that a solution with fewer inputs gets preferred at high feerates, and a solution with more inputs gets preferred at low feerates. This outcome relies on the behavior of BnB, so we move these tests under the umbrella of BnB tests.

    It is true that the outcome relies only on the BnB behavior currently but that might not be true in the future. There could be other algorithm clashing with it.

    Yeah, so the old tests assumed that because BnB behaved a certain way, we would get a specific overall outcome. The new tests just check that BnB behaves a certain way. We might still want tests that test the overall outcome as a result from the interaction of multiple coin selection tests, but this one seemed clearly to be testing BnB behavior, and it seemed strange to me to be testing that at the level where the results are combined rather than checking that BnB assumptions are fulfilled by BnB.

  36. hebasto added the label Tests on Aug 16, 2024
  37. hebasto added the label Needs CMake port on Aug 16, 2024
  38. DrahtBot added the label Needs rebase on Sep 2, 2024
  39. maflcko removed the label Needs CMake port on Sep 3, 2024
  40. murchandamus force-pushed on Sep 10, 2024
  41. DrahtBot removed the label Needs rebase on Sep 10, 2024
  42. DrahtBot commented at 11:47 am on September 11, 2024: contributor

    🚧 At least one of the CI tasks failed. Debug: https://github.com/bitcoin/bitcoin/runs/29957535397

    Make sure to run all tests locally, according to the documentation.

    The failure may happen due to a number of reasons, for example:

    • Possibly 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.

    • A sanitizer issue, which can only be found by compiling with the sanitizer and running the affected test.

    • An intermittent issue.

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

  43. DrahtBot added the label CI failed on Sep 11, 2024
  44. murchandamus force-pushed on Sep 11, 2024
  45. DrahtBot removed the label CI failed on Sep 15, 2024
  46. in src/wallet/test/coinselection_tests.cpp:12 in 3522569d61 outdated
     7+#include <wallet/test/wallet_test_fixture.h>
     8+
     9+#include <boost/test/unit_test.hpp>
    10+
    11+namespace wallet {
    12+BOOST_FIXTURE_TEST_SUITE(coinselection_tests, WalletTestingSetup)
    


    furszy commented at 3:11 pm on September 22, 2024:
    nit: As far as I can see, no test case in this file uses the WalletTestingSetup wallet. So we could save some initialization time on each test case by changing it to TestingSetup. And if any future test requires a wallet, it could be specified in-place.

    murchandamus commented at 6:26 pm on October 31, 2024:
    Thanks, great, taken!
  47. nymius commented at 6:51 pm on October 24, 2024: none
    nit: I have read the test and I don’t have any code related comment yet, but I have noticed that some of the commits are structured with some not written conventions and beside the actual comments about commit message quality in the CONTRIBUTIONS.md file, there are some other conventions used by other contributors too, in a similar fashion to conventional commits with (wallet:, ci:, rpc:). In that spirit, wouldn’t be a good idea to, in case there needs to be a change in some of the commits, to change from [test] to test: or coinselection: to scope the commits a little bit more and move towards a common format? Also, do you think is worth to add these conventions to the CONTRIBUTIONS.md file?
  48. in src/wallet/test/coinselection_tests.cpp:178 in 639a0dd520 outdated
    160+    TestBnBSuccess("Combine smallest 8 of 17 unique UTXOs", doppelganger_pool, /*selection_target=*/8 * CENT, /*expected_input_amounts=*/expected_inputs);
    161+
    162+    // Starting with 18 unique UTXOs of similar effective value we will not find the solution
    163+    AddCoins(doppelganger_pool, {1 * CENT + default_cs_params.m_cost_of_change + 17});
    164+    TestBnBFail("Exhaust looking for smallest 8 of 18 unique UTXOs", doppelganger_pool, /*selection_target=*/8 * CENT);
    165+}
    


    jasonribble commented at 10:28 pm on October 24, 2024:
    nit: perhaps the comment on line 144 can be more descriptive for this part. (or the comments in general can be improved). It’s the strangest test in the group.

    jasonribble commented at 6:19 pm on October 26, 2024:
    maybe you can rename the “18” value as a BnB threshold

    murchandamus commented at 7:07 pm on October 31, 2024:
    I added a longer explanation to this test. Luckily 18 is not generally the limit, it’s just the limit in this artificially crafted case where all of our UTXOs have very similar amounts and we have to pick eight of them to meet the target. Please let me know if the test makes sense now! :)
  49. in src/wallet/test/coinselection_tests.cpp:56 in 639a0dd520 outdated
    51+    OutputGroup group(cs_params);
    52+    group.Insert(std::make_shared<COutput>(COutPoint(tx.GetHash(), 0), tx.vout.at(0), /*depth=*/1, /*input_bytes=*/custom_spending_vsize, /*spendable=*/true, /*solvable=*/true, /*safe=*/true, /*time=*/0, /*from_me=*/false, /*fees=*/fees), /*ancestors=*/0, /*descendants=*/0);
    53+    return group;
    54+}
    55+
    56+/** Make multiple OutpuGroups with the given values as their effective value */
    


    vicariousdrama commented at 5:31 pm on October 28, 2024:
    minor spelling: OutputGroups
  50. in src/wallet/test/coinselection_tests.cpp:34 in 639a0dd520 outdated
    30+        /*tx_noinputs_size=*/11 + 31, //static header size + output size
    31+        /*avoid_partial=*/false,
    32+    };
    33+    dcsp.m_change_fee = /*155 sats=*/dcsp.m_effective_feerate.GetFee(dcsp.change_output_size);
    34+    dcsp.m_cost_of_change = /*204 + 155 sats=*/dcsp.m_discard_feerate.GetFee(dcsp.change_spend_size) + dcsp.m_change_fee;
    35+    dcsp.min_viable_change = /*204 sats=*/dcsp.m_discard_feerate.GetFee(dcsp.change_spend_size);
    


    vicariousdrama commented at 5:33 pm on October 28, 2024:

    Can we eliminate a call to GetFee via reordering assignment?

    0    dcsp.m_change_fee = /*155 sats=*/dcsp.m_effective_feerate.GetFee(dcsp.change_output_size);
    1    dcsp.min_viable_change = /*204 sats=*/dcsp.m_discard_feerate.GetFee(dcsp.change_spend_size);
    2    dcsp.m_cost_of_change = /*204 + 155 sats=*/dcsp.min_viable_change + dcsp.m_change_fee;
    

    murchandamus commented at 6:26 pm on October 31, 2024:
    Thanks, great suggestion. I’ve adopted that change.
  51. in src/wallet/test/CMakeLists.txt:12 in 639a0dd520 outdated
     8@@ -9,6 +9,7 @@ target_sources(test_bitcoin
     9     init_test_fixture.cpp
    10     wallet_test_fixture.cpp
    11     coinselector_tests.cpp
    12+    coinselection_tests.cpp
    


    vicariousdrama commented at 5:56 pm on October 28, 2024:
    Is this name going to set this portion of tests apart enough to be meaningful? It seems only some of the BnB tests that were present in coinselector_tests have been refactored

    vicariousdrama commented at 7:02 pm on October 28, 2024:
    disregard this comment. I skipped your intent in the description

    murchandamus commented at 6:29 pm on October 31, 2024:
    Yeah, as explained, the intention is to rewrite all coinselection tests with transactions that could be encountered in the wild rather than the artificial zero-fee transactions the old tests are using.
  52. in src/wallet/test/coinselection_tests.cpp:177 in 639a0dd520 outdated
    159+    // Among up to 17 unique UTXOs of similar effective value we will find a solution composed of the eight smallest
    160+    TestBnBSuccess("Combine smallest 8 of 17 unique UTXOs", doppelganger_pool, /*selection_target=*/8 * CENT, /*expected_input_amounts=*/expected_inputs);
    161+
    162+    // Starting with 18 unique UTXOs of similar effective value we will not find the solution
    163+    AddCoins(doppelganger_pool, {1 * CENT + default_cs_params.m_cost_of_change + 17});
    164+    TestBnBFail("Exhaust looking for smallest 8 of 18 unique UTXOs", doppelganger_pool, /*selection_target=*/8 * CENT);
    


    vicariousdrama commented at 6:22 pm on October 28, 2024:
    This segment of code appears to me to be intended to refactor the make_hard_case exhaustion tests, but with differing amounts (17,18 vs. 14,17) and construction (amount = iterative CENT + i vs. bitshifting?). Isn’t there double the coins being created in the make_hard_case? I appreciate that this format is more readable, though it’s unclear to me why one should exhaust based on the given coin sizes and quantity. Is this related to src/wallet/coinselection.cpp setting TOTAL_TRIES to 100,000 constraining the useful effective traversal depth?

    murchandamus commented at 6:40 pm on October 31, 2024:
    Yes, this tests that BnB will fail to find the best solution when the attempts exceed the TOTAL_TRIES limit of 100,000. This test follows the same approach but is slightly different than the one in the original tests. I wanted to replace the old test because I found it too difficult to parse, but I came up with my own way of testing it. I have expanded the explanation of what is going on here, and how I created this artificial example that manages to exhaust the limit with only 18 UTXOs.β€”To be clear, usually we would be able to handle much bigger UTXO pools and still find solutions, this UTXO pool is specifically crafted to cause all possible combinations to be explored.
  53. in src/wallet/test/coinselector_tests.cpp:147 in 639a0dd520 outdated
    133-        add_coin(CAmount{1} << (utxos+i), 2*i, utxo_pool);
    134-        add_coin((CAmount{1} << (utxos+i)) + (CAmount{1} << (utxos-1-i)), 2*i + 1, utxo_pool);
    135-    }
    136-    return target;
    137-}
    138-
    


    vicariousdrama commented at 6:24 pm on October 28, 2024:
    Worth noting that src/bench/coin_selection.cpp makes a copy of the make_hard_case function (commenting as such), so removal here may lead to confusion unless that benchmark is also updated.

    murchandamus commented at 7:10 pm on October 31, 2024:
    Thanks, I’ll make a note to update the benchmark as well.
  54. test: Create coinselection_tests
    Adds a Test Suite, default coin selection parameters as well as helper
    functions for creating available coins and to check results.
    a8bf940ae2
  55. test: Recreate simple BnB success tests f3b0593b8b
  56. test: Move BnB feerate sensitivity tests
    Originally these tests verified that at a SelectCoins level that a
    solution with fewer inputs gets preferred at high feerates, and a
    solution with more inputs gets preferred at low feerates. This outcome
    relies on the behavior of BnB, so we move these tests under the umbrella
    of BnB tests.
    
    Originally these tests relied on SFFO to work.
    bfd2fe70ae
  57. test: Recreate BnB clone skipping test c9417ee593
  58. test: Recreate simple BnB failure tests 9686a9a20e
  59. test: Remove redundant repeated test
    We do not need to repeat the same test multiple times because BnB is
    deterministic and will therefore always have the same outcome.
    Additionally, this test was redundant because it repeats the "Smallest
    combination too big" test.
    8bf2b22fcc
  60. test: Recreate BnB iteration exhaustion test 280e65d3a7
  61. murchandamus force-pushed on Oct 31, 2024
  62. murchandamus commented at 7:11 pm on October 31, 2024: contributor
    Thanks @furszy, @nymius, @jasonribble, and @vicariousdrama for the review! I updated the commit message names as suggested, adopted all of your proposed changes as indicated, and added more explanation to the test covering the attempt limit.
  63. murchandamus commented at 7:16 pm on October 31, 2024: contributor

    Also, do you think is worth to add these conventions to the CONTRIBUTIONS.md file?

    I don’t know! It would be a good thing to ask in the IRC channel or at the weekly meeting. :)

  64. jasonribble commented at 8:03 pm on November 1, 2024: none

    So C++ noob question. Not sure if this is an appropriate place to ask.

    I’m noticing that you’re calling CFeeRate with an int: https://github.com/bitcoin/bitcoin/blob/280e65d3a7993ea38ba49ae00133a7ba709c193e/src/wallet/test/coinselection_tests.cpp#L20-L38

    Okay that’s cool, clearly it compiles and runs…but how?

    src/policy/feerate.h has the class definition. I don’t see how it’s able to only take an int

    This is my best bet of how it works

    https://github.com/bitcoin/bitcoin/blob/746f88000e8b219b1318839ca1cd7fd9f2057793/src/policy/feerate.h#L38-L43

    it’s using std::integral, maybe doing some weird magic I didn’t know we had. But, I don’t see #include <concepts>.

    Btw, this curiosity all started because my editor highlighted this error

    0no instance of constructor "CFeeRate::CFeeRate" matches the argument listC/C++(289)
    

    Then in src/policy/feerate.h it the IDE says

    namespace "std" has no member "integral"C/C++(135)

    https://github.com/bitcoin/bitcoin/blob/746f88000e8b219b1318839ca1cd7fd9f2057793/src/policy/feerate.h#L38-L76

    I see that it does compile but where does CFeeRate get constructed with only an int?

  65. sipa commented at 8:06 pm on November 1, 2024: member
    @jasonribble template<std::integral I> is the same as template<typename I>, with the added restriction that I must be a type that satisfies the std::integral concept. Since int does satisfy that concept, I can be instantiated as int.
  66. jasonribble commented at 8:21 pm on November 1, 2024: none

    @jasonribble template<std::integral I> is the same as template<typename I>, with the added restriction that I must be a type that satisfies the std::integral concept. Since int does satisfy that concept, I can be instantiated as int.

    Magical. Thank you πŸ™

  67. TheCharlatan commented at 8:23 pm on November 1, 2024: contributor

    But, I don’t see #include <concepts>.

    If you check the IWYU logs in the “tidy” CI job you will see that it is indeed missing:

    0[19:28:01.280] /ci_container_base/src/policy/feerate.h should add these lines:
    1[19:28:01.280] #include <concepts>            // for integral
    

    This report is generated on each CI run, but the missing includes are not enforced. This can be confusing, so best to always check the CI output.

  68. jasonribble commented at 0:43 am on November 2, 2024: none

    it is indeed missing

    Oh interesting! Well, I guess not in this important for this PR, but perhaps it should be explicit. Especially so people don’t get those evil red lines

  69. jasonribble approved
  70. jasonribble commented at 11:26 pm on November 2, 2024: none

    ACK

    I went through each of the commits. It is an improvement of the branch and bound selection tests.

  71. ismaelsadeeq commented at 6:53 pm on November 6, 2024: member
    Concept ACK
  72. in src/wallet/test/coinselection_tests.cpp:102 in f3b0593b8b outdated
     97+}
     98+
     99+BOOST_AUTO_TEST_CASE(bnb_test)
    100+{
    101+    std::vector<OutputGroup> utxo_pool;
    102+    AddCoins(utxo_pool, {1 * CENT, 3 * CENT, 5 * CENT});
    


    furszy commented at 10:18 pm on November 8, 2024:
    In f3b0593b8: Can you explain why did you remove the other coins (2 and 4) that were here before? Guess they will be selected and break something if we re-add them because of the algo predilection for unifying utxos?
  73. in src/wallet/test/coinselection_tests.cpp:96 in f3b0593b8b outdated
    91+    }
    92+
    93+    const auto result = SelectCoinsBnB(utxo_pool, selection_target, /*cost_of_change=*/default_cs_params.m_cost_of_change, /*max_selection_weight=*/MAX_STANDARD_TX_WEIGHT);
    94+    BOOST_CHECK_MESSAGE(result, "Falsy result in BnB-Success: " + test_title);
    95+    BOOST_CHECK_MESSAGE(EquivalentResult(expected_result, *result), "Result mismatch in BnB-Success: " + test_title);
    96+    BOOST_CHECK_MESSAGE(result->GetSelectedValue() == expected_amount, "Selected amount mismatch in BnB-Success: " + test_title);
    


    furszy commented at 10:21 pm on November 8, 2024:

    In https://github.com/bitcoin/bitcoin/commit/f3b0593b8bd67bc919b21cd636a01813fc5538c8:

    This could output a bit more detailed error message:

    0BOOST_CHECK_MESSAGE(EquivalentResult(expected_result, *result), strprintf("Result mismatch in BnB-Success: %s. Expected %s vs %s", test_title, InputsToString(expected_result), InputsToString(*result)));
    1BOOST_CHECK_MESSAGE(result->GetSelectedValue() == expected_amount, strprintf("Selected amount mismatch in BnB-Success: %s. Expected %d vs %d", test_title, expected_amount, result->GetSelectedValue()));
    

    But you would need something like this too:

    0static std::string InputsToString(const SelectionResult& selection)
    1{
    2    std::string res = "[";
    3    for (const auto& input : selection.GetInputSet()) {
    4        res += util::ToString(input->txout.nValue);
    5    }
    6    return res + "]";
    7}
    
  74. in src/wallet/test/coinselection_tests.cpp:23 in a8bf940ae2 outdated
    18+ * parameters when a diverging value is relevant in the context of a test. */
    19+static CoinSelectionParams init_default_params()
    20+{
    21+    CoinSelectionParams dcsp{
    22+        /*rng_fast*/default_rand,
    23+        /*change_output_size=*/31,
    


    ismaelsadeeq commented at 8:26 pm on November 12, 2024:

    In a8bf940ae20ed5a03a1cc328212fcac3c3462b43 A comment explaining why you selected these magic numbers would be helpful. similar to tx_noinputs_size or an indication if they are just random.

    i.e values when initializing default CSP, and some defaults in MakeCoin

  75. in src/wallet/test/coinselection_tests.cpp:24 in a8bf940ae2 outdated
    19+static CoinSelectionParams init_default_params()
    20+{
    21+    CoinSelectionParams dcsp{
    22+        /*rng_fast*/default_rand,
    23+        /*change_output_size=*/31,
    24+        /*change_spend_size=*/68,
    


    ismaelsadeeq commented at 8:32 pm on November 12, 2024:
    After this PR, we will have two coin selection test files. Is there a reason why? Wouldn’t it be better to add a commit that renames coinselector_tests to coinselection_tests and modify the test name to coinselection_tests?
  76. in src/wallet/test/coinselection_tests.cpp:65 in a8bf940ae2 outdated
    60+    }
    61+}
    62+
    63+/** Check if SelectionResult a is equivalent to SelectionResult b.
    64+ * Equivalent means same input values, but maybe different inputs (i.e. same value, different prevout) */
    65+static bool EquivalentResult(const SelectionResult& a, const SelectionResult& b)
    


    ismaelsadeeq commented at 9:06 pm on November 12, 2024:
    Since we are checking for Value equivalence, this can just be CheckValueEquivalence for clarity
  77. in src/wallet/test/coinselection_tests.cpp:78 in a8bf940ae2 outdated
    73+        b_amts.push_back(coin->txout.nValue);
    74+    }
    75+    std::sort(a_amts.begin(), a_amts.end());
    76+    std::sort(b_amts.begin(), b_amts.end());
    77+
    78+    std::pair<std::vector<CAmount>::iterator, std::vector<CAmount>::iterator> ret = std::mismatch(a_amts.begin(), a_amts.end(), b_amts.begin());
    


    ismaelsadeeq commented at 9:07 pm on November 12, 2024:
    0    auto ret = std::mismatch(a_amts.begin(), a_amts.end(), b_amts.begin());
    

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 03:12 UTC

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