Skip to content

Commit daef20f

Browse files
author
MarcoFalke
committed
Merge #15596: rpc: Ignore sendmany::minconf as dummy value
fabfb79 doc: Add release notes for 15596 (MarcoFalke) fac1a0f wallet: Remove unused GetLegacyBalance (MarcoFalke) faa3a24 scripted-diff: wallet: Rename pcoin to wtx (MarcoFalke) fae5f87 rpc: Document that minconf is an ignored dummy value (MarcoFalke) Pull request description: Other RPCs such as `sendtoaddress` don't have this option at all and `sendmany` should by default spend from (lets say) our change. ACKs for commit fabfb7: jnewbery: utACK fabfb79 ryanofsky: utACK fabfb79. Nice writeup! Release notes are only change since previous review. Tree-SHA512: 2526ead2330be7c2beb78b96bc5e55440566c4a3a809bbbd66f5c9fc517f6890affa5d14005dc102644d49679a374510f9507255e870cf88aaa63e429beef658
2 parents ba54342 + fabfb79 commit daef20f

File tree

4 files changed

+65
-102
lines changed

4 files changed

+65
-102
lines changed

doc/release-notes.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,9 +66,21 @@ platform.
6666
Notable changes
6767
===============
6868

69-
Example item
69+
Updated RPCs
7070
------------
7171

72+
Note: some low-level RPC changes mainly useful for testing are described in the
73+
Low-level Changes section below.
74+
75+
* The `sendmany` RPC had an argument `minconf` that was not well specified and
76+
would lead to RPC errors even when the wallet's coin selection would succeed.
77+
The `sendtoaddress` RPC never had this check, so to normalize the behavior,
78+
`minconf` is now ignored in `sendmany`. If the coin selection does not
79+
succeed due to missing coins, it will still throw an RPC error. Be reminded
80+
that coin selection is influenced by the `-spendzeroconfchange`,
81+
`-limitancestorcount`, `-limitdescendantcount` and `-walletrejectlongchains`
82+
command line arguments.
83+
7284

7385
Low-level changes
7486
=================

src/wallet/rpcwallet.cpp

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -807,9 +807,7 @@ static UniValue sendmany(const JSONRPCRequest& request)
807807
return NullUniValue;
808808
}
809809

810-
if (request.fHelp || request.params.size() < 2 || request.params.size() > 8)
811-
throw std::runtime_error(
812-
RPCHelpMan{"sendmany",
810+
const RPCHelpMan help{"sendmany",
813811
"\nSend multiple times. Amounts are double-precision floating point numbers." +
814812
HelpRequiringPassphrase(pwallet) + "\n",
815813
{
@@ -819,7 +817,7 @@ static UniValue sendmany(const JSONRPCRequest& request)
819817
{"address", RPCArg::Type::AMOUNT, RPCArg::Optional::NO, "The bitcoin address is the key, the numeric amount (can be string) in " + CURRENCY_UNIT + " is the value"},
820818
},
821819
},
822-
{"minconf", RPCArg::Type::NUM, /* default */ "1", "Only use the balance confirmed at least this many times."},
820+
{"minconf", RPCArg::Type::NUM, RPCArg::Optional::OMITTED_NAMED_ARG, "Ignored dummy value"},
823821
{"comment", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "A comment"},
824822
{"subtractfeefrom", RPCArg::Type::ARR, RPCArg::Optional::OMITTED_NAMED_ARG, "A json array with addresses.\n"
825823
" The fee will be equally deducted from the amount of each selected address.\n"
@@ -850,7 +848,11 @@ static UniValue sendmany(const JSONRPCRequest& request)
850848
"\nAs a JSON-RPC call\n"
851849
+ HelpExampleRpc("sendmany", "\"\", {\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\":0.01,\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\":0.02}, 6, \"testing\"")
852850
},
853-
}.ToString());
851+
};
852+
853+
if (request.fHelp || !help.IsValidNumArgs(request.params.size())) {
854+
throw std::runtime_error(help.ToString());
855+
}
854856

855857
// Make sure the results are valid at least up to the most recent block
856858
// the user could have gotten from another RPC command prior to now
@@ -867,9 +869,6 @@ static UniValue sendmany(const JSONRPCRequest& request)
867869
throw JSONRPCError(RPC_INVALID_PARAMETER, "Dummy value must be set to \"\"");
868870
}
869871
UniValue sendTo = request.params[1].get_obj();
870-
int nMinDepth = 1;
871-
if (!request.params[2].isNull())
872-
nMinDepth = request.params[2].get_int();
873872

874873
mapValue_t mapValue;
875874
if (!request.params[3].isNull() && !request.params[3].get_str().empty())
@@ -897,7 +896,6 @@ static UniValue sendmany(const JSONRPCRequest& request)
897896
std::set<CTxDestination> destinations;
898897
std::vector<CRecipient> vecSend;
899898

900-
CAmount totalAmount = 0;
901899
std::vector<std::string> keys = sendTo.getKeys();
902900
for (const std::string& name_ : keys) {
903901
CTxDestination dest = DecodeDestination(name_);
@@ -914,7 +912,6 @@ static UniValue sendmany(const JSONRPCRequest& request)
914912
CAmount nAmount = AmountFromValue(sendTo[name_]);
915913
if (nAmount <= 0)
916914
throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send");
917-
totalAmount += nAmount;
918915

919916
bool fSubtractFeeFromAmount = false;
920917
for (unsigned int idx = 0; idx < subtractFeeFromAmount.size(); idx++) {
@@ -929,11 +926,6 @@ static UniValue sendmany(const JSONRPCRequest& request)
929926

930927
EnsureWalletIsUnlocked(pwallet);
931928

932-
// Check funds
933-
if (totalAmount > pwallet->GetLegacyBalance(ISMINE_SPENDABLE, nMinDepth)) {
934-
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Wallet has insufficient funds");
935-
}
936-
937929
// Shuffle recipient list
938930
std::shuffle(vecSend.begin(), vecSend.end(), FastRandomContext());
939931

src/wallet/wallet.cpp

Lines changed: 45 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -2162,9 +2162,9 @@ CAmount CWallet::GetBalance(const isminefilter& filter, const int min_depth) con
21622162
LOCK(cs_wallet);
21632163
for (const auto& entry : mapWallet)
21642164
{
2165-
const CWalletTx* pcoin = &entry.second;
2166-
if (pcoin->IsTrusted(*locked_chain) && pcoin->GetDepthInMainChain(*locked_chain) >= min_depth) {
2167-
nTotal += pcoin->GetAvailableCredit(*locked_chain, true, filter);
2165+
const CWalletTx& wtx = entry.second;
2166+
if (wtx.IsTrusted(*locked_chain) && wtx.GetDepthInMainChain(*locked_chain) >= min_depth) {
2167+
nTotal += wtx.GetAvailableCredit(*locked_chain, true, filter);
21682168
}
21692169
}
21702170
}
@@ -2180,9 +2180,9 @@ CAmount CWallet::GetUnconfirmedBalance() const
21802180
LOCK(cs_wallet);
21812181
for (const auto& entry : mapWallet)
21822182
{
2183-
const CWalletTx* pcoin = &entry.second;
2184-
if (!pcoin->IsTrusted(*locked_chain) && pcoin->GetDepthInMainChain(*locked_chain) == 0 && pcoin->InMempool())
2185-
nTotal += pcoin->GetAvailableCredit(*locked_chain);
2183+
const CWalletTx& wtx = entry.second;
2184+
if (!wtx.IsTrusted(*locked_chain) && wtx.GetDepthInMainChain(*locked_chain) == 0 && wtx.InMempool())
2185+
nTotal += wtx.GetAvailableCredit(*locked_chain);
21862186
}
21872187
}
21882188
return nTotal;
@@ -2196,8 +2196,8 @@ CAmount CWallet::GetImmatureBalance() const
21962196
LOCK(cs_wallet);
21972197
for (const auto& entry : mapWallet)
21982198
{
2199-
const CWalletTx* pcoin = &entry.second;
2200-
nTotal += pcoin->GetImmatureCredit(*locked_chain);
2199+
const CWalletTx& wtx = entry.second;
2200+
nTotal += wtx.GetImmatureCredit(*locked_chain);
22012201
}
22022202
}
22032203
return nTotal;
@@ -2211,9 +2211,9 @@ CAmount CWallet::GetUnconfirmedWatchOnlyBalance() const
22112211
LOCK(cs_wallet);
22122212
for (const auto& entry : mapWallet)
22132213
{
2214-
const CWalletTx* pcoin = &entry.second;
2215-
if (!pcoin->IsTrusted(*locked_chain) && pcoin->GetDepthInMainChain(*locked_chain) == 0 && pcoin->InMempool())
2216-
nTotal += pcoin->GetAvailableCredit(*locked_chain, true, ISMINE_WATCH_ONLY);
2214+
const CWalletTx& wtx = entry.second;
2215+
if (!wtx.IsTrusted(*locked_chain) && wtx.GetDepthInMainChain(*locked_chain) == 0 && wtx.InMempool())
2216+
nTotal += wtx.GetAvailableCredit(*locked_chain, true, ISMINE_WATCH_ONLY);
22172217
}
22182218
}
22192219
return nTotal;
@@ -2227,53 +2227,13 @@ CAmount CWallet::GetImmatureWatchOnlyBalance() const
22272227
LOCK(cs_wallet);
22282228
for (const auto& entry : mapWallet)
22292229
{
2230-
const CWalletTx* pcoin = &entry.second;
2231-
nTotal += pcoin->GetImmatureWatchOnlyCredit(*locked_chain);
2230+
const CWalletTx& wtx = entry.second;
2231+
nTotal += wtx.GetImmatureWatchOnlyCredit(*locked_chain);
22322232
}
22332233
}
22342234
return nTotal;
22352235
}
22362236

2237-
// Calculate total balance in a different way from GetBalance. The biggest
2238-
// difference is that GetBalance sums up all unspent TxOuts paying to the
2239-
// wallet, while this sums up both spent and unspent TxOuts paying to the
2240-
// wallet, and then subtracts the values of TxIns spending from the wallet. This
2241-
// also has fewer restrictions on which unconfirmed transactions are considered
2242-
// trusted.
2243-
CAmount CWallet::GetLegacyBalance(const isminefilter& filter, int minDepth) const
2244-
{
2245-
auto locked_chain = chain().lock();
2246-
LOCK(cs_wallet);
2247-
2248-
CAmount balance = 0;
2249-
for (const auto& entry : mapWallet) {
2250-
const CWalletTx& wtx = entry.second;
2251-
const int depth = wtx.GetDepthInMainChain(*locked_chain);
2252-
if (depth < 0 || !locked_chain->checkFinalTx(*wtx.tx) || wtx.IsImmatureCoinBase(*locked_chain)) {
2253-
continue;
2254-
}
2255-
2256-
// Loop through tx outputs and add incoming payments. For outgoing txs,
2257-
// treat change outputs specially, as part of the amount debited.
2258-
CAmount debit = wtx.GetDebit(filter);
2259-
const bool outgoing = debit > 0;
2260-
for (const CTxOut& out : wtx.tx->vout) {
2261-
if (outgoing && IsChange(out)) {
2262-
debit -= out.nValue;
2263-
} else if (IsMine(out) & filter && depth >= minDepth) {
2264-
balance += out.nValue;
2265-
}
2266-
}
2267-
2268-
// For outgoing txs, subtract amount debited.
2269-
if (outgoing) {
2270-
balance -= debit;
2271-
}
2272-
}
2273-
2274-
return balance;
2275-
}
2276-
22772237
CAmount CWallet::GetAvailableBalance(const CCoinControl* coinControl) const
22782238
{
22792239
auto locked_chain = chain().lock();
@@ -2300,25 +2260,25 @@ void CWallet::AvailableCoins(interfaces::Chain::Lock& locked_chain, std::vector<
23002260
for (const auto& entry : mapWallet)
23012261
{
23022262
const uint256& wtxid = entry.first;
2303-
const CWalletTx* pcoin = &entry.second;
2263+
const CWalletTx& wtx = entry.second;
23042264

2305-
if (!locked_chain.checkFinalTx(*pcoin->tx)) {
2265+
if (!locked_chain.checkFinalTx(*wtx.tx)) {
23062266
continue;
23072267
}
23082268

2309-
if (pcoin->IsImmatureCoinBase(locked_chain))
2269+
if (wtx.IsImmatureCoinBase(locked_chain))
23102270
continue;
23112271

2312-
int nDepth = pcoin->GetDepthInMainChain(locked_chain);
2272+
int nDepth = wtx.GetDepthInMainChain(locked_chain);
23132273
if (nDepth < 0)
23142274
continue;
23152275

23162276
// We should not consider coins which aren't at least in our mempool
23172277
// It's possible for these to be conflicted via ancestors which we may never be able to detect
2318-
if (nDepth == 0 && !pcoin->InMempool())
2278+
if (nDepth == 0 && !wtx.InMempool())
23192279
continue;
23202280

2321-
bool safeTx = pcoin->IsTrusted(locked_chain);
2281+
bool safeTx = wtx.IsTrusted(locked_chain);
23222282

23232283
// We should not consider coins from transactions that are replacing
23242284
// other transactions.
@@ -2335,7 +2295,7 @@ void CWallet::AvailableCoins(interfaces::Chain::Lock& locked_chain, std::vector<
23352295
// be a 1-block reorg away from the chain where transactions A and C
23362296
// were accepted to another chain where B, B', and C were all
23372297
// accepted.
2338-
if (nDepth == 0 && pcoin->mapValue.count("replaces_txid")) {
2298+
if (nDepth == 0 && wtx.mapValue.count("replaces_txid")) {
23392299
safeTx = false;
23402300
}
23412301

@@ -2347,7 +2307,7 @@ void CWallet::AvailableCoins(interfaces::Chain::Lock& locked_chain, std::vector<
23472307
// intending to replace A', but potentially resulting in a scenario
23482308
// where A, A', and D could all be accepted (instead of just B and
23492309
// D, or just A and A' like the user would want).
2350-
if (nDepth == 0 && pcoin->mapValue.count("replaced_by_txid")) {
2310+
if (nDepth == 0 && wtx.mapValue.count("replaced_by_txid")) {
23512311
safeTx = false;
23522312
}
23532313

@@ -2358,8 +2318,8 @@ void CWallet::AvailableCoins(interfaces::Chain::Lock& locked_chain, std::vector<
23582318
if (nDepth < nMinDepth || nDepth > nMaxDepth)
23592319
continue;
23602320

2361-
for (unsigned int i = 0; i < pcoin->tx->vout.size(); i++) {
2362-
if (pcoin->tx->vout[i].nValue < nMinimumAmount || pcoin->tx->vout[i].nValue > nMaximumAmount)
2321+
for (unsigned int i = 0; i < wtx.tx->vout.size(); i++) {
2322+
if (wtx.tx->vout[i].nValue < nMinimumAmount || wtx.tx->vout[i].nValue > nMaximumAmount)
23632323
continue;
23642324

23652325
if (coinControl && coinControl->HasSelected() && !coinControl->fAllowOtherInputs && !coinControl->IsSelected(COutPoint(entry.first, i)))
@@ -2371,20 +2331,20 @@ void CWallet::AvailableCoins(interfaces::Chain::Lock& locked_chain, std::vector<
23712331
if (IsSpent(locked_chain, wtxid, i))
23722332
continue;
23732333

2374-
isminetype mine = IsMine(pcoin->tx->vout[i]);
2334+
isminetype mine = IsMine(wtx.tx->vout[i]);
23752335

23762336
if (mine == ISMINE_NO) {
23772337
continue;
23782338
}
23792339

2380-
bool solvable = IsSolvable(*this, pcoin->tx->vout[i].scriptPubKey);
2340+
bool solvable = IsSolvable(*this, wtx.tx->vout[i].scriptPubKey);
23812341
bool spendable = ((mine & ISMINE_SPENDABLE) != ISMINE_NO) || (((mine & ISMINE_WATCH_ONLY) != ISMINE_NO) && (coinControl && coinControl->fAllowWatchOnly && solvable));
23822342

2383-
vCoins.push_back(COutput(pcoin, i, nDepth, spendable, solvable, safeTx, (coinControl && coinControl->fAllowWatchOnly)));
2343+
vCoins.push_back(COutput(&wtx, i, nDepth, spendable, solvable, safeTx, (coinControl && coinControl->fAllowWatchOnly)));
23842344

23852345
// Checks the sum amount of all UTXO's.
23862346
if (nMinimumSumAmount != MAX_MONEY) {
2387-
nTotal += pcoin->tx->vout[i].nValue;
2347+
nTotal += wtx.tx->vout[i].nValue;
23882348

23892349
if (nTotal >= nMinimumSumAmount) {
23902350
return;
@@ -2542,13 +2502,13 @@ bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAm
25422502
std::map<uint256, CWalletTx>::const_iterator it = mapWallet.find(outpoint.hash);
25432503
if (it != mapWallet.end())
25442504
{
2545-
const CWalletTx* pcoin = &it->second;
2505+
const CWalletTx& wtx = it->second;
25462506
// Clearly invalid input, fail
2547-
if (pcoin->tx->vout.size() <= outpoint.n)
2507+
if (wtx.tx->vout.size() <= outpoint.n)
25482508
return false;
25492509
// Just to calculate the marginal byte size
2550-
nValueFromPresetInputs += pcoin->tx->vout[outpoint.n].nValue;
2551-
setPresetCoins.insert(CInputCoin(pcoin->tx, outpoint.n));
2510+
nValueFromPresetInputs += wtx.tx->vout[outpoint.n].nValue;
2511+
setPresetCoins.insert(CInputCoin(wtx.tx, outpoint.n));
25522512
} else
25532513
return false; // TODO: Allow non-wallet inputs
25542514
}
@@ -3586,27 +3546,27 @@ std::map<CTxDestination, CAmount> CWallet::GetAddressBalances(interfaces::Chain:
35863546
LOCK(cs_wallet);
35873547
for (const auto& walletEntry : mapWallet)
35883548
{
3589-
const CWalletTx *pcoin = &walletEntry.second;
3549+
const CWalletTx& wtx = walletEntry.second;
35903550

3591-
if (!pcoin->IsTrusted(locked_chain))
3551+
if (!wtx.IsTrusted(locked_chain))
35923552
continue;
35933553

3594-
if (pcoin->IsImmatureCoinBase(locked_chain))
3554+
if (wtx.IsImmatureCoinBase(locked_chain))
35953555
continue;
35963556

3597-
int nDepth = pcoin->GetDepthInMainChain(locked_chain);
3598-
if (nDepth < (pcoin->IsFromMe(ISMINE_ALL) ? 0 : 1))
3557+
int nDepth = wtx.GetDepthInMainChain(locked_chain);
3558+
if (nDepth < (wtx.IsFromMe(ISMINE_ALL) ? 0 : 1))
35993559
continue;
36003560

3601-
for (unsigned int i = 0; i < pcoin->tx->vout.size(); i++)
3561+
for (unsigned int i = 0; i < wtx.tx->vout.size(); i++)
36023562
{
36033563
CTxDestination addr;
3604-
if (!IsMine(pcoin->tx->vout[i]))
3564+
if (!IsMine(wtx.tx->vout[i]))
36053565
continue;
3606-
if(!ExtractDestination(pcoin->tx->vout[i].scriptPubKey, addr))
3566+
if(!ExtractDestination(wtx.tx->vout[i].scriptPubKey, addr))
36073567
continue;
36083568

3609-
CAmount n = IsSpent(locked_chain, walletEntry.first, i) ? 0 : pcoin->tx->vout[i].nValue;
3569+
CAmount n = IsSpent(locked_chain, walletEntry.first, i) ? 0 : wtx.tx->vout[i].nValue;
36103570

36113571
if (!balances.count(addr))
36123572
balances[addr] = 0;
@@ -3626,13 +3586,13 @@ std::set< std::set<CTxDestination> > CWallet::GetAddressGroupings()
36263586

36273587
for (const auto& walletEntry : mapWallet)
36283588
{
3629-
const CWalletTx *pcoin = &walletEntry.second;
3589+
const CWalletTx& wtx = walletEntry.second;
36303590

3631-
if (pcoin->tx->vin.size() > 0)
3591+
if (wtx.tx->vin.size() > 0)
36323592
{
36333593
bool any_mine = false;
36343594
// group all input addresses with each other
3635-
for (const CTxIn& txin : pcoin->tx->vin)
3595+
for (const CTxIn& txin : wtx.tx->vin)
36363596
{
36373597
CTxDestination address;
36383598
if(!IsMine(txin)) /* If this input isn't mine, ignore it */
@@ -3646,7 +3606,7 @@ std::set< std::set<CTxDestination> > CWallet::GetAddressGroupings()
36463606
// group change with input addresses
36473607
if (any_mine)
36483608
{
3649-
for (const CTxOut& txout : pcoin->tx->vout)
3609+
for (const CTxOut& txout : wtx.tx->vout)
36503610
if (IsChange(txout))
36513611
{
36523612
CTxDestination txoutAddr;
@@ -3663,7 +3623,7 @@ std::set< std::set<CTxDestination> > CWallet::GetAddressGroupings()
36633623
}
36643624

36653625
// group lone addrs by themselves
3666-
for (const auto& txout : pcoin->tx->vout)
3626+
for (const auto& txout : wtx.tx->vout)
36673627
if (IsMine(txout))
36683628
{
36693629
CTxDestination address;

src/wallet/wallet.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -952,7 +952,6 @@ class CWallet final : public CCryptoKeyStore, private interfaces::Chain::Notific
952952
CAmount GetImmatureBalance() const;
953953
CAmount GetUnconfirmedWatchOnlyBalance() const;
954954
CAmount GetImmatureWatchOnlyBalance() const;
955-
CAmount GetLegacyBalance(const isminefilter& filter, int minDepth) const;
956955
CAmount GetAvailableBalance(const CCoinControl* coinControl = nullptr) const;
957956

958957
OutputType TransactionChangeType(OutputType change_type, const std::vector<CRecipient>& vecSend);

0 commit comments

Comments
 (0)