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 <blockencodings.h>
6 : : #include <chainparams.h>
7 : : #include <consensus/merkle.h>
8 : : #include <pow.h>
9 : : #include <streams.h>
10 : : #include <test/util/random.h>
11 : : #include <test/util/txmempool.h>
12 : :
13 : : #include <test/util/common.h>
14 : : #include <test/util/setup_common.h>
15 : :
16 : : #include <boost/test/unit_test.hpp>
17 : :
18 : : const std::vector<std::pair<Wtxid, CTransactionRef>> empty_extra_txn;
19 : :
20 : : BOOST_FIXTURE_TEST_SUITE(blockencodings_tests, RegTestingSetup)
21 : :
22 : 7 : static CMutableTransaction BuildTransactionTestCase() {
23 : 7 : CMutableTransaction tx;
24 [ + - ]: 7 : tx.vin.resize(1);
25 : 7 : tx.vin[0].scriptSig.resize(10);
26 [ + - ]: 7 : tx.vout.resize(1);
27 : 7 : tx.vout[0].nValue = 42;
28 : 7 : return tx;
29 : 0 : }
30 : :
31 : 4 : static CBlock BuildBlockTestCase(FastRandomContext& ctx) {
32 : 4 : CBlock block;
33 [ + - ]: 4 : CMutableTransaction tx = BuildTransactionTestCase();
34 : :
35 [ + - ]: 4 : block.vtx.resize(3);
36 [ + - - + ]: 8 : block.vtx[0] = MakeTransactionRef(tx);
37 : 4 : block.nVersion = 42;
38 : 4 : block.hashPrevBlock = ctx.rand256();
39 : 4 : block.nBits = 0x207fffff;
40 : :
41 [ + - ]: 4 : tx.vin[0].prevout.hash = Txid::FromUint256(ctx.rand256());
42 : 4 : tx.vin[0].prevout.n = 0;
43 [ + - - + ]: 8 : block.vtx[1] = MakeTransactionRef(tx);
44 : :
45 [ + - ]: 4 : tx.vin.resize(10);
46 [ - + + + ]: 44 : for (size_t i = 0; i < tx.vin.size(); i++) {
47 : 40 : tx.vin[i].prevout.hash = Txid::FromUint256(ctx.rand256());
48 : 40 : tx.vin[i].prevout.n = 0;
49 : : }
50 [ + - - + ]: 8 : block.vtx[2] = MakeTransactionRef(tx);
51 : :
52 : 4 : bool mutated;
53 [ + - ]: 4 : block.hashMerkleRoot = BlockMerkleRoot(block, &mutated);
54 [ - + ]: 4 : assert(!mutated);
55 [ + - + - : 6 : while (!CheckProofOfWork(block.GetHash(), block.nBits, Params().GetConsensus())) ++block.nNonce;
+ - + + ]
56 : 4 : return block;
57 : 4 : }
58 : :
59 : : // Number of shared use_counts we expect for a tx we haven't touched
60 : : // (block + mempool entry + our copy from the GetSharedTx call)
61 : : constexpr long SHARED_TX_OFFSET{3};
62 : :
63 [ + - + - : 7 : BOOST_AUTO_TEST_CASE(SimpleRoundTripTest)
+ - + - -
+ + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- ]
64 : : {
65 [ - + ]: 1 : CTxMemPool& pool = *Assert(m_node.mempool);
66 : 1 : TestMemPoolEntryHelper entry;
67 : 1 : auto rand_ctx(FastRandomContext(uint256{42}));
68 [ + - ]: 1 : CBlock block(BuildBlockTestCase(rand_ctx));
69 : :
70 [ + - + - ]: 1 : LOCK2(cs_main, pool.cs);
71 [ + - + - ]: 1 : TryAddToMempool(pool, entry.FromTx(block.vtx[2]));
72 [ + - + - : 2 : BOOST_CHECK_EQUAL(pool.get(block.vtx[2]->GetHash()).use_count(), SHARED_TX_OFFSET + 0);
+ - + - ]
73 : :
74 : : // Do a simple ShortTxIDs RT
75 : 1 : {
76 [ + - ]: 1 : CBlockHeaderAndShortTxIDs shortIDs{block, rand_ctx.rand64()};
77 : :
78 : 1 : DataStream stream{};
79 [ + - ]: 1 : stream << shortIDs;
80 : :
81 : 1 : CBlockHeaderAndShortTxIDs shortIDs2;
82 [ + - ]: 1 : stream >> shortIDs2;
83 : :
84 : 1 : PartiallyDownloadedBlock partialBlock(&pool);
85 [ + - + - : 2 : BOOST_CHECK(partialBlock.InitData(shortIDs2, empty_extra_txn) == READ_STATUS_OK);
+ - + - ]
86 [ + - + - : 2 : BOOST_CHECK( partialBlock.IsTxAvailable(0));
+ - + - ]
87 [ + - + - : 2 : BOOST_CHECK(!partialBlock.IsTxAvailable(1));
+ - + - ]
88 [ + - + - : 2 : BOOST_CHECK( partialBlock.IsTxAvailable(2));
+ - + - ]
89 : :
90 [ + - + - : 2 : BOOST_CHECK_EQUAL(pool.get(block.vtx[2]->GetHash()).use_count(), SHARED_TX_OFFSET + 1);
+ - + - ]
91 : :
92 [ + - ]: 1 : size_t poolSize = pool.size();
93 [ + - ]: 1 : pool.removeRecursive(*block.vtx[2], MemPoolRemovalReason::REPLACED);
94 [ + - + - : 1 : BOOST_CHECK_EQUAL(pool.size(), poolSize - 1);
+ - ]
95 : :
96 : 1 : CBlock block2;
97 : 1 : {
98 [ + - ]: 1 : PartiallyDownloadedBlock tmp = partialBlock;
99 [ + - + - : 2 : BOOST_CHECK(partialBlock.FillBlock(block2, {}, /*segwit_active=*/true) == READ_STATUS_INVALID); // No transactions
+ - + - ]
100 [ + - ]: 1 : partialBlock = tmp;
101 : 0 : }
102 : :
103 : : // Wrong transaction
104 : 1 : {
105 [ + - ]: 1 : PartiallyDownloadedBlock tmp = partialBlock;
106 [ + - + + : 2 : partialBlock.FillBlock(block2, {block.vtx[2]}, /*segwit_active=*/true); // Current implementation doesn't check txn here, but don't require that
+ - - - -
- ]
107 [ + - ]: 1 : partialBlock = tmp;
108 : 0 : }
109 : 1 : bool mutated;
110 [ + - + - : 2 : BOOST_CHECK(block.hashMerkleRoot != BlockMerkleRoot(block2, &mutated));
+ - ]
111 : :
112 : 1 : CBlock block3;
113 [ + - + - : 4 : BOOST_CHECK(partialBlock.FillBlock(block3, {block.vtx[1]}, /*segwit_active=*/true) == READ_STATUS_OK);
+ - + - +
+ + - - -
- - ]
114 [ + - + - : 1 : BOOST_CHECK_EQUAL(block.GetHash().ToString(), block3.GetHash().ToString());
+ - + - +
- + - ]
115 [ + - + - : 1 : BOOST_CHECK_EQUAL(block.hashMerkleRoot.ToString(), BlockMerkleRoot(block3, &mutated).ToString());
+ - + - +
- ]
116 [ + - + - ]: 2 : BOOST_CHECK(!mutated);
117 [ + - ]: 4 : }
118 [ + - + - : 6 : }
+ - + - +
- ]
119 : :
120 : 2 : class TestHeaderAndShortIDs {
121 : : // Utility to encode custom CBlockHeaderAndShortTxIDs
122 : : public:
123 : : CBlockHeader header;
124 : : uint64_t nonce;
125 : : std::vector<uint64_t> shorttxids;
126 : : std::vector<PrefilledTransaction> prefilledtxn;
127 : :
128 [ + - ]: 2 : explicit TestHeaderAndShortIDs(const CBlockHeaderAndShortTxIDs& orig) {
129 : 2 : DataStream stream{};
130 [ + - ]: 2 : stream << orig;
131 [ + - ]: 4 : stream >> *this;
132 : 2 : }
133 : 2 : explicit TestHeaderAndShortIDs(const CBlock& block, FastRandomContext& ctx) :
134 [ + - ]: 4 : TestHeaderAndShortIDs(CBlockHeaderAndShortTxIDs{block, ctx.rand64()}) {}
135 : :
136 : 3 : uint64_t GetShortID(const Wtxid& txhash) const {
137 : 3 : DataStream stream{};
138 [ + - ]: 3 : stream << *this;
139 : 3 : CBlockHeaderAndShortTxIDs base;
140 [ + - ]: 3 : stream >> base;
141 [ + - ]: 3 : return base.GetShortID(txhash);
142 : 3 : }
143 : :
144 : 14 : SERIALIZE_METHODS(TestHeaderAndShortIDs, obj) { READWRITE(obj.header, obj.nonce, Using<VectorFormatter<CustomUintFormatter<CBlockHeaderAndShortTxIDs::SHORTTXIDS_LENGTH>>>(obj.shorttxids), obj.prefilledtxn); }
145 : : };
146 : :
147 : 0 : struct TestPartiallyDownloadedBlock : PartiallyDownloadedBlock {
148 : 3 : using PartiallyDownloadedBlock::PartiallyDownloadedBlock;
149 : :
150 : 3 : size_t GetMempoolCount() const { return mempool_count; }
151 : 3 : size_t GetExtraCount() const { return extra_count; }
152 : : };
153 : :
154 [ + - + - : 7 : BOOST_AUTO_TEST_CASE(NonCoinbasePreforwardRTTest)
+ - + - -
+ + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- ]
155 : : {
156 [ - + ]: 1 : CTxMemPool& pool = *Assert(m_node.mempool);
157 : 1 : TestMemPoolEntryHelper entry;
158 : 1 : auto rand_ctx(FastRandomContext(uint256{42}));
159 [ + - ]: 1 : CBlock block(BuildBlockTestCase(rand_ctx));
160 : :
161 [ + - + - ]: 1 : LOCK2(cs_main, pool.cs);
162 [ + - + - ]: 1 : TryAddToMempool(pool, entry.FromTx(block.vtx[2]));
163 [ + - + - : 2 : BOOST_CHECK_EQUAL(pool.get(block.vtx[2]->GetHash()).use_count(), SHARED_TX_OFFSET + 0);
+ - + - ]
164 : :
165 [ + - ]: 1 : Txid txhash;
166 : :
167 : : // Test with pre-forwarding tx 1, but not coinbase
168 : 1 : {
169 [ + - ]: 1 : TestHeaderAndShortIDs shortIDs(block, rand_ctx);
170 [ + - ]: 1 : shortIDs.prefilledtxn.resize(1);
171 [ - + ]: 1 : shortIDs.prefilledtxn[0] = {1, block.vtx[1]};
172 [ + - ]: 1 : shortIDs.shorttxids.resize(2);
173 [ + - + - ]: 1 : shortIDs.shorttxids[0] = shortIDs.GetShortID(block.vtx[0]->GetWitnessHash());
174 [ + - + - ]: 1 : shortIDs.shorttxids[1] = shortIDs.GetShortID(block.vtx[2]->GetWitnessHash());
175 : :
176 : 1 : DataStream stream{};
177 [ + - ]: 1 : stream << shortIDs;
178 : :
179 : 1 : CBlockHeaderAndShortTxIDs shortIDs2;
180 [ + - ]: 1 : stream >> shortIDs2;
181 : :
182 : 1 : PartiallyDownloadedBlock partialBlock(&pool);
183 [ + - + - : 2 : BOOST_CHECK(partialBlock.InitData(shortIDs2, empty_extra_txn) == READ_STATUS_OK);
+ - + - ]
184 [ + - + - : 2 : BOOST_CHECK(!partialBlock.IsTxAvailable(0));
+ - + - ]
185 [ + - + - : 2 : BOOST_CHECK( partialBlock.IsTxAvailable(1));
+ - + - ]
186 [ + - + - : 2 : BOOST_CHECK( partialBlock.IsTxAvailable(2));
+ - + - ]
187 : :
188 [ + - + - : 2 : BOOST_CHECK_EQUAL(pool.get(block.vtx[2]->GetHash()).use_count(), SHARED_TX_OFFSET + 1); // +1 because of partialBlock
+ - + - ]
189 : :
190 : 1 : CBlock block2;
191 : 1 : {
192 [ + - ]: 1 : PartiallyDownloadedBlock tmp = partialBlock;
193 [ + - + - : 2 : BOOST_CHECK(partialBlock.FillBlock(block2, {}, /*segwit_active=*/true) == READ_STATUS_INVALID); // No transactions
+ - + - ]
194 [ + - ]: 1 : partialBlock = tmp;
195 : 0 : }
196 : :
197 : : // Wrong transaction
198 : 1 : {
199 [ + - ]: 1 : PartiallyDownloadedBlock tmp = partialBlock;
200 [ + - + + : 2 : partialBlock.FillBlock(block2, {block.vtx[1]}, /*segwit_active=*/true); // Current implementation doesn't check txn here, but don't require that
+ - - - -
- ]
201 [ + - ]: 1 : partialBlock = tmp;
202 : 0 : }
203 [ + - + - : 2 : BOOST_CHECK_EQUAL(pool.get(block.vtx[2]->GetHash()).use_count(), SHARED_TX_OFFSET + 2); // +2 because of partialBlock and block2
+ - + - ]
204 : 1 : bool mutated;
205 [ + - + - : 2 : BOOST_CHECK(block.hashMerkleRoot != BlockMerkleRoot(block2, &mutated));
+ - ]
206 : :
207 : 1 : CBlock block3;
208 [ + - ]: 1 : PartiallyDownloadedBlock partialBlockCopy = partialBlock;
209 [ + - + - : 4 : BOOST_CHECK(partialBlock.FillBlock(block3, {block.vtx[0]}, /*segwit_active=*/true) == READ_STATUS_OK);
+ - + - +
+ + - - -
- - ]
210 [ + - + - : 1 : BOOST_CHECK_EQUAL(block.GetHash().ToString(), block3.GetHash().ToString());
+ - + - +
- + - ]
211 [ + - + - : 1 : BOOST_CHECK_EQUAL(block.hashMerkleRoot.ToString(), BlockMerkleRoot(block3, &mutated).ToString());
+ - + - +
- ]
212 [ + - + - : 2 : BOOST_CHECK(!mutated);
+ - ]
213 : :
214 [ + - + - : 2 : BOOST_CHECK_EQUAL(pool.get(block.vtx[2]->GetHash()).use_count(), SHARED_TX_OFFSET + 3); // +2 because of partialBlock and block2 and block3
+ - + - ]
215 : :
216 : 1 : txhash = block.vtx[2]->GetHash();
217 : 1 : block.vtx.clear();
218 : 1 : block2.vtx.clear();
219 : 1 : block3.vtx.clear();
220 [ + - + - : 3 : BOOST_CHECK_EQUAL(pool.get(txhash).use_count(), SHARED_TX_OFFSET + 1 - 1); // + 1 because of partialBlock; -1 because of block.
+ - + - ]
221 : 3 : }
222 [ + - + - : 3 : BOOST_CHECK_EQUAL(pool.get(txhash).use_count(), SHARED_TX_OFFSET - 1); // -1 because of block
+ - + - +
- ]
223 [ + - + - : 8 : }
+ - + - +
- + - ]
224 : :
225 [ + - + - : 7 : BOOST_AUTO_TEST_CASE(SufficientPreforwardRTTest)
+ - + - -
+ + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- ]
226 : : {
227 [ - + ]: 1 : CTxMemPool& pool = *Assert(m_node.mempool);
228 : 1 : TestMemPoolEntryHelper entry;
229 : 1 : auto rand_ctx(FastRandomContext(uint256{42}));
230 [ + - ]: 1 : CBlock block(BuildBlockTestCase(rand_ctx));
231 : :
232 [ + - + - ]: 1 : LOCK2(cs_main, pool.cs);
233 [ + - + - ]: 1 : TryAddToMempool(pool, entry.FromTx(block.vtx[1]));
234 [ + - + - : 2 : BOOST_CHECK_EQUAL(pool.get(block.vtx[1]->GetHash()).use_count(), SHARED_TX_OFFSET + 0);
+ - + - ]
235 : :
236 [ + - ]: 1 : Txid txhash;
237 : :
238 : : // Test with pre-forwarding coinbase + tx 2 with tx 1 in mempool
239 : 1 : {
240 [ + - ]: 1 : TestHeaderAndShortIDs shortIDs(block, rand_ctx);
241 [ + - ]: 1 : shortIDs.prefilledtxn.resize(2);
242 [ - + ]: 1 : shortIDs.prefilledtxn[0] = {0, block.vtx[0]};
243 [ - + ]: 1 : shortIDs.prefilledtxn[1] = {1, block.vtx[2]}; // id == 1 as it is 1 after index 1
244 [ + - ]: 1 : shortIDs.shorttxids.resize(1);
245 [ + - + - ]: 1 : shortIDs.shorttxids[0] = shortIDs.GetShortID(block.vtx[1]->GetWitnessHash());
246 : :
247 : 1 : DataStream stream{};
248 [ + - ]: 1 : stream << shortIDs;
249 : :
250 : 1 : CBlockHeaderAndShortTxIDs shortIDs2;
251 [ + - ]: 1 : stream >> shortIDs2;
252 : :
253 : 1 : PartiallyDownloadedBlock partialBlock(&pool);
254 [ + - + - : 2 : BOOST_CHECK(partialBlock.InitData(shortIDs2, empty_extra_txn) == READ_STATUS_OK);
+ - + - ]
255 [ + - + - : 2 : BOOST_CHECK( partialBlock.IsTxAvailable(0));
+ - + - ]
256 [ + - + - : 2 : BOOST_CHECK( partialBlock.IsTxAvailable(1));
+ - + - ]
257 [ + - + - : 2 : BOOST_CHECK( partialBlock.IsTxAvailable(2));
+ - + - ]
258 : :
259 [ + - + - : 2 : BOOST_CHECK_EQUAL(pool.get(block.vtx[1]->GetHash()).use_count(), SHARED_TX_OFFSET + 1);
+ - + - ]
260 : :
261 : 1 : CBlock block2;
262 [ + - ]: 1 : PartiallyDownloadedBlock partialBlockCopy = partialBlock;
263 [ + - + - : 2 : BOOST_CHECK(partialBlock.FillBlock(block2, {}, /*segwit_active=*/true) == READ_STATUS_OK);
+ - + - ]
264 [ + - + - : 1 : BOOST_CHECK_EQUAL(block.GetHash().ToString(), block2.GetHash().ToString());
+ - + - +
- + - ]
265 : 1 : bool mutated;
266 [ + - + - : 1 : BOOST_CHECK_EQUAL(block.hashMerkleRoot.ToString(), BlockMerkleRoot(block2, &mutated).ToString());
+ - + - +
- ]
267 [ + - + - ]: 2 : BOOST_CHECK(!mutated);
268 : :
269 : 1 : txhash = block.vtx[1]->GetHash();
270 : 1 : block.vtx.clear();
271 : 1 : block2.vtx.clear();
272 [ + - + - : 3 : BOOST_CHECK_EQUAL(pool.get(txhash).use_count(), SHARED_TX_OFFSET + 1 - 1); // + 1 because of partialBlock; -1 because of block.
+ - + - ]
273 : 3 : }
274 [ + - + - : 3 : BOOST_CHECK_EQUAL(pool.get(txhash).use_count(), SHARED_TX_OFFSET - 1); // -1 because of block
+ - + - +
- ]
275 [ + - + - : 6 : }
+ - ]
276 : :
277 [ + - + - : 7 : BOOST_AUTO_TEST_CASE(EmptyBlockRoundTripTest)
+ - + - -
+ + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- ]
278 : : {
279 [ - + ]: 1 : CTxMemPool& pool = *Assert(m_node.mempool);
280 : 1 : CMutableTransaction coinbase = BuildTransactionTestCase();
281 : :
282 : 1 : CBlock block;
283 : 1 : auto rand_ctx(FastRandomContext(uint256{42}));
284 [ + - ]: 1 : block.vtx.resize(1);
285 [ + - - + ]: 2 : block.vtx[0] = MakeTransactionRef(std::move(coinbase));
286 : 1 : block.nVersion = 42;
287 : 1 : block.hashPrevBlock = rand_ctx.rand256();
288 : 1 : block.nBits = 0x207fffff;
289 : :
290 : 1 : bool mutated;
291 [ + - ]: 1 : block.hashMerkleRoot = BlockMerkleRoot(block, &mutated);
292 [ + - ]: 1 : assert(!mutated);
293 [ + - + - : 1 : while (!CheckProofOfWork(block.GetHash(), block.nBits, Params().GetConsensus())) ++block.nNonce;
+ - - + ]
294 : :
295 : : // Test simple header round-trip with only coinbase
296 : 1 : {
297 [ + - ]: 1 : CBlockHeaderAndShortTxIDs shortIDs{block, rand_ctx.rand64()};
298 : :
299 : 1 : DataStream stream{};
300 [ + - ]: 1 : stream << shortIDs;
301 : :
302 : 1 : CBlockHeaderAndShortTxIDs shortIDs2;
303 [ + - ]: 1 : stream >> shortIDs2;
304 : :
305 : 1 : PartiallyDownloadedBlock partialBlock(&pool);
306 [ + - + - : 2 : BOOST_CHECK(partialBlock.InitData(shortIDs2, empty_extra_txn) == READ_STATUS_OK);
+ - + - ]
307 [ + - + - : 2 : BOOST_CHECK(partialBlock.IsTxAvailable(0));
+ - ]
308 : :
309 : 1 : CBlock block2;
310 : 1 : std::vector<CTransactionRef> vtx_missing;
311 [ + - + - : 2 : BOOST_CHECK(partialBlock.FillBlock(block2, vtx_missing, /*segwit_active=*/true) == READ_STATUS_OK);
+ - + - ]
312 [ + - + - : 1 : BOOST_CHECK_EQUAL(block.GetHash().ToString(), block2.GetHash().ToString());
+ - + - +
- + - ]
313 [ + - + - : 1 : BOOST_CHECK_EQUAL(block.hashMerkleRoot.ToString(), BlockMerkleRoot(block2, &mutated).ToString());
+ - + - +
- ]
314 [ + - + - ]: 2 : BOOST_CHECK(!mutated);
315 : 4 : }
316 : 2 : }
317 : :
318 [ + - + - : 7 : BOOST_AUTO_TEST_CASE(ReceiveWithExtraTransactions) {
+ - + - -
+ + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- ]
319 [ - + ]: 1 : CTxMemPool& pool = *Assert(m_node.mempool);
320 : 1 : TestMemPoolEntryHelper entry;
321 : 1 : auto rand_ctx(FastRandomContext(uint256{42}));
322 : :
323 [ + - ]: 1 : CMutableTransaction mtx = BuildTransactionTestCase();
324 [ + - ]: 1 : mtx.vin[0].prevout.hash = Txid::FromUint256(rand_ctx.rand256());
325 : 1 : mtx.vin[0].prevout.n = 0;
326 [ + - ]: 1 : const CTransactionRef non_block_tx = MakeTransactionRef(std::move(mtx));
327 : :
328 [ + - ]: 1 : CBlock block(BuildBlockTestCase(rand_ctx));
329 : : // Leave one transaction missing so scanning doesn't stop before the collision.
330 [ + - ]: 1 : mtx = BuildTransactionTestCase();
331 [ + - ]: 1 : mtx.vin[0].prevout.hash = Txid::FromUint256(rand_ctx.rand256());
332 [ + - + - : 2 : block.vtx.push_back(MakeTransactionRef(std::move(mtx)));
- + ]
333 [ + - ]: 1 : block.hashMerkleRoot = BlockMerkleRoot(block);
334 [ + - + - : 2 : while (!CheckProofOfWork(block.GetHash(), block.nBits, Params().GetConsensus())) ++block.nNonce;
+ - + + ]
335 : :
336 : 1 : std::vector<std::pair<Wtxid, CTransactionRef>> extra_txn;
337 [ + - ]: 1 : extra_txn.resize(10);
338 : :
339 [ + - + - ]: 1 : LOCK2(cs_main, pool.cs);
340 [ + - + - ]: 1 : TryAddToMempool(pool, entry.FromTx(block.vtx[2]));
341 [ + - + - : 2 : BOOST_CHECK_EQUAL(pool.get(block.vtx[2]->GetHash()).use_count(), SHARED_TX_OFFSET + 0);
+ - + - ]
342 : : // Ensure the non_block_tx is actually not in the block
343 [ + + ]: 5 : for (const auto &block_tx : block.vtx) {
344 [ + - + - ]: 4 : BOOST_CHECK_NE(block_tx->GetHash(), non_block_tx->GetHash());
345 : : }
346 : : // Ensure block.vtx[1] is not in pool
347 [ + - + - : 1 : BOOST_CHECK_EQUAL(pool.get(block.vtx[1]->GetHash()), nullptr);
+ - - + ]
348 : :
349 : 1 : {
350 [ + - ]: 1 : const CBlockHeaderAndShortTxIDs cmpctblock{block, rand_ctx.rand64()};
351 : 1 : PartiallyDownloadedBlock partial_block(&pool);
352 : 1 : PartiallyDownloadedBlock partial_block_with_extra(&pool);
353 : :
354 [ + - + - : 2 : BOOST_CHECK(partial_block.InitData(cmpctblock, extra_txn) == READ_STATUS_OK);
+ - + - ]
355 [ + - + - : 2 : BOOST_CHECK( partial_block.IsTxAvailable(0));
+ - + - ]
356 [ + - + - : 2 : BOOST_CHECK(!partial_block.IsTxAvailable(1));
+ - + - ]
357 [ + - + - : 2 : BOOST_CHECK( partial_block.IsTxAvailable(2));
+ - ]
358 : :
359 : : // Add an unrelated tx to extra_txn:
360 [ - + ]: 1 : extra_txn[0] = {non_block_tx->GetWitnessHash(), non_block_tx};
361 : : // and a tx from the block that's not in the mempool:
362 [ - + ]: 1 : extra_txn[1] = {block.vtx[1]->GetWitnessHash(), block.vtx[1]};
363 : :
364 [ + - + - : 2 : BOOST_CHECK(partial_block_with_extra.InitData(cmpctblock, extra_txn) == READ_STATUS_OK);
+ - + - ]
365 [ + - + - : 2 : BOOST_CHECK(partial_block_with_extra.IsTxAvailable(0));
+ - + - ]
366 : : // This transaction is now available via extra_txn:
367 [ + - + - : 2 : BOOST_CHECK(partial_block_with_extra.IsTxAvailable(1));
+ - + - ]
368 [ + - + - : 2 : BOOST_CHECK(partial_block_with_extra.IsTxAvailable(2));
+ - ]
369 : :
370 : : // Simulate a mempool collision after finding an unrelated extra transaction.
371 [ - + ]: 1 : extra_txn[2] = {block.vtx[2]->GetWitnessHash(), non_block_tx};
372 : 1 : TestPartiallyDownloadedBlock partial_block_with_extra_collision{&pool};
373 [ + - + - : 1 : BOOST_CHECK_EQUAL(partial_block_with_extra_collision.InitData(cmpctblock, extra_txn), READ_STATUS_OK);
+ - ]
374 [ + - + - : 2 : BOOST_CHECK(partial_block_with_extra_collision.IsTxAvailable(1));
+ - + - ]
375 [ + - + - : 2 : BOOST_CHECK(!partial_block_with_extra_collision.IsTxAvailable(2));
+ - + - ]
376 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(partial_block_with_extra_collision.GetMempoolCount(), 1U);
377 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(partial_block_with_extra_collision.GetExtraCount(), 1U);
378 : :
379 : : // Now also collide the extra-sourced slot: both counters decrement exactly once.
380 [ - + ]: 1 : extra_txn[3] = {block.vtx[1]->GetWitnessHash(), non_block_tx};
381 : 1 : TestPartiallyDownloadedBlock partial_block_with_extra_source_collision{&pool};
382 [ + - + - : 1 : BOOST_CHECK_EQUAL(partial_block_with_extra_source_collision.InitData(cmpctblock, extra_txn), READ_STATUS_OK);
+ - ]
383 [ + - + - : 2 : BOOST_CHECK(!partial_block_with_extra_source_collision.IsTxAvailable(1));
+ - + - ]
384 [ + - + - : 2 : BOOST_CHECK(!partial_block_with_extra_source_collision.IsTxAvailable(2));
+ - + - ]
385 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(partial_block_with_extra_source_collision.GetMempoolCount(), 0U);
386 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(partial_block_with_extra_source_collision.GetExtraCount(), 0U);
387 : :
388 : : // Collided slots are terminal: not even the genuine transactions refill them.
389 [ - + ]: 1 : extra_txn[4] = {block.vtx[2]->GetWitnessHash(), block.vtx[2]};
390 [ - + ]: 1 : extra_txn[5] = {block.vtx[1]->GetWitnessHash(), block.vtx[1]};
391 : 1 : TestPartiallyDownloadedBlock partial_block_no_refill{&pool};
392 [ + - + - : 1 : BOOST_CHECK_EQUAL(partial_block_no_refill.InitData(cmpctblock, extra_txn), READ_STATUS_OK);
+ - ]
393 [ + - + - : 2 : BOOST_CHECK(!partial_block_no_refill.IsTxAvailable(1));
+ - + - ]
394 [ + - + - : 2 : BOOST_CHECK(!partial_block_no_refill.IsTxAvailable(2));
+ - + - ]
395 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(partial_block_no_refill.GetMempoolCount(), 0U);
396 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(partial_block_no_refill.GetExtraCount(), 0U);
397 [ + - ]: 6 : }
398 [ + - + - ]: 4 : }
399 : :
400 [ + - + - : 7 : BOOST_AUTO_TEST_CASE(TransactionsRequestSerializationTest) {
+ - + - -
+ + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- ]
401 : 1 : BlockTransactionsRequest req1;
402 : 1 : req1.blockhash = m_rng.rand256();
403 [ + - ]: 1 : req1.indexes.resize(4);
404 [ + - ]: 1 : req1.indexes[0] = 0;
405 : 1 : req1.indexes[1] = 1;
406 : 1 : req1.indexes[2] = 3;
407 : 1 : req1.indexes[3] = 4;
408 : :
409 : 1 : DataStream stream{};
410 [ + - ]: 1 : stream << req1;
411 : :
412 : 1 : BlockTransactionsRequest req2;
413 [ + - ]: 1 : stream >> req2;
414 : :
415 [ + - + - : 1 : BOOST_CHECK_EQUAL(req1.blockhash.ToString(), req2.blockhash.ToString());
+ - + - ]
416 [ + - - + : 1 : BOOST_CHECK_EQUAL(req1.indexes.size(), req2.indexes.size());
- + + - ]
417 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(req1.indexes[0], req2.indexes[0]);
418 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(req1.indexes[1], req2.indexes[1]);
419 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(req1.indexes[2], req2.indexes[2]);
420 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(req1.indexes[3], req2.indexes[3]);
421 : 1 : }
422 : :
423 [ + - + - : 7 : BOOST_AUTO_TEST_CASE(TransactionsRequestDeserializationMaxTest) {
+ - + - -
+ + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- ]
424 : : // Check that the highest legal index is decoded correctly
425 : 1 : BlockTransactionsRequest req0;
426 : 1 : req0.blockhash = m_rng.rand256();
427 [ + - ]: 1 : req0.indexes.resize(1);
428 [ + - ]: 1 : req0.indexes[0] = 0xffff;
429 : 1 : DataStream stream{};
430 [ + - ]: 1 : stream << req0;
431 : :
432 : 1 : BlockTransactionsRequest req1;
433 [ + - ]: 1 : stream >> req1;
434 [ + - - + : 1 : BOOST_CHECK_EQUAL(req0.indexes.size(), req1.indexes.size());
- + + - ]
435 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(req0.indexes[0], req1.indexes[0]);
436 : 1 : }
437 : :
438 [ + - + - : 7 : BOOST_AUTO_TEST_CASE(TransactionsRequestDeserializationOverflowTest) {
+ - + - -
+ + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- ]
439 : : // Any set of index deltas that starts with N values that sum to (0x10000 - N)
440 : : // causes the edge-case overflow that was originally not checked for. Such
441 : : // a request cannot be created by serializing a real BlockTransactionsRequest
442 : : // due to the overflow, so here we'll serialize from raw deltas.
443 : 1 : BlockTransactionsRequest req0;
444 : 1 : req0.blockhash = m_rng.rand256();
445 [ + - ]: 1 : req0.indexes.resize(3);
446 [ + - ]: 1 : req0.indexes[0] = 0x7000;
447 : 1 : req0.indexes[1] = 0x10000 - 0x7000 - 2;
448 : 1 : req0.indexes[2] = 0;
449 : 1 : DataStream stream{};
450 [ + - ]: 1 : stream << req0.blockhash;
451 [ - + + - ]: 1 : WriteCompactSize(stream, req0.indexes.size());
452 [ + - ]: 1 : WriteCompactSize(stream, req0.indexes[0]);
453 [ + - ]: 1 : WriteCompactSize(stream, req0.indexes[1]);
454 [ + - ]: 1 : WriteCompactSize(stream, req0.indexes[2]);
455 : :
456 : 1 : BlockTransactionsRequest req1;
457 : 1 : try {
458 [ - + ]: 1 : stream >> req1;
459 : : // before patch: deserialize above succeeds and this check fails, demonstrating the overflow
460 [ # # # # : 0 : BOOST_CHECK(req1.indexes[1] < req1.indexes[2]);
# # ]
461 : : // this shouldn't be reachable before or after patch
462 [ - - - - ]: 1 : BOOST_CHECK(0);
463 [ - + ]: 1 : } catch(std::ios_base::failure &) {
464 : : // deserialize should fail
465 [ + - + - ]: 2 : BOOST_CHECK(true); // Needed to suppress "Test case [...] did not check any assertions"
466 : 1 : }
467 : 1 : }
468 : :
469 : : BOOST_AUTO_TEST_SUITE_END()
|