aboutsummaryrefslogtreecommitdiff
path: root/src/wallet/wallet.cpp
diff options
context:
space:
mode:
authorKarl-Johan Alm <karljohan-alm@garage.co.jp>2018-07-19 11:45:26 +0900
committerKarl-Johan Alm <karljohan-alm@garage.co.jp>2018-07-24 15:05:37 +0900
commit59d6f7b4e2f847ec1f2ff46c84e6157655984f85 (patch)
tree259887780c4798f97fbf006d41849224f611706d /src/wallet/wallet.cpp
parent87ebce25d66952f5ce565bb5130dcf5e24049872 (diff)
downloadbitcoin-59d6f7b4e2f847ec1f2ff46c84e6157655984f85.tar.xz
wallet: Switch to using output groups instead of coins in coin selection
Diffstat (limited to 'src/wallet/wallet.cpp')
-rw-r--r--src/wallet/wallet.cpp72
1 files changed, 39 insertions, 33 deletions
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index 03c829357b..3c7e3302fb 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -2431,15 +2431,14 @@ bool CWallet::OutputEligibleForSpending(const COutput& output, const CoinEligibi
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;
@@ -2450,19 +2449,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);
@@ -2470,13 +2476,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);
@@ -2498,7 +2500,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);
}
@@ -2532,27 +2534,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;
@@ -2666,7 +2672,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;