Upon reviewing the documentation for CTxMemPool::CalculateMemPoolAncestors
, I noticed setAncestors
was meant to be an out
parameter but actually is an in,out
parameter, as can be observed by adding assert(setAncestors.empty());
as the first line in the function and running make check
. This PR fixes this unexpected behaviour and introduces refactoring improvements to make intents and effects of the code more clear.
Unexpected behaviour
This behaviour occurs only in the package acceptance path, currently only triggered by testmempoolaccept
and submitpackage
RPCs.
In MemPoolAccept::AcceptMultipleTransactions()
, we first call PreChecks()
and then SubmitPackage()
with the same Workspace ws
reference. PreChecks
leaves ws.m_ancestors
in a potentially non-empty state, before it is passed on to MemPoolAccept::SubmitPackage
. SubmitPackage
is the only place where setAncestors
isn’t guaranteed to be empty before calling CalculateMemPoolAncestors
. The most straightforward fix is to just forcefully clear setAncestors
at the beginning of CalculateMemPoolAncestors, which is done in the first bugfix commit.
Improvements
Return value instead of out-parameters
This PR updates the function signatures for CTxMemPool::CalculateMemPoolAncestors
and CTxMemPool::CalculateAncestorsAndCheckLimits
to use a util::Result
return type and eliminate both the setAncestors
in,out
-parameter as well as the error string. It simplifies the code and makes the intent and effects more explicit.
Observability
There are 7 instances where we currently call CalculateMemPoolAncestors
without actually checking if the function succeeded because we assume that it can’t fail, such as in miner.cpp. This PR adds a new wrapper AssumeCalculateMemPoolAncestors
function that logs such unexpected failures, or in case of debug builds even halts the program. It’s not crucial to the objective, more of an observability improvement that seems sensible to add on here.