rpc: Calculate ancestor data from scratch for mempool rpc calls
What changed, and why it matters
This Bitcoin Core change alters how mempool RPC calls (such as getrawmempool and getmempoolentry) report ancestor data. Previously, the node reused cached ancestor statistics stored with each transaction. Now it recalculates those numbers from scratch every time the RPC is called. The likely reason is that the cached ancestor values can become stale or inconsistent after certain mempool operations, which could cause RPC responses to show incorrect counts, sizes, or fees for a transaction's chain of unconfirmed ancestors. There is no direct evidence in the commit of an exploitable security vulnerability, but inconsistent mempool accounting can mislead wallets, fee estimators, or monitoring tools.
Treat as a correctness/reliability improvement rather than an urgent security patch. Node operators and developers relying on ancestorcount/ancestorsize/ancestor fees in mempool RPCs should upgrade to avoid acting on stale data. If you maintain fee estimation or transaction-selection tooling that consumes these fields, verify behavior after the upgrade. No immediate emergency response is warranted absent additional disclosure.
Security signals we found
Change from cached to freshly computed mempool ancestor data in RPC output
Only ancestor fields modified; descendant fields still use cached values
No explicit bug fix description or CVE reference in commit message
Small, targeted change in a single RPC serialization function
Evidence from the diff
The patch replaces direct reads of CTxMemPoolEntry cached ancestor fields (GetCountWithAncestors, GetSizeWithAncestors, GetModFeesWithAncestors) with a new pool.CalculateAncestorData(e) computation in entryToJSON(). The descendant fields remain cached. The change suggests the cached ancestor state was unreliable for RPC reporting, possibly because ancestor updates are lazy, batched, or skipped in some code paths. Recalculating from scratch ensures RPC consumers see internally consistent ancestor data for the current mempool snapshot. Performance cost is a full ancestor walk per RPC entry.
Changed components
src/rpc/mempool.cppCTxMemPoolEntry ancestor statistics reportingRPCs: getrawmempool, getmempoolentry, and any RPC using entryToJSON()Inspect captured patch +5 / −3
diff --git a/src/rpc/mempool.cpp b/src/rpc/mempool.cpp
index 147af369..fa70758a 100644
--- a/src/rpc/mempool.cpp
+++ b/src/rpc/mempool.cpp
@@ -290,20 +290,22 @@ static void entryToJSON(const CTxMemPool& pool, UniValue& info, const CTxMemPool
{
AssertLockHeld(pool.cs);
+ auto [ancestor_count, ancestor_size, ancestor_fees] = pool.CalculateAncestorData(e);
+
info.pushKV("vsize", (int)e.GetTxSize());
info.pushKV("weight", (int)e.GetTxWeight());
info.pushKV("time", count_seconds(e.GetTime()));
info.pushKV("height", (int)e.GetHeight());
info.pushKV("descendantcount", e.GetCountWithDescendants());
info.pushKV("descendantsize", e.GetSizeWithDescendants());
- info.pushKV("ancestorcount", e.GetCountWithAncestors());
- info.pushKV("ancestorsize", e.GetSizeWithAncestors());
+ info.pushKV("ancestorcount", ancestor_count);
+ info.pushKV("ancestorsize", ancestor_size);
info.pushKV("wtxid", e.GetTx().GetWitnessHash().ToString());
UniValue fees(UniValue::VOBJ);
fees.pushKV("base", ValueFromAmount(e.GetFee()));
fees.pushKV("modified", ValueFromAmount(e.GetModifiedFee()));
- fees.pushKV("ancestor", ValueFromAmount(e.GetModFeesWithAncestors()));
+ fees.pushKV("ancestor", ValueFromAmount(ancestor_fees));
fees.pushKV("descendant", ValueFromAmount(e.GetModFeesWithDescendants()));
info.pushKV("fees", std::move(fees));
Why this scored 31/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.