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