fix(wallet): reserve miner fee during z_sendmany note selection

The Sapling note-selection loop stopped once total_value >= nTotalOut, ignoring
the miner fee, so a wallet with notes covering the amount but not amount+fee
selected too few notes and failed later with a spurious "insufficient funds".
Reserve the fee (default or user-supplied) in the selection target.

Leto eb4fc52273.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-13 16:06:39 -05:00
parent b9fdc79818
commit 14e3fb6708

View File

@@ -5298,6 +5298,12 @@ UniValue z_sendmany(const UniValue& params, bool fHelp, const CPubKey& mypk)
CAmount total_value = 0; CAmount total_value = 0;
// correctness: reserve the miner fee during note selection so we don't stop at exactly nTotalOut
// and then fail later with a spurious "insufficient funds". Mirrors the nFee computed below.
CAmount nFeeReserve = ASYNC_RPC_OPERATION_DEFAULT_MINERS_FEE;
if (params.size() > 3)
nFeeReserve = (params[3].get_real() == 0.0) ? 0 : AmountFromValue(params[3]);
std::vector<SendManyInputSaplingNote> saplingNoteInputs; std::vector<SendManyInputSaplingNote> saplingNoteInputs;
// Decide which sapling notes will be spent // Decide which sapling notes will be spent
for (const SaplingNoteEntry& entry : saplingEntries) { for (const SaplingNoteEntry& entry : saplingEntries) {
@@ -5309,8 +5315,8 @@ UniValue z_sendmany(const UniValue& params, bool fHelp, const CPubKey& mypk)
saplingNoteInputs.emplace_back(entry.op, entry.note, nValue, extsk.expsk); saplingNoteInputs.emplace_back(entry.op, entry.note, nValue, extsk.expsk);
total_value += nValue; total_value += nValue;
LogPrintf("%s: adding note to spend with value=%s, total_value=%s\n", __func__, FormatMoney(nValue), FormatMoney(total_value) ); LogPrintf("%s: adding note to spend with value=%s, total_value=%s\n", __func__, FormatMoney(nValue), FormatMoney(total_value) );
if (total_value >= nTotalOut) { if (total_value >= nTotalOut + nFeeReserve) {
// we have enough note value to make the tx // we have enough note value (incl. miner fee) to make the tx
LogPrintf("%s: found enough notes, nTotalOut=%s total_value=%s\n", __func__, FormatMoney(nTotalOut), FormatMoney(total_value) ); LogPrintf("%s: found enough notes, nTotalOut=%s total_value=%s\n", __func__, FormatMoney(nTotalOut), FormatMoney(total_value) );
break; break;
} }