aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/main.cpp2
-rw-r--r--src/txmempool.cpp88
-rw-r--r--src/txmempool.h49
3 files changed, 117 insertions, 22 deletions
diff --git a/src/main.cpp b/src/main.cpp
index 7a69d96668..2ae560e01b 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -1323,7 +1323,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C
FormatMoney(nModifiedFees - nConflictingFees),
(int)nSize - (int)nConflictingSize);
}
- pool.RemoveStaged(allConflicting);
+ pool.RemoveStaged(allConflicting, false);
// Store transaction in memory
pool.addUnchecked(hash, entry, setAncestors, !IsInitialBlockDownload());
diff --git a/src/txmempool.cpp b/src/txmempool.cpp
index 693d42667b..f7961a6b80 100644
--- a/src/txmempool.cpp
+++ b/src/txmempool.cpp
@@ -38,6 +38,11 @@ CTxMemPoolEntry::CTxMemPoolEntry(const CTransaction& _tx, const CAmount& _nFee,
assert(inChainInputValue <= nValueIn);
feeDelta = 0;
+
+ nCountWithAncestors = 1;
+ nSizeWithAncestors = nTxSize;
+ nModFeesWithAncestors = nFee;
+ nSigOpCountWithAncestors = sigOpCount;
}
CTxMemPoolEntry::CTxMemPoolEntry(const CTxMemPoolEntry& other)
@@ -58,6 +63,7 @@ CTxMemPoolEntry::GetPriority(unsigned int currentHeight) const
void CTxMemPoolEntry::UpdateFeeDelta(int64_t newFeeDelta)
{
nModFeesWithDescendants += newFeeDelta - feeDelta;
+ nModFeesWithAncestors += newFeeDelta - feeDelta;
feeDelta = newFeeDelta;
}
@@ -99,6 +105,8 @@ void CTxMemPool::UpdateForDescendants(txiter updateIt, cacheMap &cachedDescendan
modifyFee += cit->GetModifiedFee();
modifyCount++;
cachedDescendants[updateIt].insert(cit);
+ // Update ancestor state for each descendant
+ mapTx.modify(cit, update_ancestor_state(updateIt->GetTxSize(), updateIt->GetModifiedFee(), 1, updateIt->GetSigOpCount()));
}
}
mapTx.modify(updateIt, update_descendant_state(modifySize, modifyFee, modifyCount));
@@ -108,6 +116,7 @@ void CTxMemPool::UpdateForDescendants(txiter updateIt, cacheMap &cachedDescendan
// which has been re-added to the mempool.
// for each entry, look for descendants that are outside hashesToUpdate, and
// add fee/size information for such descendants to the parent.
+// for each such descendant, also update the ancestor state to include the parent.
void CTxMemPool::UpdateTransactionsFromBlock(const std::vector<uint256> &vHashesToUpdate)
{
LOCK(cs);
@@ -228,6 +237,20 @@ void CTxMemPool::UpdateAncestorsOf(bool add, txiter it, setEntries &setAncestors
}
}
+void CTxMemPool::UpdateEntryForAncestors(txiter it, const setEntries &setAncestors)
+{
+ int64_t updateCount = setAncestors.size();
+ int64_t updateSize = 0;
+ CAmount updateFee = 0;
+ int updateSigOps = 0;
+ BOOST_FOREACH(txiter ancestorIt, setAncestors) {
+ updateSize += ancestorIt->GetTxSize();
+ updateFee += ancestorIt->GetModifiedFee();
+ updateSigOps += ancestorIt->GetSigOpCount();
+ }
+ mapTx.modify(it, update_ancestor_state(updateSize, updateFee, updateCount, updateSigOps));
+}
+
void CTxMemPool::UpdateChildrenForRemoval(txiter it)
{
const setEntries &setMemPoolChildren = GetMemPoolChildren(it);
@@ -236,11 +259,30 @@ void CTxMemPool::UpdateChildrenForRemoval(txiter it)
}
}
-void CTxMemPool::UpdateForRemoveFromMempool(const setEntries &entriesToRemove)
+void CTxMemPool::UpdateForRemoveFromMempool(const setEntries &entriesToRemove, bool updateDescendants)
{
// For each entry, walk back all ancestors and decrement size associated with this
// transaction
const uint64_t nNoLimit = std::numeric_limits<uint64_t>::max();
+ if (updateDescendants) {
+ // updateDescendants should be true whenever we're not recursively
+ // removing a tx and all its descendants, eg when a transaction is
+ // confirmed in a block.
+ // Here we only update statistics and not data in mapLinks (which
+ // we need to preserve until we're finished with all operations that
+ // need to traverse the mempool).
+ BOOST_FOREACH(txiter removeIt, entriesToRemove) {
+ setEntries setDescendants;
+ CalculateDescendants(removeIt, setDescendants);
+ setDescendants.erase(removeIt); // don't update state for self
+ int64_t modifySize = -((int64_t)removeIt->GetTxSize());
+ CAmount modifyFee = -removeIt->GetModifiedFee();
+ int modifySigOps = -removeIt->GetSigOpCount();
+ BOOST_FOREACH(txiter dit, setDescendants) {
+ mapTx.modify(dit, update_ancestor_state(modifySize, modifyFee, -1, modifySigOps));
+ }
+ }
+ }
BOOST_FOREACH(txiter removeIt, entriesToRemove) {
setEntries setAncestors;
const CTxMemPoolEntry &entry = *removeIt;
@@ -264,10 +306,7 @@ void CTxMemPool::UpdateForRemoveFromMempool(const setEntries &entriesToRemove)
// 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
- // 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
- // to walk back over our ancestors (but we do need the mempool
- // parents!)
+ // removeIt in the entries for the parents of removeIt.
UpdateAncestorsOf(false, removeIt, setAncestors);
}
// After updating all the ancestor sizes, we can now sever the link between each
@@ -278,7 +317,7 @@ void CTxMemPool::UpdateForRemoveFromMempool(const setEntries &entriesToRemove)
}
}
-void CTxMemPoolEntry::UpdateState(int64_t modifySize, CAmount modifyFee, int64_t modifyCount)
+void CTxMemPoolEntry::UpdateDescendantState(int64_t modifySize, CAmount modifyFee, int64_t modifyCount)
{
nSizeWithDescendants += modifySize;
assert(int64_t(nSizeWithDescendants) > 0);
@@ -287,6 +326,17 @@ void CTxMemPoolEntry::UpdateState(int64_t modifySize, CAmount modifyFee, int64_t
assert(int64_t(nCountWithDescendants) > 0);
}
+void CTxMemPoolEntry::UpdateAncestorState(int64_t modifySize, CAmount modifyFee, int64_t modifyCount, int modifySigOps)
+{
+ nSizeWithAncestors += modifySize;
+ assert(int64_t(nSizeWithAncestors) > 0);
+ nModFeesWithAncestors += modifyFee;
+ nCountWithAncestors += modifyCount;
+ assert(int64_t(nCountWithAncestors) > 0);
+ nSigOpCountWithAncestors += modifySigOps;
+ assert(int(nSigOpCountWithAncestors) >= 0);
+}
+
CTxMemPool::CTxMemPool(const CFeeRate& _minReasonableRelayFee) :
nTransactionsUpdated(0)
{
@@ -377,6 +427,7 @@ bool CTxMemPool::addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry,
}
}
UpdateAncestorsOf(true, newit, setAncestors);
+ UpdateEntryForAncestors(newit, setAncestors);
nTransactionsUpdated++;
totalTxSize += entry.GetTxSize();
@@ -459,7 +510,7 @@ void CTxMemPool::removeRecursive(const CTransaction &origTx, std::list<CTransact
BOOST_FOREACH(txiter it, setAllRemoves) {
removed.push_back(it->GetTx());
}
- RemoveStaged(setAllRemoves);
+ RemoveStaged(setAllRemoves, false);
}
}
@@ -532,7 +583,7 @@ void CTxMemPool::removeForBlock(const std::vector<CTransaction>& vtx, unsigned i
if (it != mapTx.end()) {
setEntries stage;
stage.insert(it);
- RemoveStaged(stage);
+ RemoveStaged(stage, true);
}
removeConflicts(tx, conflicts);
ClearPrioritisation(tx.GetHash());
@@ -590,6 +641,8 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const
innerUsage += memusage::DynamicUsage(links.parents) + memusage::DynamicUsage(links.children);
bool fDependsWait = false;
setEntries setParentCheck;
+ int64_t parentSizes = 0;
+ unsigned int parentSigOpCount = 0;
BOOST_FOREACH(const CTxIn &txin, tx.vin) {
// Check that every mempool transaction's inputs refer to available coins, or other mempool tx's.
indexed_transaction_set::const_iterator it2 = mapTx.find(txin.prevout.hash);
@@ -597,7 +650,10 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const
const CTransaction& tx2 = it2->GetTx();
assert(tx2.vout.size() > txin.prevout.n && !tx2.vout[txin.prevout.n].IsNull());
fDependsWait = true;
- setParentCheck.insert(it2);
+ if (setParentCheck.insert(it2).second) {
+ parentSizes += it2->GetTxSize();
+ parentSigOpCount += it2->GetSigOpCount();
+ }
} else {
const CCoins* coins = pcoins->AccessCoins(txin.prevout.hash);
assert(coins && coins->IsAvailable(txin.prevout.n));
@@ -610,17 +666,19 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const
i++;
}
assert(setParentCheck == GetMemPoolParents(it));
+ // Also check to make sure ancestor size/sigops are >= sum with immediate
+ // parents.
+ assert(it->GetSizeWithAncestors() >= parentSizes + it->GetTxSize());
+ assert(it->GetSigOpCountWithAncestors() >= parentSigOpCount + it->GetSigOpCount());
// Check children against mapNextTx
CTxMemPool::setEntries setChildrenCheck;
std::map<COutPoint, CInPoint>::const_iterator iter = mapNextTx.lower_bound(COutPoint(it->GetTx().GetHash(), 0));
int64_t childSizes = 0;
- CAmount childModFee = 0;
for (; iter != mapNextTx.end() && iter->first.hash == it->GetTx().GetHash(); ++iter) {
txiter childit = mapTx.find(iter->second.ptx->GetHash());
assert(childit != mapTx.end()); // mapNextTx points to in-mempool transactions
if (setChildrenCheck.insert(childit).second) {
childSizes += childit->GetTxSize();
- childModFee += childit->GetModifiedFee();
}
}
assert(setChildrenCheck == GetMemPoolChildren(it));
@@ -812,9 +870,9 @@ size_t CTxMemPool::DynamicMemoryUsage() const {
return memusage::MallocUsage(sizeof(CTxMemPoolEntry) + 12 * sizeof(void*)) * mapTx.size() + memusage::DynamicUsage(mapNextTx) + memusage::DynamicUsage(mapDeltas) + memusage::DynamicUsage(mapLinks) + cachedInnerUsage;
}
-void CTxMemPool::RemoveStaged(setEntries &stage) {
+void CTxMemPool::RemoveStaged(setEntries &stage, bool updateDescendants) {
AssertLockHeld(cs);
- UpdateForRemoveFromMempool(stage);
+ UpdateForRemoveFromMempool(stage, updateDescendants);
BOOST_FOREACH(const txiter& it, stage) {
removeUnchecked(it);
}
@@ -832,7 +890,7 @@ int CTxMemPool::Expire(int64_t time) {
BOOST_FOREACH(txiter removeit, toremove) {
CalculateDescendants(removeit, stage);
}
- RemoveStaged(stage);
+ RemoveStaged(stage, false);
return stage.size();
}
@@ -941,7 +999,7 @@ void CTxMemPool::TrimToSize(size_t sizelimit, std::vector<uint256>* pvNoSpendsRe
BOOST_FOREACH(txiter it, stage)
txn.push_back(it->GetTx());
}
- RemoveStaged(stage);
+ RemoveStaged(stage, false);
if (pvNoSpendsRemaining) {
BOOST_FOREACH(const CTransaction& tx, txn) {
BOOST_FOREACH(const CTxIn& txin, tx.vin) {
diff --git a/src/txmempool.h b/src/txmempool.h
index b1b6645a06..0db3d5bd27 100644
--- a/src/txmempool.h
+++ b/src/txmempool.h
@@ -80,6 +80,12 @@ private:
uint64_t nSizeWithDescendants; //! ... and size
CAmount nModFeesWithDescendants; //! ... and total fees (all including us)
+ // Analogous statistics for ancestor transactions
+ uint64_t nCountWithAncestors;
+ uint64_t nSizeWithAncestors;
+ CAmount nModFeesWithAncestors;
+ unsigned int nSigOpCountWithAncestors;
+
public:
CTxMemPoolEntry(const CTransaction& _tx, const CAmount& _nFee,
int64_t _nTime, double _entryPriority, unsigned int _entryHeight,
@@ -103,7 +109,9 @@ public:
size_t DynamicMemoryUsage() const { return nUsageSize; }
// Adjusts the descendant state, if this entry is not dirty.
- void UpdateState(int64_t modifySize, CAmount modifyFee, int64_t modifyCount);
+ void UpdateDescendantState(int64_t modifySize, CAmount modifyFee, int64_t modifyCount);
+ // Adjusts the ancestor state
+ void UpdateAncestorState(int64_t modifySize, CAmount modifyFee, int64_t modifyCount, int modifySigOps);
// Updates the fee delta used for mining priority score, and the
// modified fees with descendants.
void UpdateFeeDelta(int64_t feeDelta);
@@ -113,6 +121,11 @@ public:
CAmount GetModFeesWithDescendants() const { return nModFeesWithDescendants; }
bool GetSpendsCoinbase() const { return spendsCoinbase; }
+
+ uint64_t GetCountWithAncestors() const { return nCountWithAncestors; }
+ uint64_t GetSizeWithAncestors() const { return nSizeWithAncestors; }
+ CAmount GetModFeesWithAncestors() const { return nModFeesWithAncestors; }
+ unsigned int GetSigOpCountWithAncestors() const { return nSigOpCountWithAncestors; }
};
// Helpers for modifying CTxMemPool::mapTx, which is a boost multi_index.
@@ -123,12 +136,28 @@ struct update_descendant_state
{}
void operator() (CTxMemPoolEntry &e)
- { e.UpdateState(modifySize, modifyFee, modifyCount); }
+ { e.UpdateDescendantState(modifySize, modifyFee, modifyCount); }
+
+ private:
+ int64_t modifySize;
+ CAmount modifyFee;
+ int64_t modifyCount;
+};
+
+struct update_ancestor_state
+{
+ update_ancestor_state(int64_t _modifySize, CAmount _modifyFee, int64_t _modifyCount, int _modifySigOps) :
+ modifySize(_modifySize), modifyFee(_modifyFee), modifyCount(_modifyCount), modifySigOps(_modifySigOps)
+ {}
+
+ void operator() (CTxMemPoolEntry &e)
+ { e.UpdateAncestorState(modifySize, modifyFee, modifyCount, modifySigOps); }
private:
int64_t modifySize;
CAmount modifyFee;
int64_t modifyCount;
+ int modifySigOps;
};
struct update_fee_delta
@@ -440,8 +469,12 @@ public:
public:
/** Remove a set of transactions from the mempool.
* If a transaction is in this set, then all in-mempool descendants must
- * also be in the set.*/
- void RemoveStaged(setEntries &stage);
+ * also be in the set, unless this transaction is being removed for being
+ * in a block.
+ * Set updateDescendants to true when removing a tx that was in a block, so
+ * that any in-mempool descendants have their ancestor state updated.
+ */
+ void RemoveStaged(setEntries &stage, bool updateDescendants);
/** When adding transactions from a disconnected block back to the mempool,
* new mempool entries may have children in the mempool (which is generally
@@ -551,8 +584,12 @@ private:
const std::set<uint256> &setExclude);
/** Update ancestors of hash to add/remove it as a descendant transaction. */
void UpdateAncestorsOf(bool add, txiter hash, setEntries &setAncestors);
- /** For each transaction being removed, update ancestors and any direct children. */
- void UpdateForRemoveFromMempool(const setEntries &entriesToRemove);
+ /** Set ancestor state for an entry */
+ void UpdateEntryForAncestors(txiter it, const setEntries &setAncestors);
+ /** For each transaction being removed, update ancestors and any direct children.
+ * If updateDescendants is true, then also update in-mempool descendants'
+ * ancestor state. */
+ void UpdateForRemoveFromMempool(const setEntries &entriesToRemove, bool updateDescendants);
/** Sever link between specified transaction and direct children. */
void UpdateChildrenForRemoval(txiter entry);