/root/bitcoin/src/test/fuzz/tx_pool.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | // Copyright (c) 2021-2022 The Bitcoin Core developers |
2 | | // Distributed under the MIT software license, see the accompanying |
3 | | // file COPYING or http://www.opensource.org/licenses/mit-license.php. |
4 | | |
5 | | #include <consensus/validation.h> |
6 | | #include <node/context.h> |
7 | | #include <node/mempool_args.h> |
8 | | #include <node/miner.h> |
9 | | #include <policy/truc_policy.h> |
10 | | #include <test/fuzz/FuzzedDataProvider.h> |
11 | | #include <test/fuzz/fuzz.h> |
12 | | #include <test/fuzz/util.h> |
13 | | #include <test/fuzz/util/mempool.h> |
14 | | #include <test/util/mining.h> |
15 | | #include <test/util/script.h> |
16 | | #include <test/util/setup_common.h> |
17 | | #include <test/util/txmempool.h> |
18 | | #include <util/check.h> |
19 | | #include <util/rbf.h> |
20 | | #include <util/translation.h> |
21 | | #include <validation.h> |
22 | | #include <validationinterface.h> |
23 | | |
24 | | using node::BlockAssembler; |
25 | | using node::NodeContext; |
26 | | using util::ToString; |
27 | | |
28 | | namespace { |
29 | | |
30 | | const TestingSetup* g_setup; |
31 | | std::vector<COutPoint> g_outpoints_coinbase_init_mature; |
32 | | std::vector<COutPoint> g_outpoints_coinbase_init_immature; |
33 | | |
34 | | struct MockedTxPool : public CTxMemPool { |
35 | | void RollingFeeUpdate() EXCLUSIVE_LOCKS_REQUIRED(!cs) |
36 | 0 | { |
37 | 0 | LOCK(cs); |
38 | 0 | lastRollingFeeUpdate = GetTime(); |
39 | 0 | blockSinceLastRollingFeeBump = true; |
40 | 0 | } |
41 | | }; |
42 | | |
43 | | void initialize_tx_pool() |
44 | 0 | { |
45 | 0 | static const auto testing_setup = MakeNoLogFileContext<const TestingSetup>(); |
46 | 0 | g_setup = testing_setup.get(); |
47 | 0 | SetMockTime(WITH_LOCK(g_setup->m_node.chainman->GetMutex(), return g_setup->m_node.chainman->ActiveTip()->Time())); |
48 | |
|
49 | 0 | BlockAssembler::Options options; |
50 | 0 | options.coinbase_output_script = P2WSH_OP_TRUE; |
51 | |
|
52 | 0 | for (int i = 0; i < 2 * COINBASE_MATURITY; ++i) { |
53 | 0 | COutPoint prevout{MineBlock(g_setup->m_node, options)}; |
54 | | // Remember the txids to avoid expensive disk access later on |
55 | 0 | auto& outpoints = i < COINBASE_MATURITY ? |
56 | 0 | g_outpoints_coinbase_init_mature : |
57 | 0 | g_outpoints_coinbase_init_immature; |
58 | 0 | outpoints.push_back(prevout); |
59 | 0 | } |
60 | 0 | g_setup->m_node.validation_signals->SyncWithValidationInterfaceQueue(); |
61 | 0 | } |
62 | | |
63 | | struct TransactionsDelta final : public CValidationInterface { |
64 | | std::set<CTransactionRef>& m_removed; |
65 | | std::set<CTransactionRef>& m_added; |
66 | | |
67 | | explicit TransactionsDelta(std::set<CTransactionRef>& r, std::set<CTransactionRef>& a) |
68 | 0 | : m_removed{r}, m_added{a} {} |
69 | | |
70 | | void TransactionAddedToMempool(const NewMempoolTransactionInfo& tx, uint64_t /* mempool_sequence */) override |
71 | 0 | { |
72 | 0 | Assert(m_added.insert(tx.info.m_tx).second); |
73 | 0 | } |
74 | | |
75 | | void TransactionRemovedFromMempool(const CTransactionRef& tx, MemPoolRemovalReason reason, uint64_t /* mempool_sequence */) override |
76 | 0 | { |
77 | 0 | Assert(m_removed.insert(tx).second); |
78 | 0 | } |
79 | | }; |
80 | | |
81 | | void SetMempoolConstraints(ArgsManager& args, FuzzedDataProvider& fuzzed_data_provider) |
82 | 0 | { |
83 | 0 | args.ForceSetArg("-limitancestorcount", |
84 | 0 | ToString(fuzzed_data_provider.ConsumeIntegralInRange<unsigned>(0, 50))); |
85 | 0 | args.ForceSetArg("-limitancestorsize", |
86 | 0 | ToString(fuzzed_data_provider.ConsumeIntegralInRange<unsigned>(0, 202))); |
87 | 0 | args.ForceSetArg("-limitdescendantcount", |
88 | 0 | ToString(fuzzed_data_provider.ConsumeIntegralInRange<unsigned>(0, 50))); |
89 | 0 | args.ForceSetArg("-limitdescendantsize", |
90 | 0 | ToString(fuzzed_data_provider.ConsumeIntegralInRange<unsigned>(0, 202))); |
91 | 0 | args.ForceSetArg("-maxmempool", |
92 | 0 | ToString(fuzzed_data_provider.ConsumeIntegralInRange<unsigned>(0, 200))); |
93 | 0 | args.ForceSetArg("-mempoolexpiry", |
94 | 0 | ToString(fuzzed_data_provider.ConsumeIntegralInRange<unsigned>(0, 999))); |
95 | 0 | } |
96 | | |
97 | | void Finish(FuzzedDataProvider& fuzzed_data_provider, MockedTxPool& tx_pool, Chainstate& chainstate) |
98 | 0 | { |
99 | 0 | WITH_LOCK(::cs_main, tx_pool.check(chainstate.CoinsTip(), chainstate.m_chain.Height() + 1)); |
100 | 0 | { |
101 | 0 | BlockAssembler::Options options; |
102 | 0 | options.nBlockMaxWeight = fuzzed_data_provider.ConsumeIntegralInRange(0U, MAX_BLOCK_WEIGHT); |
103 | 0 | options.blockMinFeeRate = CFeeRate{ConsumeMoney(fuzzed_data_provider, /*max=*/COIN)}; |
104 | 0 | auto assembler = BlockAssembler{chainstate, &tx_pool, options}; |
105 | 0 | auto block_template = assembler.CreateNewBlock(); |
106 | 0 | Assert(block_template->block.vtx.size() >= 1); |
107 | 0 | } |
108 | 0 | const auto info_all = tx_pool.infoAll(); |
109 | 0 | if (!info_all.empty()) { |
110 | 0 | const auto& tx_to_remove = *PickValue(fuzzed_data_provider, info_all).tx; |
111 | 0 | WITH_LOCK(tx_pool.cs, tx_pool.removeRecursive(tx_to_remove, MemPoolRemovalReason::BLOCK /* dummy */)); |
112 | 0 | assert(tx_pool.size() < info_all.size()); |
113 | 0 | WITH_LOCK(::cs_main, tx_pool.check(chainstate.CoinsTip(), chainstate.m_chain.Height() + 1)); |
114 | 0 | } |
115 | 0 | g_setup->m_node.validation_signals->SyncWithValidationInterfaceQueue(); |
116 | 0 | } |
117 | | |
118 | | void MockTime(FuzzedDataProvider& fuzzed_data_provider, const Chainstate& chainstate) |
119 | 0 | { |
120 | 0 | const auto time = ConsumeTime(fuzzed_data_provider, |
121 | 0 | chainstate.m_chain.Tip()->GetMedianTimePast() + 1, |
122 | 0 | std::numeric_limits<decltype(chainstate.m_chain.Tip()->nTime)>::max()); |
123 | 0 | SetMockTime(time); |
124 | 0 | } |
125 | | |
126 | | std::unique_ptr<CTxMemPool> MakeMempool(FuzzedDataProvider& fuzzed_data_provider, const NodeContext& node) |
127 | 0 | { |
128 | | // Take the default options for tests... |
129 | 0 | CTxMemPool::Options mempool_opts{MemPoolOptionsForTest(node)}; |
130 | | |
131 | | // ...override specific options for this specific fuzz suite |
132 | 0 | mempool_opts.check_ratio = 1; |
133 | 0 | mempool_opts.require_standard = fuzzed_data_provider.ConsumeBool(); |
134 | | |
135 | | // ...and construct a CTxMemPool from it |
136 | 0 | bilingual_str error; |
137 | 0 | auto mempool{std::make_unique<CTxMemPool>(std::move(mempool_opts), error)}; |
138 | | // ... ignore the error since it might be beneficial to fuzz even when the |
139 | | // mempool size is unreasonably small |
140 | 0 | Assert(error.empty() || error.original.starts_with("-maxmempool must be at least ")); |
141 | 0 | return mempool; |
142 | 0 | } |
143 | | |
144 | | void CheckATMPInvariants(const MempoolAcceptResult& res, bool txid_in_mempool, bool wtxid_in_mempool) |
145 | 0 | { |
146 | |
|
147 | 0 | switch (res.m_result_type) { |
148 | 0 | case MempoolAcceptResult::ResultType::VALID: |
149 | 0 | { |
150 | 0 | Assert(txid_in_mempool); |
151 | 0 | Assert(wtxid_in_mempool); |
152 | 0 | Assert(res.m_state.IsValid()); |
153 | 0 | Assert(!res.m_state.IsInvalid()); |
154 | 0 | Assert(res.m_vsize); |
155 | 0 | Assert(res.m_base_fees); |
156 | 0 | Assert(res.m_effective_feerate); |
157 | 0 | Assert(res.m_wtxids_fee_calculations); |
158 | 0 | Assert(!res.m_other_wtxid); |
159 | 0 | break; |
160 | 0 | } |
161 | 0 | case MempoolAcceptResult::ResultType::INVALID: |
162 | 0 | { |
163 | | // It may be already in the mempool since in ATMP cases we don't set MEMPOOL_ENTRY or DIFFERENT_WITNESS |
164 | 0 | Assert(!res.m_state.IsValid()); |
165 | 0 | Assert(res.m_state.IsInvalid()); |
166 | |
|
167 | 0 | const bool is_reconsiderable{res.m_state.GetResult() == TxValidationResult::TX_RECONSIDERABLE}; |
168 | 0 | Assert(!res.m_vsize); |
169 | 0 | Assert(!res.m_base_fees); |
170 | | // Fee information is provided if the failure is TX_RECONSIDERABLE. |
171 | | // In other cases, validation may be unable or unwilling to calculate the fees. |
172 | 0 | Assert(res.m_effective_feerate.has_value() == is_reconsiderable); |
173 | 0 | Assert(res.m_wtxids_fee_calculations.has_value() == is_reconsiderable); |
174 | 0 | Assert(!res.m_other_wtxid); |
175 | 0 | break; |
176 | 0 | } |
177 | 0 | case MempoolAcceptResult::ResultType::MEMPOOL_ENTRY: |
178 | 0 | { |
179 | | // ATMP never sets this; only set in package settings |
180 | 0 | Assert(false); |
181 | 0 | break; |
182 | 0 | } |
183 | 0 | case MempoolAcceptResult::ResultType::DIFFERENT_WITNESS: |
184 | 0 | { |
185 | | // ATMP never sets this; only set in package settings |
186 | 0 | Assert(false); |
187 | 0 | break; |
188 | 0 | } |
189 | 0 | } |
190 | 0 | } |
191 | | |
192 | | FUZZ_TARGET(tx_pool_standard, .init = initialize_tx_pool) |
193 | 0 | { |
194 | 0 | SeedRandomStateForTest(SeedRand::ZEROS); |
195 | 0 | FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); |
196 | 0 | const auto& node = g_setup->m_node; |
197 | 0 | auto& chainstate{static_cast<DummyChainState&>(node.chainman->ActiveChainstate())}; |
198 | |
|
199 | 0 | MockTime(fuzzed_data_provider, chainstate); |
200 | | |
201 | | // All RBF-spendable outpoints |
202 | 0 | std::set<COutPoint> outpoints_rbf; |
203 | | // All outpoints counting toward the total supply (subset of outpoints_rbf) |
204 | 0 | std::set<COutPoint> outpoints_supply; |
205 | 0 | for (const auto& outpoint : g_outpoints_coinbase_init_mature) { |
206 | 0 | Assert(outpoints_supply.insert(outpoint).second); |
207 | 0 | } |
208 | 0 | outpoints_rbf = outpoints_supply; |
209 | | |
210 | | // The sum of the values of all spendable outpoints |
211 | 0 | constexpr CAmount SUPPLY_TOTAL{COINBASE_MATURITY * 50 * COIN}; |
212 | |
|
213 | 0 | SetMempoolConstraints(*node.args, fuzzed_data_provider); |
214 | 0 | auto tx_pool_{MakeMempool(fuzzed_data_provider, node)}; |
215 | 0 | MockedTxPool& tx_pool = *static_cast<MockedTxPool*>(tx_pool_.get()); |
216 | |
|
217 | 0 | chainstate.SetMempool(&tx_pool); |
218 | | |
219 | | // Helper to query an amount |
220 | 0 | const CCoinsViewMemPool amount_view{WITH_LOCK(::cs_main, return &chainstate.CoinsTip()), tx_pool}; |
221 | 0 | const auto GetAmount = [&](const COutPoint& outpoint) { |
222 | 0 | auto coin{amount_view.GetCoin(outpoint).value()}; |
223 | 0 | return coin.out.nValue; |
224 | 0 | }; |
225 | |
|
226 | 0 | LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 100) |
227 | 0 | { |
228 | 0 | { |
229 | | // Total supply is the mempool fee + all outpoints |
230 | 0 | CAmount supply_now{WITH_LOCK(tx_pool.cs, return tx_pool.GetTotalFee())}; |
231 | 0 | for (const auto& op : outpoints_supply) { |
232 | 0 | supply_now += GetAmount(op); |
233 | 0 | } |
234 | 0 | Assert(supply_now == SUPPLY_TOTAL); |
235 | 0 | } |
236 | 0 | Assert(!outpoints_supply.empty()); |
237 | | |
238 | | // Create transaction to add to the mempool |
239 | 0 | const CTransactionRef tx = [&] { |
240 | 0 | CMutableTransaction tx_mut; |
241 | 0 | tx_mut.version = fuzzed_data_provider.ConsumeBool() ? TRUC_VERSION : CTransaction::CURRENT_VERSION; |
242 | 0 | tx_mut.nLockTime = fuzzed_data_provider.ConsumeBool() ? 0 : fuzzed_data_provider.ConsumeIntegral<uint32_t>(); |
243 | 0 | const auto num_in = fuzzed_data_provider.ConsumeIntegralInRange<int>(1, outpoints_rbf.size()); |
244 | 0 | const auto num_out = fuzzed_data_provider.ConsumeIntegralInRange<int>(1, outpoints_rbf.size() * 2); |
245 | |
|
246 | 0 | CAmount amount_in{0}; |
247 | 0 | for (int i = 0; i < num_in; ++i) { |
248 | | // Pop random outpoint |
249 | 0 | auto pop = outpoints_rbf.begin(); |
250 | 0 | std::advance(pop, fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, outpoints_rbf.size() - 1)); |
251 | 0 | const auto outpoint = *pop; |
252 | 0 | outpoints_rbf.erase(pop); |
253 | 0 | amount_in += GetAmount(outpoint); |
254 | | |
255 | | // Create input |
256 | 0 | const auto sequence = ConsumeSequence(fuzzed_data_provider); |
257 | 0 | const auto script_sig = CScript{}; |
258 | 0 | const auto script_wit_stack = std::vector<std::vector<uint8_t>>{WITNESS_STACK_ELEM_OP_TRUE}; |
259 | 0 | CTxIn in; |
260 | 0 | in.prevout = outpoint; |
261 | 0 | in.nSequence = sequence; |
262 | 0 | in.scriptSig = script_sig; |
263 | 0 | in.scriptWitness.stack = script_wit_stack; |
264 | |
|
265 | 0 | tx_mut.vin.push_back(in); |
266 | 0 | } |
267 | 0 | const auto amount_fee = fuzzed_data_provider.ConsumeIntegralInRange<CAmount>(-1000, amount_in); |
268 | 0 | const auto amount_out = (amount_in - amount_fee) / num_out; |
269 | 0 | for (int i = 0; i < num_out; ++i) { |
270 | 0 | tx_mut.vout.emplace_back(amount_out, P2WSH_OP_TRUE); |
271 | 0 | } |
272 | 0 | auto tx = MakeTransactionRef(tx_mut); |
273 | | // Restore previously removed outpoints |
274 | 0 | for (const auto& in : tx->vin) { |
275 | 0 | Assert(outpoints_rbf.insert(in.prevout).second); |
276 | 0 | } |
277 | 0 | return tx; |
278 | 0 | }(); |
279 | |
|
280 | 0 | if (fuzzed_data_provider.ConsumeBool()) { |
281 | 0 | MockTime(fuzzed_data_provider, chainstate); |
282 | 0 | } |
283 | 0 | if (fuzzed_data_provider.ConsumeBool()) { |
284 | 0 | tx_pool.RollingFeeUpdate(); |
285 | 0 | } |
286 | 0 | if (fuzzed_data_provider.ConsumeBool()) { |
287 | 0 | const auto& txid = fuzzed_data_provider.ConsumeBool() ? |
288 | 0 | tx->GetHash() : |
289 | 0 | PickValue(fuzzed_data_provider, outpoints_rbf).hash; |
290 | 0 | const auto delta = fuzzed_data_provider.ConsumeIntegralInRange<CAmount>(-50 * COIN, +50 * COIN); |
291 | 0 | tx_pool.PrioritiseTransaction(txid, delta); |
292 | 0 | } |
293 | | |
294 | | // Remember all removed and added transactions |
295 | 0 | std::set<CTransactionRef> removed; |
296 | 0 | std::set<CTransactionRef> added; |
297 | 0 | auto txr = std::make_shared<TransactionsDelta>(removed, added); |
298 | 0 | node.validation_signals->RegisterSharedValidationInterface(txr); |
299 | | |
300 | | // Make sure ProcessNewPackage on one transaction works. |
301 | | // The result is not guaranteed to be the same as what is returned by ATMP. |
302 | 0 | const auto result_package = WITH_LOCK(::cs_main, |
303 | 0 | return ProcessNewPackage(chainstate, tx_pool, {tx}, true, /*client_maxfeerate=*/{})); |
304 | | // If something went wrong due to a package-specific policy, it might not return a |
305 | | // validation result for the transaction. |
306 | 0 | if (result_package.m_state.GetResult() != PackageValidationResult::PCKG_POLICY) { |
307 | 0 | auto it = result_package.m_tx_results.find(tx->GetWitnessHash()); |
308 | 0 | Assert(it != result_package.m_tx_results.end()); |
309 | 0 | Assert(it->second.m_result_type == MempoolAcceptResult::ResultType::VALID || |
310 | 0 | it->second.m_result_type == MempoolAcceptResult::ResultType::INVALID); |
311 | 0 | } |
312 | |
|
313 | 0 | const auto res = WITH_LOCK(::cs_main, return AcceptToMemoryPool(chainstate, tx, GetTime(), /*bypass_limits=*/false, /*test_accept=*/false)); |
314 | 0 | const bool accepted = res.m_result_type == MempoolAcceptResult::ResultType::VALID; |
315 | 0 | node.validation_signals->SyncWithValidationInterfaceQueue(); |
316 | 0 | node.validation_signals->UnregisterSharedValidationInterface(txr); |
317 | |
|
318 | 0 | bool txid_in_mempool = tx_pool.exists(tx->GetHash()); |
319 | 0 | bool wtxid_in_mempool = tx_pool.exists(tx->GetWitnessHash()); |
320 | 0 | CheckATMPInvariants(res, txid_in_mempool, wtxid_in_mempool); |
321 | |
|
322 | 0 | Assert(accepted != added.empty()); |
323 | 0 | if (accepted) { |
324 | 0 | Assert(added.size() == 1); // For now, no package acceptance |
325 | 0 | Assert(tx == *added.begin()); |
326 | 0 | CheckMempoolTRUCInvariants(tx_pool); |
327 | 0 | } else { |
328 | | // Do not consider rejected transaction removed |
329 | 0 | removed.erase(tx); |
330 | 0 | } |
331 | | |
332 | | // Helper to insert spent and created outpoints of a tx into collections |
333 | 0 | using Sets = std::vector<std::reference_wrapper<std::set<COutPoint>>>; |
334 | 0 | const auto insert_tx = [](Sets created_by_tx, Sets consumed_by_tx, const auto& tx) { |
335 | 0 | for (size_t i{0}; i < tx.vout.size(); ++i) { |
336 | 0 | for (auto& set : created_by_tx) { |
337 | 0 | Assert(set.get().emplace(tx.GetHash(), i).second); |
338 | 0 | } |
339 | 0 | } |
340 | 0 | for (const auto& in : tx.vin) { |
341 | 0 | for (auto& set : consumed_by_tx) { |
342 | 0 | Assert(set.get().insert(in.prevout).second); |
343 | 0 | } |
344 | 0 | } |
345 | 0 | }; |
346 | | // Add created outpoints, remove spent outpoints |
347 | 0 | { |
348 | | // Outpoints that no longer exist at all |
349 | 0 | std::set<COutPoint> consumed_erased; |
350 | | // Outpoints that no longer count toward the total supply |
351 | 0 | std::set<COutPoint> consumed_supply; |
352 | 0 | for (const auto& removed_tx : removed) { |
353 | 0 | insert_tx(/*created_by_tx=*/{consumed_erased}, /*consumed_by_tx=*/{outpoints_supply}, /*tx=*/*removed_tx); |
354 | 0 | } |
355 | 0 | for (const auto& added_tx : added) { |
356 | 0 | insert_tx(/*created_by_tx=*/{outpoints_supply, outpoints_rbf}, /*consumed_by_tx=*/{consumed_supply}, /*tx=*/*added_tx); |
357 | 0 | } |
358 | 0 | for (const auto& p : consumed_erased) { |
359 | 0 | Assert(outpoints_supply.erase(p) == 1); |
360 | 0 | Assert(outpoints_rbf.erase(p) == 1); |
361 | 0 | } |
362 | 0 | for (const auto& p : consumed_supply) { |
363 | 0 | Assert(outpoints_supply.erase(p) == 1); |
364 | 0 | } |
365 | 0 | } |
366 | 0 | } |
367 | 0 | Finish(fuzzed_data_provider, tx_pool, chainstate); |
368 | 0 | } |
369 | | |
370 | | FUZZ_TARGET(tx_pool, .init = initialize_tx_pool) |
371 | 0 | { |
372 | 0 | SeedRandomStateForTest(SeedRand::ZEROS); |
373 | 0 | FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); |
374 | 0 | const auto& node = g_setup->m_node; |
375 | 0 | auto& chainstate{static_cast<DummyChainState&>(node.chainman->ActiveChainstate())}; |
376 | |
|
377 | 0 | MockTime(fuzzed_data_provider, chainstate); |
378 | |
|
379 | 0 | std::vector<Txid> txids; |
380 | 0 | txids.reserve(g_outpoints_coinbase_init_mature.size()); |
381 | 0 | for (const auto& outpoint : g_outpoints_coinbase_init_mature) { |
382 | 0 | txids.push_back(outpoint.hash); |
383 | 0 | } |
384 | 0 | for (int i{0}; i <= 3; ++i) { |
385 | | // Add some immature and non-existent outpoints |
386 | 0 | txids.push_back(g_outpoints_coinbase_init_immature.at(i).hash); |
387 | 0 | txids.push_back(Txid::FromUint256(ConsumeUInt256(fuzzed_data_provider))); |
388 | 0 | } |
389 | |
|
390 | 0 | SetMempoolConstraints(*node.args, fuzzed_data_provider); |
391 | 0 | auto tx_pool_{MakeMempool(fuzzed_data_provider, node)}; |
392 | 0 | MockedTxPool& tx_pool = *static_cast<MockedTxPool*>(tx_pool_.get()); |
393 | |
|
394 | 0 | chainstate.SetMempool(&tx_pool); |
395 | | |
396 | | // If we ever bypass limits, do not do TRUC invariants checks |
397 | 0 | bool ever_bypassed_limits{false}; |
398 | |
|
399 | 0 | LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 300) |
400 | 0 | { |
401 | 0 | const auto mut_tx = ConsumeTransaction(fuzzed_data_provider, txids); |
402 | |
|
403 | 0 | if (fuzzed_data_provider.ConsumeBool()) { |
404 | 0 | MockTime(fuzzed_data_provider, chainstate); |
405 | 0 | } |
406 | 0 | if (fuzzed_data_provider.ConsumeBool()) { |
407 | 0 | tx_pool.RollingFeeUpdate(); |
408 | 0 | } |
409 | 0 | if (fuzzed_data_provider.ConsumeBool()) { |
410 | 0 | const auto txid = fuzzed_data_provider.ConsumeBool() ? |
411 | 0 | mut_tx.GetHash() : |
412 | 0 | PickValue(fuzzed_data_provider, txids); |
413 | 0 | const auto delta = fuzzed_data_provider.ConsumeIntegralInRange<CAmount>(-50 * COIN, +50 * COIN); |
414 | 0 | tx_pool.PrioritiseTransaction(txid, delta); |
415 | 0 | } |
416 | |
|
417 | 0 | const bool bypass_limits{fuzzed_data_provider.ConsumeBool()}; |
418 | 0 | ever_bypassed_limits |= bypass_limits; |
419 | |
|
420 | 0 | const auto tx = MakeTransactionRef(mut_tx); |
421 | 0 | const auto res = WITH_LOCK(::cs_main, return AcceptToMemoryPool(chainstate, tx, GetTime(), bypass_limits, /*test_accept=*/false)); |
422 | 0 | const bool accepted = res.m_result_type == MempoolAcceptResult::ResultType::VALID; |
423 | 0 | if (accepted) { |
424 | 0 | txids.push_back(tx->GetHash()); |
425 | 0 | if (!ever_bypassed_limits) { |
426 | 0 | CheckMempoolTRUCInvariants(tx_pool); |
427 | 0 | } |
428 | 0 | } |
429 | 0 | } |
430 | 0 | Finish(fuzzed_data_provider, tx_pool, chainstate); |
431 | 0 | } |
432 | | } // namespace |