aboutsummaryrefslogtreecommitdiff
path: root/src/wallet/spend.cpp
diff options
context:
space:
mode:
authorjosibake <josibake@protonmail.com>2022-03-11 16:30:04 +0100
committerjosibake <josibake@protonmail.com>2022-07-19 15:30:57 +0200
commit2e67291ca3ab2d8f498fa910738ca655fde11c5e (patch)
treebfd63274c0a07242a4912638a6f966b60daa24f6 /src/wallet/spend.cpp
parent948f5ba6363fcc64f95fed3f04dbda3d50d61827 (diff)
refactor: store by OutputType in CoinsResult
Store COutputs by OutputType in CoinsResult. The struct stores vectors of `COutput`s by `OutputType` for more convenient access
Diffstat (limited to 'src/wallet/spend.cpp')
-rw-r--r--src/wallet/spend.cpp81
1 files changed, 76 insertions, 5 deletions
diff --git a/src/wallet/spend.cpp b/src/wallet/spend.cpp
index 9be29c4709..826d50834f 100644
--- a/src/wallet/spend.cpp
+++ b/src/wallet/spend.cpp
@@ -79,6 +79,32 @@ TxSize CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *walle
return CalculateMaximumSignedTxSize(tx, wallet, txouts, coin_control);
}
+uint64_t CoinsResult::size() const
+{
+ return bech32m.size() + bech32.size() + P2SH_segwit.size() + legacy.size() + other.size();
+}
+
+std::vector<COutput> CoinsResult::all() const
+{
+ std::vector<COutput> all;
+ all.reserve(this->size());
+ all.insert(all.end(), bech32m.begin(), bech32m.end());
+ all.insert(all.end(), bech32.begin(), bech32.end());
+ all.insert(all.end(), P2SH_segwit.begin(), P2SH_segwit.end());
+ all.insert(all.end(), legacy.begin(), legacy.end());
+ all.insert(all.end(), other.begin(), other.end());
+ return all;
+}
+
+void CoinsResult::clear()
+{
+ bech32m.clear();
+ bech32.clear();
+ P2SH_segwit.clear();
+ legacy.clear();
+ other.clear();
+}
+
CoinsResult AvailableCoins(const CWallet& wallet,
const CCoinControl* coinControl,
std::optional<CFeeRate> feerate,
@@ -193,10 +219,55 @@ CoinsResult AvailableCoins(const CWallet& wallet,
// Filter by spendable outputs only
if (!spendable && only_spendable) continue;
+ // When parsing a scriptPubKey, Solver returns the parsed pubkeys or hashes (depending on the script)
+ // We don't need those here, so we are leaving them in return_values_unused
+ std::vector<std::vector<uint8_t>> return_values_unused;
+ TxoutType type;
+ bool is_from_p2sh{false};
+
+ // If the Output is P2SH and spendable, we want to know if it is
+ // a P2SH (legacy) or one of P2SH-P2WPKH, P2SH-P2WSH (P2SH-Segwit). We can determine
+ // this from the redeemScript. If the Output is not spendable, it will be classified
+ // as a P2SH (legacy), since we have no way of knowing otherwise without the redeemScript
+ if (output.scriptPubKey.IsPayToScriptHash() && solvable) {
+ CScript redeemScript;
+ CTxDestination destination;
+ if (!ExtractDestination(output.scriptPubKey, destination))
+ continue;
+ const CScriptID& hash = CScriptID(std::get<ScriptHash>(destination));
+ if (!provider->GetCScript(hash, redeemScript))
+ continue;
+ type = Solver(redeemScript, return_values_unused);
+ is_from_p2sh = true;
+ } else {
+ type = Solver(output.scriptPubKey, return_values_unused);
+ }
+
int input_bytes = CalculateMaximumSignedInputSize(output, COutPoint(), provider.get(), coinControl);
- result.coins.emplace_back(outpoint, output, nDepth, input_bytes, spendable, solvable, safeTx, wtx.GetTxTime(), tx_from_me, feerate);
+ COutput coin(outpoint, output, nDepth, input_bytes, spendable, solvable, safeTx, wtx.GetTxTime(), tx_from_me, feerate);
+ switch (type) {
+ case TxoutType::WITNESS_UNKNOWN:
+ case TxoutType::WITNESS_V1_TAPROOT:
+ result.bech32m.push_back(coin);
+ break;
+ case TxoutType::WITNESS_V0_KEYHASH:
+ case TxoutType::WITNESS_V0_SCRIPTHASH:
+ if (is_from_p2sh) {
+ result.P2SH_segwit.push_back(coin);
+ break;
+ }
+ result.bech32.push_back(coin);
+ break;
+ case TxoutType::SCRIPTHASH:
+ case TxoutType::PUBKEYHASH:
+ result.legacy.push_back(coin);
+ break;
+ default:
+ result.other.push_back(coin);
+ };
+
+ // Cache total amount as we go
result.total_amount += output.nValue;
-
// Checks the sum amount of all UTXO's.
if (nMinimumSumAmount != MAX_MONEY) {
if (result.total_amount >= nMinimumSumAmount) {
@@ -205,7 +276,7 @@ CoinsResult AvailableCoins(const CWallet& wallet,
}
// Checks the maximum number of UTXO's.
- if (nMaximumCount > 0 && result.coins.size() >= nMaximumCount) {
+ if (nMaximumCount > 0 && result.size() >= nMaximumCount) {
return result;
}
}
@@ -261,7 +332,7 @@ std::map<CTxDestination, std::vector<COutput>> ListCoins(const CWallet& wallet)
std::map<CTxDestination, std::vector<COutput>> result;
- for (const COutput& coin : AvailableCoinsListUnspent(wallet).coins) {
+ for (const COutput& coin : AvailableCoinsListUnspent(wallet).all()) {
CTxDestination address;
if ((coin.spendable || (wallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) && coin.solvable)) &&
ExtractDestination(FindNonChangeParentOutput(wallet, coin.outpoint).scriptPubKey, address)) {
@@ -787,7 +858,7 @@ static BResult<CreatedTransactionResult> CreateTransactionInternal(
0); /*nMaximumCount*/
// Choose coins to use
- std::optional<SelectionResult> result = SelectCoins(wallet, res_available_coins.coins, /*nTargetValue=*/selection_target, coin_control, coin_selection_params);
+ std::optional<SelectionResult> result = SelectCoins(wallet, res_available_coins.all(), /*nTargetValue=*/selection_target, coin_control, coin_selection_params);
if (!result) {
return _("Insufficient funds");
}