Branch data Line data Source code
1 : : // Copyright (c) 2011-present 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>
6 : : #include <consensus/validation.h>
7 : : #include <net_processing.h>
8 : : #include <node/txdownloadman_impl.h>
9 : : #include <primitives/transaction.h>
10 : : #include <script/script.h>
11 : : #include <test/util/common.h>
12 : : #include <test/util/random.h>
13 : : #include <test/util/setup_common.h>
14 : : #include <validation.h>
15 : :
16 : : #include <array>
17 : :
18 : : #include <boost/test/unit_test.hpp>
19 : :
20 : : BOOST_FIXTURE_TEST_SUITE(txdownload_tests, TestingSetup)
21 : :
22 : : struct Behaviors {
23 : : bool m_txid_in_rejects;
24 : : bool m_wtxid_in_rejects;
25 : : bool m_txid_in_rejects_recon;
26 : : bool m_wtxid_in_rejects_recon;
27 : : bool m_keep_for_compact;
28 : : bool m_ignore_inv_txid;
29 : : bool m_ignore_inv_wtxid;
30 : :
31 : : // Constructor. We are passing and casting ints because they are more readable in a table (see expected_behaviors).
32 : 48 : Behaviors(bool txid_rejects, bool wtxid_rejects, bool txid_recon, bool wtxid_recon, bool keep, bool txid_inv, bool wtxid_inv) :
33 : 48 : m_txid_in_rejects(txid_rejects),
34 : 48 : m_wtxid_in_rejects(wtxid_rejects),
35 : 48 : m_txid_in_rejects_recon(txid_recon),
36 : 48 : m_wtxid_in_rejects_recon(wtxid_recon),
37 : 48 : m_keep_for_compact(keep),
38 : 48 : m_ignore_inv_txid(txid_inv),
39 : 48 : m_ignore_inv_wtxid(wtxid_inv)
40 : : {}
41 : :
42 : 48 : void CheckEqual(const Behaviors& other, bool segwit)
43 : : {
44 [ + - ]: 48 : BOOST_CHECK_EQUAL(other.m_wtxid_in_rejects, m_wtxid_in_rejects);
45 [ + - ]: 48 : BOOST_CHECK_EQUAL(other.m_wtxid_in_rejects_recon, m_wtxid_in_rejects_recon);
46 [ + - ]: 48 : BOOST_CHECK_EQUAL(other.m_keep_for_compact, m_keep_for_compact);
47 [ + - ]: 48 : BOOST_CHECK_EQUAL(other.m_ignore_inv_wtxid, m_ignore_inv_wtxid);
48 : :
49 : : // false negatives for nonsegwit transactions, since txid == wtxid.
50 [ + + ]: 48 : if (segwit) {
51 [ + - ]: 24 : BOOST_CHECK_EQUAL(other.m_txid_in_rejects, m_txid_in_rejects);
52 [ + - ]: 24 : BOOST_CHECK_EQUAL(other.m_txid_in_rejects_recon, m_txid_in_rejects_recon);
53 [ + - ]: 24 : BOOST_CHECK_EQUAL(other.m_ignore_inv_txid, m_ignore_inv_txid);
54 : : }
55 : 48 : }
56 : : };
57 : :
58 : : // Map from failure reason to expected behavior for a segwit tx that fails
59 : : // Txid and Wtxid are assumed to be different here. For a nonsegwit transaction, use the wtxid results.
60 : : static std::map<TxValidationResult, Behaviors> expected_behaviors{
61 : : {TxValidationResult::TX_CONSENSUS, {/*txid_rejects*/0,/*wtxid_rejects*/1,/*txid_recon*/0,/*wtxid_recon*/0,/*keep*/1,/*txid_inv*/0,/*wtxid_inv*/1}},
62 : : {TxValidationResult::TX_INPUTS_NOT_STANDARD, { 1, 1, 0, 0, 1, 1, 1}},
63 : : {TxValidationResult::TX_NOT_STANDARD, { 0, 1, 0, 0, 1, 0, 1}},
64 : : {TxValidationResult::TX_MISSING_INPUTS, { 0, 0, 0, 0, 1, 0, 1}},
65 : : {TxValidationResult::TX_PREMATURE_SPEND, { 0, 1, 0, 0, 1, 0, 1}},
66 : : {TxValidationResult::TX_WITNESS_MUTATED, { 0, 1, 0, 0, 1, 0, 1}},
67 : : {TxValidationResult::TX_WITNESS_STRIPPED, { 0, 0, 0, 0, 0, 0, 0}},
68 : : {TxValidationResult::TX_CONFLICT, { 0, 1, 0, 0, 1, 0, 1}},
69 : : {TxValidationResult::TX_MEMPOOL_POLICY, { 0, 1, 0, 0, 1, 0, 1}},
70 : : {TxValidationResult::TX_NO_MEMPOOL, { 0, 1, 0, 0, 1, 0, 1}},
71 : : {TxValidationResult::TX_RECONSIDERABLE, { 0, 0, 0, 1, 1, 0, 1}},
72 : : {TxValidationResult::TX_UNKNOWN, { 0, 1, 0, 0, 1, 0, 1}},
73 : : };
74 : :
75 : 21 : static bool CheckOrphanBehavior(node::TxDownloadManagerImpl& txdownload_impl, const CTransactionRef& tx, const node::RejectedTxTodo& ret, std::string& err_msg,
76 : : bool expect_orphan, bool expect_keep, unsigned int expected_parents)
77 : : {
78 : : // Missing inputs can never result in a PackageToValidate.
79 [ - + ]: 21 : if (ret.m_package_to_validate.has_value()) {
80 : 0 : err_msg = strprintf("returned a PackageToValidate on missing inputs");
81 : 0 : return false;
82 : : }
83 : :
84 [ - + ]: 21 : if (expect_orphan != txdownload_impl.m_orphanage->HaveTx(tx->GetWitnessHash())) {
85 [ # # ]: 0 : err_msg = strprintf("unexpectedly %s tx in orphanage", expect_orphan ? "did not find" : "found");
86 : 0 : return false;
87 : : }
88 [ - + ]: 21 : if (expect_keep != ret.m_should_add_extra_compact_tx) {
89 [ # # ]: 0 : err_msg = strprintf("unexpectedly returned %s add to vExtraTxnForCompact", expect_keep ? "should not" : "should");
90 : 0 : return false;
91 : : }
92 [ - + - + ]: 21 : if (expected_parents != ret.m_unique_parents.size()) {
93 : 0 : err_msg = strprintf("expected %u unique_parents, got %u", expected_parents, ret.m_unique_parents.size());
94 : 0 : return false;
95 : : }
96 : : return true;
97 : : }
98 : :
99 : 8 : static CTransactionRef CreatePlaceholderTx(bool segwit)
100 : : {
101 : : // Each tx returned from here spends the previous one.
102 [ + + + - ]: 8 : static Txid prevout_hash{};
103 : :
104 : 8 : CMutableTransaction mtx;
105 [ + - ]: 8 : mtx.vin.emplace_back(prevout_hash, 0);
106 : : // This makes txid != wtxid
107 [ + + + - ]: 12 : if (segwit) mtx.vin[0].scriptWitness.stack.push_back({1});
108 [ + - ]: 8 : mtx.vout.emplace_back(CENT, CScript());
109 [ + - ]: 8 : auto ptx = MakeTransactionRef(mtx);
110 : 8 : prevout_hash = ptx->GetHash();
111 : 8 : return ptx;
112 : 8 : }
113 : :
114 [ + - + - : 7 : BOOST_FIXTURE_TEST_CASE(tx_rejection_types, TestChain100Setup)
+ - + - -
+ + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- ]
115 : : {
116 [ - + ]: 1 : CTxMemPool& pool = *Assert(m_node.mempool);
117 : 1 : FastRandomContext det_rand{true};
118 : 1 : node::TxDownloadOptions DEFAULT_OPTS{pool, det_rand, true};
119 : :
120 : : // A new TxDownloadManagerImpl is created for each tx so we can just reuse the same one.
121 [ + - ]: 1 : TxValidationState state;
122 : 1 : NodeId nodeid{0};
123 [ + - ]: 1 : std::chrono::microseconds now{GetTime()};
124 : 1 : node::TxDownloadConnectionInfo connection_info{/*m_preferred=*/false, /*m_relay_permissions=*/false, /*m_wtxid_relay=*/true};
125 : :
126 [ + + ]: 3 : for (const auto segwit_parent : {true, false}) {
127 [ + + ]: 6 : for (const auto segwit_child : {true, false}) {
128 [ + - ]: 4 : const auto ptx_parent = CreatePlaceholderTx(segwit_parent);
129 [ + - ]: 4 : const auto ptx_child = CreatePlaceholderTx(segwit_child);
130 : 4 : const auto& parent_txid = ptx_parent->GetHash();
131 : 4 : const auto& parent_wtxid = ptx_parent->GetWitnessHash();
132 : 4 : const auto& child_txid = ptx_child->GetHash();
133 : 4 : const auto& child_wtxid = ptx_child->GetWitnessHash();
134 : :
135 [ + - + + ]: 52 : for (const auto& [result, expected_behavior] : expected_behaviors) {
136 [ + - ]: 48 : node::TxDownloadManagerImpl txdownload_impl{DEFAULT_OPTS};
137 [ + - ]: 48 : txdownload_impl.ConnectedPeer(nodeid, connection_info);
138 : : // Parent failure
139 [ + - + - : 96 : state.Invalid(result, "");
+ - ]
140 [ + - ]: 48 : const auto& [keep, unique_txids, package_to_validate] = txdownload_impl.MempoolRejectedTx(ptx_parent, state, nodeid, /*first_time_failure=*/true);
141 : :
142 : : // No distinction between txid and wtxid caching for nonsegwit transactions, so only test these specific
143 : : // behaviors for segwit transactions.
144 : 48 : Behaviors actual_behavior{
145 [ + - + - ]: 48 : /*txid_rejects=*/txdownload_impl.RecentRejectsFilter().contains(parent_txid.ToUint256()),
146 [ + - + - ]: 48 : /*wtxid_rejects=*/txdownload_impl.RecentRejectsFilter().contains(parent_wtxid.ToUint256()),
147 [ + - + - ]: 48 : /*txid_recon=*/txdownload_impl.RecentRejectsReconsiderableFilter().contains(parent_txid.ToUint256()),
148 [ + - + - ]: 48 : /*wtxid_recon=*/txdownload_impl.RecentRejectsReconsiderableFilter().contains(parent_wtxid.ToUint256()),
149 : : /*keep=*/keep,
150 [ + - ]: 48 : /*txid_inv=*/txdownload_impl.AddTxAnnouncement(nodeid, parent_txid, now),
151 [ + - ]: 48 : /*wtxid_inv=*/txdownload_impl.AddTxAnnouncement(nodeid, parent_wtxid, now),
152 [ + - + - ]: 48 : };
153 [ + - + - : 72 : BOOST_TEST_MESSAGE("Testing behavior for " << result << (segwit_parent ? " segwit " : " nonsegwit"));
+ + + - ]
154 [ + - ]: 48 : actual_behavior.CheckEqual(expected_behavior, /*segwit=*/segwit_parent);
155 : :
156 : : // Later, a child of this transaction fails for missing inputs
157 [ + - + - : 96 : state.Invalid(TxValidationResult::TX_MISSING_INPUTS, "");
+ - ]
158 [ + - ]: 48 : txdownload_impl.MempoolRejectedTx(ptx_child, state, nodeid, /*first_time_failure=*/true);
159 : :
160 : : // If parent (by txid) was rejected, child is too.
161 [ + + ]: 48 : const bool parent_txid_rejected{segwit_parent ? expected_behavior.m_txid_in_rejects : expected_behavior.m_wtxid_in_rejects};
162 [ + - + - : 48 : BOOST_CHECK_EQUAL(parent_txid_rejected, txdownload_impl.RecentRejectsFilter().contains(child_txid.ToUint256()));
+ - + - ]
163 [ + - + - : 48 : BOOST_CHECK_EQUAL(parent_txid_rejected, txdownload_impl.RecentRejectsFilter().contains(child_wtxid.ToUint256()));
+ - + - ]
164 : :
165 : : // Unless rejected, the child should be in orphanage.
166 [ + - + - : 48 : BOOST_CHECK_EQUAL(!parent_txid_rejected, txdownload_impl.m_orphanage->HaveTx(ptx_child->GetWitnessHash()));
+ - ]
167 : 48 : }
168 [ + - ]: 8 : }
169 : : }
170 : 1 : }
171 : :
172 [ + - + - : 7 : BOOST_FIXTURE_TEST_CASE(handle_missing_inputs, TestChain100Setup)
+ - + - -
+ + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- ]
173 : : {
174 [ - + ]: 1 : CTxMemPool& pool = *Assert(m_node.mempool);
175 : 1 : FastRandomContext det_rand{true};
176 : 1 : node::TxDownloadOptions DEFAULT_OPTS{pool, det_rand, true};
177 : 1 : NodeId nodeid{1};
178 : 1 : node::TxDownloadConnectionInfo DEFAULT_CONN{/*m_preferred=*/false, /*m_relay_permissions=*/false, /*m_wtxid_relay=*/true};
179 : :
180 : : // We need mature coinbases
181 [ + - ]: 1 : mineBlocks(20);
182 : :
183 : : // Transactions with missing inputs are treated differently depending on how much we know about
184 : : // their parents.
185 : 1 : CKey wallet_key = GenerateRandomKey();
186 [ + - + - : 1 : CScript destination = GetScriptForDestination(PKHash(wallet_key.GetPubKey()));
+ - ]
187 : : // Amount for spending coinbase in a 1-in-1-out tx, at depth n, each time deducting 1000 from the amount as fees.
188 : 1 : CAmount amount_depth_1{50 * COIN - 1000};
189 : 1 : CAmount amount_depth_2{amount_depth_1 - 1000};
190 : : // Amount for spending coinbase in a 1-in-2-out tx, deducting 1000 in fees
191 : 1 : CAmount amount_split_half{25 * COIN - 500};
192 : 1 : int test_chain_height{100};
193 : :
194 [ + - ]: 1 : TxValidationState state_orphan;
195 [ + - + - : 2 : state_orphan.Invalid(TxValidationResult::TX_MISSING_INPUTS, "");
+ - ]
196 : :
197 : : // Transactions are not all submitted to mempool. Conserve the number of m_coinbase_txns we
198 : : // consume, and only increment this index number when we would conflict with an existing
199 : : // mempool transaction.
200 : 1 : size_t coinbase_idx{0};
201 : :
202 [ + + ]: 17 : for (int decisions = 0; decisions < (1 << 4); ++decisions) {
203 [ + - + - ]: 32 : auto mtx_single_parent = CreateValidMempoolTransaction(m_coinbase_txns[coinbase_idx], /*input_vout=*/0, test_chain_height, coinbaseKey, destination, amount_depth_1, /*submit=*/false);
204 [ + - ]: 16 : auto single_parent = MakeTransactionRef(mtx_single_parent);
205 : :
206 [ + - + - ]: 48 : auto mtx_orphan = CreateValidMempoolTransaction(single_parent, /*input_vout=*/0, test_chain_height, wallet_key, destination, amount_depth_2, /*submit=*/false);
207 [ + - ]: 16 : auto orphan = MakeTransactionRef(mtx_orphan);
208 : :
209 [ + - ]: 16 : node::TxDownloadManagerImpl txdownload_impl{DEFAULT_OPTS};
210 [ + - ]: 16 : txdownload_impl.ConnectedPeer(nodeid, DEFAULT_CONN);
211 : :
212 : : // Each bit of decisions tells us whether the parent is in a particular cache.
213 : : // It is definitely possible for a transaction to be in multiple caches. For example, it
214 : : // may have both a low feerate and found to violate some mempool policy when validated
215 : : // in a 1p1c.
216 : 16 : const bool parent_recent_rej(decisions & 1);
217 : 16 : const bool parent_recent_rej_recon((decisions >> 1) & 1);
218 : 16 : const bool parent_recent_conf((decisions >> 2) & 1);
219 : 16 : const bool parent_in_mempool((decisions >> 3) & 1);
220 : :
221 [ + + + - : 16 : if (parent_recent_rej) txdownload_impl.RecentRejectsFilter().insert(single_parent->GetHash().ToUint256());
+ - ]
222 [ + + + - : 16 : if (parent_recent_rej_recon) txdownload_impl.RecentRejectsReconsiderableFilter().insert(single_parent->GetHash().ToUint256());
+ - ]
223 [ + + + - : 16 : if (parent_recent_conf) txdownload_impl.RecentConfirmedTransactionsFilter().insert(single_parent->GetHash().ToUint256());
+ - ]
224 [ + + ]: 16 : if (parent_in_mempool) {
225 [ + - ]: 24 : const auto mempool_result = WITH_LOCK(::cs_main, return m_node.chainman->ProcessTransaction(single_parent));
226 [ + - + - : 16 : BOOST_CHECK(mempool_result.m_result_type == MempoolAcceptResult::ResultType::VALID);
- + ]
227 : 8 : coinbase_idx += 1;
228 [ - + - + ]: 8 : assert(coinbase_idx < m_coinbase_txns.size());
229 : 8 : }
230 : :
231 : : // Whether or not the transaction is added as an orphan depends solely on whether or not
232 : : // it's in RecentRejectsFilter. Specifically, the parent is allowed to be in
233 : : // RecentRejectsReconsiderableFilter, but it cannot be in RecentRejectsFilter.
234 : 16 : const bool expect_keep_orphan = !parent_recent_rej;
235 [ + + + + ]: 16 : const unsigned int expected_parents = parent_recent_rej || parent_recent_conf || parent_in_mempool ? 0 : 1;
236 : : // If we don't expect to keep the orphan then expected_parents is 0.
237 : : // !expect_keep_orphan => (expected_parents == 0)
238 [ + - + - : 32 : BOOST_CHECK(expect_keep_orphan || expected_parents == 0);
+ - ]
239 [ + - ]: 16 : const auto ret_1p1c = txdownload_impl.MempoolRejectedTx(orphan, state_orphan, nodeid, /*first_time_failure=*/true);
240 [ + - ]: 16 : std::string err_msg;
241 [ + - ]: 16 : const bool ok = CheckOrphanBehavior(txdownload_impl, orphan, ret_1p1c, err_msg,
242 : : /*expect_orphan=*/expect_keep_orphan, /*expect_keep=*/true, /*expected_parents=*/expected_parents);
243 [ + - + - ]: 32 : BOOST_CHECK_MESSAGE(ok, err_msg);
244 [ + - + - ]: 80 : }
245 : :
246 : : // Orphan with multiple parents
247 : 1 : {
248 : 1 : std::vector<CTransactionRef> parents;
249 : 1 : std::vector<COutPoint> outpoints;
250 : 1 : int32_t num_parents{24};
251 [ + + ]: 25 : for (int32_t i = 0; i < num_parents; ++i) {
252 [ - + - + ]: 24 : assert(coinbase_idx < m_coinbase_txns.size());
253 [ + - ]: 48 : auto mtx_parent = CreateValidMempoolTransaction(m_coinbase_txns[coinbase_idx++], /*input_vout=*/0, test_chain_height,
254 [ + - + - ]: 48 : coinbaseKey, destination, amount_depth_1 + i, /*submit=*/false);
255 [ + - ]: 24 : auto ptx_parent = MakeTransactionRef(mtx_parent);
256 [ + - ]: 24 : parents.emplace_back(ptx_parent);
257 [ + - ]: 24 : outpoints.emplace_back(ptx_parent->GetHash(), 0);
258 : 48 : }
259 : :
260 : : // Send all coins to 1 output.
261 [ + - + - : 5 : auto mtx_orphan = CreateValidMempoolTransaction(parents, outpoints, test_chain_height, {wallet_key}, {{amount_depth_2 * num_parents, destination}}, /*submit=*/false);
+ - + + +
+ - - -
- ]
262 [ + - ]: 1 : auto orphan = MakeTransactionRef(mtx_orphan);
263 : :
264 : : // 1 parent in RecentRejectsReconsiderableFilter, the rest are unknown
265 : 1 : {
266 [ + - ]: 1 : node::TxDownloadManagerImpl txdownload_impl{DEFAULT_OPTS};
267 [ + - ]: 1 : txdownload_impl.ConnectedPeer(nodeid, DEFAULT_CONN);
268 : :
269 [ + - + - ]: 1 : txdownload_impl.RecentRejectsReconsiderableFilter().insert(parents[0]->GetHash().ToUint256());
270 [ + - ]: 1 : const auto ret_1p1c_parent_reconsiderable = txdownload_impl.MempoolRejectedTx(orphan, state_orphan, nodeid, /*first_time_failure=*/true);
271 [ + - ]: 1 : std::string err_msg;
272 [ + - ]: 1 : const bool ok = CheckOrphanBehavior(txdownload_impl, orphan, ret_1p1c_parent_reconsiderable, err_msg,
273 : : /*expect_orphan=*/true, /*expect_keep=*/true, /*expected_parents=*/num_parents);
274 [ + - + - ]: 2 : BOOST_CHECK_MESSAGE(ok, err_msg);
275 : 1 : }
276 : :
277 : : // 1 parent in RecentRejectsReconsiderableFilter, the rest are confirmed
278 : 1 : {
279 [ + - ]: 1 : node::TxDownloadManagerImpl txdownload_impl{DEFAULT_OPTS};
280 [ + - ]: 1 : txdownload_impl.ConnectedPeer(nodeid, DEFAULT_CONN);
281 : :
282 [ + - + - ]: 1 : txdownload_impl.RecentRejectsReconsiderableFilter().insert(parents[0]->GetHash().ToUint256());
283 [ + + ]: 24 : for (int32_t i = 1; i < num_parents; ++i) {
284 [ + - + - ]: 23 : txdownload_impl.RecentConfirmedTransactionsFilter().insert(parents[i]->GetHash().ToUint256());
285 : : }
286 : 1 : const unsigned int expected_parents = 1;
287 : :
288 [ + - ]: 1 : const auto ret_1recon_conf = txdownload_impl.MempoolRejectedTx(orphan, state_orphan, nodeid, /*first_time_failure=*/true);
289 [ + - ]: 1 : std::string err_msg;
290 [ + - ]: 1 : const bool ok = CheckOrphanBehavior(txdownload_impl, orphan, ret_1recon_conf, err_msg,
291 : : /*expect_orphan=*/true, /*expect_keep=*/true, /*expected_parents=*/expected_parents);
292 [ + - + - ]: 2 : BOOST_CHECK_MESSAGE(ok, err_msg);
293 : 1 : }
294 : :
295 : : // 1 parent in RecentRejectsReconsiderableFilter, 1 other in {RecentRejectsReconsiderableFilter, RecentRejectsFilter}
296 [ + + ]: 3 : for (int i = 0; i < 2; ++i) {
297 [ + - ]: 2 : node::TxDownloadManagerImpl txdownload_impl{DEFAULT_OPTS};
298 [ + - ]: 2 : txdownload_impl.ConnectedPeer(nodeid, DEFAULT_CONN);
299 : :
300 [ + - + - ]: 2 : txdownload_impl.RecentRejectsReconsiderableFilter().insert(parents[1]->GetHash().ToUint256());
301 : :
302 : : // Doesn't really matter which parent
303 [ + + ]: 2 : auto& alreadyhave_parent = parents[0];
304 [ + + ]: 2 : if (i == 0) {
305 [ + - + - ]: 1 : txdownload_impl.RecentRejectsReconsiderableFilter().insert(alreadyhave_parent->GetHash().ToUint256());
306 [ + - ]: 1 : } else if (i == 1) {
307 [ + - + - ]: 1 : txdownload_impl.RecentRejectsFilter().insert(alreadyhave_parent->GetHash().ToUint256());
308 : : }
309 : :
310 [ + - ]: 2 : const auto ret_2_problems = txdownload_impl.MempoolRejectedTx(orphan, state_orphan, nodeid, /*first_time_failure=*/true);
311 [ + - ]: 2 : std::string err_msg;
312 [ + - ]: 2 : const bool ok = CheckOrphanBehavior(txdownload_impl, orphan, ret_2_problems, err_msg,
313 : : /*expect_orphan=*/false, /*expect_keep=*/true, /*expected_parents=*/0);
314 [ + - + - ]: 4 : BOOST_CHECK_MESSAGE(ok, err_msg);
315 : 2 : }
316 : 2 : }
317 : :
318 : : // Orphan with multiple inputs spending from a single parent
319 : 1 : {
320 [ - + - + ]: 1 : assert(coinbase_idx < m_coinbase_txns.size());
321 [ + - + - : 10 : auto parent_2outputs = MakeTransactionRef(CreateValidMempoolTransaction({m_coinbase_txns[coinbase_idx]}, {{m_coinbase_txns[coinbase_idx]->GetHash(), 0}}, test_chain_height, {coinbaseKey},
+ - + - +
- + + + -
+ + + + -
- - - - -
- - ]
322 : 1 : {{amount_split_half, destination}, {amount_split_half, destination}}, /*submit=*/false));
323 : :
324 [ + - + - : 8 : auto orphan = MakeTransactionRef(CreateValidMempoolTransaction({parent_2outputs}, {{parent_2outputs->GetHash(), 0}, {parent_2outputs->GetHash(), 1}},
+ - + - +
+ + - + +
+ + - - -
- - - -
- ]
325 : 1 : test_chain_height, {wallet_key}, {{amount_depth_2, destination}}, /*submit=*/false));
326 : : // Parent is in RecentRejectsReconsiderableFilter. Inputs will find it twice, but this
327 : : // should only counts as 1 parent in the filter.
328 : 1 : {
329 [ + - ]: 1 : node::TxDownloadManagerImpl txdownload_impl{DEFAULT_OPTS};
330 [ + - ]: 1 : txdownload_impl.ConnectedPeer(nodeid, DEFAULT_CONN);
331 : :
332 [ + - + - ]: 1 : txdownload_impl.RecentRejectsReconsiderableFilter().insert(parent_2outputs->GetHash().ToUint256());
333 [ + - ]: 1 : const auto ret_1p1c_2reconsiderable = txdownload_impl.MempoolRejectedTx(orphan, state_orphan, nodeid, /*first_time_failure=*/true);
334 [ + - ]: 1 : std::string err_msg;
335 [ + - ]: 1 : const bool ok = CheckOrphanBehavior(txdownload_impl, orphan, ret_1p1c_2reconsiderable, err_msg,
336 : : /*expect_orphan=*/true, /*expect_keep=*/true, /*expected_parents=*/1);
337 [ + - + - ]: 2 : BOOST_CHECK_MESSAGE(ok, err_msg);
338 [ + - ]: 1 : }
339 [ + - ]: 2 : }
340 [ + - + - : 10 : }
+ - + - +
- + - + -
+ - + - +
- - - ]
341 : :
342 : : BOOST_AUTO_TEST_SUITE_END()
|