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 : 6 : static CMutableTransaction BuildTransactionTestCase() {
23 : 6 : CMutableTransaction tx;
24 [ + - ]: 6 : tx.vin.resize(1);
25 : 6 : tx.vin[0].scriptSig.resize(10);
26 [ + - ]: 6 : tx.vout.resize(1);
27 : 6 : tx.vout[0].nValue = 42;
28 : 6 : 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 : 0 : }
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 [ + - + - : 7 : BOOST_AUTO_TEST_CASE(NonCoinbasePreforwardRTTest)
+ - + - -
+ + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- ]
148 : : {
149 [ - + ]: 1 : CTxMemPool& pool = *Assert(m_node.mempool);
150 : 1 : TestMemPoolEntryHelper entry;
151 : 1 : auto rand_ctx(FastRandomContext(uint256{42}));
152 [ + - ]: 1 : CBlock block(BuildBlockTestCase(rand_ctx));
153 : :
154 [ + - + - ]: 1 : LOCK2(cs_main, pool.cs);
155 [ + - + - ]: 1 : TryAddToMempool(pool, entry.FromTx(block.vtx[2]));
156 [ + - + - : 2 : BOOST_CHECK_EQUAL(pool.get(block.vtx[2]->GetHash()).use_count(), SHARED_TX_OFFSET + 0);
+ - + - ]
157 : :
158 [ + - ]: 1 : Txid txhash;
159 : :
160 : : // Test with pre-forwarding tx 1, but not coinbase
161 : 1 : {
162 [ + - ]: 1 : TestHeaderAndShortIDs shortIDs(block, rand_ctx);
163 [ + - ]: 1 : shortIDs.prefilledtxn.resize(1);
164 [ - + ]: 1 : shortIDs.prefilledtxn[0] = {1, block.vtx[1]};
165 [ + - ]: 1 : shortIDs.shorttxids.resize(2);
166 [ + - + - ]: 1 : shortIDs.shorttxids[0] = shortIDs.GetShortID(block.vtx[0]->GetWitnessHash());
167 [ + - + - ]: 1 : shortIDs.shorttxids[1] = shortIDs.GetShortID(block.vtx[2]->GetWitnessHash());
168 : :
169 : 1 : DataStream stream{};
170 [ + - ]: 1 : stream << shortIDs;
171 : :
172 : 1 : CBlockHeaderAndShortTxIDs shortIDs2;
173 [ + - ]: 1 : stream >> shortIDs2;
174 : :
175 : 1 : PartiallyDownloadedBlock partialBlock(&pool);
176 [ + - + - : 2 : BOOST_CHECK(partialBlock.InitData(shortIDs2, empty_extra_txn) == READ_STATUS_OK);
+ - + - ]
177 [ + - + - : 2 : BOOST_CHECK(!partialBlock.IsTxAvailable(0));
+ - + - ]
178 [ + - + - : 2 : BOOST_CHECK( partialBlock.IsTxAvailable(1));
+ - + - ]
179 [ + - + - : 2 : BOOST_CHECK( partialBlock.IsTxAvailable(2));
+ - + - ]
180 : :
181 [ + - + - : 2 : BOOST_CHECK_EQUAL(pool.get(block.vtx[2]->GetHash()).use_count(), SHARED_TX_OFFSET + 1); // +1 because of partialBlock
+ - + - ]
182 : :
183 : 1 : CBlock block2;
184 : 1 : {
185 [ + - ]: 1 : PartiallyDownloadedBlock tmp = partialBlock;
186 [ + - + - : 2 : BOOST_CHECK(partialBlock.FillBlock(block2, {}, /*segwit_active=*/true) == READ_STATUS_INVALID); // No transactions
+ - + - ]
187 [ + - ]: 1 : partialBlock = tmp;
188 : 0 : }
189 : :
190 : : // Wrong transaction
191 : 1 : {
192 [ + - ]: 1 : PartiallyDownloadedBlock tmp = partialBlock;
193 [ + - + + : 2 : partialBlock.FillBlock(block2, {block.vtx[1]}, /*segwit_active=*/true); // Current implementation doesn't check txn here, but don't require that
+ - - - -
- ]
194 [ + - ]: 1 : partialBlock = tmp;
195 : 0 : }
196 [ + - + - : 2 : BOOST_CHECK_EQUAL(pool.get(block.vtx[2]->GetHash()).use_count(), SHARED_TX_OFFSET + 2); // +2 because of partialBlock and block2
+ - + - ]
197 : 1 : bool mutated;
198 [ + - + - : 2 : BOOST_CHECK(block.hashMerkleRoot != BlockMerkleRoot(block2, &mutated));
+ - ]
199 : :
200 : 1 : CBlock block3;
201 [ + - ]: 1 : PartiallyDownloadedBlock partialBlockCopy = partialBlock;
202 [ + - + - : 4 : BOOST_CHECK(partialBlock.FillBlock(block3, {block.vtx[0]}, /*segwit_active=*/true) == READ_STATUS_OK);
+ - + - +
+ + - - -
- - ]
203 [ + - + - : 1 : BOOST_CHECK_EQUAL(block.GetHash().ToString(), block3.GetHash().ToString());
+ - + - +
- + - ]
204 [ + - + - : 1 : BOOST_CHECK_EQUAL(block.hashMerkleRoot.ToString(), BlockMerkleRoot(block3, &mutated).ToString());
+ - + - +
- ]
205 [ + - + - : 2 : BOOST_CHECK(!mutated);
+ - ]
206 : :
207 [ + - + - : 2 : BOOST_CHECK_EQUAL(pool.get(block.vtx[2]->GetHash()).use_count(), SHARED_TX_OFFSET + 3); // +2 because of partialBlock and block2 and block3
+ - + - ]
208 : :
209 : 1 : txhash = block.vtx[2]->GetHash();
210 : 1 : block.vtx.clear();
211 : 1 : block2.vtx.clear();
212 : 1 : block3.vtx.clear();
213 [ + - + - : 3 : BOOST_CHECK_EQUAL(pool.get(txhash).use_count(), SHARED_TX_OFFSET + 1 - 1); // + 1 because of partialBlock; -1 because of block.
+ - + - ]
214 : 3 : }
215 [ + - + - : 3 : BOOST_CHECK_EQUAL(pool.get(txhash).use_count(), SHARED_TX_OFFSET - 1); // -1 because of block
+ - + - +
- ]
216 [ + - + - : 8 : }
+ - + - +
- + - ]
217 : :
218 [ + - + - : 7 : BOOST_AUTO_TEST_CASE(SufficientPreforwardRTTest)
+ - + - -
+ + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- ]
219 : : {
220 [ - + ]: 1 : CTxMemPool& pool = *Assert(m_node.mempool);
221 : 1 : TestMemPoolEntryHelper entry;
222 : 1 : auto rand_ctx(FastRandomContext(uint256{42}));
223 [ + - ]: 1 : CBlock block(BuildBlockTestCase(rand_ctx));
224 : :
225 [ + - + - ]: 1 : LOCK2(cs_main, pool.cs);
226 [ + - + - ]: 1 : TryAddToMempool(pool, entry.FromTx(block.vtx[1]));
227 [ + - + - : 2 : BOOST_CHECK_EQUAL(pool.get(block.vtx[1]->GetHash()).use_count(), SHARED_TX_OFFSET + 0);
+ - + - ]
228 : :
229 [ + - ]: 1 : Txid txhash;
230 : :
231 : : // Test with pre-forwarding coinbase + tx 2 with tx 1 in mempool
232 : 1 : {
233 [ + - ]: 1 : TestHeaderAndShortIDs shortIDs(block, rand_ctx);
234 [ + - ]: 1 : shortIDs.prefilledtxn.resize(2);
235 [ - + ]: 1 : shortIDs.prefilledtxn[0] = {0, block.vtx[0]};
236 [ - + ]: 1 : shortIDs.prefilledtxn[1] = {1, block.vtx[2]}; // id == 1 as it is 1 after index 1
237 [ + - ]: 1 : shortIDs.shorttxids.resize(1);
238 [ + - + - ]: 1 : shortIDs.shorttxids[0] = shortIDs.GetShortID(block.vtx[1]->GetWitnessHash());
239 : :
240 : 1 : DataStream stream{};
241 [ + - ]: 1 : stream << shortIDs;
242 : :
243 : 1 : CBlockHeaderAndShortTxIDs shortIDs2;
244 [ + - ]: 1 : stream >> shortIDs2;
245 : :
246 : 1 : PartiallyDownloadedBlock partialBlock(&pool);
247 [ + - + - : 2 : BOOST_CHECK(partialBlock.InitData(shortIDs2, empty_extra_txn) == READ_STATUS_OK);
+ - + - ]
248 [ + - + - : 2 : BOOST_CHECK( partialBlock.IsTxAvailable(0));
+ - + - ]
249 [ + - + - : 2 : BOOST_CHECK( partialBlock.IsTxAvailable(1));
+ - + - ]
250 [ + - + - : 2 : BOOST_CHECK( partialBlock.IsTxAvailable(2));
+ - + - ]
251 : :
252 [ + - + - : 2 : BOOST_CHECK_EQUAL(pool.get(block.vtx[1]->GetHash()).use_count(), SHARED_TX_OFFSET + 1);
+ - + - ]
253 : :
254 : 1 : CBlock block2;
255 [ + - ]: 1 : PartiallyDownloadedBlock partialBlockCopy = partialBlock;
256 [ + - + - : 2 : BOOST_CHECK(partialBlock.FillBlock(block2, {}, /*segwit_active=*/true) == READ_STATUS_OK);
+ - + - ]
257 [ + - + - : 1 : BOOST_CHECK_EQUAL(block.GetHash().ToString(), block2.GetHash().ToString());
+ - + - +
- + - ]
258 : 1 : bool mutated;
259 [ + - + - : 1 : BOOST_CHECK_EQUAL(block.hashMerkleRoot.ToString(), BlockMerkleRoot(block2, &mutated).ToString());
+ - + - +
- ]
260 [ + - + - ]: 2 : BOOST_CHECK(!mutated);
261 : :
262 : 1 : txhash = block.vtx[1]->GetHash();
263 : 1 : block.vtx.clear();
264 : 1 : block2.vtx.clear();
265 [ + - + - : 3 : BOOST_CHECK_EQUAL(pool.get(txhash).use_count(), SHARED_TX_OFFSET + 1 - 1); // + 1 because of partialBlock; -1 because of block.
+ - + - ]
266 : 3 : }
267 [ + - + - : 3 : BOOST_CHECK_EQUAL(pool.get(txhash).use_count(), SHARED_TX_OFFSET - 1); // -1 because of block
+ - + - +
- ]
268 [ + - + - : 6 : }
+ - ]
269 : :
270 [ + - + - : 7 : BOOST_AUTO_TEST_CASE(EmptyBlockRoundTripTest)
+ - + - -
+ + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- ]
271 : : {
272 [ - + ]: 1 : CTxMemPool& pool = *Assert(m_node.mempool);
273 : 1 : CMutableTransaction coinbase = BuildTransactionTestCase();
274 : :
275 : 1 : CBlock block;
276 : 1 : auto rand_ctx(FastRandomContext(uint256{42}));
277 [ + - ]: 1 : block.vtx.resize(1);
278 [ + - - + ]: 2 : block.vtx[0] = MakeTransactionRef(std::move(coinbase));
279 : 1 : block.nVersion = 42;
280 : 1 : block.hashPrevBlock = rand_ctx.rand256();
281 : 1 : block.nBits = 0x207fffff;
282 : :
283 : 1 : bool mutated;
284 [ + - ]: 1 : block.hashMerkleRoot = BlockMerkleRoot(block, &mutated);
285 [ + - ]: 1 : assert(!mutated);
286 [ + - + - : 1 : while (!CheckProofOfWork(block.GetHash(), block.nBits, Params().GetConsensus())) ++block.nNonce;
+ - - + ]
287 : :
288 : : // Test simple header round-trip with only coinbase
289 : 1 : {
290 [ + - ]: 1 : CBlockHeaderAndShortTxIDs shortIDs{block, rand_ctx.rand64()};
291 : :
292 : 1 : DataStream stream{};
293 [ + - ]: 1 : stream << shortIDs;
294 : :
295 : 1 : CBlockHeaderAndShortTxIDs shortIDs2;
296 [ + - ]: 1 : stream >> shortIDs2;
297 : :
298 : 1 : PartiallyDownloadedBlock partialBlock(&pool);
299 [ + - + - : 2 : BOOST_CHECK(partialBlock.InitData(shortIDs2, empty_extra_txn) == READ_STATUS_OK);
+ - + - ]
300 [ + - + - : 2 : BOOST_CHECK(partialBlock.IsTxAvailable(0));
+ - ]
301 : :
302 : 1 : CBlock block2;
303 : 1 : std::vector<CTransactionRef> vtx_missing;
304 [ + - + - : 2 : BOOST_CHECK(partialBlock.FillBlock(block2, vtx_missing, /*segwit_active=*/true) == READ_STATUS_OK);
+ - + - ]
305 [ + - + - : 1 : BOOST_CHECK_EQUAL(block.GetHash().ToString(), block2.GetHash().ToString());
+ - + - +
- + - ]
306 [ + - + - : 1 : BOOST_CHECK_EQUAL(block.hashMerkleRoot.ToString(), BlockMerkleRoot(block2, &mutated).ToString());
+ - + - +
- ]
307 [ + - + - ]: 2 : BOOST_CHECK(!mutated);
308 : 4 : }
309 : 2 : }
310 : :
311 [ + - + - : 7 : BOOST_AUTO_TEST_CASE(ReceiveWithExtraTransactions) {
+ - + - -
+ + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- ]
312 [ - + ]: 1 : CTxMemPool& pool = *Assert(m_node.mempool);
313 : 1 : TestMemPoolEntryHelper entry;
314 : 1 : auto rand_ctx(FastRandomContext(uint256{42}));
315 : :
316 [ + - ]: 1 : CMutableTransaction mtx = BuildTransactionTestCase();
317 [ + - ]: 1 : mtx.vin[0].prevout.hash = Txid::FromUint256(rand_ctx.rand256());
318 : 1 : mtx.vin[0].prevout.n = 0;
319 [ + - ]: 1 : const CTransactionRef non_block_tx = MakeTransactionRef(std::move(mtx));
320 : :
321 [ + - ]: 1 : CBlock block(BuildBlockTestCase(rand_ctx));
322 : 1 : std::vector<std::pair<Wtxid, CTransactionRef>> extra_txn;
323 [ + - ]: 1 : extra_txn.resize(10);
324 : :
325 [ + - + - ]: 1 : LOCK2(cs_main, pool.cs);
326 [ + - + - ]: 1 : TryAddToMempool(pool, entry.FromTx(block.vtx[2]));
327 [ + - + - : 2 : BOOST_CHECK_EQUAL(pool.get(block.vtx[2]->GetHash()).use_count(), SHARED_TX_OFFSET + 0);
+ - + - ]
328 : : // Ensure the non_block_tx is actually not in the block
329 [ + + ]: 4 : for (const auto &block_tx : block.vtx) {
330 [ + - + - ]: 3 : BOOST_CHECK_NE(block_tx->GetHash(), non_block_tx->GetHash());
331 : : }
332 : : // Ensure block.vtx[1] is not in pool
333 [ + - + - : 1 : BOOST_CHECK_EQUAL(pool.get(block.vtx[1]->GetHash()), nullptr);
+ - - + ]
334 : :
335 : 1 : {
336 [ + - ]: 1 : const CBlockHeaderAndShortTxIDs cmpctblock{block, rand_ctx.rand64()};
337 : 1 : PartiallyDownloadedBlock partial_block(&pool);
338 : 1 : PartiallyDownloadedBlock partial_block_with_extra(&pool);
339 : :
340 [ + - + - : 2 : BOOST_CHECK(partial_block.InitData(cmpctblock, extra_txn) == READ_STATUS_OK);
+ - + - ]
341 [ + - + - : 2 : BOOST_CHECK( partial_block.IsTxAvailable(0));
+ - + - ]
342 [ + - + - : 2 : BOOST_CHECK(!partial_block.IsTxAvailable(1));
+ - + - ]
343 [ + - + - : 2 : BOOST_CHECK( partial_block.IsTxAvailable(2));
+ - ]
344 : :
345 : : // Add an unrelated tx to extra_txn:
346 [ - + ]: 1 : extra_txn[0] = {non_block_tx->GetWitnessHash(), non_block_tx};
347 : : // and a tx from the block that's not in the mempool:
348 [ - + ]: 1 : extra_txn[1] = {block.vtx[1]->GetWitnessHash(), block.vtx[1]};
349 : :
350 [ + - + - : 2 : BOOST_CHECK(partial_block_with_extra.InitData(cmpctblock, extra_txn) == READ_STATUS_OK);
+ - + - ]
351 [ + - + - : 2 : BOOST_CHECK(partial_block_with_extra.IsTxAvailable(0));
+ - + - ]
352 : : // This transaction is now available via extra_txn:
353 [ + - + - : 2 : BOOST_CHECK(partial_block_with_extra.IsTxAvailable(1));
+ - + - ]
354 [ + - + - : 2 : BOOST_CHECK(partial_block_with_extra.IsTxAvailable(2));
+ - ]
355 [ + - ]: 3 : }
356 [ + - + - ]: 4 : }
357 : :
358 [ + - + - : 7 : BOOST_AUTO_TEST_CASE(TransactionsRequestSerializationTest) {
+ - + - -
+ + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- ]
359 : 1 : BlockTransactionsRequest req1;
360 : 1 : req1.blockhash = m_rng.rand256();
361 [ + - ]: 1 : req1.indexes.resize(4);
362 [ + - ]: 1 : req1.indexes[0] = 0;
363 : 1 : req1.indexes[1] = 1;
364 : 1 : req1.indexes[2] = 3;
365 : 1 : req1.indexes[3] = 4;
366 : :
367 : 1 : DataStream stream{};
368 [ + - ]: 1 : stream << req1;
369 : :
370 : 1 : BlockTransactionsRequest req2;
371 [ + - ]: 1 : stream >> req2;
372 : :
373 [ + - + - : 1 : BOOST_CHECK_EQUAL(req1.blockhash.ToString(), req2.blockhash.ToString());
+ - + - ]
374 [ + - - + : 1 : BOOST_CHECK_EQUAL(req1.indexes.size(), req2.indexes.size());
- + + - ]
375 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(req1.indexes[0], req2.indexes[0]);
376 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(req1.indexes[1], req2.indexes[1]);
377 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(req1.indexes[2], req2.indexes[2]);
378 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(req1.indexes[3], req2.indexes[3]);
379 : 1 : }
380 : :
381 [ + - + - : 7 : BOOST_AUTO_TEST_CASE(TransactionsRequestDeserializationMaxTest) {
+ - + - -
+ + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- ]
382 : : // Check that the highest legal index is decoded correctly
383 : 1 : BlockTransactionsRequest req0;
384 : 1 : req0.blockhash = m_rng.rand256();
385 [ + - ]: 1 : req0.indexes.resize(1);
386 [ + - ]: 1 : req0.indexes[0] = 0xffff;
387 : 1 : DataStream stream{};
388 [ + - ]: 1 : stream << req0;
389 : :
390 : 1 : BlockTransactionsRequest req1;
391 [ + - ]: 1 : stream >> req1;
392 [ + - - + : 1 : BOOST_CHECK_EQUAL(req0.indexes.size(), req1.indexes.size());
- + + - ]
393 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(req0.indexes[0], req1.indexes[0]);
394 : 1 : }
395 : :
396 [ + - + - : 7 : BOOST_AUTO_TEST_CASE(TransactionsRequestDeserializationOverflowTest) {
+ - + - -
+ + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- ]
397 : : // Any set of index deltas that starts with N values that sum to (0x10000 - N)
398 : : // causes the edge-case overflow that was originally not checked for. Such
399 : : // a request cannot be created by serializing a real BlockTransactionsRequest
400 : : // due to the overflow, so here we'll serialize from raw deltas.
401 : 1 : BlockTransactionsRequest req0;
402 : 1 : req0.blockhash = m_rng.rand256();
403 [ + - ]: 1 : req0.indexes.resize(3);
404 [ + - ]: 1 : req0.indexes[0] = 0x7000;
405 : 1 : req0.indexes[1] = 0x10000 - 0x7000 - 2;
406 : 1 : req0.indexes[2] = 0;
407 : 1 : DataStream stream{};
408 [ + - ]: 1 : stream << req0.blockhash;
409 [ - + + - ]: 1 : WriteCompactSize(stream, req0.indexes.size());
410 [ + - ]: 1 : WriteCompactSize(stream, req0.indexes[0]);
411 [ + - ]: 1 : WriteCompactSize(stream, req0.indexes[1]);
412 [ + - ]: 1 : WriteCompactSize(stream, req0.indexes[2]);
413 : :
414 : 1 : BlockTransactionsRequest req1;
415 : 1 : try {
416 [ - + ]: 1 : stream >> req1;
417 : : // before patch: deserialize above succeeds and this check fails, demonstrating the overflow
418 [ # # # # : 0 : BOOST_CHECK(req1.indexes[1] < req1.indexes[2]);
# # ]
419 : : // this shouldn't be reachable before or after patch
420 [ - - - - ]: 1 : BOOST_CHECK(0);
421 [ - + ]: 1 : } catch(std::ios_base::failure &) {
422 : : // deserialize should fail
423 [ + - + - ]: 2 : BOOST_CHECK(true); // Needed to suppress "Test case [...] did not check any assertions"
424 : 1 : }
425 : 1 : }
426 : :
427 : : BOOST_AUTO_TEST_SUITE_END()
|