Bitcoin Core 28.99.0
P2P Digital Currency
txdownload_tests.cpp
Go to the documentation of this file.
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 <addresstype.h>
7#include <net_processing.h>
10#include <script/script.h>
11#include <test/util/random.h>
13#include <validation.h>
14
15#include <array>
16
17#include <boost/test/unit_test.hpp>
18
20
21struct Behaviors {
29
30 // Constructor. We are passing and casting ints because they are more readable in a table (see expected_behaviors).
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)
39 {}
40
41 void CheckEqual(const Behaviors& other, bool segwit)
42 {
43 BOOST_CHECK_EQUAL(other.m_wtxid_in_rejects, m_wtxid_in_rejects);
44 BOOST_CHECK_EQUAL(other.m_wtxid_in_rejects_recon, m_wtxid_in_rejects_recon);
45 BOOST_CHECK_EQUAL(other.m_keep_for_compact, m_keep_for_compact);
46 BOOST_CHECK_EQUAL(other.m_ignore_inv_wtxid, m_ignore_inv_wtxid);
47
48 // false negatives for nonsegwit transactions, since txid == wtxid.
49 if (segwit) {
50 BOOST_CHECK_EQUAL(other.m_txid_in_rejects, m_txid_in_rejects);
51 BOOST_CHECK_EQUAL(other.m_txid_in_rejects_recon, m_txid_in_rejects_recon);
52 BOOST_CHECK_EQUAL(other.m_ignore_inv_txid, m_ignore_inv_txid);
53 }
54 }
55};
56
57// Map from failure reason to expected behavior for a segwit tx that fails
58// Txid and Wtxid are assumed to be different here. For a nonsegwit transaction, use the wtxid results.
59static std::map<TxValidationResult, Behaviors> expected_behaviors{
60 {TxValidationResult::TX_CONSENSUS, {/*txid_rejects*/0,/*wtxid_rejects*/1,/*txid_recon*/0,/*wtxid_recon*/0,/*keep*/1,/*txid_inv*/0,/*wtxid_inv*/1}},
61 {TxValidationResult::TX_INPUTS_NOT_STANDARD, { 1, 1, 0, 0, 1, 1, 1}},
62 {TxValidationResult::TX_NOT_STANDARD, { 0, 1, 0, 0, 1, 0, 1}},
63 {TxValidationResult::TX_MISSING_INPUTS, { 0, 0, 0, 0, 1, 0, 1}},
64 {TxValidationResult::TX_PREMATURE_SPEND, { 0, 1, 0, 0, 1, 0, 1}},
65 {TxValidationResult::TX_WITNESS_MUTATED, { 0, 1, 0, 0, 1, 0, 1}},
66 {TxValidationResult::TX_WITNESS_STRIPPED, { 0, 0, 0, 0, 0, 0, 0}},
67 {TxValidationResult::TX_CONFLICT, { 0, 1, 0, 0, 1, 0, 1}},
68 {TxValidationResult::TX_MEMPOOL_POLICY, { 0, 1, 0, 0, 1, 0, 1}},
69 {TxValidationResult::TX_NO_MEMPOOL, { 0, 1, 0, 0, 1, 0, 1}},
70 {TxValidationResult::TX_RECONSIDERABLE, { 0, 0, 0, 1, 1, 0, 1}},
71 {TxValidationResult::TX_UNKNOWN, { 0, 1, 0, 0, 1, 0, 1}},
72};
73
74static bool CheckOrphanBehavior(node::TxDownloadManagerImpl& txdownload_impl, const CTransactionRef& tx, const node::RejectedTxTodo& ret, std::string& err_msg,
75 bool expect_orphan, bool expect_keep, unsigned int expected_parents)
76{
77 // Missing inputs can never result in a PackageToValidate.
78 if (ret.m_package_to_validate.has_value()) {
79 err_msg = strprintf("returned a PackageToValidate on missing inputs");
80 return false;
81 }
82
83 if (expect_orphan != txdownload_impl.m_orphanage.HaveTx(tx->GetWitnessHash())) {
84 err_msg = strprintf("unexpectedly %s tx in orphanage", expect_orphan ? "did not find" : "found");
85 return false;
86 }
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");
89 return false;
90 }
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());
93 return false;
94 }
95 return true;
96}
97
99{
100 // Each tx returned from here spends the previous one.
101 static Txid prevout_hash{};
102
104 mtx.vin.emplace_back(prevout_hash, 0);
105 // This makes txid != wtxid
106 if (segwit) mtx.vin[0].scriptWitness.stack.push_back({1});
107 mtx.vout.emplace_back(CENT, CScript());
108 auto ptx = MakeTransactionRef(mtx);
109 prevout_hash = ptx->GetHash();
110 return ptx;
111}
112
114{
116 FastRandomContext det_rand{true};
117 node::TxDownloadOptions DEFAULT_OPTS{pool, det_rand, DEFAULT_MAX_ORPHAN_TRANSACTIONS, true};
118
119 // A new TxDownloadManagerImpl is created for each tx so we can just reuse the same one.
120 TxValidationState state;
121 NodeId nodeid{0};
122 std::chrono::microseconds now{GetTime()};
123 node::TxDownloadConnectionInfo connection_info{/*m_preferred=*/false, /*m_relay_permissions=*/false, /*m_wtxid_relay=*/true};
124
125 for (const auto segwit_parent : {true, false}) {
126 for (const auto segwit_child : {true, false}) {
127 const auto ptx_parent = CreatePlaceholderTx(segwit_parent);
128 const auto ptx_child = CreatePlaceholderTx(segwit_child);
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();
133
134 for (const auto& [result, expected_behavior] : expected_behaviors) {
135 node::TxDownloadManagerImpl txdownload_impl{DEFAULT_OPTS};
136 txdownload_impl.ConnectedPeer(nodeid, connection_info);
137 // Parent failure
138 state.Invalid(result, "");
139 const auto& [keep, unique_txids, package_to_validate] = txdownload_impl.MempoolRejectedTx(ptx_parent, state, nodeid, /*first_time_failure=*/true);
140
141 // No distinction between txid and wtxid caching for nonsegwit transactions, so only test these specific
142 // behaviors for segwit transactions.
143 Behaviors actual_behavior{
144 /*txid_rejects=*/txdownload_impl.RecentRejectsFilter().contains(parent_txid),
145 /*wtxid_rejects=*/txdownload_impl.RecentRejectsFilter().contains(parent_wtxid),
146 /*txid_recon=*/txdownload_impl.RecentRejectsReconsiderableFilter().contains(parent_txid),
147 /*wtxid_recon=*/txdownload_impl.RecentRejectsReconsiderableFilter().contains(parent_wtxid),
148 /*keep=*/keep,
149 /*txid_inv=*/txdownload_impl.AddTxAnnouncement(nodeid, GenTxid::Txid(parent_txid), now, /*p2p_inv=*/true),
150 /*wtxid_inv=*/txdownload_impl.AddTxAnnouncement(nodeid, GenTxid::Wtxid(parent_wtxid), now, /*p2p_inv=*/true),
151 };
152 BOOST_TEST_MESSAGE("Testing behavior for " << result << (segwit_parent ? " segwit " : " nonsegwit"));
153 actual_behavior.CheckEqual(expected_behavior, /*segwit=*/segwit_parent);
154
155 // Later, a child of this transaction fails for missing inputs
157 txdownload_impl.MempoolRejectedTx(ptx_child, state, nodeid, /*first_time_failure=*/true);
158
159 // If parent (by txid) was rejected, child is too.
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));
163
164 // Unless rejected, the child should be in orphanage.
165 BOOST_CHECK_EQUAL(!parent_txid_rejected, txdownload_impl.m_orphanage.HaveTx(ptx_child->GetWitnessHash()));
166 }
167 }
168 }
169}
170
172{
174 FastRandomContext det_rand{true};
175 node::TxDownloadOptions DEFAULT_OPTS{pool, det_rand, DEFAULT_MAX_ORPHAN_TRANSACTIONS, true};
176 NodeId nodeid{1};
177 node::TxDownloadConnectionInfo DEFAULT_CONN{/*m_preferred=*/false, /*m_relay_permissions=*/false, /*m_wtxid_relay=*/true};
178
179 // We need mature coinbases
180 mineBlocks(20);
181
182 // Transactions with missing inputs are treated differently depending on how much we know about
183 // their parents.
184 CKey wallet_key = GenerateRandomKey();
185 CScript destination = GetScriptForDestination(PKHash(wallet_key.GetPubKey()));
186 // Amount for spending coinbase in a 1-in-1-out tx, at depth n, each time deducting 1000 from the amount as fees.
187 CAmount amount_depth_1{50 * COIN - 1000};
188 CAmount amount_depth_2{amount_depth_1 - 1000};
189 // Amount for spending coinbase in a 1-in-2-out tx, deducting 1000 in fees
190 CAmount amount_split_half{25 * COIN - 500};
191 int test_chain_height{100};
192
193 TxValidationState state_orphan;
195
196 // Transactions are not all submitted to mempool. Conserve the number of m_coinbase_txns we
197 // consume, and only increment this index number when we would conflict with an existing
198 // mempool transaction.
199 size_t coinbase_idx{0};
200
201 for (int decisions = 0; decisions < (1 << 4); ++decisions) {
202 auto mtx_single_parent = CreateValidMempoolTransaction(m_coinbase_txns[coinbase_idx], /*input_vout=*/0, test_chain_height, coinbaseKey, destination, amount_depth_1, /*submit=*/false);
203 auto single_parent = MakeTransactionRef(mtx_single_parent);
204
205 auto mtx_orphan = CreateValidMempoolTransaction(single_parent, /*input_vout=*/0, test_chain_height, wallet_key, destination, amount_depth_2, /*submit=*/false);
206 auto orphan = MakeTransactionRef(mtx_orphan);
207
208 node::TxDownloadManagerImpl txdownload_impl{DEFAULT_OPTS};
209 txdownload_impl.ConnectedPeer(nodeid, DEFAULT_CONN);
210
211 // Each bit of decisions tells us whether the parent is in a particular cache.
212 // It is definitely possible for a transaction to be in multiple caches. For example, it
213 // may have both a low feerate and found to violate some mempool policy when validated
214 // in a 1p1c.
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);
219
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) {
224 const auto mempool_result = WITH_LOCK(::cs_main, return m_node.chainman->ProcessTransaction(single_parent));
225 BOOST_CHECK(mempool_result.m_result_type == MempoolAcceptResult::ResultType::VALID);
226 coinbase_idx += 1;
227 assert(coinbase_idx < m_coinbase_txns.size());
228 }
229
230 // Whether or not the transaction is added as an orphan depends solely on whether or not
231 // it's in RecentRejectsFilter. Specifically, the parent is allowed to be in
232 // RecentRejectsReconsiderableFilter, but it cannot be in RecentRejectsFilter.
233 const bool expect_keep_orphan = !parent_recent_rej;
234 const auto ret_1p1c = txdownload_impl.MempoolRejectedTx(orphan, state_orphan, nodeid, /*first_time_failure=*/true);
235 std::string err_msg;
236 const bool ok = CheckOrphanBehavior(txdownload_impl, orphan, ret_1p1c, err_msg,
237 /*expect_orphan=*/expect_keep_orphan, /*expect_keep=*/true, /*expected_parents=*/expect_keep_orphan ? 1 : 0);
238 BOOST_CHECK_MESSAGE(ok, err_msg);
239 }
240
241 // Orphan with multiple parents
242 {
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++], /*input_vout=*/0, test_chain_height,
249 coinbaseKey, destination, amount_depth_1 + i, /*submit=*/false);
250 auto ptx_parent = MakeTransactionRef(mtx_parent);
251 parents.emplace_back(ptx_parent);
252 outpoints.emplace_back(ptx_parent->GetHash(), 0);
253 }
254
255 // Send all coins to 1 output.
256 auto mtx_orphan = CreateValidMempoolTransaction(parents, outpoints, test_chain_height, {wallet_key}, {{amount_depth_2 * num_parents, destination}}, /*submit=*/false);
257 auto orphan = MakeTransactionRef(mtx_orphan);
258
259 // 1 parent in RecentRejectsReconsiderableFilter, the rest are unknown
260 {
261 node::TxDownloadManagerImpl txdownload_impl{DEFAULT_OPTS};
262 txdownload_impl.ConnectedPeer(nodeid, DEFAULT_CONN);
263
264 txdownload_impl.RecentRejectsReconsiderableFilter().insert(parents[0]->GetHash().ToUint256());
265 const auto ret_1p1c_parent_reconsiderable = txdownload_impl.MempoolRejectedTx(orphan, state_orphan, nodeid, /*first_time_failure=*/true);
266 std::string err_msg;
267 const bool ok = CheckOrphanBehavior(txdownload_impl, orphan, ret_1p1c_parent_reconsiderable, err_msg,
268 /*expect_orphan=*/true, /*expect_keep=*/true, /*expected_parents=*/num_parents);
269 BOOST_CHECK_MESSAGE(ok, err_msg);
270 }
271
272 // 1 parent in RecentRejectsReconsiderableFilter, the rest are confirmed
273 {
274 node::TxDownloadManagerImpl txdownload_impl{DEFAULT_OPTS};
275 txdownload_impl.ConnectedPeer(nodeid, DEFAULT_CONN);
276
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());
280 }
281
282 const auto ret_1recon_conf = txdownload_impl.MempoolRejectedTx(orphan, state_orphan, nodeid, /*first_time_failure=*/true);
283 std::string err_msg;
284 const bool ok = CheckOrphanBehavior(txdownload_impl, orphan, ret_1recon_conf, err_msg,
285 /*expect_orphan=*/true, /*expect_keep=*/true, /*expected_parents=*/num_parents);
286 BOOST_CHECK_MESSAGE(ok, err_msg);
287 }
288
289 // 1 parent in RecentRejectsReconsiderableFilter, 1 other in {RecentRejectsReconsiderableFilter, RecentRejectsFilter}
290 for (int i = 0; i < 2; ++i) {
291 node::TxDownloadManagerImpl txdownload_impl{DEFAULT_OPTS};
292 txdownload_impl.ConnectedPeer(nodeid, DEFAULT_CONN);
293
294 txdownload_impl.RecentRejectsReconsiderableFilter().insert(parents[1]->GetHash().ToUint256());
295
296 // Doesn't really matter which parent
297 auto& alreadyhave_parent = parents[0];
298 if (i == 0) {
299 txdownload_impl.RecentRejectsReconsiderableFilter().insert(alreadyhave_parent->GetHash().ToUint256());
300 } else if (i == 1) {
301 txdownload_impl.RecentRejectsFilter().insert(alreadyhave_parent->GetHash().ToUint256());
302 }
303
304 const auto ret_2_problems = txdownload_impl.MempoolRejectedTx(orphan, state_orphan, nodeid, /*first_time_failure=*/true);
305 std::string err_msg;
306 const bool ok = CheckOrphanBehavior(txdownload_impl, orphan, ret_2_problems, err_msg,
307 /*expect_orphan=*/false, /*expect_keep=*/true, /*expected_parents=*/0);
308 BOOST_CHECK_MESSAGE(ok, err_msg);
309 }
310 }
311
312 // Orphan with multiple inputs spending from a single parent
313 {
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}}, /*submit=*/false));
317
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}}, /*submit=*/false));
320 // Parent is in RecentRejectsReconsiderableFilter. Inputs will find it twice, but this
321 // should only counts as 1 parent in the filter.
322 {
323 node::TxDownloadManagerImpl txdownload_impl{DEFAULT_OPTS};
324 txdownload_impl.ConnectedPeer(nodeid, DEFAULT_CONN);
325
326 txdownload_impl.RecentRejectsReconsiderableFilter().insert(parent_2outputs->GetHash().ToUint256());
327 const auto ret_1p1c_2reconsiderable = txdownload_impl.MempoolRejectedTx(orphan, state_orphan, nodeid, /*first_time_failure=*/true);
328 std::string err_msg;
329 const bool ok = CheckOrphanBehavior(txdownload_impl, orphan, ret_1p1c_2reconsiderable, err_msg,
330 /*expect_orphan=*/true, /*expect_keep=*/true, /*expected_parents=*/1);
331 BOOST_CHECK_MESSAGE(ok, err_msg);
332 }
333 }
334}
335
CScript GetScriptForDestination(const CTxDestination &dest)
Generate a Bitcoin scriptPubKey for the given CTxDestination.
int64_t CAmount
Amount in satoshis (Can be negative)
Definition: amount.h:12
static constexpr CAmount COIN
The amount of satoshis in one BTC.
Definition: amount.h:15
int ret
node::NodeContext m_node
Definition: bitcoin-gui.cpp:42
#define Assert(val)
Identity function.
Definition: check.h:85
An encapsulated private key.
Definition: key.h:35
CPubKey GetPubKey() const
Compute the public key from a private key.
Definition: key.cpp:182
Serialized script, used inside transaction inputs and outputs.
Definition: script.h:415
CTxMemPool stores valid-according-to-the-current-best-chain transactions that may be included in the ...
Definition: txmempool.h:304
Fast randomness source.
Definition: random.h:377
static GenTxid Wtxid(const uint256 &hash)
Definition: transaction.h:435
static GenTxid Txid(const uint256 &hash)
Definition: transaction.h:434
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="")
Definition: validation.h:89
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.
Definition: cs_main.cpp:8
BOOST_FIXTURE_TEST_SUITE(cuckoocache_tests, BasicTestingSetup)
Test Suite for CuckooCache.
BOOST_AUTO_TEST_SUITE_END()
CKey GenerateRandomKey(bool compressed) noexcept
Definition: key.cpp:352
int64_t NodeId
Definition: net.h:97
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)
Definition: object.cpp:18
#define BOOST_CHECK(expr)
Definition: object.cpp:17
static CTransactionRef MakeTransactionRef(Tx &&txIn)
Definition: transaction.h:424
std::shared_ptr< const CTransaction > CTransactionRef
Definition: transaction.h:423
static constexpr CAmount CENT
Definition: setup_common.h:46
bool m_wtxid_in_rejects
bool m_txid_in_rejects_recon
bool m_ignore_inv_txid
Behaviors(bool txid_rejects, bool wtxid_rejects, bool txid_recon, bool wtxid_recon, bool keep, bool txid_inv, bool wtxid_inv)
bool m_keep_for_compact
void CheckEqual(const Behaviors &other, bool segwit)
bool m_wtxid_in_rejects_recon
bool m_txid_in_rejects
bool m_ignore_inv_wtxid
A mutable version of CTransaction.
Definition: transaction.h:378
std::vector< CTxOut > vout
Definition: transaction.h:380
std::vector< CTxIn > vin
Definition: transaction.h:379
Testing fixture that pre-creates a 100-block REGTEST-mode block chain.
Definition: setup_common.h:139
Testing setup that configures a complete environment.
Definition: setup_common.h:120
std::unique_ptr< CTxMemPool > mempool
Definition: context.h:68
std::unique_ptr< ChainstateManager > chainman
Definition: context.h:72
#define WITH_LOCK(cs, code)
Run code while locking a mutex.
Definition: sync.h:301
int64_t GetTime()
DEPRECATED Use either ClockType::now() or Now<TimePointType>() if a cast is needed.
Definition: time.cpp:47
#define strprintf
Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for...
Definition: tinyformat.h:1165
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
assert(!tx.IsCoinBase())