|
|
|
@ -281,23 +281,25 @@ void TxConfirmStats::removeTx(unsigned int entryHeight, unsigned int nBestSeenHe
@@ -281,23 +281,25 @@ void TxConfirmStats::removeTx(unsigned int entryHeight, unsigned int nBestSeenHe
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void CBlockPolicyEstimator::removeTx(uint256 hash) |
|
|
|
|
// This function is called from CTxMemPool::removeUnchecked to ensure
|
|
|
|
|
// txs removed from the mempool for any reason are no longer
|
|
|
|
|
// tracked. Txs that were part of a block have already been removed in
|
|
|
|
|
// processBlockTx to ensure they are never double tracked, but it is
|
|
|
|
|
// of no harm to try to remove them again.
|
|
|
|
|
bool CBlockPolicyEstimator::removeTx(uint256 hash) |
|
|
|
|
{ |
|
|
|
|
std::map<uint256, TxStatsInfo>::iterator pos = mapMemPoolTxs.find(hash); |
|
|
|
|
if (pos == mapMemPoolTxs.end()) { |
|
|
|
|
LogPrint("estimatefee", "Blockpolicy error mempool tx %s not found for removeTx\n", |
|
|
|
|
hash.ToString().c_str()); |
|
|
|
|
return; |
|
|
|
|
if (pos != mapMemPoolTxs.end()) { |
|
|
|
|
feeStats.removeTx(pos->second.blockHeight, nBestSeenHeight, pos->second.bucketIndex); |
|
|
|
|
mapMemPoolTxs.erase(hash); |
|
|
|
|
return true; |
|
|
|
|
} else { |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
unsigned int entryHeight = pos->second.blockHeight; |
|
|
|
|
unsigned int bucketIndex = pos->second.bucketIndex; |
|
|
|
|
|
|
|
|
|
feeStats.removeTx(entryHeight, nBestSeenHeight, bucketIndex); |
|
|
|
|
mapMemPoolTxs.erase(hash); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
CBlockPolicyEstimator::CBlockPolicyEstimator(const CFeeRate& _minRelayFee) |
|
|
|
|
: nBestSeenHeight(0) |
|
|
|
|
: nBestSeenHeight(0), trackedTxs(0), untrackedTxs(0) |
|
|
|
|
{ |
|
|
|
|
static_assert(MIN_FEERATE > 0, "Min feerate must be nonzero"); |
|
|
|
|
minTrackedFee = _minRelayFee < CFeeRate(MIN_FEERATE) ? CFeeRate(MIN_FEERATE) : _minRelayFee; |
|
|
|
@ -309,7 +311,7 @@ CBlockPolicyEstimator::CBlockPolicyEstimator(const CFeeRate& _minRelayFee)
@@ -309,7 +311,7 @@ CBlockPolicyEstimator::CBlockPolicyEstimator(const CFeeRate& _minRelayFee)
|
|
|
|
|
feeStats.Initialize(vfeelist, MAX_BLOCK_CONFIRMS, DEFAULT_DECAY); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void CBlockPolicyEstimator::processTransaction(const CTxMemPoolEntry& entry, bool fCurrentEstimate) |
|
|
|
|
void CBlockPolicyEstimator::processTransaction(const CTxMemPoolEntry& entry, bool validFeeEstimate) |
|
|
|
|
{ |
|
|
|
|
unsigned int txHeight = entry.GetHeight(); |
|
|
|
|
uint256 hash = entry.GetTx().GetHash(); |
|
|
|
@ -319,23 +321,21 @@ void CBlockPolicyEstimator::processTransaction(const CTxMemPoolEntry& entry, boo
@@ -319,23 +321,21 @@ void CBlockPolicyEstimator::processTransaction(const CTxMemPoolEntry& entry, boo
|
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (txHeight < nBestSeenHeight) { |
|
|
|
|
if (txHeight != nBestSeenHeight) { |
|
|
|
|
// Ignore side chains and re-orgs; assuming they are random they don't
|
|
|
|
|
// affect the estimate. We'll potentially double count transactions in 1-block reorgs.
|
|
|
|
|
// Ignore txs if BlockPolicyEstimator is not in sync with chainActive.Tip().
|
|
|
|
|
// It will be synced next time a block is processed.
|
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Only want to be updating estimates when our blockchain is synced,
|
|
|
|
|
// otherwise we'll miscalculate how many blocks its taking to get included.
|
|
|
|
|
if (!fCurrentEstimate) |
|
|
|
|
return; |
|
|
|
|
|
|
|
|
|
if (!entry.WasClearAtEntry()) { |
|
|
|
|
// This transaction depends on other transactions in the mempool to
|
|
|
|
|
// be included in a block before it will be able to be included, so
|
|
|
|
|
// we shouldn't include it in our calculations
|
|
|
|
|
if (!validFeeEstimate) { |
|
|
|
|
untrackedTxs++; |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
trackedTxs++; |
|
|
|
|
|
|
|
|
|
// Feerates are stored and reported as BTC-per-kb:
|
|
|
|
|
CFeeRate feeRate(entry.GetFee(), entry.GetTxSize()); |
|
|
|
@ -344,34 +344,33 @@ void CBlockPolicyEstimator::processTransaction(const CTxMemPoolEntry& entry, boo
@@ -344,34 +344,33 @@ void CBlockPolicyEstimator::processTransaction(const CTxMemPoolEntry& entry, boo
|
|
|
|
|
mapMemPoolTxs[hash].bucketIndex = feeStats.NewTx(txHeight, (double)feeRate.GetFeePerK()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void CBlockPolicyEstimator::processBlockTx(unsigned int nBlockHeight, const CTxMemPoolEntry& entry) |
|
|
|
|
bool CBlockPolicyEstimator::processBlockTx(unsigned int nBlockHeight, const CTxMemPoolEntry* entry) |
|
|
|
|
{ |
|
|
|
|
if (!entry.WasClearAtEntry()) { |
|
|
|
|
// This transaction depended on other transactions in the mempool to
|
|
|
|
|
// be included in a block before it was able to be included, so
|
|
|
|
|
// we shouldn't include it in our calculations
|
|
|
|
|
return; |
|
|
|
|
if (!removeTx(entry->GetTx().GetHash())) { |
|
|
|
|
// This transaction wasn't being tracked for fee estimation
|
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// How many blocks did it take for miners to include this transaction?
|
|
|
|
|
// blocksToConfirm is 1-based, so a transaction included in the earliest
|
|
|
|
|
// possible block has confirmation count of 1
|
|
|
|
|
int blocksToConfirm = nBlockHeight - entry.GetHeight(); |
|
|
|
|
int blocksToConfirm = nBlockHeight - entry->GetHeight(); |
|
|
|
|
if (blocksToConfirm <= 0) { |
|
|
|
|
// This can't happen because we don't process transactions from a block with a height
|
|
|
|
|
// lower than our greatest seen height
|
|
|
|
|
LogPrint("estimatefee", "Blockpolicy error Transaction had negative blocksToConfirm\n"); |
|
|
|
|
return; |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Feerates are stored and reported as BTC-per-kb:
|
|
|
|
|
CFeeRate feeRate(entry.GetFee(), entry.GetTxSize()); |
|
|
|
|
CFeeRate feeRate(entry->GetFee(), entry->GetTxSize()); |
|
|
|
|
|
|
|
|
|
feeStats.Record(blocksToConfirm, (double)feeRate.GetFeePerK()); |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void CBlockPolicyEstimator::processBlock(unsigned int nBlockHeight, |
|
|
|
|
std::vector<CTxMemPoolEntry>& entries, bool fCurrentEstimate) |
|
|
|
|
std::vector<const CTxMemPoolEntry*>& entries) |
|
|
|
|
{ |
|
|
|
|
if (nBlockHeight <= nBestSeenHeight) { |
|
|
|
|
// Ignore side chains and re-orgs; assuming they are random
|
|
|
|
@ -381,25 +380,30 @@ void CBlockPolicyEstimator::processBlock(unsigned int nBlockHeight,
@@ -381,25 +380,30 @@ void CBlockPolicyEstimator::processBlock(unsigned int nBlockHeight,
|
|
|
|
|
// transaction fees."
|
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
nBestSeenHeight = nBlockHeight; |
|
|
|
|
|
|
|
|
|
// Only want to be updating estimates when our blockchain is synced,
|
|
|
|
|
// otherwise we'll miscalculate how many blocks its taking to get included.
|
|
|
|
|
if (!fCurrentEstimate) |
|
|
|
|
return; |
|
|
|
|
// Must update nBestSeenHeight in sync with ClearCurrent so that
|
|
|
|
|
// calls to removeTx (via processBlockTx) correctly calculate age
|
|
|
|
|
// of unconfirmed txs to remove from tracking.
|
|
|
|
|
nBestSeenHeight = nBlockHeight; |
|
|
|
|
|
|
|
|
|
// Clear the current block state
|
|
|
|
|
// Clear the current block state and update unconfirmed circular buffer
|
|
|
|
|
feeStats.ClearCurrent(nBlockHeight); |
|
|
|
|
|
|
|
|
|
unsigned int countedTxs = 0; |
|
|
|
|
// Repopulate the current block states
|
|
|
|
|
for (unsigned int i = 0; i < entries.size(); i++) |
|
|
|
|
processBlockTx(nBlockHeight, entries[i]); |
|
|
|
|
for (unsigned int i = 0; i < entries.size(); i++) { |
|
|
|
|
if (processBlockTx(nBlockHeight, entries[i])) |
|
|
|
|
countedTxs++; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Update all exponential averages with the current block state
|
|
|
|
|
feeStats.UpdateMovingAverages(); |
|
|
|
|
|
|
|
|
|
LogPrint("estimatefee", "Blockpolicy after updating estimates for %u confirmed entries, new mempool map size %u\n", |
|
|
|
|
entries.size(), mapMemPoolTxs.size()); |
|
|
|
|
LogPrint("estimatefee", "Blockpolicy after updating estimates for %u of %u txs in block, since last block %u of %u tracked, new mempool map size %u\n", |
|
|
|
|
countedTxs, entries.size(), trackedTxs, trackedTxs + untrackedTxs, mapMemPoolTxs.size()); |
|
|
|
|
|
|
|
|
|
trackedTxs = 0; |
|
|
|
|
untrackedTxs = 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
CFeeRate CBlockPolicyEstimator::estimateFee(int confTarget) |
|
|
|
|