Fix mempool package tracking edge case
CalculateMemPoolAncestors was always looping over a transaction's inputs to find in-mempool parents. When adding a new transaction, this is the correct behavior, but when removing a transaction, we want to use the ancestor set that would be calculated by walking mapLinks (which should in general be the same set, except during a reorg when the mempool is in an inconsistent state, and the mapLinks-based calculation would be the correct one).
This commit is contained in:
parent
598b25d5ee
commit
60de0d5826
@ -159,26 +159,30 @@ void CTxMemPool::UpdateTransactionsFromBlock(const std::vector<uint256> &vHashes
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CTxMemPool::CalculateMemPoolAncestors(const CTxMemPoolEntry &entry, setEntries &setAncestors, uint64_t limitAncestorCount, uint64_t limitAncestorSize, uint64_t limitDescendantCount, uint64_t limitDescendantSize, std::string &errString)
|
bool CTxMemPool::CalculateMemPoolAncestors(const CTxMemPoolEntry &entry, setEntries &setAncestors, uint64_t limitAncestorCount, uint64_t limitAncestorSize, uint64_t limitDescendantCount, uint64_t limitDescendantSize, std::string &errString, bool fSearchForParents /* = true */)
|
||||||
{
|
{
|
||||||
setEntries parentHashes;
|
setEntries parentHashes;
|
||||||
const CTransaction &tx = entry.GetTx();
|
const CTransaction &tx = entry.GetTx();
|
||||||
|
|
||||||
// Get parents of this transaction that are in the mempool
|
if (fSearchForParents) {
|
||||||
// Entry may or may not already be in the mempool, and GetMemPoolParents()
|
// Get parents of this transaction that are in the mempool
|
||||||
// is only valid for entries in the mempool, so we iterate mapTx to find
|
// GetMemPoolParents() is only valid for entries in the mempool, so we
|
||||||
// parents.
|
// iterate mapTx to find parents.
|
||||||
// TODO: optimize this so that we only check limits and walk
|
for (unsigned int i = 0; i < tx.vin.size(); i++) {
|
||||||
// tx.vin when called on entries not already in the mempool.
|
txiter piter = mapTx.find(tx.vin[i].prevout.hash);
|
||||||
for (unsigned int i = 0; i < tx.vin.size(); i++) {
|
if (piter != mapTx.end()) {
|
||||||
txiter piter = mapTx.find(tx.vin[i].prevout.hash);
|
parentHashes.insert(piter);
|
||||||
if (piter != mapTx.end()) {
|
if (parentHashes.size() + 1 > limitAncestorCount) {
|
||||||
parentHashes.insert(piter);
|
errString = strprintf("too many unconfirmed parents [limit: %u]", limitAncestorCount);
|
||||||
if (parentHashes.size() + 1 > limitAncestorCount) {
|
return false;
|
||||||
errString = strprintf("too many unconfirmed parents [limit: %u]", limitAncestorCount);
|
}
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// If we're not searching for parents, we require this to be an
|
||||||
|
// entry in the mempool already.
|
||||||
|
txiter it = mapTx.iterator_to(entry);
|
||||||
|
parentHashes = GetMemPoolParents(it);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t totalSizeWithAncestors = entry.GetTxSize();
|
size_t totalSizeWithAncestors = entry.GetTxSize();
|
||||||
@ -249,7 +253,24 @@ void CTxMemPool::UpdateForRemoveFromMempool(const setEntries &entriesToRemove)
|
|||||||
setEntries setAncestors;
|
setEntries setAncestors;
|
||||||
const CTxMemPoolEntry &entry = *removeIt;
|
const CTxMemPoolEntry &entry = *removeIt;
|
||||||
std::string dummy;
|
std::string dummy;
|
||||||
CalculateMemPoolAncestors(entry, setAncestors, nNoLimit, nNoLimit, nNoLimit, nNoLimit, dummy);
|
// Since this is a tx that is already in the mempool, we can call CMPA
|
||||||
|
// with fSearchForParents = false. If the mempool is in a consistent
|
||||||
|
// state, then using true or false should both be correct, though false
|
||||||
|
// should be a bit faster.
|
||||||
|
// However, if we happen to be in the middle of processing a reorg, then
|
||||||
|
// the mempool can be in an inconsistent state. In this case, the set
|
||||||
|
// of ancestors reachable via mapLinks will be the same as the set of
|
||||||
|
// ancestors whose packages include this transaction, because when we
|
||||||
|
// add a new transaction to the mempool in addUnchecked(), we assume it
|
||||||
|
// has no children, and in the case of a reorg where that assumption is
|
||||||
|
// false, the in-mempool children aren't linked to the in-block tx's
|
||||||
|
// until UpdateTransactionsFromBlock() is called.
|
||||||
|
// So if we're being called during a reorg, ie before
|
||||||
|
// UpdateTransactionsFromBlock() has been called, then mapLinks[] will
|
||||||
|
// differ from the set of mempool parents we'd calculate by searching,
|
||||||
|
// and it's important that we use the mapLinks[] notion of ancestor
|
||||||
|
// transactions as the set of things to update for removal.
|
||||||
|
CalculateMemPoolAncestors(entry, setAncestors, nNoLimit, nNoLimit, nNoLimit, nNoLimit, dummy, false);
|
||||||
// Note that UpdateAncestorsOf severs the child links that point to
|
// Note that UpdateAncestorsOf severs the child links that point to
|
||||||
// removeIt in the entries for the parents of removeIt. This is
|
// removeIt in the entries for the parents of removeIt. This is
|
||||||
// fine since we don't need to use the mempool children of any entries
|
// fine since we don't need to use the mempool children of any entries
|
||||||
|
@ -392,8 +392,10 @@ public:
|
|||||||
* limitDescendantCount = max number of descendants any ancestor can have
|
* limitDescendantCount = max number of descendants any ancestor can have
|
||||||
* limitDescendantSize = max size of descendants any ancestor can have
|
* limitDescendantSize = max size of descendants any ancestor can have
|
||||||
* errString = populated with error reason if any limits are hit
|
* errString = populated with error reason if any limits are hit
|
||||||
|
* fSearchForParents = whether to search a tx's vin for in-mempool parents, or
|
||||||
|
* look up parents from mapLinks. Must be true for entries not in the mempool
|
||||||
*/
|
*/
|
||||||
bool CalculateMemPoolAncestors(const CTxMemPoolEntry &entry, setEntries &setAncestors, uint64_t limitAncestorCount, uint64_t limitAncestorSize, uint64_t limitDescendantCount, uint64_t limitDescendantSize, std::string &errString);
|
bool CalculateMemPoolAncestors(const CTxMemPoolEntry &entry, setEntries &setAncestors, uint64_t limitAncestorCount, uint64_t limitAncestorSize, uint64_t limitDescendantCount, uint64_t limitDescendantSize, std::string &errString, bool fSearchForParents = true);
|
||||||
|
|
||||||
unsigned long size()
|
unsigned long size()
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user