Bitcoin Core 30.99.0
P2P Digital Currency
txvalidation_tests.cpp
Go to the documentation of this file.
1// Copyright (c) 2017-2021 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
6#include <key_io.h>
7#include <policy/packages.h>
8#include <policy/policy.h>
10#include <policy/truc_policy.h>
12#include <random.h>
13#include <script/script.h>
15#include <test/util/txmempool.h>
16#include <validation.h>
17
18#include <boost/test/unit_test.hpp>
19
20
21BOOST_AUTO_TEST_SUITE(txvalidation_tests)
22
23
26BOOST_FIXTURE_TEST_CASE(tx_mempool_reject_coinbase, TestChain100Setup)
27{
28 CScript scriptPubKey = CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG;
29 CMutableTransaction coinbaseTx;
30
31 coinbaseTx.version = 1;
32 coinbaseTx.vin.resize(1);
33 coinbaseTx.vout.resize(1);
34 coinbaseTx.vin[0].scriptSig = CScript() << OP_11 << OP_EQUAL;
35 coinbaseTx.vout[0].nValue = 1 * CENT;
36 coinbaseTx.vout[0].scriptPubKey = scriptPubKey;
37
38 BOOST_CHECK(CTransaction(coinbaseTx).IsCoinBase());
39
41
42 unsigned int initialPoolSize = m_node.mempool->size();
43 const MempoolAcceptResult result = m_node.chainman->ProcessTransaction(MakeTransactionRef(coinbaseTx));
44
46
47 // Check that the transaction hasn't been added to mempool.
48 BOOST_CHECK_EQUAL(m_node.mempool->size(), initialPoolSize);
49
50 // Check that the validation state reflects the unsuccessful attempt.
52 BOOST_CHECK_EQUAL(result.m_state.GetRejectReason(), "coinbase");
54}
55
56// Generate a number of random, nonexistent outpoints.
57static inline std::vector<COutPoint> random_outpoints(size_t num_outpoints) {
58 std::vector<COutPoint> outpoints;
59 for (size_t i{0}; i < num_outpoints; ++i) {
60 outpoints.emplace_back(Txid::FromUint256(GetRandHash()), 0);
61 }
62 return outpoints;
63}
64
65static inline std::vector<CPubKey> random_keys(size_t num_keys) {
66 std::vector<CPubKey> keys;
67 keys.reserve(num_keys);
68 for (size_t i{0}; i < num_keys; ++i) {
69 CKey key;
70 key.MakeNewKey(true);
71 keys.emplace_back(key.GetPubKey());
72 }
73 return keys;
74}
75
76// Creates a placeholder tx (not valid) with 25 outputs. Specify the version and the inputs.
77static inline CTransactionRef make_tx(const std::vector<COutPoint>& inputs, int32_t version)
78{
80 mtx.version = version;
81 mtx.vin.resize(inputs.size());
82 mtx.vout.resize(25);
83 for (size_t i{0}; i < inputs.size(); ++i) {
84 mtx.vin[i].prevout = inputs[i];
85 }
86 for (auto i{0}; i < 25; ++i) {
87 mtx.vout[i].scriptPubKey = CScript() << OP_TRUE;
88 mtx.vout[i].nValue = 10000;
89 }
90 return MakeTransactionRef(mtx);
91}
92
93static constexpr auto NUM_EPHEMERAL_TX_OUTPUTS = 3;
95
96// Same as make_tx but adds 2 normal outputs and 0-value dust to end of vout
97static inline CTransactionRef make_ephemeral_tx(const std::vector<COutPoint>& inputs, int32_t version)
98{
100 mtx.version = version;
101 mtx.vin.resize(inputs.size());
102 for (size_t i{0}; i < inputs.size(); ++i) {
103 mtx.vin[i].prevout = inputs[i];
104 }
105 mtx.vout.resize(NUM_EPHEMERAL_TX_OUTPUTS);
106 for (auto i{0}; i < NUM_EPHEMERAL_TX_OUTPUTS; ++i) {
107 mtx.vout[i].scriptPubKey = CScript() << OP_TRUE;
108 mtx.vout[i].nValue = (i == EPHEMERAL_DUST_INDEX) ? 0 : 10000;
109 }
110 return MakeTransactionRef(mtx);
111}
112
114{
116 LOCK2(cs_main, pool.cs);
118
119 TxValidationState child_state;
120 Wtxid child_wtxid;
121
122 // Arbitrary non-0 feerate for these tests
123 CFeeRate dustrelay(DUST_RELAY_TX_FEE);
124
125 // Basic transaction with dust
126 auto grandparent_tx_1 = make_ephemeral_tx(random_outpoints(1), /*version=*/2);
127 const auto dust_txid = grandparent_tx_1->GetHash();
128
129 // Child transaction spending dust
130 auto dust_spend = make_tx({COutPoint{dust_txid, EPHEMERAL_DUST_INDEX}}, /*version=*/2);
131
132 // We first start with nothing "in the mempool", using package checks
133
134 // Trivial single transaction with no dust
135 BOOST_CHECK(CheckEphemeralSpends({dust_spend}, dustrelay, pool, child_state, child_wtxid));
136 BOOST_CHECK(child_state.IsValid());
137 BOOST_CHECK_EQUAL(child_wtxid, Wtxid());
138
139 // Now with dust, ok because the tx has no dusty parents
140 BOOST_CHECK(CheckEphemeralSpends({grandparent_tx_1}, dustrelay, pool, child_state, child_wtxid));
141 BOOST_CHECK(child_state.IsValid());
142 BOOST_CHECK_EQUAL(child_wtxid, Wtxid());
143
144 // Dust checks pass
145 BOOST_CHECK(CheckEphemeralSpends({grandparent_tx_1, dust_spend}, CFeeRate(0), pool, child_state, child_wtxid));
146 BOOST_CHECK(child_state.IsValid());
147 BOOST_CHECK_EQUAL(child_wtxid, Wtxid());
148 BOOST_CHECK(CheckEphemeralSpends({grandparent_tx_1, dust_spend}, dustrelay, pool, child_state, child_wtxid));
149 BOOST_CHECK(child_state.IsValid());
150 BOOST_CHECK_EQUAL(child_wtxid, Wtxid());
151
152 auto dust_non_spend = make_tx({COutPoint{dust_txid, EPHEMERAL_DUST_INDEX - 1}}, /*version=*/2);
153
154 // Child spending non-dust only from parent should be disallowed even if dust otherwise spent
155 const auto dust_non_spend_wtxid{dust_non_spend->GetWitnessHash()};
156 BOOST_CHECK(!CheckEphemeralSpends({grandparent_tx_1, dust_non_spend, dust_spend}, dustrelay, pool, child_state, child_wtxid));
157 BOOST_CHECK(!child_state.IsValid());
158 BOOST_CHECK_EQUAL(child_wtxid, dust_non_spend_wtxid);
159 child_state = TxValidationState();
160 child_wtxid = Wtxid();
161
162 BOOST_CHECK(!CheckEphemeralSpends({grandparent_tx_1, dust_spend, dust_non_spend}, dustrelay, pool, child_state, child_wtxid));
163 BOOST_CHECK(!child_state.IsValid());
164 BOOST_CHECK_EQUAL(child_wtxid, dust_non_spend_wtxid);
165 child_state = TxValidationState();
166 child_wtxid = Wtxid();
167
168 BOOST_CHECK(!CheckEphemeralSpends({grandparent_tx_1, dust_non_spend}, dustrelay, pool, child_state, child_wtxid));
169 BOOST_CHECK(!child_state.IsValid());
170 BOOST_CHECK_EQUAL(child_wtxid, dust_non_spend_wtxid);
171 child_state = TxValidationState();
172 child_wtxid = Wtxid();
173
174 auto grandparent_tx_2 = make_ephemeral_tx(random_outpoints(1), /*version=*/2);
175 const auto dust_txid_2 = grandparent_tx_2->GetHash();
176
177 // Spend dust from one but not another is ok, as long as second grandparent has no child
178 BOOST_CHECK(CheckEphemeralSpends({grandparent_tx_1, grandparent_tx_2, dust_spend}, dustrelay, pool, child_state, child_wtxid));
179 BOOST_CHECK(child_state.IsValid());
180 BOOST_CHECK_EQUAL(child_wtxid, Wtxid());
181
182 auto dust_non_spend_both_parents = make_tx({COutPoint{dust_txid, EPHEMERAL_DUST_INDEX}, COutPoint{dust_txid_2, EPHEMERAL_DUST_INDEX - 1}}, /*version=*/2);
183 // But if we spend from the parent, it must spend dust
184 BOOST_CHECK(!CheckEphemeralSpends({grandparent_tx_1, grandparent_tx_2, dust_non_spend_both_parents}, dustrelay, pool, child_state, child_wtxid));
185 BOOST_CHECK(!child_state.IsValid());
186 BOOST_CHECK_EQUAL(child_wtxid, dust_non_spend_both_parents->GetWitnessHash());
187 child_state = TxValidationState();
188 child_wtxid = Wtxid();
189
190 auto dust_spend_both_parents = make_tx({COutPoint{dust_txid, EPHEMERAL_DUST_INDEX}, COutPoint{dust_txid_2, EPHEMERAL_DUST_INDEX}}, /*version=*/2);
191 BOOST_CHECK(CheckEphemeralSpends({grandparent_tx_1, grandparent_tx_2, dust_spend_both_parents}, dustrelay, pool, child_state, child_wtxid));
192 BOOST_CHECK(child_state.IsValid());
193 BOOST_CHECK_EQUAL(child_wtxid, Wtxid());
194
195 // Spending other outputs is also correct, as long as the dusty one is spent
196 const std::vector<COutPoint> all_outpoints{COutPoint(dust_txid, 0), COutPoint(dust_txid, 1), COutPoint(dust_txid, 2),
197 COutPoint(dust_txid_2, 0), COutPoint(dust_txid_2, 1), COutPoint(dust_txid_2, 2)};
198 auto dust_spend_all_outpoints = make_tx(all_outpoints, /*version=*/2);
199 BOOST_CHECK(CheckEphemeralSpends({grandparent_tx_1, grandparent_tx_2, dust_spend_all_outpoints}, dustrelay, pool, child_state, child_wtxid));
200 BOOST_CHECK(child_state.IsValid());
201 BOOST_CHECK_EQUAL(child_wtxid, Wtxid());
202
203 // 2 grandparents with dust <- 1 dust-spending parent with dust <- child with no dust
204 auto parent_with_dust = make_ephemeral_tx({COutPoint{dust_txid, EPHEMERAL_DUST_INDEX}, COutPoint{dust_txid_2, EPHEMERAL_DUST_INDEX}}, /*version=*/2);
205 // Ok for parent to have dust
206 BOOST_CHECK(CheckEphemeralSpends({grandparent_tx_1, grandparent_tx_2, parent_with_dust}, dustrelay, pool, child_state, child_wtxid));
207 BOOST_CHECK(child_state.IsValid());
208 BOOST_CHECK_EQUAL(child_wtxid, Wtxid());
209 auto child_no_dust = make_tx({COutPoint{parent_with_dust->GetHash(), EPHEMERAL_DUST_INDEX}}, /*version=*/2);
210 BOOST_CHECK(CheckEphemeralSpends({grandparent_tx_1, grandparent_tx_2, parent_with_dust, child_no_dust}, dustrelay, pool, child_state, child_wtxid));
211 BOOST_CHECK(child_state.IsValid());
212 BOOST_CHECK_EQUAL(child_wtxid, Wtxid());
213
214 // 2 grandparents with dust <- 1 dust-spending parent with dust <- child with dust
215 auto child_with_dust = make_ephemeral_tx({COutPoint{parent_with_dust->GetHash(), EPHEMERAL_DUST_INDEX}}, /*version=*/2);
216 BOOST_CHECK(CheckEphemeralSpends({grandparent_tx_1, grandparent_tx_2, parent_with_dust, child_with_dust}, dustrelay, pool, child_state, child_wtxid));
217 BOOST_CHECK(child_state.IsValid());
218 BOOST_CHECK_EQUAL(child_wtxid, Wtxid());
219
220 // Tests with parents in mempool
221
222 // Nothing in mempool, this should pass for any transaction
223 BOOST_CHECK(CheckEphemeralSpends({grandparent_tx_1}, dustrelay, pool, child_state, child_wtxid));
224 BOOST_CHECK(child_state.IsValid());
225 BOOST_CHECK_EQUAL(child_wtxid, Wtxid());
226
227 // Add first grandparent to mempool and fetch entry
228 TryAddToMempool(pool, entry.FromTx(grandparent_tx_1));
229
230 // Ignores ancestors that aren't direct parents
231 BOOST_CHECK(CheckEphemeralSpends({child_no_dust}, dustrelay, pool, child_state, child_wtxid));
232 BOOST_CHECK(child_state.IsValid());
233 BOOST_CHECK_EQUAL(child_wtxid, Wtxid());
234
235 // Valid spend of dust with grandparent in mempool
236 BOOST_CHECK(CheckEphemeralSpends({parent_with_dust}, dustrelay, pool, child_state, child_wtxid));
237 BOOST_CHECK(child_state.IsValid());
238 BOOST_CHECK_EQUAL(child_wtxid, Wtxid());
239
240 // Second grandparent in same package
241 BOOST_CHECK(CheckEphemeralSpends({parent_with_dust, grandparent_tx_2}, dustrelay, pool, child_state, child_wtxid));
242 BOOST_CHECK(child_state.IsValid());
243 BOOST_CHECK_EQUAL(child_wtxid, Wtxid());
244
245 // Order in package doesn't matter
246 BOOST_CHECK(CheckEphemeralSpends({grandparent_tx_2, parent_with_dust}, dustrelay, pool, child_state, child_wtxid));
247 BOOST_CHECK(child_state.IsValid());
248 BOOST_CHECK_EQUAL(child_wtxid, Wtxid());
249
250 // Add second grandparent to mempool
251 TryAddToMempool(pool, entry.FromTx(grandparent_tx_2));
252
253 // Only spends single dust out of two direct parents
254 BOOST_CHECK(!CheckEphemeralSpends({dust_non_spend_both_parents}, dustrelay, pool, child_state, child_wtxid));
255 BOOST_CHECK(!child_state.IsValid());
256 BOOST_CHECK_EQUAL(child_wtxid, dust_non_spend_both_parents->GetWitnessHash());
257 child_state = TxValidationState();
258 child_wtxid = Wtxid();
259
260 // Spends both parents' dust
261 BOOST_CHECK(CheckEphemeralSpends({parent_with_dust}, dustrelay, pool, child_state, child_wtxid));
262 BOOST_CHECK(child_state.IsValid());
263 BOOST_CHECK_EQUAL(child_wtxid, Wtxid());
264
265 // Now add dusty parent to mempool
266 TryAddToMempool(pool, entry.FromTx(parent_with_dust));
267
268 // Passes dust checks even with non-parent ancestors
269 BOOST_CHECK(CheckEphemeralSpends({child_no_dust}, dustrelay, pool, child_state, child_wtxid));
270 BOOST_CHECK(child_state.IsValid());
271 BOOST_CHECK_EQUAL(child_wtxid, Wtxid());
272}
273
275{
276 // Test TRUC policy helper functions
278 LOCK2(cs_main, pool.cs);
280 std::set<Txid> empty_conflicts_set;
281 std::vector<CTxMemPoolEntry::CTxMemPoolEntryRef> empty_parents;
282
283 auto mempool_tx_v3 = make_tx(random_outpoints(1), /*version=*/3);
284 TryAddToMempool(pool, entry.FromTx(mempool_tx_v3));
285 auto mempool_tx_v2 = make_tx(random_outpoints(1), /*version=*/2);
286 TryAddToMempool(pool, entry.FromTx(mempool_tx_v2));
287
288 // Cannot spend from an unconfirmed TRUC transaction unless this tx is also TRUC.
289 {
290 // mempool_tx_v3
291 // ^
292 // tx_v2_from_v3
293 auto tx_v2_from_v3 = make_tx({COutPoint{mempool_tx_v3->GetHash(), 0}}, /*version=*/2);
294 auto parents_v2_from_v3 = pool.GetParents(entry.FromTx(tx_v2_from_v3));
295 const auto expected_error_str{strprintf("non-version=3 tx %s (wtxid=%s) cannot spend from version=3 tx %s (wtxid=%s)",
296 tx_v2_from_v3->GetHash().ToString(), tx_v2_from_v3->GetWitnessHash().ToString(),
297 mempool_tx_v3->GetHash().ToString(), mempool_tx_v3->GetWitnessHash().ToString())};
298 auto result_v2_from_v3{SingleTRUCChecks(pool, tx_v2_from_v3, parents_v2_from_v3, empty_conflicts_set, GetVirtualTransactionSize(*tx_v2_from_v3))};
299 BOOST_CHECK_EQUAL(result_v2_from_v3->first, expected_error_str);
300 BOOST_CHECK_EQUAL(result_v2_from_v3->second, nullptr);
301
302 Package package_v3_v2{mempool_tx_v3, tx_v2_from_v3};
303 BOOST_CHECK_EQUAL(*PackageTRUCChecks(pool, tx_v2_from_v3, GetVirtualTransactionSize(*tx_v2_from_v3), package_v3_v2, empty_parents), expected_error_str);
304 BOOST_CHECK_EQUAL(*PackageTRUCChecks(pool, tx_v2_from_v3, GetVirtualTransactionSize(*tx_v2_from_v3), {tx_v2_from_v3}, parents_v2_from_v3), expected_error_str);
305
306 // mempool_tx_v3 mempool_tx_v2
307 // ^ ^
308 // tx_v2_from_v2_and_v3
309 auto tx_v2_from_v2_and_v3 = make_tx({COutPoint{mempool_tx_v3->GetHash(), 0}, COutPoint{mempool_tx_v2->GetHash(), 0}}, /*version=*/2);
310 auto parents_2_from_both{pool.GetParents(entry.FromTx(tx_v2_from_v2_and_v3))};
311 const auto expected_error_str_2{strprintf("non-version=3 tx %s (wtxid=%s) cannot spend from version=3 tx %s (wtxid=%s)",
312 tx_v2_from_v2_and_v3->GetHash().ToString(), tx_v2_from_v2_and_v3->GetWitnessHash().ToString(),
313 mempool_tx_v3->GetHash().ToString(), mempool_tx_v3->GetWitnessHash().ToString())};
314 auto result_v2_from_both{SingleTRUCChecks(pool, tx_v2_from_v2_and_v3, parents_2_from_both, empty_conflicts_set, GetVirtualTransactionSize(*tx_v2_from_v2_and_v3))};
315 BOOST_CHECK_EQUAL(result_v2_from_both->first, expected_error_str_2);
316 BOOST_CHECK_EQUAL(result_v2_from_both->second, nullptr);
317
318 Package package_v3_v2_v2{mempool_tx_v3, mempool_tx_v2, tx_v2_from_v2_and_v3};
319 BOOST_CHECK_EQUAL(*PackageTRUCChecks(pool, tx_v2_from_v2_and_v3, GetVirtualTransactionSize(*tx_v2_from_v2_and_v3), package_v3_v2_v2, empty_parents), expected_error_str_2);
320 }
321
322 // TRUC cannot spend from an unconfirmed non-TRUC transaction.
323 {
324 // mempool_tx_v2
325 // ^
326 // tx_v3_from_v2
327 auto tx_v3_from_v2 = make_tx({COutPoint{mempool_tx_v2->GetHash(), 0}}, /*version=*/3);
328 auto parents_v3_from_v2{pool.GetParents(entry.FromTx(tx_v3_from_v2))};
329 const auto expected_error_str{strprintf("version=3 tx %s (wtxid=%s) cannot spend from non-version=3 tx %s (wtxid=%s)",
330 tx_v3_from_v2->GetHash().ToString(), tx_v3_from_v2->GetWitnessHash().ToString(),
331 mempool_tx_v2->GetHash().ToString(), mempool_tx_v2->GetWitnessHash().ToString())};
332 auto result_v3_from_v2{SingleTRUCChecks(pool, tx_v3_from_v2, parents_v3_from_v2, empty_conflicts_set, GetVirtualTransactionSize(*tx_v3_from_v2))};
333 BOOST_CHECK_EQUAL(result_v3_from_v2->first, expected_error_str);
334 BOOST_CHECK_EQUAL(result_v3_from_v2->second, nullptr);
335
336 Package package_v2_v3{mempool_tx_v2, tx_v3_from_v2};
337 BOOST_CHECK_EQUAL(*PackageTRUCChecks(pool, tx_v3_from_v2, GetVirtualTransactionSize(*tx_v3_from_v2), package_v2_v3, empty_parents), expected_error_str);
338 BOOST_CHECK_EQUAL(*PackageTRUCChecks(pool, tx_v3_from_v2, GetVirtualTransactionSize(*tx_v3_from_v2), {tx_v3_from_v2}, parents_v3_from_v2), expected_error_str);
339
340 // mempool_tx_v3 mempool_tx_v2
341 // ^ ^
342 // tx_v3_from_v2_and_v3
343 auto tx_v3_from_v2_and_v3 = make_tx({COutPoint{mempool_tx_v3->GetHash(), 0}, COutPoint{mempool_tx_v2->GetHash(), 0}}, /*version=*/3);
344 auto parents_v3_from_both{pool.GetParents(entry.FromTx(tx_v3_from_v2_and_v3))};
345 const auto expected_error_str_2{strprintf("version=3 tx %s (wtxid=%s) cannot spend from non-version=3 tx %s (wtxid=%s)",
346 tx_v3_from_v2_and_v3->GetHash().ToString(), tx_v3_from_v2_and_v3->GetWitnessHash().ToString(),
347 mempool_tx_v2->GetHash().ToString(), mempool_tx_v2->GetWitnessHash().ToString())};
348 auto result_v3_from_both{SingleTRUCChecks(pool, tx_v3_from_v2_and_v3, parents_v3_from_both, empty_conflicts_set, GetVirtualTransactionSize(*tx_v3_from_v2_and_v3))};
349 BOOST_CHECK_EQUAL(result_v3_from_both->first, expected_error_str_2);
350 BOOST_CHECK_EQUAL(result_v3_from_both->second, nullptr);
351
352 // tx_v3_from_v2_and_v3 also violates TRUC_ANCESTOR_LIMIT.
353 const auto expected_error_str_3{strprintf("tx %s (wtxid=%s) would have too many ancestors",
354 tx_v3_from_v2_and_v3->GetHash().ToString(), tx_v3_from_v2_and_v3->GetWitnessHash().ToString())};
355 Package package_v3_v2_v3{mempool_tx_v3, mempool_tx_v2, tx_v3_from_v2_and_v3};
356 BOOST_CHECK_EQUAL(*PackageTRUCChecks(pool, tx_v3_from_v2_and_v3, GetVirtualTransactionSize(*tx_v3_from_v2_and_v3), package_v3_v2_v3, empty_parents), expected_error_str_3);
357 }
358 // V3 from V3 is ok, and non-V3 from non-V3 is ok.
359 {
360 // mempool_tx_v3
361 // ^
362 // tx_v3_from_v3
363 auto tx_v3_from_v3 = make_tx({COutPoint{mempool_tx_v3->GetHash(), 0}}, /*version=*/3);
364 auto parents_v3{pool.GetParents(entry.FromTx(tx_v3_from_v3))};
365 BOOST_CHECK(SingleTRUCChecks(pool, tx_v3_from_v3, parents_v3, empty_conflicts_set, GetVirtualTransactionSize(*tx_v3_from_v3))
366 == std::nullopt);
367
368 Package package_v3_v3{mempool_tx_v3, tx_v3_from_v3};
369 BOOST_CHECK(PackageTRUCChecks(pool, tx_v3_from_v3, GetVirtualTransactionSize(*tx_v3_from_v3), package_v3_v3, empty_parents) == std::nullopt);
370
371 // mempool_tx_v2
372 // ^
373 // tx_v2_from_v2
374 auto tx_v2_from_v2 = make_tx({COutPoint{mempool_tx_v2->GetHash(), 0}}, /*version=*/2);
375 auto parents_v2{pool.GetParents(entry.FromTx(tx_v2_from_v2))};
376 BOOST_CHECK(SingleTRUCChecks(pool, tx_v2_from_v2, parents_v2, empty_conflicts_set, GetVirtualTransactionSize(*tx_v2_from_v2))
377 == std::nullopt);
378
379 Package package_v2_v2{mempool_tx_v2, tx_v2_from_v2};
380 BOOST_CHECK(PackageTRUCChecks(pool, tx_v2_from_v2, GetVirtualTransactionSize(*tx_v2_from_v2), package_v2_v2, empty_parents) == std::nullopt);
381 }
382
383 // Tx spending TRUC cannot have too many mempool ancestors
384 // Configuration where the tx has multiple direct parents.
385 {
386 Package package_multi_parents;
387 std::vector<COutPoint> mempool_outpoints;
388 mempool_outpoints.emplace_back(mempool_tx_v3->GetHash(), 0);
389 package_multi_parents.emplace_back(mempool_tx_v3);
390 for (size_t i{0}; i < 2; ++i) {
391 auto mempool_tx = make_tx(random_outpoints(i + 1), /*version=*/3);
392 TryAddToMempool(pool, entry.FromTx(mempool_tx));
393 mempool_outpoints.emplace_back(mempool_tx->GetHash(), 0);
394 package_multi_parents.emplace_back(mempool_tx);
395 }
396 auto tx_v3_multi_parent = make_tx(mempool_outpoints, /*version=*/3);
397 package_multi_parents.emplace_back(tx_v3_multi_parent);
398 auto parents{pool.GetParents(entry.FromTx(tx_v3_multi_parent))};
399 BOOST_CHECK_EQUAL(parents.size(), 3);
400 const auto expected_error_str{strprintf("tx %s (wtxid=%s) would have too many ancestors",
401 tx_v3_multi_parent->GetHash().ToString(), tx_v3_multi_parent->GetWitnessHash().ToString())};
402 auto result{SingleTRUCChecks(pool, tx_v3_multi_parent, parents, empty_conflicts_set, GetVirtualTransactionSize(*tx_v3_multi_parent))};
403 BOOST_CHECK_EQUAL(result->first, expected_error_str);
404 BOOST_CHECK_EQUAL(result->second, nullptr);
405
406 BOOST_CHECK_EQUAL(*PackageTRUCChecks(pool, tx_v3_multi_parent, GetVirtualTransactionSize(*tx_v3_multi_parent), package_multi_parents, empty_parents),
407 expected_error_str);
408 }
409
410 // Configuration where the tx is in a multi-generation chain.
411 {
412 Package package_multi_gen;
413 CTransactionRef middle_tx;
414 auto last_outpoint{random_outpoints(1)[0]};
415 for (size_t i{0}; i < 2; ++i) {
416 auto mempool_tx = make_tx({last_outpoint}, /*version=*/3);
417 TryAddToMempool(pool, entry.FromTx(mempool_tx));
418 last_outpoint = COutPoint{mempool_tx->GetHash(), 0};
419 package_multi_gen.emplace_back(mempool_tx);
420 if (i == 1) middle_tx = mempool_tx;
421 }
422 auto tx_v3_multi_gen = make_tx({last_outpoint}, /*version=*/3);
423 package_multi_gen.emplace_back(tx_v3_multi_gen);
424 auto parents{pool.GetParents(entry.FromTx(tx_v3_multi_gen))};
425 const auto expected_error_str{strprintf("tx %s (wtxid=%s) would have too many ancestors",
426 tx_v3_multi_gen->GetHash().ToString(), tx_v3_multi_gen->GetWitnessHash().ToString())};
427 auto result{SingleTRUCChecks(pool, tx_v3_multi_gen, parents, empty_conflicts_set, GetVirtualTransactionSize(*tx_v3_multi_gen))};
428 BOOST_CHECK_EQUAL(result->first, expected_error_str);
429 BOOST_CHECK_EQUAL(result->second, nullptr);
430
431 // Middle tx is what triggers a failure for the grandchild:
432 BOOST_CHECK_EQUAL(*PackageTRUCChecks(pool, middle_tx, GetVirtualTransactionSize(*middle_tx), package_multi_gen, empty_parents), expected_error_str);
433 BOOST_CHECK(PackageTRUCChecks(pool, tx_v3_multi_gen, GetVirtualTransactionSize(*tx_v3_multi_gen), package_multi_gen, empty_parents) == std::nullopt);
434 }
435
436 // Tx spending TRUC cannot be too large in virtual size.
437 auto many_inputs{random_outpoints(100)};
438 many_inputs.emplace_back(mempool_tx_v3->GetHash(), 0);
439 {
440 auto tx_v3_child_big = make_tx(many_inputs, /*version=*/3);
441 const auto vsize{GetVirtualTransactionSize(*tx_v3_child_big)};
442 auto parents{pool.GetParents(entry.FromTx(tx_v3_child_big))};
443 const auto expected_error_str{strprintf("version=3 child tx %s (wtxid=%s) is too big: %u > %u virtual bytes",
444 tx_v3_child_big->GetHash().ToString(), tx_v3_child_big->GetWitnessHash().ToString(), vsize, TRUC_CHILD_MAX_VSIZE)};
445 auto result{SingleTRUCChecks(pool, tx_v3_child_big, parents, empty_conflicts_set, GetVirtualTransactionSize(*tx_v3_child_big))};
446 BOOST_CHECK_EQUAL(result->first, expected_error_str);
447 BOOST_CHECK_EQUAL(result->second, nullptr);
448
449 Package package_child_big{mempool_tx_v3, tx_v3_child_big};
450 BOOST_CHECK_EQUAL(*PackageTRUCChecks(pool, tx_v3_child_big, GetVirtualTransactionSize(*tx_v3_child_big), package_child_big, empty_parents),
451 expected_error_str);
452 }
453
454 // Tx spending TRUC cannot have too many sigops.
455 // This child has 10 P2WSH multisig inputs.
456 auto multisig_outpoints{random_outpoints(10)};
457 multisig_outpoints.emplace_back(mempool_tx_v3->GetHash(), 0);
458 auto keys{random_keys(2)};
459 CScript script_multisig;
460 script_multisig << OP_1;
461 for (const auto& key : keys) {
462 script_multisig << ToByteVector(key);
463 }
464 script_multisig << OP_2 << OP_CHECKMULTISIG;
465 {
466 CMutableTransaction mtx_many_sigops = CMutableTransaction{};
467 mtx_many_sigops.version = TRUC_VERSION;
468 for (const auto& outpoint : multisig_outpoints) {
469 mtx_many_sigops.vin.emplace_back(outpoint);
470 mtx_many_sigops.vin.back().scriptWitness.stack.emplace_back(script_multisig.begin(), script_multisig.end());
471 }
472 mtx_many_sigops.vout.resize(1);
473 mtx_many_sigops.vout.back().scriptPubKey = CScript() << OP_TRUE;
474 mtx_many_sigops.vout.back().nValue = 10000;
475 auto tx_many_sigops{MakeTransactionRef(mtx_many_sigops)};
476
477 auto parents{pool.GetParents(entry.FromTx(tx_many_sigops))};
478 // legacy uses fAccurate = false, and the maximum number of multisig keys is used
479 const int64_t total_sigops{static_cast<int64_t>(tx_many_sigops->vin.size()) * static_cast<int64_t>(script_multisig.GetSigOpCount(/*fAccurate=*/false))};
480 BOOST_CHECK_EQUAL(total_sigops, tx_many_sigops->vin.size() * MAX_PUBKEYS_PER_MULTISIG);
481 const int64_t bip141_vsize{GetVirtualTransactionSize(*tx_many_sigops)};
482 // Weight limit is not reached...
483 BOOST_CHECK(SingleTRUCChecks(pool, tx_many_sigops, parents, empty_conflicts_set, bip141_vsize) == std::nullopt);
484 // ...but sigop limit is.
485 const auto expected_error_str{strprintf("version=3 child tx %s (wtxid=%s) is too big: %u > %u virtual bytes",
486 tx_many_sigops->GetHash().ToString(), tx_many_sigops->GetWitnessHash().ToString(),
488 auto result{SingleTRUCChecks(pool, tx_many_sigops, parents, empty_conflicts_set,
489 GetVirtualTransactionSize(*tx_many_sigops, /*nSigOpCost=*/total_sigops, /*bytes_per_sigop=*/ DEFAULT_BYTES_PER_SIGOP))};
490 BOOST_CHECK_EQUAL(result->first, expected_error_str);
491 BOOST_CHECK_EQUAL(result->second, nullptr);
492
493 Package package_child_sigops{mempool_tx_v3, tx_many_sigops};
494 BOOST_CHECK_EQUAL(*PackageTRUCChecks(pool, tx_many_sigops, total_sigops * DEFAULT_BYTES_PER_SIGOP / WITNESS_SCALE_FACTOR, package_child_sigops, empty_parents),
495 expected_error_str);
496 }
497
498 // Parent + child with TRUC in the mempool. Child is allowed as long as it is under TRUC_CHILD_MAX_VSIZE.
499 auto tx_mempool_v3_child = make_tx({COutPoint{mempool_tx_v3->GetHash(), 0}}, /*version=*/3);
500 {
502 auto parents{pool.GetParents(entry.FromTx(tx_mempool_v3_child))};
503 BOOST_CHECK(SingleTRUCChecks(pool, tx_mempool_v3_child, parents, empty_conflicts_set, GetVirtualTransactionSize(*tx_mempool_v3_child)) == std::nullopt);
504 TryAddToMempool(pool, entry.FromTx(tx_mempool_v3_child));
505
506 Package package_v3_1p1c{mempool_tx_v3, tx_mempool_v3_child};
507 BOOST_CHECK(PackageTRUCChecks(pool, tx_mempool_v3_child, GetVirtualTransactionSize(*tx_mempool_v3_child), package_v3_1p1c, empty_parents) == std::nullopt);
508 }
509
510 // A TRUC transaction cannot have more than 1 descendant. Sibling is returned when exactly 1 exists.
511 {
512 auto tx_v3_child2 = make_tx({COutPoint{mempool_tx_v3->GetHash(), 1}}, /*version=*/3);
513 // Configuration where parent already has 1 other child in mempool
514 auto parents_1sibling{pool.GetParents(entry.FromTx(tx_v3_child2))};
515 const auto expected_error_str{strprintf("tx %s (wtxid=%s) would exceed descendant count limit",
516 mempool_tx_v3->GetHash().ToString(), mempool_tx_v3->GetWitnessHash().ToString())};
517 auto result_with_sibling_eviction{SingleTRUCChecks(pool, tx_v3_child2, parents_1sibling, empty_conflicts_set, GetVirtualTransactionSize(*tx_v3_child2))};
518 BOOST_CHECK_EQUAL(result_with_sibling_eviction->first, expected_error_str);
519 // The other mempool child is returned to allow for sibling eviction.
520 BOOST_CHECK_EQUAL(result_with_sibling_eviction->second, tx_mempool_v3_child);
521
522 // If directly replacing the child, make sure there is no double-counting.
523 BOOST_CHECK(SingleTRUCChecks(pool, tx_v3_child2, parents_1sibling, {tx_mempool_v3_child->GetHash()}, GetVirtualTransactionSize(*tx_v3_child2))
524 == std::nullopt);
525
526 Package package_v3_1p2c{mempool_tx_v3, tx_mempool_v3_child, tx_v3_child2};
527 BOOST_CHECK_EQUAL(*PackageTRUCChecks(pool, tx_v3_child2, GetVirtualTransactionSize(*tx_v3_child2), package_v3_1p2c, empty_parents),
528 expected_error_str);
529
530 // Configuration where parent already has 2 other children in mempool (no sibling eviction allowed). This may happen as the result of a reorg.
531 TryAddToMempool(pool, entry.FromTx(tx_v3_child2));
532 auto tx_v3_child3 = make_tx({COutPoint{mempool_tx_v3->GetHash(), 24}}, /*version=*/3);
533 auto entry_mempool_parent = pool.GetIter(mempool_tx_v3->GetHash()).value();
534 BOOST_CHECK_EQUAL(pool.GetDescendantCount(entry_mempool_parent), 3);
535 auto parents_2siblings{pool.GetParents(entry.FromTx(tx_v3_child3))};
536
537 auto result_2children{SingleTRUCChecks(pool, tx_v3_child3, parents_2siblings, empty_conflicts_set, GetVirtualTransactionSize(*tx_v3_child3))};
538 BOOST_CHECK_EQUAL(result_2children->first, expected_error_str);
539 // The other mempool child is not returned because sibling eviction is not allowed.
540 BOOST_CHECK_EQUAL(result_2children->second, nullptr);
541 }
542
543 // Sibling eviction: parent already has 1 other child, which also has its own child (no sibling eviction allowed). This may happen as the result of a reorg.
544 {
545 auto tx_mempool_grandparent = make_tx(random_outpoints(1), /*version=*/3);
546 auto tx_mempool_sibling = make_tx({COutPoint{tx_mempool_grandparent->GetHash(), 0}}, /*version=*/3);
547 auto tx_mempool_nibling = make_tx({COutPoint{tx_mempool_sibling->GetHash(), 0}}, /*version=*/3);
548 auto tx_to_submit = make_tx({COutPoint{tx_mempool_grandparent->GetHash(), 1}}, /*version=*/3);
549
550 TryAddToMempool(pool, entry.FromTx(tx_mempool_grandparent));
551 TryAddToMempool(pool, entry.FromTx(tx_mempool_sibling));
552 TryAddToMempool(pool, entry.FromTx(tx_mempool_nibling));
553
554 auto parents_3gen{pool.GetParents(entry.FromTx(tx_to_submit))};
555 const auto expected_error_str{strprintf("tx %s (wtxid=%s) would exceed descendant count limit",
556 tx_mempool_grandparent->GetHash().ToString(), tx_mempool_grandparent->GetWitnessHash().ToString())};
557 auto result_3gen{SingleTRUCChecks(pool, tx_to_submit, parents_3gen, empty_conflicts_set, GetVirtualTransactionSize(*tx_to_submit))};
558 BOOST_CHECK_EQUAL(result_3gen->first, expected_error_str);
559 // The other mempool child is not returned because sibling eviction is not allowed.
560 BOOST_CHECK_EQUAL(result_3gen->second, nullptr);
561 }
562
563 // Configuration where tx has multiple generations of descendants is not tested because that is
564 // equivalent to the tx with multiple generations of ancestors.
565}
566
TryAddToMempool(pool, CTxMemPoolEntry(TxGraph::Ref(), tx, fee, 0, 1, 0, false, 4, lp))
node::NodeContext m_node
Definition: bitcoin-gui.cpp:43
#define Assert(val)
Identity function.
Definition: check.h:113
Fee rate in satoshis per virtualbyte: CAmount / vB the feerate is represented internally as FeeFrac.
Definition: feerate.h:35
An encapsulated private key.
Definition: key.h:36
void MakeNewKey(bool fCompressed)
Generate a new private key using a cryptographic PRNG.
Definition: key.cpp:162
CPubKey GetPubKey() const
Compute the public key from a private key.
Definition: key.cpp:183
An outpoint - a combination of a transaction hash and an index n into its vout.
Definition: transaction.h:29
Serialized script, used inside transaction inputs and outputs.
Definition: script.h:413
unsigned int GetSigOpCount(bool fAccurate) const
Pre-version-0.6, Bitcoin always counted CHECKMULTISIGs as 20 sigops.
Definition: script.cpp:159
The basic transaction that is broadcasted on the network and contained in blocks.
Definition: transaction.h:296
CTxMemPool stores valid-according-to-the-current-best-chain transactions that may be included in the ...
Definition: txmempool.h:189
std::optional< txiter > GetIter(const Txid &txid) const EXCLUSIVE_LOCKS_REQUIRED(cs)
Returns an iterator to the given hash, if found.
Definition: txmempool.cpp:678
RecursiveMutex cs
This mutex needs to be locked when accessing mapTx or other members that are guarded by it.
Definition: txmempool.h:263
int64_t GetDescendantCount(txiter it) const
Definition: txmempool.h:277
std::vector< CTxMemPoolEntry::CTxMemPoolEntryRef > GetParents(const CTxMemPoolEntry &entry) const
Definition: txmempool.cpp:71
bool IsValid() const
Definition: validation.h:105
std::string GetRejectReason() const
Definition: validation.h:109
Result GetResult() const
Definition: validation.h:108
bool IsInvalid() const
Definition: validation.h:106
iterator begin()
Definition: prevector.h:263
iterator end()
Definition: prevector.h:265
transaction_identifier represents the two canonical transaction identifier types (txid,...
static transaction_identifier FromUint256(const uint256 &id)
static int32_t GetTransactionWeight(const CTransaction &tx)
Definition: validation.h:132
@ TX_CONSENSUS
invalid by consensus rules
static const int WITNESS_SCALE_FACTOR
Definition: consensus.h:21
RecursiveMutex cs_main
Mutex to guard access to validation specific variables, such as reading or changing the chainstate.
Definition: cs_main.cpp:8
BOOST_AUTO_TEST_SUITE_END()
bool CheckEphemeralSpends(const Package &package, CFeeRate dust_relay_rate, const CTxMemPool &tx_pool, TxValidationState &out_child_state, Wtxid &out_child_wtxid)
Must be called for each transaction(package) if any dust is in the package.
#define BOOST_CHECK_EQUAL(v1, v2)
Definition: object.cpp:18
#define BOOST_CHECK(expr)
Definition: object.cpp:17
std::vector< CTransactionRef > Package
A package is an ordered list of transactions.
Definition: packages.h:45
int64_t GetVirtualTransactionSize(int64_t nWeight, int64_t nSigOpCost, unsigned int bytes_per_sigop)
Compute the virtual transaction size (weight reinterpreted as bytes).
Definition: policy.cpp:381
static constexpr unsigned int DUST_RELAY_TX_FEE
Min feerate for defining dust.
Definition: policy.h:65
static constexpr unsigned int DEFAULT_BYTES_PER_SIGOP
Default for -bytespersigop.
Definition: policy.h:47
static CTransactionRef MakeTransactionRef(Tx &&txIn)
Definition: transaction.h:424
std::shared_ptr< const CTransaction > CTransactionRef
Definition: transaction.h:423
uint256 GetRandHash() noexcept
Generate a random uint256.
Definition: random.h:463
@ OP_2
Definition: script.h:85
@ OP_CHECKMULTISIG
Definition: script.h:192
@ OP_CHECKSIG
Definition: script.h:190
@ OP_EQUAL
Definition: script.h:146
@ OP_1
Definition: script.h:83
@ OP_TRUE
Definition: script.h:84
@ OP_11
Definition: script.h:94
static const int MAX_PUBKEYS_PER_MULTISIG
Definition: script.h:34
std::vector< unsigned char > ToByteVector(const T &in)
Definition: script.h:67
static constexpr CAmount CENT
Definition: setup_common.h:47
A mutable version of CTransaction.
Definition: transaction.h:378
std::vector< CTxOut > vout
Definition: transaction.h:380
std::vector< CTxIn > vin
Definition: transaction.h:379
Validation result for a transaction evaluated by MemPoolAccept (single or package).
Definition: validation.h:127
const ResultType m_result_type
Result type.
Definition: validation.h:136
const TxValidationState m_state
Contains information about why the transaction failed.
Definition: validation.h:139
@ INVALID
‍Fully validated, valid.
Identical to TestingSetup, but chain set to regtest.
Definition: setup_common.h:128
Testing fixture that pre-creates a 100-block REGTEST-mode block chain.
Definition: setup_common.h:146
Definition: txmempool.h:19
CTxMemPoolEntry FromTx(const CMutableTransaction &tx) const
Definition: txmempool.cpp:34
std::unique_ptr< CTxMemPool > mempool
Definition: context.h:68
std::unique_ptr< ChainstateManager > chainman
Definition: context.h:72
#define LOCK2(cs1, cs2)
Definition: sync.h:260
#define LOCK(cs)
Definition: sync.h:259
#define strprintf
Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for...
Definition: tinyformat.h:1172
transaction_identifier< true > Wtxid
Wtxid commits to all transaction fields including the witness.
std::optional< std::pair< std::string, CTransactionRef > > SingleTRUCChecks(const CTxMemPool &pool, const CTransactionRef &ptx, const std::vector< CTxMemPoolEntry::CTxMemPoolEntryRef > &mempool_parents, const std::set< Txid > &direct_conflicts, int64_t vsize)
Must be called for every transaction, even if not TRUC.
std::optional< std::string > PackageTRUCChecks(const CTxMemPool &pool, const CTransactionRef &ptx, int64_t vsize, const Package &package, const std::vector< CTxMemPoolEntry::CTxMemPoolEntryRef > &mempool_parents)
Must be called for every transaction that is submitted within a package, even if not TRUC.
Definition: truc_policy.cpp:58
static constexpr int64_t TRUC_CHILD_MAX_VSIZE
Maximum sigop-adjusted virtual size of a tx which spends from an unconfirmed TRUC transaction.
Definition: truc_policy.h:33
static constexpr decltype(CTransaction::version) TRUC_VERSION
Definition: truc_policy.h:20
static CTransactionRef make_ephemeral_tx(const std::vector< COutPoint > &inputs, int32_t version)
static CTransactionRef make_tx(const std::vector< COutPoint > &inputs, int32_t version)
static std::vector< COutPoint > random_outpoints(size_t num_outpoints)
static constexpr auto NUM_EPHEMERAL_TX_OUTPUTS
BOOST_FIXTURE_TEST_CASE(tx_mempool_reject_coinbase, TestChain100Setup)
Ensure that the mempool won't accept coinbase transactions.
static std::vector< CPubKey > random_keys(size_t num_keys)
static constexpr auto EPHEMERAL_DUST_INDEX