Merge pull request #7296

bebe58b SQUASHME: Fix rpc tests that assumed fallback to minRelayTxFee (Alex Morcos)
e420a1b Add sane fallback for fee estimation (Alex Morcos)
995b9f3 Always respect GetRequiredFee for wallet txs (Alex Morcos)
This commit is contained in:
Wladimir J. van der Laan 2016-01-13 11:04:18 +01:00
commit c49551886a
No known key found for this signature in database
GPG Key ID: 74810B012346C9A6
6 changed files with 40 additions and 9 deletions

View File

@ -30,6 +30,11 @@ class RawTransactionsTest(BitcoinTestFramework):
print "Mining blocks..." print "Mining blocks..."
min_relay_tx_fee = self.nodes[0].getnetworkinfo()['relayfee'] min_relay_tx_fee = self.nodes[0].getnetworkinfo()['relayfee']
# This test is not meant to test fee estimation and we'd like
# to be sure all txs are sent at a consistent desired feerate
for node in self.nodes:
node.settxfee(min_relay_tx_fee)
# if the fee's positive delta is higher than this value tests will fail, # if the fee's positive delta is higher than this value tests will fail,
# neg. delta always fail the tests. # neg. delta always fail the tests.
# The size of the signature of every input may be at most 2 bytes larger # The size of the signature of every input may be at most 2 bytes larger
@ -447,6 +452,10 @@ class RawTransactionsTest(BitcoinTestFramework):
wait_bitcoinds() wait_bitcoinds()
self.nodes = start_nodes(4, self.options.tmpdir) self.nodes = start_nodes(4, self.options.tmpdir)
# This test is not meant to test fee estimation and we'd like
# to be sure all txs are sent at a consistent desired feerate
for node in self.nodes:
node.settxfee(min_relay_tx_fee)
connect_nodes_bi(self.nodes,0,1) connect_nodes_bi(self.nodes,0,1)
connect_nodes_bi(self.nodes,1,2) connect_nodes_bi(self.nodes,1,2)

View File

@ -33,7 +33,9 @@ class MempoolLimitTest(BitcoinTestFramework):
inputs = [{ "txid" : us0["txid"], "vout" : us0["vout"]}] inputs = [{ "txid" : us0["txid"], "vout" : us0["vout"]}]
outputs = {self.nodes[0].getnewaddress() : 0.0001} outputs = {self.nodes[0].getnewaddress() : 0.0001}
tx = self.nodes[0].createrawtransaction(inputs, outputs) tx = self.nodes[0].createrawtransaction(inputs, outputs)
self.nodes[0].settxfee(self.relayfee) # specifically fund this tx with low fee
txF = self.nodes[0].fundrawtransaction(tx) txF = self.nodes[0].fundrawtransaction(tx)
self.nodes[0].settxfee(0) # return to automatic fee selection
txFS = self.nodes[0].signrawtransaction(txF['hex']) txFS = self.nodes[0].signrawtransaction(txF['hex'])
txid = self.nodes[0].sendrawtransaction(txFS['hex']) txid = self.nodes[0].sendrawtransaction(txFS['hex'])
self.nodes[0].lockunspent(True, [us0]) self.nodes[0].lockunspent(True, [us0])

View File

@ -393,6 +393,8 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += HelpMessageGroup(_("Wallet options:")); strUsage += HelpMessageGroup(_("Wallet options:"));
strUsage += HelpMessageOpt("-disablewallet", _("Do not load the wallet and disable wallet RPC calls")); strUsage += HelpMessageOpt("-disablewallet", _("Do not load the wallet and disable wallet RPC calls"));
strUsage += HelpMessageOpt("-keypool=<n>", strprintf(_("Set key pool size to <n> (default: %u)"), DEFAULT_KEYPOOL_SIZE)); strUsage += HelpMessageOpt("-keypool=<n>", strprintf(_("Set key pool size to <n> (default: %u)"), DEFAULT_KEYPOOL_SIZE));
strUsage += HelpMessageOpt("-fallbackfee=<amt>", strprintf(_("A fee rate (in %s/kB) that will be used when fee estimation has insufficient data (default: %s)"),
CURRENCY_UNIT, FormatMoney(DEFAULT_FALLBACK_FEE)));
strUsage += HelpMessageOpt("-mintxfee=<amt>", strprintf(_("Fees (in %s/kB) smaller than this are considered zero fee for transaction creation (default: %s)"), strUsage += HelpMessageOpt("-mintxfee=<amt>", strprintf(_("Fees (in %s/kB) smaller than this are considered zero fee for transaction creation (default: %s)"),
CURRENCY_UNIT, FormatMoney(DEFAULT_TRANSACTION_MINFEE))); CURRENCY_UNIT, FormatMoney(DEFAULT_TRANSACTION_MINFEE)));
strUsage += HelpMessageOpt("-paytxfee=<amt>", strprintf(_("Fee (in %s/kB) to add to transactions you send (default: %s)"), strUsage += HelpMessageOpt("-paytxfee=<amt>", strprintf(_("Fee (in %s/kB) to add to transactions you send (default: %s)"),
@ -959,6 +961,15 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
else else
return InitError(AmountErrMsg("mintxfee", mapArgs["-mintxfee"])); return InitError(AmountErrMsg("mintxfee", mapArgs["-mintxfee"]));
} }
if (mapArgs.count("-fallbackfee"))
{
CAmount nFeePerK = 0;
if (!ParseMoney(mapArgs["-fallbackfee"], nFeePerK))
return InitError(strprintf(_("Invalid amount for -fallbackfee=<amount>: '%s'"), mapArgs["-fallbackfee"]));
if (nFeePerK > nHighTransactionFeeWarning)
InitWarning(_("-fallbackfee is set very high! This is the transaction fee you may pay when fee estimates are not available."));
CWallet::fallbackFee = CFeeRate(nFeePerK);
}
if (mapArgs.count("-paytxfee")) if (mapArgs.count("-paytxfee"))
{ {
CAmount nFeePerK = 0; CAmount nFeePerK = 0;

View File

@ -640,13 +640,15 @@ void SendCoinsDialog::updateSmartFeeLabel()
CFeeRate feeRate = mempool.estimateSmartFee(nBlocksToConfirm, &estimateFoundAtBlocks); CFeeRate feeRate = mempool.estimateSmartFee(nBlocksToConfirm, &estimateFoundAtBlocks);
if (feeRate <= CFeeRate(0)) // not enough data => minfee if (feeRate <= CFeeRate(0)) // not enough data => minfee
{ {
ui->labelSmartFee->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), CWallet::GetRequiredFee(1000)) + "/kB"); ui->labelSmartFee->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(),
std::max(CWallet::fallbackFee.GetFeePerK(), CWallet::GetRequiredFee(1000))) + "/kB");
ui->labelSmartFee2->show(); // (Smart fee not initialized yet. This usually takes a few blocks...) ui->labelSmartFee2->show(); // (Smart fee not initialized yet. This usually takes a few blocks...)
ui->labelFeeEstimation->setText(""); ui->labelFeeEstimation->setText("");
} }
else else
{ {
ui->labelSmartFee->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), feeRate.GetFeePerK()) + "/kB"); ui->labelSmartFee->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(),
std::max(feeRate.GetFeePerK(), CWallet::GetRequiredFee(1000))) + "/kB");
ui->labelSmartFee2->hide(); ui->labelSmartFee2->hide();
ui->labelFeeEstimation->setText(tr("Estimated to begin confirmation within %n block(s).", "", estimateFoundAtBlocks)); ui->labelFeeEstimation->setText(tr("Estimated to begin confirmation within %n block(s).", "", estimateFoundAtBlocks));
} }

View File

@ -47,6 +47,12 @@ bool fSendFreeTransactions = DEFAULT_SEND_FREE_TRANSACTIONS;
* Override with -mintxfee * Override with -mintxfee
*/ */
CFeeRate CWallet::minTxFee = CFeeRate(DEFAULT_TRANSACTION_MINFEE); CFeeRate CWallet::minTxFee = CFeeRate(DEFAULT_TRANSACTION_MINFEE);
/**
* If fee estimation does not have enough data to provide estimates, use this fee instead.
* Has no effect if not using fee estimation
* Override with -fallbackfee
*/
CFeeRate CWallet::fallbackFee = CFeeRate(DEFAULT_FALLBACK_FEE);
/** @defgroup mapWallet /** @defgroup mapWallet
* *
@ -2230,14 +2236,12 @@ CAmount CWallet::GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarge
if (nFeeNeeded == 0) { if (nFeeNeeded == 0) {
int estimateFoundTarget = nConfirmTarget; int estimateFoundTarget = nConfirmTarget;
nFeeNeeded = pool.estimateSmartFee(nConfirmTarget, &estimateFoundTarget).GetFee(nTxBytes); nFeeNeeded = pool.estimateSmartFee(nConfirmTarget, &estimateFoundTarget).GetFee(nTxBytes);
// ... unless we don't have enough mempool data for our desired target // ... unless we don't have enough mempool data for estimatefee, then use fallbackFee
// so we make sure we're paying at least minTxFee if (nFeeNeeded == 0)
if (nFeeNeeded == 0 || (unsigned int)estimateFoundTarget > nConfirmTarget) nFeeNeeded = fallbackFee.GetFee(nTxBytes);
nFeeNeeded = std::max(nFeeNeeded, GetRequiredFee(nTxBytes));
} }
// prevent user from paying a non-sense fee (like 1 satoshi): 0 < fee < minRelayFee // prevent user from paying a fee below minRelayTxFee or minTxFee
if (nFeeNeeded < ::minRelayTxFee.GetFee(nTxBytes)) nFeeNeeded = std::max(nFeeNeeded, GetRequiredFee(nTxBytes));
nFeeNeeded = ::minRelayTxFee.GetFee(nTxBytes);
// But always obey the maximum // But always obey the maximum
if (nFeeNeeded > maxTxFee) if (nFeeNeeded > maxTxFee)
nFeeNeeded = maxTxFee; nFeeNeeded = maxTxFee;

View File

@ -41,6 +41,8 @@ static const unsigned int DEFAULT_KEYPOOL_SIZE = 100;
static const CAmount DEFAULT_TRANSACTION_FEE = 0; static const CAmount DEFAULT_TRANSACTION_FEE = 0;
//! -paytxfee will warn if called with a higher fee than this amount (in satoshis) per KB //! -paytxfee will warn if called with a higher fee than this amount (in satoshis) per KB
static const CAmount nHighTransactionFeeWarning = 0.01 * COIN; static const CAmount nHighTransactionFeeWarning = 0.01 * COIN;
//! -fallbackfee default
static const CAmount DEFAULT_FALLBACK_FEE = 20000;
//! -mintxfee default //! -mintxfee default
static const CAmount DEFAULT_TRANSACTION_MINFEE = 1000; static const CAmount DEFAULT_TRANSACTION_MINFEE = 1000;
//! -maxtxfee default //! -maxtxfee default
@ -745,6 +747,7 @@ public:
bool AddAccountingEntry(const CAccountingEntry&, CWalletDB & pwalletdb); bool AddAccountingEntry(const CAccountingEntry&, CWalletDB & pwalletdb);
static CFeeRate minTxFee; static CFeeRate minTxFee;
static CFeeRate fallbackFee;
/** /**
* Estimate the minimum fee considering user set parameters * Estimate the minimum fee considering user set parameters
* and the required fee * and the required fee