@@ -1390,6 +1390,96 @@ BOOST_AUTO_TEST_CASE(srd_tests)
1390
1390
}
1391
1391
}
1392
1392
1393
+ static util::Result<SelectionResult> SandCompactorHelper (const CAmount& target,
1394
+ const CoinSelectionParams& cs_params,
1395
+ const node::NodeContext& m_node,
1396
+ int max_weight,
1397
+ std::function<CoinsResult(CWallet&)> coin_setup)
1398
+ {
1399
+ std::unique_ptr<CWallet> wallet = NewWallet (m_node);
1400
+ CoinEligibilityFilter filter (0 , 0 , 0 ); // accept all coins without ancestors
1401
+ Groups groups = GroupOutputs (*wallet, coin_setup (*wallet), cs_params, {{filter}})[filter].all_groups ;
1402
+
1403
+ std::vector<OutputGroup> sand_compactor_utxo_pool = cs_params.m_discard_feerate > cs_params.m_effective_feerate ? groups.mixed_group : groups.positive_group ;
1404
+ return SandCompactor (sand_compactor_utxo_pool, target, cs_params.m_min_change_target , max_weight);
1405
+ }
1406
+
1407
+ BOOST_AUTO_TEST_CASE (sand_compactor_tests)
1408
+ {
1409
+ // Test SandCompactor:
1410
+ // 1) Insufficient funds
1411
+ // 2) Exceeded max weight, coin selection always surpasses the max allowed weight.
1412
+ // 3) Select coins without surpassing the max weight (some coins surpasses the max allowed weight, some others not)
1413
+
1414
+ FastRandomContext rand;
1415
+ CoinSelectionParams dummy_params{ // Only used to provide the 'avoid_partial' flag.
1416
+ rand,
1417
+ /* change_output_size=*/ 34 ,
1418
+ /* change_spend_size=*/ 68 ,
1419
+ /* min_change_target=*/ CENT,
1420
+ /* effective_feerate=*/ CFeeRate (0 ),
1421
+ /* long_term_feerate=*/ CFeeRate (0 ),
1422
+ /* discard_feerate=*/ CFeeRate (0 ),
1423
+ /* tx_noinputs_size=*/ 10 + 34 , // static header size + output size
1424
+ /* avoid_partial=*/ false ,
1425
+ };
1426
+
1427
+ {
1428
+ // #########################################################
1429
+ // 1) Insufficient funds, select all provided coins and fail
1430
+ // #########################################################
1431
+ CAmount target = 49 .5L * COIN;
1432
+ int max_weight = 10000 ; // high enough to not fail for this reason.
1433
+ const auto & res = SandCompactorHelper (target, dummy_params, m_node, max_weight, [&](CWallet& wallet) {
1434
+ CoinsResult available_coins;
1435
+ for (int j = 0 ; j < 10 ; ++j) {
1436
+ add_coin (available_coins, wallet, CAmount (1 * COIN));
1437
+ add_coin (available_coins, wallet, CAmount (2 * COIN));
1438
+ }
1439
+ return available_coins;
1440
+ });
1441
+ BOOST_CHECK (!res);
1442
+ BOOST_CHECK (util::ErrorString (res).empty ()); // empty means "insufficient funds"
1443
+ }
1444
+
1445
+ {
1446
+ // ###########################
1447
+ // 2) Test max weight exceeded
1448
+ // ###########################
1449
+ CAmount target = 29 .5L * COIN;
1450
+ int max_weight = 3000 ;
1451
+ const auto & res = SandCompactorHelper (target, dummy_params, m_node, max_weight, [&](CWallet& wallet) {
1452
+ CoinsResult available_coins;
1453
+ for (int j = 0 ; j < 10 ; ++j) {
1454
+ add_coin (available_coins, wallet, CAmount (1 * COIN), CFeeRate (0 ), 144 , false , 0 , true );
1455
+ add_coin (available_coins, wallet, CAmount (2 * COIN), CFeeRate (0 ), 144 , false , 0 , true );
1456
+ }
1457
+ return available_coins;
1458
+ });
1459
+ BOOST_CHECK (!res);
1460
+ BOOST_CHECK (util::ErrorString (res).original .find (" The inputs size exceeds the maximum weight" ) != std::string::npos);
1461
+ }
1462
+
1463
+ {
1464
+ // ################################################################################################################
1465
+ // 3) Test selection when some coins surpass the max allowed weight while others not. --> must find a good solution
1466
+ // ################################################################################################################
1467
+ CAmount target = 25 .33L * COIN;
1468
+ int max_weight = 10000 ; // WU
1469
+ const auto & res = SandCompactorHelper (target, dummy_params, m_node, max_weight, [&](CWallet& wallet) {
1470
+ CoinsResult available_coins;
1471
+ for (int j = 0 ; j < 60 ; ++j) { // 60 UTXO --> 19,8 BTC total --> 60 × 272 WU = 16320 WU
1472
+ add_coin (available_coins, wallet, CAmount (0.33 * COIN), CFeeRate (0 ), 144 , false , 0 , true );
1473
+ }
1474
+ for (int i = 0 ; i < 10 ; i++) { // 10 UTXO --> 20 BTC total --> 10 × 272 WU = 2720 WU
1475
+ add_coin (available_coins, wallet, CAmount (2 * COIN), CFeeRate (0 ), 144 , false , 0 , true );
1476
+ }
1477
+ return available_coins;
1478
+ });
1479
+ BOOST_CHECK (res);
1480
+ }
1481
+ }
1482
+
1393
1483
static util::Result<SelectionResult> select_coins (const CAmount& target, const CoinSelectionParams& cs_params, const CCoinControl& cc, std::function<CoinsResult(CWallet&)> coin_setup, const node::NodeContext& m_node)
1394
1484
{
1395
1485
std::unique_ptr<CWallet> wallet = NewWallet (m_node);
0 commit comments