upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorBitflash <btf01@proton.me>2026-07-31 16:37:13 -0300
committerGitHub <noreply@github.com>2026-07-31 16:37:13 -0300
commit2d10bf257ffb177306b9cd10f7fc57f92d34b7e1 (patch)
tree70577a961c626035d9d37af47af7f4c4b5fc1cc8 /src
parent417bfed08ded5c6afaed25f4ef724b5629b13fae (diff)
wallet: stop paying the recipient the transaction fee on a fee retry (#74)
CreateTransaction kept the payee amount in nValue and then did int64 nValueOut = nValue; nValue += nFee; with both lines inside the retry loop. The first pass is fine. When the fee turns out to be too low the loop starts over, and on the second pass nValueOut is read from an nValue that already carries the fee -- so the recipient is sent the amount plus the fee, out of the sender's pocket, and it compounds on every further retry. Reachable, but it takes two things at once, which is why it has sat here: the user must have set a transaction fee in Options (with the default of zero, nValue += nFee adds nothing on the first pass and nothing is corrupted), and the transaction must be big enough for GetMinFee to demand more than that -- over 10 KB, so a wallet paying from many small inputs. Keep nValue meaning what the payee asked for and carry nValue + nFee in a separate nTotalValue, the way Bitcoin fixed it in 107d9e288. Verified by building the same transaction on both versions, with a synthetic wallet of 200 coins of 50, a configured fee of 0.05, and a request of 5,000 that needs 101 inputs and 28 KB -- enough for GetMinFee to demand 0.12 and force the retry: before payee gets 5,000.05 change 49.83 after payee gets 5,000.00 change 49.88 Two other fixes from the same era were considered and left out. Satoshi's CENT floor on the change output (496823249) exists to avoid a dust output that his fee rules then penalised; this fee schedule has no such rule, so adopting it would only donate sub-cent change to the miner. The 1-confirmation rule (a790fa46f) is a spending-policy change, not a bug fix, and deserves its own decision.
Diffstat (limited to 'src')
-rw-r--r--src/main.cpp26
1 files changed, 20 insertions, 6 deletions
diff --git a/src/main.cpp b/src/main.cpp
index c3fb33b..5610de9 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -3437,22 +3437,36 @@ bool CreateTransaction(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, in
3437 wtxNew.vout.clear(); 3437 wtxNew.vout.clear();
3438 if (nValue < 0) 3438 if (nValue < 0)
3439 return false; 3439 return false;
3440 int64 nValueOut = nValue; 3440
3441 nValue += nFee; 3441 // nValue is the amount the payee asked for and must stay that.
3442 //
3443 // This used to read "nValueOut = nValue; nValue += nFee;", and
3444 // both lines are inside the retry loop. The first pass was
3445 // fine. But when the fee turned out to be too low the loop
3446 // starts over, and on the second pass nValueOut is read from
3447 // an nValue that already had the fee added -- so the payee is
3448 // sent the amount plus the fee, and it compounds on every
3449 // further retry, with the sender covering it.
3450 //
3451 // The retry fires whenever GetMinFee exceeds the fee we
3452 // guessed, which for this fee schedule means any transaction
3453 // over 10 KB: a wallet paying from many small inputs. Bitcoin
3454 // fixed this in 107d9e288.
3455 int64 nTotalValue = nValue + nFee;
3442 3456
3443 // Choose coins to use 3457 // Choose coins to use
3444 set<CWalletTx*> setCoins; 3458 set<CWalletTx*> setCoins;
3445 if (!SelectCoins(nValue, setCoins)) 3459 if (!SelectCoins(nTotalValue, setCoins))
3446 return false; 3460 return false;
3447 int64 nValueIn = 0; 3461 int64 nValueIn = 0;
3448 foreach(CWalletTx* pcoin, setCoins) 3462 foreach(CWalletTx* pcoin, setCoins)
3449 nValueIn += pcoin->GetCredit(); 3463 nValueIn += pcoin->GetCredit();
3450 3464
3451 // Fill vout[0] to the payee 3465 // Fill vout[0] to the payee
3452 wtxNew.vout.push_back(CTxOut(nValueOut, scriptPubKey)); 3466 wtxNew.vout.push_back(CTxOut(nValue, scriptPubKey));
3453 3467
3454 // Fill vout[1] back to self with any change 3468 // Fill vout[1] back to self with any change
3455 if (nValueIn > nValue) 3469 if (nValueIn > nTotalValue)
3456 { 3470 {
3457 // Use the same key as one of the coins 3471 // Use the same key as one of the coins
3458 vector<unsigned char> vchPubKey; 3472 vector<unsigned char> vchPubKey;
@@ -3467,7 +3481,7 @@ bool CreateTransaction(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, in
3467 // Fill vout[1] to ourself 3481 // Fill vout[1] to ourself
3468 CScript scriptPubKey; 3482 CScript scriptPubKey;
3469 scriptPubKey << vchPubKey << OP_CHECKSIG; 3483 scriptPubKey << vchPubKey << OP_CHECKSIG;
3470 wtxNew.vout.push_back(CTxOut(nValueIn - nValue, scriptPubKey)); 3484 wtxNew.vout.push_back(CTxOut(nValueIn - nTotalValue, scriptPubKey));
3471 } 3485 }
3472 3486
3473 // Fill vin 3487 // Fill vin