tiny nit: as an extra sanity check, could check we still process a third valid child after rejecting the invalid ones.
<details>
<summary>Click to expand the diff</summary>
diff --git a/src/test/denialofservice_tests.cpp b/src/test/denialofservice_tests.cpp
--- a/src/test/denialofservice_tests.cpp
+++ b/src/test/denialofservice_tests.cpp
@@ -538,30 +538,44 @@
BOOST_CHECK(!connman.ProcessMessagesOnce(peer));
// Create a new parent for the purpose of testing a single orphan is reconsidered per call.
- mineBlocks(1);
- const int child_count{2};
+ mineBlocks(1); // Mature a second coinbase output for the new parent to spend.
+ const int num_missing_input_children{2};
+ const int num_new_children{num_missing_input_children + 1}; // Plus a valid child.
const auto new_parent{MakeTransactionRef(CreateValidMempoolTransaction(
{m_coinbase_txns[1]}, {COutPoint{m_coinbase_txns[1]->GetHash(), 0}}, /*input_height=*/2, {coinbaseKey},
- std::vector<CTxOut>(child_count, CTxOut{5 * COIN, script}), /*submit=*/false))};
+ std::vector<CTxOut>(num_new_children, CTxOut{5 * COIN, script}), /*submit=*/false))};
- // Now create 2 children. Each spends the parent and a non-existing input.
- for (int i{0}; i < child_count; ++i) {
+ // Now create the missing-input children. Each spends the parent and a non-existing input.
+ for (int i{0}; i < num_missing_input_children; ++i) {
CMutableTransaction mtx;
mtx.vin.emplace_back(COutPoint{new_parent->GetHash(), uint32_t(i)});
mtx.vin.emplace_back(COutPoint{Txid::FromUint256(m_rng.rand256()), 0});
mtx.vout.emplace_back(COIN, script);
send_tx(MakeTransactionRef(mtx));
}
- BOOST_CHECK_EQUAL(peerman.GetOrphanTransactions().size(), child_count);
+ // Then a valid child, spending the parent's last output. Orphans are reconsidered oldest
+ // announcement first, so it is queued behind the missing-input children.
+ const auto valid_child{MakeTransactionRef(CreateValidMempoolTransaction(
+ new_parent, /*input_vout=*/num_missing_input_children, /*input_height=*/101, coinbaseKey, script, 4 * COIN, /*submit=*/false))};
+ send_tx(valid_child);
+ BOOST_CHECK_EQUAL(peerman.GetOrphanTransactions().size(), num_new_children);
// Processing the parent will lead to processing a single orphan per run, even though each fails
- // on a missing-input error (orphanage size does not decrease).
+ // on a missing-input error (orphanage size does not decrease). The valid child must wait for a
+ // call of its own.
BOOST_CHECK(send_tx(new_parent));
BOOST_CHECK(mempool.exists(new_parent->GetHash()));
- for (int i{0}; i < child_count; ++i) {
+ for (int i{0}; i < num_missing_input_children; ++i) {
BOOST_CHECK(connman.ProcessMessagesOnce(peer));
- BOOST_CHECK_EQUAL(peerman.GetOrphanTransactions().size(), child_count);
+ BOOST_CHECK(!mempool.exists(valid_child->GetHash()));
+ BOOST_CHECK_EQUAL(peerman.GetOrphanTransactions().size(), num_new_children);
}
+
+ // The next call accepts the valid child.
+ BOOST_CHECK(connman.ProcessMessagesOnce(peer));
+ BOOST_CHECK(mempool.exists(valid_child->GetHash()));
+ BOOST_CHECK_EQUAL(peerman.GetOrphanTransactions().size(), num_missing_input_children);
+ // No more orphans to process.
BOOST_CHECK(!connman.ProcessMessagesOnce(peer));
peerman.FinalizeNode(peer);
</details>