17#include <boost/test/unit_test.hpp>
31 Behaviors(
bool txid_rejects,
bool wtxid_rejects,
bool txid_recon,
bool wtxid_recon,
bool keep,
bool txid_inv,
bool wtxid_inv) :
32 m_txid_in_rejects(txid_rejects),
33 m_wtxid_in_rejects(wtxid_rejects),
34 m_txid_in_rejects_recon(txid_recon),
35 m_wtxid_in_rejects_recon(wtxid_recon),
36 m_keep_for_compact(keep),
37 m_ignore_inv_txid(txid_inv),
38 m_ignore_inv_wtxid(wtxid_inv)
75 bool expect_orphan,
bool expect_keep,
unsigned int expected_parents)
78 if (
ret.m_package_to_validate.has_value()) {
79 err_msg =
strprintf(
"returned a PackageToValidate on missing inputs");
84 err_msg =
strprintf(
"unexpectedly %s tx in orphanage", expect_orphan ?
"did not find" :
"found");
87 if (expect_keep !=
ret.m_should_add_extra_compact_tx) {
88 err_msg =
strprintf(
"unexpectedly returned %s add to vExtraTxnForCompact", expect_keep ?
"should not" :
"should");
91 if (expected_parents !=
ret.m_unique_parents.size()) {
92 err_msg =
strprintf(
"expected %u unique_parents, got %u", expected_parents,
ret.m_unique_parents.size());
101 static Txid prevout_hash{};
104 mtx.
vin.emplace_back(prevout_hash, 0);
106 if (segwit) mtx.
vin[0].scriptWitness.stack.push_back({1});
109 prevout_hash = ptx->GetHash();
122 std::chrono::microseconds now{
GetTime()};
125 for (
const auto segwit_parent : {
true,
false}) {
126 for (
const auto segwit_child : {
true,
false}) {
129 const auto& parent_txid = ptx_parent->GetHash().ToUint256();
130 const auto& parent_wtxid = ptx_parent->GetWitnessHash().ToUint256();
131 const auto& child_txid = ptx_child->GetHash().ToUint256();
132 const auto& child_wtxid = ptx_child->GetWitnessHash().ToUint256();
139 const auto& [keep, unique_txids, package_to_validate] = txdownload_impl.MempoolRejectedTx(ptx_parent, state, nodeid,
true);
144 txdownload_impl.RecentRejectsFilter().contains(parent_txid),
145 txdownload_impl.RecentRejectsFilter().contains(parent_wtxid),
146 txdownload_impl.RecentRejectsReconsiderableFilter().contains(parent_txid),
147 txdownload_impl.RecentRejectsReconsiderableFilter().contains(parent_wtxid),
149 txdownload_impl.AddTxAnnouncement(nodeid,
GenTxid::Txid(parent_txid), now,
true),
150 txdownload_impl.AddTxAnnouncement(nodeid,
GenTxid::Wtxid(parent_wtxid), now,
true),
152 BOOST_TEST_MESSAGE(
"Testing behavior for " << result << (segwit_parent ?
" segwit " :
" nonsegwit"));
153 actual_behavior.CheckEqual(expected_behavior, segwit_parent);
157 txdownload_impl.MempoolRejectedTx(ptx_child, state, nodeid,
true);
160 const bool parent_txid_rejected{segwit_parent ? expected_behavior.m_txid_in_rejects : expected_behavior.m_wtxid_in_rejects};
161 BOOST_CHECK_EQUAL(parent_txid_rejected, txdownload_impl.RecentRejectsFilter().contains(child_txid));
162 BOOST_CHECK_EQUAL(parent_txid_rejected, txdownload_impl.RecentRejectsFilter().contains(child_wtxid));
165 BOOST_CHECK_EQUAL(!parent_txid_rejected, txdownload_impl.m_orphanage.HaveTx(ptx_child->GetWitnessHash()));
188 CAmount amount_depth_2{amount_depth_1 - 1000};
191 int test_chain_height{100};
199 size_t coinbase_idx{0};
201 for (
int decisions = 0; decisions < (1 << 4); ++decisions) {
202 auto mtx_single_parent = CreateValidMempoolTransaction(m_coinbase_txns[coinbase_idx], 0, test_chain_height, coinbaseKey, destination, amount_depth_1,
false);
205 auto mtx_orphan = CreateValidMempoolTransaction(single_parent, 0, test_chain_height, wallet_key, destination, amount_depth_2,
false);
215 const bool parent_recent_rej(decisions & 1);
216 const bool parent_recent_rej_recon((decisions >> 1) & 1);
217 const bool parent_recent_conf((decisions >> 2) & 1);
218 const bool parent_in_mempool((decisions >> 3) & 1);
220 if (parent_recent_rej) txdownload_impl.RecentRejectsFilter().insert(single_parent->GetHash().ToUint256());
221 if (parent_recent_rej_recon) txdownload_impl.RecentRejectsReconsiderableFilter().insert(single_parent->GetHash().ToUint256());
222 if (parent_recent_conf) txdownload_impl.RecentConfirmedTransactionsFilter().insert(single_parent->GetHash().ToUint256());
223 if (parent_in_mempool) {
227 assert(coinbase_idx < m_coinbase_txns.size());
233 const bool expect_keep_orphan = !parent_recent_rej;
234 const auto ret_1p1c = txdownload_impl.MempoolRejectedTx(orphan, state_orphan, nodeid,
true);
237 expect_keep_orphan,
true, expect_keep_orphan ? 1 : 0);
238 BOOST_CHECK_MESSAGE(ok, err_msg);
243 std::vector<CTransactionRef> parents;
244 std::vector<COutPoint> outpoints;
245 int32_t num_parents{24};
246 for (int32_t i = 0; i < num_parents; ++i) {
247 assert(coinbase_idx < m_coinbase_txns.size());
248 auto mtx_parent = CreateValidMempoolTransaction(m_coinbase_txns[coinbase_idx++], 0, test_chain_height,
249 coinbaseKey, destination, amount_depth_1 + i,
false);
251 parents.emplace_back(ptx_parent);
252 outpoints.emplace_back(ptx_parent->GetHash(), 0);
256 auto mtx_orphan = CreateValidMempoolTransaction(parents, outpoints, test_chain_height, {wallet_key}, {{amount_depth_2 * num_parents, destination}},
false);
264 txdownload_impl.RecentRejectsReconsiderableFilter().insert(parents[0]->GetHash().ToUint256());
265 const auto ret_1p1c_parent_reconsiderable = txdownload_impl.MempoolRejectedTx(orphan, state_orphan, nodeid,
true);
267 const bool ok =
CheckOrphanBehavior(txdownload_impl, orphan, ret_1p1c_parent_reconsiderable, err_msg,
268 true,
true, num_parents);
269 BOOST_CHECK_MESSAGE(ok, err_msg);
277 txdownload_impl.RecentRejectsReconsiderableFilter().insert(parents[0]->GetHash().ToUint256());
278 for (int32_t i = 1; i < num_parents; ++i) {
279 txdownload_impl.RecentConfirmedTransactionsFilter().insert(parents[i]->GetHash().ToUint256());
282 const auto ret_1recon_conf = txdownload_impl.MempoolRejectedTx(orphan, state_orphan, nodeid,
true);
285 true,
true, num_parents);
286 BOOST_CHECK_MESSAGE(ok, err_msg);
290 for (
int i = 0; i < 2; ++i) {
294 txdownload_impl.RecentRejectsReconsiderableFilter().insert(parents[1]->GetHash().ToUint256());
297 auto& alreadyhave_parent = parents[0];
299 txdownload_impl.RecentRejectsReconsiderableFilter().insert(alreadyhave_parent->GetHash().ToUint256());
301 txdownload_impl.RecentRejectsFilter().insert(alreadyhave_parent->GetHash().ToUint256());
304 const auto ret_2_problems = txdownload_impl.MempoolRejectedTx(orphan, state_orphan, nodeid,
true);
308 BOOST_CHECK_MESSAGE(ok, err_msg);
314 assert(coinbase_idx < m_coinbase_txns.size());
315 auto parent_2outputs =
MakeTransactionRef(CreateValidMempoolTransaction({m_coinbase_txns[coinbase_idx]}, {{m_coinbase_txns[coinbase_idx]->GetHash(), 0}}, test_chain_height, {coinbaseKey},
316 {{amount_split_half, destination}, {amount_split_half, destination}},
false));
318 auto orphan =
MakeTransactionRef(CreateValidMempoolTransaction({parent_2outputs}, {{parent_2outputs->GetHash(), 0}, {parent_2outputs->GetHash(), 1}},
319 test_chain_height, {wallet_key}, {{amount_depth_2, destination}},
false));
326 txdownload_impl.RecentRejectsReconsiderableFilter().insert(parent_2outputs->GetHash().ToUint256());
327 const auto ret_1p1c_2reconsiderable = txdownload_impl.MempoolRejectedTx(orphan, state_orphan, nodeid,
true);
329 const bool ok =
CheckOrphanBehavior(txdownload_impl, orphan, ret_1p1c_2reconsiderable, err_msg,
331 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 ...
static GenTxid Wtxid(const uint256 &hash)
static GenTxid Txid(const uint256 &hash)
bool HaveTx(const Wtxid &wtxid) const
Check if we already have an orphan transaction (by wtxid only)
bool Invalid(Result result, const std::string &reject_reason="", const std::string &debug_message="")
void ConnectedPeer(NodeId nodeid, const TxDownloadConnectionInfo &info)
TxOrphanage m_orphanage
Manages unvalidated tx data (orphan transactions for which we are downloading ancestors).
@ 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
static const uint32_t DEFAULT_MAX_ORPHAN_TRANSACTIONS
Default for -maxorphantx, maximum number of orphan transactions kept in memory.
#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.
int64_t GetTime()
DEPRECATED Use either ClockType::now() or Now<TimePointType>() if a cast is needed.
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