@@ -929,6 +929,100 @@ BOOST_AUTO_TEST_CASE(effective_value_test)
929
929
BOOST_CHECK_EQUAL (output5.GetEffectiveValue (), nValue); // The effective value should be equal to the absolute value if input_bytes is -1
930
930
}
931
931
932
+
933
+ static util::Result<SelectionResult> SelectCoinsSRD (const CAmount& target,
934
+ const CoinSelectionParams& cs_params,
935
+ const node::NodeContext& m_node,
936
+ int max_weight,
937
+ std::function<CoinsResult(CWallet&)> coin_setup)
938
+ {
939
+ std::unique_ptr<CWallet> wallet = std::make_unique<CWallet>(m_node.chain .get (), " " , CreateMockWalletDatabase ());
940
+ wallet->LoadWallet ();
941
+ LOCK (wallet->cs_wallet );
942
+ wallet->SetWalletFlag (WALLET_FLAG_DESCRIPTORS);
943
+ wallet->SetupDescriptorScriptPubKeyMans ();
944
+
945
+ CoinEligibilityFilter filter (0 , 0 , 0 ); // accept all coins without ancestors
946
+ Groups group = GroupOutputs (*wallet, coin_setup (*wallet), cs_params, {{filter}})[filter].all_groups ;
947
+ return SelectCoinsSRD (group.positive_group , target, cs_params.rng_fast , max_weight);
948
+ }
949
+
950
+ BOOST_AUTO_TEST_CASE (srd_tests)
951
+ {
952
+ // Test SRD:
953
+ // 1) Insufficient funds, select all provided coins and fail.
954
+ // 2) Exceeded max weight, coin selection always surpasses the max allowed weight.
955
+ // 3) Select coins without surpassing the max weight (some coins surpasses the max allowed weight, some others not)
956
+
957
+ FastRandomContext rand;
958
+ CoinSelectionParams dummy_params{ // Only used to provide the 'avoid_partial' flag.
959
+ rand,
960
+ /* change_output_size=*/ 34 ,
961
+ /* change_spend_size=*/ 68 ,
962
+ /* min_change_target=*/ CENT,
963
+ /* effective_feerate=*/ CFeeRate (0 ),
964
+ /* long_term_feerate=*/ CFeeRate (0 ),
965
+ /* discard_feerate=*/ CFeeRate (0 ),
966
+ /* tx_noinputs_size=*/ 10 + 34 , // static header size + output size
967
+ /* avoid_partial=*/ false ,
968
+ };
969
+
970
+ {
971
+ // #########################################################
972
+ // 1) Insufficient funds, select all provided coins and fail
973
+ // #########################################################
974
+ CAmount target = 49 .5L * COIN;
975
+ int max_weight = 10000 ; // high enough to not fail for this reason.
976
+ const auto & res = SelectCoinsSRD (target, dummy_params, m_node, max_weight, [&](CWallet& wallet) {
977
+ CoinsResult available_coins;
978
+ for (int j = 0 ; j < 10 ; ++j) {
979
+ add_coin (available_coins, wallet, CAmount (1 * COIN));
980
+ add_coin (available_coins, wallet, CAmount (2 * COIN));
981
+ }
982
+ return available_coins;
983
+ });
984
+ BOOST_CHECK (!res);
985
+ BOOST_CHECK (util::ErrorString (res).empty ()); // empty means "insufficient funds"
986
+ }
987
+
988
+ {
989
+ // ###########################
990
+ // 2) Test max weight exceeded
991
+ // ###########################
992
+ CAmount target = 49 .5L * COIN;
993
+ int max_weight = 3000 ;
994
+ const auto & res = SelectCoinsSRD (target, dummy_params, m_node, max_weight, [&](CWallet& wallet) {
995
+ CoinsResult available_coins;
996
+ for (int j = 0 ; j < 10 ; ++j) {
997
+ add_coin (available_coins, wallet, CAmount (1 * COIN), CFeeRate (0 ), 144 , false , 0 , true );
998
+ add_coin (available_coins, wallet, CAmount (2 * COIN), CFeeRate (0 ), 144 , false , 0 , true );
999
+ }
1000
+ return available_coins;
1001
+ });
1002
+ BOOST_CHECK (!res);
1003
+ BOOST_CHECK (util::ErrorString (res).original .find (" The inputs size exceeds the maximum weight" ) != std::string::npos);
1004
+ }
1005
+
1006
+ {
1007
+ // ################################################################################################################
1008
+ // 3) Test selection when some coins surpass the max allowed weight while others not. --> must find a good solution
1009
+ // ################################################################################################################
1010
+ CAmount target = 25 .33L * COIN;
1011
+ int max_weight = 10000 ; // WU
1012
+ const auto & res = SelectCoinsSRD (target, dummy_params, m_node, max_weight, [&](CWallet& wallet) {
1013
+ CoinsResult available_coins;
1014
+ for (int j = 0 ; j < 60 ; ++j) { // 60 UTXO --> 19,8 BTC total --> 60 × 272 WU = 16320 WU
1015
+ add_coin (available_coins, wallet, CAmount (0.33 * COIN), CFeeRate (0 ), 144 , false , 0 , true );
1016
+ }
1017
+ for (int i = 0 ; i < 10 ; i++) { // 10 UTXO --> 20 BTC total --> 10 × 272 WU = 2720 WU
1018
+ add_coin (available_coins, wallet, CAmount (2 * COIN), CFeeRate (0 ), 144 , false , 0 , true );
1019
+ }
1020
+ return available_coins;
1021
+ });
1022
+ BOOST_CHECK (res);
1023
+ }
1024
+ }
1025
+
932
1026
static util::Result<SelectionResult> select_coins (const CAmount& target, const CoinSelectionParams& cs_params, const CCoinControl& cc, std::function<CoinsResult(CWallet&)> coin_setup, interfaces::Chain* chain)
933
1027
{
934
1028
std::unique_ptr<CWallet> wallet = std::make_unique<CWallet>(chain, " " , CreateMockWalletDatabase ());
0 commit comments