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