Bitcoin Core 28.99.0
P2P Digital Currency
blockencodings_tests.cpp
Go to the documentation of this file.
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
14
15#include <boost/test/unit_test.hpp>
16
17const std::vector<CTransactionRef> empty_extra_txn;
18
19BOOST_FIXTURE_TEST_SUITE(blockencodings_tests, RegTestingSetup)
20
23 tx.vin.resize(1);
24 tx.vin[0].scriptSig.resize(10);
25 tx.vout.resize(1);
26 tx.vout[0].nValue = 42;
27 return tx;
28}
29
31 CBlock block;
33
34 block.vtx.resize(3);
35 block.vtx[0] = MakeTransactionRef(tx);
36 block.nVersion = 42;
37 block.hashPrevBlock = ctx.rand256();
38 block.nBits = 0x207fffff;
39
40 tx.vin[0].prevout.hash = Txid::FromUint256(ctx.rand256());
41 tx.vin[0].prevout.n = 0;
42 block.vtx[1] = MakeTransactionRef(tx);
43
44 tx.vin.resize(10);
45 for (size_t i = 0; i < tx.vin.size(); i++) {
46 tx.vin[i].prevout.hash = Txid::FromUint256(ctx.rand256());
47 tx.vin[i].prevout.n = 0;
48 }
49 block.vtx[2] = MakeTransactionRef(tx);
50
51 bool mutated;
52 block.hashMerkleRoot = BlockMerkleRoot(block, &mutated);
53 assert(!mutated);
54 while (!CheckProofOfWork(block.GetHash(), block.nBits, Params().GetConsensus())) ++block.nNonce;
55 return block;
56}
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)
60constexpr long SHARED_TX_OFFSET{4};
61
62BOOST_AUTO_TEST_CASE(SimpleRoundTripTest)
63{
66 auto rand_ctx(FastRandomContext(uint256{42}));
67 CBlock block(BuildBlockTestCase(rand_ctx));
68
69 LOCK2(cs_main, pool.cs);
70 AddToMempool(pool, entry.FromTx(block.vtx[2]));
71 BOOST_CHECK_EQUAL(pool.get(block.vtx[2]->GetHash()).use_count(), SHARED_TX_OFFSET + 0);
72
73 // Do a simple ShortTxIDs RT
74 {
75 CBlockHeaderAndShortTxIDs shortIDs{block, rand_ctx.rand64()};
76
77 DataStream stream{};
78 stream << shortIDs;
79
81 stream >> shortIDs2;
82
83 PartiallyDownloadedBlock partialBlock(&pool);
84 BOOST_CHECK(partialBlock.InitData(shortIDs2, empty_extra_txn) == READ_STATUS_OK);
85 BOOST_CHECK( partialBlock.IsTxAvailable(0));
86 BOOST_CHECK(!partialBlock.IsTxAvailable(1));
87 BOOST_CHECK( partialBlock.IsTxAvailable(2));
88
89 BOOST_CHECK_EQUAL(pool.get(block.vtx[2]->GetHash()).use_count(), SHARED_TX_OFFSET + 1);
90
91 size_t poolSize = pool.size();
93 BOOST_CHECK_EQUAL(pool.size(), poolSize - 1);
94
95 CBlock block2;
96 {
97 PartiallyDownloadedBlock tmp = partialBlock;
98 BOOST_CHECK(partialBlock.FillBlock(block2, {}) == READ_STATUS_INVALID); // No transactions
99 partialBlock = tmp;
100 }
101
102 // Wrong transaction
103 {
104 PartiallyDownloadedBlock tmp = partialBlock;
105 partialBlock.FillBlock(block2, {block.vtx[2]}); // Current implementation doesn't check txn here, but don't require that
106 partialBlock = tmp;
107 }
108 bool mutated;
109 BOOST_CHECK(block.hashMerkleRoot != BlockMerkleRoot(block2, &mutated));
110
111 CBlock block3;
112 BOOST_CHECK(partialBlock.FillBlock(block3, {block.vtx[1]}) == READ_STATUS_OK);
113 BOOST_CHECK_EQUAL(block.GetHash().ToString(), block3.GetHash().ToString());
115 BOOST_CHECK(!mutated);
116 }
117}
118
120 // Utility to encode custom CBlockHeaderAndShortTxIDs
121public:
123 uint64_t nonce;
124 std::vector<uint64_t> shorttxids;
125 std::vector<PrefilledTransaction> prefilledtxn;
126
128 DataStream stream{};
129 stream << orig;
130 stream >> *this;
131 }
132 explicit TestHeaderAndShortIDs(const CBlock& block, FastRandomContext& ctx) :
133 TestHeaderAndShortIDs(CBlockHeaderAndShortTxIDs{block, ctx.rand64()}) {}
134
135 uint64_t GetShortID(const Wtxid& txhash) const {
136 DataStream stream{};
137 stream << *this;
139 stream >> base;
140 return base.GetShortID(txhash);
141 }
142
144};
145
146BOOST_AUTO_TEST_CASE(NonCoinbasePreforwardRTTest)
147{
150 auto rand_ctx(FastRandomContext(uint256{42}));
151 CBlock block(BuildBlockTestCase(rand_ctx));
152
153 LOCK2(cs_main, pool.cs);
154 AddToMempool(pool, entry.FromTx(block.vtx[2]));
155 BOOST_CHECK_EQUAL(pool.get(block.vtx[2]->GetHash()).use_count(), SHARED_TX_OFFSET + 0);
156
157 uint256 txhash;
158
159 // Test with pre-forwarding tx 1, but not coinbase
160 {
161 TestHeaderAndShortIDs shortIDs(block, rand_ctx);
162 shortIDs.prefilledtxn.resize(1);
163 shortIDs.prefilledtxn[0] = {1, block.vtx[1]};
164 shortIDs.shorttxids.resize(2);
165 shortIDs.shorttxids[0] = shortIDs.GetShortID(block.vtx[0]->GetWitnessHash());
166 shortIDs.shorttxids[1] = shortIDs.GetShortID(block.vtx[2]->GetWitnessHash());
167
168 DataStream stream{};
169 stream << shortIDs;
170
172 stream >> shortIDs2;
173
174 PartiallyDownloadedBlock partialBlock(&pool);
175 BOOST_CHECK(partialBlock.InitData(shortIDs2, empty_extra_txn) == READ_STATUS_OK);
176 BOOST_CHECK(!partialBlock.IsTxAvailable(0));
177 BOOST_CHECK( partialBlock.IsTxAvailable(1));
178 BOOST_CHECK( partialBlock.IsTxAvailable(2));
179
180 BOOST_CHECK_EQUAL(pool.get(block.vtx[2]->GetHash()).use_count(), SHARED_TX_OFFSET + 1); // +1 because of partialBlock
181
182 CBlock block2;
183 {
184 PartiallyDownloadedBlock tmp = partialBlock;
185 BOOST_CHECK(partialBlock.FillBlock(block2, {}) == READ_STATUS_INVALID); // No transactions
186 partialBlock = tmp;
187 }
188
189 // Wrong transaction
190 {
191 PartiallyDownloadedBlock tmp = partialBlock;
192 partialBlock.FillBlock(block2, {block.vtx[1]}); // Current implementation doesn't check txn here, but don't require that
193 partialBlock = tmp;
194 }
195 BOOST_CHECK_EQUAL(pool.get(block.vtx[2]->GetHash()).use_count(), SHARED_TX_OFFSET + 2); // +2 because of partialBlock and block2
196 bool mutated;
197 BOOST_CHECK(block.hashMerkleRoot != BlockMerkleRoot(block2, &mutated));
198
199 CBlock block3;
200 PartiallyDownloadedBlock partialBlockCopy = partialBlock;
201 BOOST_CHECK(partialBlock.FillBlock(block3, {block.vtx[0]}) == READ_STATUS_OK);
202 BOOST_CHECK_EQUAL(block.GetHash().ToString(), block3.GetHash().ToString());
204 BOOST_CHECK(!mutated);
205
206 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 txhash = block.vtx[2]->GetHash();
209 block.vtx.clear();
210 block2.vtx.clear();
211 block3.vtx.clear();
212 BOOST_CHECK_EQUAL(pool.get(txhash).use_count(), SHARED_TX_OFFSET + 1 - 1); // + 1 because of partialBlock; -1 because of block.
213 }
214 BOOST_CHECK_EQUAL(pool.get(txhash).use_count(), SHARED_TX_OFFSET - 1); // -1 because of block
215}
216
217BOOST_AUTO_TEST_CASE(SufficientPreforwardRTTest)
218{
221 auto rand_ctx(FastRandomContext(uint256{42}));
222 CBlock block(BuildBlockTestCase(rand_ctx));
223
224 LOCK2(cs_main, pool.cs);
225 AddToMempool(pool, entry.FromTx(block.vtx[1]));
226 BOOST_CHECK_EQUAL(pool.get(block.vtx[1]->GetHash()).use_count(), SHARED_TX_OFFSET + 0);
227
228 uint256 txhash;
229
230 // Test with pre-forwarding coinbase + tx 2 with tx 1 in mempool
231 {
232 TestHeaderAndShortIDs shortIDs(block, rand_ctx);
233 shortIDs.prefilledtxn.resize(2);
234 shortIDs.prefilledtxn[0] = {0, block.vtx[0]};
235 shortIDs.prefilledtxn[1] = {1, block.vtx[2]}; // id == 1 as it is 1 after index 1
236 shortIDs.shorttxids.resize(1);
237 shortIDs.shorttxids[0] = shortIDs.GetShortID(block.vtx[1]->GetWitnessHash());
238
239 DataStream stream{};
240 stream << shortIDs;
241
243 stream >> shortIDs2;
244
245 PartiallyDownloadedBlock partialBlock(&pool);
246 BOOST_CHECK(partialBlock.InitData(shortIDs2, empty_extra_txn) == READ_STATUS_OK);
247 BOOST_CHECK( partialBlock.IsTxAvailable(0));
248 BOOST_CHECK( partialBlock.IsTxAvailable(1));
249 BOOST_CHECK( partialBlock.IsTxAvailable(2));
250
251 BOOST_CHECK_EQUAL(pool.get(block.vtx[1]->GetHash()).use_count(), SHARED_TX_OFFSET + 1);
252
253 CBlock block2;
254 PartiallyDownloadedBlock partialBlockCopy = partialBlock;
255 BOOST_CHECK(partialBlock.FillBlock(block2, {}) == READ_STATUS_OK);
256 BOOST_CHECK_EQUAL(block.GetHash().ToString(), block2.GetHash().ToString());
257 bool mutated;
259 BOOST_CHECK(!mutated);
260
261 txhash = block.vtx[1]->GetHash();
262 block.vtx.clear();
263 block2.vtx.clear();
264 BOOST_CHECK_EQUAL(pool.get(txhash).use_count(), SHARED_TX_OFFSET + 1 - 1); // + 1 because of partialBlock; -1 because of block.
265 }
266 BOOST_CHECK_EQUAL(pool.get(txhash).use_count(), SHARED_TX_OFFSET - 1); // -1 because of block
267}
268
269BOOST_AUTO_TEST_CASE(EmptyBlockRoundTripTest)
270{
273
274 CBlock block;
275 auto rand_ctx(FastRandomContext(uint256{42}));
276 block.vtx.resize(1);
277 block.vtx[0] = MakeTransactionRef(std::move(coinbase));
278 block.nVersion = 42;
279 block.hashPrevBlock = rand_ctx.rand256();
280 block.nBits = 0x207fffff;
281
282 bool mutated;
283 block.hashMerkleRoot = BlockMerkleRoot(block, &mutated);
284 assert(!mutated);
285 while (!CheckProofOfWork(block.GetHash(), block.nBits, Params().GetConsensus())) ++block.nNonce;
286
287 // Test simple header round-trip with only coinbase
288 {
289 CBlockHeaderAndShortTxIDs shortIDs{block, rand_ctx.rand64()};
290
291 DataStream stream{};
292 stream << shortIDs;
293
295 stream >> shortIDs2;
296
297 PartiallyDownloadedBlock partialBlock(&pool);
298 BOOST_CHECK(partialBlock.InitData(shortIDs2, empty_extra_txn) == READ_STATUS_OK);
299 BOOST_CHECK(partialBlock.IsTxAvailable(0));
300
301 CBlock block2;
302 std::vector<CTransactionRef> vtx_missing;
303 BOOST_CHECK(partialBlock.FillBlock(block2, vtx_missing) == READ_STATUS_OK);
304 BOOST_CHECK_EQUAL(block.GetHash().ToString(), block2.GetHash().ToString());
306 BOOST_CHECK(!mutated);
307 }
308}
309
310BOOST_AUTO_TEST_CASE(ReceiveWithExtraTransactions) {
313 auto rand_ctx(FastRandomContext(uint256{42}));
314
316 mtx.vin[0].prevout.hash = Txid::FromUint256(rand_ctx.rand256());
317 mtx.vin[0].prevout.n = 0;
318 const CTransactionRef non_block_tx = MakeTransactionRef(std::move(mtx));
319
320 CBlock block(BuildBlockTestCase(rand_ctx));
321 std::vector<CTransactionRef> extra_txn;
322 extra_txn.resize(10);
323
324 LOCK2(cs_main, pool.cs);
325 AddToMempool(pool, entry.FromTx(block.vtx[2]));
326 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 for (const auto &block_tx : block.vtx) {
329 BOOST_CHECK_NE(block_tx->GetHash(), non_block_tx->GetHash());
330 }
331 // Ensure block.vtx[1] is not in pool
332 BOOST_CHECK_EQUAL(pool.get(block.vtx[1]->GetHash()), nullptr);
333
334 {
335 const CBlockHeaderAndShortTxIDs cmpctblock{block, rand_ctx.rand64()};
336 PartiallyDownloadedBlock partial_block(&pool);
337 PartiallyDownloadedBlock partial_block_with_extra(&pool);
338
339 BOOST_CHECK(partial_block.InitData(cmpctblock, extra_txn) == READ_STATUS_OK);
340 BOOST_CHECK( partial_block.IsTxAvailable(0));
341 BOOST_CHECK(!partial_block.IsTxAvailable(1));
342 BOOST_CHECK( partial_block.IsTxAvailable(2));
343
344 // Add an unrelated tx to extra_txn:
345 extra_txn[0] = non_block_tx;
346 // and a tx from the block that's not in the mempool:
347 extra_txn[1] = block.vtx[1];
348
349 BOOST_CHECK(partial_block_with_extra.InitData(cmpctblock, extra_txn) == READ_STATUS_OK);
350 BOOST_CHECK(partial_block_with_extra.IsTxAvailable(0));
351 // This transaction is now available via extra_txn:
352 BOOST_CHECK(partial_block_with_extra.IsTxAvailable(1));
353 BOOST_CHECK(partial_block_with_extra.IsTxAvailable(2));
354 }
355}
356
357BOOST_AUTO_TEST_CASE(TransactionsRequestSerializationTest) {
359 req1.blockhash = m_rng.rand256();
360 req1.indexes.resize(4);
361 req1.indexes[0] = 0;
362 req1.indexes[1] = 1;
363 req1.indexes[2] = 3;
364 req1.indexes[3] = 4;
365
366 DataStream stream{};
367 stream << req1;
368
370 stream >> req2;
371
373 BOOST_CHECK_EQUAL(req1.indexes.size(), req2.indexes.size());
374 BOOST_CHECK_EQUAL(req1.indexes[0], req2.indexes[0]);
375 BOOST_CHECK_EQUAL(req1.indexes[1], req2.indexes[1]);
376 BOOST_CHECK_EQUAL(req1.indexes[2], req2.indexes[2]);
377 BOOST_CHECK_EQUAL(req1.indexes[3], req2.indexes[3]);
378}
379
380BOOST_AUTO_TEST_CASE(TransactionsRequestDeserializationMaxTest) {
381 // Check that the highest legal index is decoded correctly
383 req0.blockhash = m_rng.rand256();
384 req0.indexes.resize(1);
385 req0.indexes[0] = 0xffff;
386 DataStream stream{};
387 stream << req0;
388
390 stream >> req1;
391 BOOST_CHECK_EQUAL(req0.indexes.size(), req1.indexes.size());
392 BOOST_CHECK_EQUAL(req0.indexes[0], req1.indexes[0]);
393}
394
395BOOST_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.
401 req0.blockhash = m_rng.rand256();
402 req0.indexes.resize(3);
403 req0.indexes[0] = 0x7000;
404 req0.indexes[1] = 0x10000 - 0x7000 - 2;
405 req0.indexes[2] = 0;
406 DataStream stream{};
407 stream << req0.blockhash;
408 WriteCompactSize(stream, req0.indexes.size());
409 WriteCompactSize(stream, req0.indexes[0]);
410 WriteCompactSize(stream, req0.indexes[1]);
411 WriteCompactSize(stream, req0.indexes[2]);
412
414 try {
415 stream >> req1;
416 // before patch: deserialize above succeeds and this check fails, demonstrating the overflow
417 BOOST_CHECK(req1.indexes[1] < req1.indexes[2]);
418 // this shouldn't be reachable before or after patch
419 BOOST_CHECK(0);
420 } catch(std::ios_base::failure &) {
421 // deserialize should fail
422 BOOST_CHECK(true); // Needed to suppress "Test case [...] did not check any assertions"
423 }
424}
425
node::NodeContext m_node
Definition: bitcoin-gui.cpp:42
@ READ_STATUS_OK
@ READ_STATUS_INVALID
static CBlock BuildBlockTestCase(FastRandomContext &ctx)
BOOST_AUTO_TEST_CASE(SimpleRoundTripTest)
const std::vector< CTransactionRef > empty_extra_txn
constexpr long SHARED_TX_OFFSET
static CMutableTransaction BuildTransactionTestCase()
const CChainParams & Params()
Return the currently selected parameters.
#define Assert(val)
Identity function.
Definition: check.h:85
std::vector< uint16_t > indexes
uint64_t GetShortID(const Wtxid &wtxid) const
Nodes collect new transactions into a block, hash them into a hash tree, and scan through nonce value...
Definition: block.h:22
uint32_t nNonce
Definition: block.h:30
uint32_t nBits
Definition: block.h:29
int32_t nVersion
Definition: block.h:25
uint256 hashPrevBlock
Definition: block.h:26
uint256 hashMerkleRoot
Definition: block.h:27
uint256 GetHash() const
Definition: block.cpp:11
Definition: block.h:69
std::vector< CTransactionRef > vtx
Definition: block.h:72
CTxMemPool stores valid-according-to-the-current-best-chain transactions that may be included in the ...
Definition: txmempool.h:304
RecursiveMutex cs
This mutex needs to be locked when accessing mapTx or other members that are guarded by it.
Definition: txmempool.h:390
void check(const CCoinsViewCache &active_coins_tip, int64_t spendheight) const EXCLUSIVE_LOCKS_REQUIRED(void removeRecursive(const CTransaction &tx, MemPoolRemovalReason reason) 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:457
CTransactionRef get(const uint256 &hash) const
Definition: txmempool.cpp:884
unsigned long size() const
Definition: txmempool.h:629
Double ended buffer combining vector and stream-like interfaces.
Definition: streams.h:147
Fast randomness source.
Definition: random.h:377
ReadStatus InitData(const CBlockHeaderAndShortTxIDs &cmpctblock, const std::vector< CTransactionRef > &extra_txn)
bool IsTxAvailable(size_t index) const
ReadStatus FillBlock(CBlock &block, const std::vector< CTransactionRef > &vtx_missing)
uint256 rand256() noexcept
generate a random uint256.
Definition: random.h:308
TestHeaderAndShortIDs(const CBlockHeaderAndShortTxIDs &orig)
std::vector< uint64_t > shorttxids
std::vector< PrefilledTransaction > prefilledtxn
TestHeaderAndShortIDs(const CBlock &block, FastRandomContext &ctx)
uint64_t GetShortID(const Wtxid &txhash) const
SERIALIZE_METHODS(TestHeaderAndShortIDs, obj)
std::string ToString() const
Definition: uint256.cpp:47
transaction_identifier represents the two canonical transaction identifier types (txid,...
static transaction_identifier FromUint256(const uint256 &id)
256-bit opaque blob.
Definition: uint256.h:190
RecursiveMutex cs_main
Mutex to guard access to validation specific variables, such as reading or changing the chainstate.
Definition: cs_main.cpp:8
BOOST_FIXTURE_TEST_SUITE(cuckoocache_tests, BasicTestingSetup)
Test Suite for CuckooCache.
BOOST_AUTO_TEST_SUITE_END()
AddToMempool(pool, CTxMemPoolEntry(tx, fee, nTime, nHeight, sequence, spendsCoinbase, sigOpCost, lp))
@ REPLACED
Removed for replacement.
uint256 BlockMerkleRoot(const CBlock &block, bool *mutated)
Definition: merkle.cpp:65
std::string ToString(const T &t)
Locale-independent version of std::to_string.
Definition: string.h:233
#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:140
static CTransactionRef MakeTransactionRef(Tx &&txIn)
Definition: transaction.h:424
std::shared_ptr< const CTransaction > CTransactionRef
Definition: transaction.h:423
void WriteCompactSize(SizeComputer &os, uint64_t nSize)
Definition: serialize.h:1095
static Wrapper< Formatter, T & > Using(T &&t)
Cause serialization/deserialization of an object to be done using a specified formatter class.
Definition: serialize.h:495
#define READWRITE(...)
Definition: serialize.h:156
A mutable version of CTransaction.
Definition: transaction.h:378
std::vector< CTxOut > vout
Definition: transaction.h:380
std::vector< CTxIn > vin
Definition: transaction.h:379
Serialization wrapper class for custom integers and enums.
Definition: serialize.h:528
Identical to TestingSetup, but chain set to regtest.
Definition: setup_common.h:127
Definition: txmempool.h:19
CTxMemPoolEntry FromTx(const CMutableTransaction &tx) const
Definition: txmempool.cpp:33
Formatter to serialize/deserialize vector elements using another formatter.
Definition: serialize.h:657
std::unique_ptr< CTxMemPool > mempool
Definition: context.h:68
#define LOCK2(cs1, cs2)
Definition: sync.h:258
assert(!tx.IsCoinBase())