Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fee estimate patch #6618

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
31 changes: 27 additions & 4 deletions src/policy/fees.cpp
Expand Up @@ -207,8 +207,8 @@ void TxConfirmStats::Read(CAutoFile& filein)
throw std::runtime_error("Corrupt estimates file. Mismatch in tx count bucket count");
filein >> fileConfAvg;
maxConfirms = fileConfAvg.size();
if (maxConfirms <= 0 || maxConfirms > 6 * 24 * 7) // one week
throw std::runtime_error("Corrupt estimates file. Must maintain estimates for between 1 and 1008 (one week) confirms");
if (maxConfirms <= 1 || maxConfirms > 6 * 24 * 7) // one week
throw std::runtime_error("Corrupt estimates file. Must maintain estimates for between 2 and 1008 (one week) confirms");
for (unsigned int i = 0; i < maxConfirms; i++) {
if (fileConfAvg[i].size() != numBuckets)
throw std::runtime_error("Corrupt estimates file. Mismatch in fee/pri conf average bucket count");
Expand Down Expand Up @@ -307,6 +307,7 @@ CBlockPolicyEstimator::CBlockPolicyEstimator(const CFeeRate& _minRelayFee)
vfeelist.push_back(bucketBoundary);
}
vfeelist.push_back(INF_FEERATE);
assert(MAX_BLOCK_CONFIRMS >= 2); // Have to track at least to 2 blocks
feeStats.Initialize(vfeelist, MAX_BLOCK_CONFIRMS, DEFAULT_DECAY, "FeeRate");

minTrackedPriority = AllowFreeThreshold() < MIN_PRIORITY ? MIN_PRIORITY : AllowFreeThreshold();
Expand Down Expand Up @@ -496,7 +497,17 @@ CFeeRate CBlockPolicyEstimator::estimateFee(int confTarget)
if (confTarget <= 0 || (unsigned int)confTarget > feeStats.GetMaxConfirms())
return CFeeRate(0);

double median = feeStats.EstimateMedianVal(confTarget, SUFFICIENT_FEETXS, MIN_SUCCESS_PCT, true, nBestSeenHeight);
double median = -1;
if (confTarget == 1) {
// Special case 1 with a lower success threshold, but ensure it can't give a lower estimate than 2.
median = feeStats.EstimateMedianVal(1, SUFFICIENT_FEETXS, MIN_SUCCESS_PCT_1CONF, true, nBestSeenHeight);
if (median > 0) {
median = std::max(median, feeStats.EstimateMedianVal(2, SUFFICIENT_FEETXS, MIN_SUCCESS_PCT, true, nBestSeenHeight));
}
}
else {
median = feeStats.EstimateMedianVal(confTarget, SUFFICIENT_FEETXS, MIN_SUCCESS_PCT, true, nBestSeenHeight);
}

if (median < 0)
return CFeeRate(0);
Expand All @@ -510,7 +521,19 @@ double CBlockPolicyEstimator::estimatePriority(int confTarget)
if (confTarget <= 0 || (unsigned int)confTarget > priStats.GetMaxConfirms())
return -1;

return priStats.EstimateMedianVal(confTarget, SUFFICIENT_PRITXS, MIN_SUCCESS_PCT, true, nBestSeenHeight);
double median = -1;
if (confTarget == 1) {
// Special case 1 with a lower success threshold, but ensure it can't give a lower estimate than 2.
median = priStats.EstimateMedianVal(1, SUFFICIENT_PRITXS, MIN_SUCCESS_PCT_1CONF, true, nBestSeenHeight);
if (median > 0) {
median = std::max(median, priStats.EstimateMedianVal(2, SUFFICIENT_PRITXS, MIN_SUCCESS_PCT, true, nBestSeenHeight));
}
}
else {
median = priStats.EstimateMedianVal(confTarget, SUFFICIENT_PRITXS, MIN_SUCCESS_PCT, true, nBestSeenHeight);
}

return median;
}

void CBlockPolicyEstimator::Write(CAutoFile& fileout)
Expand Down
8 changes: 6 additions & 2 deletions src/policy/fees.h
Expand Up @@ -182,8 +182,12 @@ static const unsigned int MAX_BLOCK_CONFIRMS = 25;
/** Decay of .998 is a half-life of 346 blocks or about 2.4 days */
static const double DEFAULT_DECAY = .998;

/** Require greater than 85% of X fee transactions to be confirmed within Y blocks for X to be big enough */
static const double MIN_SUCCESS_PCT = .85;
/**
* Require greater than 95% of X fee transactions to be confirmed within Y blocks
* for X to be high enough. Use 85% for target of 1.
*/
static const double MIN_SUCCESS_PCT = .95;
static const double MIN_SUCCESS_PCT_1CONF = .85;
static const double UNLIKELY_PCT = .5;

/** Require an avg of 1 tx in the combined fee bucket per block to have stat significance */
Expand Down
39 changes: 22 additions & 17 deletions src/test/policyestimator_tests.cpp
Expand Up @@ -83,11 +83,13 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates)
block.clear();
if (blocknum == 30) {
// At this point we should need to combine 5 buckets to get enough data points
// So estimateFee(1) should fail and estimateFee(2) should return somewhere around
// 8*baserate
// So estimateFee(1,2,3) should fail and estimateFee(4) should return somewhere around
// 8*baserate. estimateFee(4) %'s are 100,100,100,100,90 = average 98%
BOOST_CHECK(mpool.estimateFee(1) == CFeeRate(0));
BOOST_CHECK(mpool.estimateFee(2).GetFeePerK() < 8*baseRate.GetFeePerK() + deltaFee);
BOOST_CHECK(mpool.estimateFee(2).GetFeePerK() > 8*baseRate.GetFeePerK() - deltaFee);
BOOST_CHECK(mpool.estimateFee(2) == CFeeRate(0));
BOOST_CHECK(mpool.estimateFee(3) == CFeeRate(0));
BOOST_CHECK(mpool.estimateFee(4).GetFeePerK() < 8*baseRate.GetFeePerK() + deltaFee);
BOOST_CHECK(mpool.estimateFee(4).GetFeePerK() > 8*baseRate.GetFeePerK() - deltaFee);
}
}

Expand All @@ -96,20 +98,23 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates)
// Highest feerate is 10*baseRate and gets in all blocks,
// second highest feerate is 9*baseRate and gets in 9/10 blocks = 90%,
// third highest feerate is 8*base rate, and gets in 8/10 blocks = 80%,
// so estimateFee(1) should return 9*baseRate.
// Third highest feerate has 90% chance of being included by 2 blocks,
// so estimateFee(2) should return 8*baseRate etc...
// so estimateFee(1) should return 9*baseRate. (success thresh only 85%)
// Second highest feerate has 100% chance of being included by 2 blocks,
// so estimateFee(2) should return 9*baseRate etc...
for (int i = 1; i < 10;i++) {
origFeeEst.push_back(mpool.estimateFee(i).GetFeePerK());
origPriEst.push_back(mpool.estimatePriority(i));
if (i > 1) { // Fee estimates should be monotonically decreasing
BOOST_CHECK(origFeeEst[i-1] <= origFeeEst[i-2]);
BOOST_CHECK(origPriEst[i-1] <= origPriEst[i-2]);
}
BOOST_CHECK(origFeeEst[i-1] < (10-i)*baseRate.GetFeePerK() + deltaFee);
BOOST_CHECK(origFeeEst[i-1] > (10-i)*baseRate.GetFeePerK() - deltaFee);
BOOST_CHECK(origPriEst[i-1] < pow(10,10-i) * basepri + deltaPri);
BOOST_CHECK(origPriEst[i-1] > pow(10,10-i) * basepri - deltaPri);
int mult = 11-i;
if (i == 1)
mult = 9; // success thresh is only 85% for 1
BOOST_CHECK(origFeeEst[i-1] < mult*baseRate.GetFeePerK() + deltaFee);
BOOST_CHECK(origFeeEst[i-1] > mult*baseRate.GetFeePerK() - deltaFee);
BOOST_CHECK(origPriEst[i-1] < pow(10,mult) * basepri + deltaPri);
BOOST_CHECK(origPriEst[i-1] > pow(10,mult) * basepri - deltaPri);
}

// Mine 50 more blocks with no transactions happening, estimates shouldn't change
Expand Down Expand Up @@ -140,8 +145,8 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates)
}

for (int i = 1; i < 10;i++) {
BOOST_CHECK(mpool.estimateFee(i).GetFeePerK() > origFeeEst[i-1] - deltaFee);
BOOST_CHECK(mpool.estimatePriority(i) > origPriEst[i-1] - deltaPri);
BOOST_CHECK(mpool.estimateFee(i) == CFeeRate(0) || mpool.estimateFee(i).GetFeePerK() > origFeeEst[i-1] - deltaFee);
BOOST_CHECK(mpool.estimatePriority(i) == -1 || mpool.estimatePriority(i) > origPriEst[i-1] - deltaPri);
}

// Mine all those transactions
Expand All @@ -161,9 +166,9 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates)
BOOST_CHECK(mpool.estimatePriority(i) > origPriEst[i-1] - deltaPri);
}

// Mine 100 more blocks where everything is mined every block
// Estimates should be below original estimates (not possible for last estimate)
while (blocknum < 365) {
// Mine 200 more blocks where everything is mined every block
// Estimates should be below original estimates
while (blocknum < 465) {
for (int j = 0; j < 10; j++) { // For each fee/pri multiple
for (int k = 0; k < 5; k++) { // add 4 fee txs for every priority tx
tx.vin[0].prevout.n = 10000*blocknum+100*j+k;
Expand All @@ -177,7 +182,7 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates)
mpool.removeForBlock(block, ++blocknum, dummyConflicted);
block.clear();
}
for (int i = 1; i < 9; i++) {
for (int i = 1; i < 10; i++) {
BOOST_CHECK(mpool.estimateFee(i).GetFeePerK() < origFeeEst[i-1] - deltaFee);
BOOST_CHECK(mpool.estimatePriority(i) < origPriEst[i-1] - deltaPri);
}
Expand Down