@@ -455,6 +455,44 @@ BOOST_AUTO_TEST_CASE(bnb_search_test)
455455 }
456456}
457457
458+ BOOST_AUTO_TEST_CASE (bnb_sffo_restriction)
459+ {
460+ // Verify the coin selection process does not produce a BnB solution when SFFO is enabled.
461+ // This is currently problematic because it could require a change output. And BnB is specialized on changeless solutions.
462+ std::unique_ptr<CWallet> wallet = NewWallet (m_node);
463+ WITH_LOCK (wallet->cs_wallet , wallet->SetLastBlockProcessed (300 , uint256{})); // set a high block so internal UTXOs are selectable
464+
465+ FastRandomContext rand{};
466+ CoinSelectionParams params{
467+ rand,
468+ /* change_output_size=*/ 31 , // unused value, p2wpkh output size (wallet default change type)
469+ /* change_spend_size=*/ 68 , // unused value, p2wpkh input size (high-r signature)
470+ /* min_change_target=*/ 0 , // dummy, set later
471+ /* effective_feerate=*/ CFeeRate (3000 ),
472+ /* long_term_feerate=*/ CFeeRate (1000 ),
473+ /* discard_feerate=*/ CFeeRate (1000 ),
474+ /* tx_noinputs_size=*/ 0 ,
475+ /* avoid_partial=*/ false ,
476+ };
477+ params.m_subtract_fee_outputs = true ;
478+ params.m_change_fee = params.m_effective_feerate .GetFee (params.change_output_size );
479+ params.m_cost_of_change = params.m_discard_feerate .GetFee (params.change_spend_size ) + params.m_change_fee ;
480+ params.m_min_change_target = params.m_cost_of_change + 1 ;
481+ // Add spendable coin at the BnB selection upper bound
482+ CoinsResult available_coins;
483+ add_coin (available_coins, *wallet, COIN + params.m_cost_of_change , /* feerate=*/ params.m_effective_feerate , /* nAge=*/ 6 , /* fIsFromMe=*/ true , /* nInput=*/ 0 , /* spendable=*/ true );
484+ add_coin (available_coins, *wallet, 0.5 * COIN + params.m_cost_of_change , /* feerate=*/ params.m_effective_feerate , /* nAge=*/ 6 , /* fIsFromMe=*/ true , /* nInput=*/ 0 , /* spendable=*/ true );
485+ add_coin (available_coins, *wallet, 0.5 * COIN, /* feerate=*/ params.m_effective_feerate , /* nAge=*/ 6 , /* fIsFromMe=*/ true , /* nInput=*/ 0 , /* spendable=*/ true );
486+ // Knapsack will only find a changeless solution on an exact match to the satoshi, SRD doesn’t look for changeless
487+ // If BnB were run, it would produce a single input solution with the best waste score
488+ auto result = WITH_LOCK (wallet->cs_wallet , return SelectCoins (*wallet, available_coins, /* pre_set_inputs=*/ {}, COIN, /* coin_control=*/ {}, params));
489+ BOOST_CHECK (result.has_value ());
490+ BOOST_CHECK_NE (result->GetAlgo (), SelectionAlgorithm::BNB);
491+ BOOST_CHECK (result->GetInputSet ().size () == 2 );
492+ // We have only considered BnB, SRD, and Knapsack. Test needs to be reevaluated if new algo is added
493+ BOOST_CHECK (result->GetAlgo () == SelectionAlgorithm::SRD || result->GetAlgo () == SelectionAlgorithm::KNAPSACK);
494+ }
495+
458496BOOST_AUTO_TEST_CASE (knapsack_solver_test)
459497{
460498 FastRandomContext rand{};
0 commit comments