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