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