test: Add CheckMempoolEphemeralInvariants

Checks that transactions in mempool with dust
follow expected invariants.
This commit is contained in:
Greg Sanders 2024-07-24 12:51:24 -04:00
parent e2e30e89ba
commit 127719f516
2 changed files with 60 additions and 0 deletions

View file

@ -141,6 +141,54 @@ std::optional<std::string> CheckPackageMempoolAcceptResult(const Package& txns,
return std::nullopt;
}
std::vector<uint32_t> GetDustIndexes(const CTransactionRef tx_ref, CFeeRate dust_relay_rate)
{
std::vector<uint32_t> dust_indexes;
for (size_t i = 0; i < tx_ref->vout.size(); ++i) {
const auto& output = tx_ref->vout[i];
if (IsDust(output, dust_relay_rate)) dust_indexes.push_back(i);
}
return dust_indexes;
}
void CheckMempoolEphemeralInvariants(const CTxMemPool& tx_pool)
{
LOCK(tx_pool.cs);
for (const auto& tx_info : tx_pool.infoAll()) {
const auto& entry = *Assert(tx_pool.GetEntry(tx_info.tx->GetHash()));
std::vector<uint32_t> dust_indexes = GetDustIndexes(tx_info.tx, tx_pool.m_opts.dust_relay_feerate);
Assert(dust_indexes.size() < 2);
if (dust_indexes.empty()) continue;
// Transaction must have no base fee
Assert(entry.GetFee() == 0 && entry.GetModifiedFee() == 0);
// Transaction has single dust; make sure it's swept or will not be mined
const auto& children = entry.GetMemPoolChildrenConst();
// Multiple children should never happen as non-dust-spending child
// can get mined as package
Assert(children.size() < 2);
if (children.empty()) {
// No children and no fees; modified fees aside won't get mined so it's fine
// Happens naturally if child spend is RBF cycled away.
continue;
}
// Only-child should be spending the dust
const auto& only_child = children.begin()->get().GetTx();
COutPoint dust_outpoint{tx_info.tx->GetHash(), dust_indexes[0]};
Assert(std::any_of(only_child.vin.begin(), only_child.vin.end(), [&dust_outpoint](const CTxIn& txin) {
return txin.prevout == dust_outpoint;
}));
}
}
void CheckMempoolTRUCInvariants(const CTxMemPool& tx_pool)
{
LOCK(tx_pool.cs);

View file

@ -47,6 +47,18 @@ std::optional<std::string> CheckPackageMempoolAcceptResult(const Package& txns,
bool expect_valid,
const CTxMemPool* mempool);
/** Check that we never get into a state where an ephemeral dust
* transaction would be mined without the spend of the dust
* also being mined. This assumes standardness checks are being
* enforced.
*/
void CheckMempoolEphemeralInvariants(const CTxMemPool& tx_pool);
/** Return indexes of the transaction's outputs that are considered dust
* at given dust_relay_rate.
*/
std::vector<uint32_t> GetDustIndexes(const CTransactionRef tx_ref, CFeeRate dust_relay_rate);
/** For every transaction in tx_pool, check TRUC invariants:
* - a TRUC tx's ancestor count must be within TRUC_ANCESTOR_LIMIT
* - a TRUC tx's descendant count must be within TRUC_DESCENDANT_LIMIT