Merge #10284: Always log debug information for fee calculation in CreateTransaction

1bebfc8 Output Fee Estimation Calculations in CreateTransaction (Alex Morcos)

Tree-SHA512: e25a27f7acbbc3a666d5d85da2554c5aaec4c923ee2fdbcfc532c29c6fbdec3c9e0d6ae6044543ecc339e7bd81df09c8d228e0b53a2c5c2dae0f1098c9453272
This commit is contained in:
Wladimir J. van der Laan 2017-06-15 13:56:16 +02:00 committed by Pasta
parent 1a75762b58
commit bfa64fd15f
No known key found for this signature in database
GPG Key ID: D362C9F7142766AE
6 changed files with 133 additions and 43 deletions

View File

@ -15,6 +15,26 @@
static constexpr double INF_FEERATE = 1e99; static constexpr double INF_FEERATE = 1e99;
std::string StringForFeeReason(FeeReason reason) {
static const std::map<FeeReason, std::string> fee_reason_strings = {
{FeeReason::NONE, "None"},
{FeeReason::HALF_ESTIMATE, "Half Target 60% Threshold"},
{FeeReason::FULL_ESTIMATE, "Target 85% Threshold"},
{FeeReason::DOUBLE_ESTIMATE, "Double Target 95% Threshold"},
{FeeReason::CONSERVATIVE, "Conservative Double Target longer horizon"},
{FeeReason::MEMPOOL_MIN, "Mempool Min Fee"},
{FeeReason::PAYTXFEE, "PayTxFee set"},
{FeeReason::FALLBACK, "Fallback fee"},
{FeeReason::REQUIRED, "Minimum Required Fee"},
{FeeReason::MAXTXFEE, "MaxTxFee limit"}
};
auto reason_string = fee_reason_strings.find(reason);
if (reason_string == fee_reason_strings.end()) return "Unknown";
return reason_string->second;
}
/** /**
* We will instantiate an instance of this class to track transactions that were * We will instantiate an instance of this class to track transactions that were
* included in a block. We will lump transactions into a bucket according to their * included in a block. We will lump transactions into a bucket according to their
@ -696,31 +716,36 @@ unsigned int CBlockPolicyEstimator::MaxUsableEstimate() const
* time horizon which tracks confirmations up to the desired target. If * time horizon which tracks confirmations up to the desired target. If
* checkShorterHorizon is requested, also allow short time horizon estimates * checkShorterHorizon is requested, also allow short time horizon estimates
* for a lower target to reduce the given answer */ * for a lower target to reduce the given answer */
double CBlockPolicyEstimator::estimateCombinedFee(unsigned int confTarget, double successThreshold, bool checkShorterHorizon) const double CBlockPolicyEstimator::estimateCombinedFee(unsigned int confTarget, double successThreshold, bool checkShorterHorizon, EstimationResult *result) const
{ {
double estimate = -1; double estimate = -1;
if (confTarget >= 1 && confTarget <= longStats->GetMaxConfirms()) { if (confTarget >= 1 && confTarget <= longStats->GetMaxConfirms()) {
// Find estimate from shortest time horizon possible // Find estimate from shortest time horizon possible
if (confTarget <= shortStats->GetMaxConfirms()) { // short horizon if (confTarget <= shortStats->GetMaxConfirms()) { // short horizon
estimate = shortStats->EstimateMedianVal(confTarget, SUFFICIENT_TXS_SHORT, successThreshold, true, nBestSeenHeight); estimate = shortStats->EstimateMedianVal(confTarget, SUFFICIENT_TXS_SHORT, successThreshold, true, nBestSeenHeight, result);
} }
else if (confTarget <= feeStats->GetMaxConfirms()) { // medium horizon else if (confTarget <= feeStats->GetMaxConfirms()) { // medium horizon
estimate = feeStats->EstimateMedianVal(confTarget, SUFFICIENT_FEETXS, successThreshold, true, nBestSeenHeight); estimate = feeStats->EstimateMedianVal(confTarget, SUFFICIENT_FEETXS, successThreshold, true, nBestSeenHeight, result);
} }
else { // long horizon else { // long horizon
estimate = longStats->EstimateMedianVal(confTarget, SUFFICIENT_FEETXS, successThreshold, true, nBestSeenHeight); estimate = longStats->EstimateMedianVal(confTarget, SUFFICIENT_FEETXS, successThreshold, true, nBestSeenHeight, result);
} }
if (checkShorterHorizon) { if (checkShorterHorizon) {
EstimationResult tempResult;
// If a lower confTarget from a more recent horizon returns a lower answer use it. // If a lower confTarget from a more recent horizon returns a lower answer use it.
if (confTarget > feeStats->GetMaxConfirms()) { if (confTarget > feeStats->GetMaxConfirms()) {
double medMax = feeStats->EstimateMedianVal(feeStats->GetMaxConfirms(), SUFFICIENT_FEETXS, successThreshold, true, nBestSeenHeight); double medMax = feeStats->EstimateMedianVal(feeStats->GetMaxConfirms(), SUFFICIENT_FEETXS, successThreshold, true, nBestSeenHeight, &tempResult);
if (medMax > 0 && (estimate == -1 || medMax < estimate)) if (medMax > 0 && (estimate == -1 || medMax < estimate)) {
estimate = medMax; estimate = medMax;
if (result) *result = tempResult;
}
} }
if (confTarget > shortStats->GetMaxConfirms()) { if (confTarget > shortStats->GetMaxConfirms()) {
double shortMax = shortStats->EstimateMedianVal(shortStats->GetMaxConfirms(), SUFFICIENT_TXS_SHORT, successThreshold, true, nBestSeenHeight); double shortMax = shortStats->EstimateMedianVal(shortStats->GetMaxConfirms(), SUFFICIENT_TXS_SHORT, successThreshold, true, nBestSeenHeight, &tempResult);
if (shortMax > 0 && (estimate == -1 || shortMax < estimate)) if (shortMax > 0 && (estimate == -1 || shortMax < estimate)) {
estimate = shortMax; estimate = shortMax;
if (result) *result = tempResult;
}
} }
} }
} }
@ -730,16 +755,18 @@ double CBlockPolicyEstimator::estimateCombinedFee(unsigned int confTarget, doubl
/** Ensure that for a conservative estimate, the DOUBLE_SUCCESS_PCT is also met /** Ensure that for a conservative estimate, the DOUBLE_SUCCESS_PCT is also met
* at 2 * target for any longer time horizons. * at 2 * target for any longer time horizons.
*/ */
double CBlockPolicyEstimator::estimateConservativeFee(unsigned int doubleTarget) const double CBlockPolicyEstimator::estimateConservativeFee(unsigned int doubleTarget, EstimationResult *result) const
{ {
double estimate = -1; double estimate = -1;
EstimationResult tempResult;
if (doubleTarget <= shortStats->GetMaxConfirms()) { if (doubleTarget <= shortStats->GetMaxConfirms()) {
estimate = feeStats->EstimateMedianVal(doubleTarget, SUFFICIENT_FEETXS, DOUBLE_SUCCESS_PCT, true, nBestSeenHeight); estimate = feeStats->EstimateMedianVal(doubleTarget, SUFFICIENT_FEETXS, DOUBLE_SUCCESS_PCT, true, nBestSeenHeight, result);
} }
if (doubleTarget <= feeStats->GetMaxConfirms()) { if (doubleTarget <= feeStats->GetMaxConfirms()) {
double longEstimate = longStats->EstimateMedianVal(doubleTarget, SUFFICIENT_FEETXS, DOUBLE_SUCCESS_PCT, true, nBestSeenHeight); double longEstimate = longStats->EstimateMedianVal(doubleTarget, SUFFICIENT_FEETXS, DOUBLE_SUCCESS_PCT, true, nBestSeenHeight, &tempResult);
if (longEstimate > estimate) { if (longEstimate > estimate) {
estimate = longEstimate; estimate = longEstimate;
if (result) *result = tempResult;
} }
} }
return estimate; return estimate;
@ -752,12 +779,15 @@ double CBlockPolicyEstimator::estimateConservativeFee(unsigned int doubleTarget)
* estimates, however, required the 95% threshold at 2 * target be met for any * estimates, however, required the 95% threshold at 2 * target be met for any
* longer time horizons also. * longer time horizons also.
*/ */
CFeeRate CBlockPolicyEstimator::estimateSmartFee(int confTarget, int *answerFoundAtTarget, const CTxMemPool& pool, bool conservative) const CFeeRate CBlockPolicyEstimator::estimateSmartFee(int confTarget, FeeCalculation *feeCalc, const CTxMemPool& pool, bool conservative) const
{ {
if (answerFoundAtTarget) if (feeCalc) {
*answerFoundAtTarget = confTarget; feeCalc->desiredTarget = confTarget;
feeCalc->returnedTarget = confTarget;
}
double median = -1; double median = -1;
EstimationResult tempResult;
{ {
LOCK(cs_feeEstimator); LOCK(cs_feeEstimator);
@ -778,7 +808,6 @@ CFeeRate CBlockPolicyEstimator::estimateSmartFee(int confTarget, int *answerFoun
} }
assert(confTarget > 0); //estimateCombinedFee and estimateConservativeFee take unsigned ints assert(confTarget > 0); //estimateCombinedFee and estimateConservativeFee take unsigned ints
/** true is passed to estimateCombined fee for target/2 and target so /** true is passed to estimateCombined fee for target/2 and target so
* that we check the max confirms for shorter time horizons as well. * that we check the max confirms for shorter time horizons as well.
* This is necessary to preserve monotonically increasing estimates. * This is necessary to preserve monotonically increasing estimates.
@ -789,32 +818,49 @@ CFeeRate CBlockPolicyEstimator::estimateSmartFee(int confTarget, int *answerFoun
* the purpose of conservative estimates is not to let short term * the purpose of conservative estimates is not to let short term
* fluctuations lower our estimates by too much. * fluctuations lower our estimates by too much.
*/ */
double halfEst = estimateCombinedFee(confTarget/2, HALF_SUCCESS_PCT, true); double halfEst = estimateCombinedFee(confTarget/2, HALF_SUCCESS_PCT, true, &tempResult);
double actualEst = estimateCombinedFee(confTarget, SUCCESS_PCT, true); if (feeCalc) {
double doubleEst = estimateCombinedFee(2 * confTarget, DOUBLE_SUCCESS_PCT, !conservative); feeCalc->est = tempResult;
feeCalc->reason = FeeReason::HALF_ESTIMATE;
}
median = halfEst; median = halfEst;
double actualEst = estimateCombinedFee(confTarget, SUCCESS_PCT, true, &tempResult);
if (actualEst > median) { if (actualEst > median) {
median = actualEst; median = actualEst;
if (feeCalc) {
feeCalc->est = tempResult;
feeCalc->reason = FeeReason::FULL_ESTIMATE;
}
} }
double doubleEst = estimateCombinedFee(2 * confTarget, DOUBLE_SUCCESS_PCT, !conservative, &tempResult);
if (doubleEst > median) { if (doubleEst > median) {
median = doubleEst; median = doubleEst;
if (feeCalc) {
feeCalc->est = tempResult;
feeCalc->reason = FeeReason::DOUBLE_ESTIMATE;
}
} }
if (conservative || median == -1) { if (conservative || median == -1) {
double consEst = estimateConservativeFee(2 * confTarget); double consEst = estimateConservativeFee(2 * confTarget, &tempResult);
if (consEst > median) { if (consEst > median) {
median = consEst; median = consEst;
if (feeCalc) {
feeCalc->est = tempResult;
feeCalc->reason = FeeReason::CONSERVATIVE;
}
} }
} }
} // Must unlock cs_feeEstimator before taking mempool locks } // Must unlock cs_feeEstimator before taking mempool locks
if (answerFoundAtTarget) if (feeCalc) feeCalc->returnedTarget = confTarget;
*answerFoundAtTarget = confTarget;
// If mempool is limiting txs , return at least the min feerate from the mempool // If mempool is limiting txs , return at least the min feerate from the mempool
CAmount minPoolFee = pool.GetMinFee(gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFeePerK(); CAmount minPoolFee = pool.GetMinFee(gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFeePerK();
if (minPoolFee > 0 && minPoolFee > median) if (minPoolFee > 0 && minPoolFee > median) {
if (feeCalc) feeCalc->reason = FeeReason::MEMPOOL_MIN;
return CFeeRate(minPoolFee); return CFeeRate(minPoolFee);
}
if (median < 0) if (median < 0)
return CFeeRate(0); return CFeeRate(0);

View File

@ -74,6 +74,22 @@ enum FeeEstimateHorizon {
LONG_HALFLIFE = 2 LONG_HALFLIFE = 2
}; };
/* Enumeration of reason for returned fee estimate */
enum class FeeReason {
NONE,
HALF_ESTIMATE,
FULL_ESTIMATE,
DOUBLE_ESTIMATE,
CONSERVATIVE,
MEMPOOL_MIN,
PAYTXFEE,
FALLBACK,
REQUIRED,
MAXTXFEE,
};
std::string StringForFeeReason(FeeReason reason);
/* Used to return detailed information about a feerate bucket */ /* Used to return detailed information about a feerate bucket */
struct EstimatorBucket struct EstimatorBucket
{ {
@ -90,8 +106,16 @@ struct EstimationResult
{ {
EstimatorBucket pass; EstimatorBucket pass;
EstimatorBucket fail; EstimatorBucket fail;
double decay; double decay = 0;
unsigned int scale; unsigned int scale = 0;
};
struct FeeCalculation
{
EstimationResult est;
FeeReason reason = FeeReason::NONE;
int desiredTarget = 0;
int returnedTarget = 0;
}; };
/** /**
@ -173,7 +197,7 @@ public:
* the closest target where one can be given. 'conservative' estimates are * the closest target where one can be given. 'conservative' estimates are
* valid over longer time horizons also. * valid over longer time horizons also.
*/ */
CFeeRate estimateSmartFee(int confTarget, int *answerFoundAtTarget, const CTxMemPool& pool, bool conservative = true) const; CFeeRate estimateSmartFee(int confTarget, FeeCalculation *feeCalc, const CTxMemPool& pool, bool conservative = true) const;
/** Return a specific fee estimate calculation with a given success /** Return a specific fee estimate calculation with a given success
* threshold and time horizon, and optionally return detailed data about * threshold and time horizon, and optionally return detailed data about
@ -223,9 +247,9 @@ private:
bool processBlockTx(unsigned int nBlockHeight, const CTxMemPoolEntry* entry); bool processBlockTx(unsigned int nBlockHeight, const CTxMemPoolEntry* entry);
/** Helper for estimateSmartFee */ /** Helper for estimateSmartFee */
double estimateCombinedFee(unsigned int confTarget, double successThreshold, bool checkShorterHorizon) const; double estimateCombinedFee(unsigned int confTarget, double successThreshold, bool checkShorterHorizon, EstimationResult *result) const;
/** Helper for estimateSmartFee */ /** Helper for estimateSmartFee */
double estimateConservativeFee(unsigned int doubleTarget) const; double estimateConservativeFee(unsigned int doubleTarget, EstimationResult *result) const;
/** Number of blocks of data recorded while fee estimates have been running */ /** Number of blocks of data recorded while fee estimates have been running */
unsigned int BlockSpan() const; unsigned int BlockSpan() const;
/** Number of blocks of recorded fee estimate data represented in saved data file */ /** Number of blocks of recorded fee estimate data represented in saved data file */

View File

@ -763,8 +763,8 @@ void SendCoinsDialog::updateSmartFeeLabel()
return; return;
int nBlocksToConfirm = getConfTargetForIndex(ui->confTargetSelector->currentIndex()); int nBlocksToConfirm = getConfTargetForIndex(ui->confTargetSelector->currentIndex());
int estimateFoundAtBlocks = nBlocksToConfirm; FeeCalculation feeCalc;
CFeeRate feeRate = ::feeEstimator.estimateSmartFee(nBlocksToConfirm, &estimateFoundAtBlocks, ::mempool); CFeeRate feeRate = ::feeEstimator.estimateSmartFee(nBlocksToConfirm, &feeCalc, ::mempool);
if (feeRate <= CFeeRate(0)) // not enough data => minfee if (feeRate <= CFeeRate(0)) // not enough data => minfee
{ {
ui->labelSmartFee->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), ui->labelSmartFee->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(),
@ -779,7 +779,7 @@ void SendCoinsDialog::updateSmartFeeLabel()
ui->labelSmartFee->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), ui->labelSmartFee->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(),
std::max(feeRate.GetFeePerK(), CWallet::GetRequiredFee(1000))) + "/kB"); 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).", "", feeCalc.returnedTarget));
ui->fallbackFeeWarningLabel->setVisible(false); ui->fallbackFeeWarningLabel->setVisible(false);
} }

View File

@ -856,10 +856,10 @@ UniValue estimatesmartfee(const JSONRPCRequest& request)
} }
UniValue result(UniValue::VOBJ); UniValue result(UniValue::VOBJ);
int answerFound; FeeCalculation feeCalc;
CFeeRate feeRate = ::feeEstimator.estimateSmartFee(nBlocks, &answerFound, ::mempool, conservative); CFeeRate feeRate = ::feeEstimator.estimateSmartFee(nBlocks, &feeCalc, ::mempool, conservative);
result.push_back(Pair("feerate", feeRate == CFeeRate(0) ? -1.0 : ValueFromAmount(feeRate.GetFeePerK()))); result.push_back(Pair("feerate", feeRate == CFeeRate(0) ? -1.0 : ValueFromAmount(feeRate.GetFeePerK())));
result.push_back(Pair("blocks", answerFound)); result.push_back(Pair("blocks", feeCalc.returnedTarget));
return result; return result;
} }

View File

@ -3549,7 +3549,8 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT
assert(txNew.nLockTime <= (unsigned int)chainActive.Height()); assert(txNew.nLockTime <= (unsigned int)chainActive.Height());
assert(txNew.nLockTime < LOCKTIME_THRESHOLD); assert(txNew.nLockTime < LOCKTIME_THRESHOLD);
FeeCalculation feeCalc;
unsigned int nBytes;
{ {
std::set<CInputCoin> setCoins; std::set<CInputCoin> setCoins;
std::vector<CTxDSIn> vecTxDSInTmp; std::vector<CTxDSIn> vecTxDSInTmp;
@ -3739,7 +3740,7 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT
nIn++; nIn++;
} }
unsigned int nBytes = ::GetSerializeSize(txNew, SER_NETWORK, PROTOCOL_VERSION); nBytes = ::GetSerializeSize(txNew, SER_NETWORK, PROTOCOL_VERSION);
if (nExtraPayloadSize != 0) { if (nExtraPayloadSize != 0) {
// account for extra payload in fee calculation // account for extra payload in fee calculation
@ -3762,7 +3763,7 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT
if (coinControl && coinControl->nConfirmTarget > 0) if (coinControl && coinControl->nConfirmTarget > 0)
currentConfirmationTarget = coinControl->nConfirmTarget; currentConfirmationTarget = coinControl->nConfirmTarget;
CAmount nFeeNeeded = GetMinimumFee(nBytes, currentConfirmationTarget, ::mempool, ::feeEstimator); CAmount nFeeNeeded = GetMinimumFee(nBytes, currentConfirmationTarget, ::mempool, ::feeEstimator, &feeCalc);
if (coinControl && coinControl->fOverrideFeeRate) if (coinControl && coinControl->fOverrideFeeRate)
nFeeNeeded = coinControl->nFeeRate.GetFee(nBytes); nFeeNeeded = coinControl->nFeeRate.GetFee(nBytes);
@ -3850,6 +3851,15 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT
return false; return false;
} }
} }
LogPrintf("Fee Calculation: Fee:%d Bytes:%u Tgt:%d (requested %d) Reason:\"%s\" Decay %.5f: Estimation: (%g - %g) %.2f%% %.1f/(%.1f %d mem %.1f out) Fail: (%g - %g) %.2f%% %.1f/(%.1f %d mem %.1f out)\n",
nFeeRet, nBytes, feeCalc.returnedTarget, feeCalc.desiredTarget, StringForFeeReason(feeCalc.reason), feeCalc.est.decay,
feeCalc.est.pass.start, feeCalc.est.pass.end,
100 * feeCalc.est.pass.withinTarget / (feeCalc.est.pass.totalConfirmed + feeCalc.est.pass.inMempool + feeCalc.est.pass.leftMempool),
feeCalc.est.pass.withinTarget, feeCalc.est.pass.totalConfirmed, feeCalc.est.pass.inMempool, feeCalc.est.pass.leftMempool,
feeCalc.est.fail.start, feeCalc.est.fail.end,
100 * feeCalc.est.fail.withinTarget / (feeCalc.est.fail.totalConfirmed + feeCalc.est.fail.inMempool + feeCalc.est.fail.leftMempool),
feeCalc.est.fail.withinTarget, feeCalc.est.fail.totalConfirmed, feeCalc.est.fail.inMempool, feeCalc.est.fail.leftMempool);
return true; return true;
} }
@ -3931,23 +3941,32 @@ CAmount CWallet::GetRequiredFee(unsigned int nTxBytes)
return std::max(minTxFee.GetFee(nTxBytes), ::minRelayTxFee.GetFee(nTxBytes)); return std::max(minTxFee.GetFee(nTxBytes), ::minRelayTxFee.GetFee(nTxBytes));
} }
CAmount CWallet::GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarget, const CTxMemPool& pool, const CBlockPolicyEstimator& estimator, bool ignoreUserSetFee) CAmount CWallet::GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarget, const CTxMemPool& pool, const CBlockPolicyEstimator& estimator, FeeCalculation *feeCalc, bool ignoreUserSetFee)
{ {
// payTxFee is the user-set global for desired feerate // payTxFee is the user-set global for desired feerate
CAmount nFeeNeeded = payTxFee.GetFee(nTxBytes); CAmount nFeeNeeded = payTxFee.GetFee(nTxBytes);
// User didn't set: use -txconfirmtarget to estimate... // User didn't set: use -txconfirmtarget to estimate...
if (nFeeNeeded == 0 || ignoreUserSetFee) { if (nFeeNeeded == 0 || ignoreUserSetFee) {
int estimateFoundTarget = nConfirmTarget; nFeeNeeded = estimator.estimateSmartFee(nConfirmTarget, feeCalc, pool, true).GetFee(nTxBytes);
nFeeNeeded = estimator.estimateSmartFee(nConfirmTarget, &estimateFoundTarget, pool).GetFee(nTxBytes);
// ... unless we don't have enough mempool data for estimatefee, then use fallbackFee // ... unless we don't have enough mempool data for estimatefee, then use fallbackFee
if (nFeeNeeded == 0) if (nFeeNeeded == 0) {
nFeeNeeded = fallbackFee.GetFee(nTxBytes); nFeeNeeded = fallbackFee.GetFee(nTxBytes);
if (feeCalc) feeCalc->reason = FeeReason::FALLBACK;
}
} else {
if (feeCalc) feeCalc->reason = FeeReason::PAYTXFEE;
} }
// prevent user from paying a fee below minRelayTxFee or minTxFee // prevent user from paying a fee below minRelayTxFee or minTxFee
nFeeNeeded = std::max(nFeeNeeded, GetRequiredFee(nTxBytes)); CAmount requiredFee = GetRequiredFee(nTxBytes);
if (requiredFee > nFeeNeeded) {
nFeeNeeded = requiredFee;
if (feeCalc) feeCalc->reason = FeeReason::REQUIRED;
}
// But always obey the maximum // But always obey the maximum
if (nFeeNeeded > maxTxFee) if (nFeeNeeded > maxTxFee) {
nFeeNeeded = maxTxFee; nFeeNeeded = maxTxFee;
if (feeCalc) feeCalc->reason = FeeReason::MAXTXFEE;
}
return nFeeNeeded; return nFeeNeeded;
} }

View File

@ -85,6 +85,7 @@ class CScheduler;
class CTxMemPool; class CTxMemPool;
class CBlockPolicyEstimator; class CBlockPolicyEstimator;
class CWalletTx; class CWalletTx;
class FeeCalculation;
/** (client) version numbers for particular wallet features */ /** (client) version numbers for particular wallet features */
enum WalletFeature enum WalletFeature
@ -1073,7 +1074,7 @@ public:
* Estimate the minimum fee considering user set parameters * Estimate the minimum fee considering user set parameters
* and the required fee * and the required fee
*/ */
static CAmount GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarget, const CTxMemPool& pool, const CBlockPolicyEstimator& estimator, bool ignoreUserSetFee = false); static CAmount GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarget, const CTxMemPool& pool, const CBlockPolicyEstimator& estimator, FeeCalculation *feeCalc = nullptr, bool ignoreUserSetFee = false);
/** /**
* Return the minimum required fee taking into account the * Return the minimum required fee taking into account the
* floating relay fee and user set minimum transaction fee * floating relay fee and user set minimum transaction fee