18#include <boost/test/unit_test.hpp>
32 Behaviors(
bool txid_rejects,
bool wtxid_rejects,
bool txid_recon,
bool wtxid_recon,
bool keep,
bool txid_inv,
bool wtxid_inv) :
33 m_txid_in_rejects(txid_rejects),
34 m_wtxid_in_rejects(wtxid_rejects),
35 m_txid_in_rejects_recon(txid_recon),
36 m_wtxid_in_rejects_recon(wtxid_recon),
37 m_keep_for_compact(keep),
38 m_ignore_inv_txid(txid_inv),
39 m_ignore_inv_wtxid(wtxid_inv)
76 bool expect_orphan,
bool expect_keep,
unsigned int expected_parents)
79 if (
ret.m_package_to_validate.has_value()) {
80 err_msg =
strprintf(
"returned a PackageToValidate on missing inputs");
84 if (expect_orphan != txdownload_impl.
m_orphanage->HaveTx(tx->GetWitnessHash())) {
85 err_msg =
strprintf(
"unexpectedly %s tx in orphanage", expect_orphan ?
"did not find" :
"found");
88 if (expect_keep !=
ret.m_should_add_extra_compact_tx) {
89 err_msg =
strprintf(
"unexpectedly returned %s add to vExtraTxnForCompact", expect_keep ?
"should not" :
"should");
92 if (expected_parents !=
ret.m_unique_parents.size()) {
93 err_msg =
strprintf(
"expected %u unique_parents, got %u", expected_parents,
ret.m_unique_parents.size());
102 static Txid prevout_hash{};
105 mtx.
vin.emplace_back(prevout_hash, 0);
107 if (segwit) mtx.
vin[0].scriptWitness.stack.push_back({1});
110 prevout_hash = ptx->GetHash();
123 std::chrono::microseconds now{
GetTime()};
126 for (
const auto segwit_parent : {
true,
false}) {
127 for (
const auto segwit_child : {
true,
false}) {
130 const auto& parent_txid = ptx_parent->GetHash();
131 const auto& parent_wtxid = ptx_parent->GetWitnessHash();
132 const auto& child_txid = ptx_child->GetHash();
133 const auto& child_wtxid = ptx_child->GetWitnessHash();
140 const auto& [keep, unique_txids, package_to_validate] = txdownload_impl.MempoolRejectedTx(ptx_parent, state, nodeid,
true);
145 txdownload_impl.RecentRejectsFilter().contains(parent_txid.ToUint256()),
146 txdownload_impl.RecentRejectsFilter().contains(parent_wtxid.ToUint256()),
147 txdownload_impl.RecentRejectsReconsiderableFilter().contains(parent_txid.ToUint256()),
148 txdownload_impl.RecentRejectsReconsiderableFilter().contains(parent_wtxid.ToUint256()),
150 txdownload_impl.AddTxAnnouncement(nodeid, parent_txid, now),
151 txdownload_impl.AddTxAnnouncement(nodeid, parent_wtxid, now),
153 BOOST_TEST_MESSAGE(
"Testing behavior for " << result << (segwit_parent ?
" segwit " :
" nonsegwit"));
154 actual_behavior.CheckEqual(expected_behavior, segwit_parent);
158 txdownload_impl.MempoolRejectedTx(ptx_child, state, nodeid,
true);
161 const bool parent_txid_rejected{segwit_parent ? expected_behavior.m_txid_in_rejects : expected_behavior.m_wtxid_in_rejects};
162 BOOST_CHECK_EQUAL(parent_txid_rejected, txdownload_impl.RecentRejectsFilter().contains(child_txid.ToUint256()));
163 BOOST_CHECK_EQUAL(parent_txid_rejected, txdownload_impl.RecentRejectsFilter().contains(child_wtxid.ToUint256()));
166 BOOST_CHECK_EQUAL(!parent_txid_rejected, txdownload_impl.m_orphanage->HaveTx(ptx_child->GetWitnessHash()));
189 CAmount amount_depth_2{amount_depth_1 - 1000};
192 int test_chain_height{100};
200 size_t coinbase_idx{0};
202 for (
int decisions = 0; decisions < (1 << 4); ++decisions) {
203 auto mtx_single_parent = CreateValidMempoolTransaction(m_coinbase_txns[coinbase_idx], 0, test_chain_height, coinbaseKey, destination, amount_depth_1,
false);
206 auto mtx_orphan = CreateValidMempoolTransaction(single_parent, 0, test_chain_height, wallet_key, destination, amount_depth_2,
false);
216 const bool parent_recent_rej(decisions & 1);
217 const bool parent_recent_rej_recon((decisions >> 1) & 1);
218 const bool parent_recent_conf((decisions >> 2) & 1);
219 const bool parent_in_mempool((decisions >> 3) & 1);
221 if (parent_recent_rej) txdownload_impl.RecentRejectsFilter().insert(single_parent->GetHash().ToUint256());
222 if (parent_recent_rej_recon) txdownload_impl.RecentRejectsReconsiderableFilter().insert(single_parent->GetHash().ToUint256());
223 if (parent_recent_conf) txdownload_impl.RecentConfirmedTransactionsFilter().insert(single_parent->GetHash().ToUint256());
224 if (parent_in_mempool) {
228 assert(coinbase_idx < m_coinbase_txns.size());
234 const bool expect_keep_orphan = !parent_recent_rej;
235 const unsigned int expected_parents = parent_recent_rej || parent_recent_conf || parent_in_mempool ? 0 : 1;
238 BOOST_CHECK(expect_keep_orphan || expected_parents == 0);
239 const auto ret_1p1c = txdownload_impl.MempoolRejectedTx(orphan, state_orphan, nodeid,
true);
242 expect_keep_orphan,
true, expected_parents);
243 BOOST_CHECK_MESSAGE(ok, err_msg);
248 std::vector<CTransactionRef> parents;
249 std::vector<COutPoint> outpoints;
250 int32_t num_parents{24};
251 for (int32_t i = 0; i < num_parents; ++i) {
252 assert(coinbase_idx < m_coinbase_txns.size());
253 auto mtx_parent = CreateValidMempoolTransaction(m_coinbase_txns[coinbase_idx++], 0, test_chain_height,
254 coinbaseKey, destination, amount_depth_1 + i,
false);
256 parents.emplace_back(ptx_parent);
257 outpoints.emplace_back(ptx_parent->GetHash(), 0);
261 auto mtx_orphan = CreateValidMempoolTransaction(parents, outpoints, test_chain_height, {wallet_key}, {{amount_depth_2 * num_parents, destination}},
false);
269 txdownload_impl.RecentRejectsReconsiderableFilter().insert(parents[0]->GetHash().ToUint256());
270 const auto ret_1p1c_parent_reconsiderable = txdownload_impl.MempoolRejectedTx(orphan, state_orphan, nodeid,
true);
272 const bool ok =
CheckOrphanBehavior(txdownload_impl, orphan, ret_1p1c_parent_reconsiderable, err_msg,
273 true,
true, num_parents);
274 BOOST_CHECK_MESSAGE(ok, err_msg);
282 txdownload_impl.RecentRejectsReconsiderableFilter().insert(parents[0]->GetHash().ToUint256());
283 for (int32_t i = 1; i < num_parents; ++i) {
284 txdownload_impl.RecentConfirmedTransactionsFilter().insert(parents[i]->GetHash().ToUint256());
286 const unsigned int expected_parents = 1;
288 const auto ret_1recon_conf = txdownload_impl.MempoolRejectedTx(orphan, state_orphan, nodeid,
true);
291 true,
true, expected_parents);
292 BOOST_CHECK_MESSAGE(ok, err_msg);
296 for (
int i = 0; i < 2; ++i) {
300 txdownload_impl.RecentRejectsReconsiderableFilter().insert(parents[1]->GetHash().ToUint256());
303 auto& alreadyhave_parent = parents[0];
305 txdownload_impl.RecentRejectsReconsiderableFilter().insert(alreadyhave_parent->GetHash().ToUint256());
307 txdownload_impl.RecentRejectsFilter().insert(alreadyhave_parent->GetHash().ToUint256());
310 const auto ret_2_problems = txdownload_impl.MempoolRejectedTx(orphan, state_orphan, nodeid,
true);
314 BOOST_CHECK_MESSAGE(ok, err_msg);
320 assert(coinbase_idx < m_coinbase_txns.size());
321 auto parent_2outputs =
MakeTransactionRef(CreateValidMempoolTransaction({m_coinbase_txns[coinbase_idx]}, {{m_coinbase_txns[coinbase_idx]->GetHash(), 0}}, test_chain_height, {coinbaseKey},
322 {{amount_split_half, destination}, {amount_split_half, destination}},
false));
324 auto orphan =
MakeTransactionRef(CreateValidMempoolTransaction({parent_2outputs}, {{parent_2outputs->GetHash(), 0}, {parent_2outputs->GetHash(), 1}},
325 test_chain_height, {wallet_key}, {{amount_depth_2, destination}},
false));
332 txdownload_impl.RecentRejectsReconsiderableFilter().insert(parent_2outputs->GetHash().ToUint256());
333 const auto ret_1p1c_2reconsiderable = txdownload_impl.MempoolRejectedTx(orphan, state_orphan, nodeid,
true);
335 const bool ok =
CheckOrphanBehavior(txdownload_impl, orphan, ret_1p1c_2reconsiderable, err_msg,
337 BOOST_CHECK_MESSAGE(ok, err_msg);
CScript GetScriptForDestination(const CTxDestination &dest)
Generate a Bitcoin scriptPubKey for the given CTxDestination.
int64_t CAmount
Amount in satoshis (Can be negative)
static constexpr CAmount COIN
The amount of satoshis in one BTC.
#define Assert(val)
Identity function.
An encapsulated private key.
CPubKey GetPubKey() const
Compute the public key from a private key.
Serialized script, used inside transaction inputs and outputs.
CTxMemPool stores valid-according-to-the-current-best-chain transactions that may be included in the ...
bool Invalid(Result result, const std::string &reject_reason="", const std::string &debug_message="")
std::unique_ptr< TxOrphanage > m_orphanage
Manages unvalidated tx data (orphan transactions for which we are downloading ancestors).
void ConnectedPeer(NodeId nodeid, const TxDownloadConnectionInfo &info)
@ TX_MISSING_INPUTS
transaction was missing some of its inputs
@ TX_MEMPOOL_POLICY
violated mempool's fee/size/descendant/RBF/etc limits
@ TX_UNKNOWN
transaction was not validated because package failed
@ TX_PREMATURE_SPEND
transaction spends a coinbase too early, or violates locktime/sequence locks
@ TX_INPUTS_NOT_STANDARD
inputs (covered by txid) failed policy rules
@ TX_WITNESS_STRIPPED
Transaction is missing a witness.
@ TX_CONFLICT
Tx already in mempool or conflicts with a tx in the chain (if it conflicts with another tx in mempool...
@ TX_NOT_STANDARD
otherwise didn't meet our local policy rules
@ TX_WITNESS_MUTATED
Transaction might have a witness prior to SegWit activation, or witness may have been malleated (whic...
@ TX_NO_MEMPOOL
this node does not have a mempool so can't validate the transaction
@ TX_CONSENSUS
invalid by consensus rules
@ TX_RECONSIDERABLE
fails some policy, but might be acceptable if submitted in a (different) package
RecursiveMutex cs_main
Mutex to guard access to validation specific variables, such as reading or changing the chainstate.
BOOST_FIXTURE_TEST_SUITE(cuckoocache_tests, BasicTestingSetup)
Test Suite for CuckooCache.
BOOST_AUTO_TEST_SUITE_END()
CKey GenerateRandomKey(bool compressed) noexcept
#define BOOST_CHECK_EQUAL(v1, v2)
#define BOOST_CHECK(expr)
static CTransactionRef MakeTransactionRef(Tx &&txIn)
std::shared_ptr< const CTransaction > CTransactionRef
static constexpr CAmount CENT
bool m_txid_in_rejects_recon
Behaviors(bool txid_rejects, bool wtxid_rejects, bool txid_recon, bool wtxid_recon, bool keep, bool txid_inv, bool wtxid_inv)
void CheckEqual(const Behaviors &other, bool segwit)
bool m_wtxid_in_rejects_recon
A mutable version of CTransaction.
std::vector< CTxOut > vout
Testing fixture that pre-creates a 100-block REGTEST-mode block chain.
Testing setup that configures a complete environment.
std::unique_ptr< CTxMemPool > mempool
std::unique_ptr< ChainstateManager > chainman
#define WITH_LOCK(cs, code)
Run code while locking a mutex.
static CTransactionRef CreatePlaceholderTx(bool segwit)
BOOST_FIXTURE_TEST_CASE(tx_rejection_types, TestChain100Setup)
static bool CheckOrphanBehavior(node::TxDownloadManagerImpl &txdownload_impl, const CTransactionRef &tx, const node::RejectedTxTodo &ret, std::string &err_msg, bool expect_orphan, bool expect_keep, unsigned int expected_parents)
static std::map< TxValidationResult, Behaviors > expected_behaviors
int64_t GetTime()
DEPRECATED Use either ClockType::now() or Now<TimePointType>() if a cast is needed.