Ok, that is probably my fault. I wrote the fuzz code without reading the validation code. I should read more about the interface and the meaning of valid/invalid in the context of packages. Apparently we can't deduce any meaningful information useful for fuzzing from the result. So for now I'd suggest to remove the failing Asserts.
Moreover, the "tracking" code seems not prepared for packages. I think it can be fixed by tracking the history of added/removed transactions chronologically. (That is, switching the two std::set into one std::vector)
See this diff:
diff --git a/src/test/fuzz/tx_pool.cpp b/src/test/fuzz/tx_pool.cpp
index c38e3420f6..b01b4f978a 100644
--- a/src/test/fuzz/tx_pool.cpp
+++ b/src/test/fuzz/tx_pool.cpp
@@ -70,6 +70,24 @@ struct TransactionsDelta final : public CValidationInterface {
}
};
+struct TransactionsHistory final : public CValidationInterface {
+ std::vector<std::tuple<bool, CTransactionRef>>& m_history;
+
+ explicit TransactionsHistory(std::vector<std::tuple<bool, CTransactionRef>>& h)
+ : m_history{h} {}
+
+ void TransactionAddedToMempool(const CTransactionRef& tx, uint64_t /* mempool_sequence */) override
+ {
+ m_history.emplace_back(true, tx);
+ }
+
+ void TransactionRemovedFromMempool(const CTransactionRef& tx, MemPoolRemovalReason reason, uint64_t /* mempool_sequence */) override
+ {
+ if (reason != MemPoolRemovalReason::SIZELIMIT)
+ m_history.emplace_back(false, tx);
+ }
+};
+
void SetMempoolConstraints(ArgsManager& args, FuzzedDataProvider& fuzzed_data_provider)
{
args.ForceSetArg("-limitancestorcount",
@@ -247,9 +265,8 @@ FUZZ_TARGET_INIT(tx_pool_standard, initialize_tx_pool)
}
// Remember all removed and added transactions
- std::set<CTransactionRef> removed;
- std::set<CTransactionRef> added;
- auto txr = std::make_shared<TransactionsDelta>(removed, added);
+ std::vector<std::tuple<bool, CTransactionRef>> history;
+ auto txr = std::make_shared<TransactionsHistory>(history);
RegisterSharedValidationInterface(txr);
const bool bypass_limits = fuzzed_data_provider.ConsumeBool();
@@ -283,7 +300,6 @@ FUZZ_TARGET_INIT(tx_pool_standard, initialize_tx_pool)
}
}
if (accepted) {
- Assert(result_package.m_tx_results.size() == package.size());
for (const auto& tx_res : result_package.m_tx_results) {
Assert(tx_res.second.m_result_type == MempoolAcceptResult::ResultType::VALID);
}
@@ -291,15 +307,6 @@ FUZZ_TARGET_INIT(tx_pool_standard, initialize_tx_pool)
}
SyncWithValidationInterfaceQueue();
UnregisterSharedValidationInterface(txr);
- Assert(accepted != added.empty());
- if (accepted) {
- Assert(Package(added.begin(), added.end()) == package);
- } else {
- // Do not consider rejected transactions removed
- for (const auto& tx : package) {
- removed.erase(tx);
- }
- }
// Helper to insert spent and created outpoints of a tx into collections
using Sets = std::vector<std::reference_wrapper<std::set<COutPoint>>>;
@@ -317,16 +324,17 @@ FUZZ_TARGET_INIT(tx_pool_standard, initialize_tx_pool)
};
// Add created outpoints, remove spent outpoints
{
+ for (const auto& [added,tx]:history){
// Outpoints that no longer exist at all
std::set<COutPoint> consumed_erased;
// Outpoints that no longer count toward the total supply
std::set<COutPoint> consumed_supply;
- for (const auto& removed_tx : removed) {
- insert_tx(/*created_by_tx=*/{consumed_erased}, /*consumed_by_tx=*/{outpoints_supply}, /*tx=*/*removed_tx);
- }
- for (const auto& added_tx : added) {
- insert_tx(/*created_by_tx=*/{outpoints_supply, outpoints_rbf}, /*consumed_by_tx=*/{consumed_supply}, /*tx=*/*added_tx);
+ if (added) {
+ insert_tx(/*created_by_tx=*/{outpoints_supply, outpoints_rbf}, /*consumed_by_tx=*/{consumed_supply}, /*tx=*/*tx);
+ } else {
+ insert_tx(/*created_by_tx=*/{consumed_erased}, /*consumed_by_tx=*/{outpoints_supply}, /*tx=*/*tx);
}
+
for (const auto& p : consumed_erased) {
Assert(outpoints_supply.erase(p) == 1);
Assert(outpoints_rbf.erase(p) == 1);
@@ -334,6 +342,7 @@ FUZZ_TARGET_INIT(tx_pool_standard, initialize_tx_pool)
for (const auto& p : consumed_supply) {
Assert(outpoints_supply.erase(p) == 1);
}
+ }
}
}
Finish(fuzzed_data_provider, tx_pool, chainstate);
However, now I am running into an assert in validation, which might be an actual bug :smiling_face_with_tear: