test: Rework Single Random Draw coin selection tests #34886

pull murchandamus wants to merge 5 commits into bitcoin:master from murchandamus:2026-03-18-refactor-SRD-tests changing 2 files +95 −124
  1. murchandamus commented at 11:14 PM on March 20, 2026: member

    This transfers the tests from the old coin selection test framework that was based on ignoring fees to the new coin selection framework that tests the coin selection algorithms with realistic feerates and fees.

    The PR also includes a minor improvement of the test framework to allow the CoinSelectionParams used by the tests to be initialized with other feerates, and adds some feerates around the new minimum feerate to the tested feerates.

  2. DrahtBot added the label Tests on Mar 20, 2026
  3. DrahtBot commented at 11:15 PM on March 20, 2026: contributor

    <!--e57a25ab6845829454e8d69fc972939a-->

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

    <!--021abf342d371248e50ceaed478a90ca-->

    Reviews

    See the guideline for information on the review process.

    Type Reviewers
    ACK w0xlt, brunoerg, achow101
    Concept ACK yancyribbens

    If your review is incorrectly listed, please copy-paste <code>&lt;!--meta-tag:bot-skip--&gt;</code> into the comment that the bot should ignore.

    <!--174a7506f384e20aa4161008e828411d-->

    Conflicts

    Reviewers, this pull request conflicts with the following ones:

    • #32150 (coinselection: Optimize BnB exploration by murchandamus)

    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.

    <!--5faf32d7da4f0f540f40219e4f7537a3-->

  4. murchandamus force-pushed on Mar 20, 2026
  5. DrahtBot added the label CI failed on Mar 20, 2026
  6. DrahtBot commented at 11:32 PM on March 20, 2026: contributor

    <!--85328a0da195eb286784d51f73fa0af9-->

    🚧 At least one of the CI tasks failed. <sub>Task test ancestor commits: https://github.com/bitcoin/bitcoin/actions/runs/23366323668/job/67980934546</sub> <sub>LLM reason (✨ experimental): CMake build failed, with gmake exiting Error 2 (non-zero exit status from cmake --build).</sub>

    <details><summary>Hints</summary>

    Try to run the tests locally, according to the documentation. However, a CI failure may still 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.

    </details>

  7. murchandamus force-pushed on Mar 20, 2026
  8. murchandamus force-pushed on Mar 20, 2026
  9. in src/wallet/test/coinselection_tests.cpp:272 in d37131f7a9
     264 | @@ -265,6 +265,11 @@ BOOST_AUTO_TEST_CASE(srd_test)
     265 |          TestSRDFail("Undershoot minimum change by one sat", utxo_pool, /*selection_target=*/9 * CENT - cs_params.m_change_fee - CHANGE_LOWER + 1, cs_params);
     266 |          TestSRDFail("Spend more than available", utxo_pool, /*selection_target=*/9 * CENT + 1, cs_params);
     267 |          TestSRDFail("Spend everything", utxo_pool, /*selection_target=*/9 * CENT, cs_params);
     268 | +
     269 | +        AddDuplicateCoins(utxo_pool, /*count=*/100, /*amount=*/5 * CENT, cs_params);
     270 | +        AddDuplicateCoins(utxo_pool, /*count=*/3, /*amount=*/7 * CENT, cs_params);
     271 | +        TestSRDSuccess("Select most valuable UTXOs for acceptable weight", utxo_pool, /*selection_target=*/20 * CENT, cs_params, /*max_selection_weight=*/4 * 4 * (P2WPKH_INPUT_VSIZE - 1 ));
     272 | +        TestSRDFail("No acceptable weight possible", utxo_pool, /*selection_target=*/25 * CENT, cs_params, /*max_selection_weight=*/4 * 3 * (P2WPKH_INPUT_VSIZE), /*expect_max_weight_exceeded=*/true);
    


    murchandamus commented at 12:09 AM on March 21, 2026:
            TestSRDFail("No acceptable weight possible", utxo_pool, /*selection_target=*/25 * CENT, cs_params, /*max_selection_weight=*/4 * 3 * P2WPKH_INPUT_VSIZE, /*expect_max_weight_exceeded=*/true);
    
  10. murchandamus force-pushed on Mar 21, 2026
  11. DrahtBot removed the label CI failed on Mar 21, 2026
  12. w0xlt commented at 8:01 AM on March 22, 2026: contributor

    Approach ACK

  13. in src/wallet/test/coinselection_tests.cpp:126 in 81827b160f outdated
     122 | @@ -123,7 +123,7 @@ static void TestBnBFail(std::string test_title, std::vector<OutputGroup>& utxo_p
     123 |  
     124 |  BOOST_AUTO_TEST_CASE(bnb_test)
     125 |  {
     126 | -    std::vector<int> feerates = {0, 1, 5'000, 10'000, 25'000, 59'764, 500'000, 999'000, 1'500'000};
     127 | +    std::vector<int> feerates = {0, 1, 99, 100, 315, 1'000, 2'345, 10'292, 59'764, 1'500'000};
    


    yancyribbens commented at 8:40 PM on March 22, 2026:

    in 81827b160f28b11e181856038badf4134923bdd0 it would be nice to have a little more detail in the commit message about why the new feerates are added. Is it because these are more realistic values?


    murchandamus commented at 8:34 PM on March 24, 2026:

    Thanks, I added some commentary to the commit message to motivate the feerates I picked.

  14. yancyribbens commented at 8:49 PM on March 22, 2026: contributor

    Concept ACK on adding representative feerates to SRD tests. Also Concept ACK on continuing to get all these tests in the same file. It's confusing to contributes (like myself) when the tests are in a few different places since I forget which is which.

    Also, it would make things easier to review I think if the commit messages provide a little more detail about why a change is being done. For example https://cbea.ms/git-commit/. I don't think it's a rule to be followed to always say why, however it can lead to less head scratching about what the diff shows if practiced judiciously.

  15. brunoerg commented at 11:49 PM on March 23, 2026: contributor

    Concept ACK

  16. murchandamus force-pushed on Mar 24, 2026
  17. murchandamus commented at 8:29 PM on March 24, 2026: member

    Thanks for the review and comments. @yancyribbens: I reworked the commit messages to add more explanation to each commit. There were no code changes in this update, only the commit messages were changed.

  18. murchandamus force-pushed on Mar 24, 2026
  19. DrahtBot added the label CI failed on Mar 24, 2026
  20. murchandamus commented at 1:18 AM on March 25, 2026: member

    Failure seems to be unrelated: #34782

  21. DrahtBot removed the label CI failed on Mar 25, 2026
  22. in src/wallet/test/coinselection_tests.cpp:236 in 6b5abcfa8c
     231 | +    BOOST_CHECK(expect_max_weight_exceeded == max_weight_exceeded);
     232 | +}
     233 | +
     234 | +BOOST_AUTO_TEST_CASE(srd_test)
     235 | +{
     236 | +    std::vector<int> feerates = {0, 1, 99, 100, 315, 1'000, 2'345, 10'292, 59'764, 1'500'000};
    


    brunoerg commented at 1:36 PM on March 25, 2026:

    6b5abcfa8cd2fa378b3aa6225f1dcde75524c8e0: nit: The feerates could be a static const to avoid duplication since we're using the same for both srd_test and bnb_test?


    murchandamus commented at 2:32 PM on March 25, 2026:

    Sure, will do

  23. in src/wallet/test/coinselection_tests.cpp:228 in fe38494e04
     222 | @@ -225,5 +223,55 @@ BOOST_AUTO_TEST_CASE(bnb_feerate_sensitivity_test)
     223 |      TestBnBSuccess("Prefer two light inputs over two heavy inputs at high feerates", high_feerate_pool, /*selection_target=*/13 * CENT, /*expected_input_amounts=*/{3 * CENT, 10 * CENT}, high_feerate_params);
     224 |  }
     225 |  
     226 | +static void TestSRDSuccess(std::string test_title, std::vector<OutputGroup>& utxo_pool, const CAmount& selection_target, const CoinSelectionParams& cs_params = default_cs_params, const int max_selection_weight = MAX_STANDARD_TX_WEIGHT)
     227 | +{
     228 | +    SelectionResult expected_result(CAmount(0), SelectionAlgorithm::SRD);
    


    brunoerg commented at 1:49 PM on March 25, 2026:

    expected_result is never used.


    murchandamus commented at 3:24 PM on March 25, 2026:

    Thanks, removed.

  24. brunoerg approved
  25. brunoerg commented at 1:55 PM on March 25, 2026: contributor

    code review ACK fe38494e04189583b96980c634176ad7506a002d

  26. DrahtBot requested review from yancyribbens on Mar 25, 2026
  27. murchandamus force-pushed on Mar 25, 2026
  28. murchandamus commented at 3:25 PM on March 25, 2026: member

    Thanks for the review @brunoerg. I removed the obsolete line, made the feerates a static const, and introduced a weight check into the success cases.

  29. brunoerg commented at 12:00 AM on March 26, 2026: contributor

    reACK 31d21f105d74953d03b7e6ebae6d1529c162aa87

  30. in src/wallet/test/coinselection_tests.cpp:246 in 31d21f105d
     241 | +{
     242 | +    CAmount expected_min_amount = selection_target + cs_params.m_change_fee + CHANGE_LOWER;
     243 | +
     244 | +    const auto result = SelectCoinsSRD(utxo_pool, selection_target, cs_params.m_change_fee, cs_params.rng_fast, max_selection_weight);
     245 | +    BOOST_CHECK_MESSAGE(result, "Falsy result in SRD-Success: " + test_title);
     246 | +    BOOST_CHECK_MESSAGE(result->GetSelectedValue() >= expected_min_amount, strprintf("Selected amount is lower than expected in SRD-Success: %s. Expected %d, but got %d", test_title, expected_min_amount, result->GetSelectedValue()));
    


    w0xlt commented at 9:24 PM on March 26, 2026:

    Shouldn't effective value be used here? GetSelectedValue() returns the sum of absolute txout.nValue amounts (before fee deduction).

        const CAmount selected_effective_value = result->GetSelectedEffectiveValue();
        BOOST_CHECK_MESSAGE(selected_effective_value >= expected_min_amount, strprintf("Selected effective value is lower than expected in SRD-Success: %s. Expected %d, but got %d", test_title, expected_min_amount, selected_effective_value));
    
    

    murchandamus commented at 10:38 PM on April 7, 2026:

    Yes, thanks for catching that!

  31. DrahtBot requested review from w0xlt on Mar 26, 2026
  32. in src/wallet/test/coinselection_tests.cpp:263 in 31d21f105d
     258 | +BOOST_AUTO_TEST_CASE(srd_test)
     259 | +{
     260 | +    for (int feerate : FEERATES) {
     261 | +        std::vector<OutputGroup> utxo_pool;
     262 | +
     263 | +        CoinSelectionParams cs_params = init_cs_params(feerate);
    


    w0xlt commented at 9:26 PM on March 26, 2026:

    nit:

            const CoinSelectionParams cs_params = init_cs_params(feerate);
    

    murchandamus commented at 10:48 PM on April 7, 2026:

    Taken and also applied to the BnB tests

  33. DrahtBot requested review from w0xlt on Mar 26, 2026
  34. in src/wallet/test/coinselection_tests.cpp:32 in 31d21f105d
      31 | + * - new default minimum feerate: 100 s/kvB
      32 | + * - old default minimum feerate: 1000 s/kvB
      33 | + * - a few non-round realistic feerates around default minimum feerate,
      34 | + * dust feerate, and default LTFRE: 315 s/kvB, 2345 s/kvB, and
      35 | + * 10'292 s/kvB
      36 | + * - a high feerate that has been exceeded occassionally: 59'764 s/kvB
    


    w0xlt commented at 9:29 PM on March 26, 2026:

    nit:

     * - a high feerate that has been exceeded occasionally.: 59'764 s/kvB
    
  35. DrahtBot requested review from w0xlt on Mar 26, 2026
  36. in src/wallet/test/coinselection_tests.cpp:55 in 31d21f105d
      61 | -    dcsp.m_cost_of_change = /*204 + 155 sats=*/dcsp.min_viable_change + dcsp.m_change_fee;
      62 | -    dcsp.m_subtract_fee_outputs = false;
      63 | -    return dcsp;
      64 | +    csp.m_change_fee = /*155 sats=*/csp.m_effective_feerate.GetFee(csp.change_output_size);
      65 | +    csp.min_viable_change = /*204 sats=*/csp.m_discard_feerate.GetFee(csp.change_spend_size);
      66 | +    csp.m_cost_of_change = /*204 + 155 sats=*/csp.min_viable_change + csp.m_change_fee;
    


    w0xlt commented at 9:42 PM on March 26, 2026:

    m_change_fee depends on m_effective_feerate (which is no longer hardcoded). m_cost_of_change sums both of the preceding values; therefore, it depends on m_change_fee.

        csp.m_change_fee = csp.m_effective_feerate.GetFee(csp.change_output_size);
        csp.min_viable_change = /*204 sats=*/csp.m_discard_feerate.GetFee(csp.change_spend_size);
        csp.m_cost_of_change = csp.min_viable_change + csp.m_change_fee;
    

    murchandamus commented at 10:53 PM on April 7, 2026:

    I turned this into comments at the end of the lines clarifying that it is for the default feerate.

  37. DrahtBot requested review from w0xlt on Mar 26, 2026
  38. test: Init coin selection params with feerate
    The most frequent change to CoinSelectionParams so far seems to be
    amendments to the feerate, and the feerate propagates to other members
    of the CoinSelectionParams, so we add it as an input parameter.
    
    This also adds the CoinSelectionParams to the BnBFail tests, which
    previously were always run with the default CoinSelectionParams.
    65900f8dc6
  39. Test: Add new minimum to tested feerates
    The default minimum feerate was lowered from 1000 s/kvB to 100 s/kvB.
    This adjusts the set of feerates used in the tests to accommodate that
    new feerate and to cover other potential special cases:
    - zero: 0 s/kvB
    - minimum non-zero s/kvB: 1 s/kvB
    - just below the new default minimum feerate: 99 s/kvB
    - new default minimum feerate: 100 s/kvB
    - old default minimum feerate: 1000 s/kvB
    - a few non-round realistic feerates around default minimum feerate,
    dust feerate, and default LTFRE: 315 s/kvB, 2345 s/kvB, and
    10'292 s/kvB
    - a high feerate that has been exceeded occassionally: 59'764 s/kvB
    - a huge feerate that is extremely uncommon: 1'500'000 s/kvB
    64ab97466f
  40. test: Rework SRD insufficient balance test
    This refactor is part of the effort to move the coin selection tests to
    a framework that can use non-zero, realistic feerates. The insufficient
    funds failure case is extended with a few additional similar variants of
    the failure.
    2840f041c5
  41. test: Add SRD success tests
    Previously, SRD had only failure and edge-case coverage, so we add a few
    simple coin selection targets that should quickly succeed, including one
    that will create a minimal change output.
    fe9f53bf0b
  42. test: Add SRD maximum weight tests
    Also:
    - Add weight check to Success cases.
    
    Per the introduction of TRUC transactions, it is more likely that we
    will attempt to build transactions of limited weight (e.g., TRUC child
    transactions may not exceed 4000 WU). When SRD exceeds the input weight
    limit, it evicts the OutputGroup with the lowest effective value before
    selecting additional UTXOs: we test that SRD will find a solution that
    depends on the eviction working correctly, and that it fails as expected
    when no solution is possible.
    858a0a9c96
  43. murchandamus force-pushed on Apr 7, 2026
  44. murchandamus commented at 10:52 PM on April 7, 2026: member

    Thanks for the review, @w0xlt. I took all your suggestions.

  45. w0xlt commented at 6:23 PM on April 8, 2026: contributor

    ACK 858a0a9c96b622e7d8a6b2b700f26c898971c10e

  46. DrahtBot requested review from brunoerg on Apr 8, 2026
  47. brunoerg approved
  48. brunoerg commented at 6:26 PM on April 9, 2026: contributor

    reACK 858a0a9c96b622e7d8a6b2b700f26c898971c10e

    will update mutation testing for coin selection as soon as it gets merged.

  49. achow101 assigned achow101 on Apr 9, 2026
  50. achow101 commented at 8:51 PM on April 9, 2026: member

    ACK 858a0a9c96b622e7d8a6b2b700f26c898971c10e

  51. achow101 merged this on Apr 9, 2026
  52. achow101 closed this on Apr 9, 2026


github-metadata-mirror

This is a metadata mirror of the GitHub repository bitcoin/bitcoin. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2026-04-25 03:12 UTC

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