Skip to content

Commit

Permalink
[docs] add doxygen docs to coin selection code
Browse files Browse the repository at this point in the history
Co-authored-by: Xekyo <murch@murch.one>
  • Loading branch information
glozow and murchandamus committed Apr 22, 2021
1 parent 04d85fd commit f9cafd0
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 3 deletions.
30 changes: 29 additions & 1 deletion src/wallet/coinselection.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ static constexpr CAmount MIN_CHANGE{COIN / 100};
//! final minimum change amount after paying for fees
static const CAmount MIN_FINAL_CHANGE = MIN_CHANGE/2;

/** A UTXO under consideration for use in funding a new transaction. */
class CInputCoin {
public:
CInputCoin(const CTransactionRef& tx, unsigned int i)
Expand Down Expand Up @@ -56,31 +57,58 @@ class CInputCoin {
}
};

/** Parameters for filtering which OutputGroups we may use in coin selection.
* We start by being very selective and requiring multiple confirmations and
* then get more permissive if we cannot fund the transaction. */
struct CoinEligibilityFilter
{
/** Minimum number of confirmations for outputs that we sent to ourselves.
* We may use unconfirmed UTXOs sent from ourselves, e.g. change outputs. */
const int conf_mine;
/** Minimum number of confirmations for outputs received from a different
* wallet. We never spend unconfirmed foreign outputs as we cannot rely on these funds yet. */
const int conf_theirs;
/** Maximum number of total unconfirmed ancestors for all UTXOs in the OutputGroup. */
const uint64_t max_ancestors;
/** Maximum number of descendants that a single UTXO in the OutputGroup may have. */
const uint64_t max_descendants;
const bool m_include_partial_groups{false}; //! Include partial destination groups when avoid_reuse and there are full groups
/** When avoid_reuse=true and there are full groups (OUTPUT_GROUP_MAX_ENTRIES), whether or not to use any partial groups.*/
const bool m_include_partial_groups{false};

CoinEligibilityFilter(int conf_mine, int conf_theirs, uint64_t max_ancestors) : conf_mine(conf_mine), conf_theirs(conf_theirs), max_ancestors(max_ancestors), max_descendants(max_ancestors) {}
CoinEligibilityFilter(int conf_mine, int conf_theirs, uint64_t max_ancestors, uint64_t max_descendants) : conf_mine(conf_mine), conf_theirs(conf_theirs), max_ancestors(max_ancestors), max_descendants(max_descendants) {}
CoinEligibilityFilter(int conf_mine, int conf_theirs, uint64_t max_ancestors, uint64_t max_descendants, bool include_partial) : conf_mine(conf_mine), conf_theirs(conf_theirs), max_ancestors(max_ancestors), max_descendants(max_descendants), m_include_partial_groups(include_partial) {}
};

/** A group of UTXOs paid to the same output script. */
struct OutputGroup
{
/** The list of UTXOs contained in this output group. */
std::vector<CInputCoin> m_outputs;
/** Whether the UTXOs were sent by the wallet to itself. This is relevant because we may want at
* least certain number of confirmations on UTXOs received from outside wallets while trusting
* our own UTXOs more. */
bool m_from_me{true};
/** The total value of the UTXOs in sum. */
CAmount m_value{0};
/** The minimum number of confirmations the UTXOs in the group have. Unconfirmed is 0. */
int m_depth{999};
/** The aggregated count of unconfirmed ancestors of all UTXOs in this
* group. Not deduplicated and may overestimate when ancestors are shared. */
size_t m_ancestors{0};
/** The maximum count of descendants of a single UTXO in this output group. */
size_t m_descendants{0};
/** The value of the UTXOs after deducting the cost of spending them at the effective feerate. */
CAmount effective_value{0};
/** The fee to spend these UTXOs at the effective feerate. */
CAmount fee{0};
/** The target feerate of the transaction we're trying to build. */
CFeeRate m_effective_feerate{0};
/** The fee to spend these UTXOs at the long term feerate. */
CAmount long_term_fee{0};
/** The feerate for spending a created change output eventually (i.e. not urgently, and thus at
* a lower feerate). Calculated using long term fee estimate. This is used to decide whether
* it could be economical to create a change output. */
CFeeRate m_long_term_feerate{0};

OutputGroup() {}
Expand Down
23 changes: 21 additions & 2 deletions src/wallet/wallet.h
Original file line number Diff line number Diff line change
Expand Up @@ -612,17 +612,28 @@ class COutput
}
};

/** Parameters for one iteration of Coin Selection. */
struct CoinSelectionParams
{
/** When true, try to use the Branch and Bound algorithm before falling back to knapsack. */
bool use_bnb = true;
/** Size of a change output in bytes, determined by the output type. */
size_t change_output_size = 0;
/** Size of the input to spend a change output in virtual bytes. */
size_t change_spend_size = 0;
/** The targeted feerate of the transaction being built. */
CFeeRate m_effective_feerate;
/** The feerate estimate used to calculate the upper bound of the cost to spend a change output. */
CFeeRate m_long_term_feerate;
/** If the cost to spend a change output at the discard feerate exceeds its value, drop it to fees. */
CFeeRate m_discard_feerate;
/** Size of the transaction before inputs are selected, the header and recipient output(s). */
size_t tx_noinputs_size = 0;
/** Indicate that we are subtracting the fee from outputs */
bool m_subtract_fee_outputs = false;
/** When true, always spend all (up to OUTPUT_GROUP_MAX_ENTRIES) or none of the outputs
* associated with the same address. This helps reduce privacy leaks resulting from address
* reuse. Dust outputs are not eligible to be added to output groups and thus not considered. */
bool m_avoid_partial_spends = false;

CoinSelectionParams(bool use_bnb, size_t change_output_size, size_t change_spend_size, CFeeRate effective_feerate,
Expand Down Expand Up @@ -765,8 +776,11 @@ class CWallet final : public WalletStorage, public interfaces::Chain::Notificati

/**
* Select a set of coins such that nValueRet >= nTargetValue and at least
* all coins from coinControl are selected; Never select unconfirmed coins
* if they are not ours
* all coins from coin_ontrol are selected; Never select unconfirmed coins if they are not ours
* param@[out] setCoinsRet Populated with inputs including pre-selected inputs from
* coin_control and Coin Selection if successful.
* param@[out] nValueRet Total value of selected coins including pre-selected ones
* from coin_control and Coin Selection if successful.
*/
bool SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAmount& nTargetValue, std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet,
const CCoinControl& coin_control, CoinSelectionParams& coin_selection_params, bool& bnb_used) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
Expand Down Expand Up @@ -851,6 +865,11 @@ class CWallet final : public WalletStorage, public interfaces::Chain::Notificati
* small change; This method is stochastic for some inputs and upon
* completion the coin set and corresponding actual target value is
* assembled
* param@[in] coins Set of UTXOs to consider. These will be categorized into
* OutputGroups and filtered using eligibility_filter before
* selecting coins.
* param@[out] setCoinsRet Populated with the coins selected if successful.
* param@[out] nValueRet Used to return the total value of selected coins.
*/
bool SelectCoinsMinConf(const CAmount& nTargetValue, const CoinEligibilityFilter& eligibility_filter, std::vector<COutput> coins,
std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet, const CoinSelectionParams& coin_selection_params, bool& bnb_used) const;
Expand Down

0 comments on commit f9cafd0

Please sign in to comment.