Skip to content

Commit 10ddbc3

Browse files
committed
merge bitcoin#25118: unify "allow/block other inputs" concept
We don't fully embrace ignoring vCoins as it would be a departure from earlier behavior. Instead, we will only give it preference if Dash-specific conditions are met but default to upstream behavior if they aren't.
1 parent ab93b98 commit 10ddbc3

File tree

5 files changed

+51
-65
lines changed

5 files changed

+51
-65
lines changed

src/coinjoin/util.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ CTransactionBuilder::CTransactionBuilder(CWallet& wallet, const CompactTallyItem
128128
// Change always goes back to origin
129129
coinControl.destChange = tallyItemIn.txdest;
130130
// Only allow tallyItems inputs for tx creation
131-
coinControl.fAllowOtherInputs = false;
131+
coinControl.m_allow_other_inputs = false;
132132
// Create dummy tx to calculate the exact required fees upfront for accurate amount and fee calculations
133133
CMutableTransaction dummyTx;
134134
// Select all tallyItem outputs in the coinControl so that CreateTransaction knows what to use

src/wallet/coincontrol.h

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,12 @@ class CCoinControl
4444
public:
4545
//! Custom change destination, if not set an address is generated
4646
CTxDestination destChange = CNoDestination();
47-
//! If false, only selected inputs are used
48-
bool m_add_inputs = true;
4947
//! If false, only safe inputs will be used
5048
bool m_include_unsafe_inputs = false;
51-
//! If false, allows unselected inputs, but requires all selected inputs be used if fAllowOtherInputs is true (default)
52-
bool fAllowOtherInputs = false;
53-
//! If false, only include as many inputs as necessary to fulfill a coin selection request. Only usable together with fAllowOtherInputs
49+
//! If true, the selection process can add extra unselected inputs from the wallet
50+
//! while requires all selected inputs be used
51+
bool m_allow_other_inputs = false;
52+
//! If false, only include as many inputs as necessary to fulfill a coin selection request. Only usable together with m_allow_other_inputs
5453
bool fRequireAllInputs = true;
5554
//! Includes watch only addresses which are solvable
5655
bool fAllowWatchOnly = false;

src/wallet/rpc/spend.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -401,8 +401,8 @@ void FundTransaction(CWallet& wallet, CMutableTransaction& tx, CAmount& fee_out,
401401
},
402402
true, true);
403403

404-
if (options.exists("add_inputs") ) {
405-
coinControl.m_add_inputs = options["add_inputs"].get_bool();
404+
if (options.exists("add_inputs")) {
405+
coinControl.m_allow_other_inputs = options["add_inputs"].get_bool();
406406
}
407407

408408
if (options.exists("changeAddress") || options.exists("change_address")) {
@@ -627,7 +627,7 @@ RPCHelpMan fundrawtransaction()
627627
int change_position;
628628
CCoinControl coin_control;
629629
// Automatically select (additional) coins. Can be overridden by options.add_inputs.
630-
coin_control.m_add_inputs = true;
630+
coin_control.m_allow_other_inputs = true;
631631
FundTransaction(*pwallet, tx, fee, change_position, request.params[1], coin_control, /* override_min_fee */ true);
632632

633633
UniValue result(UniValue::VOBJ);
@@ -895,7 +895,7 @@ RPCHelpMan send()
895895
CCoinControl coin_control;
896896
// Automatically select coins, unless at least one is manually selected. Can
897897
// be overridden by options.add_inputs.
898-
coin_control.m_add_inputs = rawTx.vin.size() == 0;
898+
coin_control.m_allow_other_inputs = rawTx.vin.size() == 0;
899899
FundTransaction(*pwallet, rawTx, fee, change_position, options, coin_control, /* override_min_fee */ false);
900900

901901
bool add_to_wallet = true;
@@ -1143,7 +1143,7 @@ RPCHelpMan walletcreatefundedpsbt()
11431143
CCoinControl coin_control;
11441144
// Automatically select coins, unless at least one is manually selected. Can
11451145
// be overridden by options.add_inputs.
1146-
coin_control.m_add_inputs = rawTx.vin.size() == 0;
1146+
coin_control.m_allow_other_inputs = rawTx.vin.size() == 0;
11471147
FundTransaction(*pwallet, rawTx, fee, change_position, request.params[3], coin_control, /* override_min_fee */ true);
11481148

11491149
// Make a blank psbt

src/wallet/spend.cpp

Lines changed: 37 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -165,17 +165,12 @@ CoinsResult AvailableCoins(const CWallet& wallet,
165165
break;
166166
}
167167
} // no default case, so the compiler can warn about missing cases
168-
if(!found) continue;
169-
170-
// Only consider selected coins if add_inputs is false
171-
if (coinControl && !coinControl->m_add_inputs && !coinControl->IsSelected(outpoint)) {
172-
continue;
173-
}
168+
if (!found) continue;
174169

175170
if (output.nValue < nMinimumAmount || output.nValue > nMaximumAmount)
176171
continue;
177172

178-
if (coinControl && coinControl->HasSelected() && !coinControl->fAllowOtherInputs && !coinControl->IsSelected(outpoint))
173+
if (coinControl && coinControl->HasSelected() && !coinControl->m_allow_other_inputs && !coinControl->IsSelected(outpoint))
179174
continue;
180175

181176
if (wallet.IsLockedCoin(outpoint) && nCoinType != CoinType::ONLY_MASTERNODE_COLLATERAL)
@@ -454,41 +449,6 @@ std::optional<SelectionResult> SelectCoins(const CWallet& wallet, const std::vec
454449

455450
OutputGroup preset_inputs(coin_selection_params);
456451

457-
// coin control -> return all selected outputs (we want all selected to go into the transaction for sure)
458-
if (coin_control.HasSelected() && !coin_control.fAllowOtherInputs)
459-
{
460-
for (const COutput& out : vCoins) {
461-
if (!out.spendable) continue;
462-
/* Set ancestors and descendants to 0 as these don't matter for preset inputs as no actual selection is being done.
463-
* positive_only is set to false because we want to include all preset inputs, even if they are dust.
464-
*/
465-
preset_inputs.Insert(out, /*ancestors=*/ 0, /*descendants=*/ 0, /*positive_only=*/ false);
466-
}
467-
SelectionResult result(nTargetValue, SelectionAlgorithm::MANUAL);
468-
bool all_inputs{coin_control.fRequireAllInputs};
469-
if (!all_inputs) {
470-
// Calculate the smallest set of inputs required to meet nTargetValue
471-
bool success{false};
472-
OutputGroup preset_candidates(coin_selection_params);
473-
for (const auto& output : preset_inputs.m_outputs) {
474-
preset_candidates.Insert(output, /*ancestors=*/0, /*descendants=*/0, /*positive_only=*/false);
475-
if (preset_candidates.GetSelectionAmount() >= nTargetValue) {
476-
result.AddInput(preset_candidates);
477-
success = true;
478-
break;
479-
}
480-
}
481-
// Couldn't meet target, add all inputs
482-
if (!success) all_inputs = true;
483-
}
484-
if (all_inputs) {
485-
result.AddInput(preset_inputs);
486-
}
487-
if (result.GetSelectedValue() < nTargetValue) return std::nullopt;
488-
result.ComputeAndSetWaste(coin_selection_params.m_cost_of_change);
489-
return result;
490-
}
491-
492452
// calculate value from preset inputs and store them
493453
std::set<COutPoint> preset_coins;
494454

@@ -497,15 +457,14 @@ std::optional<SelectionResult> SelectCoins(const CWallet& wallet, const std::vec
497457
for (const COutPoint& outpoint : vPresetInputs) {
498458
int input_bytes = -1;
499459
CTxOut txout;
500-
std::map<uint256, CWalletTx>::const_iterator it = wallet.mapWallet.find(outpoint.hash);
501-
if (it != wallet.mapWallet.end()) {
502-
const CWalletTx& wtx = it->second;
460+
auto ptr_wtx = wallet.GetWalletTx(outpoint.hash);
461+
if (ptr_wtx) {
503462
// Clearly invalid input, fail
504-
if (wtx.tx->vout.size() <= outpoint.n) {
463+
if (ptr_wtx->tx->vout.size() <= outpoint.n) {
505464
return std::nullopt;
506465
}
507-
input_bytes = GetTxSpendSize(wallet, wtx, outpoint.n, false);
508-
txout = wtx.tx->vout.at(outpoint.n);
466+
input_bytes = GetTxSpendSize(wallet, *ptr_wtx, outpoint.n, false);
467+
txout = ptr_wtx->tx->vout.at(outpoint.n);
509468
} else {
510469
// The input is external. We did not find the tx in mapWallet.
511470
if (!coin_control.GetExternalOutput(outpoint, txout)) {
@@ -536,6 +495,36 @@ std::optional<SelectionResult> SelectCoins(const CWallet& wallet, const std::vec
536495
preset_inputs.Insert(output, /*ancestors=*/ 0, /*descendants=*/ 0, /*positive_only=*/ false);
537496
}
538497

498+
// coin control -> return all selected outputs (we want all selected to go into the transaction for sure)
499+
if (coin_control.HasSelected() && !coin_control.m_allow_other_inputs) {
500+
SelectionResult result(nTargetValue, SelectionAlgorithm::MANUAL);
501+
bool all_inputs{coin_control.fRequireAllInputs};
502+
if (!all_inputs) {
503+
// Calculate the smallest set of inputs required to meet nTargetValue from vCoins
504+
bool success{false};
505+
OutputGroup preset_candidates(coin_selection_params);
506+
for (const COutput& out : vCoins) {
507+
if (!out.spendable) continue;
508+
if (preset_coins.count(out.outpoint)) {
509+
preset_candidates.Insert(out, /*ancestors=*/0, /*descendants=*/0, /*positive_only=*/false);
510+
}
511+
if (preset_candidates.GetSelectionAmount() >= nTargetValue) {
512+
result.AddInput(preset_candidates);
513+
success = true;
514+
break;
515+
}
516+
}
517+
// Couldn't meet target, add all inputs
518+
if (!success) all_inputs = true;
519+
}
520+
if (all_inputs) {
521+
result.AddInput(preset_inputs);
522+
}
523+
if (result.GetSelectedValue() < nTargetValue) return std::nullopt;
524+
result.ComputeAndSetWaste(coin_selection_params.m_cost_of_change);
525+
return result;
526+
}
527+
539528
// remove preset inputs from vCoins so that Coin Selection doesn't pick them.
540529
for (std::vector<COutput>::iterator it = vCoins.begin(); it != vCoins.end() && coin_control.HasSelected();)
541530
{
@@ -1102,8 +1091,6 @@ bool FundTransaction(CWallet& wallet, CMutableTransaction& tx, CAmount& nFeeRet,
11021091
vecSend.push_back(recipient);
11031092
}
11041093

1105-
coinControl.fAllowOtherInputs = true;
1106-
11071094
// Acquire the locks to prevent races to the new locked unspents between the
11081095
// CreateTransaction call and LockCoin calls (when lockUnspents is true).
11091096
LOCK(wallet.cs_wallet);

src/wallet/test/coinselector_tests.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,7 @@ BOOST_AUTO_TEST_CASE(bnb_search_test)
333333
add_coin(coins, *wallet, 3 * CENT, coin_selection_params_bnb.m_effective_feerate, 6 * 24, false, 0, true);
334334
add_coin(coins, *wallet, 2 * CENT, coin_selection_params_bnb.m_effective_feerate, 6 * 24, false, 0, true);
335335
CCoinControl coin_control;
336-
coin_control.fAllowOtherInputs = true;
336+
coin_control.m_allow_other_inputs = true;
337337
coin_control.Select(coins.at(0).outpoint);
338338
coin_selection_params_bnb.m_effective_feerate = CFeeRate(0);
339339
const auto result10 = SelectCoins(*wallet, coins, 10 * CENT, coin_control, coin_selection_params_bnb);
@@ -390,7 +390,7 @@ BOOST_AUTO_TEST_CASE(bnb_search_test)
390390
expected_result.Clear();
391391
add_coin(9 * CENT, 2, expected_result);
392392
add_coin(1 * CENT, 2, expected_result);
393-
coin_control.fAllowOtherInputs = true;
393+
coin_control.m_allow_other_inputs = true;
394394
coin_control.Select(coins.at(1).outpoint); // pre select 9 coin
395395
const auto result13 = SelectCoins(*wallet, coins, 10 * CENT, coin_control, coin_selection_params_bnb);
396396
BOOST_CHECK(EquivalentResult(expected_result, *result13));
@@ -933,10 +933,10 @@ BOOST_AUTO_TEST_CASE(minimum_inputs_test)
933933
add_coin(coins, *wallet, 16 * COIN, CFeeRate(0), /*nAge=*/6*24, /*fIsFromMe=*/false, /*nInput=*/0, /*spendable=*/true);
934934
add_coin(coins, *wallet, 24 * COIN, CFeeRate(0), /*nAge=*/6*24, /*fIsFromMe=*/false, /*nInput=*/0, /*spendable=*/true);
935935

936-
// Setup coin control to select from the given coins (!fAllowOtherInputs) *but* consume as little
936+
// Setup coin control to select from the given coins (!m_allow_other_inputs) *but* consume as little
937937
// as possible (!fRequireAllInputs) and select our coins.
938938
CCoinControl coin_control{};
939-
coin_control.fAllowOtherInputs = false;
939+
coin_control.m_allow_other_inputs = false;
940940
coin_control.fRequireAllInputs = false;
941941
for (const auto& coin : coins) {
942942
coin_control.Select(coin.outpoint);

0 commit comments

Comments
 (0)