[fuzz] TxOrphanage::SanityCheck accounting

This commit is contained in:
glozow 2025-01-30 00:46:01 -05:00
parent 22dccea553
commit e107bf78f9
4 changed files with 48 additions and 1 deletions

View file

@ -285,6 +285,7 @@ static void CheckInvariants(const node::TxDownloadManagerImpl& txdownload_impl,
// Orphanage usage should never exceed what is allowed // Orphanage usage should never exceed what is allowed
Assert(orphanage.Size() <= max_orphan_count); Assert(orphanage.Size() <= max_orphan_count);
txdownload_impl.m_orphanage.SanityCheck();
// We should never have more than the maximum in-flight requests out for a peer. // We should never have more than the maximum in-flight requests out for a peer.
for (NodeId peer = 0; peer < NUM_PEERS; ++peer) { for (NodeId peer = 0; peer < NUM_PEERS; ++peer) {
@ -437,8 +438,8 @@ FUZZ_TARGET(txdownloadman_impl, .init = initialize)
auto time_skip = fuzzed_data_provider.PickValueInArray(TIME_SKIPS); auto time_skip = fuzzed_data_provider.PickValueInArray(TIME_SKIPS);
if (fuzzed_data_provider.ConsumeBool()) time_skip *= -1; if (fuzzed_data_provider.ConsumeBool()) time_skip *= -1;
time += time_skip; time += time_skip;
CheckInvariants(txdownload_impl, max_orphan_count);
} }
CheckInvariants(txdownload_impl, max_orphan_count);
// Disconnect everybody, check that all data structures are empty. // Disconnect everybody, check that all data structures are empty.
for (NodeId nodeid = 0; nodeid < NUM_PEERS; ++nodeid) { for (NodeId nodeid = 0; nodeid < NUM_PEERS; ++nodeid) {
txdownload_impl.DisconnectedPeer(nodeid); txdownload_impl.DisconnectedPeer(nodeid);

View file

@ -204,6 +204,7 @@ FUZZ_TARGET(txorphan, .init = initialize_orphanage)
}); });
} }
// Set tx as potential parent to be used for future GetChildren() calls. // Set tx as potential parent to be used for future GetChildren() calls.
if (!ptx_potential_parent || fuzzed_data_provider.ConsumeBool()) { if (!ptx_potential_parent || fuzzed_data_provider.ConsumeBool()) {
ptx_potential_parent = tx; ptx_potential_parent = tx;
@ -213,4 +214,5 @@ FUZZ_TARGET(txorphan, .init = initialize_orphanage)
const bool get_tx_nonnull{orphanage.GetTx(tx->GetWitnessHash()) != nullptr}; const bool get_tx_nonnull{orphanage.GetTx(tx->GetWitnessHash()) != nullptr};
Assert(have_tx == get_tx_nonnull); Assert(have_tx == get_tx_nonnull);
} }
orphanage.SanityCheck();
} }

View file

@ -321,3 +321,43 @@ std::vector<TxOrphanage::OrphanTxBase> TxOrphanage::GetOrphanTransactions() cons
} }
return ret; return ret;
} }
void TxOrphanage::SanityCheck() const
{
// Check that cached m_total_announcements is correct
unsigned int counted_total_announcements{0};
// Check that m_total_orphan_usage is correct
unsigned int counted_total_usage{0};
// Check that cached PeerOrphanInfo::m_total_size is correct
std::map<NodeId, unsigned int> counted_size_per_peer;
for (const auto& [wtxid, orphan] : m_orphans) {
counted_total_announcements += orphan.announcers.size();
counted_total_usage += orphan.GetUsage();
Assume(!orphan.announcers.empty());
for (const auto& peer : orphan.announcers) {
auto& count_peer_entry = counted_size_per_peer.try_emplace(peer).first->second;
count_peer_entry += orphan.GetUsage();
}
}
Assume(m_total_announcements >= m_orphans.size());
Assume(counted_total_announcements == m_total_announcements);
Assume(counted_total_usage == m_total_orphan_usage);
// There must be an entry in m_peer_orphanage_info for each peer
// However, there may be m_peer_orphanage_info entries corresponding to peers for whom we
// previously had orphans but no longer do.
Assume(counted_size_per_peer.size() <= m_peer_orphanage_info.size());
for (const auto& [peerid, info] : m_peer_orphanage_info) {
auto it_counted = counted_size_per_peer.find(peerid);
if (it_counted == counted_size_per_peer.end()) {
Assume(info.m_total_usage == 0);
} else {
Assume(it_counted->second == info.m_total_usage);
}
}
}

View file

@ -105,6 +105,10 @@ public:
return peer_it == m_peer_orphanage_info.end() ? 0 : peer_it->second.m_total_usage; return peer_it == m_peer_orphanage_info.end() ? 0 : peer_it->second.m_total_usage;
} }
/** Check consistency between PeerOrphanInfo and m_orphans. Recalculate counters and ensure they
* match what is cached. */
void SanityCheck() const;
protected: protected:
struct OrphanTx : public OrphanTxBase { struct OrphanTx : public OrphanTxBase {
size_t list_pos; size_t list_pos;