Branch data Line data Source code
1 : : // Copyright (c) 2023 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::NodeContext;
25 : :
26 : : namespace {
27 : :
28 : : const TestingSetup* g_setup;
29 : : std::vector<COutPoint> g_outpoints_coinbase_init_mature;
30 : :
31 : : struct MockedTxPool : public CTxMemPool {
32 : 39010 : void RollingFeeUpdate() EXCLUSIVE_LOCKS_REQUIRED(!cs)
33 : : {
34 : 39010 : LOCK(cs);
35 [ + - ]: 39010 : lastRollingFeeUpdate = GetTime();
36 [ + - ]: 39010 : blockSinceLastRollingFeeBump = true;
37 : 39010 : }
38 : : };
39 : :
40 : 2 : void initialize_tx_pool()
41 : : {
42 [ + - + - ]: 4 : static const auto testing_setup = MakeNoLogFileContext<const TestingSetup>();
43 : 2 : g_setup = testing_setup.get();
44 : :
45 [ + + ]: 402 : for (int i = 0; i < 2 * COINBASE_MATURITY; ++i) {
46 : 400 : COutPoint prevout{MineBlock(g_setup->m_node, P2WSH_EMPTY)};
47 [ + + ]: 400 : if (i < COINBASE_MATURITY) {
48 : : // Remember the txids to avoid expensive disk access later on
49 : 200 : g_outpoints_coinbase_init_mature.push_back(prevout);
50 : : }
51 : : }
52 : 2 : g_setup->m_node.validation_signals->SyncWithValidationInterfaceQueue();
53 [ + - ]: 4 : }
54 : :
55 : : struct OutpointsUpdater final : public CValidationInterface {
56 : : std::set<COutPoint>& m_mempool_outpoints;
57 : :
58 : 2232 : explicit OutpointsUpdater(std::set<COutPoint>& r)
59 : 2232 : : m_mempool_outpoints{r} {}
60 : :
61 : 21994 : void TransactionAddedToMempool(const NewMempoolTransactionInfo& tx, uint64_t /* mempool_sequence */) override
62 : : {
63 : : // for coins spent we always want to be able to rbf so they're not removed
64 : :
65 : : // outputs from this tx can now be spent
66 [ + + ]: 96551 : for (uint32_t index{0}; index < tx.info.m_tx->vout.size(); ++index) {
67 : 74557 : m_mempool_outpoints.insert(COutPoint{tx.info.m_tx->GetHash(), index});
68 : : }
69 : 21994 : }
70 : :
71 : 6853 : void TransactionRemovedFromMempool(const CTransactionRef& tx, MemPoolRemovalReason reason, uint64_t /* mempool_sequence */) override
72 : : {
73 : : // outpoints spent by this tx are now available
74 [ + + ]: 19216 : for (const auto& input : tx->vin) {
75 : : // Could already exist if this was a replacement
76 : 12363 : m_mempool_outpoints.insert(input.prevout);
77 : : }
78 : : // outpoints created by this tx no longer exist
79 [ + + ]: 29374 : for (uint32_t index{0}; index < tx->vout.size(); ++index) {
80 : 22521 : m_mempool_outpoints.erase(COutPoint{tx->GetHash(), index});
81 : : }
82 : 6853 : }
83 : : };
84 : :
85 : : struct TransactionsDelta final : public CValidationInterface {
86 : : std::set<CTransactionRef>& m_added;
87 : :
88 : 76132 : explicit TransactionsDelta(std::set<CTransactionRef>& a)
89 : 76132 : : m_added{a} {}
90 : :
91 : 4071 : void TransactionAddedToMempool(const NewMempoolTransactionInfo& tx, uint64_t /* mempool_sequence */) override
92 : : {
93 : : // Transactions may be entered and booted any number of times
94 : 4071 : m_added.insert(tx.info.m_tx);
95 : 4071 : }
96 : :
97 : 2805 : void TransactionRemovedFromMempool(const CTransactionRef& tx, MemPoolRemovalReason reason, uint64_t /* mempool_sequence */) override
98 : : {
99 : : // Transactions may be entered and booted any number of times
100 : 2805 : m_added.erase(tx);
101 : 2805 : }
102 : : };
103 : :
104 : 35297 : void MockTime(FuzzedDataProvider& fuzzed_data_provider, const Chainstate& chainstate)
105 : : {
106 : 35297 : const auto time = ConsumeTime(fuzzed_data_provider,
107 [ + - ]: 70594 : chainstate.m_chain.Tip()->GetMedianTimePast() + 1,
108 [ + - ]: 35297 : std::numeric_limits<decltype(chainstate.m_chain.Tip()->nTime)>::max());
109 : 35297 : SetMockTime(time);
110 : 35297 : }
111 : :
112 : 1709 : std::unique_ptr<CTxMemPool> MakeMempool(FuzzedDataProvider& fuzzed_data_provider, const NodeContext& node)
113 : : {
114 : : // Take the default options for tests...
115 : 1709 : CTxMemPool::Options mempool_opts{MemPoolOptionsForTest(node)};
116 : :
117 : :
118 : : // ...override specific options for this specific fuzz suite
119 : 1709 : mempool_opts.limits.ancestor_count = fuzzed_data_provider.ConsumeIntegralInRange<unsigned>(0, 50);
120 : 1709 : mempool_opts.limits.ancestor_size_vbytes = fuzzed_data_provider.ConsumeIntegralInRange<unsigned>(0, 202) * 1'000;
121 : 1709 : mempool_opts.limits.descendant_count = fuzzed_data_provider.ConsumeIntegralInRange<unsigned>(0, 50);
122 : 1709 : mempool_opts.limits.descendant_size_vbytes = fuzzed_data_provider.ConsumeIntegralInRange<unsigned>(0, 202) * 1'000;
123 : 1709 : mempool_opts.max_size_bytes = fuzzed_data_provider.ConsumeIntegralInRange<unsigned>(0, 200) * 1'000'000;
124 : 1709 : mempool_opts.expiry = std::chrono::hours{fuzzed_data_provider.ConsumeIntegralInRange<unsigned>(0, 999)};
125 : : // Only interested in 2 cases: sigop cost 0 or when single legacy sigop cost is >> 1KvB
126 : 1709 : nBytesPerSigOp = fuzzed_data_provider.ConsumeIntegralInRange<unsigned>(0, 1) * 10'000;
127 : :
128 : 1709 : mempool_opts.check_ratio = 1;
129 : 1709 : mempool_opts.require_standard = fuzzed_data_provider.ConsumeBool();
130 : :
131 [ + - ]: 1709 : bilingual_str error;
132 : : // ...and construct a CTxMemPool from it
133 [ + - ]: 1709 : auto mempool{std::make_unique<CTxMemPool>(std::move(mempool_opts), error)};
134 : : // ... ignore the error since it might be beneficial to fuzz even when the
135 : : // mempool size is unreasonably small
136 [ + + + - : 2417 : Assert(error.empty() || error.original.starts_with("-maxmempool must be at least "));
+ - ]
137 : 1709 : return mempool;
138 : 1709 : }
139 : :
140 : 523 : std::unique_ptr<CTxMemPool> MakeEphemeralMempool(const NodeContext& node)
141 : : {
142 : : // Take the default options for tests...
143 : 523 : CTxMemPool::Options mempool_opts{MemPoolOptionsForTest(node)};
144 : :
145 : 523 : mempool_opts.check_ratio = 1;
146 : :
147 : : // Require standardness rules otherwise ephemeral dust is no-op
148 : 523 : mempool_opts.require_standard = true;
149 : :
150 : : // And set minrelay to 0 to allow ephemeral parent tx even with non-TRUC
151 [ + - ]: 523 : mempool_opts.min_relay_feerate = CFeeRate(0);
152 : :
153 [ + - ]: 523 : bilingual_str error;
154 : : // ...and construct a CTxMemPool from it
155 [ + - ]: 523 : auto mempool{std::make_unique<CTxMemPool>(std::move(mempool_opts), error)};
156 [ + - ]: 523 : Assert(error.empty());
157 : 523 : return mempool;
158 : 523 : }
159 : :
160 : : // Scan mempool for a tx that has spent dust and return a
161 : : // prevout of the child that isn't the dusty parent itself.
162 : : // This is used to double-spend the child out of the mempool,
163 : : // leaving the parent childless.
164 : : // This assumes CheckMempoolEphemeralInvariants has passed for tx_pool.
165 : 41261 : std::optional<COutPoint> GetChildEvictingPrevout(const CTxMemPool& tx_pool)
166 : : {
167 : 41261 : LOCK(tx_pool.cs);
168 [ + - + + ]: 1001308 : for (const auto& tx_info : tx_pool.infoAll()) {
169 [ + - + - ]: 964537 : const auto& entry = *Assert(tx_pool.GetEntry(tx_info.tx->GetHash()));
170 [ + - ]: 964537 : std::vector<uint32_t> dust_indexes{GetDust(*tx_info.tx, tx_pool.m_opts.dust_relay_feerate)};
171 [ + + ]: 964537 : if (!dust_indexes.empty()) {
172 [ + + ]: 8749 : const auto& children = entry.GetMemPoolChildrenConst();
173 [ + + ]: 8749 : if (!children.empty()) {
174 [ + - ]: 6026 : Assert(children.size() == 1);
175 : : // Find an input that doesn't spend from parent's txid
176 : 6026 : const auto& only_child = children.begin()->get().GetTx();
177 [ + + ]: 11775 : for (const auto& tx_input : only_child.vin) {
178 [ + + ]: 10239 : if (tx_input.prevout.hash != tx_info.tx->GetHash()) {
179 : 4490 : return tx_input.prevout;
180 : : }
181 : : }
182 : : }
183 : : }
184 : 1001308 : }
185 : :
186 : 36771 : return std::nullopt;
187 : 41261 : }
188 : :
189 [ + - ]: 935 : FUZZ_TARGET(ephemeral_package_eval, .init = initialize_tx_pool)
190 : : {
191 : 523 : FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
192 : 523 : const auto& node = g_setup->m_node;
193 : 523 : auto& chainstate{static_cast<DummyChainState&>(node.chainman->ActiveChainstate())};
194 : :
195 : 523 : MockTime(fuzzed_data_provider, chainstate);
196 : :
197 : : // All RBF-spendable outpoints outside of the unsubmitted package
198 : 523 : std::set<COutPoint> mempool_outpoints;
199 : 523 : std::map<COutPoint, CAmount> outpoints_value;
200 [ + + ]: 52823 : for (const auto& outpoint : g_outpoints_coinbase_init_mature) {
201 [ + - + - ]: 52300 : Assert(mempool_outpoints.insert(outpoint).second);
202 [ + - ]: 52300 : outpoints_value[outpoint] = 50 * COIN;
203 : : }
204 : :
205 [ + - ]: 523 : auto outpoints_updater = std::make_shared<OutpointsUpdater>(mempool_outpoints);
206 [ + - + - ]: 1046 : node.validation_signals->RegisterSharedValidationInterface(outpoints_updater);
207 : :
208 [ + - ]: 523 : auto tx_pool_{MakeEphemeralMempool(node)};
209 : 523 : MockedTxPool& tx_pool = *static_cast<MockedTxPool*>(tx_pool_.get());
210 : :
211 : 523 : chainstate.SetMempool(&tx_pool);
212 : :
213 [ + + + + ]: 71679 : LIMITED_WHILE(fuzzed_data_provider.remaining_bytes() > 0, 300)
214 : : {
215 [ + - ]: 71156 : Assert(!mempool_outpoints.empty());
216 : :
217 : 71156 : std::vector<CTransactionRef> txs;
218 : :
219 : : // Find something we may want to double-spend with two input single tx
220 [ + + + - ]: 71156 : std::optional<COutPoint> outpoint_to_rbf{fuzzed_data_provider.ConsumeBool() ? GetChildEvictingPrevout(tx_pool) : std::nullopt};
221 : :
222 : : // Make small packages
223 [ + + ]: 71156 : const auto num_txs = outpoint_to_rbf ? 1 : (size_t) fuzzed_data_provider.ConsumeIntegralInRange<int>(1, 4);
224 : :
225 : 71156 : std::set<COutPoint> package_outpoints;
226 [ + + ]: 216627 : while (txs.size() < num_txs) {
227 : : // Create transaction to add to the mempool
228 [ + - ]: 290942 : txs.emplace_back([&] {
229 : 145471 : CMutableTransaction tx_mut;
230 : 145471 : tx_mut.version = CTransaction::CURRENT_VERSION;
231 : 145471 : tx_mut.nLockTime = 0;
232 : : // Last transaction in a package needs to be a child of parents to get further in validation
233 : : // so the last transaction to be generated(in a >1 package) must spend all package-made outputs
234 : : // Note that this test currently only spends package outputs in last transaction.
235 [ + + + + ]: 145471 : bool last_tx = num_txs > 1 && txs.size() == num_txs - 1;
236 [ + + ]: 145471 : const auto num_in = outpoint_to_rbf ? 2 :
237 [ + + ]: 140981 : last_tx ? fuzzed_data_provider.ConsumeIntegralInRange<int>(package_outpoints.size()/2 + 1, package_outpoints.size()) :
238 : 96302 : fuzzed_data_provider.ConsumeIntegralInRange<int>(1, 4);
239 [ + + ]: 145471 : const auto num_out = outpoint_to_rbf ? 1 : fuzzed_data_provider.ConsumeIntegralInRange<int>(1, 4);
240 : :
241 [ + + ]: 145471 : auto& outpoints = last_tx ? package_outpoints : mempool_outpoints;
242 : :
243 [ + - - + : 145471 : Assert((int)outpoints.size() >= num_in && num_in > 0);
+ - ]
244 : :
245 : : CAmount amount_in{0};
246 [ + + ]: 558170 : for (int i = 0; i < num_in; ++i) {
247 : : // Pop random outpoint. We erase them to avoid double-spending
248 : : // while in this loop, but later add them back (unless last_tx).
249 : 412699 : auto pop = outpoints.begin();
250 : 412699 : std::advance(pop, fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, outpoints.size() - 1));
251 [ + + ]: 412699 : auto outpoint = *pop;
252 : :
253 [ + + + + ]: 412699 : if (i == 0 && outpoint_to_rbf) {
254 : 4490 : outpoint = *outpoint_to_rbf;
255 : 4490 : outpoints.erase(outpoint);
256 : : } else {
257 : 408209 : outpoints.erase(pop);
258 : : }
259 : : // no need to update or erase from outpoints_value
260 [ + - ]: 412699 : amount_in += outpoints_value.at(outpoint);
261 : :
262 : : // Create input
263 : 412699 : CTxIn in;
264 : 412699 : in.prevout = outpoint;
265 [ + - ]: 412699 : in.scriptWitness.stack = P2WSH_EMPTY_TRUE_STACK;
266 : :
267 [ + - ]: 412699 : tx_mut.vin.push_back(in);
268 : 412699 : }
269 : :
270 : 145471 : const auto amount_fee = fuzzed_data_provider.ConsumeIntegralInRange<CAmount>(0, amount_in);
271 : 145471 : const auto amount_out = (amount_in - amount_fee) / num_out;
272 [ + + ]: 485476 : for (int i = 0; i < num_out; ++i) {
273 [ + - ]: 340005 : tx_mut.vout.emplace_back(amount_out, P2WSH_EMPTY);
274 : : }
275 : :
276 : : // Note output amounts can naturally drop to dust on their own.
277 [ + + + + ]: 145471 : if (!outpoint_to_rbf && fuzzed_data_provider.ConsumeBool()) {
278 : 61237 : uint32_t dust_index = fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(0, num_out);
279 [ + - + - ]: 61237 : tx_mut.vout.insert(tx_mut.vout.begin() + dust_index, CTxOut(0, P2WSH_EMPTY));
280 : : }
281 : :
282 [ + - ]: 145471 : auto tx = MakeTransactionRef(tx_mut);
283 : : // Restore previously removed outpoints, except in-package outpoints (to allow RBF)
284 [ + + ]: 145471 : if (!last_tx) {
285 [ + + ]: 334668 : for (const auto& in : tx->vin) {
286 [ + - + - ]: 233876 : Assert(outpoints.insert(in.prevout).second);
287 : : }
288 : : // Cache the in-package outpoints being made
289 [ + + ]: 370816 : for (size_t i = 0; i < tx->vout.size(); ++i) {
290 [ + - ]: 270024 : package_outpoints.emplace(tx->GetHash(), i);
291 : : }
292 : : }
293 : : // We need newly-created values for the duration of this run
294 [ + + ]: 546713 : for (size_t i = 0; i < tx->vout.size(); ++i) {
295 [ + - ]: 401242 : outpoints_value[COutPoint(tx->GetHash(), i)] = tx->vout[i].nValue;
296 : : }
297 : 145471 : return tx;
298 [ + - ]: 436413 : }());
299 : : }
300 : :
301 [ + + ]: 71156 : if (fuzzed_data_provider.ConsumeBool()) {
302 [ + + ]: 40072 : const auto& txid = fuzzed_data_provider.ConsumeBool() ?
303 : 22142 : txs.back()->GetHash() :
304 : 8965 : PickValue(fuzzed_data_provider, mempool_outpoints).hash;
305 : 31107 : const auto delta = fuzzed_data_provider.ConsumeIntegralInRange<CAmount>(-50 * COIN, +50 * COIN);
306 : : // We only prioritise out of mempool transactions since PrioritiseTransaction doesn't
307 : : // filter for ephemeral dust
308 [ + - + + ]: 31107 : if (tx_pool.exists(GenTxid::Txid(txid))) {
309 [ + - ]: 4973 : const auto tx_info{tx_pool.info(GenTxid::Txid(txid))};
310 [ + - + + ]: 4973 : if (GetDust(*tx_info.tx, tx_pool.m_opts.dust_relay_feerate).empty()) {
311 [ + - ]: 4863 : tx_pool.PrioritiseTransaction(txid.ToUint256(), delta);
312 : : }
313 : 4973 : }
314 : : }
315 : :
316 [ + - ]: 71156 : auto single_submit = txs.size() == 1;
317 : :
318 [ + - ]: 213468 : const auto result_package = WITH_LOCK(::cs_main,
319 : : return ProcessNewPackage(chainstate, tx_pool, txs, /*test_accept=*/single_submit, /*client_maxfeerate=*/{}));
320 : :
321 [ + - + - ]: 213468 : const auto res = WITH_LOCK(::cs_main, return AcceptToMemoryPool(chainstate, txs.back(), GetTime(),
322 : : /*bypass_limits=*/fuzzed_data_provider.ConsumeBool(), /*test_accept=*/!single_submit));
323 : :
324 [ + + + + ]: 71156 : if (!single_submit && result_package.m_state.GetResult() != PackageValidationResult::PCKG_POLICY) {
325 : : // We don't know anything about the validity since transactions were randomly generated, so
326 : : // just use result_package.m_state here. This makes the expect_valid check meaningless, but
327 : : // we can still verify that the contents of m_tx_results are consistent with m_state.
328 [ + - ]: 30009 : const bool expect_valid{result_package.m_state.IsValid()};
329 [ + - + - ]: 60018 : Assert(!CheckPackageMempoolAcceptResult(txs, result_package, expect_valid, &tx_pool));
330 : : }
331 : :
332 [ + - ]: 71156 : node.validation_signals->SyncWithValidationInterfaceQueue();
333 : :
334 [ + - ]: 71156 : CheckMempoolEphemeralInvariants(tx_pool);
335 : 71156 : }
336 : :
337 [ + - + - ]: 1046 : node.validation_signals->UnregisterSharedValidationInterface(outpoints_updater);
338 : :
339 [ + - + - ]: 1569 : WITH_LOCK(::cs_main, tx_pool.check(chainstate.CoinsTip(), chainstate.m_chain.Height() + 1));
340 [ + - ]: 1046 : }
341 : :
342 : :
343 [ + - ]: 2121 : FUZZ_TARGET(tx_package_eval, .init = initialize_tx_pool)
344 : : {
345 : 1709 : FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
346 : 1709 : const auto& node = g_setup->m_node;
347 : 1709 : auto& chainstate{static_cast<DummyChainState&>(node.chainman->ActiveChainstate())};
348 : :
349 : 1709 : MockTime(fuzzed_data_provider, chainstate);
350 : :
351 : : // All RBF-spendable outpoints outside of the unsubmitted package
352 : 1709 : std::set<COutPoint> mempool_outpoints;
353 : 1709 : std::map<COutPoint, CAmount> outpoints_value;
354 [ + + ]: 172609 : for (const auto& outpoint : g_outpoints_coinbase_init_mature) {
355 [ + - + - ]: 170900 : Assert(mempool_outpoints.insert(outpoint).second);
356 [ + - ]: 170900 : outpoints_value[outpoint] = 50 * COIN;
357 : : }
358 : :
359 [ + - ]: 1709 : auto outpoints_updater = std::make_shared<OutpointsUpdater>(mempool_outpoints);
360 [ + - + - ]: 3418 : node.validation_signals->RegisterSharedValidationInterface(outpoints_updater);
361 : :
362 [ + - ]: 1709 : auto tx_pool_{MakeMempool(fuzzed_data_provider, node)};
363 : 1709 : MockedTxPool& tx_pool = *static_cast<MockedTxPool*>(tx_pool_.get());
364 : :
365 : 1709 : chainstate.SetMempool(&tx_pool);
366 : :
367 [ + + + + ]: 77841 : LIMITED_WHILE(fuzzed_data_provider.remaining_bytes() > 0, 300)
368 : : {
369 [ + - ]: 76132 : Assert(!mempool_outpoints.empty());
370 : :
371 : 76132 : std::vector<CTransactionRef> txs;
372 : :
373 : : // Make packages of 1-to-26 transactions
374 : 76132 : const auto num_txs = (size_t) fuzzed_data_provider.ConsumeIntegralInRange<int>(1, 26);
375 : 76132 : std::set<COutPoint> package_outpoints;
376 [ + + ]: 612182 : while (txs.size() < num_txs) {
377 : : // Create transaction to add to the mempool
378 [ + - ]: 1072100 : txs.emplace_back([&] {
379 : 536050 : CMutableTransaction tx_mut;
380 [ + + ]: 536050 : tx_mut.version = fuzzed_data_provider.ConsumeBool() ? TRUC_VERSION : CTransaction::CURRENT_VERSION;
381 [ + + ]: 536050 : tx_mut.nLockTime = fuzzed_data_provider.ConsumeBool() ? 0 : fuzzed_data_provider.ConsumeIntegral<uint32_t>();
382 : : // Last transaction in a package needs to be a child of parents to get further in validation
383 : : // so the last transaction to be generated(in a >1 package) must spend all package-made outputs
384 : : // Note that this test currently only spends package outputs in last transaction.
385 [ + + + + ]: 536050 : bool last_tx = num_txs > 1 && txs.size() == num_txs - 1;
386 : 536050 : const auto num_in = last_tx ? package_outpoints.size() : fuzzed_data_provider.ConsumeIntegralInRange<int>(1, mempool_outpoints.size());
387 : 536050 : auto num_out = fuzzed_data_provider.ConsumeIntegralInRange<int>(1, mempool_outpoints.size() * 2);
388 : :
389 [ + + ]: 536050 : auto& outpoints = last_tx ? package_outpoints : mempool_outpoints;
390 : :
391 [ + - ]: 536050 : Assert(!outpoints.empty());
392 : :
393 : : CAmount amount_in{0};
394 [ + + ]: 8778180 : for (size_t i = 0; i < num_in; ++i) {
395 : : // Pop random outpoint. We erase them to avoid double-spending
396 : : // while in this loop, but later add them back (unless last_tx).
397 : 8242130 : auto pop = outpoints.begin();
398 : 8242130 : std::advance(pop, fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, outpoints.size() - 1));
399 : 8242130 : const auto outpoint = *pop;
400 : 8242130 : outpoints.erase(pop);
401 : : // no need to update or erase from outpoints_value
402 [ + - ]: 8242130 : amount_in += outpoints_value.at(outpoint);
403 : :
404 : : // Create input
405 : 8242130 : const auto sequence = ConsumeSequence(fuzzed_data_provider);
406 : 8242130 : const auto script_sig = CScript{};
407 [ + + + - ]: 11126607 : const auto script_wit_stack = fuzzed_data_provider.ConsumeBool() ? P2WSH_EMPTY_TRUE_STACK : P2WSH_EMPTY_TWO_STACK;
408 : :
409 : 8242130 : CTxIn in;
410 : 8242130 : in.prevout = outpoint;
411 : 8242130 : in.nSequence = sequence;
412 : 8242130 : in.scriptSig = script_sig;
413 [ + - ]: 8242130 : in.scriptWitness.stack = script_wit_stack;
414 : :
415 [ + - ]: 8242130 : tx_mut.vin.push_back(in);
416 : 8242130 : }
417 : :
418 : : // Duplicate an input
419 : 536050 : bool dup_input = fuzzed_data_provider.ConsumeBool();
420 [ + + ]: 536050 : if (dup_input) {
421 [ + - ]: 417168 : tx_mut.vin.push_back(tx_mut.vin.back());
422 : : }
423 : :
424 : : // Refer to a non-existent input
425 [ + + ]: 536050 : if (fuzzed_data_provider.ConsumeBool()) {
426 [ + - ]: 269806 : tx_mut.vin.emplace_back();
427 : : }
428 : :
429 : : // Make a p2pk output to make sigops adjusted vsize to violate TRUC rules, potentially, which is never spent
430 [ + + + + ]: 536050 : if (last_tx && amount_in > 1000 && fuzzed_data_provider.ConsumeBool()) {
431 [ + - + - : 70526 : tx_mut.vout.emplace_back(1000, CScript() << std::vector<unsigned char>(33, 0x02) << OP_CHECKSIG);
+ - ]
432 : : // Don't add any other outputs.
433 : 35263 : num_out = 1;
434 : 35263 : amount_in -= 1000;
435 : : }
436 : :
437 : 536050 : const auto amount_fee = fuzzed_data_provider.ConsumeIntegralInRange<CAmount>(0, amount_in);
438 : 536050 : const auto amount_out = (amount_in - amount_fee) / num_out;
439 [ + + ]: 11616751 : for (int i = 0; i < num_out; ++i) {
440 [ + - ]: 11080701 : tx_mut.vout.emplace_back(amount_out, P2WSH_EMPTY);
441 : : }
442 [ + - ]: 536050 : auto tx = MakeTransactionRef(tx_mut);
443 : : // Restore previously removed outpoints, except in-package outpoints
444 [ + + ]: 536050 : if (!last_tx) {
445 [ + + ]: 5317837 : for (const auto& in : tx->vin) {
446 : : // It's a fake input, or a new input, or a duplicate
447 [ + + + - : 10059297 : Assert(in == CTxIn() || outpoints.insert(in.prevout).second || dup_input);
+ + + - +
- ]
448 : : }
449 : : // Cache the in-package outpoints being made
450 [ + + ]: 11032264 : for (size_t i = 0; i < tx->vout.size(); ++i) {
451 [ + - ]: 10551133 : package_outpoints.emplace(tx->GetHash(), i);
452 : : }
453 : : }
454 : : // We need newly-created values for the duration of this run
455 [ + + ]: 11652014 : for (size_t i = 0; i < tx->vout.size(); ++i) {
456 [ + - ]: 11115964 : outpoints_value[COutPoint(tx->GetHash(), i)] = tx->vout[i].nValue;
457 : : }
458 : 536050 : return tx;
459 [ + - ]: 1608150 : }());
460 : : }
461 : :
462 [ + + ]: 76132 : if (fuzzed_data_provider.ConsumeBool()) {
463 [ + - ]: 33065 : MockTime(fuzzed_data_provider, chainstate);
464 : : }
465 [ + + ]: 76132 : if (fuzzed_data_provider.ConsumeBool()) {
466 [ + - ]: 39010 : tx_pool.RollingFeeUpdate();
467 : : }
468 [ + + ]: 76132 : if (fuzzed_data_provider.ConsumeBool()) {
469 [ + + ]: 65122 : const auto& txid = fuzzed_data_provider.ConsumeBool() ?
470 : 24584 : txs.back()->GetHash() :
471 : 20269 : PickValue(fuzzed_data_provider, mempool_outpoints).hash;
472 : 44853 : const auto delta = fuzzed_data_provider.ConsumeIntegralInRange<CAmount>(-50 * COIN, +50 * COIN);
473 [ + - ]: 44853 : tx_pool.PrioritiseTransaction(txid.ToUint256(), delta);
474 : : }
475 : :
476 : : // Remember all added transactions
477 [ + - ]: 76132 : std::set<CTransactionRef> added;
478 [ + - ]: 76132 : auto txr = std::make_shared<TransactionsDelta>(added);
479 [ + - + - ]: 152264 : node.validation_signals->RegisterSharedValidationInterface(txr);
480 : :
481 : : // When there are multiple transactions in the package, we call ProcessNewPackage(txs, test_accept=false)
482 : : // and AcceptToMemoryPool(txs.back(), test_accept=true). When there is only 1 transaction, we might flip it
483 : : // (the package is a test accept and ATMP is a submission).
484 [ + + + + ]: 97345 : auto single_submit = txs.size() == 1 && fuzzed_data_provider.ConsumeBool();
485 : :
486 : : // Exercise client_maxfeerate logic
487 : 76132 : std::optional<CFeeRate> client_maxfeerate{};
488 [ + + ]: 76132 : if (fuzzed_data_provider.ConsumeBool()) {
489 [ + - ]: 29770 : client_maxfeerate = CFeeRate(fuzzed_data_provider.ConsumeIntegralInRange<CAmount>(-1, 50 * COIN), 100);
490 : : }
491 : :
492 [ + - ]: 228396 : const auto result_package = WITH_LOCK(::cs_main,
493 : : return ProcessNewPackage(chainstate, tx_pool, txs, /*test_accept=*/single_submit, client_maxfeerate));
494 : :
495 : : // Always set bypass_limits to false because it is not supported in ProcessNewPackage and
496 : : // can be a source of divergence.
497 [ + - + - ]: 228396 : const auto res = WITH_LOCK(::cs_main, return AcceptToMemoryPool(chainstate, txs.back(), GetTime(),
498 : : /*bypass_limits=*/false, /*test_accept=*/!single_submit));
499 : 76132 : const bool passed = res.m_result_type == MempoolAcceptResult::ResultType::VALID;
500 : :
501 [ + - ]: 76132 : node.validation_signals->SyncWithValidationInterfaceQueue();
502 [ + - + - ]: 152264 : node.validation_signals->UnregisterSharedValidationInterface(txr);
503 : :
504 : : // There is only 1 transaction in the package. We did a test-package-accept and a ATMP
505 [ + + ]: 76132 : if (single_submit) {
506 [ + - ]: 2082 : Assert(passed != added.empty());
507 [ + - ]: 2082 : Assert(passed == res.m_state.IsValid());
508 [ + + ]: 2082 : if (passed) {
509 [ + - ]: 372 : Assert(added.size() == 1);
510 [ + - ]: 372 : Assert(txs.back() == *added.begin());
511 : : }
512 [ + + ]: 74050 : } else if (result_package.m_state.GetResult() != PackageValidationResult::PCKG_POLICY) {
513 : : // We don't know anything about the validity since transactions were randomly generated, so
514 : : // just use result_package.m_state here. This makes the expect_valid check meaningless, but
515 : : // we can still verify that the contents of m_tx_results are consistent with m_state.
516 [ + - ]: 23416 : const bool expect_valid{result_package.m_state.IsValid()};
517 [ + - + - ]: 46832 : Assert(!CheckPackageMempoolAcceptResult(txs, result_package, expect_valid, &tx_pool));
518 : : } else {
519 : : // This is empty if it fails early checks, or "full" if transactions are looked at deeper
520 [ + + + - : 101006 : Assert(result_package.m_tx_results.size() == txs.size() || result_package.m_tx_results.empty());
+ - ]
521 : : }
522 : :
523 [ + - ]: 76132 : CheckMempoolTRUCInvariants(tx_pool);
524 : :
525 : : // Dust checks only make sense when dust is enforced
526 [ + + ]: 76132 : if (tx_pool.m_opts.require_standard) {
527 [ + - ]: 26549 : CheckMempoolEphemeralInvariants(tx_pool);
528 : : }
529 [ + - ]: 152264 : }
530 : :
531 [ + - + - ]: 3418 : node.validation_signals->UnregisterSharedValidationInterface(outpoints_updater);
532 : :
533 [ + - + - ]: 5127 : WITH_LOCK(::cs_main, tx_pool.check(chainstate.CoinsTip(), chainstate.m_chain.Height() + 1));
534 [ + - ]: 3418 : }
535 : : } // namespace
|