aboutsummaryrefslogtreecommitdiff
path: root/src/wallet/spend.cpp
diff options
context:
space:
mode:
authorfanquake <fanquake@gmail.com>2022-03-24 20:22:51 +0000
committerfanquake <fanquake@gmail.com>2022-03-24 20:46:43 +0000
commit3740cdd12551fab2cc9ef12afff8680c34086343 (patch)
treec3356a68d8ba8f98c49c9d75f97f03cd799b2f67 /src/wallet/spend.cpp
parentf0c9ba2b48c3aabdf445076574be7eeb77fe2f48 (diff)
parent049003fe68a4183f6f20da16f58f10079d1e02df (diff)
Merge bitcoin/bitcoin#24091: wallet: Consolidate CInputCoin and COutput
049003fe68a4183f6f20da16f58f10079d1e02df coinselection: Remove COutput operators == and != (Andrew Chow) f6c39c6adb6cbf9c87f04d3d667701905ef5c0a0 coinselection: Remove CInputCoin (Andrew Chow) 70f31f1a81710aa59e95770de9a84bf58cbce1e8 coinselection: Use COutput instead of CInputCoin (Andrew Chow) 14fbb57b79c664090f6a4e60d7bdfc9759ff4307 coinselection: Add effective value and fees to COutput (Andrew Chow) f0821230b8de2eec21a869d1edf9e2b9f502de25 moveonly: move COutput to coinselection.h (Andrew Chow) 42e974e15c6deba1d9395a4da9341c9ebec6e8e5 wallet: Remove CWallet and CWalletTx from COutput's constructor (Andrew Chow) 14d04d5ad15ae56df56edee7ca9a202b52037889 wallet: Replace CWalletTx in COutput with COutPoint and CTxOut (Andrew Chow) 0ba4d1916e26e2a5d603edcdb7625463989d25b6 wallet: Provide input bytes to COutput (Andrew Chow) d51f27d3bb0d6e3ca55bcd23ce53e4fe413a9360 wallet: Store whether a COutput is from the wallet (Andrew Chow) b799814bbd53736b79495072f3c9e05989a465e8 wallet: Store tx time in COutput (Andrew Chow) 46022953ee2e8113167bafd1fd48a383a578b13c wallet: Remove use_max_sig default value (Andrew Chow) 10379f007fd2c18f4cd24d0a0783d6d929f45556 scripted-diff: Rename COutput member variables (Andrew Chow) c7c64db41e1718584aa2f30ff27f60ab0966de62 wallet: cleanup COutput constructor (Andrew Chow) Pull request description: While working on coin selection code, it occurred to me that `CInputCoin` is really a subset of `COutput` and the conversion of a `COutput` to a `CInputCoin` does not appear to be all that useful. So this PR adds fields that are present in `CInputCoin` to `COutput` and replaces the usage of `CInputCoin` with `COutput`. `COutput` is also moved to coinselection.h. As part of this move, the usage of `CWalletTx` is removed from `COutput`. It is instead replaced by storing a `COutPoint` and the `CTxOut` rather than the entire `CWalletTx` as coin selection does not really need the full `CWalletTx`. The `CWalletTx` was only used for figuring out whether the transaction containing the output was from the current wallet, and for the transaction's time. These are now parameters to `COutput`'s constructor. ACKs for top commit: ryanofsky: Code review ACK 049003fe68a4183f6f20da16f58f10079d1e02df, just adding comments and removing == operators since last review w0xlt: reACK 049003f Xekyo: reACK 049003fe68a4183f6f20da16f58f10079d1e02df Tree-SHA512: 048b4cd620a0415e1d9fe8597257ee4bc64656566e1d28a9bdd147d6d72dc87c3f34a3339fa9ab6acf42c388df7901fc4ee900ccaabc3de790ffad162b544c15
Diffstat (limited to 'src/wallet/spend.cpp')
-rw-r--r--src/wallet/spend.cpp89
1 files changed, 47 insertions, 42 deletions
diff --git a/src/wallet/spend.cpp b/src/wallet/spend.cpp
index 931baeac90..304a9f10ca 100644
--- a/src/wallet/spend.cpp
+++ b/src/wallet/spend.cpp
@@ -29,11 +29,6 @@ int GetTxSpendSize(const CWallet& wallet, const CWalletTx& wtx, unsigned int out
return CalculateMaximumSignedInputSize(wtx.tx->vout[out], &wallet, use_max_sig);
}
-std::string COutput::ToString() const
-{
- return strprintf("COutput(%s, %d, %d) [%s]", tx->GetHash().ToString(), i, nDepth, FormatMoney(tx->tx->vout[i].nValue));
-}
-
int CalculateMaximumSignedInputSize(const CTxOut& txout, const SigningProvider* provider, bool use_max_sig)
{
CMutableTransaction txn;
@@ -158,6 +153,8 @@ void AvailableCoins(const CWallet& wallet, std::vector<COutput>& vCoins, const C
continue;
}
+ bool tx_from_me = CachedTxIsFromMe(wallet, wtx, ISMINE_ALL);
+
for (unsigned int i = 0; i < wtx.tx->vout.size(); i++) {
// Only consider selected coins if add_inputs is false
if (coinControl && !coinControl->m_add_inputs && !coinControl->IsSelected(COutPoint(entry.first, i))) {
@@ -190,8 +187,9 @@ void AvailableCoins(const CWallet& wallet, std::vector<COutput>& vCoins, const C
bool solvable = provider ? IsSolvable(*provider, wtx.tx->vout[i].scriptPubKey) : false;
bool spendable = ((mine & ISMINE_SPENDABLE) != ISMINE_NO) || (((mine & ISMINE_WATCH_ONLY) != ISMINE_NO) && (coinControl && coinControl->fAllowWatchOnly && solvable));
+ int input_bytes = GetTxSpendSize(wallet, wtx, i, (coinControl && coinControl->fAllowWatchOnly));
- vCoins.push_back(COutput(wallet, wtx, i, nDepth, spendable, solvable, safeTx, (coinControl && coinControl->fAllowWatchOnly)));
+ vCoins.emplace_back(COutPoint(wtx.GetHash(), i), wtx.tx->vout.at(i), nDepth, input_bytes, spendable, solvable, safeTx, wtx.GetTxTime(), tx_from_me);
// Checks the sum amount of all UTXO's.
if (nMinimumSumAmount != MAX_MONEY) {
@@ -218,8 +216,8 @@ CAmount GetAvailableBalance(const CWallet& wallet, const CCoinControl* coinContr
std::vector<COutput> vCoins;
AvailableCoins(wallet, vCoins, coinControl);
for (const COutput& out : vCoins) {
- if (out.fSpendable) {
- balance += out.tx->tx->vout[out.i].nValue;
+ if (out.spendable) {
+ balance += out.txout.nValue;
}
}
return balance;
@@ -243,6 +241,12 @@ const CTxOut& FindNonChangeParentOutput(const CWallet& wallet, const CTransactio
return ptx->vout[n];
}
+const CTxOut& FindNonChangeParentOutput(const CWallet& wallet, const COutPoint& outpoint)
+{
+ AssertLockHeld(wallet.cs_wallet);
+ return FindNonChangeParentOutput(wallet, *wallet.GetWalletTx(outpoint.hash)->tx, outpoint.n);
+}
+
std::map<CTxDestination, std::vector<COutput>> ListCoins(const CWallet& wallet)
{
AssertLockHeld(wallet.cs_wallet);
@@ -254,8 +258,8 @@ std::map<CTxDestination, std::vector<COutput>> ListCoins(const CWallet& wallet)
for (const COutput& coin : availableCoins) {
CTxDestination address;
- if ((coin.fSpendable || (wallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) && coin.fSolvable)) &&
- ExtractDestination(FindNonChangeParentOutput(wallet, *coin.tx->tx, coin.i).scriptPubKey, address)) {
+ if ((coin.spendable || (wallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) && coin.solvable)) &&
+ ExtractDestination(FindNonChangeParentOutput(wallet, coin.outpoint).scriptPubKey, address)) {
result[address].emplace_back(std::move(coin));
}
}
@@ -268,14 +272,15 @@ std::map<CTxDestination, std::vector<COutput>> ListCoins(const CWallet& wallet)
for (const COutPoint& output : lockedCoins) {
auto it = wallet.mapWallet.find(output.hash);
if (it != wallet.mapWallet.end()) {
- int depth = wallet.GetTxDepthInMainChain(it->second);
- if (depth >= 0 && output.n < it->second.tx->vout.size() &&
- wallet.IsMine(it->second.tx->vout[output.n]) == is_mine_filter
+ const auto& wtx = it->second;
+ int depth = wallet.GetTxDepthInMainChain(wtx);
+ if (depth >= 0 && output.n < wtx.tx->vout.size() &&
+ wallet.IsMine(wtx.tx->vout[output.n]) == is_mine_filter
) {
CTxDestination address;
- if (ExtractDestination(FindNonChangeParentOutput(wallet, *it->second.tx, output.n).scriptPubKey, address)) {
+ if (ExtractDestination(FindNonChangeParentOutput(wallet, *wtx.tx, output.n).scriptPubKey, address)) {
result[address].emplace_back(
- wallet, it->second, output.n, depth, true /* spendable */, true /* solvable */, false /* safe */);
+ COutPoint(wtx.GetHash(), output.n), wtx.tx->vout.at(output.n), depth, GetTxSpendSize(wallet, wtx, output.n), /*spendable=*/ true, /*solvable=*/ true, /*safe=*/ false, wtx.GetTxTime(), CachedTxIsFromMe(wallet, wtx, ISMINE_ALL));
}
}
}
@@ -292,15 +297,14 @@ std::vector<OutputGroup> GroupOutputs(const CWallet& wallet, const std::vector<C
// Allowing partial spends means no grouping. Each COutput gets its own OutputGroup.
for (const COutput& output : outputs) {
// Skip outputs we cannot spend
- if (!output.fSpendable) continue;
+ if (!output.spendable) continue;
size_t ancestors, descendants;
- wallet.chain().getTransactionAncestry(output.tx->GetHash(), ancestors, descendants);
- CInputCoin input_coin = output.GetInputCoin();
+ wallet.chain().getTransactionAncestry(output.outpoint.hash, ancestors, descendants);
// Make an OutputGroup containing just this output
OutputGroup group{coin_sel_params};
- group.Insert(input_coin, output.nDepth, CachedTxIsFromMe(wallet, *output.tx, ISMINE_ALL), ancestors, descendants, positive_only);
+ group.Insert(output, ancestors, descendants, positive_only);
// Check the OutputGroup's eligibility. Only add the eligible ones.
if (positive_only && group.GetSelectionAmount() <= 0) continue;
@@ -312,18 +316,17 @@ std::vector<OutputGroup> GroupOutputs(const CWallet& wallet, const std::vector<C
// We want to combine COutputs that have the same scriptPubKey into single OutputGroups
// except when there are more than OUTPUT_GROUP_MAX_ENTRIES COutputs grouped in an OutputGroup.
// To do this, we maintain a map where the key is the scriptPubKey and the value is a vector of OutputGroups.
- // For each COutput, we check if the scriptPubKey is in the map, and if it is, the COutput's CInputCoin is added
+ // For each COutput, we check if the scriptPubKey is in the map, and if it is, the COutput is added
// to the last OutputGroup in the vector for the scriptPubKey. When the last OutputGroup has
- // OUTPUT_GROUP_MAX_ENTRIES CInputCoins, a new OutputGroup is added to the end of the vector.
+ // OUTPUT_GROUP_MAX_ENTRIES COutputs, a new OutputGroup is added to the end of the vector.
std::map<CScript, std::vector<OutputGroup>> spk_to_groups_map;
for (const auto& output : outputs) {
// Skip outputs we cannot spend
- if (!output.fSpendable) continue;
+ if (!output.spendable) continue;
size_t ancestors, descendants;
- wallet.chain().getTransactionAncestry(output.tx->GetHash(), ancestors, descendants);
- CInputCoin input_coin = output.GetInputCoin();
- CScript spk = input_coin.txout.scriptPubKey;
+ wallet.chain().getTransactionAncestry(output.outpoint.hash, ancestors, descendants);
+ CScript spk = output.txout.scriptPubKey;
std::vector<OutputGroup>& groups = spk_to_groups_map[spk];
@@ -332,7 +335,7 @@ std::vector<OutputGroup> GroupOutputs(const CWallet& wallet, const std::vector<C
groups.emplace_back(coin_sel_params);
}
- // Get the last OutputGroup in the vector so that we can add the CInputCoin to it
+ // Get the last OutputGroup in the vector so that we can add the COutput to it
// A pointer is used here so that group can be reassigned later if it is full.
OutputGroup* group = &groups.back();
@@ -344,8 +347,8 @@ std::vector<OutputGroup> GroupOutputs(const CWallet& wallet, const std::vector<C
group = &groups.back();
}
- // Add the input_coin to group
- group->Insert(input_coin, output.nDepth, CachedTxIsFromMe(wallet, *output.tx, ISMINE_ALL), ancestors, descendants, positive_only);
+ // Add the output to group
+ group->Insert(output, ancestors, descendants, positive_only);
}
// Now we go through the entire map and pull out the OutputGroups
@@ -421,11 +424,11 @@ std::optional<SelectionResult> SelectCoins(const CWallet& wallet, const std::vec
if (coin_control.HasSelected() && !coin_control.fAllowOtherInputs)
{
for (const COutput& out : vCoins) {
- if (!out.fSpendable) continue;
- /* Set depth, from_me, ancestors, and descendants to 0 or false as these don't matter for preset inputs as no actual selection is being done.
+ if (!out.spendable) continue;
+ /* Set ancestors and descendants to 0 as these don't matter for preset inputs as no actual selection is being done.
* positive_only is set to false because we want to include all preset inputs, even if they are dust.
*/
- preset_inputs.Insert(out.GetInputCoin(), 0, false, 0, 0, false);
+ preset_inputs.Insert(out, /*ancestors=*/ 0, /*descendants=*/ 0, /*positive_only=*/ false);
}
SelectionResult result(nTargetValue);
result.AddInput(preset_inputs);
@@ -434,7 +437,7 @@ std::optional<SelectionResult> SelectCoins(const CWallet& wallet, const std::vec
}
// calculate value from preset inputs and store them
- std::set<CInputCoin> setPresetCoins;
+ std::set<COutPoint> preset_coins;
std::vector<COutPoint> vPresetInputs;
coin_control.ListSelected(vPresetInputs);
@@ -462,27 +465,29 @@ std::optional<SelectionResult> SelectCoins(const CWallet& wallet, const std::vec
input_bytes = GetVirtualTransactionSize(coin_control.GetInputWeight(outpoint), 0, 0);
}
- CInputCoin coin(outpoint, txout, input_bytes);
- if (coin.m_input_bytes == -1) {
+ if (input_bytes == -1) {
return std::nullopt; // Not solvable, can't estimate size for fee
}
- coin.effective_value = coin.txout.nValue - coin_selection_params.m_effective_feerate.GetFee(coin.m_input_bytes);
+
+ /* Set some defaults for depth, spendable, solvable, safe, time, and from_me as these don't matter for preset inputs since no selection is being done. */
+ COutput output(outpoint, txout, /*depth=*/ 0, input_bytes, /*spendable=*/ true, /*solvable=*/ true, /*safe=*/ true, /*time=*/ 0, /*from_me=*/ false);
+ output.effective_value = output.txout.nValue - coin_selection_params.m_effective_feerate.GetFee(output.input_bytes);
if (coin_selection_params.m_subtract_fee_outputs) {
- value_to_select -= coin.txout.nValue;
+ value_to_select -= output.txout.nValue;
} else {
- value_to_select -= coin.effective_value;
+ value_to_select -= output.effective_value;
}
- setPresetCoins.insert(coin);
- /* Set depth, from_me, ancestors, and descendants to 0 or false as don't matter for preset inputs as no actual selection is being done.
+ preset_coins.insert(outpoint);
+ /* Set ancestors and descendants to 0 as they don't matter for preset inputs since no actual selection is being done.
* positive_only is set to false because we want to include all preset inputs, even if they are dust.
*/
- preset_inputs.Insert(coin, 0, false, 0, 0, false);
+ preset_inputs.Insert(output, /*ancestors=*/ 0, /*descendants=*/ 0, /*positive_only=*/ false);
}
// remove preset inputs from vCoins so that Coin Selection doesn't pick them.
for (std::vector<COutput>::iterator it = vCoins.begin(); it != vCoins.end() && coin_control.HasSelected();)
{
- if (setPresetCoins.count(it->GetInputCoin()))
+ if (preset_coins.count(it->outpoint))
it = vCoins.erase(it);
else
++it;
@@ -798,7 +803,7 @@ static bool CreateTransactionInternal(
auto change_position = txNew.vout.insert(txNew.vout.begin() + nChangePosInOut, newTxOut);
// Shuffle selected coins and fill in final vin
- std::vector<CInputCoin> selected_coins = result->GetShuffledInputVector();
+ std::vector<COutput> selected_coins = result->GetShuffledInputVector();
// The sequence number is set to non-maxint so that DiscourageFeeSniping
// works.