Skip to content

Commit 249473e

Browse files
committed
Merge 260ede1 into merged_master (Bitcoin PR bitcoin/bitcoin#24644)
2 parents 44911e8 + 260ede1 commit 249473e

File tree

7 files changed

+298
-11
lines changed

7 files changed

+298
-11
lines changed

doc/tracing.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,49 @@ Arguments passed:
168168
4. Value of the coin as `int64`
169169
5. If the coin is a coinbase as `bool`
170170

171+
### Context `coin_selection`
172+
173+
#### Tracepoint `coin_selection:selected_coins`
174+
175+
Is called when `SelectCoins` completes.
176+
177+
Arguments passed:
178+
1. Wallet name as `pointer to C-style string`
179+
2. Coin selection algorithm name as `pointer to C-style string`
180+
3. Selection target value as `int64`
181+
4. Calculated waste metric of the solution as `int64`
182+
5. Total value of the selected inputs as `int64`
183+
184+
#### Tracepoint `coin_selection:normal_create_tx_internal`
185+
186+
Is called when the first `CreateTransactionInternal` completes.
187+
188+
Arguments passed:
189+
1. Wallet name as `pointer to C-style string`
190+
2. Whether `CreateTransactionInternal` succeeded as `bool`
191+
3. The expected transaction fee as an `int64`
192+
4. The position of the change output as an `int32`
193+
194+
#### Tracepoint `coin_selection:attempting_aps_create_tx`
195+
196+
Is called when `CreateTransactionInternal` is called the second time for the optimistic
197+
Avoid Partial Spends selection attempt. This is used to determine whether the next
198+
tracepoints called are for the Avoid Partial Spends solution, or a different transaction.
199+
200+
Arguments passed:
201+
1. Wallet name as `pointer to C-style string`
202+
203+
#### Tracepoint `coin_selection:aps_create_tx_internal`
204+
205+
Is called when the second `CreateTransactionInternal` with Avoid Partial Spends enabled completes.
206+
207+
Arguments passed:
208+
1. Wallet name as `pointer to C-style string`
209+
2. Whether the Avoid Partial Spends solution will be used as `bool`
210+
3. Whether `CreateTransactionInternal` succeeded as` bool`
211+
4. The expected transaction fee as an `int64`
212+
5. The position of the change output as an `int32`
213+
171214
## Adding tracepoints to Bitcoin Core
172215

173216
To add a new tracepoint, `#include <util/trace.h>` in the compilation unit where

src/wallet/coinselection.cpp

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ static const size_t TOTAL_TRIES = 100000;
106106
std::optional<SelectionResult> SelectCoinsBnB(std::vector<OutputGroup>& utxo_pool, const CAmount& selection_target, const CAmount& cost_of_change)
107107
{
108108
CAmountMap map_target{{ ::policyAsset, selection_target}};
109-
SelectionResult result(map_target);
109+
SelectionResult result(map_target, SelectionAlgorithm::BNB);
110110
CAmount curr_value = 0;
111111
std::vector<size_t> curr_selection; // selected utxo indexes
112112

@@ -210,7 +210,7 @@ std::optional<SelectionResult> SelectCoinsBnB(std::vector<OutputGroup>& utxo_poo
210210
std::optional<SelectionResult> SelectCoinsSRD(const std::vector<OutputGroup>& utxo_pool, CAmount target_value, FastRandomContext& rng)
211211
{
212212
CAmountMap map_target{{ ::policyAsset, target_value}};
213-
SelectionResult result(map_target);
213+
SelectionResult result(map_target, SelectionAlgorithm::SRD);
214214

215215
std::vector<size_t> indexes;
216216
indexes.resize(utxo_pool.size());
@@ -293,7 +293,7 @@ static void ApproximateBestSubset(FastRandomContext& insecure_rand, const std::v
293293
std::optional<SelectionResult> KnapsackSolver(std::vector<OutputGroup>& groups, const CAmountMap& mapTargetValue,
294294
CAmount change_target, FastRandomContext& rng)
295295
{
296-
SelectionResult result(mapTargetValue);
296+
SelectionResult result(mapTargetValue, SelectionAlgorithm::KNAPSACK);
297297

298298
std::vector<OutputGroup> inner_groups;
299299
// ELEMENTS: this is not used
@@ -404,7 +404,7 @@ std::optional<SelectionResult> KnapsackSolver(std::vector<OutputGroup>& groups,
404404
CAmount change_target, FastRandomContext& rng, const CAsset& asset)
405405
{
406406
CAmountMap map_target{{ asset, nTargetValue }};
407-
SelectionResult result(map_target);
407+
SelectionResult result(map_target, SelectionAlgorithm::KNAPSACK);
408408

409409
// List of values less than target
410410
std::optional<OutputGroup> lowest_larger;
@@ -677,4 +677,17 @@ std::string COutput::ToString() const
677677
{
678678
return strprintf("COutput(%s, %d, %d) [%s] [%s]", outpoint.hash.ToString(), outpoint.n, depth, FormatMoney(value), asset.GetHex());
679679
}
680+
681+
std::string GetAlgorithmName(const SelectionAlgorithm algo)
682+
{
683+
switch (algo)
684+
{
685+
case SelectionAlgorithm::BNB: return "bnb";
686+
case SelectionAlgorithm::KNAPSACK: return "knapsack";
687+
case SelectionAlgorithm::SRD: return "srd";
688+
case SelectionAlgorithm::MANUAL: return "manual";
689+
// No default case to allow for compiler to warn
690+
}
691+
assert(false);
692+
}
680693
} // namespace wallet

src/wallet/coinselection.h

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -254,21 +254,34 @@ struct OutputGroup
254254
*/
255255
[[nodiscard]] CAmount GenerateChangeTarget(CAmount payment_value, FastRandomContext& rng);
256256

257+
enum class SelectionAlgorithm : uint8_t
258+
{
259+
BNB = 0,
260+
KNAPSACK = 1,
261+
SRD = 2,
262+
MANUAL = 3,
263+
};
264+
265+
std::string GetAlgorithmName(const SelectionAlgorithm algo);
266+
257267
struct SelectionResult
258268
{
259269
private:
260270
/** Set of inputs selected by the algorithm to use in the transaction */
261271
std::set<COutput> m_selected_inputs;
262-
/** The target the algorithm selected for. Note that this may not be equal to the recipient amount as it can include non-input fees */
263-
const CAmountMap m_target;
264272
/** Whether the input values for calculations should be the effective value (true) or normal value (false) */
265273
bool m_use_effective{false};
266274
/** The computed waste */
267275
std::optional<CAmount> m_waste;
268276

269277
public:
270-
explicit SelectionResult(const CAmountMap target)
271-
: m_target(target) {}
278+
/** The target the algorithm selected for. Note that this may not be equal to the recipient amount as it can include non-input fees */
279+
const CAmountMap m_target;
280+
/** The algorithm used to produce this result */
281+
const SelectionAlgorithm m_algo;
282+
283+
explicit SelectionResult(const CAmountMap target, SelectionAlgorithm algo)
284+
: m_target(target), m_algo(algo) {}
272285

273286
SelectionResult() = delete;
274287

src/wallet/spend.cpp

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include <util/fees.h>
1515
#include <util/moneystr.h>
1616
#include <util/rbf.h>
17+
#include <util/trace.h>
1718
#include <util/translation.h>
1819
#include <wallet/coincontrol.h>
1920
#include <wallet/fees.h>
@@ -581,9 +582,10 @@ std::optional<SelectionResult> SelectCoins(const CWallet& wallet, const std::vec
581582
*/
582583
preset_inputs.Insert(out, /*ancestors=*/ 0, /*descendants=*/ 0, /*positive_only=*/ false);
583584
}
584-
SelectionResult result(mapTargetValue);
585+
SelectionResult result(mapTargetValue, SelectionAlgorithm::MANUAL);
585586
result.AddInput(preset_inputs);
586587
if (result.GetSelectedValue() < mapTargetValue) return std::nullopt;
588+
result.ComputeAndSetWaste(coin_selection_params.m_cost_of_change);
587589
return result;
588590
}
589591

@@ -706,7 +708,7 @@ std::optional<SelectionResult> SelectCoins(const CWallet& wallet, const std::vec
706708
// permissive CoinEligibilityFilter.
707709
std::optional<SelectionResult> res = [&] {
708710
// Pre-selected inputs already cover the target amount.
709-
if (value_to_select <= CAmountMap{}) return std::make_optional(SelectionResult(mapTargetValue));
711+
if (value_to_select <= CAmountMap{}) return std::make_optional(SelectionResult(mapTargetValue, SelectionAlgorithm::MANUAL));
710712

711713
// If possible, fund the transaction with confirmed UTXOs only. Prefer at least six
712714
// confirmations on outputs received from other wallets and only spend confirmed change.
@@ -761,6 +763,9 @@ std::optional<SelectionResult> SelectCoins(const CWallet& wallet, const std::vec
761763
// add preset inputs to the total value selected
762764
// Add preset inputs to result
763765
res->AddInput(preset_inputs);
766+
if (res->m_algo == SelectionAlgorithm::MANUAL) {
767+
res->ComputeAndSetWaste(coin_selection_params.m_cost_of_change);
768+
}
764769

765770
return res;
766771
}
@@ -1240,6 +1245,7 @@ static bool CreateTransactionInternal(
12401245
error = _("Insufficient funds");
12411246
return false;
12421247
}
1248+
TRACE5(coin_selection, selected_coins, wallet.GetName().c_str(), GetAlgorithmName(result->m_algo).c_str(), result->m_target, result->GetWaste(), result->GetSelectedValue());
12431249

12441250
// If all of our inputs are explicit, we don't need a blinded dummy
12451251
if (may_need_blinded_dummy) {
@@ -1793,8 +1799,10 @@ bool CreateTransaction(
17931799
int nChangePosIn = nChangePosInOut;
17941800
Assert(!tx); // tx is an out-param. TODO change the return type from bool to tx (or nullptr)
17951801
bool res = CreateTransactionInternal(wallet, vecSend, tx, nFeeRet, nChangePosInOut, error, coin_control, fee_calc_out, sign, blind_details, issuance_details);
1802+
TRACE4(coin_selection, normal_create_tx_internal, wallet.GetName().c_str(), res, nFeeRet, nChangePosInOut);
17961803
// try with avoidpartialspends unless it's enabled already
17971804
if (res && nFeeRet > 0 /* 0 means non-functional fee rate estimation */ && wallet.m_max_aps_fee > -1 && !coin_control.m_avoid_partial_spends) {
1805+
TRACE1(coin_selection, attempting_aps_create_tx, wallet.GetName().c_str());
17981806
CCoinControl tmp_cc = coin_control;
17991807
tmp_cc.m_avoid_partial_spends = true;
18001808
CAmount nFeeRet2;
@@ -1807,6 +1815,7 @@ bool CreateTransaction(
18071815
// if fee of this alternative one is within the range of the max fee, we use this one
18081816
const bool use_aps = nFeeRet2 <= nFeeRet + wallet.m_max_aps_fee;
18091817
wallet.WalletLogPrintf("Fee non-grouped = %lld, grouped = %lld, using %s\n", nFeeRet, nFeeRet2, use_aps ? "grouped" : "non-grouped");
1818+
TRACE5(coin_selection, aps_create_tx_internal, wallet.GetName().c_str(), use_aps, res, nFeeRet2, nChangePosInOut2);
18101819
if (use_aps) {
18111820
tx = tx2;
18121821
nFeeRet = nFeeRet2;

src/wallet/test/coinselector_tests.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ BOOST_AUTO_TEST_CASE(bnb_search_test)
175175
FastRandomContext rand{};
176176
// Setup
177177
std::vector<COutput> utxo_pool;
178-
SelectionResult expected_result(CAmountMap{{::policyAsset, 0}});
178+
SelectionResult expected_result(CAmountMap{{::policyAsset, 0}}, SelectionAlgorithm::BNB);
179179

180180
/////////////////////////
181181
// Known Outcome tests //

0 commit comments

Comments
 (0)