Skip to content

Commit aa279d6

Browse files
committed
Enforce minRelayTxFee on wallet created tx and add a maxtxfee option.
Previously the minRelayTxFee was only enforced on user specified values. It was possible for smartfee to produce a fee below minRelayTxFee which would just result in the transaction getting stuck because it can't be relayed. This also introduces a maxtxfee option which sets an absolute maximum for any fee created by the wallet, with an intention of increasing user confidence that the automatic fees won't burn them. This was frequently a concern even before smartfees. If the configured fee policy won't even allow the wallet to meet the relay fee the transaction creation may be aborted.
1 parent bf4bf40 commit aa279d6

File tree

3 files changed

+48
-19
lines changed

3 files changed

+48
-19
lines changed

src/init.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,7 @@ std::string HelpMessage(HelpMessageMode mode)
288288
strUsage += " -sendfreetransactions " + strprintf(_("Send transactions as zero-fee transactions if possible (default: %u)"), 0) + "\n";
289289
strUsage += " -spendzeroconfchange " + strprintf(_("Spend unconfirmed change when sending transactions (default: %u)"), 1) + "\n";
290290
strUsage += " -txconfirmtarget=<n> " + strprintf(_("If paytxfee is not set, include enough fee so transactions are confirmed on average within n blocks (default: %u)"), 1) + "\n";
291+
strUsage += " -maxtxfee=<amt> " + strprintf(_("Maximum total fees to use in a single wallet transaction, setting too low may abort large transactions (default: %s)"), FormatMoney(maxTxFee)) + "\n";
291292
strUsage += " -upgradewallet " + _("Upgrade wallet to latest format") + " " + _("on startup") + "\n";
292293
strUsage += " -wallet=<file> " + _("Specify wallet file (within data directory)") + " " + strprintf(_("(default: %s)"), "wallet.dat") + "\n";
293294
strUsage += " -walletnotify=<cmd> " + _("Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)") + "\n";
@@ -697,6 +698,20 @@ bool AppInit2(boost::thread_group& threadGroup)
697698
mapArgs["-paytxfee"], ::minRelayTxFee.ToString()));
698699
}
699700
}
701+
if (mapArgs.count("-maxtxfee"))
702+
{
703+
CAmount nMaxFee = 0;
704+
if (!ParseMoney(mapArgs["-maxtxfee"], nMaxFee))
705+
return InitError(strprintf(_("Invalid amount for -maxtxfee=<amount>: '%s'"), mapArgs["-maptxfee"]));
706+
if (nMaxFee > nHighTransactionMaxFeeWarning)
707+
InitWarning(_("Warning: -maxtxfee is set very high! Fees this large could be paid on a single transaction."));
708+
maxTxFee = nMaxFee;
709+
if (CFeeRate(maxTxFee, 1000) < ::minRelayTxFee)
710+
{
711+
return InitError(strprintf(_("Invalid amount for -maxtxfee=<amount>: '%s' (must be at least the minrelay fee of %s to prevent stuck transactions)"),
712+
mapArgs["-maxtxfee"], ::minRelayTxFee.ToString()));
713+
}
714+
}
700715
nTxConfirmTarget = GetArg("-txconfirmtarget", 1);
701716
bSpendZeroConfChange = GetArg("-spendzeroconfchange", true);
702717
fSendFreeTransactions = GetArg("-sendfreetransactions", false);

src/wallet.cpp

Lines changed: 28 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ using namespace std;
2626
* Settings
2727
*/
2828
CFeeRate payTxFee(DEFAULT_TRANSACTION_FEE);
29+
CAmount maxTxFee = DEFAULT_TRANSACTION_MAXFEE;
2930
unsigned int nTxConfirmTarget = 1;
3031
bool bSpendZeroConfChange = true;
3132
bool fSendFreeTransactions = false;
@@ -1499,27 +1500,32 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, CAmount> >& vecSend,
14991500
}
15001501
dPriority = wtxNew.ComputePriority(dPriority, nBytes);
15011502

1502-
CAmount nFeeNeeded = GetMinimumFee(nBytes, nTxConfirmTarget, mempool);
1503+
// Can we complete this as a free transaction?
1504+
if (fSendFreeTransactions && nBytes <= MAX_FREE_TRANSACTION_CREATE_SIZE)
1505+
{
1506+
// Not enough fee: enough priority?
1507+
double dPriorityNeeded = mempool.estimatePriority(nTxConfirmTarget);
1508+
// Not enough mempool history to estimate: use hard-coded AllowFree.
1509+
if (dPriorityNeeded <= 0 && AllowFree(dPriority))
1510+
break;
1511+
1512+
// Small enough, and priority high enough, to send for free
1513+
if (dPriorityNeeded > 0 && dPriority >= dPriorityNeeded)
1514+
break;
1515+
}
15031516

1504-
if (nFeeRet >= nFeeNeeded)
1505-
break; // Done, enough fee included.
1517+
CAmount nFeeNeeded = GetMinimumFee(nBytes, nTxConfirmTarget, mempool);
15061518

1507-
// Too big to send for free? Include more fee and try again:
1508-
if (!fSendFreeTransactions || nBytes > MAX_FREE_TRANSACTION_CREATE_SIZE)
1519+
// If we made it here and we aren't even able to meet the relay fee on the next pass, give up
1520+
// because we must be at the maximum allowed fee.
1521+
if (nFeeNeeded < ::minRelayTxFee.GetFee(nBytes))
15091522
{
1510-
nFeeRet = nFeeNeeded;
1511-
continue;
1523+
strFailReason = _("Transaction too large for fee policy");
1524+
return false;
15121525
}
15131526

1514-
// Not enough fee: enough priority?
1515-
double dPriorityNeeded = mempool.estimatePriority(nTxConfirmTarget);
1516-
// Not enough mempool history to estimate: use hard-coded AllowFree.
1517-
if (dPriorityNeeded <= 0 && AllowFree(dPriority))
1518-
break;
1519-
1520-
// Small enough, and priority high enough, to send for free
1521-
if (dPriorityNeeded > 0 && dPriority >= dPriorityNeeded)
1522-
break;
1527+
if (nFeeRet >= nFeeNeeded)
1528+
break; // Done, enough fee included.
15231529

15241530
// Include more fee and try again.
15251531
nFeeRet = nFeeNeeded;
@@ -1591,9 +1597,6 @@ CAmount CWallet::GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarge
15911597
{
15921598
// payTxFee is user-set "I want to pay this much"
15931599
CAmount nFeeNeeded = payTxFee.GetFee(nTxBytes);
1594-
// prevent user from paying a non-sense fee (like 1 satoshi): 0 < fee < minRelayFee
1595-
if (nFeeNeeded > 0 && nFeeNeeded < ::minRelayTxFee.GetFee(nTxBytes))
1596-
nFeeNeeded = ::minRelayTxFee.GetFee(nTxBytes);
15971600
// user selected total at least (default=true)
15981601
if (fPayAtLeastCustomFee && nFeeNeeded > 0 && nFeeNeeded < payTxFee.GetFeePerK())
15991602
nFeeNeeded = payTxFee.GetFeePerK();
@@ -1604,6 +1607,12 @@ CAmount CWallet::GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarge
16041607
// back to a hard-coded fee
16051608
if (nFeeNeeded == 0)
16061609
nFeeNeeded = minTxFee.GetFee(nTxBytes);
1610+
// prevent user from paying a non-sense fee (like 1 satoshi): 0 < fee < minRelayFee
1611+
if (nFeeNeeded < ::minRelayTxFee.GetFee(nTxBytes))
1612+
nFeeNeeded = ::minRelayTxFee.GetFee(nTxBytes);
1613+
// But always obey the maximum
1614+
if (nFeeNeeded > maxTxFee)
1615+
nFeeNeeded = maxTxFee;
16071616
return nFeeNeeded;
16081617
}
16091618

src/wallet.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
* Settings
3131
*/
3232
extern CFeeRate payTxFee;
33+
extern CAmount maxTxFee;
3334
extern unsigned int nTxConfirmTarget;
3435
extern bool bSpendZeroConfChange;
3536
extern bool fSendFreeTransactions;
@@ -39,6 +40,10 @@ extern bool fPayAtLeastCustomFee;
3940
static const CAmount DEFAULT_TRANSACTION_FEE = 0;
4041
//! -paytxfee will warn if called with a higher fee than this amount (in satoshis) per KB
4142
static const CAmount nHighTransactionFeeWarning = 0.01 * COIN;
43+
//! -maxtxfee default
44+
static const CAmount DEFAULT_TRANSACTION_MAXFEE = 0.1 * COIN;
45+
//! -maxtxfee will warn if called with a higher fee than this amount (in satoshis)
46+
static const CAmount nHighTransactionMaxFeeWarning = 100 * nHighTransactionFeeWarning;
4247
//! Largest (in bytes) free transaction we're willing to create
4348
static const unsigned int MAX_FREE_TRANSACTION_CREATE_SIZE = 1000;
4449

0 commit comments

Comments
 (0)