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:
0diff --git a/src/test/fuzz/tx_pool.cpp b/src/test/fuzz/tx_pool.cpp
1index c38e3420f6..b01b4f978a 100644
2--- a/src/test/fuzz/tx_pool.cpp
3+++ b/src/test/fuzz/tx_pool.cpp
4@@ -70,6 +70,24 @@ struct TransactionsDelta final : public CValidationInterface {
5 }
6 };
7
8+struct TransactionsHistory final : public CValidationInterface {
9+ std::vector<std::tuple<bool, CTransactionRef>>& m_history;
10+
11+ explicit TransactionsHistory(std::vector<std::tuple<bool, CTransactionRef>>& h)
12+ : m_history{h} {}
13+
14+ void TransactionAddedToMempool(const CTransactionRef& tx, uint64_t /* mempool_sequence */) override
15+ {
16+ m_history.emplace_back(true, tx);
17+ }
18+
19+ void TransactionRemovedFromMempool(const CTransactionRef& tx, MemPoolRemovalReason reason, uint64_t /* mempool_sequence */) override
20+ {
21+ if (reason != MemPoolRemovalReason::SIZELIMIT)
22+ m_history.emplace_back(false, tx);
23+ }
24+};
25+
26 void SetMempoolConstraints(ArgsManager& args, FuzzedDataProvider& fuzzed_data_provider)
27 {
28 args.ForceSetArg("-limitancestorcount",
29@@ -247,9 +265,8 @@ FUZZ_TARGET_INIT(tx_pool_standard, initialize_tx_pool)
30 }
31
32 // Remember all removed and added transactions
33- std::set<CTransactionRef> removed;
34- std::set<CTransactionRef> added;
35- auto txr = std::make_shared<TransactionsDelta>(removed, added);
36+ std::vector<std::tuple<bool, CTransactionRef>> history;
37+ auto txr = std::make_shared<TransactionsHistory>(history);
38 RegisterSharedValidationInterface(txr);
39 const bool bypass_limits = fuzzed_data_provider.ConsumeBool();
40
41@@ -283,7 +300,6 @@ FUZZ_TARGET_INIT(tx_pool_standard, initialize_tx_pool)
42 }
43 }
44 if (accepted) {
45- Assert(result_package.m_tx_results.size() == package.size());
46 for (const auto& tx_res : result_package.m_tx_results) {
47 Assert(tx_res.second.m_result_type == MempoolAcceptResult::ResultType::VALID);
48 }
49@@ -291,15 +307,6 @@ FUZZ_TARGET_INIT(tx_pool_standard, initialize_tx_pool)
50 }
51 SyncWithValidationInterfaceQueue();
52 UnregisterSharedValidationInterface(txr);
53- Assert(accepted != added.empty());
54- if (accepted) {
55- Assert(Package(added.begin(), added.end()) == package);
56- } else {
57- // Do not consider rejected transactions removed
58- for (const auto& tx : package) {
59- removed.erase(tx);
60- }
61- }
62
63 // Helper to insert spent and created outpoints of a tx into collections
64 using Sets = std::vector<std::reference_wrapper<std::set<COutPoint>>>;
65@@ -317,16 +324,17 @@ FUZZ_TARGET_INIT(tx_pool_standard, initialize_tx_pool)
66 };
67 // Add created outpoints, remove spent outpoints
68 {
69+ for (const auto& [added,tx]:history){
70 // Outpoints that no longer exist at all
71 std::set<COutPoint> consumed_erased;
72 // Outpoints that no longer count toward the total supply
73 std::set<COutPoint> consumed_supply;
74- for (const auto& removed_tx : removed) {
75- insert_tx(/*created_by_tx=*/{consumed_erased}, /*consumed_by_tx=*/{outpoints_supply}, /*tx=*/*removed_tx);
76- }
77- for (const auto& added_tx : added) {
78- insert_tx(/*created_by_tx=*/{outpoints_supply, outpoints_rbf}, /*consumed_by_tx=*/{consumed_supply}, /*tx=*/*added_tx);
79+ if (added) {
80+ insert_tx(/*created_by_tx=*/{outpoints_supply, outpoints_rbf}, /*consumed_by_tx=*/{consumed_supply}, /*tx=*/*tx);
81+ } else {
82+ insert_tx(/*created_by_tx=*/{consumed_erased}, /*consumed_by_tx=*/{outpoints_supply}, /*tx=*/*tx);
83 }
84+
85 for (const auto& p : consumed_erased) {
86 Assert(outpoints_supply.erase(p) == 1);
87 Assert(outpoints_rbf.erase(p) == 1);
88@@ -334,6 +342,7 @@ FUZZ_TARGET_INIT(tx_pool_standard, initialize_tx_pool)
89 for (const auto& p : consumed_supply) {
90 Assert(outpoints_supply.erase(p) == 1);
91 }
92+ }
93 }
94 }
95 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: