1 // Copyright (c) 2021-2022 The Limenka developers
2 // Distributed under the MIT software license, see the accompanying
3 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4 5 #ifndef LIMENKA_WALLET_SPEND_H
6 #define LIMENKA_WALLET_SPEND_H
7 8 #include <consensus/amount.h>
9 #include <policy/fees.h> // for FeeCalculation
10 #include <util/result.h>
11 #include <wallet/coinselection.h>
12 #include <wallet/transaction.h>
13 #include <wallet/wallet.h>
14 15 #include <optional>
16 17 namespace wallet {
18 /** Get the marginal bytes if spending the specified output from this transaction.
19 * Use CoinControl to determine whether to expect signature grinding when calculating the size of the input spend. */
20 int CalculateMaximumSignedInputSize(const CTxOut& txout, const CWallet* pwallet, const CCoinControl* coin_control);
21 int CalculateMaximumSignedInputSize(const CTxOut& txout, const COutPoint outpoint, const SigningProvider* pwallet, bool can_grind_r, const CCoinControl* coin_control);
22 struct TxSize {
23 int64_t vsize{-1};
24 int64_t weight{-1};
25 };
26 27 /** Calculate the size of the transaction using CoinControl to determine
28 * whether to expect signature grinding when calculating the size of the input spend. */
29 TxSize CalculateMaximumSignedTxSize(const CTransaction& tx, const CWallet* wallet, const std::vector<CTxOut>& txouts, const CCoinControl* coin_control = nullptr);
30 TxSize CalculateMaximumSignedTxSize(const CTransaction& tx, const CWallet* wallet, const CCoinControl* coin_control = nullptr) EXCLUSIVE_LOCKS_REQUIRED(wallet->cs_wallet);
31 32 /**
33 * COutputs available for spending, stored by OutputType.
34 * This struct is really just a wrapper around OutputType vectors with a convenient
35 * method for concatenating and returning all COutputs as one vector.
36 *
37 * Size(), Clear(), Erase(), Shuffle(), and Add() methods are implemented to
38 * allow easy interaction with the struct.
39 */
40 struct CoinsResult {
41 std::map<OutputType, std::vector<COutput>> coins;
42 43 /** Concatenate and return all COutputs as one vector */
44 std::vector<COutput> All() const;
45 46 /** The following methods are provided so that CoinsResult can mimic a vector,
47 * i.e., methods can work with individual OutputType vectors or on the entire object */
48 size_t Size() const;
49 /** Return how many different output types this struct stores */
50 size_t TypesCount() const { return coins.size(); }
51 void Clear();
52 void Erase(const std::unordered_set<COutPoint, SaltedOutpointHasher>& coins_to_remove);
53 void Shuffle(FastRandomContext& rng_fast);
54 void Add(OutputType type, const COutput& out);
55 56 CAmount GetTotalAmount() { return total_amount; }
57 std::optional<CAmount> GetEffectiveTotalAmount() {return total_effective_amount; }
58 59 private:
60 /** Sum of all available coins raw value */
61 CAmount total_amount{0};
62 /** Sum of all available coins effective value (each output value minus fees required to spend it) */
63 std::optional<CAmount> total_effective_amount{0};
64 };
65 66 struct CoinFilterParams {
67 // Outputs below the minimum amount will not get selected
68 CAmount min_amount{1};
69 // Outputs above the maximum amount will not get selected
70 CAmount max_amount{MAX_MONEY};
71 // Return outputs until the minimum sum amount is covered
72 CAmount min_sum_amount{MAX_MONEY};
73 // Maximum number of outputs that can be returned
74 uint64_t max_count{0};
75 // By default, return only spendable outputs
76 bool only_spendable{true};
77 // By default, do not include immature coinbase outputs
78 bool include_immature_coinbase{false};
79 // By default, skip locked UTXOs
80 bool skip_locked{true};
81 };
82 83 /**
84 * Populate the CoinsResult struct with vectors of available COutputs, organized by OutputType.
85 */
86 CoinsResult AvailableCoins(const CWallet& wallet,
87 const CCoinControl* coinControl = nullptr,
88 std::optional<CFeeRate> feerate = std::nullopt,
89 const CoinFilterParams& params = {}) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet);
90 91 /**
92 * Wrapper function for AvailableCoins which skips the `feerate` and `CoinFilterParams::only_spendable` parameters. Use this function
93 * to list all available coins (e.g. listunspent RPC) while not intending to fund a transaction.
94 */
95 CoinsResult AvailableCoinsListUnspent(const CWallet& wallet, const CCoinControl* coinControl = nullptr, CoinFilterParams params = {}) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet);
96 97 /**
98 * Find non-change parent output.
99 */
100 const CTxOut& FindNonChangeParentOutput(const CWallet& wallet, const COutPoint& outpoint) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet);
101 102 /**
103 * Return list of available coins and locked coins grouped by non-change output address.
104 */
105 std::map<CTxDestination, std::vector<COutput>> ListCoins(const CWallet& wallet) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet);
106 107 void MaybeDiscourageFeeSniping2(const CWallet &wallet,
108 CMutableTransaction& tx);
109 110 struct SelectionFilter {
111 CoinEligibilityFilter filter;
112 bool allow_mixed_output_types{true};
113 };
114 115 /**
116 * Group coins by the provided filters.
117 */
118 FilteredOutputGroups GroupOutputs(const CWallet& wallet,
119 const CoinsResult& coins,
120 const CoinSelectionParams& coin_sel_params,
121 const std::vector<SelectionFilter>& filters);
122 123 /**
124 * Attempt to find a valid input set that preserves privacy by not mixing OutputTypes.
125 * `ChooseSelectionResult()` will be called on each OutputType individually and the best
126 * the solution (according to the waste metric) will be chosen. If a valid input cannot be found from any
127 * single OutputType, fallback to running `ChooseSelectionResult()` over all available coins.
128 *
129 * @param[in] chain The chain interface to get information on bump fees for unconfirmed UTXOs
130 * @param[in] nTargetValue The target value
131 * @param[in] groups The grouped outputs mapped by coin eligibility filters
132 * @param[in] coin_selection_params Parameters for the coin selection
133 * @param[in] allow_mixed_output_types Relax restriction that SelectionResults must be of the same OutputType
134 * returns If successful, a SelectionResult containing the input set
135 * If failed, returns (1) an empty error message if the target was not reached (general "Insufficient funds")
136 * or (2) a specific error message if there was something particularly wrong (e.g. a selection
137 * result that surpassed the tx max weight size).
138 */
139 util::Result<SelectionResult> AttemptSelection(interfaces::Chain& chain, const CAmount& nTargetValue, OutputGroupTypeMap& groups,
140 const CoinSelectionParams& coin_selection_params, bool allow_mixed_output_types);
141 142 /**
143 * Attempt to find a valid input set that meets the provided eligibility filter and target.
144 * Multiple coin selection algorithms will be run and the input set that produces the least waste
145 * (according to the waste metric) will be chosen.
146 *
147 * @param[in] chain The chain interface to get information on bump fees for unconfirmed UTXOs
148 * @param[in] nTargetValue The target value
149 * @param[in] groups The struct containing the outputs grouped by script and divided by (1) positive only outputs and (2) all outputs (positive + negative).
150 * @param[in] coin_selection_params Parameters for the coin selection
151 * returns If successful, a SelectionResult containing the input set
152 * If failed, returns (1) an empty error message if the target was not reached (general "Insufficient funds")
153 * or (2) a specific error message if there was something particularly wrong (e.g. a selection
154 * result that surpassed the tx max weight size).
155 */
156 util::Result<SelectionResult> ChooseSelectionResult(interfaces::Chain& chain, const CAmount& nTargetValue, Groups& groups, const CoinSelectionParams& coin_selection_params);
157 158 // User manually selected inputs that must be part of the transaction
159 struct PreSelectedInputs
160 {
161 std::set<std::shared_ptr<COutput>> coins;
162 // If subtract fee from outputs is disabled, the 'total_amount'
163 // will be the sum of each output effective value
164 // instead of the sum of the outputs amount
165 CAmount total_amount{0};
166 167 void Insert(const COutput& output, bool subtract_fee_outputs)
168 {
169 if (subtract_fee_outputs) {
170 total_amount += output.txout.nValue;
171 } else {
172 total_amount += output.GetEffectiveValue();
173 }
174 coins.insert(std::make_shared<COutput>(output));
175 }
176 };
177 178 /**
179 * Fetch and validate coin control selected inputs.
180 * Coins could be internal (from the wallet) or external.
181 */
182 util::Result<PreSelectedInputs> FetchSelectedInputs(const CWallet& wallet, const CCoinControl& coin_control,
183 const CoinSelectionParams& coin_selection_params) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet);
184 185 /**
186 * Select a set of coins such that nTargetValue is met; never select unconfirmed coins if they are not ours
187 * @param[in] wallet The wallet which provides data necessary to spend the selected coins
188 * @param[in] available_coins The struct of coins, organized by OutputType, available for selection prior to filtering
189 * @param[in] nTargetValue The target value
190 * @param[in] coin_selection_params Parameters for this coin selection such as feerates, whether to avoid partial spends,
191 * and whether to subtract the fee from the outputs.
192 * returns If successful, a SelectionResult containing the selected coins
193 * If failed, returns (1) an empty error message if the target was not reached (general "Insufficient funds")
194 * or (2) an specific error message if there was something particularly wrong (e.g. a selection
195 * result that surpassed the tx max weight size).
196 */
197 util::Result<SelectionResult> AutomaticCoinSelection(const CWallet& wallet, CoinsResult& available_coins, const CAmount& nTargetValue,
198 const CoinSelectionParams& coin_selection_params) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet);
199 200 /**
201 * Select all coins from coin_control, and if coin_control 'm_allow_other_inputs=true', call 'AutomaticCoinSelection' to
202 * select a set of coins such that nTargetValue - pre_set_inputs.total_amount is met.
203 */
204 util::Result<SelectionResult> SelectCoins(const CWallet& wallet, CoinsResult& available_coins, const PreSelectedInputs& pre_set_inputs,
205 const CAmount& nTargetValue, const CCoinControl& coin_control,
206 const CoinSelectionParams& coin_selection_params) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet);
207 208 struct CreatedTransactionResult
209 {
210 CTransactionRef tx;
211 CAmount fee;
212 FeeCalculation fee_calc;
213 std::optional<unsigned int> change_pos;
214 215 CreatedTransactionResult(CTransactionRef _tx, CAmount _fee, std::optional<unsigned int> _change_pos, const FeeCalculation& _fee_calc)
216 : tx(_tx), fee(_fee), fee_calc(_fee_calc), change_pos(_change_pos) {}
217 };
218 219 /**
220 * Create a new transaction paying the recipients with a set of coins
221 * selected by SelectCoins(); Also create the change output, when needed
222 * @note passing change_pos as std::nullopt will result in setting a random position
223 */
224 util::Result<CreatedTransactionResult> CreateTransaction(CWallet& wallet, const std::vector<CRecipient>& vecSend, std::optional<unsigned int> change_pos, const CCoinControl& coin_control, bool sign = true);
225 226 /**
227 * Insert additional inputs into the transaction by
228 * calling CreateTransaction();
229 */
230 util::Result<CreatedTransactionResult> FundTransaction(CWallet& wallet, const CMutableTransaction& tx, const std::vector<CRecipient>& recipients, std::optional<unsigned int> change_pos, bool lockUnspents, CCoinControl);
231 } // namespace wallet
232 233 #endif // LIMENKA_WALLET_SPEND_H
234