1 // Copyright (c) 2011-2015 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 "consensus/merkle.h"
7 #include "chainparams.h"
8 #include "random.h"
9
10 #include "test/test_bitcoin.h"
11
12 #include <boost/test/unit_test.hpp>
13
14 struct RegtestingSetup : public TestingSetup {
RegtestingSetupRegtestingSetup15 RegtestingSetup() : TestingSetup(CBaseChainParams::REGTEST) {}
16 };
17
BOOST_FIXTURE_TEST_SUITE(blockencodings_tests,RegtestingSetup)18 BOOST_FIXTURE_TEST_SUITE(blockencodings_tests, RegtestingSetup)
19
20 static CBlock BuildBlockTestCase() {
21 CBlock block;
22 CMutableTransaction tx;
23 tx.vin.resize(1);
24 tx.vin[0].scriptSig.resize(10);
25 tx.vout.resize(1);
26 tx.vout[0].nValue = 42;
27
28 block.vtx.resize(3);
29 block.vtx[0] = tx;
30 block.nVersion = 42;
31 block.hashPrevBlock = GetRandHash();
32 block.nBits = 0x207fffff;
33
34 tx.vin[0].prevout.hash = GetRandHash();
35 tx.vin[0].prevout.n = 0;
36 block.vtx[1] = tx;
37
38 tx.vin.resize(10);
39 for (size_t i = 0; i < tx.vin.size(); i++) {
40 tx.vin[i].prevout.hash = GetRandHash();
41 tx.vin[i].prevout.n = 0;
42 }
43 block.vtx[2] = tx;
44
45 bool mutated;
46 block.hashMerkleRoot = BlockMerkleRoot(block, &mutated);
47 assert(!mutated);
48 while (!CheckProofOfWork(block.GetHash(), block.nBits, Params().GetConsensus())) ++block.nNonce;
49 return block;
50 }
51
52 // Number of shared use_counts we expect for a tx we havent touched
53 // == 2 (mempool + our copy from the GetSharedTx call)
54 #define SHARED_TX_OFFSET 2
55
BOOST_AUTO_TEST_CASE(SimpleRoundTripTest)56 BOOST_AUTO_TEST_CASE(SimpleRoundTripTest)
57 {
58 CTxMemPool pool(CFeeRate(0));
59 TestMemPoolEntryHelper entry;
60 CBlock block(BuildBlockTestCase());
61
62 pool.addUnchecked(block.vtx[2].GetHash(), entry.FromTx(block.vtx[2]));
63 BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[2].GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 0);
64
65 // Do a simple ShortTxIDs RT
66 {
67 CBlockHeaderAndShortTxIDs shortIDs(block, true);
68
69 CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
70 stream << shortIDs;
71
72 CBlockHeaderAndShortTxIDs shortIDs2;
73 stream >> shortIDs2;
74
75 PartiallyDownloadedBlock partialBlock(&pool);
76 BOOST_CHECK(partialBlock.InitData(shortIDs2) == READ_STATUS_OK);
77 BOOST_CHECK( partialBlock.IsTxAvailable(0));
78 BOOST_CHECK(!partialBlock.IsTxAvailable(1));
79 BOOST_CHECK( partialBlock.IsTxAvailable(2));
80
81 BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[2].GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 1);
82
83 std::list<CTransaction> removed;
84 pool.removeRecursive(block.vtx[2], removed);
85 BOOST_CHECK_EQUAL(removed.size(), 1);
86
87 CBlock block2;
88 std::vector<CTransaction> vtx_missing;
89 BOOST_CHECK(partialBlock.FillBlock(block2, vtx_missing) == READ_STATUS_INVALID); // No transactions
90
91 vtx_missing.push_back(block.vtx[2]); // Wrong transaction
92 partialBlock.FillBlock(block2, vtx_missing); // Current implementation doesn't check txn here, but don't require that
93 bool mutated;
94 BOOST_CHECK(block.hashMerkleRoot != BlockMerkleRoot(block2, &mutated));
95
96 vtx_missing[0] = block.vtx[1];
97 CBlock block3;
98 BOOST_CHECK(partialBlock.FillBlock(block3, vtx_missing) == READ_STATUS_OK);
99 BOOST_CHECK_EQUAL(block.GetHash().ToString(), block3.GetHash().ToString());
100 BOOST_CHECK_EQUAL(block.hashMerkleRoot.ToString(), BlockMerkleRoot(block3, &mutated).ToString());
101 BOOST_CHECK(!mutated);
102 }
103 }
104
105 class TestHeaderAndShortIDs {
106 // Utility to encode custom CBlockHeaderAndShortTxIDs
107 public:
108 CBlockHeader header;
109 uint64_t nonce;
110 std::vector<uint64_t> shorttxids;
111 std::vector<PrefilledTransaction> prefilledtxn;
112
TestHeaderAndShortIDs(const CBlockHeaderAndShortTxIDs & orig)113 TestHeaderAndShortIDs(const CBlockHeaderAndShortTxIDs& orig) {
114 CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
115 stream << orig;
116 stream >> *this;
117 }
TestHeaderAndShortIDs(const CBlock & block)118 TestHeaderAndShortIDs(const CBlock& block) :
119 TestHeaderAndShortIDs(CBlockHeaderAndShortTxIDs(block, true)) {}
120
GetShortID(const uint256 & txhash) const121 uint64_t GetShortID(const uint256& txhash) const {
122 CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
123 stream << *this;
124 CBlockHeaderAndShortTxIDs base;
125 stream >> base;
126 return base.GetShortID(txhash);
127 }
128
129 ADD_SERIALIZE_METHODS;
130
131 template <typename Stream, typename Operation>
SerializationOp(Stream & s,Operation ser_action,int nType,int nVersion)132 inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
133 READWRITE(header);
134 READWRITE(nonce);
135 size_t shorttxids_size = shorttxids.size();
136 READWRITE(VARINT(shorttxids_size));
137 shorttxids.resize(shorttxids_size);
138 for (size_t i = 0; i < shorttxids.size(); i++) {
139 uint32_t lsb = shorttxids[i] & 0xffffffff;
140 uint16_t msb = (shorttxids[i] >> 32) & 0xffff;
141 READWRITE(lsb);
142 READWRITE(msb);
143 shorttxids[i] = (uint64_t(msb) << 32) | uint64_t(lsb);
144 }
145 READWRITE(prefilledtxn);
146 }
147 };
148
BOOST_AUTO_TEST_CASE(NonCoinbasePreforwardRTTest)149 BOOST_AUTO_TEST_CASE(NonCoinbasePreforwardRTTest)
150 {
151 CTxMemPool pool(CFeeRate(0));
152 TestMemPoolEntryHelper entry;
153 CBlock block(BuildBlockTestCase());
154
155 pool.addUnchecked(block.vtx[2].GetHash(), entry.FromTx(block.vtx[2]));
156 BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[2].GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 0);
157
158 // Test with pre-forwarding tx 1, but not coinbase
159 {
160 TestHeaderAndShortIDs shortIDs(block);
161 shortIDs.prefilledtxn.resize(1);
162 shortIDs.prefilledtxn[0] = {1, block.vtx[1]};
163 shortIDs.shorttxids.resize(2);
164 shortIDs.shorttxids[0] = shortIDs.GetShortID(block.vtx[0].GetHash());
165 shortIDs.shorttxids[1] = shortIDs.GetShortID(block.vtx[2].GetHash());
166
167 CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
168 stream << shortIDs;
169
170 CBlockHeaderAndShortTxIDs shortIDs2;
171 stream >> shortIDs2;
172
173 PartiallyDownloadedBlock partialBlock(&pool);
174 BOOST_CHECK(partialBlock.InitData(shortIDs2) == READ_STATUS_OK);
175 BOOST_CHECK(!partialBlock.IsTxAvailable(0));
176 BOOST_CHECK( partialBlock.IsTxAvailable(1));
177 BOOST_CHECK( partialBlock.IsTxAvailable(2));
178
179 BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[2].GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 1);
180
181 CBlock block2;
182 std::vector<CTransaction> vtx_missing;
183 BOOST_CHECK(partialBlock.FillBlock(block2, vtx_missing) == READ_STATUS_INVALID); // No transactions
184
185 vtx_missing.push_back(block.vtx[1]); // Wrong transaction
186 partialBlock.FillBlock(block2, vtx_missing); // Current implementation doesn't check txn here, but don't require that
187 bool mutated;
188 BOOST_CHECK(block.hashMerkleRoot != BlockMerkleRoot(block2, &mutated));
189
190 vtx_missing[0] = block.vtx[0];
191 CBlock block3;
192 BOOST_CHECK(partialBlock.FillBlock(block3, vtx_missing) == READ_STATUS_OK);
193 BOOST_CHECK_EQUAL(block.GetHash().ToString(), block3.GetHash().ToString());
194 BOOST_CHECK_EQUAL(block.hashMerkleRoot.ToString(), BlockMerkleRoot(block3, &mutated).ToString());
195 BOOST_CHECK(!mutated);
196
197 BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[2].GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 1);
198 }
199 BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[2].GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 0);
200 }
201
BOOST_AUTO_TEST_CASE(SufficientPreforwardRTTest)202 BOOST_AUTO_TEST_CASE(SufficientPreforwardRTTest)
203 {
204 CTxMemPool pool(CFeeRate(0));
205 TestMemPoolEntryHelper entry;
206 CBlock block(BuildBlockTestCase());
207
208 pool.addUnchecked(block.vtx[1].GetHash(), entry.FromTx(block.vtx[1]));
209 BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[1].GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 0);
210
211 // Test with pre-forwarding coinbase + tx 2 with tx 1 in mempool
212 {
213 TestHeaderAndShortIDs shortIDs(block);
214 shortIDs.prefilledtxn.resize(2);
215 shortIDs.prefilledtxn[0] = {0, block.vtx[0]};
216 shortIDs.prefilledtxn[1] = {1, block.vtx[2]}; // id == 1 as it is 1 after index 1
217 shortIDs.shorttxids.resize(1);
218 shortIDs.shorttxids[0] = shortIDs.GetShortID(block.vtx[1].GetHash());
219
220 CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
221 stream << shortIDs;
222
223 CBlockHeaderAndShortTxIDs shortIDs2;
224 stream >> shortIDs2;
225
226 PartiallyDownloadedBlock partialBlock(&pool);
227 BOOST_CHECK(partialBlock.InitData(shortIDs2) == READ_STATUS_OK);
228 BOOST_CHECK( partialBlock.IsTxAvailable(0));
229 BOOST_CHECK( partialBlock.IsTxAvailable(1));
230 BOOST_CHECK( partialBlock.IsTxAvailable(2));
231
232 BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[1].GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 1);
233
234 CBlock block2;
235 std::vector<CTransaction> vtx_missing;
236 BOOST_CHECK(partialBlock.FillBlock(block2, vtx_missing) == READ_STATUS_OK);
237 BOOST_CHECK_EQUAL(block.GetHash().ToString(), block2.GetHash().ToString());
238 bool mutated;
239 BOOST_CHECK_EQUAL(block.hashMerkleRoot.ToString(), BlockMerkleRoot(block2, &mutated).ToString());
240 BOOST_CHECK(!mutated);
241
242 BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[1].GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 1);
243 }
244 BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[1].GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 0);
245 }
246
BOOST_AUTO_TEST_CASE(EmptyBlockRoundTripTest)247 BOOST_AUTO_TEST_CASE(EmptyBlockRoundTripTest)
248 {
249 CTxMemPool pool(CFeeRate(0));
250 CMutableTransaction coinbase;
251 coinbase.vin.resize(1);
252 coinbase.vin[0].scriptSig.resize(10);
253 coinbase.vout.resize(1);
254 coinbase.vout[0].nValue = 42;
255
256 CBlock block;
257 block.vtx.resize(1);
258 block.vtx[0] = coinbase;
259 block.nVersion = 42;
260 block.hashPrevBlock = GetRandHash();
261 block.nBits = 0x207fffff;
262
263 bool mutated;
264 block.hashMerkleRoot = BlockMerkleRoot(block, &mutated);
265 assert(!mutated);
266 while (!CheckProofOfWork(block.GetHash(), block.nBits, Params().GetConsensus())) ++block.nNonce;
267
268 // Test simple header round-trip with only coinbase
269 {
270 CBlockHeaderAndShortTxIDs shortIDs(block, false);
271
272 CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
273 stream << shortIDs;
274
275 CBlockHeaderAndShortTxIDs shortIDs2;
276 stream >> shortIDs2;
277
278 PartiallyDownloadedBlock partialBlock(&pool);
279 BOOST_CHECK(partialBlock.InitData(shortIDs2) == READ_STATUS_OK);
280 BOOST_CHECK(partialBlock.IsTxAvailable(0));
281
282 CBlock block2;
283 std::vector<CTransaction> vtx_missing;
284 BOOST_CHECK(partialBlock.FillBlock(block2, vtx_missing) == READ_STATUS_OK);
285 BOOST_CHECK_EQUAL(block.GetHash().ToString(), block2.GetHash().ToString());
286 bool mutated;
287 BOOST_CHECK_EQUAL(block.hashMerkleRoot.ToString(), BlockMerkleRoot(block2, &mutated).ToString());
288 BOOST_CHECK(!mutated);
289 }
290 }
291
BOOST_AUTO_TEST_CASE(TransactionsRequestSerializationTest)292 BOOST_AUTO_TEST_CASE(TransactionsRequestSerializationTest) {
293 BlockTransactionsRequest req1;
294 req1.blockhash = GetRandHash();
295 req1.indexes.resize(4);
296 req1.indexes[0] = 0;
297 req1.indexes[1] = 1;
298 req1.indexes[2] = 3;
299 req1.indexes[3] = 4;
300
301 CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
302 stream << req1;
303
304 BlockTransactionsRequest req2;
305 stream >> req2;
306
307 BOOST_CHECK_EQUAL(req1.blockhash.ToString(), req2.blockhash.ToString());
308 BOOST_CHECK_EQUAL(req1.indexes.size(), req2.indexes.size());
309 BOOST_CHECK_EQUAL(req1.indexes[0], req2.indexes[0]);
310 BOOST_CHECK_EQUAL(req1.indexes[1], req2.indexes[1]);
311 BOOST_CHECK_EQUAL(req1.indexes[2], req2.indexes[2]);
312 BOOST_CHECK_EQUAL(req1.indexes[3], req2.indexes[3]);
313 }
314
315 BOOST_AUTO_TEST_SUITE_END()
316