Bitcoin Core 31.99.0
P2P Digital Currency
blockencodings_tests.cpp
Go to the documentation of this file.
1// Copyright (c) 2011-present 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
13#include <test/util/common.h>
15
16#include <boost/test/unit_test.hpp>
17
18const std::vector<std::pair<Wtxid, CTransactionRef>> empty_extra_txn;
19
20BOOST_FIXTURE_TEST_SUITE(blockencodings_tests, RegTestingSetup)
21
24 tx.vin.resize(1);
25 tx.vin[0].scriptSig.resize(10);
26 tx.vout.resize(1);
27 tx.vout[0].nValue = 42;
28 return tx;
29}
30
32 CBlock block;
34
35 block.vtx.resize(3);
36 block.vtx[0] = MakeTransactionRef(tx);
37 block.nVersion = 42;
38 block.hashPrevBlock = ctx.rand256();
39 block.nBits = 0x207fffff;
40
41 tx.vin[0].prevout.hash = Txid::FromUint256(ctx.rand256());
42 tx.vin[0].prevout.n = 0;
43 block.vtx[1] = MakeTransactionRef(tx);
44
45 tx.vin.resize(10);
46 for (size_t i = 0; i < tx.vin.size(); i++) {
47 tx.vin[i].prevout.hash = Txid::FromUint256(ctx.rand256());
48 tx.vin[i].prevout.n = 0;
49 }
50 block.vtx[2] = MakeTransactionRef(tx);
51
52 bool mutated;
53 block.hashMerkleRoot = BlockMerkleRoot(block, &mutated);
54 assert(!mutated);
55 while (!CheckProofOfWork(block.GetHash(), block.nBits, Params().GetConsensus())) ++block.nNonce;
56 return block;
57}
58
59// Number of shared use_counts we expect for a tx we haven't touched
60// (block + mempool entry + our copy from the GetSharedTx call)
61constexpr long SHARED_TX_OFFSET{3};
62
63BOOST_AUTO_TEST_CASE(SimpleRoundTripTest)
64{
67 auto rand_ctx(FastRandomContext(uint256{42}));
68 CBlock block(BuildBlockTestCase(rand_ctx));
69
70 LOCK2(cs_main, pool.cs);
71 TryAddToMempool(pool, entry.FromTx(block.vtx[2]));
72 BOOST_CHECK_EQUAL(pool.get(block.vtx[2]->GetHash()).use_count(), SHARED_TX_OFFSET + 0);
73
74 // Do a simple ShortTxIDs RT
75 {
76 CBlockHeaderAndShortTxIDs shortIDs{block, rand_ctx.rand64()};
77
78 DataStream stream{};
79 stream << shortIDs;
80
82 stream >> shortIDs2;
83
84 PartiallyDownloadedBlock partialBlock(&pool);
85 BOOST_CHECK(partialBlock.InitData(shortIDs2, empty_extra_txn) == READ_STATUS_OK);
86 BOOST_CHECK( partialBlock.IsTxAvailable(0));
87 BOOST_CHECK(!partialBlock.IsTxAvailable(1));
88 BOOST_CHECK( partialBlock.IsTxAvailable(2));
89
90 BOOST_CHECK_EQUAL(pool.get(block.vtx[2]->GetHash()).use_count(), SHARED_TX_OFFSET + 1);
91
92 size_t poolSize = pool.size();
94 BOOST_CHECK_EQUAL(pool.size(), poolSize - 1);
95
96 CBlock block2;
97 {
98 PartiallyDownloadedBlock tmp = partialBlock;
99 BOOST_CHECK(partialBlock.FillBlock(block2, {}, /*segwit_active=*/true) == READ_STATUS_INVALID); // No transactions
100 partialBlock = tmp;
101 }
102
103 // Wrong transaction
104 {
105 PartiallyDownloadedBlock tmp = partialBlock;
106 partialBlock.FillBlock(block2, {block.vtx[2]}, /*segwit_active=*/true); // Current implementation doesn't check txn here, but don't require that
107 partialBlock = tmp;
108 }
109 bool mutated;
110 BOOST_CHECK(block.hashMerkleRoot != BlockMerkleRoot(block2, &mutated));
111
112 CBlock block3;
113 BOOST_CHECK(partialBlock.FillBlock(block3, {block.vtx[1]}, /*segwit_active=*/true) == READ_STATUS_OK);
114 BOOST_CHECK_EQUAL(block.GetHash().ToString(), block3.GetHash().ToString());
116 BOOST_CHECK(!mutated);
117 }
118}
119
121 // Utility to encode custom CBlockHeaderAndShortTxIDs
122public:
124 uint64_t nonce;
125 std::vector<uint64_t> shorttxids;
126 std::vector<PrefilledTransaction> prefilledtxn;
127
129 DataStream stream{};
130 stream << orig;
131 stream >> *this;
132 }
133 explicit TestHeaderAndShortIDs(const CBlock& block, FastRandomContext& ctx) :
134 TestHeaderAndShortIDs(CBlockHeaderAndShortTxIDs{block, ctx.rand64()}) {}
135
136 uint64_t GetShortID(const Wtxid& txhash) const {
137 DataStream stream{};
138 stream << *this;
140 stream >> base;
141 return base.GetShortID(txhash);
142 }
143
145};
146
147BOOST_AUTO_TEST_CASE(NonCoinbasePreforwardRTTest)
148{
151 auto rand_ctx(FastRandomContext(uint256{42}));
152 CBlock block(BuildBlockTestCase(rand_ctx));
153
154 LOCK2(cs_main, pool.cs);
155 TryAddToMempool(pool, entry.FromTx(block.vtx[2]));
156 BOOST_CHECK_EQUAL(pool.get(block.vtx[2]->GetHash()).use_count(), SHARED_TX_OFFSET + 0);
157
158 Txid txhash;
159
160 // Test with pre-forwarding tx 1, but not coinbase
161 {
162 TestHeaderAndShortIDs shortIDs(block, rand_ctx);
163 shortIDs.prefilledtxn.resize(1);
164 shortIDs.prefilledtxn[0] = {1, block.vtx[1]};
165 shortIDs.shorttxids.resize(2);
166 shortIDs.shorttxids[0] = shortIDs.GetShortID(block.vtx[0]->GetWitnessHash());
167 shortIDs.shorttxids[1] = shortIDs.GetShortID(block.vtx[2]->GetWitnessHash());
168
169 DataStream stream{};
170 stream << shortIDs;
171
173 stream >> shortIDs2;
174
175 PartiallyDownloadedBlock partialBlock(&pool);
176 BOOST_CHECK(partialBlock.InitData(shortIDs2, empty_extra_txn) == READ_STATUS_OK);
177 BOOST_CHECK(!partialBlock.IsTxAvailable(0));
178 BOOST_CHECK( partialBlock.IsTxAvailable(1));
179 BOOST_CHECK( partialBlock.IsTxAvailable(2));
180
181 BOOST_CHECK_EQUAL(pool.get(block.vtx[2]->GetHash()).use_count(), SHARED_TX_OFFSET + 1); // +1 because of partialBlock
182
183 CBlock block2;
184 {
185 PartiallyDownloadedBlock tmp = partialBlock;
186 BOOST_CHECK(partialBlock.FillBlock(block2, {}, /*segwit_active=*/true) == READ_STATUS_INVALID); // No transactions
187 partialBlock = tmp;
188 }
189
190 // Wrong transaction
191 {
192 PartiallyDownloadedBlock tmp = partialBlock;
193 partialBlock.FillBlock(block2, {block.vtx[1]}, /*segwit_active=*/true); // Current implementation doesn't check txn here, but don't require that
194 partialBlock = tmp;
195 }
196 BOOST_CHECK_EQUAL(pool.get(block.vtx[2]->GetHash()).use_count(), SHARED_TX_OFFSET + 2); // +2 because of partialBlock and block2
197 bool mutated;
198 BOOST_CHECK(block.hashMerkleRoot != BlockMerkleRoot(block2, &mutated));
199
200 CBlock block3;
201 PartiallyDownloadedBlock partialBlockCopy = partialBlock;
202 BOOST_CHECK(partialBlock.FillBlock(block3, {block.vtx[0]}, /*segwit_active=*/true) == READ_STATUS_OK);
203 BOOST_CHECK_EQUAL(block.GetHash().ToString(), block3.GetHash().ToString());
205 BOOST_CHECK(!mutated);
206
207 BOOST_CHECK_EQUAL(pool.get(block.vtx[2]->GetHash()).use_count(), SHARED_TX_OFFSET + 3); // +2 because of partialBlock and block2 and block3
208
209 txhash = block.vtx[2]->GetHash();
210 block.vtx.clear();
211 block2.vtx.clear();
212 block3.vtx.clear();
213 BOOST_CHECK_EQUAL(pool.get(txhash).use_count(), SHARED_TX_OFFSET + 1 - 1); // + 1 because of partialBlock; -1 because of block.
214 }
215 BOOST_CHECK_EQUAL(pool.get(txhash).use_count(), SHARED_TX_OFFSET - 1); // -1 because of block
216}
217
218BOOST_AUTO_TEST_CASE(SufficientPreforwardRTTest)
219{
222 auto rand_ctx(FastRandomContext(uint256{42}));
223 CBlock block(BuildBlockTestCase(rand_ctx));
224
225 LOCK2(cs_main, pool.cs);
226 TryAddToMempool(pool, entry.FromTx(block.vtx[1]));
227 BOOST_CHECK_EQUAL(pool.get(block.vtx[1]->GetHash()).use_count(), SHARED_TX_OFFSET + 0);
228
229 Txid txhash;
230
231 // Test with pre-forwarding coinbase + tx 2 with tx 1 in mempool
232 {
233 TestHeaderAndShortIDs shortIDs(block, rand_ctx);
234 shortIDs.prefilledtxn.resize(2);
235 shortIDs.prefilledtxn[0] = {0, block.vtx[0]};
236 shortIDs.prefilledtxn[1] = {1, block.vtx[2]}; // id == 1 as it is 1 after index 1
237 shortIDs.shorttxids.resize(1);
238 shortIDs.shorttxids[0] = shortIDs.GetShortID(block.vtx[1]->GetWitnessHash());
239
240 DataStream stream{};
241 stream << shortIDs;
242
244 stream >> shortIDs2;
245
246 PartiallyDownloadedBlock partialBlock(&pool);
247 BOOST_CHECK(partialBlock.InitData(shortIDs2, empty_extra_txn) == READ_STATUS_OK);
248 BOOST_CHECK( partialBlock.IsTxAvailable(0));
249 BOOST_CHECK( partialBlock.IsTxAvailable(1));
250 BOOST_CHECK( partialBlock.IsTxAvailable(2));
251
252 BOOST_CHECK_EQUAL(pool.get(block.vtx[1]->GetHash()).use_count(), SHARED_TX_OFFSET + 1);
253
254 CBlock block2;
255 PartiallyDownloadedBlock partialBlockCopy = partialBlock;
256 BOOST_CHECK(partialBlock.FillBlock(block2, {}, /*segwit_active=*/true) == READ_STATUS_OK);
257 BOOST_CHECK_EQUAL(block.GetHash().ToString(), block2.GetHash().ToString());
258 bool mutated;
260 BOOST_CHECK(!mutated);
261
262 txhash = block.vtx[1]->GetHash();
263 block.vtx.clear();
264 block2.vtx.clear();
265 BOOST_CHECK_EQUAL(pool.get(txhash).use_count(), SHARED_TX_OFFSET + 1 - 1); // + 1 because of partialBlock; -1 because of block.
266 }
267 BOOST_CHECK_EQUAL(pool.get(txhash).use_count(), SHARED_TX_OFFSET - 1); // -1 because of block
268}
269
270BOOST_AUTO_TEST_CASE(EmptyBlockRoundTripTest)
271{
274
275 CBlock block;
276 auto rand_ctx(FastRandomContext(uint256{42}));
277 block.vtx.resize(1);
278 block.vtx[0] = MakeTransactionRef(std::move(coinbase));
279 block.nVersion = 42;
280 block.hashPrevBlock = rand_ctx.rand256();
281 block.nBits = 0x207fffff;
282
283 bool mutated;
284 block.hashMerkleRoot = BlockMerkleRoot(block, &mutated);
285 assert(!mutated);
286 while (!CheckProofOfWork(block.GetHash(), block.nBits, Params().GetConsensus())) ++block.nNonce;
287
288 // Test simple header round-trip with only coinbase
289 {
290 CBlockHeaderAndShortTxIDs shortIDs{block, rand_ctx.rand64()};
291
292 DataStream stream{};
293 stream << shortIDs;
294
296 stream >> shortIDs2;
297
298 PartiallyDownloadedBlock partialBlock(&pool);
299 BOOST_CHECK(partialBlock.InitData(shortIDs2, empty_extra_txn) == READ_STATUS_OK);
300 BOOST_CHECK(partialBlock.IsTxAvailable(0));
301
302 CBlock block2;
303 std::vector<CTransactionRef> vtx_missing;
304 BOOST_CHECK(partialBlock.FillBlock(block2, vtx_missing, /*segwit_active=*/true) == READ_STATUS_OK);
305 BOOST_CHECK_EQUAL(block.GetHash().ToString(), block2.GetHash().ToString());
307 BOOST_CHECK(!mutated);
308 }
309}
310
311BOOST_AUTO_TEST_CASE(ReceiveWithExtraTransactions) {
314 auto rand_ctx(FastRandomContext(uint256{42}));
315
317 mtx.vin[0].prevout.hash = Txid::FromUint256(rand_ctx.rand256());
318 mtx.vin[0].prevout.n = 0;
319 const CTransactionRef non_block_tx = MakeTransactionRef(std::move(mtx));
320
321 CBlock block(BuildBlockTestCase(rand_ctx));
322 std::vector<std::pair<Wtxid, CTransactionRef>> extra_txn;
323 extra_txn.resize(10);
324
325 LOCK2(cs_main, pool.cs);
326 TryAddToMempool(pool, entry.FromTx(block.vtx[2]));
327 BOOST_CHECK_EQUAL(pool.get(block.vtx[2]->GetHash()).use_count(), SHARED_TX_OFFSET + 0);
328 // Ensure the non_block_tx is actually not in the block
329 for (const auto &block_tx : block.vtx) {
330 BOOST_CHECK_NE(block_tx->GetHash(), non_block_tx->GetHash());
331 }
332 // Ensure block.vtx[1] is not in pool
333 BOOST_CHECK_EQUAL(pool.get(block.vtx[1]->GetHash()), nullptr);
334
335 {
336 const CBlockHeaderAndShortTxIDs cmpctblock{block, rand_ctx.rand64()};
337 PartiallyDownloadedBlock partial_block(&pool);
338 PartiallyDownloadedBlock partial_block_with_extra(&pool);
339
340 BOOST_CHECK(partial_block.InitData(cmpctblock, extra_txn) == READ_STATUS_OK);
341 BOOST_CHECK( partial_block.IsTxAvailable(0));
342 BOOST_CHECK(!partial_block.IsTxAvailable(1));
343 BOOST_CHECK( partial_block.IsTxAvailable(2));
344
345 // Add an unrelated tx to extra_txn:
346 extra_txn[0] = {non_block_tx->GetWitnessHash(), non_block_tx};
347 // and a tx from the block that's not in the mempool:
348 extra_txn[1] = {block.vtx[1]->GetWitnessHash(), block.vtx[1]};
349
350 BOOST_CHECK(partial_block_with_extra.InitData(cmpctblock, extra_txn) == READ_STATUS_OK);
351 BOOST_CHECK(partial_block_with_extra.IsTxAvailable(0));
352 // This transaction is now available via extra_txn:
353 BOOST_CHECK(partial_block_with_extra.IsTxAvailable(1));
354 BOOST_CHECK(partial_block_with_extra.IsTxAvailable(2));
355 }
356}
357
358BOOST_AUTO_TEST_CASE(TransactionsRequestSerializationTest) {
360 req1.blockhash = m_rng.rand256();
361 req1.indexes.resize(4);
362 req1.indexes[0] = 0;
363 req1.indexes[1] = 1;
364 req1.indexes[2] = 3;
365 req1.indexes[3] = 4;
366
367 DataStream stream{};
368 stream << req1;
369
371 stream >> req2;
372
374 BOOST_CHECK_EQUAL(req1.indexes.size(), req2.indexes.size());
375 BOOST_CHECK_EQUAL(req1.indexes[0], req2.indexes[0]);
376 BOOST_CHECK_EQUAL(req1.indexes[1], req2.indexes[1]);
377 BOOST_CHECK_EQUAL(req1.indexes[2], req2.indexes[2]);
378 BOOST_CHECK_EQUAL(req1.indexes[3], req2.indexes[3]);
379}
380
381BOOST_AUTO_TEST_CASE(TransactionsRequestDeserializationMaxTest) {
382 // Check that the highest legal index is decoded correctly
384 req0.blockhash = m_rng.rand256();
385 req0.indexes.resize(1);
386 req0.indexes[0] = 0xffff;
387 DataStream stream{};
388 stream << req0;
389
391 stream >> req1;
392 BOOST_CHECK_EQUAL(req0.indexes.size(), req1.indexes.size());
393 BOOST_CHECK_EQUAL(req0.indexes[0], req1.indexes[0]);
394}
395
396BOOST_AUTO_TEST_CASE(TransactionsRequestDeserializationOverflowTest) {
397 // Any set of index deltas that starts with N values that sum to (0x10000 - N)
398 // causes the edge-case overflow that was originally not checked for. Such
399 // a request cannot be created by serializing a real BlockTransactionsRequest
400 // due to the overflow, so here we'll serialize from raw deltas.
402 req0.blockhash = m_rng.rand256();
403 req0.indexes.resize(3);
404 req0.indexes[0] = 0x7000;
405 req0.indexes[1] = 0x10000 - 0x7000 - 2;
406 req0.indexes[2] = 0;
407 DataStream stream{};
408 stream << req0.blockhash;
409 WriteCompactSize(stream, req0.indexes.size());
410 WriteCompactSize(stream, req0.indexes[0]);
411 WriteCompactSize(stream, req0.indexes[1]);
412 WriteCompactSize(stream, req0.indexes[2]);
413
415 try {
416 stream >> req1;
417 // before patch: deserialize above succeeds and this check fails, demonstrating the overflow
418 BOOST_CHECK(req1.indexes[1] < req1.indexes[2]);
419 // this shouldn't be reachable before or after patch
420 BOOST_CHECK(0);
421 } catch(std::ios_base::failure &) {
422 // deserialize should fail
423 BOOST_CHECK(true); // Needed to suppress "Test case [...] did not check any assertions"
424 }
425}
426
TryAddToMempool(pool, CTxMemPoolEntry(tx, fee, 0, 1, 0, false, 4, lp))
node::NodeContext m_node
Definition: bitcoin-gui.cpp:43
@ READ_STATUS_OK
@ READ_STATUS_INVALID
static CBlock BuildBlockTestCase(FastRandomContext &ctx)
BOOST_AUTO_TEST_CASE(SimpleRoundTripTest)
const std::vector< std::pair< Wtxid, 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:113
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:27
uint32_t nNonce
Definition: block.h:35
uint32_t nBits
Definition: block.h:34
int32_t nVersion
Definition: block.h:30
uint256 hashPrevBlock
Definition: block.h:31
uint256 hashMerkleRoot
Definition: block.h:32
uint256 GetHash() const
Definition: block.cpp:15
Definition: block.h:74
std::vector< CTransactionRef > vtx
Definition: block.h:77
CTxMemPool stores valid-according-to-the-current-best-chain transactions that may be included in the ...
Definition: txmempool.h:187
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:325
CTransactionRef get(const Txid &hash) const
Definition: txmempool.cpp:621
unsigned long size() const
Definition: txmempool.h:485
Double ended buffer combining vector and stream-like interfaces.
Definition: streams.h:133
Fast randomness source.
Definition: random.h:386
ReadStatus FillBlock(CBlock &block, const std::vector< CTransactionRef > &vtx_missing, bool segwit_active)
bool IsTxAvailable(size_t index) const
ReadStatus InitData(const CBlockHeaderAndShortTxIDs &cmpctblock, const std::vector< std::pair< Wtxid, CTransactionRef > > &extra_txn)
uint256 rand256() noexcept
generate a random uint256.
Definition: random.h:317
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:21
transaction_identifier represents the two canonical transaction identifier types (txid,...
static transaction_identifier FromUint256(const uint256 &id)
256-bit opaque blob.
Definition: uint256.h:195
uint256 BlockMerkleRoot(const CBlock &block, bool *mutated)
Definition: merkle.cpp:66
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()
@ REPLACED
Removed for replacement.
std::string ToString(const T &t)
Locale-independent version of std::to_string.
Definition: string.h:246
#define BOOST_CHECK_EQUAL(v1, v2)
Definition: object.cpp:17
#define BOOST_CHECK(expr)
Definition: object.cpp:16
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:404
std::shared_ptr< const CTransaction > CTransactionRef
Definition: transaction.h:403
void WriteCompactSize(SizeComputer &os, uint64_t nSize)
Definition: serialize.h:1090
static Wrapper< Formatter, T & > Using(T &&t)
Cause serialization/deserialization of an object to be done using a specified formatter class.
Definition: serialize.h:489
#define READWRITE(...)
Definition: serialize.h:146
A mutable version of CTransaction.
Definition: transaction.h:358
std::vector< CTxOut > vout
Definition: transaction.h:360
std::vector< CTxIn > vin
Definition: transaction.h:359
Serialization wrapper class for custom integers and enums.
Definition: serialize.h:522
Identical to TestingSetup, but chain set to regtest.
Definition: setup_common.h:128
Definition: txmempool.h:19
CTxMemPoolEntry FromTx(const CMutableTransaction &tx) const
Definition: txmempool.cpp:34
Formatter to serialize/deserialize vector elements using another formatter.
Definition: serialize.h:651
std::unique_ptr< CTxMemPool > mempool
Definition: context.h:68
#define LOCK2(cs1, cs2)
Definition: sync.h:259
assert(!tx.IsCoinBase())