From 14e3fb670801c325866d1fa7997fc8d47582f3ef Mon Sep 17 00:00:00 2001 From: DanS Date: Mon, 13 Jul 2026 16:06:39 -0500 Subject: [PATCH] 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 --- src/wallet/rpcwallet.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index e6f202713..12f9aca5b 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -5298,6 +5298,12 @@ UniValue z_sendmany(const UniValue& params, bool fHelp, const CPubKey& mypk) 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 saplingNoteInputs; // Decide which sapling notes will be spent 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); total_value += nValue; LogPrintf("%s: adding note to spend with value=%s, total_value=%s\n", __func__, FormatMoney(nValue), FormatMoney(total_value) ); - if (total_value >= nTotalOut) { - // we have enough note value to make the tx + if (total_value >= nTotalOut + nFeeReserve) { + // 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) ); break; }