Skip to content

Commit

Permalink
test: Add CheckMempoolEphemeralInvariants
Browse files Browse the repository at this point in the history
  • Loading branch information
instagibbs committed Aug 2, 2024
1 parent 2238785 commit 551a5d2
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 0 deletions.
48 changes: 48 additions & 0 deletions src/test/util/txmempool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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()) return;

// Transaction must have no base fee (but may have modified)
Assert(entry.GetFee() == 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.
return;
}

// 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);
Expand Down
13 changes: 13 additions & 0 deletions src/test/util/txmempool.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,19 @@ 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 bypass_limits was never set to true
* during transaction submission, and 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
Expand Down

0 comments on commit 551a5d2

Please sign in to comment.