This PR is partly a refactor, partly bug fix, and partly improved robustness in edge cases. Outside of the edge cases (when your node is behind) the results are unchanged.
Explanations of the changes in the individual commits.
This PR is partly a refactor, partly bug fix, and partly improved robustness in edge cases. Outside of the edge cases (when your node is behind) the results are unchanged.
Explanations of the changes in the individual commits.
1179 | + return false; 1180 | + if (chainActive.Tip()->GetBlockTime() < (GetTime() - MAX_FEE_ESTIMATION_TIP_AGE)) 1181 | + return false; 1182 | + if (chainActive.Height() < pindexBestHeader->nHeight - 1) 1183 | + return false; 1184 | + return true;
nit - usually a blank line before this line (but probably not a standard)
352 | 353 | // How many blocks did it take for miners to include this transaction? 354 | // blocksToConfirm is 1-based, so a transaction included in the earliest 355 | // possible block has confirmation count of 1 356 | - int blocksToConfirm = nBlockHeight - entry.GetHeight(); 357 | + int blocksToConfirm = nBlockHeight - entry->GetHeight();
what did this fix please?
Please see commit message "Pass pointers to existing CTxMemPoolEntries to fee estimation" The idea is this would save unnecessary copying.
rebased
Concept ACK.
Ran this code on top of master on one of my nodes. Just compared it to the node running master (same network but independent mempool), see below. The differences for a conf. target of 2 blocks are quite large.
Next step would be to run master and this PR with the same mempool and maybe dump 2,4,8,20 target every ~10mins.
masternode ~/node/bitcoin $ ./src/bitcoin-cli getmempoolinfo
{
"size": 22022,
"bytes": 133099706,
"usage": 252978016,
"maxmempool": 300000000,
"mempoolminfee": 0.00001014
}
masternode ~/node/bitcoin $ ./src/bitcoin-cli estimatesmartfee 2
{
"feerate": 0.00761447,
"blocks": 2
}
masternode ~/node/bitcoin $ ./src/bitcoin-cli estimatesmartfee 3
{
"feerate": 0.00069881,
"blocks": 3
}
masternode ~/node/bitcoin $ ./src/bitcoin-cli estimatesmartfee 8
{
"feerate": 0.00069881,
"blocks": 8
}
thisprnode ~/bitcoin/bitcoin $ ./src/bitcoin-cli getmempoolinfo
{
"size": 24593,
"bytes": 140534928,
"usage": 268029424,
"maxmempool": 300000000,
"mempoolminfee": 0.00001000
}
thisprnode ~/bitcoin/bitcoin $ ./src/bitcoin-cli estimatesmartfee 2
{
"feerate": 0.00083575,
"blocks": 2
}
thisprnode ~/bitcoin/bitcoin $ ./src/bitcoin-cli estimatesmartfee 3
{
"feerate": 0.00077770,
"blocks": 3
}
thisprnode ~/bitcoin/bitcoin $ ./src/bitcoin-cli estimatesmartfee 8
{
"feerate": 0.00069872,
"blocks": 8
}
@jonasschnelli I simulated this PR's code against master and the differences were very slight. If your testing setups were identical (started with the same fee estimates and started the nodes at the same time), then the way you might see substantial differences is if your testing nodes have to catch up a little bit when you start them, then this code will decay your old estimates and the old code wouldn't. So this code will more quickly reflect current conditions instead of still having a heavy weight for old fee estimates. Furthermore if you started this code after being down for more than a few weeks, you might even start seeing messages about fee estimates need to warm up as the historical data will have decayed so much.
381 | @@ -392,6 +382,9 @@ void CBlockPolicyEstimator::processBlock(unsigned int nBlockHeight, 382 | for (unsigned int i = 0; i < entries.size(); i++) 383 | processBlockTx(nBlockHeight, entries[i]); 384 | 385 | + // Update best seen height after removals so they find the right mempool txs
Can you clarify this comment? I have been staring at this for a while and can't figure out what you meant.
127 | @@ -128,6 +128,8 @@ static const int64_t BLOCK_DOWNLOAD_TIMEOUT_PER_PEER = 500000; 128 | static const unsigned int DEFAULT_LIMITFREERELAY = 0; 129 | static const bool DEFAULT_RELAYPRIORITY = true; 130 | static const int64_t DEFAULT_MAX_TIP_AGE = 24 * 60 * 60; 131 | +/** Maximum age of our tip for us to be considered current for fee estimation */
nit: Can you update the comment to include the unit (ie seconds)?
Code review ACK. Will test.
335 | @@ -343,10 +336,8 @@ void CBlockPolicyEstimator::processTransaction(const CTxMemPoolEntry& entry, boo 336 | 337 | void CBlockPolicyEstimator::processBlockTx(unsigned int nBlockHeight, const CTxMemPoolEntry& entry) 338 | { 339 | - if (!entry.WasClearAtEntry()) { 340 | - // This transaction depended on other transactions in the mempool to 341 | - // be included in a block before it was able to be included, so 342 | - // we shouldn't include it in our calculations 343 | + if (!removeTx(entry.GetTx().GetHash())) {
Does calling this here result in one extra call to removeTx() for each in-mempool, in-block transaction? It looks like we also will call this in CTxMempool::removeUnchecked, which I guess is harmless, but worth commenting.
1172 | @@ -1173,6 +1173,17 @@ std::string FormatStateMessage(const CValidationState &state) 1173 | state.GetRejectCode()); 1174 | } 1175 | 1176 | +static bool IsCurrentForFeeEstimation() 1177 | +{ 1178 | + if (IsInitialBlockDownload()) 1179 | + return false; 1180 | + if (chainActive.Tip()->GetBlockTime() < (GetTime() - MAX_FEE_ESTIMATION_TIP_AGE))
nit: I think we should AssertLockHeld(cs_main) here.
Tested this code in simulation on 10 days in July, comparing the transactions tracked and the fee estimates returned before and after this PR. ACK apart from the AssertLockHeld nit above.
re-ACK 98fffcb
Simple rebase due to expected conflict on function signature of CTxMemPool:removeForBlock
re-ACK 761a9be
292 | + if (pos != mapMemPoolTxs.end()) { 293 | + feeStats.removeTx(pos->second.blockHeight, nBestSeenHeight, pos->second.bucketIndex); 294 | + mapMemPoolTxs.erase(hash); 295 | + return true; 296 | + } 297 | + else {
Coding style nit: else on the same line.
601 | uint256 hash = tx->GetHash(); 602 | 603 | indexed_transaction_set::iterator i = mapTx.find(hash); 604 | if (i != mapTx.end()) 605 | - entries.push_back(*i); 606 | + entries.push_back(&(*i));
Parentheses aren't needed here.
Code review ACK. I don't understand the fee estimation code enough to judge the changes to the semantics.
reviewed and tested this before it got conflicted ACK. needs rebase.
Once priority estimation was removed, not all transactions in the mempool are tracked in the fee estimation mempool tracking. So there is no error if a transaction is not found for removal.
This was an oversight, where blocks and mempool tracking were ignored during IBD, but transactions that arrived during IBD but were included in blocks after IBD were not ignored.
Fee estimation can just check its own mapMemPoolTxs to determine the same information. Note that now fee estimation for block processing must happen before those transactions are removed, but this shoudl be a speedup.
All decisions about whether the transactions are valid data points are made at the time the transaction arrives. Updating on blocks all the time will now cause stale fee estimates to decay quickly when we restart a node.
Make a more conservative notion of whether the node is caught up to the rest of the network and only count transactions as fee estimation data points if the node is caught up.
rebased
re-ACK 44b64b933dec10f5ffcd7f34e7bf5e4ed97f671e
utACK 44b64b933dec10f5ffcd7f34e7bf5e4ed97f671e
Milestone
0.14.0