Inside Bitcoin
Opening the selected file…
Inside Bitcoin
Opening the selected file…
The code behind the network, with room to understand it.
Release published 2026-10-28 · Snapshot fetched 2026-11-01. Latest stable release at fetch time; development branches and Git history are excluded. Only the selected code window and visible folders load here.
1001CAmount target = 29.5L * COIN;1002int max_selection_weight = 3000;1003const auto& res = CoinGrinder(target, dummy_params, m_node, max_selection_weight, [&](CWallet& wallet) {1004CoinsResult available_coins;1005for (int j = 0; j < 10; ++j) {1006add_coin(available_coins, wallet, CAmount(1 * COIN), CFeeRate(5000), 144, false, 0, true);1007add_coin(available_coins, wallet, CAmount(2 * COIN), CFeeRate(5000), 144, false, 0, true);1008}1009return available_coins;1010});1011BOOST_CHECK(!res);1012BOOST_CHECK(util::ErrorString(res).original.find("The inputs size exceeds the maximum weight") != std::string::npos);1013}1015{1016// ###############################################################################################################1017// 3) Test that the lowest-weight solution is found when some combinations would exceed the allowed weight1018// ################################################################################################################1019CAmount target = 25.33L * COIN;1020int max_selection_weight = 10'000; // WU1021const auto& res = CoinGrinder(target, dummy_params, m_node, max_selection_weight, [&](CWallet& wallet) {1022CoinsResult available_coins;1023for (int j = 0; j < 60; ++j) { // 60 UTXO --> 19,8 BTC total --> 60 × 272 WU = 16320 WU1024add_coin(available_coins, wallet, CAmount(0.33 * COIN), CFeeRate(5000), 144, false, 0, true);1025}1026for (int i = 0; i < 10; i++) { // 10 UTXO --> 20 BTC total --> 10 × 272 WU = 2720 WU1027add_coin(available_coins, wallet, CAmount(2 * COIN), CFeeRate(5000), 144, false, 0, true);1028}1029return available_coins;1030});1031SelectionResult expected_result(CAmount(0), SelectionAlgorithm::CG);1032for (int i = 0; i < 10; ++i) {1033add_coin(2 * COIN, i, expected_result);1034}1035for (int j = 0; j < 17; ++j) {1036add_coin(0.33 * COIN, j + 10, expected_result);1037}1038BOOST_CHECK(EquivalentResult(expected_result, *res));1039// Demonstrate how following improvements reduce iteration count and catch any regressions in the future.1040size_t expected_attempts = 37;1041BOOST_CHECK_MESSAGE(res->GetSelectionsEvaluated() == expected_attempts, strprintf("Expected %i attempts, but got %i", expected_attempts, res->GetSelectionsEvaluated()));1042}1044{1045// #################################################################################################################1046// 4) Test that two less valuable UTXOs with a combined lower weight are preferred over a more valuable heavier UTXO1047// #################################################################################################################1048CAmount target = 1.9L * COIN;1049int max_selection_weight = 400'000; // WU1050const auto& res = CoinGrinder(target, dummy_params, m_node, max_selection_weight, [&](CWallet& wallet) {1051CoinsResult available_coins;1052add_coin(available_coins, wallet, CAmount(2 * COIN), CFeeRate(5000), 144, false, 0, true, 148);1053add_coin(available_coins, wallet, CAmount(1 * COIN), CFeeRate(5000), 144, false, 0, true, 68);1054add_coin(available_coins, wallet, CAmount(1 * COIN), CFeeRate(5000), 144, false, 0, true, 68);1055return available_coins;1056});1057SelectionResult expected_result(CAmount(0), SelectionAlgorithm::CG);1058add_coin(1 * COIN, 1, expected_result);1059add_coin(1 * COIN, 2, expected_result);1060BOOST_CHECK(EquivalentResult(expected_result, *res));1061// Demonstrate how following improvements reduce iteration count and catch any regressions in the future.1062size_t expected_attempts = 3;1063BOOST_CHECK_MESSAGE(res->GetSelectionsEvaluated() == expected_attempts, strprintf("Expected %i attempts, but got %i", expected_attempts, res->GetSelectionsEvaluated()));1064}1066{1067// ###############################################################################################################1068// 5) Test finding a solution in a UTXO pool with mixed weights1069// ################################################################################################################1070CAmount target = 30L * COIN;1071int max_selection_weight = 400'000; // WU1072const auto& res = CoinGrinder(target, dummy_params, m_node, max_selection_weight, [&](CWallet& wallet) {1073CoinsResult available_coins;1074for (int j = 0; j < 5; ++j) {1075// Add heavy coins {3, 6, 9, 12, 15}1076add_coin(available_coins, wallet, CAmount((3 + 3 * j) * COIN), CFeeRate(5000), 144, false, 0, true, 350);1077// Add medium coins {2, 5, 8, 11, 14}1078add_coin(available_coins, wallet, CAmount((2 + 3 * j) * COIN), CFeeRate(5000), 144, false, 0, true, 250);1079// Add light coins {1, 4, 7, 10, 13}1080add_coin(available_coins, wallet, CAmount((1 + 3 * j) * COIN), CFeeRate(5000), 144, false, 0, true, 150);1081}1082return available_coins;1083});1084BOOST_CHECK(res);1085SelectionResult expected_result(CAmount(0), SelectionAlgorithm::CG);1086add_coin(14 * COIN, 1, expected_result);1087add_coin(13 * COIN, 2, expected_result);1088add_coin(4 * COIN, 3, expected_result);1089BOOST_CHECK(EquivalentResult(expected_result, *res));1090// Demonstrate how following improvements reduce iteration count and catch any regressions in the future.1091size_t expected_attempts = 92;1092BOOST_CHECK_MESSAGE(res->GetSelectionsEvaluated() == expected_attempts, strprintf("Expected %i attempts, but got %i", expected_attempts, res->GetSelectionsEvaluated()));1093}1095{1096// #################################################################################################################1097// 6) Test that the lightest solution among many clones is found1098// #################################################################################################################1099CAmount target = 9.9L * COIN;1100int max_selection_weight = 400'000; // WU1101const auto& res = CoinGrinder(target, dummy_params, m_node, max_selection_weight, [&](CWallet& wallet) {1102CoinsResult available_coins;1103// Expected Result: 4 + 3 + 2 + 1 = 10 BTC at 400 vB1104add_coin(available_coins, wallet, CAmount(4 * COIN), CFeeRate(5000), 144, false, 0, true, 100);1105add_coin(available_coins, wallet, CAmount(3 * COIN), CFeeRate(5000), 144, false, 0, true, 100);1106add_coin(available_coins, wallet, CAmount(2 * COIN), CFeeRate(5000), 144, false, 0, true, 100);1107add_coin(available_coins, wallet, CAmount(1 * COIN), CFeeRate(5000), 144, false, 0, true, 100);1108// Distracting clones:1109for (int j = 0; j < 100; ++j) {1110add_coin(available_coins, wallet, CAmount(8 * COIN), CFeeRate(5000), 144, false, 0, true, 1000);1111}1112for (int j = 0; j < 100; ++j) {1113add_coin(available_coins, wallet, CAmount(7 * COIN), CFeeRate(5000), 144, false, 0, true, 800);1114}1115for (int j = 0; j < 100; ++j) {1116add_coin(available_coins, wallet, CAmount(6 * COIN), CFeeRate(5000), 144, false, 0, true, 600);1117}1118for (int j = 0; j < 100; ++j) {1119add_coin(available_coins, wallet, CAmount(5 * COIN), CFeeRate(5000), 144, false, 0, true, 400);1120}1121return available_coins;1122});1123SelectionResult expected_result(CAmount(0), SelectionAlgorithm::CG);1124add_coin(4 * COIN, 0, expected_result);1125add_coin(3 * COIN, 0, expected_result);1126add_coin(2 * COIN, 0, expected_result);1127add_coin(1 * COIN, 0, expected_result);1128BOOST_CHECK(EquivalentResult(expected_result, *res));1129// Demonstrate how following improvements reduce iteration count and catch any regressions in the future.1130size_t expected_attempts = 38;1131BOOST_CHECK_MESSAGE(res->GetSelectionsEvaluated() == expected_attempts, strprintf("Expected %i attempts, but got %i", expected_attempts, res->GetSelectionsEvaluated()));1132}1134{1135// #################################################################################################################1136// 7) Test that lots of tiny UTXOs can be skipped if they are too heavy while there are enough funds in lookahead1137// #################################################################################################################1138CAmount target = 1.9L * COIN;1139int max_selection_weight = 40000; // WU1140const auto& res = CoinGrinder(target, dummy_params, m_node, max_selection_weight, [&](CWallet& wallet) {1141CoinsResult available_coins;1142add_coin(available_coins, wallet, CAmount(1.8 * COIN), CFeeRate(5000), 144, false, 0, true, 2500);1143add_coin(available_coins, wallet, CAmount(1 * COIN), CFeeRate(5000), 144, false, 0, true, 1000);1144add_coin(available_coins, wallet, CAmount(1 * COIN), CFeeRate(5000), 144, false, 0, true, 1000);1145for (int j = 0; j < 100; ++j) {1146// make a 100 unique coins only differing by one sat1147add_coin(available_coins, wallet, CAmount(0.01 * COIN + j), CFeeRate(5000), 144, false, 0, true, 110);1148}1149return available_coins;1150});1151SelectionResult expected_result(CAmount(0), SelectionAlgorithm::CG);1152add_coin(1 * COIN, 1, expected_result);1153add_coin(1 * COIN, 2, expected_result);1154BOOST_CHECK(EquivalentResult(expected_result, *res));1155// Demonstrate how following improvements reduce iteration count and catch any regressions in the future.1156size_t expected_attempts = 7;1157BOOST_CHECK_MESSAGE(res->GetSelectionsEvaluated() == expected_attempts, strprintf("Expected %i attempts, but got %i", expected_attempts, res->GetSelectionsEvaluated()));1158}1160{1161// #################################################################################################################1162// 8) Test input set that has a solution will not find a solution before reaching the attempt limit1163// #################################################################################################################1164CAmount target = 8 * COIN;1165int max_selection_weight = 3200; // WU1166dummy_params.m_min_change_target = 0;1167const auto& result_a = CoinGrinder(target, dummy_params, m_node, max_selection_weight, [&](CWallet& wallet) {1168CoinsResult doppelgangers;1169for (int i = 0; i < 18; ++i) {1170add_coin(doppelgangers, wallet, CAmount(1 * COIN + i), CFeeRate(0), 144, false, 0, true, 96 + i);1171}1172return doppelgangers;1173});1174BOOST_CHECK(result_a);1175SelectionResult expected_result(CAmount(0), SelectionAlgorithm::CG);1176for (int i = 0; i < 8; ++i) {1177add_coin(1 * COIN + i, 0, expected_result);1178}1179BOOST_CHECK(EquivalentResult(expected_result, *result_a));1180// Demonstrate a solution is found before the attempts limit is reached.1181size_t expected_attempts = 87'525;1182BOOST_CHECK_MESSAGE(result_a->GetSelectionsEvaluated() == expected_attempts, strprintf("Expected %i attempts, but got %i", expected_attempts, result_a->GetSelectionsEvaluated()));1184// Adding one more doppelganger causes the attempt limit to be reached before finding a solution.1185const auto& result_b = CoinGrinder(target, dummy_params, m_node, max_selection_weight, [&](CWallet& wallet) {1186CoinsResult doppelgangers;1187for (int i = 0; i < 19; ++i) {1188add_coin(doppelgangers, wallet, CAmount(1 * COIN + i), CFeeRate(0), 144, false, 0, true, 96 + i);1189}1190return doppelgangers;1191});1192BOOST_CHECK(!result_b);1193}1194}1196static 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)1197{1198std::unique_ptr<CWallet> wallet = NewWallet(m_node);1199auto available_coins = coin_setup(*wallet);1201LOCK(wallet->cs_wallet);1202auto result = SelectCoins(*wallet, available_coins, /*pre_set_inputs=*/ {}, target, cc, cs_params);1203if (result) {1204const auto signedTxSize = 10 + 34 + 68 * result->GetInputSet().size(); // static header size + output size + inputs size (P2WPKH)1205BOOST_CHECK_LE(signedTxSize * WITNESS_SCALE_FACTOR, MAX_STANDARD_TX_WEIGHT);1207BOOST_CHECK_GE(result->GetSelectedValue(), target);1208}1209return result;1210}1212static bool has_coin(const OutputSet& set, CAmount amount)1213{1214return std::any_of(set.begin(), set.end(), [&](const auto& coin) { return coin->GetEffectiveValue() == amount; });1215}1217BOOST_AUTO_TEST_CASE(check_max_selection_weight)1218{1219const CAmount target = 49.5L * COIN;1220CCoinControl cc;1222FastRandomContext rand;1223CoinSelectionParams cs_params{1224rand,1225/*change_output_size=*/34,1226/*change_spend_size=*/68,1227/*min_change_target=*/CENT,1228/*effective_feerate=*/CFeeRate(0),1229/*long_term_feerate=*/CFeeRate(0),1230/*discard_feerate=*/CFeeRate(0),1231/*tx_noinputs_size=*/10 + 34, // static header size + output size1232/*avoid_partial=*/false,1233};1235int max_weight = MAX_STANDARD_TX_WEIGHT - WITNESS_SCALE_FACTOR * (cs_params.tx_noinputs_size + cs_params.change_output_size);1236{1237// Scenario 1:1238// The actor starts with 1x 50.0 BTC and 1515x 0.033 BTC (~100.0 BTC total) unspent outputs1239// Then tries to spend 49.5 BTC1240// The 50.0 BTC output should be selected, because the transaction would otherwise be too large1242// Perform selection1244const auto result = select_coins(1245target, cs_params, cc, [&](CWallet& wallet) {1246CoinsResult available_coins;1247for (int j = 0; j < 1515; ++j) {1248add_coin(available_coins, wallet, CAmount(0.033 * COIN), CFeeRate(0), 144, false, 0, true);1249}
Independent explanations by MyBitcoinExplorer. Not affiliated with or endorsed by Bitcoin Core. Guides are educational and do not replace the source or its tests.
Bitcoin Core source © its contributors, distributed under the MIT license. Original notices and bundled third-party licenses are retained in the source tree.