Branch data Line data Source code
1 : : // Copyright (c) 2017-2021 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 <consensus/validation.h>
6 : : #include <key_io.h>
7 : : #include <policy/packages.h>
8 : : #include <policy/policy.h>
9 : : #include <policy/ephemeral_policy.h>
10 : : #include <policy/truc_policy.h>
11 : : #include <primitives/transaction.h>
12 : : #include <random.h>
13 : : #include <script/script.h>
14 : : #include <test/util/setup_common.h>
15 : : #include <test/util/txmempool.h>
16 : : #include <validation.h>
17 : :
18 : : #include <boost/test/unit_test.hpp>
19 : :
20 : :
21 : : BOOST_AUTO_TEST_SUITE(txvalidation_tests)
22 : :
23 : : /**
24 : : * Ensure that the mempool won't accept coinbase transactions.
25 : : */
26 [ + - + - : 7 : BOOST_FIXTURE_TEST_CASE(tx_mempool_reject_coinbase, TestChain100Setup)
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- ]
27 : : {
28 [ + - + - : 1 : CScript scriptPubKey = CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG;
+ - ]
29 [ + - ]: 1 : CMutableTransaction coinbaseTx;
30 : :
31 : 1 : coinbaseTx.version = 1;
32 [ + - ]: 1 : coinbaseTx.vin.resize(1);
33 [ + - ]: 1 : coinbaseTx.vout.resize(1);
34 [ + - + - ]: 1 : coinbaseTx.vin[0].scriptSig = CScript() << OP_11 << OP_EQUAL;
35 : 1 : coinbaseTx.vout[0].nValue = 1 * CENT;
36 : 1 : coinbaseTx.vout[0].scriptPubKey = scriptPubKey;
37 : :
38 [ + - + - : 3 : BOOST_CHECK(CTransaction(coinbaseTx).IsCoinBase());
+ - + - ]
39 : :
40 [ + - ]: 1 : LOCK(cs_main);
41 : :
42 [ + - ]: 1 : unsigned int initialPoolSize = m_node.mempool->size();
43 [ + - + - ]: 2 : const MempoolAcceptResult result = m_node.chainman->ProcessTransaction(MakeTransactionRef(coinbaseTx));
44 : :
45 [ + - + - : 2 : BOOST_CHECK(result.m_result_type == MempoolAcceptResult::ResultType::INVALID);
+ - ]
46 : :
47 : : // Check that the transaction hasn't been added to mempool.
48 [ + - + - : 1 : BOOST_CHECK_EQUAL(m_node.mempool->size(), initialPoolSize);
+ - ]
49 : :
50 : : // Check that the validation state reflects the unsuccessful attempt.
51 [ + - + - : 2 : BOOST_CHECK(result.m_state.IsInvalid());
+ - ]
52 [ + - + - : 1 : BOOST_CHECK_EQUAL(result.m_state.GetRejectReason(), "coinbase");
+ - ]
53 [ + - + - ]: 2 : BOOST_CHECK(result.m_state.GetResult() == TxValidationResult::TX_CONSENSUS);
54 [ + - ]: 3 : }
55 : :
56 : : // Generate a number of random, nonexistent outpoints.
57 : 10 : static inline std::vector<COutPoint> random_outpoints(size_t num_outpoints) {
58 : 10 : std::vector<COutPoint> outpoints;
59 [ + + ]: 129 : for (size_t i{0}; i < num_outpoints; ++i) {
60 [ + - ]: 119 : outpoints.emplace_back(Txid::FromUint256(GetRandHash()), 0);
61 : : }
62 : 10 : return outpoints;
63 : 0 : }
64 : :
65 : 1 : static inline std::vector<CPubKey> random_keys(size_t num_keys) {
66 : 1 : std::vector<CPubKey> keys;
67 [ + - ]: 1 : keys.reserve(num_keys);
68 [ + + ]: 3 : for (size_t i{0}; i < num_keys; ++i) {
69 : 2 : CKey key;
70 [ + - ]: 2 : key.MakeNewKey(true);
71 [ + - + - ]: 2 : keys.emplace_back(key.GetPubKey());
72 : 2 : }
73 : 1 : return keys;
74 : 0 : }
75 : :
76 : : // Creates a placeholder tx (not valid) with 25 outputs. Specify the version and the inputs.
77 : 28 : static inline CTransactionRef make_tx(const std::vector<COutPoint>& inputs, int32_t version)
78 : : {
79 : 28 : CMutableTransaction mtx = CMutableTransaction{};
80 : 28 : mtx.version = version;
81 [ + - ]: 28 : mtx.vin.resize(inputs.size());
82 [ + - ]: 28 : mtx.vout.resize(25);
83 [ + + ]: 168 : for (size_t i{0}; i < inputs.size(); ++i) {
84 : 140 : mtx.vin[i].prevout = inputs[i];
85 : : }
86 [ + + ]: 728 : for (auto i{0}; i < 25; ++i) {
87 [ + - ]: 700 : mtx.vout[i].scriptPubKey = CScript() << OP_TRUE;
88 : 700 : mtx.vout[i].nValue = 10000;
89 : : }
90 [ + - ]: 56 : return MakeTransactionRef(mtx);
91 : 28 : }
92 : :
93 : : static constexpr auto NUM_EPHEMERAL_TX_OUTPUTS = 3;
94 : : static constexpr auto EPHEMERAL_DUST_INDEX = NUM_EPHEMERAL_TX_OUTPUTS - 1;
95 : :
96 : : // Same as make_tx but adds 2 normal outputs and 0-value dust to end of vout
97 : 4 : static inline CTransactionRef make_ephemeral_tx(const std::vector<COutPoint>& inputs, int32_t version)
98 : : {
99 : 4 : CMutableTransaction mtx = CMutableTransaction{};
100 : 4 : mtx.version = version;
101 [ + - ]: 4 : mtx.vin.resize(inputs.size());
102 [ + + ]: 9 : for (size_t i{0}; i < inputs.size(); ++i) {
103 : 5 : mtx.vin[i].prevout = inputs[i];
104 : : }
105 [ + - ]: 4 : mtx.vout.resize(NUM_EPHEMERAL_TX_OUTPUTS);
106 [ + + ]: 16 : for (auto i{0}; i < NUM_EPHEMERAL_TX_OUTPUTS; ++i) {
107 [ + - ]: 12 : mtx.vout[i].scriptPubKey = CScript() << OP_TRUE;
108 [ + + ]: 20 : mtx.vout[i].nValue = (i == EPHEMERAL_DUST_INDEX) ? 0 : 10000;
109 : : }
110 [ + - ]: 8 : return MakeTransactionRef(mtx);
111 : 4 : }
112 : :
113 [ + - + - : 7 : BOOST_FIXTURE_TEST_CASE(ephemeral_tests, RegTestingSetup)
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- ]
114 : : {
115 : 1 : CTxMemPool& pool = *Assert(m_node.mempool);
116 [ + - ]: 1 : LOCK2(cs_main, pool.cs);
117 : 1 : TestMemPoolEntryHelper entry;
118 [ + - ]: 1 : CTxMemPool::setEntries empty_ancestors;
119 : :
120 [ + - ]: 1 : TxValidationState child_state;
121 [ + - ]: 1 : Txid child_txid;
122 : :
123 : : // Arbitrary non-0 feerate for these tests
124 [ + - ]: 1 : CFeeRate dustrelay(DUST_RELAY_TX_FEE);
125 : :
126 : : // Basic transaction with dust
127 [ + - + - ]: 1 : auto grandparent_tx_1 = make_ephemeral_tx(random_outpoints(1), /*version=*/2);
128 [ + - ]: 1 : const auto dust_txid = grandparent_tx_1->GetHash();
129 : :
130 : : // Child transaction spending dust
131 [ + - + - : 2 : auto dust_spend = make_tx({COutPoint{dust_txid, EPHEMERAL_DUST_INDEX}}, /*version=*/2);
+ - ]
132 : :
133 : : // We first start with nothing "in the mempool", using package checks
134 : :
135 : : // Trivial single transaction with no dust
136 [ + - + - : 4 : BOOST_CHECK(CheckEphemeralSpends({dust_spend}, dustrelay, pool, child_state, child_txid));
+ - + - +
+ + - - -
- - ]
137 [ + - + - : 2 : BOOST_CHECK(child_state.IsValid());
+ - ]
138 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(child_txid, Txid());
139 : :
140 : : // Now with dust, ok because the tx has no dusty parents
141 [ + - + - : 4 : BOOST_CHECK(CheckEphemeralSpends({grandparent_tx_1}, dustrelay, pool, child_state, child_txid));
+ - + - +
+ + - - -
- - ]
142 [ + - + - : 2 : BOOST_CHECK(child_state.IsValid());
+ - ]
143 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(child_txid, Txid());
144 : :
145 : : // Dust checks pass
146 [ + - + - : 5 : BOOST_CHECK(CheckEphemeralSpends({grandparent_tx_1, dust_spend}, CFeeRate(0), pool, child_state, child_txid));
+ - + - +
+ + - - -
- - ]
147 [ + - + - : 2 : BOOST_CHECK(child_state.IsValid());
+ - ]
148 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(child_txid, Txid());
149 [ + - + - : 5 : BOOST_CHECK(CheckEphemeralSpends({grandparent_tx_1, dust_spend}, dustrelay, pool, child_state, child_txid));
+ - + - +
+ + - - -
- - ]
150 [ + - + - : 2 : BOOST_CHECK(child_state.IsValid());
+ - ]
151 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(child_txid, Txid());
152 : :
153 [ + - + - : 2 : auto dust_non_spend = make_tx({COutPoint{dust_txid, EPHEMERAL_DUST_INDEX - 1}}, /*version=*/2);
+ - ]
154 : :
155 : : // Child spending non-dust only from parent should be disallowed even if dust otherwise spent
156 [ + - ]: 1 : const auto dust_non_spend_txid{dust_non_spend->GetHash()};
157 [ + - + - : 6 : BOOST_CHECK(!CheckEphemeralSpends({grandparent_tx_1, dust_non_spend, dust_spend}, dustrelay, pool, child_state, child_txid));
+ - + - +
+ + - - -
- - ]
158 [ + - + - : 2 : BOOST_CHECK(!child_state.IsValid());
+ - ]
159 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(child_txid, dust_non_spend_txid);
160 : 1 : child_state = TxValidationState();
161 [ + - ]: 1 : child_txid = Txid();
162 : :
163 [ + - + - : 6 : BOOST_CHECK(!CheckEphemeralSpends({grandparent_tx_1, dust_spend, dust_non_spend}, dustrelay, pool, child_state, child_txid));
+ - + - +
+ + - - -
- - ]
164 [ + - + - : 2 : BOOST_CHECK(!child_state.IsValid());
+ - ]
165 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(child_txid, dust_non_spend_txid);
166 : 1 : child_state = TxValidationState();
167 [ + - ]: 1 : child_txid = Txid();
168 : :
169 [ + - + - : 5 : BOOST_CHECK(!CheckEphemeralSpends({grandparent_tx_1, dust_non_spend}, dustrelay, pool, child_state, child_txid));
+ - + - +
+ + - - -
- - ]
170 [ + - + - : 2 : BOOST_CHECK(!child_state.IsValid());
+ - ]
171 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(child_txid, dust_non_spend_txid);
172 : 1 : child_state = TxValidationState();
173 [ + - ]: 1 : child_txid = Txid();
174 : :
175 [ + - + - ]: 1 : auto grandparent_tx_2 = make_ephemeral_tx(random_outpoints(1), /*version=*/2);
176 [ + - ]: 1 : const auto dust_txid_2 = grandparent_tx_2->GetHash();
177 : :
178 : : // Spend dust from one but not another is ok, as long as second grandparent has no child
179 [ + - + - : 6 : BOOST_CHECK(CheckEphemeralSpends({grandparent_tx_1, grandparent_tx_2, dust_spend}, dustrelay, pool, child_state, child_txid));
+ - + - +
+ + - - -
- - ]
180 [ + - + - : 2 : BOOST_CHECK(child_state.IsValid());
+ - ]
181 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(child_txid, Txid());
182 : :
183 [ + - + - : 2 : auto dust_non_spend_both_parents = make_tx({COutPoint{dust_txid, EPHEMERAL_DUST_INDEX}, COutPoint{dust_txid_2, EPHEMERAL_DUST_INDEX - 1}}, /*version=*/2);
+ - ]
184 : : // But if we spend from the parent, it must spend dust
185 [ + - + - : 6 : BOOST_CHECK(!CheckEphemeralSpends({grandparent_tx_1, grandparent_tx_2, dust_non_spend_both_parents}, dustrelay, pool, child_state, child_txid));
+ - + - +
+ + - - -
- - ]
186 [ + - + - : 2 : BOOST_CHECK(!child_state.IsValid());
+ - ]
187 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(child_txid, dust_non_spend_both_parents->GetHash());
188 : 1 : child_state = TxValidationState();
189 [ + - ]: 1 : child_txid = Txid();
190 : :
191 [ + - + - : 2 : auto dust_spend_both_parents = make_tx({COutPoint{dust_txid, EPHEMERAL_DUST_INDEX}, COutPoint{dust_txid_2, EPHEMERAL_DUST_INDEX}}, /*version=*/2);
+ - ]
192 [ + - + - : 6 : BOOST_CHECK(CheckEphemeralSpends({grandparent_tx_1, grandparent_tx_2, dust_spend_both_parents}, dustrelay, pool, child_state, child_txid));
+ - + - +
+ + - - -
- - ]
193 [ + - + - : 2 : BOOST_CHECK(child_state.IsValid());
+ - ]
194 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(child_txid, Txid());
195 : :
196 : : // Spending other outputs is also correct, as long as the dusty one is spent
197 [ + - ]: 1 : const std::vector<COutPoint> all_outpoints{COutPoint(dust_txid, 0), COutPoint(dust_txid, 1), COutPoint(dust_txid, 2),
198 [ + - ]: 1 : COutPoint(dust_txid_2, 0), COutPoint(dust_txid_2, 1), COutPoint(dust_txid_2, 2)};
199 [ + - ]: 1 : auto dust_spend_all_outpoints = make_tx(all_outpoints, /*version=*/2);
200 [ + - + - : 6 : BOOST_CHECK(CheckEphemeralSpends({grandparent_tx_1, grandparent_tx_2, dust_spend_all_outpoints}, dustrelay, pool, child_state, child_txid));
+ - + - +
+ + - - -
- - ]
201 [ + - + - : 2 : BOOST_CHECK(child_state.IsValid());
+ - ]
202 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(child_txid, Txid());
203 : :
204 : : // 2 grandparents with dust <- 1 dust-spending parent with dust <- child with no dust
205 [ + - + - : 2 : auto parent_with_dust = make_ephemeral_tx({COutPoint{dust_txid, EPHEMERAL_DUST_INDEX}, COutPoint{dust_txid_2, EPHEMERAL_DUST_INDEX}}, /*version=*/2);
+ - ]
206 : : // Ok for parent to have dust
207 [ + - + - : 6 : BOOST_CHECK(CheckEphemeralSpends({grandparent_tx_1, grandparent_tx_2, parent_with_dust}, dustrelay, pool, child_state, child_txid));
+ - + - +
+ + - - -
- - ]
208 [ + - + - : 2 : BOOST_CHECK(child_state.IsValid());
+ - ]
209 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(child_txid, Txid());
210 [ + - + - : 2 : auto child_no_dust = make_tx({COutPoint{parent_with_dust->GetHash(), EPHEMERAL_DUST_INDEX}}, /*version=*/2);
+ - ]
211 [ + - + - : 7 : BOOST_CHECK(CheckEphemeralSpends({grandparent_tx_1, grandparent_tx_2, parent_with_dust, child_no_dust}, dustrelay, pool, child_state, child_txid));
+ - + - +
+ + - - -
- - ]
212 [ + - + - : 2 : BOOST_CHECK(child_state.IsValid());
+ - ]
213 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(child_txid, Txid());
214 : :
215 : : // 2 grandparents with dust <- 1 dust-spending parent with dust <- child with dust
216 [ + - + - : 2 : auto child_with_dust = make_ephemeral_tx({COutPoint{parent_with_dust->GetHash(), EPHEMERAL_DUST_INDEX}}, /*version=*/2);
+ - ]
217 [ + - + - : 7 : BOOST_CHECK(CheckEphemeralSpends({grandparent_tx_1, grandparent_tx_2, parent_with_dust, child_with_dust}, dustrelay, pool, child_state, child_txid));
+ - + - +
+ + - - -
- - ]
218 [ + - + - : 2 : BOOST_CHECK(child_state.IsValid());
+ - ]
219 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(child_txid, Txid());
220 : :
221 : : // Tests with parents in mempool
222 : :
223 : : // Nothing in mempool, this should pass for any transaction
224 [ + - + - : 4 : BOOST_CHECK(CheckEphemeralSpends({grandparent_tx_1}, dustrelay, pool, child_state, child_txid));
+ - + - +
+ + - - -
- - ]
225 [ + - + - : 2 : BOOST_CHECK(child_state.IsValid());
+ - ]
226 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(child_txid, Txid());
227 : :
228 : : // Add first grandparent to mempool and fetch entry
229 [ + - + - ]: 1 : AddToMempool(pool, entry.FromTx(grandparent_tx_1));
230 : :
231 : : // Ignores ancestors that aren't direct parents
232 [ + - + - : 4 : BOOST_CHECK(CheckEphemeralSpends({child_no_dust}, dustrelay, pool, child_state, child_txid));
+ - + - +
+ + - - -
- - ]
233 [ + - + - : 2 : BOOST_CHECK(child_state.IsValid());
+ - ]
234 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(child_txid, Txid());
235 : :
236 : : // Valid spend of dust with grandparent in mempool
237 [ + - + - : 4 : BOOST_CHECK(CheckEphemeralSpends({parent_with_dust}, dustrelay, pool, child_state, child_txid));
+ - + - +
+ + - - -
- - ]
238 [ + - + - : 2 : BOOST_CHECK(child_state.IsValid());
+ - ]
239 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(child_txid, Txid());
240 : :
241 : : // Second grandparent in same package
242 [ + - + - : 5 : BOOST_CHECK(CheckEphemeralSpends({parent_with_dust, grandparent_tx_2}, dustrelay, pool, child_state, child_txid));
+ - + - +
+ + - - -
- - ]
243 [ + - + - : 2 : BOOST_CHECK(child_state.IsValid());
+ - ]
244 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(child_txid, Txid());
245 : :
246 : : // Order in package doesn't matter
247 [ + - + - : 5 : BOOST_CHECK(CheckEphemeralSpends({grandparent_tx_2, parent_with_dust}, dustrelay, pool, child_state, child_txid));
+ - + - +
+ + - - -
- - ]
248 [ + - + - : 2 : BOOST_CHECK(child_state.IsValid());
+ - ]
249 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(child_txid, Txid());
250 : :
251 : : // Add second grandparent to mempool
252 [ + - + - ]: 1 : AddToMempool(pool, entry.FromTx(grandparent_tx_2));
253 : :
254 : : // Only spends single dust out of two direct parents
255 [ + - + - : 4 : BOOST_CHECK(!CheckEphemeralSpends({dust_non_spend_both_parents}, dustrelay, pool, child_state, child_txid));
+ - + - +
+ + - - -
- - ]
256 [ + - + - : 2 : BOOST_CHECK(!child_state.IsValid());
+ - ]
257 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(child_txid, dust_non_spend_both_parents->GetHash());
258 : 1 : child_state = TxValidationState();
259 [ + - ]: 1 : child_txid = Txid();
260 : :
261 : : // Spends both parents' dust
262 [ + - + - : 4 : BOOST_CHECK(CheckEphemeralSpends({parent_with_dust}, dustrelay, pool, child_state, child_txid));
+ - + - +
+ + - - -
- - ]
263 [ + - + - : 2 : BOOST_CHECK(child_state.IsValid());
+ - ]
264 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(child_txid, Txid());
265 : :
266 : : // Now add dusty parent to mempool
267 [ + - + - ]: 1 : AddToMempool(pool, entry.FromTx(parent_with_dust));
268 : :
269 : : // Passes dust checks even with non-parent ancestors
270 [ + - + - : 4 : BOOST_CHECK(CheckEphemeralSpends({child_no_dust}, dustrelay, pool, child_state, child_txid));
+ - + - +
+ + - - -
- - ]
271 [ + - + - : 2 : BOOST_CHECK(child_state.IsValid());
+ - ]
272 [ + - + - : 1 : BOOST_CHECK_EQUAL(child_txid, Txid());
+ - ]
273 [ + - + - : 82 : }
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- ]
274 : :
275 [ + - + - : 7 : BOOST_FIXTURE_TEST_CASE(version3_tests, RegTestingSetup)
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- ]
276 : : {
277 : : // Test TRUC policy helper functions
278 : 1 : CTxMemPool& pool = *Assert(m_node.mempool);
279 [ + - ]: 1 : LOCK2(cs_main, pool.cs);
280 : 1 : TestMemPoolEntryHelper entry;
281 [ + - ]: 1 : std::set<Txid> empty_conflicts_set;
282 : 1 : CTxMemPool::setEntries empty_ancestors;
283 : :
284 [ + - + - ]: 1 : auto mempool_tx_v3 = make_tx(random_outpoints(1), /*version=*/3);
285 [ + - + - ]: 1 : AddToMempool(pool, entry.FromTx(mempool_tx_v3));
286 [ + - + - ]: 1 : auto mempool_tx_v2 = make_tx(random_outpoints(1), /*version=*/2);
287 [ + - + - ]: 1 : AddToMempool(pool, entry.FromTx(mempool_tx_v2));
288 : : // Default values.
289 : 1 : CTxMemPool::Limits m_limits{};
290 : :
291 : : // Cannot spend from an unconfirmed TRUC transaction unless this tx is also TRUC.
292 : 1 : {
293 : : // mempool_tx_v3
294 : : // ^
295 : : // tx_v2_from_v3
296 [ + - + - : 2 : auto tx_v2_from_v3 = make_tx({COutPoint{mempool_tx_v3->GetHash(), 0}}, /*version=*/2);
+ - ]
297 [ + - + - ]: 1 : auto ancestors_v2_from_v3{pool.CalculateMemPoolAncestors(entry.FromTx(tx_v2_from_v3), m_limits)};
298 [ + - ]: 1 : const auto expected_error_str{strprintf("non-version=3 tx %s (wtxid=%s) cannot spend from version=3 tx %s (wtxid=%s)",
299 [ + - + - ]: 2 : tx_v2_from_v3->GetHash().ToString(), tx_v2_from_v3->GetWitnessHash().ToString(),
300 [ + - + - : 3 : mempool_tx_v3->GetHash().ToString(), mempool_tx_v3->GetWitnessHash().ToString())};
+ - + - ]
301 [ + - + - ]: 1 : auto result_v2_from_v3{SingleTRUCChecks(tx_v2_from_v3, *ancestors_v2_from_v3, empty_conflicts_set, GetVirtualTransactionSize(*tx_v2_from_v3))};
302 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(result_v2_from_v3->first, expected_error_str);
303 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(result_v2_from_v3->second, nullptr);
304 : :
305 [ + + + - : 3 : Package package_v3_v2{mempool_tx_v3, tx_v2_from_v3};
- - - - ]
306 [ + - + - : 1 : BOOST_CHECK_EQUAL(*PackageTRUCChecks(tx_v2_from_v3, GetVirtualTransactionSize(*tx_v2_from_v3), package_v3_v2, empty_ancestors), expected_error_str);
+ - + - ]
307 [ + - + - ]: 2 : CTxMemPool::setEntries entries_mempool_v3{pool.GetIter(mempool_tx_v3->GetHash().ToUint256()).value()};
308 [ + - + - : 3 : BOOST_CHECK_EQUAL(*PackageTRUCChecks(tx_v2_from_v3, GetVirtualTransactionSize(*tx_v2_from_v3), {tx_v2_from_v3}, entries_mempool_v3), expected_error_str);
+ - + - +
+ + - - -
- - ]
309 : :
310 : : // mempool_tx_v3 mempool_tx_v2
311 : : // ^ ^
312 : : // tx_v2_from_v2_and_v3
313 [ + - + - : 2 : auto tx_v2_from_v2_and_v3 = make_tx({COutPoint{mempool_tx_v3->GetHash(), 0}, COutPoint{mempool_tx_v2->GetHash(), 0}}, /*version=*/2);
+ - ]
314 [ + - + - ]: 1 : auto ancestors_v2_from_both{pool.CalculateMemPoolAncestors(entry.FromTx(tx_v2_from_v2_and_v3), m_limits)};
315 [ + - ]: 1 : const auto expected_error_str_2{strprintf("non-version=3 tx %s (wtxid=%s) cannot spend from version=3 tx %s (wtxid=%s)",
316 [ + - + - ]: 2 : tx_v2_from_v2_and_v3->GetHash().ToString(), tx_v2_from_v2_and_v3->GetWitnessHash().ToString(),
317 [ + - + - : 3 : mempool_tx_v3->GetHash().ToString(), mempool_tx_v3->GetWitnessHash().ToString())};
+ - + - ]
318 [ + - + - ]: 1 : auto result_v2_from_both{SingleTRUCChecks(tx_v2_from_v2_and_v3, *ancestors_v2_from_both, empty_conflicts_set, GetVirtualTransactionSize(*tx_v2_from_v2_and_v3))};
319 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(result_v2_from_both->first, expected_error_str_2);
320 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(result_v2_from_both->second, nullptr);
321 : :
322 [ + + + - : 4 : Package package_v3_v2_v2{mempool_tx_v3, mempool_tx_v2, tx_v2_from_v2_and_v3};
- - - - ]
323 [ + - + - : 1 : BOOST_CHECK_EQUAL(*PackageTRUCChecks(tx_v2_from_v2_and_v3, GetVirtualTransactionSize(*tx_v2_from_v2_and_v3), package_v3_v2_v2, empty_ancestors), expected_error_str_2);
+ - + - ]
324 [ + - + - ]: 2 : }
325 : :
326 : : // TRUC cannot spend from an unconfirmed non-TRUC transaction.
327 : 1 : {
328 : : // mempool_tx_v2
329 : : // ^
330 : : // tx_v3_from_v2
331 [ + - + - : 2 : auto tx_v3_from_v2 = make_tx({COutPoint{mempool_tx_v2->GetHash(), 0}}, /*version=*/3);
+ - ]
332 [ + - + - ]: 1 : auto ancestors_v3_from_v2{pool.CalculateMemPoolAncestors(entry.FromTx(tx_v3_from_v2), m_limits)};
333 [ + - ]: 1 : const auto expected_error_str{strprintf("version=3 tx %s (wtxid=%s) cannot spend from non-version=3 tx %s (wtxid=%s)",
334 [ + - + - ]: 2 : tx_v3_from_v2->GetHash().ToString(), tx_v3_from_v2->GetWitnessHash().ToString(),
335 [ + - + - : 3 : mempool_tx_v2->GetHash().ToString(), mempool_tx_v2->GetWitnessHash().ToString())};
+ - + - ]
336 [ + - + - ]: 1 : auto result_v3_from_v2{SingleTRUCChecks(tx_v3_from_v2, *ancestors_v3_from_v2, empty_conflicts_set, GetVirtualTransactionSize(*tx_v3_from_v2))};
337 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(result_v3_from_v2->first, expected_error_str);
338 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(result_v3_from_v2->second, nullptr);
339 : :
340 [ + + + - : 3 : Package package_v2_v3{mempool_tx_v2, tx_v3_from_v2};
- - - - ]
341 [ + - + - : 1 : BOOST_CHECK_EQUAL(*PackageTRUCChecks(tx_v3_from_v2, GetVirtualTransactionSize(*tx_v3_from_v2), package_v2_v3, empty_ancestors), expected_error_str);
+ - + - ]
342 [ + - + - ]: 2 : CTxMemPool::setEntries entries_mempool_v2{pool.GetIter(mempool_tx_v2->GetHash().ToUint256()).value()};
343 [ + - + - : 3 : BOOST_CHECK_EQUAL(*PackageTRUCChecks(tx_v3_from_v2, GetVirtualTransactionSize(*tx_v3_from_v2), {tx_v3_from_v2}, entries_mempool_v2), expected_error_str);
+ - + - +
+ + - - -
- - ]
344 : :
345 : : // mempool_tx_v3 mempool_tx_v2
346 : : // ^ ^
347 : : // tx_v3_from_v2_and_v3
348 [ + - + - : 2 : auto tx_v3_from_v2_and_v3 = make_tx({COutPoint{mempool_tx_v3->GetHash(), 0}, COutPoint{mempool_tx_v2->GetHash(), 0}}, /*version=*/3);
+ - ]
349 [ + - + - ]: 1 : auto ancestors_v3_from_both{pool.CalculateMemPoolAncestors(entry.FromTx(tx_v3_from_v2_and_v3), m_limits)};
350 [ + - ]: 1 : const auto expected_error_str_2{strprintf("version=3 tx %s (wtxid=%s) cannot spend from non-version=3 tx %s (wtxid=%s)",
351 [ + - + - ]: 2 : tx_v3_from_v2_and_v3->GetHash().ToString(), tx_v3_from_v2_and_v3->GetWitnessHash().ToString(),
352 [ + - + - : 3 : mempool_tx_v2->GetHash().ToString(), mempool_tx_v2->GetWitnessHash().ToString())};
+ - + - ]
353 [ + - + - ]: 1 : auto result_v3_from_both{SingleTRUCChecks(tx_v3_from_v2_and_v3, *ancestors_v3_from_both, empty_conflicts_set, GetVirtualTransactionSize(*tx_v3_from_v2_and_v3))};
354 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(result_v3_from_both->first, expected_error_str_2);
355 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(result_v3_from_both->second, nullptr);
356 : :
357 : : // tx_v3_from_v2_and_v3 also violates TRUC_ANCESTOR_LIMIT.
358 [ + - ]: 1 : const auto expected_error_str_3{strprintf("tx %s (wtxid=%s) would have too many ancestors",
359 [ + - + - : 2 : tx_v3_from_v2_and_v3->GetHash().ToString(), tx_v3_from_v2_and_v3->GetWitnessHash().ToString())};
+ - ]
360 [ + + + - : 4 : Package package_v3_v2_v3{mempool_tx_v3, mempool_tx_v2, tx_v3_from_v2_and_v3};
- - - - ]
361 [ + - + - : 1 : BOOST_CHECK_EQUAL(*PackageTRUCChecks(tx_v3_from_v2_and_v3, GetVirtualTransactionSize(*tx_v3_from_v2_and_v3), package_v3_v2_v3, empty_ancestors), expected_error_str_3);
+ - + - ]
362 [ + - + - ]: 2 : }
363 : : // V3 from V3 is ok, and non-V3 from non-V3 is ok.
364 : 1 : {
365 : : // mempool_tx_v3
366 : : // ^
367 : : // tx_v3_from_v3
368 [ + - + - : 2 : auto tx_v3_from_v3 = make_tx({COutPoint{mempool_tx_v3->GetHash(), 0}}, /*version=*/3);
+ - ]
369 [ + - + - ]: 1 : auto ancestors_v3{pool.CalculateMemPoolAncestors(entry.FromTx(tx_v3_from_v3), m_limits)};
370 [ + - + - : 2 : BOOST_CHECK(SingleTRUCChecks(tx_v3_from_v3, *ancestors_v3, empty_conflicts_set, GetVirtualTransactionSize(*tx_v3_from_v3))
+ - + - +
- ]
371 : : == std::nullopt);
372 : :
373 [ + + + - : 3 : Package package_v3_v3{mempool_tx_v3, tx_v3_from_v3};
- - - - ]
374 [ + - + - : 2 : BOOST_CHECK(PackageTRUCChecks(tx_v3_from_v3, GetVirtualTransactionSize(*tx_v3_from_v3), package_v3_v3, empty_ancestors) == std::nullopt);
+ - + - +
- ]
375 : :
376 : : // mempool_tx_v2
377 : : // ^
378 : : // tx_v2_from_v2
379 [ + - + - : 2 : auto tx_v2_from_v2 = make_tx({COutPoint{mempool_tx_v2->GetHash(), 0}}, /*version=*/2);
+ - ]
380 [ + - + - ]: 1 : auto ancestors_v2{pool.CalculateMemPoolAncestors(entry.FromTx(tx_v2_from_v2), m_limits)};
381 [ + - + - : 2 : BOOST_CHECK(SingleTRUCChecks(tx_v2_from_v2, *ancestors_v2, empty_conflicts_set, GetVirtualTransactionSize(*tx_v2_from_v2))
+ - + - +
- ]
382 : : == std::nullopt);
383 : :
384 [ + + + - : 3 : Package package_v2_v2{mempool_tx_v2, tx_v2_from_v2};
- - - - ]
385 [ + - + - : 2 : BOOST_CHECK(PackageTRUCChecks(tx_v2_from_v2, GetVirtualTransactionSize(*tx_v2_from_v2), package_v2_v2, empty_ancestors) == std::nullopt);
+ - + - ]
386 [ + - + - ]: 2 : }
387 : :
388 : : // Tx spending TRUC cannot have too many mempool ancestors
389 : : // Configuration where the tx has multiple direct parents.
390 : 1 : {
391 : 1 : Package package_multi_parents;
392 : 1 : std::vector<COutPoint> mempool_outpoints;
393 [ + - ]: 1 : mempool_outpoints.emplace_back(mempool_tx_v3->GetHash(), 0);
394 [ + - ]: 1 : package_multi_parents.emplace_back(mempool_tx_v3);
395 [ + + ]: 3 : for (size_t i{0}; i < 2; ++i) {
396 [ + - + - ]: 2 : auto mempool_tx = make_tx(random_outpoints(i + 1), /*version=*/3);
397 [ + - + - ]: 2 : AddToMempool(pool, entry.FromTx(mempool_tx));
398 [ + - ]: 2 : mempool_outpoints.emplace_back(mempool_tx->GetHash(), 0);
399 [ + - ]: 2 : package_multi_parents.emplace_back(mempool_tx);
400 : 2 : }
401 [ + - ]: 1 : auto tx_v3_multi_parent = make_tx(mempool_outpoints, /*version=*/3);
402 [ + - ]: 1 : package_multi_parents.emplace_back(tx_v3_multi_parent);
403 [ + - + - ]: 1 : auto ancestors{pool.CalculateMemPoolAncestors(entry.FromTx(tx_v3_multi_parent), m_limits)};
404 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(ancestors->size(), 3);
405 [ + - ]: 1 : const auto expected_error_str{strprintf("tx %s (wtxid=%s) would have too many ancestors",
406 [ + - + - : 2 : tx_v3_multi_parent->GetHash().ToString(), tx_v3_multi_parent->GetWitnessHash().ToString())};
+ - ]
407 [ + - + - ]: 1 : auto result{SingleTRUCChecks(tx_v3_multi_parent, *ancestors, empty_conflicts_set, GetVirtualTransactionSize(*tx_v3_multi_parent))};
408 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(result->first, expected_error_str);
409 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(result->second, nullptr);
410 : :
411 [ + - + - : 1 : BOOST_CHECK_EQUAL(*PackageTRUCChecks(tx_v3_multi_parent, GetVirtualTransactionSize(*tx_v3_multi_parent), package_multi_parents, empty_ancestors),
+ - + - ]
412 : : expected_error_str);
413 [ + - ]: 2 : }
414 : :
415 : : // Configuration where the tx is in a multi-generation chain.
416 : 1 : {
417 : 1 : Package package_multi_gen;
418 : 1 : CTransactionRef middle_tx;
419 [ + - ]: 1 : auto last_outpoint{random_outpoints(1)[0]};
420 [ + + ]: 3 : for (size_t i{0}; i < 2; ++i) {
421 [ + - + - : 4 : auto mempool_tx = make_tx({last_outpoint}, /*version=*/3);
+ - ]
422 [ + - + - ]: 2 : AddToMempool(pool, entry.FromTx(mempool_tx));
423 [ + - ]: 2 : last_outpoint = COutPoint{mempool_tx->GetHash(), 0};
424 [ + - ]: 2 : package_multi_gen.emplace_back(mempool_tx);
425 [ + + ]: 2 : if (i == 1) middle_tx = mempool_tx;
426 : 2 : }
427 [ + - + - : 2 : auto tx_v3_multi_gen = make_tx({last_outpoint}, /*version=*/3);
+ - ]
428 [ + - ]: 1 : package_multi_gen.emplace_back(tx_v3_multi_gen);
429 [ + - + - ]: 1 : auto ancestors{pool.CalculateMemPoolAncestors(entry.FromTx(tx_v3_multi_gen), m_limits)};
430 [ + - ]: 1 : const auto expected_error_str{strprintf("tx %s (wtxid=%s) would have too many ancestors",
431 [ + - + - : 2 : tx_v3_multi_gen->GetHash().ToString(), tx_v3_multi_gen->GetWitnessHash().ToString())};
+ - ]
432 [ + - + - ]: 1 : auto result{SingleTRUCChecks(tx_v3_multi_gen, *ancestors, empty_conflicts_set, GetVirtualTransactionSize(*tx_v3_multi_gen))};
433 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(result->first, expected_error_str);
434 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(result->second, nullptr);
435 : :
436 : : // Middle tx is what triggers a failure for the grandchild:
437 [ + - + - : 1 : BOOST_CHECK_EQUAL(*PackageTRUCChecks(middle_tx, GetVirtualTransactionSize(*middle_tx), package_multi_gen, empty_ancestors), expected_error_str);
+ - + - ]
438 [ + - + - : 2 : BOOST_CHECK(PackageTRUCChecks(tx_v3_multi_gen, GetVirtualTransactionSize(*tx_v3_multi_gen), package_multi_gen, empty_ancestors) == std::nullopt);
+ - + - ]
439 [ + - + - ]: 3 : }
440 : :
441 : : // Tx spending TRUC cannot be too large in virtual size.
442 [ + - ]: 1 : auto many_inputs{random_outpoints(100)};
443 [ + - ]: 1 : many_inputs.emplace_back(mempool_tx_v3->GetHash(), 0);
444 : 1 : {
445 [ + - ]: 1 : auto tx_v3_child_big = make_tx(many_inputs, /*version=*/3);
446 [ + - ]: 1 : const auto vsize{GetVirtualTransactionSize(*tx_v3_child_big)};
447 [ + - + - ]: 1 : auto ancestors{pool.CalculateMemPoolAncestors(entry.FromTx(tx_v3_child_big), m_limits)};
448 [ + - ]: 1 : const auto expected_error_str{strprintf("version=3 child tx %s (wtxid=%s) is too big: %u > %u virtual bytes",
449 [ + - + - : 2 : tx_v3_child_big->GetHash().ToString(), tx_v3_child_big->GetWitnessHash().ToString(), vsize, TRUC_CHILD_MAX_VSIZE)};
+ - ]
450 [ + - + - ]: 1 : auto result{SingleTRUCChecks(tx_v3_child_big, *ancestors, empty_conflicts_set, GetVirtualTransactionSize(*tx_v3_child_big))};
451 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(result->first, expected_error_str);
452 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(result->second, nullptr);
453 : :
454 [ + + + - : 3 : Package package_child_big{mempool_tx_v3, tx_v3_child_big};
- - - - ]
455 [ + - + - : 1 : BOOST_CHECK_EQUAL(*PackageTRUCChecks(tx_v3_child_big, GetVirtualTransactionSize(*tx_v3_child_big), package_child_big, empty_ancestors),
+ - + - ]
456 : : expected_error_str);
457 [ + - ]: 1 : }
458 : :
459 : : // Tx spending TRUC cannot have too many sigops.
460 : : // This child has 10 P2WSH multisig inputs.
461 [ + - ]: 1 : auto multisig_outpoints{random_outpoints(10)};
462 [ + - ]: 1 : multisig_outpoints.emplace_back(mempool_tx_v3->GetHash(), 0);
463 [ + - ]: 1 : auto keys{random_keys(2)};
464 : 1 : CScript script_multisig;
465 [ + - ]: 1 : script_multisig << OP_1;
466 [ + + ]: 3 : for (const auto& key : keys) {
467 [ + - ]: 2 : script_multisig << ToByteVector(key);
468 : : }
469 [ + - + - ]: 1 : script_multisig << OP_2 << OP_CHECKMULTISIG;
470 : 1 : {
471 [ + - ]: 1 : CMutableTransaction mtx_many_sigops = CMutableTransaction{};
472 : 1 : mtx_many_sigops.version = TRUC_VERSION;
473 [ + + ]: 12 : for (const auto& outpoint : multisig_outpoints) {
474 [ + - ]: 11 : mtx_many_sigops.vin.emplace_back(outpoint);
475 [ + - + - ]: 22 : mtx_many_sigops.vin.back().scriptWitness.stack.emplace_back(script_multisig.begin(), script_multisig.end());
476 : : }
477 [ + - ]: 1 : mtx_many_sigops.vout.resize(1);
478 [ + - ]: 1 : mtx_many_sigops.vout.back().scriptPubKey = CScript() << OP_TRUE;
479 : 1 : mtx_many_sigops.vout.back().nValue = 10000;
480 [ + - ]: 1 : auto tx_many_sigops{MakeTransactionRef(mtx_many_sigops)};
481 : :
482 [ + - + - ]: 1 : auto ancestors{pool.CalculateMemPoolAncestors(entry.FromTx(tx_many_sigops), m_limits)};
483 : : // legacy uses fAccurate = false, and the maximum number of multisig keys is used
484 [ + - ]: 1 : const int64_t total_sigops{static_cast<int64_t>(tx_many_sigops->vin.size()) * static_cast<int64_t>(script_multisig.GetSigOpCount(/*fAccurate=*/false))};
485 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(total_sigops, tx_many_sigops->vin.size() * MAX_PUBKEYS_PER_MULTISIG);
486 [ + - ]: 1 : const int64_t bip141_vsize{GetVirtualTransactionSize(*tx_many_sigops)};
487 : : // Weight limit is not reached...
488 [ + - + - : 2 : BOOST_CHECK(SingleTRUCChecks(tx_many_sigops, *ancestors, empty_conflicts_set, bip141_vsize) == std::nullopt);
+ - + - ]
489 : : // ...but sigop limit is.
490 : 1 : const auto expected_error_str{strprintf("version=3 child tx %s (wtxid=%s) is too big: %u > %u virtual bytes",
491 [ + - + - ]: 2 : tx_many_sigops->GetHash().ToString(), tx_many_sigops->GetWitnessHash().ToString(),
492 [ + - + - ]: 2 : total_sigops * DEFAULT_BYTES_PER_SIGOP / WITNESS_SCALE_FACTOR, TRUC_CHILD_MAX_VSIZE)};
493 : 2 : auto result{SingleTRUCChecks(tx_many_sigops, *ancestors, empty_conflicts_set,
494 [ + - + - ]: 1 : GetVirtualTransactionSize(*tx_many_sigops, /*nSigOpCost=*/total_sigops, /*bytes_per_sigop=*/ DEFAULT_BYTES_PER_SIGOP))};
495 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(result->first, expected_error_str);
496 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(result->second, nullptr);
497 : :
498 [ + + + - : 3 : Package package_child_sigops{mempool_tx_v3, tx_many_sigops};
- - - - ]
499 [ + - + - : 1 : BOOST_CHECK_EQUAL(*PackageTRUCChecks(tx_many_sigops, total_sigops * DEFAULT_BYTES_PER_SIGOP / WITNESS_SCALE_FACTOR, package_child_sigops, empty_ancestors),
+ - ]
500 : : expected_error_str);
501 [ + - ]: 2 : }
502 : :
503 : : // Parent + child with TRUC in the mempool. Child is allowed as long as it is under TRUC_CHILD_MAX_VSIZE.
504 [ + - + - : 2 : auto tx_mempool_v3_child = make_tx({COutPoint{mempool_tx_v3->GetHash(), 0}}, /*version=*/3);
+ - ]
505 : 1 : {
506 [ + - + - : 2 : BOOST_CHECK(GetTransactionWeight(*tx_mempool_v3_child) <= TRUC_CHILD_MAX_VSIZE * WITNESS_SCALE_FACTOR);
+ - ]
507 [ + - + - ]: 1 : auto ancestors{pool.CalculateMemPoolAncestors(entry.FromTx(tx_mempool_v3_child), m_limits)};
508 [ + - + - : 2 : BOOST_CHECK(SingleTRUCChecks(tx_mempool_v3_child, *ancestors, empty_conflicts_set, GetVirtualTransactionSize(*tx_mempool_v3_child)) == std::nullopt);
+ - + - +
- ]
509 [ + - + - ]: 1 : AddToMempool(pool, entry.FromTx(tx_mempool_v3_child));
510 : :
511 [ + + + - : 3 : Package package_v3_1p1c{mempool_tx_v3, tx_mempool_v3_child};
- - - - ]
512 [ + - + - : 2 : BOOST_CHECK(PackageTRUCChecks(tx_mempool_v3_child, GetVirtualTransactionSize(*tx_mempool_v3_child), package_v3_1p1c, empty_ancestors) == std::nullopt);
+ - + - ]
513 : 1 : }
514 : :
515 : : // A TRUC transaction cannot have more than 1 descendant. Sibling is returned when exactly 1 exists.
516 : 1 : {
517 [ + - + - : 2 : auto tx_v3_child2 = make_tx({COutPoint{mempool_tx_v3->GetHash(), 1}}, /*version=*/3);
+ - ]
518 : :
519 : : // Configuration where parent already has 1 other child in mempool
520 [ + - + - ]: 1 : auto ancestors_1sibling{pool.CalculateMemPoolAncestors(entry.FromTx(tx_v3_child2), m_limits)};
521 [ + - ]: 1 : const auto expected_error_str{strprintf("tx %s (wtxid=%s) would exceed descendant count limit",
522 [ + - + - : 2 : mempool_tx_v3->GetHash().ToString(), mempool_tx_v3->GetWitnessHash().ToString())};
+ - ]
523 [ + - + - ]: 1 : auto result_with_sibling_eviction{SingleTRUCChecks(tx_v3_child2, *ancestors_1sibling, empty_conflicts_set, GetVirtualTransactionSize(*tx_v3_child2))};
524 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(result_with_sibling_eviction->first, expected_error_str);
525 : : // The other mempool child is returned to allow for sibling eviction.
526 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(result_with_sibling_eviction->second, tx_mempool_v3_child);
527 : :
528 : : // If directly replacing the child, make sure there is no double-counting.
529 [ + - + - : 3 : BOOST_CHECK(SingleTRUCChecks(tx_v3_child2, *ancestors_1sibling, {tx_mempool_v3_child->GetHash()}, GetVirtualTransactionSize(*tx_v3_child2))
+ - + - +
- + - ]
530 : : == std::nullopt);
531 : :
532 [ + + + - : 4 : Package package_v3_1p2c{mempool_tx_v3, tx_mempool_v3_child, tx_v3_child2};
- - - - ]
533 [ + - + - : 1 : BOOST_CHECK_EQUAL(*PackageTRUCChecks(tx_v3_child2, GetVirtualTransactionSize(*tx_v3_child2), package_v3_1p2c, empty_ancestors),
+ - + - ]
534 : : expected_error_str);
535 : :
536 : : // Configuration where parent already has 2 other children in mempool (no sibling eviction allowed). This may happen as the result of a reorg.
537 [ + - + - ]: 1 : AddToMempool(pool, entry.FromTx(tx_v3_child2));
538 [ + - + - : 2 : auto tx_v3_child3 = make_tx({COutPoint{mempool_tx_v3->GetHash(), 24}}, /*version=*/3);
+ - ]
539 [ + - ]: 1 : auto entry_mempool_parent = pool.GetIter(mempool_tx_v3->GetHash().ToUint256()).value();
540 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(entry_mempool_parent->GetCountWithDescendants(), 3);
541 [ + - + - ]: 1 : auto ancestors_2siblings{pool.CalculateMemPoolAncestors(entry.FromTx(tx_v3_child3), m_limits)};
542 : :
543 [ + - + - ]: 1 : auto result_2children{SingleTRUCChecks(tx_v3_child3, *ancestors_2siblings, empty_conflicts_set, GetVirtualTransactionSize(*tx_v3_child3))};
544 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(result_2children->first, expected_error_str);
545 : : // The other mempool child is not returned because sibling eviction is not allowed.
546 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(result_2children->second, nullptr);
547 [ + - + - ]: 2 : }
548 : :
549 : : // Sibling eviction: parent already has 1 other child, which also has its own child (no sibling eviction allowed). This may happen as the result of a reorg.
550 : 1 : {
551 [ + - + - ]: 1 : auto tx_mempool_grandparent = make_tx(random_outpoints(1), /*version=*/3);
552 [ + - + - : 2 : auto tx_mempool_sibling = make_tx({COutPoint{tx_mempool_grandparent->GetHash(), 0}}, /*version=*/3);
+ - ]
553 [ + - + - : 2 : auto tx_mempool_nibling = make_tx({COutPoint{tx_mempool_sibling->GetHash(), 0}}, /*version=*/3);
+ - ]
554 [ + - + - : 2 : auto tx_to_submit = make_tx({COutPoint{tx_mempool_grandparent->GetHash(), 1}}, /*version=*/3);
+ - ]
555 : :
556 [ + - + - ]: 1 : AddToMempool(pool, entry.FromTx(tx_mempool_grandparent));
557 [ + - + - ]: 1 : AddToMempool(pool, entry.FromTx(tx_mempool_sibling));
558 [ + - + - ]: 1 : AddToMempool(pool, entry.FromTx(tx_mempool_nibling));
559 : :
560 [ + - + - ]: 1 : auto ancestors_3gen{pool.CalculateMemPoolAncestors(entry.FromTx(tx_to_submit), m_limits)};
561 [ + - ]: 1 : const auto expected_error_str{strprintf("tx %s (wtxid=%s) would exceed descendant count limit",
562 [ + - + - : 2 : tx_mempool_grandparent->GetHash().ToString(), tx_mempool_grandparent->GetWitnessHash().ToString())};
+ - ]
563 [ + - + - ]: 1 : auto result_3gen{SingleTRUCChecks(tx_to_submit, *ancestors_3gen, empty_conflicts_set, GetVirtualTransactionSize(*tx_to_submit))};
564 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(result_3gen->first, expected_error_str);
565 : : // The other mempool child is not returned because sibling eviction is not allowed.
566 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(result_3gen->second, nullptr);
567 [ + - + - : 5 : }
+ - + - +
- ]
568 : :
569 : : // Configuration where tx has multiple generations of descendants is not tested because that is
570 : : // equivalent to the tx with multiple generations of ancestors.
571 [ + - + - : 42 : }
- + + - +
- + - + -
+ - - + +
- + - - +
+ - + - +
- + - + -
- + + - +
- - + + -
+ - - + +
- + - - +
+ - + - -
+ + - + -
- + + - +
- + - - +
+ - + - +
- + - ]
572 : :
573 : : BOOST_AUTO_TEST_SUITE_END()
|