Bitcoin Core 22.99.0
P2P Digital Currency
blockencodings_tests.cpp
Go to the documentation of this file.
1// Copyright (c) 2011-2020 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
12
13#include <boost/test/unit_test.hpp>
14
15std::vector<std::pair<uint256, CTransactionRef>> extra_txn;
16
17BOOST_FIXTURE_TEST_SUITE(blockencodings_tests, RegTestingSetup)
18
20 CBlock block;
22 tx.vin.resize(1);
23 tx.vin[0].scriptSig.resize(10);
24 tx.vout.resize(1);
25 tx.vout[0].nValue = 42;
26
27 block.vtx.resize(3);
28 block.vtx[0] = MakeTransactionRef(tx);
29 block.nVersion = 42;
31 block.nBits = 0x207fffff;
32
33 tx.vin[0].prevout.hash = InsecureRand256();
34 tx.vin[0].prevout.n = 0;
35 block.vtx[1] = MakeTransactionRef(tx);
36
37 tx.vin.resize(10);
38 for (size_t i = 0; i < tx.vin.size(); i++) {
39 tx.vin[i].prevout.hash = InsecureRand256();
40 tx.vin[i].prevout.n = 0;
41 }
42 block.vtx[2] = MakeTransactionRef(tx);
43
44 bool mutated;
45 block.hashMerkleRoot = BlockMerkleRoot(block, &mutated);
47 while (!CheckProofOfWork(block.GetHash(), block.nBits, Params().GetConsensus())) ++block.nNonce;
48 return block;
49}
50
51// Number of shared use_counts we expect for a tx we haven't touched
52// (block + mempool + our copy from the GetSharedTx call)
53constexpr long SHARED_TX_OFFSET{3};
54
55BOOST_AUTO_TEST_CASE(SimpleRoundTripTest)
56{
57 CTxMemPool pool;
60
61 LOCK2(cs_main, pool.cs);
62 pool.addUnchecked(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
70 stream << shortIDs;
71
73 stream >> shortIDs2;
74
75 PartiallyDownloadedBlock partialBlock(&pool);
76 BOOST_CHECK(partialBlock.InitData(shortIDs2, extra_txn) == 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 size_t poolSize = pool.size();
85 BOOST_CHECK_EQUAL(pool.size(), poolSize - 1);
86
87 CBlock block2;
88 {
89 PartiallyDownloadedBlock tmp = partialBlock;
90 BOOST_CHECK(partialBlock.FillBlock(block2, {}) == READ_STATUS_INVALID); // No transactions
91 partialBlock = tmp;
92 }
93
94 // Wrong transaction
95 {
96 PartiallyDownloadedBlock tmp = partialBlock;
97 partialBlock.FillBlock(block2, {block.vtx[2]}); // Current implementation doesn't check txn here, but don't require that
98 partialBlock = tmp;
99 }
100 bool mutated;
102
103 CBlock block3;
104 BOOST_CHECK(partialBlock.FillBlock(block3, {block.vtx[1]}) == READ_STATUS_OK);
105 BOOST_CHECK_EQUAL(block.GetHash().ToString(), block3.GetHash().ToString());
108 }
109}
110
112 // Utility to encode custom CBlockHeaderAndShortTxIDs
113public:
115 uint64_t nonce;
116 std::vector<uint64_t> shorttxids;
117 std::vector<PrefilledTransaction> prefilledtxn;
118
121 stream << orig;
122 stream >> *this;
123 }
124 explicit TestHeaderAndShortIDs(const CBlock& block) :
126
127 uint64_t GetShortID(const uint256& txhash) const {
129 stream << *this;
131 stream >> base;
132 return base.GetShortID(txhash);
133 }
134
136};
137
138BOOST_AUTO_TEST_CASE(NonCoinbasePreforwardRTTest)
139{
140 CTxMemPool pool;
142 CBlock block(BuildBlockTestCase());
143
144 LOCK2(cs_main, pool.cs);
145 pool.addUnchecked(entry.FromTx(block.vtx[2]));
146 BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[2]->GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 0);
147
148 uint256 txhash;
149
150 // Test with pre-forwarding tx 1, but not coinbase
151 {
152 TestHeaderAndShortIDs shortIDs(block);
153 shortIDs.prefilledtxn.resize(1);
154 shortIDs.prefilledtxn[0] = {1, block.vtx[1]};
155 shortIDs.shorttxids.resize(2);
156 shortIDs.shorttxids[0] = shortIDs.GetShortID(block.vtx[0]->GetHash());
157 shortIDs.shorttxids[1] = shortIDs.GetShortID(block.vtx[2]->GetHash());
158
160 stream << shortIDs;
161
163 stream >> shortIDs2;
164
165 PartiallyDownloadedBlock partialBlock(&pool);
166 BOOST_CHECK(partialBlock.InitData(shortIDs2, extra_txn) == READ_STATUS_OK);
167 BOOST_CHECK(!partialBlock.IsTxAvailable(0));
168 BOOST_CHECK( partialBlock.IsTxAvailable(1));
169 BOOST_CHECK( partialBlock.IsTxAvailable(2));
170
171 BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[2]->GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 1); // +1 because of partialBlock
172
173 CBlock block2;
174 {
175 PartiallyDownloadedBlock tmp = partialBlock;
176 BOOST_CHECK(partialBlock.FillBlock(block2, {}) == READ_STATUS_INVALID); // No transactions
177 partialBlock = tmp;
178 }
179
180 // Wrong transaction
181 {
182 PartiallyDownloadedBlock tmp = partialBlock;
183 partialBlock.FillBlock(block2, {block.vtx[1]}); // Current implementation doesn't check txn here, but don't require that
184 partialBlock = tmp;
185 }
186 BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[2]->GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 2); // +2 because of partialBlock and block2
187 bool mutated;
189
190 CBlock block3;
191 PartiallyDownloadedBlock partialBlockCopy = partialBlock;
192 BOOST_CHECK(partialBlock.FillBlock(block3, {block.vtx[0]}) == READ_STATUS_OK);
193 BOOST_CHECK_EQUAL(block.GetHash().ToString(), block3.GetHash().ToString());
196
197 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
198
199 txhash = block.vtx[2]->GetHash();
200 block.vtx.clear();
201 block2.vtx.clear();
202 block3.vtx.clear();
203 BOOST_CHECK_EQUAL(pool.mapTx.find(txhash)->GetSharedTx().use_count(), SHARED_TX_OFFSET + 1 - 1); // + 1 because of partialBlock; -1 because of block.
204 }
205 BOOST_CHECK_EQUAL(pool.mapTx.find(txhash)->GetSharedTx().use_count(), SHARED_TX_OFFSET - 1); // -1 because of block
206}
207
208BOOST_AUTO_TEST_CASE(SufficientPreforwardRTTest)
209{
210 CTxMemPool pool;
212 CBlock block(BuildBlockTestCase());
213
214 LOCK2(cs_main, pool.cs);
215 pool.addUnchecked(entry.FromTx(block.vtx[1]));
216 BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[1]->GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 0);
217
218 uint256 txhash;
219
220 // Test with pre-forwarding coinbase + tx 2 with tx 1 in mempool
221 {
222 TestHeaderAndShortIDs shortIDs(block);
223 shortIDs.prefilledtxn.resize(2);
224 shortIDs.prefilledtxn[0] = {0, block.vtx[0]};
225 shortIDs.prefilledtxn[1] = {1, block.vtx[2]}; // id == 1 as it is 1 after index 1
226 shortIDs.shorttxids.resize(1);
227 shortIDs.shorttxids[0] = shortIDs.GetShortID(block.vtx[1]->GetHash());
228
230 stream << shortIDs;
231
233 stream >> shortIDs2;
234
235 PartiallyDownloadedBlock partialBlock(&pool);
236 BOOST_CHECK(partialBlock.InitData(shortIDs2, extra_txn) == READ_STATUS_OK);
237 BOOST_CHECK( partialBlock.IsTxAvailable(0));
238 BOOST_CHECK( partialBlock.IsTxAvailable(1));
239 BOOST_CHECK( partialBlock.IsTxAvailable(2));
240
241 BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[1]->GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 1);
242
243 CBlock block2;
244 PartiallyDownloadedBlock partialBlockCopy = partialBlock;
245 BOOST_CHECK(partialBlock.FillBlock(block2, {}) == READ_STATUS_OK);
246 BOOST_CHECK_EQUAL(block.GetHash().ToString(), block2.GetHash().ToString());
247 bool mutated;
250
251 txhash = block.vtx[1]->GetHash();
252 block.vtx.clear();
253 block2.vtx.clear();
254 BOOST_CHECK_EQUAL(pool.mapTx.find(txhash)->GetSharedTx().use_count(), SHARED_TX_OFFSET + 1 - 1); // + 1 because of partialBlock; -1 because of block.
255 }
256 BOOST_CHECK_EQUAL(pool.mapTx.find(txhash)->GetSharedTx().use_count(), SHARED_TX_OFFSET - 1); // -1 because of block
257}
258
259BOOST_AUTO_TEST_CASE(EmptyBlockRoundTripTest)
260{
261 CTxMemPool pool;
262 CMutableTransaction coinbase;
263 coinbase.vin.resize(1);
264 coinbase.vin[0].scriptSig.resize(10);
265 coinbase.vout.resize(1);
266 coinbase.vout[0].nValue = 42;
267
268 CBlock block;
269 block.vtx.resize(1);
270 block.vtx[0] = MakeTransactionRef(std::move(coinbase));
271 block.nVersion = 42;
273 block.nBits = 0x207fffff;
274
275 bool mutated;
276 block.hashMerkleRoot = BlockMerkleRoot(block, &mutated);
277 assert(!mutated);
278 while (!CheckProofOfWork(block.GetHash(), block.nBits, Params().GetConsensus())) ++block.nNonce;
279
280 // Test simple header round-trip with only coinbase
281 {
282 CBlockHeaderAndShortTxIDs shortIDs(block, false);
283
285 stream << shortIDs;
286
288 stream >> shortIDs2;
289
290 PartiallyDownloadedBlock partialBlock(&pool);
291 BOOST_CHECK(partialBlock.InitData(shortIDs2, extra_txn) == READ_STATUS_OK);
292 BOOST_CHECK(partialBlock.IsTxAvailable(0));
293
294 CBlock block2;
295 std::vector<CTransactionRef> vtx_missing;
296 BOOST_CHECK(partialBlock.FillBlock(block2, vtx_missing) == READ_STATUS_OK);
297 BOOST_CHECK_EQUAL(block.GetHash().ToString(), block2.GetHash().ToString());
300 }
301}
302
303BOOST_AUTO_TEST_CASE(TransactionsRequestSerializationTest) {
305 req1.blockhash = InsecureRand256();
306 req1.indexes.resize(4);
307 req1.indexes[0] = 0;
308 req1.indexes[1] = 1;
309 req1.indexes[2] = 3;
310 req1.indexes[3] = 4;
311
313 stream << req1;
314
316 stream >> req2;
317
319 BOOST_CHECK_EQUAL(req1.indexes.size(), req2.indexes.size());
320 BOOST_CHECK_EQUAL(req1.indexes[0], req2.indexes[0]);
321 BOOST_CHECK_EQUAL(req1.indexes[1], req2.indexes[1]);
322 BOOST_CHECK_EQUAL(req1.indexes[2], req2.indexes[2]);
323 BOOST_CHECK_EQUAL(req1.indexes[3], req2.indexes[3]);
324}
325
326BOOST_AUTO_TEST_CASE(TransactionsRequestDeserializationMaxTest) {
327 // Check that the highest legal index is decoded correctly
329 req0.blockhash = InsecureRand256();
330 req0.indexes.resize(1);
331 req0.indexes[0] = 0xffff;
333 stream << req0;
334
336 stream >> req1;
337 BOOST_CHECK_EQUAL(req0.indexes.size(), req1.indexes.size());
338 BOOST_CHECK_EQUAL(req0.indexes[0], req1.indexes[0]);
339}
340
341BOOST_AUTO_TEST_CASE(TransactionsRequestDeserializationOverflowTest) {
342 // Any set of index deltas that starts with N values that sum to (0x10000 - N)
343 // causes the edge-case overflow that was originally not checked for. Such
344 // a request cannot be created by serializing a real BlockTransactionsRequest
345 // due to the overflow, so here we'll serialize from raw deltas.
347 req0.blockhash = InsecureRand256();
348 req0.indexes.resize(3);
349 req0.indexes[0] = 0x7000;
350 req0.indexes[1] = 0x10000 - 0x7000 - 2;
351 req0.indexes[2] = 0;
353 stream << req0.blockhash;
354 WriteCompactSize(stream, req0.indexes.size());
355 WriteCompactSize(stream, req0.indexes[0]);
356 WriteCompactSize(stream, req0.indexes[1]);
357 WriteCompactSize(stream, req0.indexes[2]);
358
360 try {
361 stream >> req1;
362 // before patch: deserialize above succeeds and this check fails, demonstrating the overflow
363 BOOST_CHECK(req1.indexes[1] < req1.indexes[2]);
364 // this shouldn't be reachable before or after patch
365 BOOST_CHECK(0);
366 } catch(std::ios_base::failure &) {
367 // deserialize should fail
368 BOOST_CHECK(true); // Needed to suppress "Test case [...] did not check any assertions"
369 }
370}
371
RecursiveMutex cs_main
Mutex to guard access to validation specific variables, such as reading or changing the chainstate.
Definition: validation.cpp:118
@ READ_STATUS_OK
@ READ_STATUS_INVALID
static CBlock BuildBlockTestCase()
BOOST_AUTO_TEST_CASE(SimpleRoundTripTest)
std::vector< std::pair< uint256, CTransactionRef > > extra_txn
constexpr long SHARED_TX_OFFSET
const CChainParams & Params()
Return the currently selected parameters.
std::vector< uint16_t > indexes
uint64_t GetShortID(const uint256 &txhash) const
Nodes collect new transactions into a block, hash them into a hash tree, and scan through nonce value...
Definition: block.h:21
uint32_t nNonce
Definition: block.h:29
uint32_t nBits
Definition: block.h:28
int32_t nVersion
Definition: block.h:24
uint256 hashPrevBlock
Definition: block.h:25
uint256 hashMerkleRoot
Definition: block.h:26
uint256 GetHash() const
Definition: block.cpp:11
Definition: block.h:63
std::vector< CTransactionRef > vtx
Definition: block.h:66
Double ended buffer combining vector and stream-like interfaces.
Definition: streams.h:205
CTxMemPool stores valid-according-to-the-current-best-chain transactions that may be included in the ...
Definition: txmempool.h:424
RecursiveMutex cs
This mutex needs to be locked when accessing mapTx or other members that are guarded by it.
Definition: txmempool.h:511
void removeRecursive(const CTransaction &tx, MemPoolRemovalReason reason) EXCLUSIVE_LOCKS_REQUIRED(cs)
Definition: txmempool.cpp:589
void check(const CCoinsViewCache &active_coins_tip, int64_t spendheight) const EXCLUSIVE_LOCKS_REQUIRED(void addUnchecked(const CTxMemPoolEntry &entry, bool validFeeEstimate=true) EXCLUSIVE_LOCKS_REQUIRED(cs
If sanity-checking is turned on, check makes sure the pool is consistent (does not contain two transa...
Definition: txmempool.h:582
unsigned long size() const
Definition: txmempool.h:707
ReadStatus InitData(const CBlockHeaderAndShortTxIDs &cmpctblock, const std::vector< std::pair< uint256, CTransactionRef > > &extra_txn)
bool IsTxAvailable(size_t index) const
ReadStatus FillBlock(CBlock &block, const std::vector< CTransactionRef > &vtx_missing)
TestHeaderAndShortIDs(const CBlock &block)
TestHeaderAndShortIDs(const CBlockHeaderAndShortTxIDs &orig)
std::vector< uint64_t > shorttxids
std::vector< PrefilledTransaction > prefilledtxn
uint64_t GetShortID(const uint256 &txhash) const
SERIALIZE_METHODS(TestHeaderAndShortIDs, obj)
std::string ToString() const
Definition: uint256.cpp:64
256-bit opaque blob.
Definition: uint256.h:124
BOOST_AUTO_TEST_SUITE_END()
bool mutated
uint256 BlockMerkleRoot(const CBlock &block, bool *mutated)
Definition: merkle.cpp:65
#define BOOST_FIXTURE_TEST_SUITE(a, b)
Definition: object.cpp:14
#define BOOST_CHECK_EQUAL(v1, v2)
Definition: object.cpp:18
#define BOOST_CHECK(expr)
Definition: object.cpp:17
bool CheckProofOfWork(uint256 hash, unsigned int nBits, const Consensus::Params &params)
Check whether a block hash satisfies the proof-of-work requirement specified by nBits.
Definition: pow.cpp:74
static CTransactionRef MakeTransactionRef(Tx &&txIn)
Definition: transaction.h:387
@ SER_NETWORK
Definition: serialize.h:138
static Wrapper< Formatter, T & > Using(T &&t)
Cause serialization/deserialization of an object to be done using a specified formatter class.
Definition: serialize.h:440
#define READWRITE(...)
Definition: serialize.h:147
void WriteCompactSize(CSizeComputer &os, uint64_t nSize)
Definition: serialize.h:1074
static uint256 InsecureRand256()
Definition: setup_common.h:66
std::string ToString(const T &t)
Locale-independent version of std::to_string.
Definition: string.h:87
A mutable version of CTransaction.
Definition: transaction.h:345
std::vector< CTxOut > vout
Definition: transaction.h:347
std::vector< CTxIn > vin
Definition: transaction.h:346
Serialization wrapper class for custom integers and enums.
Definition: serialize.h:473
Identical to TestingSetup, but chain set to regtest.
Definition: setup_common.h:104
Definition: setup_common.h:183
CTxMemPoolEntry FromTx(const CMutableTransaction &tx) const
Formatter to serialize/deserialize vector elements using another formatter.
Definition: serialize.h:566
#define LOCK2(cs1, cs2)
Definition: sync.h:227
@ REPLACED
Removed for replacement.
assert(!tx.IsCoinBase())
static const int PROTOCOL_VERSION
network protocol versioning
Definition: version.h:12