wallet: return 'CoinsResult' struct in AvailableCoins

Instead of accepting a `vCoins` reference that is cleared at the beginning of the method.

Note:
This new struct, down the commits line, will contain other `AvailableCoins` useful results.
This commit is contained in:
furszy
2022-04-22 17:31:28 -03:00
parent fbb90c44ac
commit 4ce235ef8f
5 changed files with 30 additions and 32 deletions

View File

@ -638,7 +638,7 @@ RPCHelpMan listunspent()
cctl.m_max_depth = nMaxDepth; cctl.m_max_depth = nMaxDepth;
cctl.m_include_unsafe_inputs = include_unsafe; cctl.m_include_unsafe_inputs = include_unsafe;
LOCK(pwallet->cs_wallet); LOCK(pwallet->cs_wallet);
AvailableCoinsListUnspent(*pwallet, vecOutputs, &cctl, nMinimumAmount, nMaximumAmount, nMinimumSumAmount, nMaximumCount); vecOutputs = AvailableCoinsListUnspent(*pwallet, &cctl, nMinimumAmount, nMaximumAmount, nMinimumSumAmount, nMaximumCount).coins;
} }
LOCK(pwallet->cs_wallet); LOCK(pwallet->cs_wallet);

View File

@ -1380,7 +1380,6 @@ RPCHelpMan sendall()
CMutableTransaction rawTx{ConstructTransaction(options["inputs"], recipient_key_value_pairs, options["locktime"], rbf)}; CMutableTransaction rawTx{ConstructTransaction(options["inputs"], recipient_key_value_pairs, options["locktime"], rbf)};
LOCK(pwallet->cs_wallet); LOCK(pwallet->cs_wallet);
std::vector<COutput> all_the_utxos;
CAmount total_input_value(0); CAmount total_input_value(0);
bool send_max{options.exists("send_max") ? options["send_max"].get_bool() : false}; bool send_max{options.exists("send_max") ? options["send_max"].get_bool() : false};
@ -1398,8 +1397,7 @@ RPCHelpMan sendall()
total_input_value += tx->tx->vout[input.prevout.n].nValue; total_input_value += tx->tx->vout[input.prevout.n].nValue;
} }
} else { } else {
AvailableCoins(*pwallet, all_the_utxos, &coin_control, fee_rate, /*nMinimumAmount=*/0); for (const COutput& output : AvailableCoins(*pwallet, &coin_control, fee_rate, /*nMinimumAmount=*/0).coins) {
for (const COutput& output : all_the_utxos) {
CHECK_NONFATAL(output.input_bytes > 0); CHECK_NONFATAL(output.input_bytes > 0);
if (send_max && fee_rate.GetFee(output.input_bytes) > output.txout.nValue) { if (send_max && fee_rate.GetFee(output.input_bytes) > output.txout.nValue) {
continue; continue;

View File

@ -84,11 +84,17 @@ TxSize CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *walle
return CalculateMaximumSignedTxSize(tx, wallet, txouts, coin_control); return CalculateMaximumSignedTxSize(tx, wallet, txouts, coin_control);
} }
void AvailableCoins(const CWallet& wallet, std::vector<COutput>& vCoins, const CCoinControl* coinControl, std::optional<CFeeRate> feerate, const CAmount& nMinimumAmount, const CAmount& nMaximumAmount, const CAmount& nMinimumSumAmount, const uint64_t nMaximumCount) CoinsResult AvailableCoins(const CWallet& wallet,
const CCoinControl* coinControl,
std::optional<CFeeRate> feerate,
const CAmount& nMinimumAmount,
const CAmount& nMaximumAmount,
const CAmount& nMinimumSumAmount,
const uint64_t nMaximumCount)
{ {
AssertLockHeld(wallet.cs_wallet); AssertLockHeld(wallet.cs_wallet);
vCoins.clear(); CoinsResult result;
CAmount nTotal = 0; CAmount nTotal = 0;
// Either the WALLET_FLAG_AVOID_REUSE flag is not set (in which case we always allow), or we default to avoiding, and only in the case where // Either the WALLET_FLAG_AVOID_REUSE flag is not set (in which case we always allow), or we default to avoiding, and only in the case where
// a coin control object is provided, and has the avoid address reuse flag set to false, do we allow already used addresses // a coin control object is provided, and has the avoid address reuse flag set to false, do we allow already used addresses
@ -191,29 +197,30 @@ void AvailableCoins(const CWallet& wallet, std::vector<COutput>& vCoins, const C
bool solvable = provider ? IsSolvable(*provider, wtx.tx->vout[i].scriptPubKey) : false; 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)); 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)); int input_bytes = GetTxSpendSize(wallet, wtx, i, (coinControl && coinControl->fAllowWatchOnly));
result.coins.emplace_back(COutPoint(wtx.GetHash(), i), wtx.tx->vout.at(i), nDepth, input_bytes, spendable, solvable, safeTx, wtx.GetTxTime(), tx_from_me, feerate);
vCoins.emplace_back(COutPoint(wtx.GetHash(), i), wtx.tx->vout.at(i), nDepth, input_bytes, spendable, solvable, safeTx, wtx.GetTxTime(), tx_from_me, feerate);
// Checks the sum amount of all UTXO's. // Checks the sum amount of all UTXO's.
if (nMinimumSumAmount != MAX_MONEY) { if (nMinimumSumAmount != MAX_MONEY) {
nTotal += wtx.tx->vout[i].nValue; nTotal += wtx.tx->vout[i].nValue;
if (nTotal >= nMinimumSumAmount) { if (nTotal >= nMinimumSumAmount) {
return; return result;
} }
} }
// Checks the maximum number of UTXO's. // Checks the maximum number of UTXO's.
if (nMaximumCount > 0 && vCoins.size() >= nMaximumCount) { if (nMaximumCount > 0 && result.coins.size() >= nMaximumCount) {
return; return result;
} }
} }
} }
return result;
} }
void AvailableCoinsListUnspent(const CWallet& wallet, std::vector<COutput>& vCoins, const CCoinControl* coinControl, const CAmount& nMinimumAmount, const CAmount& nMaximumAmount, const CAmount& nMinimumSumAmount, const uint64_t nMaximumCount) CoinsResult AvailableCoinsListUnspent(const CWallet& wallet, const CCoinControl* coinControl, const CAmount& nMinimumAmount, const CAmount& nMaximumAmount, const CAmount& nMinimumSumAmount, const uint64_t nMaximumCount)
{ {
AvailableCoins(wallet, vCoins, coinControl, /*feerate=*/ std::nullopt, nMinimumAmount, nMaximumAmount, nMinimumSumAmount, nMaximumCount); return AvailableCoins(wallet, coinControl, /*feerate=*/ std::nullopt, nMinimumAmount, nMaximumAmount, nMinimumSumAmount, nMaximumCount);
} }
CAmount GetAvailableBalance(const CWallet& wallet, const CCoinControl* coinControl) CAmount GetAvailableBalance(const CWallet& wallet, const CCoinControl* coinControl)
@ -221,9 +228,7 @@ CAmount GetAvailableBalance(const CWallet& wallet, const CCoinControl* coinContr
LOCK(wallet.cs_wallet); LOCK(wallet.cs_wallet);
CAmount balance = 0; CAmount balance = 0;
std::vector<COutput> vCoins; for (const COutput& out : AvailableCoinsListUnspent(wallet, coinControl).coins) {
AvailableCoinsListUnspent(wallet, vCoins, coinControl);
for (const COutput& out : vCoins) {
if (out.spendable) { if (out.spendable) {
balance += out.txout.nValue; balance += out.txout.nValue;
} }
@ -260,11 +265,8 @@ std::map<CTxDestination, std::vector<COutput>> ListCoins(const CWallet& wallet)
AssertLockHeld(wallet.cs_wallet); AssertLockHeld(wallet.cs_wallet);
std::map<CTxDestination, std::vector<COutput>> result; std::map<CTxDestination, std::vector<COutput>> result;
std::vector<COutput> availableCoins;
AvailableCoinsListUnspent(wallet, availableCoins); for (const COutput& coin : AvailableCoinsListUnspent(wallet).coins) {
for (const COutput& coin : availableCoins) {
CTxDestination address; CTxDestination address;
if ((coin.spendable || (wallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) && coin.solvable)) && if ((coin.spendable || (wallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) && coin.solvable)) &&
ExtractDestination(FindNonChangeParentOutput(wallet, coin.outpoint).scriptPubKey, address)) { ExtractDestination(FindNonChangeParentOutput(wallet, coin.outpoint).scriptPubKey, address)) {
@ -791,11 +793,10 @@ static std::optional<CreatedTransactionResult> CreateTransactionInternal(
CAmount selection_target = recipients_sum + not_input_fees; CAmount selection_target = recipients_sum + not_input_fees;
// Get available coins // Get available coins
std::vector<COutput> vAvailableCoins; auto res_available_coins = AvailableCoins(wallet, &coin_control, coin_selection_params.m_effective_feerate, 1, MAX_MONEY, MAX_MONEY, 0);
AvailableCoins(wallet, vAvailableCoins, &coin_control, coin_selection_params.m_effective_feerate, 1, MAX_MONEY, MAX_MONEY, 0);
// Choose coins to use // Choose coins to use
std::optional<SelectionResult> result = SelectCoins(wallet, vAvailableCoins, /*nTargetValue=*/selection_target, coin_control, coin_selection_params); std::optional<SelectionResult> result = SelectCoins(wallet, res_available_coins.coins, /*nTargetValue=*/selection_target, coin_control, coin_selection_params);
if (!result) { if (!result) {
error = _("Insufficient funds"); error = _("Insufficient funds");
return std::nullopt; return std::nullopt;

View File

@ -34,16 +34,19 @@ struct TxSize {
TxSize CalculateMaximumSignedTxSize(const CTransaction& tx, const CWallet* wallet, const std::vector<CTxOut>& txouts, const CCoinControl* coin_control = nullptr); TxSize CalculateMaximumSignedTxSize(const CTransaction& tx, const CWallet* wallet, const std::vector<CTxOut>& txouts, const CCoinControl* coin_control = nullptr);
TxSize CalculateMaximumSignedTxSize(const CTransaction& tx, const CWallet* wallet, const CCoinControl* coin_control = nullptr) EXCLUSIVE_LOCKS_REQUIRED(wallet->cs_wallet); TxSize CalculateMaximumSignedTxSize(const CTransaction& tx, const CWallet* wallet, const CCoinControl* coin_control = nullptr) EXCLUSIVE_LOCKS_REQUIRED(wallet->cs_wallet);
struct CoinsResult {
std::vector<COutput> coins;
};
/** /**
* populate vCoins with vector of available COutputs. * Return vector of available COutputs.
*/ */
void AvailableCoins(const CWallet& wallet, std::vector<COutput>& vCoins, const CCoinControl* coinControl = nullptr, std::optional<CFeeRate> feerate = std::nullopt, const CAmount& nMinimumAmount = 1, const CAmount& nMaximumAmount = MAX_MONEY, const CAmount& nMinimumSumAmount = MAX_MONEY, const uint64_t nMaximumCount = 0) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet); CoinsResult AvailableCoins(const CWallet& wallet, const CCoinControl* coinControl = nullptr, std::optional<CFeeRate> feerate = std::nullopt, const CAmount& nMinimumAmount = 1, const CAmount& nMaximumAmount = MAX_MONEY, const CAmount& nMinimumSumAmount = MAX_MONEY, const uint64_t nMaximumCount = 0) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet);
/** /**
* Wrapper function for AvailableCoins which skips the `feerate` parameter. Use this function * Wrapper function for AvailableCoins which skips the `feerate` parameter. Use this function
* to list all available coins (e.g. listunspent RPC) while not intending to fund a transaction. * to list all available coins (e.g. listunspent RPC) while not intending to fund a transaction.
*/ */
void AvailableCoinsListUnspent(const CWallet& wallet, std::vector<COutput>& vCoins, const CCoinControl* coinControl = nullptr, const CAmount& nMinimumAmount = 1, const CAmount& nMaximumAmount = MAX_MONEY, const CAmount& nMinimumSumAmount = MAX_MONEY, const uint64_t nMaximumCount = 0) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet); CoinsResult AvailableCoinsListUnspent(const CWallet& wallet, const CCoinControl* coinControl = nullptr, const CAmount& nMinimumAmount = 1, const CAmount& nMaximumAmount = MAX_MONEY, const CAmount& nMinimumSumAmount = MAX_MONEY, const uint64_t nMaximumCount = 0) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet);
CAmount GetAvailableBalance(const CWallet& wallet, const CCoinControl* coinControl = nullptr); CAmount GetAvailableBalance(const CWallet& wallet, const CCoinControl* coinControl = nullptr);

View File

@ -583,9 +583,7 @@ BOOST_FIXTURE_TEST_CASE(ListCoinsTest, ListCoinsTestingSetup)
// Lock both coins. Confirm number of available coins drops to 0. // Lock both coins. Confirm number of available coins drops to 0.
{ {
LOCK(wallet->cs_wallet); LOCK(wallet->cs_wallet);
std::vector<COutput> available; BOOST_CHECK_EQUAL(AvailableCoinsListUnspent(*wallet).coins.size(), 2U);
AvailableCoinsListUnspent(*wallet, available);
BOOST_CHECK_EQUAL(available.size(), 2U);
} }
for (const auto& group : list) { for (const auto& group : list) {
for (const auto& coin : group.second) { for (const auto& coin : group.second) {
@ -595,9 +593,7 @@ BOOST_FIXTURE_TEST_CASE(ListCoinsTest, ListCoinsTestingSetup)
} }
{ {
LOCK(wallet->cs_wallet); LOCK(wallet->cs_wallet);
std::vector<COutput> available; BOOST_CHECK_EQUAL(AvailableCoinsListUnspent(*wallet).coins.size(), 0U);
AvailableCoinsListUnspent(*wallet, available);
BOOST_CHECK_EQUAL(available.size(), 0U);
} }
// Confirm ListCoins still returns same result as before, despite coins // Confirm ListCoins still returns same result as before, despite coins
// being locked. // being locked.