orphanage_tests.cpp
1 // Copyright (c) 2011-2022 The Bitcoin Core developers 2 // Distributed under the MIT software license, see the accompanying 3 // file COPYING or http://www.opensource.org/licenses/mit-license.php. 4 5 #include <arith_uint256.h> 6 #include <consensus/validation.h> 7 #include <policy/policy.h> 8 #include <primitives/transaction.h> 9 #include <pubkey.h> 10 #include <script/sign.h> 11 #include <script/signingprovider.h> 12 #include <test/util/random.h> 13 #include <test/util/setup_common.h> 14 #include <test/util/transaction_utils.h> 15 #include <txorphanage.h> 16 17 #include <array> 18 #include <cstdint> 19 20 #include <boost/test/unit_test.hpp> 21 22 BOOST_FIXTURE_TEST_SUITE(orphanage_tests, TestingSetup) 23 24 class TxOrphanageTest : public TxOrphanage 25 { 26 public: 27 TxOrphanageTest(FastRandomContext& rng) : m_rng{rng} {} 28 29 inline size_t CountOrphans() const 30 { 31 return m_orphans.size(); 32 } 33 34 CTransactionRef RandomOrphan() 35 { 36 std::map<Wtxid, OrphanTx>::iterator it; 37 it = m_orphans.lower_bound(Wtxid::FromUint256(m_rng.rand256())); 38 if (it == m_orphans.end()) 39 it = m_orphans.begin(); 40 return it->second.tx; 41 } 42 43 FastRandomContext& m_rng; 44 }; 45 46 static void MakeNewKeyWithFastRandomContext(CKey& key, FastRandomContext& rand_ctx) 47 { 48 std::vector<unsigned char> keydata; 49 keydata = rand_ctx.randbytes(32); 50 key.Set(keydata.data(), keydata.data() + keydata.size(), /*fCompressedIn=*/true); 51 assert(key.IsValid()); 52 } 53 54 // Creates a transaction with 2 outputs. Spends all outpoints. If outpoints is empty, spends a random one. 55 static CTransactionRef MakeTransactionSpending(const std::vector<COutPoint>& outpoints, FastRandomContext& det_rand) 56 { 57 CKey key; 58 MakeNewKeyWithFastRandomContext(key, det_rand); 59 CMutableTransaction tx; 60 // If no outpoints are given, create a random one. 61 if (outpoints.empty()) { 62 tx.vin.emplace_back(Txid::FromUint256(det_rand.rand256()), 0); 63 } else { 64 for (const auto& outpoint : outpoints) { 65 tx.vin.emplace_back(outpoint); 66 } 67 } 68 // Ensure txid != wtxid 69 tx.vin[0].scriptWitness.stack.push_back({1}); 70 tx.vout.resize(2); 71 tx.vout[0].nValue = CENT; 72 tx.vout[0].scriptPubKey = GetScriptForDestination(PKHash(key.GetPubKey())); 73 tx.vout[1].nValue = 3 * CENT; 74 tx.vout[1].scriptPubKey = GetScriptForDestination(WitnessV0KeyHash(key.GetPubKey())); 75 return MakeTransactionRef(tx); 76 } 77 78 // Make another (not necessarily valid) tx with the same txid but different wtxid. 79 static CTransactionRef MakeMutation(const CTransactionRef& ptx) 80 { 81 CMutableTransaction tx(*ptx); 82 tx.vin[0].scriptWitness.stack.push_back({5}); 83 auto mutated_tx = MakeTransactionRef(tx); 84 assert(ptx->GetHash() == mutated_tx->GetHash()); 85 return mutated_tx; 86 } 87 88 static bool EqualTxns(const std::set<CTransactionRef>& set_txns, const std::vector<CTransactionRef>& vec_txns) 89 { 90 if (vec_txns.size() != set_txns.size()) return false; 91 for (const auto& tx : vec_txns) { 92 if (!set_txns.contains(tx)) return false; 93 } 94 return true; 95 } 96 97 BOOST_AUTO_TEST_CASE(DoS_mapOrphans) 98 { 99 // This test had non-deterministic coverage due to 100 // randomly selected seeds. 101 // This seed is chosen so that all branches of the function 102 // ecdsa_signature_parse_der_lax are executed during this test. 103 // Specifically branches that run only when an ECDSA 104 // signature's R and S values have leading zeros. 105 m_rng.Reseed(uint256{33}); 106 107 TxOrphanageTest orphanage{m_rng}; 108 CKey key; 109 MakeNewKeyWithFastRandomContext(key, m_rng); 110 FillableSigningProvider keystore; 111 BOOST_CHECK(keystore.AddKey(key)); 112 113 // Freeze time for length of test 114 auto now{GetTime<std::chrono::seconds>()}; 115 SetMockTime(now); 116 117 // 50 orphan transactions: 118 for (int i = 0; i < 50; i++) 119 { 120 CMutableTransaction tx; 121 tx.vin.resize(1); 122 tx.vin[0].prevout.n = 0; 123 tx.vin[0].prevout.hash = Txid::FromUint256(m_rng.rand256()); 124 tx.vin[0].scriptSig << OP_1; 125 tx.vout.resize(1); 126 tx.vout[0].nValue = i*CENT; 127 tx.vout[0].scriptPubKey = GetScriptForDestination(PKHash(key.GetPubKey())); 128 129 orphanage.AddTx(MakeTransactionRef(tx), i); 130 } 131 132 // ... and 50 that depend on other orphans: 133 for (int i = 0; i < 50; i++) 134 { 135 CTransactionRef txPrev = orphanage.RandomOrphan(); 136 137 CMutableTransaction tx; 138 tx.vin.resize(1); 139 tx.vin[0].prevout.n = 0; 140 tx.vin[0].prevout.hash = txPrev->GetHash(); 141 tx.vout.resize(1); 142 tx.vout[0].nValue = i*CENT; 143 tx.vout[0].scriptPubKey = GetScriptForDestination(PKHash(key.GetPubKey())); 144 SignatureData empty; 145 BOOST_CHECK(SignSignature(keystore, *txPrev, tx, 0, SIGHASH_ALL, empty)); 146 147 orphanage.AddTx(MakeTransactionRef(tx), i); 148 } 149 150 // This really-big orphan should be ignored: 151 for (int i = 0; i < 10; i++) 152 { 153 CTransactionRef txPrev = orphanage.RandomOrphan(); 154 155 CMutableTransaction tx; 156 tx.vout.resize(1); 157 tx.vout[0].nValue = 1*CENT; 158 tx.vout[0].scriptPubKey = GetScriptForDestination(PKHash(key.GetPubKey())); 159 tx.vin.resize(2777); 160 for (unsigned int j = 0; j < tx.vin.size(); j++) 161 { 162 tx.vin[j].prevout.n = j; 163 tx.vin[j].prevout.hash = txPrev->GetHash(); 164 } 165 SignatureData empty; 166 BOOST_CHECK(SignSignature(keystore, *txPrev, tx, 0, SIGHASH_ALL, empty)); 167 // Reuse same signature for other inputs 168 // (they don't have to be valid for this test) 169 for (unsigned int j = 1; j < tx.vin.size(); j++) 170 tx.vin[j].scriptSig = tx.vin[0].scriptSig; 171 172 BOOST_CHECK(!orphanage.AddTx(MakeTransactionRef(tx), i)); 173 } 174 175 size_t expected_num_orphans = orphanage.CountOrphans(); 176 177 // Non-existent peer; nothing should be deleted 178 orphanage.EraseForPeer(/*peer=*/-1); 179 BOOST_CHECK_EQUAL(orphanage.CountOrphans(), expected_num_orphans); 180 181 // Each of first three peers stored 182 // two transactions each. 183 for (NodeId i = 0; i < 3; i++) 184 { 185 orphanage.EraseForPeer(i); 186 expected_num_orphans -= 2; 187 BOOST_CHECK(orphanage.CountOrphans() == expected_num_orphans); 188 } 189 190 // Test LimitOrphanTxSize() function, nothing should timeout: 191 FastRandomContext rng{/*fDeterministic=*/true}; 192 orphanage.LimitOrphans(/*max_orphans=*/expected_num_orphans, rng); 193 BOOST_CHECK_EQUAL(orphanage.CountOrphans(), expected_num_orphans); 194 expected_num_orphans -= 1; 195 orphanage.LimitOrphans(/*max_orphans=*/expected_num_orphans, rng); 196 BOOST_CHECK_EQUAL(orphanage.CountOrphans(), expected_num_orphans); 197 assert(expected_num_orphans > 40); 198 orphanage.LimitOrphans(40, rng); 199 BOOST_CHECK_EQUAL(orphanage.CountOrphans(), 40); 200 orphanage.LimitOrphans(10, rng); 201 BOOST_CHECK_EQUAL(orphanage.CountOrphans(), 10); 202 orphanage.LimitOrphans(0, rng); 203 BOOST_CHECK_EQUAL(orphanage.CountOrphans(), 0); 204 205 // Add one more orphan, check timeout logic 206 auto timeout_tx = MakeTransactionSpending(/*outpoints=*/{}, rng); 207 orphanage.AddTx(timeout_tx, 0); 208 orphanage.LimitOrphans(1, rng); 209 BOOST_CHECK_EQUAL(orphanage.CountOrphans(), 1); 210 211 // One second shy of expiration 212 SetMockTime(now + ORPHAN_TX_EXPIRE_TIME - 1s); 213 orphanage.LimitOrphans(1, rng); 214 BOOST_CHECK_EQUAL(orphanage.CountOrphans(), 1); 215 216 // Jump one more second, orphan should be timed out on limiting 217 SetMockTime(now + ORPHAN_TX_EXPIRE_TIME); 218 BOOST_CHECK_EQUAL(orphanage.CountOrphans(), 1); 219 orphanage.LimitOrphans(1, rng); 220 BOOST_CHECK_EQUAL(orphanage.CountOrphans(), 0); 221 } 222 223 BOOST_AUTO_TEST_CASE(same_txid_diff_witness) 224 { 225 FastRandomContext det_rand{true}; 226 TxOrphanage orphanage; 227 NodeId peer{0}; 228 229 std::vector<COutPoint> empty_outpoints; 230 auto parent = MakeTransactionSpending(empty_outpoints, det_rand); 231 232 // Create children to go into orphanage. 233 auto child_normal = MakeTransactionSpending({{parent->GetHash(), 0}}, det_rand); 234 auto child_mutated = MakeMutation(child_normal); 235 236 const auto& normal_wtxid = child_normal->GetWitnessHash(); 237 const auto& mutated_wtxid = child_mutated->GetWitnessHash(); 238 BOOST_CHECK(normal_wtxid != mutated_wtxid); 239 240 BOOST_CHECK(orphanage.AddTx(child_normal, peer)); 241 // EraseTx fails as transaction by this wtxid doesn't exist. 242 BOOST_CHECK_EQUAL(orphanage.EraseTx(mutated_wtxid), 0); 243 BOOST_CHECK(orphanage.HaveTx(normal_wtxid)); 244 BOOST_CHECK(orphanage.GetTx(normal_wtxid) == child_normal); 245 BOOST_CHECK(!orphanage.HaveTx(mutated_wtxid)); 246 BOOST_CHECK(orphanage.GetTx(mutated_wtxid) == nullptr); 247 248 // Must succeed. Both transactions should be present in orphanage. 249 BOOST_CHECK(orphanage.AddTx(child_mutated, peer)); 250 BOOST_CHECK(orphanage.HaveTx(normal_wtxid)); 251 BOOST_CHECK(orphanage.HaveTx(mutated_wtxid)); 252 253 // Outpoints map should track all entries: check that both are returned as children of the parent. 254 std::set<CTransactionRef> expected_children{child_normal, child_mutated}; 255 BOOST_CHECK(EqualTxns(expected_children, orphanage.GetChildrenFromSamePeer(parent, peer))); 256 257 // Erase by wtxid: mutated first 258 BOOST_CHECK_EQUAL(orphanage.EraseTx(mutated_wtxid), 1); 259 BOOST_CHECK(orphanage.HaveTx(normal_wtxid)); 260 BOOST_CHECK(!orphanage.HaveTx(mutated_wtxid)); 261 262 BOOST_CHECK_EQUAL(orphanage.EraseTx(normal_wtxid), 1); 263 BOOST_CHECK(!orphanage.HaveTx(normal_wtxid)); 264 BOOST_CHECK(!orphanage.HaveTx(mutated_wtxid)); 265 } 266 267 268 BOOST_AUTO_TEST_CASE(get_children) 269 { 270 FastRandomContext det_rand{true}; 271 std::vector<COutPoint> empty_outpoints; 272 273 auto parent1 = MakeTransactionSpending(empty_outpoints, det_rand); 274 auto parent2 = MakeTransactionSpending(empty_outpoints, det_rand); 275 276 // Make sure these parents have different txids otherwise this test won't make sense. 277 while (parent1->GetHash() == parent2->GetHash()) { 278 parent2 = MakeTransactionSpending(empty_outpoints, det_rand); 279 } 280 281 // Create children to go into orphanage. 282 auto child_p1n0 = MakeTransactionSpending({{parent1->GetHash(), 0}}, det_rand); 283 auto child_p2n1 = MakeTransactionSpending({{parent2->GetHash(), 1}}, det_rand); 284 // Spends the same tx twice. Should not cause duplicates. 285 auto child_p1n0_p1n1 = MakeTransactionSpending({{parent1->GetHash(), 0}, {parent1->GetHash(), 1}}, det_rand); 286 // Spends the same outpoint as previous tx. Should still be returned; don't assume outpoints are unique. 287 auto child_p1n0_p2n0 = MakeTransactionSpending({{parent1->GetHash(), 0}, {parent2->GetHash(), 0}}, det_rand); 288 289 const NodeId node1{1}; 290 const NodeId node2{2}; 291 292 // All orphans provided by node1 293 { 294 TxOrphanage orphanage; 295 BOOST_CHECK(orphanage.AddTx(child_p1n0, node1)); 296 BOOST_CHECK(orphanage.AddTx(child_p2n1, node1)); 297 BOOST_CHECK(orphanage.AddTx(child_p1n0_p1n1, node1)); 298 BOOST_CHECK(orphanage.AddTx(child_p1n0_p2n0, node1)); 299 300 std::set<CTransactionRef> expected_parent1_children{child_p1n0, child_p1n0_p2n0, child_p1n0_p1n1}; 301 std::set<CTransactionRef> expected_parent2_children{child_p2n1, child_p1n0_p2n0}; 302 303 BOOST_CHECK(EqualTxns(expected_parent1_children, orphanage.GetChildrenFromSamePeer(parent1, node1))); 304 BOOST_CHECK(EqualTxns(expected_parent2_children, orphanage.GetChildrenFromSamePeer(parent2, node1))); 305 306 // The peer must match 307 BOOST_CHECK(orphanage.GetChildrenFromSamePeer(parent1, node2).empty()); 308 BOOST_CHECK(orphanage.GetChildrenFromSamePeer(parent2, node2).empty()); 309 310 // There shouldn't be any children of this tx in the orphanage 311 BOOST_CHECK(orphanage.GetChildrenFromSamePeer(child_p1n0_p2n0, node1).empty()); 312 BOOST_CHECK(orphanage.GetChildrenFromSamePeer(child_p1n0_p2n0, node2).empty()); 313 } 314 315 // Orphans provided by node1 and node2 316 { 317 TxOrphanage orphanage; 318 BOOST_CHECK(orphanage.AddTx(child_p1n0, node1)); 319 BOOST_CHECK(orphanage.AddTx(child_p2n1, node1)); 320 BOOST_CHECK(orphanage.AddTx(child_p1n0_p1n1, node2)); 321 BOOST_CHECK(orphanage.AddTx(child_p1n0_p2n0, node2)); 322 323 // +----------------+---------------+----------------------------------+ 324 // | | sender=node1 | sender=node2 | 325 // +----------------+---------------+----------------------------------+ 326 // | spends parent1 | child_p1n0 | child_p1n0_p1n1, child_p1n0_p2n0 | 327 // | spends parent2 | child_p2n1 | child_p1n0_p2n0 | 328 // +----------------+---------------+----------------------------------+ 329 330 // Children of parent1 from node1: 331 { 332 std::set<CTransactionRef> expected_parent1_node1{child_p1n0}; 333 334 BOOST_CHECK(EqualTxns(expected_parent1_node1, orphanage.GetChildrenFromSamePeer(parent1, node1))); 335 } 336 337 // Children of parent2 from node1: 338 { 339 std::set<CTransactionRef> expected_parent2_node1{child_p2n1}; 340 341 BOOST_CHECK(EqualTxns(expected_parent2_node1, orphanage.GetChildrenFromSamePeer(parent2, node1))); 342 } 343 344 // Children of parent1 from node2: 345 { 346 std::set<CTransactionRef> expected_parent1_node2{child_p1n0_p1n1, child_p1n0_p2n0}; 347 348 BOOST_CHECK(EqualTxns(expected_parent1_node2, orphanage.GetChildrenFromSamePeer(parent1, node2))); 349 } 350 351 // Children of parent2 from node2: 352 { 353 std::set<CTransactionRef> expected_parent2_node2{child_p1n0_p2n0}; 354 355 BOOST_CHECK(EqualTxns(expected_parent2_node2, orphanage.GetChildrenFromSamePeer(parent2, node2))); 356 } 357 } 358 } 359 360 BOOST_AUTO_TEST_CASE(too_large_orphan_tx) 361 { 362 TxOrphanage orphanage; 363 CMutableTransaction tx; 364 tx.vin.resize(1); 365 366 // check that txs larger than MAX_STANDARD_TX_WEIGHT are not added to the orphanage 367 BulkTransaction(tx, MAX_STANDARD_TX_WEIGHT + 4); 368 BOOST_CHECK_EQUAL(GetTransactionWeight(CTransaction(tx)), MAX_STANDARD_TX_WEIGHT + 4); 369 BOOST_CHECK(!orphanage.AddTx(MakeTransactionRef(tx), 0)); 370 371 tx.vout.clear(); 372 BulkTransaction(tx, MAX_STANDARD_TX_WEIGHT); 373 BOOST_CHECK_EQUAL(GetTransactionWeight(CTransaction(tx)), MAX_STANDARD_TX_WEIGHT); 374 BOOST_CHECK(orphanage.AddTx(MakeTransactionRef(tx), 0)); 375 } 376 377 BOOST_AUTO_TEST_CASE(process_block) 378 { 379 FastRandomContext det_rand{true}; 380 TxOrphanageTest orphanage{det_rand}; 381 382 // Create outpoints that will be spent by transactions in the block 383 std::vector<COutPoint> outpoints; 384 const uint32_t num_outpoints{6}; 385 outpoints.reserve(num_outpoints); 386 for (uint32_t i{0}; i < num_outpoints; ++i) { 387 // All the hashes should be different, but change the n just in case. 388 outpoints.emplace_back(Txid::FromUint256(det_rand.rand256()), i); 389 } 390 391 CBlock block; 392 const NodeId node{0}; 393 394 auto control_tx = MakeTransactionSpending({}, det_rand); 395 BOOST_CHECK(orphanage.AddTx(control_tx, node)); 396 397 auto bo_tx_same_txid = MakeTransactionSpending({outpoints.at(0)}, det_rand); 398 BOOST_CHECK(orphanage.AddTx(bo_tx_same_txid, node)); 399 block.vtx.emplace_back(bo_tx_same_txid); 400 401 // 2 transactions with the same txid but different witness 402 auto b_tx_same_txid_diff_witness = MakeTransactionSpending({outpoints.at(1)}, det_rand); 403 block.vtx.emplace_back(b_tx_same_txid_diff_witness); 404 405 auto o_tx_same_txid_diff_witness = MakeMutation(b_tx_same_txid_diff_witness); 406 BOOST_CHECK(orphanage.AddTx(o_tx_same_txid_diff_witness, node)); 407 408 // 2 different transactions that spend the same input. 409 auto b_tx_conflict = MakeTransactionSpending({outpoints.at(2)}, det_rand); 410 block.vtx.emplace_back(b_tx_conflict); 411 412 auto o_tx_conflict = MakeTransactionSpending({outpoints.at(2)}, det_rand); 413 BOOST_CHECK(orphanage.AddTx(o_tx_conflict, node)); 414 415 // 2 different transactions that have 1 overlapping input. 416 auto b_tx_conflict_partial = MakeTransactionSpending({outpoints.at(3), outpoints.at(4)}, det_rand); 417 block.vtx.emplace_back(b_tx_conflict_partial); 418 419 auto o_tx_conflict_partial_2 = MakeTransactionSpending({outpoints.at(4), outpoints.at(5)}, det_rand); 420 BOOST_CHECK(orphanage.AddTx(o_tx_conflict_partial_2, node)); 421 422 orphanage.EraseForBlock(block); 423 for (const auto& expected_removed : {bo_tx_same_txid, o_tx_same_txid_diff_witness, o_tx_conflict, o_tx_conflict_partial_2}) { 424 const auto& expected_removed_wtxid = expected_removed->GetWitnessHash(); 425 BOOST_CHECK(!orphanage.HaveTx(expected_removed_wtxid)); 426 } 427 // Only remaining tx is control_tx 428 BOOST_CHECK_EQUAL(orphanage.Size(), 1); 429 BOOST_CHECK(orphanage.HaveTx(control_tx->GetWitnessHash())); 430 } 431 432 BOOST_AUTO_TEST_CASE(multiple_announcers) 433 { 434 const NodeId node0{0}; 435 const NodeId node1{1}; 436 const NodeId node2{2}; 437 size_t expected_total_count{0}; 438 FastRandomContext det_rand{true}; 439 TxOrphanageTest orphanage{det_rand}; 440 441 // Check accounting per peer. 442 // Check that EraseForPeer works with multiple announcers. 443 { 444 auto ptx = MakeTransactionSpending({}, det_rand); 445 const auto& wtxid = ptx->GetWitnessHash(); 446 BOOST_CHECK(orphanage.AddTx(ptx, node0)); 447 BOOST_CHECK(orphanage.HaveTx(wtxid)); 448 expected_total_count += 1; 449 BOOST_CHECK_EQUAL(orphanage.Size(), expected_total_count); 450 451 // Adding again should do nothing. 452 BOOST_CHECK(!orphanage.AddTx(ptx, node0)); 453 BOOST_CHECK_EQUAL(orphanage.Size(), expected_total_count); 454 455 // We can add another tx with the same txid but different witness. 456 auto ptx_mutated{MakeMutation(ptx)}; 457 BOOST_CHECK(orphanage.AddTx(ptx_mutated, node0)); 458 BOOST_CHECK(orphanage.HaveTx(ptx_mutated->GetWitnessHash())); 459 expected_total_count += 1; 460 461 BOOST_CHECK(!orphanage.AddTx(ptx, node0)); 462 463 // Adding a new announcer should not change overall accounting. 464 BOOST_CHECK(orphanage.AddAnnouncer(ptx->GetWitnessHash(), node2)); 465 BOOST_CHECK_EQUAL(orphanage.Size(), expected_total_count); 466 467 // If we already have this announcer, AddAnnouncer returns false. 468 BOOST_CHECK(orphanage.HaveTxFromPeer(ptx->GetWitnessHash(), node2)); 469 BOOST_CHECK(!orphanage.AddAnnouncer(ptx->GetWitnessHash(), node2)); 470 471 // Same with using AddTx for an existing tx, which is equivalent to using AddAnnouncer 472 BOOST_CHECK(!orphanage.AddTx(ptx, node1)); 473 BOOST_CHECK_EQUAL(orphanage.Size(), expected_total_count); 474 475 // if EraseForPeer is called for an orphan with multiple announcers, the orphanage should only 476 // erase that peer from the announcers set. 477 orphanage.EraseForPeer(node0); 478 BOOST_CHECK(orphanage.HaveTx(ptx->GetWitnessHash())); 479 BOOST_CHECK(!orphanage.HaveTxFromPeer(ptx->GetWitnessHash(), node0)); 480 // node0 is the only one that announced ptx_mutated 481 BOOST_CHECK(!orphanage.HaveTx(ptx_mutated->GetWitnessHash())); 482 expected_total_count -= 1; 483 BOOST_CHECK_EQUAL(orphanage.Size(), expected_total_count); 484 485 // EraseForPeer should delete the orphan if it's the only announcer left. 486 orphanage.EraseForPeer(node1); 487 BOOST_CHECK_EQUAL(orphanage.Size(), expected_total_count); 488 BOOST_CHECK(orphanage.HaveTx(ptx->GetWitnessHash())); 489 orphanage.EraseForPeer(node2); 490 expected_total_count -= 1; 491 BOOST_CHECK_EQUAL(orphanage.Size(), expected_total_count); 492 BOOST_CHECK(!orphanage.HaveTx(ptx->GetWitnessHash())); 493 } 494 495 // Check that erasure for blocks removes for all peers. 496 { 497 CBlock block; 498 auto tx_block = MakeTransactionSpending({}, det_rand); 499 block.vtx.emplace_back(tx_block); 500 BOOST_CHECK(orphanage.AddTx(tx_block, node0)); 501 BOOST_CHECK(!orphanage.AddTx(tx_block, node1)); 502 503 expected_total_count += 1; 504 505 BOOST_CHECK_EQUAL(orphanage.Size(), expected_total_count); 506 507 orphanage.EraseForBlock(block); 508 509 expected_total_count -= 1; 510 511 BOOST_CHECK_EQUAL(orphanage.Size(), expected_total_count); 512 } 513 } 514 BOOST_AUTO_TEST_CASE(peer_worksets) 515 { 516 const NodeId node0{0}; 517 const NodeId node1{1}; 518 const NodeId node2{2}; 519 FastRandomContext det_rand{true}; 520 TxOrphanageTest orphanage{det_rand}; 521 // AddChildrenToWorkSet should pick an announcer randomly 522 { 523 auto tx_missing_parent = MakeTransactionSpending({}, det_rand); 524 auto tx_orphan = MakeTransactionSpending({COutPoint{tx_missing_parent->GetHash(), 0}}, det_rand); 525 const auto& orphan_wtxid = tx_orphan->GetWitnessHash(); 526 527 // All 3 peers are announcers. 528 BOOST_CHECK(orphanage.AddTx(tx_orphan, node0)); 529 BOOST_CHECK(!orphanage.AddTx(tx_orphan, node1)); 530 BOOST_CHECK(orphanage.AddAnnouncer(orphan_wtxid, node2)); 531 for (NodeId node = node0; node <= node2; ++node) { 532 BOOST_CHECK(orphanage.HaveTxFromPeer(orphan_wtxid, node)); 533 } 534 535 // Parent accepted: child is added to 1 of 3 worksets. 536 orphanage.AddChildrenToWorkSet(*tx_missing_parent, det_rand); 537 int node0_reconsider = orphanage.HaveTxToReconsider(node0); 538 int node1_reconsider = orphanage.HaveTxToReconsider(node1); 539 int node2_reconsider = orphanage.HaveTxToReconsider(node2); 540 BOOST_CHECK_EQUAL(node0_reconsider + node1_reconsider + node2_reconsider, 1); 541 542 NodeId assigned_peer; 543 if (node0_reconsider) { 544 assigned_peer = node0; 545 } else if (node1_reconsider) { 546 assigned_peer = node1; 547 } else { 548 BOOST_CHECK(node2_reconsider); 549 assigned_peer = node2; 550 } 551 552 // EraseForPeer also removes that tx from the workset. 553 orphanage.EraseForPeer(assigned_peer); 554 BOOST_CHECK_EQUAL(orphanage.GetTxToReconsider(node0), nullptr); 555 556 // Delete this tx, clearing the orphanage. 557 BOOST_CHECK_EQUAL(orphanage.EraseTx(orphan_wtxid), 1); 558 BOOST_CHECK_EQUAL(orphanage.Size(), 0); 559 for (NodeId node = node0; node <= node2; ++node) { 560 BOOST_CHECK_EQUAL(orphanage.GetTxToReconsider(node), nullptr); 561 BOOST_CHECK(!orphanage.HaveTxFromPeer(orphan_wtxid, node)); 562 } 563 } 564 } 565 BOOST_AUTO_TEST_SUITE_END()