0
0
Fork 0
mirror of https://github.com/bitcoin/bitcoin.git synced 2025-02-04 10:07:27 -05:00
bitcoin-bitcoin-core/src/wallet/spend.h
furszy dc1cc1c359
gui: bugfix, getAvailableBalance skips selected coins
The previous behavior for getAvailableBalance when coin control
has selected coins was to return the sum of them. Instead, we
are currently returning the wallet's available total balance minus
the selected coins total amount.

This turns into a GUI-only issue for the "use available balance"
button when the user manually select coins in the send screen.

Reason:
We missed to update the GetAvailableBalance function to include
the coin control selected coins on #25685.

Context:
Since #25685 we skip the selected coins inside `AvailableCoins`,
the reason is that there is no need to traverse the wallet's
txes map just to get coins that can directly be fetched by
their id.
2023-04-03 17:23:42 -03:00

228 lines
12 KiB
C++

// 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 <consensus/amount.h>
#include <policy/fees.h> // for FeeCalculation
#include <util/result.h>
#include <wallet/coinselection.h>
#include <wallet/transaction.h>
#include <wallet/wallet.h>
#include <optional>
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<CTxOut>& 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<OutputType, std::vector<COutput>> coins;
/** Concatenate and return all COutputs as one vector */
std::vector<COutput> 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<COutPoint, SaltedOutpointHasher>& coins_to_remove);
void Shuffle(FastRandomContext& rng_fast);
void Add(OutputType type, const COutput& out);
CAmount GetTotalAmount() { return total_amount; }
std::optional<CAmount> 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<CAmount> 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<CFeeRate> 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);
/**
* 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<CTxDestination, std::vector<COutput>> 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<SelectionFilter>& 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<SelectionResult> 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<SelectionResult> 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<std::shared_ptr<COutput>> 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<COutput>(output));
}
};
/**
* Fetch and validate coin control selected inputs.
* Coins could be internal (from the wallet) or external.
*/
util::Result<PreSelectedInputs> 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<SelectionResult> AutomaticCoinSelection(const CWallet& wallet, CoinsResult& available_coins, const CAmount& nTargetValue,
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<SelectionResult> 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<CreatedTransactionResult> CreateTransaction(CWallet& wallet, const std::vector<CRecipient>& 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<int>& setSubtractFeeFromOutputs, CCoinControl);
} // namespace wallet
#endif // BITCOIN_WALLET_SPEND_H