Skip to content

Commit 64e84e2

Browse files
committed
Add ancestor tracking to mempool This implements caching of ancestor state to each mempool entry, similar to descendant tracking, but also including caching sigops-with-ancestors (as that metric will be helpful to future code that implements better transaction selection in CreatenewBlock).
Coming from btc@72abd2ce3c5ad8157d3a993693df1919a6ad79c3
1 parent 8325bb4 commit 64e84e2

File tree

3 files changed

+117
-21
lines changed

3 files changed

+117
-21
lines changed

src/txmempool.cpp

Lines changed: 73 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,11 @@ CTxMemPoolEntry::CTxMemPoolEntry(const CTransactionRef& _tx, const CAmount& _nFe
3838
assert(inChainInputValue <= nValueIn);
3939

4040
feeDelta = 0;
41+
42+
nCountWithAncestors = 1;
43+
nSizeWithAncestors = nTxSize;
44+
nModFeesWithAncestors = nFee;
45+
nSigOpCountWithAncestors = sigOpCount;
4146
}
4247

4348
CTxMemPoolEntry::CTxMemPoolEntry(const CTxMemPoolEntry& other)
@@ -58,6 +63,7 @@ CTxMemPoolEntry::GetPriority(unsigned int currentHeight) const
5863
void CTxMemPoolEntry::UpdateFeeDelta(int64_t newFeeDelta)
5964
{
6065
nModFeesWithDescendants += newFeeDelta - feeDelta;
66+
nModFeesWithAncestors += newFeeDelta - feeDelta;
6167
feeDelta = newFeeDelta;
6268
}
6369

@@ -99,6 +105,8 @@ void CTxMemPool::UpdateForDescendants(txiter updateIt, cacheMap &cachedDescendan
99105
modifyFee += cit->GetModifiedFee();
100106
modifyCount++;
101107
cachedDescendants[updateIt].insert(cit);
108+
// Update ancestor state for each descendant
109+
mapTx.modify(cit, update_ancestor_state(updateIt->GetTxSize(), updateIt->GetModifiedFee(), 1, updateIt->GetSigOpCount()));
102110
}
103111
}
104112
mapTx.modify(updateIt, update_descendant_state(modifySize, modifyFee, modifyCount));
@@ -108,6 +116,7 @@ void CTxMemPool::UpdateForDescendants(txiter updateIt, cacheMap &cachedDescendan
108116
// which has been re-added to the mempool.
109117
// for each entry, look for descendants that are outside hashesToUpdate, and
110118
// add fee/size information for such descendants to the parent.
119+
// for each such descendant, also update the ancestor state to include the parent.
111120
void CTxMemPool::UpdateTransactionsFromBlock(const std::vector<uint256> &vHashesToUpdate)
112121
{
113122
LOCK(cs);
@@ -228,6 +237,20 @@ void CTxMemPool::UpdateAncestorsOf(bool add, txiter it, setEntries &setAncestors
228237
}
229238
}
230239

240+
void CTxMemPool::UpdateEntryForAncestors(txiter it, const setEntries &setAncestors)
241+
{
242+
int64_t updateCount = setAncestors.size();
243+
int64_t updateSize = 0;
244+
CAmount updateFee = 0;
245+
int updateSigOps = 0;
246+
for (const txiter& ancestorIt : setAncestors) {
247+
updateSize += ancestorIt->GetTxSize();
248+
updateFee += ancestorIt->GetModifiedFee();
249+
updateSigOps += ancestorIt->GetSigOpCount();
250+
}
251+
mapTx.modify(it, update_ancestor_state(updateSize, updateFee, updateCount, updateSigOps));
252+
}
253+
231254
void CTxMemPool::UpdateChildrenForRemoval(txiter it)
232255
{
233256
const setEntries &setMemPoolChildren = GetMemPoolChildren(it);
@@ -236,11 +259,30 @@ void CTxMemPool::UpdateChildrenForRemoval(txiter it)
236259
}
237260
}
238261

239-
void CTxMemPool::UpdateForRemoveFromMempool(const setEntries &entriesToRemove)
262+
void CTxMemPool::UpdateForRemoveFromMempool(const setEntries &entriesToRemove, bool updateDescendants)
240263
{
241264
// For each entry, walk back all ancestors and decrement size associated with this
242265
// transaction
243266
const uint64_t nNoLimit = std::numeric_limits<uint64_t>::max();
267+
if (updateDescendants) {
268+
// updateDescendants should be true whenever we're not recursively
269+
// removing a tx and all its descendants, eg when a transaction is
270+
// confirmed in a block.
271+
// Here we only update statistics and not data in mapLinks (which
272+
// we need to preserve until we're finished with all operations that
273+
// need to traverse the mempool).
274+
for (const txiter& removeIt : entriesToRemove) {
275+
setEntries setDescendants;
276+
CalculateDescendants(removeIt, setDescendants);
277+
setDescendants.erase(removeIt); // don't update state for self
278+
int64_t modifySize = -((int64_t)removeIt->GetTxSize());
279+
CAmount modifyFee = -removeIt->GetModifiedFee();
280+
int modifySigOps = -removeIt->GetSigOpCount();
281+
for (const txiter& dit : setDescendants) {
282+
mapTx.modify(dit, update_ancestor_state(modifySize, modifyFee, -1, modifySigOps));
283+
}
284+
}
285+
}
244286
for (const txiter& removeIt : entriesToRemove) {
245287
setEntries setAncestors;
246288
const CTxMemPoolEntry &entry = *removeIt;
@@ -264,10 +306,7 @@ void CTxMemPool::UpdateForRemoveFromMempool(const setEntries &entriesToRemove)
264306
// transactions as the set of things to update for removal.
265307
CalculateMemPoolAncestors(entry, setAncestors, nNoLimit, nNoLimit, nNoLimit, nNoLimit, dummy, false);
266308
// Note that UpdateAncestorsOf severs the child links that point to
267-
// removeIt in the entries for the parents of removeIt. This is
268-
// fine since we don't need to use the mempool children of any entries
269-
// to walk back over our ancestors (but we do need the mempool
270-
// parents!)
309+
// removeIt in the entries for the parents of removeIt.
271310
UpdateAncestorsOf(false, removeIt, setAncestors);
272311
}
273312
// After updating all the ancestor sizes, we can now sever the link between each
@@ -278,7 +317,7 @@ void CTxMemPool::UpdateForRemoveFromMempool(const setEntries &entriesToRemove)
278317
}
279318
}
280319

281-
void CTxMemPoolEntry::UpdateState(int64_t modifySize, CAmount modifyFee, int64_t modifyCount)
320+
void CTxMemPoolEntry::UpdateDescendantState(int64_t modifySize, CAmount modifyFee, int64_t modifyCount)
282321
{
283322
nSizeWithDescendants += modifySize;
284323
assert(int64_t(nSizeWithDescendants) > 0);
@@ -287,6 +326,17 @@ void CTxMemPoolEntry::UpdateState(int64_t modifySize, CAmount modifyFee, int64_t
287326
assert(int64_t(nCountWithDescendants) > 0);
288327
}
289328

329+
void CTxMemPoolEntry::UpdateAncestorState(int64_t modifySize, CAmount modifyFee, int64_t modifyCount, int modifySigOps)
330+
{
331+
nSizeWithAncestors += modifySize;
332+
assert(int64_t(nSizeWithAncestors) > 0);
333+
nModFeesWithAncestors += modifyFee;
334+
nCountWithAncestors += modifyCount;
335+
assert(int64_t(nCountWithAncestors) > 0);
336+
nSigOpCountWithAncestors += modifySigOps;
337+
assert(int(nSigOpCountWithAncestors) >= 0);
338+
}
339+
290340
CTxMemPool::CTxMemPool(const CFeeRate& _minReasonableRelayFee) :
291341
nTransactionsUpdated(0)
292342
{
@@ -373,6 +423,7 @@ bool CTxMemPool::addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry,
373423
}
374424
}
375425
UpdateAncestorsOf(true, newit, setAncestors);
426+
UpdateEntryForAncestors(newit, setAncestors);
376427

377428
// Save spent nullifiers
378429
if (tx.IsShieldedTx()) {
@@ -468,7 +519,7 @@ void CTxMemPool::removeRecursive(const CTransaction& origTx, std::list<CTransact
468519
for (const txiter& it : setAllRemoves) {
469520
removed.emplace_back(it->GetSharedTx());
470521
}
471-
RemoveStaged(setAllRemoves);
522+
RemoveStaged(setAllRemoves, false);
472523
}
473524
}
474525

@@ -573,7 +624,7 @@ void CTxMemPool::removeForBlock(const std::vector<CTransactionRef>& vtx, unsigne
573624
if (it != mapTx.end()) {
574625
setEntries stage;
575626
stage.insert(it);
576-
RemoveStaged(stage);
627+
RemoveStaged(stage, true);
577628
}
578629
removeConflicts(*tx, conflicts);
579630
ClearPrioritisation(tx->GetHash());
@@ -632,6 +683,8 @@ void CTxMemPool::check(const CCoinsViewCache* pcoins) const
632683
innerUsage += memusage::DynamicUsage(links.parents) + memusage::DynamicUsage(links.children);
633684
bool fDependsWait = false;
634685
setEntries setParentCheck;
686+
int64_t parentSizes = 0;
687+
unsigned int parentSigOpCount = 0;
635688
bool fHasZerocoinSpends = false;
636689
for (const CTxIn& txin : tx.vin) {
637690
// Check that every mempool transaction's inputs refer to available coins, or other mempool tx's.
@@ -640,7 +693,10 @@ void CTxMemPool::check(const CCoinsViewCache* pcoins) const
640693
const CTransaction& tx2 = it2->GetTx();
641694
assert(tx2.vout.size() > txin.prevout.n && !tx2.vout[txin.prevout.n].IsNull());
642695
fDependsWait = true;
643-
setParentCheck.insert(it2);
696+
if (setParentCheck.insert(it2).second) {
697+
parentSizes += it2->GetTxSize();
698+
parentSigOpCount += it2->GetSigOpCount();
699+
}
644700
} else if(!txin.IsZerocoinSpend() && !txin.IsZerocoinPublicSpend()) {
645701
assert(pcoins->HaveCoin(txin.prevout));
646702
} else {
@@ -666,18 +722,20 @@ void CTxMemPool::check(const CCoinsViewCache* pcoins) const
666722
}
667723
}
668724
assert(setParentCheck == GetMemPoolParents(it));
725+
// Also check to make sure ancestor size/sigops are >= sum with immediate
726+
// parents.
727+
assert(it->GetSizeWithAncestors() >= parentSizes + it->GetTxSize());
728+
assert(it->GetSigOpCountWithAncestors() >= parentSigOpCount + it->GetSigOpCount());
669729
// Check children against mapNextTx
670730
if (!fHasZerocoinSpends) {
671731
CTxMemPool::setEntries setChildrenCheck;
672732
std::map<COutPoint, CInPoint>::const_iterator iter = mapNextTx.lower_bound(COutPoint(tx.GetHash(), 0));
673733
int64_t childSizes = 0;
674-
CAmount childModFee = 0;
675734
for (; iter != mapNextTx.end() && iter->first.hash == tx.GetHash(); ++iter) {
676735
txiter childit = mapTx.find(iter->second.ptx->GetHash());
677736
assert(childit != mapTx.end()); // mapNextTx points to in-mempool transactions
678737
if (setChildrenCheck.insert(childit).second) {
679738
childSizes += childit->GetTxSize();
680-
childModFee += childit->GetModifiedFee();
681739
}
682740
}
683741
assert(setChildrenCheck == GetMemPoolChildren(it));
@@ -919,10 +977,10 @@ size_t CTxMemPool::DynamicMemoryUsage() const
919977
memusage::DynamicUsage(mapSaplingNullifiers);
920978
}
921979

922-
void CTxMemPool::RemoveStaged(setEntries &stage)
980+
void CTxMemPool::RemoveStaged(setEntries &stage, bool updateDescendants)
923981
{
924982
AssertLockHeld(cs);
925-
UpdateForRemoveFromMempool(stage);
983+
UpdateForRemoveFromMempool(stage, updateDescendants);
926984
for (const txiter& it : stage) {
927985
removeUnchecked(it);
928986
}
@@ -941,7 +999,7 @@ int CTxMemPool::Expire(int64_t time)
941999
for (const txiter& removeit : toremove) {
9421000
CalculateDescendants(removeit, stage);
9431001
}
944-
RemoveStaged(stage);
1002+
RemoveStaged(stage, false);
9451003
return stage.size();
9461004
}
9471005

@@ -1052,7 +1110,7 @@ void CTxMemPool::TrimToSize(size_t sizelimit, std::vector<COutPoint>* pvNoSpends
10521110
for (txiter it: stage)
10531111
txn.push_back(it->GetTx());
10541112
}
1055-
RemoveStaged(stage);
1113+
RemoveStaged(stage, false);
10561114
if (pvNoSpendsRemaining) {
10571115
for (const CTransaction& tx: txn) {
10581116
for (const CTxIn& txin: tx.vin) {

src/txmempool.h

Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,12 @@ class CTxMemPoolEntry
8787
uint64_t nSizeWithDescendants; //! ... and size
8888
CAmount nModFeesWithDescendants; //! ... and total fees (all including us)
8989

90+
// Analogous statistics for ancestor transactions
91+
uint64_t nCountWithAncestors;
92+
uint64_t nSizeWithAncestors;
93+
CAmount nModFeesWithAncestors;
94+
unsigned int nSigOpCountWithAncestors;
95+
9096
public:
9197
CTxMemPoolEntry(const CTransactionRef& _tx, const CAmount& _nFee,
9298
int64_t _nTime, double _entryPriority, unsigned int _entryHeight,
@@ -113,7 +119,9 @@ class CTxMemPoolEntry
113119
size_t DynamicMemoryUsage() const { return nUsageSize; }
114120

115121
// Adjusts the descendant state, if this entry is not dirty.
116-
void UpdateState(int64_t modifySize, CAmount modifyFee, int64_t modifyCount);
122+
void UpdateDescendantState(int64_t modifySize, CAmount modifyFee, int64_t modifyCount);
123+
// Adjusts the ancestor state
124+
void UpdateAncestorState(int64_t modifySize, CAmount modifyFee, int64_t modifyCount, int modifySigOps);
117125
// Updates the fee delta used for mining priority score, and the
118126
// modified fees with descendants.
119127
void UpdateFeeDelta(int64_t feeDelta);
@@ -123,6 +131,11 @@ class CTxMemPoolEntry
123131
CAmount GetModFeesWithDescendants() const { return nModFeesWithDescendants; }
124132

125133
bool GetSpendsCoinbaseOrCoinstake() const { return spendsCoinbaseOrCoinstake; }
134+
135+
uint64_t GetCountWithAncestors() const { return nCountWithAncestors; }
136+
uint64_t GetSizeWithAncestors() const { return nSizeWithAncestors; }
137+
CAmount GetModFeesWithAncestors() const { return nModFeesWithAncestors; }
138+
unsigned int GetSigOpCountWithAncestors() const { return nSigOpCountWithAncestors; }
126139
};
127140

128141
// Helpers for modifying CTxMemPool::mapTx, which is a boost multi_index.
@@ -133,14 +146,30 @@ struct update_descendant_state
133146
{}
134147

135148
void operator() (CTxMemPoolEntry &e)
136-
{ e.UpdateState(modifySize, modifyFee, modifyCount); }
149+
{ e.UpdateDescendantState(modifySize, modifyFee, modifyCount); }
137150

138151
private:
139152
int64_t modifySize;
140153
CAmount modifyFee;
141154
int64_t modifyCount;
142155
};
143156

157+
struct update_ancestor_state
158+
{
159+
update_ancestor_state(int64_t _modifySize, CAmount _modifyFee, int64_t _modifyCount, int _modifySigOps) :
160+
modifySize(_modifySize), modifyFee(_modifyFee), modifyCount(_modifyCount), modifySigOps(_modifySigOps)
161+
{}
162+
163+
void operator() (CTxMemPoolEntry &e)
164+
{ e.UpdateAncestorState(modifySize, modifyFee, modifyCount, modifySigOps); }
165+
166+
private:
167+
int64_t modifySize;
168+
CAmount modifyFee;
169+
int64_t modifyCount;
170+
int modifySigOps;
171+
};
172+
144173
struct update_fee_delta
145174
{
146175
update_fee_delta(int64_t _feeDelta) : feeDelta(_feeDelta) { }
@@ -492,8 +521,12 @@ class CTxMemPool
492521

493522
/** Remove a set of transactions from the mempool.
494523
* If a transaction is in this set, then all in-mempool descendants must
495-
* also be in the set.*/
496-
void RemoveStaged(setEntries &stage);
524+
* also be in the set, unless this transaction is being removed for being
525+
* in a block.
526+
* Set updateDescendants to true when removing a tx that was in a block, so
527+
* that any in-mempool descendants have their ancestor state updated.
528+
*/
529+
void RemoveStaged(setEntries &stage, bool updateDescendants);
497530

498531
/** When adding transactions from a disconnected block back to the mempool,
499532
* new mempool entries may have children in the mempool (which is generally
@@ -604,8 +637,12 @@ class CTxMemPool
604637
const std::set<uint256> &setExclude);
605638
/** Update ancestors of hash to add/remove it as a descendant transaction. */
606639
void UpdateAncestorsOf(bool add, txiter hash, setEntries &setAncestors);
607-
/** For each transaction being removed, update ancestors and any direct children. */
608-
void UpdateForRemoveFromMempool(const setEntries &entriesToRemove);
640+
/** Set ancestor state for an entry */
641+
void UpdateEntryForAncestors(txiter it, const setEntries &setAncestors);
642+
/** For each transaction being removed, update ancestors and any direct children.
643+
* If updateDescendants is true, then also update in-mempool descendants'
644+
* ancestor state. */
645+
void UpdateForRemoveFromMempool(const setEntries &entriesToRemove, bool updateDescendants);
609646
/** Sever link between specified transaction and direct children. */
610647
void UpdateChildrenForRemoval(txiter entry);
611648
/** Populate setDescendants with all in-mempool descendants of hash.

src/validation.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -589,6 +589,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const C
589589
return error("%s: BUG! PLEASE REPORT THIS! ConnectInputs failed against MANDATORY but not STANDARD flags %s, %s",
590590
__func__, hash.ToString(), FormatStateMessage(state));
591591
}
592+
// todo: pool.removeStaged for all conflicting entries
592593

593594
// Store transaction in memory
594595
pool.addUnchecked(hash, entry, setAncestors, !IsInitialBlockDownload());

0 commit comments

Comments
 (0)