diff --git a/src/wallet/coinselection.h b/src/wallet/coinselection.h index f0e1addaf15dc..5c1b36be6e32e 100644 --- a/src/wallet/coinselection.h +++ b/src/wallet/coinselection.h @@ -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) @@ -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 unconfirmed ancestors aggregated across all UTXOs in an 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 m_outputs; + /** Whether the UTXOs were sent by the wallet to itself. This is relevant because we may want at + * least a 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() {} diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index cc197d329c890..03adca7a896bd 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -564,7 +564,15 @@ class COutput { public: const CWalletTx *tx; + + /** Index in tx->vout. */ int i; + + /** + * Depth in block chain. + * If > 0: the tx is on chain and has this many confirmations. + * If = 0: the tx is waiting confirmation. + * If < 0: a conflicting tx is on chain and has this many confirmations. */ int nDepth; /** Pre-computed estimated size of this output as a fully-signed input in a transaction. Can be -1 if it could not be calculated */ @@ -604,17 +612,30 @@ class COutput } }; +/** Parameters for one iteration of Coin Selection. */ struct CoinSelectionParams { + /** Toggles use of Branch and Bound instead of Knapsack solver. */ 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 estimate an upper bound on what should be sufficient to spend + * the change output sometime in the future. */ 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 coin selection, consisting of the header and recipient + * output(s), excluding the inputs and change 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, @@ -652,7 +673,10 @@ class CWallet final : public WalletStorage, public interfaces::Chain::Notificati //! the current wallet version: clients below this version are not able to load the wallet int nWalletVersion GUARDED_BY(cs_wallet){FEATURE_BASE}; + /** The next scheduled rebroadcast of wallet transactions. */ int64_t nNextResend = 0; + /** Whether this wallet will submit newly created transactions to the node's mempool and + * prompt rebroadcasts (see ResendWalletTransactions()). */ bool fBroadcastTransactions = false; // Local time that the tip block was received. Used to schedule wallet rebroadcasts. std::atomic m_best_block_time {0}; @@ -694,6 +718,7 @@ class CWallet final : public WalletStorage, public interfaces::Chain::Notificati * Should be called with non-zero block_hash and posInBlock if this is for a transaction that is included in a block. */ void SyncTransaction(const CTransactionRef& tx, CWalletTx::Confirmation confirm, bool update_tx = true) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); + /** WalletFlags set on this wallet. */ std::atomic m_wallet_flags{0}; bool SetAddressBookWithDB(WalletBatch& batch, const CTxDestination& address, const std::string& strName, const std::string& strPurpose); @@ -753,8 +778,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_control 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& vAvailableCoins, const CAmount& nTargetValue, std::set& setCoinsRet, CAmount& nValueRet, const CCoinControl& coin_control, CoinSelectionParams& coin_selection_params, bool& bnb_used) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); @@ -788,6 +816,8 @@ class CWallet final : public WalletStorage, public interfaces::Chain::Notificati /** Interface to assert chain access */ bool HaveChain() const { return m_chain ? true : false; } + /** Map from txid to CWalletTx for all transactions this wallet is + * interested in, including received and sent transactions. */ std::map mapWallet GUARDED_BY(cs_wallet); typedef std::multimap TxItems; @@ -799,6 +829,10 @@ class CWallet final : public WalletStorage, public interfaces::Chain::Notificati std::map m_address_book GUARDED_BY(cs_wallet); const CAddressBookData* FindAddressBookEntry(const CTxDestination&, bool allow_change = false) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); + /** Set of Coins owned by this wallet that we won't try to spend from. A + * Coin may be locked if it has already been used to fund a transaction + * that hasn't confirmed yet. We wouldn't consider the Coin spent already, + * but also shouldn't try to use it again. */ std::set setLockedCoins GUARDED_BY(cs_wallet); /** Registered interfaces::Chain::Notifications handler. */ @@ -833,6 +867,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 coins, std::set& setCoinsRet, CAmount& nValueRet, const CoinSelectionParams& coin_selection_params, bool& bnb_used) const; @@ -1015,6 +1054,8 @@ class CWallet final : public WalletStorage, public interfaces::Chain::Notificati CFeeRate m_pay_tx_fee{DEFAULT_PAY_TX_FEE}; unsigned int m_confirm_target{DEFAULT_TX_CONFIRM_TARGET}; + /** Allow Coin Selection to pick unconfirmed UTXOs that were sent from our own wallet if it + * cannot fund the transaction otherwise. */ bool m_spend_zero_conf_change{DEFAULT_SPEND_ZEROCONF_CHANGE}; bool m_signal_rbf{DEFAULT_WALLET_RBF}; bool m_allow_fallback_fee{true}; //!< will be false if -fallbackfee=0 @@ -1025,7 +1066,12 @@ class CWallet final : public WalletStorage, public interfaces::Chain::Notificati * Override with -fallbackfee */ CFeeRate m_fallback_fee{DEFAULT_FALLBACK_FEE}; + + /** If the cost to spend a change output at this feerate is greater than the value of the + * output itself, just drop it to fees. */ CFeeRate m_discard_rate{DEFAULT_DISCARD_FEE}; + + /** The maximum fee amount we're willing to pay to prioritize partial spend avoidance. */ CAmount m_max_aps_fee{DEFAULT_MAX_AVOIDPARTIALSPEND_FEE}; //!< note: this is absolute fee, not fee rate OutputType m_default_address_type{DEFAULT_ADDRESS_TYPE}; /**