aboutsummaryrefslogtreecommitdiff
path: root/src/wallet/wallet.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/wallet/wallet.cpp')
-rw-r--r--src/wallet/wallet.cpp115
1 files changed, 65 insertions, 50 deletions
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index d38f943558..3ec6aefaec 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -2415,32 +2415,14 @@ const CTxOut& CWallet::FindNonChangeParentOutput(const CTransaction& tx, int out
return ptx->vout[n];
}
-bool CWallet::OutputEligibleForSpending(const COutput& output, const CoinEligibilityFilter& eligibility_filter) const
-{
- if (!output.fSpendable)
- return false;
-
- if (output.nDepth < (output.tx->IsFromMe(ISMINE_ALL) ? eligibility_filter.conf_mine : eligibility_filter.conf_theirs))
- return false;
-
- size_t ancestors, descendants;
- mempool.GetTransactionAncestry(output.tx->GetHash(), ancestors, descendants);
- if (ancestors > eligibility_filter.max_ancestors || descendants > eligibility_filter.max_descendants) {
- return false;
- }
-
- return true;
-}
-
-bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, const CoinEligibilityFilter& eligibility_filter, std::vector<COutput> vCoins,
+bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, const CoinEligibilityFilter& eligibility_filter, std::vector<OutputGroup> groups,
std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet, const CoinSelectionParams& coin_selection_params, bool& bnb_used) const
{
setCoinsRet.clear();
nValueRet = 0;
- std::vector<CInputCoin> utxo_pool;
+ std::vector<OutputGroup> utxo_pool;
if (coin_selection_params.use_bnb) {
-
// Get long term estimate
FeeCalculation feeCalc;
CCoinControl temp;
@@ -2451,19 +2433,26 @@ bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, const CoinEligibil
CAmount cost_of_change = GetDiscardRate(*this, ::feeEstimator).GetFee(coin_selection_params.change_spend_size) + coin_selection_params.effective_fee.GetFee(coin_selection_params.change_output_size);
// Filter by the min conf specs and add to utxo_pool and calculate effective value
- for (const COutput &output : vCoins)
- {
- if (!OutputEligibleForSpending(output, eligibility_filter))
- continue;
-
- CInputCoin coin(output.tx->tx, output.i);
- coin.effective_value = coin.txout.nValue - (output.nInputBytes < 0 ? 0 : coin_selection_params.effective_fee.GetFee(output.nInputBytes));
- // Only include outputs that are positive effective value (i.e. not dust)
- if (coin.effective_value > 0) {
- coin.fee = output.nInputBytes < 0 ? 0 : coin_selection_params.effective_fee.GetFee(output.nInputBytes);
- coin.long_term_fee = output.nInputBytes < 0 ? 0 : long_term_feerate.GetFee(output.nInputBytes);
- utxo_pool.push_back(coin);
+ for (OutputGroup& group : groups) {
+ if (!group.EligibleForSpending(eligibility_filter)) continue;
+
+ group.fee = 0;
+ group.long_term_fee = 0;
+ group.effective_value = 0;
+ for (auto it = group.m_outputs.begin(); it != group.m_outputs.end(); ) {
+ const CInputCoin& coin = *it;
+ CAmount effective_value = coin.txout.nValue - (coin.m_input_bytes < 0 ? 0 : coin_selection_params.effective_fee.GetFee(coin.m_input_bytes));
+ // Only include outputs that are positive effective value (i.e. not dust)
+ if (effective_value > 0) {
+ group.fee += coin.m_input_bytes < 0 ? 0 : coin_selection_params.effective_fee.GetFee(coin.m_input_bytes);
+ group.long_term_fee += coin.m_input_bytes < 0 ? 0 : long_term_feerate.GetFee(coin.m_input_bytes);
+ group.effective_value += effective_value;
+ ++it;
+ } else {
+ it = group.Discard(coin);
+ }
}
+ if (group.effective_value > 0) utxo_pool.push_back(group);
}
// Calculate the fees for things that aren't inputs
CAmount not_input_fees = coin_selection_params.effective_fee.GetFee(coin_selection_params.tx_noinputs_size);
@@ -2471,13 +2460,9 @@ bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, const CoinEligibil
return SelectCoinsBnB(utxo_pool, nTargetValue, cost_of_change, setCoinsRet, nValueRet, not_input_fees);
} else {
// Filter by the min conf specs and add to utxo_pool
- for (const COutput &output : vCoins)
- {
- if (!OutputEligibleForSpending(output, eligibility_filter))
- continue;
-
- CInputCoin coin = CInputCoin(output.tx->tx, output.i);
- utxo_pool.push_back(coin);
+ for (const OutputGroup& group : groups) {
+ if (!group.EligibleForSpending(eligibility_filter)) continue;
+ utxo_pool.push_back(group);
}
bnb_used = false;
return KnapsackSolver(nTargetValue, utxo_pool, setCoinsRet, nValueRet);
@@ -2499,7 +2484,7 @@ bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAm
if (!out.fSpendable)
continue;
nValueRet += out.tx->tx->vout[out.i].nValue;
- setCoinsRet.insert(CInputCoin(out.tx->tx, out.i));
+ setCoinsRet.insert(out.GetInputCoin());
}
return (nValueRet >= nTargetValue);
}
@@ -2533,27 +2518,31 @@ bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAm
// remove preset inputs from vCoins
for (std::vector<COutput>::iterator it = vCoins.begin(); it != vCoins.end() && coin_control.HasSelected();)
{
- if (setPresetCoins.count(CInputCoin(it->tx->tx, it->i)))
+ if (setPresetCoins.count(it->GetInputCoin()))
it = vCoins.erase(it);
else
++it;
}
+ // form groups from remaining coins; note that preset coins will not
+ // automatically have their associated (same address) coins included
+ std::vector<OutputGroup> groups = GroupOutputs(vCoins, !coin_control.m_avoid_partial_spends);
+
size_t max_ancestors = (size_t)std::max<int64_t>(1, gArgs.GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT));
size_t max_descendants = (size_t)std::max<int64_t>(1, gArgs.GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT));
bool fRejectLongChains = gArgs.GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS);
bool res = nTargetValue <= nValueFromPresetInputs ||
- SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, CoinEligibilityFilter(1, 6, 0), vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used) ||
- SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, CoinEligibilityFilter(1, 1, 0), vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used) ||
- (m_spend_zero_conf_change && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, CoinEligibilityFilter(0, 1, 2), vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)) ||
- (m_spend_zero_conf_change && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, CoinEligibilityFilter(0, 1, std::min((size_t)4, max_ancestors/3), std::min((size_t)4, max_descendants/3)), vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)) ||
- (m_spend_zero_conf_change && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, CoinEligibilityFilter(0, 1, max_ancestors/2, max_descendants/2), vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)) ||
- (m_spend_zero_conf_change && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, CoinEligibilityFilter(0, 1, max_ancestors-1, max_descendants-1), vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used)) ||
- (m_spend_zero_conf_change && !fRejectLongChains && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, CoinEligibilityFilter(0, 1, std::numeric_limits<uint64_t>::max()), vCoins, setCoinsRet, nValueRet, coin_selection_params, bnb_used));
+ SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, CoinEligibilityFilter(1, 6, 0), groups, setCoinsRet, nValueRet, coin_selection_params, bnb_used) ||
+ SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, CoinEligibilityFilter(1, 1, 0), groups, setCoinsRet, nValueRet, coin_selection_params, bnb_used) ||
+ (m_spend_zero_conf_change && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, CoinEligibilityFilter(0, 1, 2), groups, setCoinsRet, nValueRet, coin_selection_params, bnb_used)) ||
+ (m_spend_zero_conf_change && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, CoinEligibilityFilter(0, 1, std::min((size_t)4, max_ancestors/3), std::min((size_t)4, max_descendants/3)), groups, setCoinsRet, nValueRet, coin_selection_params, bnb_used)) ||
+ (m_spend_zero_conf_change && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, CoinEligibilityFilter(0, 1, max_ancestors/2, max_descendants/2), groups, setCoinsRet, nValueRet, coin_selection_params, bnb_used)) ||
+ (m_spend_zero_conf_change && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, CoinEligibilityFilter(0, 1, max_ancestors-1, max_descendants-1), groups, setCoinsRet, nValueRet, coin_selection_params, bnb_used)) ||
+ (m_spend_zero_conf_change && !fRejectLongChains && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, CoinEligibilityFilter(0, 1, std::numeric_limits<uint64_t>::max()), groups, setCoinsRet, nValueRet, coin_selection_params, bnb_used));
// because SelectCoinsMinConf clears the setCoinsRet, we now add the possible inputs to the coinset
- setCoinsRet.insert(setPresetCoins.begin(), setPresetCoins.end());
+ util::insert(setCoinsRet, setPresetCoins);
// add preset inputs to the total value selected
nValueRet += nValueFromPresetInputs;
@@ -2667,7 +2656,7 @@ OutputType CWallet::TransactionChangeType(OutputType change_type, const std::vec
}
bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CTransactionRef& tx, CReserveKey& reservekey, CAmount& nFeeRet,
- int& nChangePosInOut, std::string& strFailReason, const CCoinControl& coin_control, bool sign)
+ int& nChangePosInOut, std::string& strFailReason, const CCoinControl& coin_control, bool sign)
{
CAmount nValue = 0;
int nChangePosRequest = nChangePosInOut;
@@ -4439,3 +4428,29 @@ void CWallet::LearnAllRelatedScripts(const CPubKey& key)
LearnRelatedScripts(key, OutputType::P2SH_SEGWIT);
}
+std::vector<OutputGroup> CWallet::GroupOutputs(const std::vector<COutput>& outputs, bool single_coin) const {
+ std::vector<OutputGroup> groups;
+ std::map<CTxDestination, OutputGroup> gmap;
+ CTxDestination dst;
+ for (const auto& output : outputs) {
+ if (output.fSpendable) {
+ CInputCoin input_coin = output.GetInputCoin();
+
+ size_t ancestors, descendants;
+ mempool.GetTransactionAncestry(output.tx->GetHash(), ancestors, descendants);
+ if (!single_coin && ExtractDestination(output.tx->tx->vout[output.i].scriptPubKey, dst)) {
+ if (gmap.count(dst) == 10) {
+ groups.push_back(gmap[dst]);
+ gmap.erase(dst);
+ }
+ gmap[dst].Insert(input_coin, output.nDepth, output.tx->IsFromMe(ISMINE_ALL), ancestors, descendants);
+ } else {
+ groups.emplace_back(input_coin, output.nDepth, output.tx->IsFromMe(ISMINE_ALL), ancestors, descendants);
+ }
+ }
+ }
+ if (!single_coin) {
+ for (const auto& it : gmap) groups.push_back(it.second);
+ }
+ return groups;
+}