// Copyright (c) 2021-2022 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef BITCOIN_WALLET_SPEND_H #define BITCOIN_WALLET_SPEND_H #include #include // for FeeCalculation #include #include #include #include #include namespace wallet { /** Get the marginal bytes if spending the specified output from this transaction. * Use CoinControl to determine whether to expect signature grinding when calculating the size of the input spend. */ int CalculateMaximumSignedInputSize(const CTxOut& txout, const CWallet* pwallet, const CCoinControl* coin_control); int CalculateMaximumSignedInputSize(const CTxOut& txout, const COutPoint outpoint, const SigningProvider* pwallet, bool can_grind_r, const CCoinControl* coin_control); struct TxSize { int64_t vsize{-1}; int64_t weight{-1}; }; /** Calculate the size of the transaction using CoinControl to determine * whether to expect signature grinding when calculating the size of the input spend. */ TxSize CalculateMaximumSignedTxSize(const CTransaction& tx, const CWallet* wallet, const std::vector& txouts, const CCoinControl* coin_control = nullptr); TxSize CalculateMaximumSignedTxSize(const CTransaction& tx, const CWallet* wallet, const CCoinControl* coin_control = nullptr) EXCLUSIVE_LOCKS_REQUIRED(wallet->cs_wallet); /** * COutputs available for spending, stored by OutputType. * This struct is really just a wrapper around OutputType vectors with a convenient * method for concatenating and returning all COutputs as one vector. * * Size(), Clear(), Erase(), Shuffle(), and Add() methods are implemented to * allow easy interaction with the struct. */ struct CoinsResult { std::map> coins; /** Concatenate and return all COutputs as one vector */ std::vector All() const; /** The following methods are provided so that CoinsResult can mimic a vector, * i.e., methods can work with individual OutputType vectors or on the entire object */ size_t Size() const; /** Return how many different output types this struct stores */ size_t TypesCount() const { return coins.size(); } void Clear(); void Erase(const std::unordered_set& coins_to_remove); void Shuffle(FastRandomContext& rng_fast); void Add(OutputType type, const COutput& out); CAmount GetTotalAmount() { return total_amount; } std::optional GetEffectiveTotalAmount() {return total_effective_amount; } private: /** Sum of all available coins raw value */ CAmount total_amount{0}; /** Sum of all available coins effective value (each output value minus fees required to spend it) */ std::optional total_effective_amount{0}; }; struct CoinFilterParams { // Outputs below the minimum amount will not get selected CAmount min_amount{1}; // Outputs above the maximum amount will not get selected CAmount max_amount{MAX_MONEY}; // Return outputs until the minimum sum amount is covered CAmount min_sum_amount{MAX_MONEY}; // Maximum number of outputs that can be returned uint64_t max_count{0}; // By default, return only spendable outputs bool only_spendable{true}; // By default, do not include immature coinbase outputs bool include_immature_coinbase{false}; // By default, skip locked UTXOs bool skip_locked{true}; }; /** * Populate the CoinsResult struct with vectors of available COutputs, organized by OutputType. */ CoinsResult AvailableCoins(const CWallet& wallet, const CCoinControl* coinControl = nullptr, std::optional feerate = std::nullopt, const CoinFilterParams& params = {}) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet); /** * Wrapper function for AvailableCoins which skips the `feerate` and `CoinFilterParams::only_spendable` parameters. Use this function * to list all available coins (e.g. listunspent RPC) while not intending to fund a transaction. */ CoinsResult AvailableCoinsListUnspent(const CWallet& wallet, const CCoinControl* coinControl = nullptr, CoinFilterParams params = {}) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet); CAmount GetAvailableBalance(const CWallet& wallet, const CCoinControl* coinControl = nullptr); /** * Find non-change parent output. */ const CTxOut& FindNonChangeParentOutput(const CWallet& wallet, const COutPoint& outpoint) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet); /** * Return list of available coins and locked coins grouped by non-change output address. */ std::map> ListCoins(const CWallet& wallet) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet); struct SelectionFilter { CoinEligibilityFilter filter; bool allow_mixed_output_types{true}; }; /** * Group coins by the provided filters. */ FilteredOutputGroups GroupOutputs(const CWallet& wallet, const CoinsResult& coins, const CoinSelectionParams& coin_sel_params, const std::vector& filters); /** * Attempt to find a valid input set that preserves privacy by not mixing OutputTypes. * `ChooseSelectionResult()` will be called on each OutputType individually and the best * the solution (according to the waste metric) will be chosen. If a valid input cannot be found from any * single OutputType, fallback to running `ChooseSelectionResult()` over all available coins. * * param@[in] nTargetValue The target value * param@[in] groups The grouped outputs mapped by coin eligibility filters * param@[in] coin_selection_params Parameters for the coin selection * param@[in] allow_mixed_output_types Relax restriction that SelectionResults must be of the same OutputType * returns If successful, a SelectionResult containing the input set * If failed, returns (1) an empty error message if the target was not reached (general "Insufficient funds") * or (2) an specific error message if there was something particularly wrong (e.g. a selection * result that surpassed the tx max weight size). */ util::Result AttemptSelection(const CAmount& nTargetValue, OutputGroupTypeMap& groups, const CoinSelectionParams& coin_selection_params, bool allow_mixed_output_types); /** * Attempt to find a valid input set that meets the provided eligibility filter and target. * Multiple coin selection algorithms will be run and the input set that produces the least waste * (according to the waste metric) will be chosen. * * param@[in] nTargetValue The target value * param@[in] groups The struct containing the outputs grouped by script and divided by (1) positive only outputs and (2) all outputs (positive + negative). * param@[in] coin_selection_params Parameters for the coin selection * returns If successful, a SelectionResult containing the input set * If failed, returns (1) an empty error message if the target was not reached (general "Insufficient funds") * or (2) an specific error message if there was something particularly wrong (e.g. a selection * result that surpassed the tx max weight size). */ util::Result ChooseSelectionResult(const CAmount& nTargetValue, Groups& groups, const CoinSelectionParams& coin_selection_params); // User manually selected inputs that must be part of the transaction struct PreSelectedInputs { std::set> coins; // If subtract fee from outputs is disabled, the 'total_amount' // will be the sum of each output effective value // instead of the sum of the outputs amount CAmount total_amount{0}; void Insert(const COutput& output, bool subtract_fee_outputs) { if (subtract_fee_outputs) { total_amount += output.txout.nValue; } else { total_amount += output.GetEffectiveValue(); } coins.insert(std::make_shared(output)); } }; /** * Fetch and validate coin control selected inputs. * Coins could be internal (from the wallet) or external. */ util::Result FetchSelectedInputs(const CWallet& wallet, const CCoinControl& coin_control, const CoinSelectionParams& coin_selection_params) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet); /** * Select a set of coins such that nTargetValue is met; never select unconfirmed coins if they are not ours * param@[in] wallet The wallet which provides data necessary to spend the selected coins * param@[in] available_coins The struct of coins, organized by OutputType, available for selection prior to filtering * param@[in] nTargetValue The target value * param@[in] coin_selection_params Parameters for this coin selection such as feerates, whether to avoid partial spends, * and whether to subtract the fee from the outputs. * returns If successful, a SelectionResult containing the selected coins * If failed, returns (1) an empty error message if the target was not reached (general "Insufficient funds") * or (2) an specific error message if there was something particularly wrong (e.g. a selection * result that surpassed the tx max weight size). */ util::Result AutomaticCoinSelection(const CWallet& wallet, CoinsResult& available_coins, const CAmount& nTargetValue, const CCoinControl& coin_control, const CoinSelectionParams& coin_selection_params) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet); /** * Select all coins from coin_control, and if coin_control 'm_allow_other_inputs=true', call 'AutomaticCoinSelection' to * select a set of coins such that nTargetValue - pre_set_inputs.total_amount is met. */ util::Result SelectCoins(const CWallet& wallet, CoinsResult& available_coins, const PreSelectedInputs& pre_set_inputs, const CAmount& nTargetValue, const CCoinControl& coin_control, const CoinSelectionParams& coin_selection_params) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet); struct CreatedTransactionResult { CTransactionRef tx; CAmount fee; FeeCalculation fee_calc; int change_pos; CreatedTransactionResult(CTransactionRef _tx, CAmount _fee, int _change_pos, const FeeCalculation& _fee_calc) : tx(_tx), fee(_fee), fee_calc(_fee_calc), change_pos(_change_pos) {} }; /** * Create a new transaction paying the recipients with a set of coins * selected by SelectCoins(); Also create the change output, when needed * @note passing change_pos as -1 will result in setting a random position */ util::Result CreateTransaction(CWallet& wallet, const std::vector& vecSend, int change_pos, const CCoinControl& coin_control, bool sign = true); /** * Insert additional inputs into the transaction by * calling CreateTransaction(); */ bool FundTransaction(CWallet& wallet, CMutableTransaction& tx, CAmount& nFeeRet, int& nChangePosInOut, bilingual_str& error, bool lockUnspents, const std::set& setSubtractFeeFromOutputs, CCoinControl); } // namespace wallet #endif // BITCOIN_WALLET_SPEND_H