Bitcoin Core  24.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/txmempool.h>
11 
12 #include <test/util/setup_common.h>
13 
14 #include <boost/test/unit_test.hpp>
15 
16 std::vector<std::pair<uint256, CTransactionRef>> extra_txn;
17 
18 BOOST_FIXTURE_TEST_SUITE(blockencodings_tests, RegTestingSetup)
19 
21  CBlock block;
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] = MakeTransactionRef(tx);
30  block.nVersion = 42;
32  block.nBits = 0x207fffff;
33 
34  tx.vin[0].prevout.hash = InsecureRand256();
35  tx.vin[0].prevout.n = 0;
36  block.vtx[1] = MakeTransactionRef(tx);
37 
38  tx.vin.resize(10);
39  for (size_t i = 0; i < tx.vin.size(); i++) {
40  tx.vin[i].prevout.hash = InsecureRand256();
41  tx.vin[i].prevout.n = 0;
42  }
43  block.vtx[2] = MakeTransactionRef(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 haven't touched
53 // (block + mempool + our copy from the GetSharedTx call)
54 constexpr long SHARED_TX_OFFSET{3};
55 
56 BOOST_AUTO_TEST_CASE(SimpleRoundTripTest)
57 {
58  CTxMemPool& pool = *Assert(m_node.mempool);
60  CBlock block(BuildBlockTestCase());
61 
62  LOCK2(cs_main, pool.cs);
63  pool.addUnchecked(entry.FromTx(block.vtx[2]));
64  BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[2]->GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 0);
65 
66  // Do a simple ShortTxIDs RT
67  {
68  CBlockHeaderAndShortTxIDs shortIDs{block};
69 
71  stream << shortIDs;
72 
73  CBlockHeaderAndShortTxIDs shortIDs2;
74  stream >> shortIDs2;
75 
76  PartiallyDownloadedBlock partialBlock(&pool);
77  BOOST_CHECK(partialBlock.InitData(shortIDs2, extra_txn) == READ_STATUS_OK);
78  BOOST_CHECK( partialBlock.IsTxAvailable(0));
79  BOOST_CHECK(!partialBlock.IsTxAvailable(1));
80  BOOST_CHECK( partialBlock.IsTxAvailable(2));
81 
82  BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[2]->GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 1);
83 
84  size_t poolSize = pool.size();
86  BOOST_CHECK_EQUAL(pool.size(), poolSize - 1);
87 
88  CBlock block2;
89  {
90  PartiallyDownloadedBlock tmp = partialBlock;
91  BOOST_CHECK(partialBlock.FillBlock(block2, {}) == READ_STATUS_INVALID); // No transactions
92  partialBlock = tmp;
93  }
94 
95  // Wrong transaction
96  {
97  PartiallyDownloadedBlock tmp = partialBlock;
98  partialBlock.FillBlock(block2, {block.vtx[2]}); // Current implementation doesn't check txn here, but don't require that
99  partialBlock = tmp;
100  }
101  bool mutated;
103 
104  CBlock block3;
105  BOOST_CHECK(partialBlock.FillBlock(block3, {block.vtx[1]}) == READ_STATUS_OK);
106  BOOST_CHECK_EQUAL(block.GetHash().ToString(), block3.GetHash().ToString());
109  }
110 }
111 
113  // Utility to encode custom CBlockHeaderAndShortTxIDs
114 public:
116  uint64_t nonce;
117  std::vector<uint64_t> shorttxids;
118  std::vector<PrefilledTransaction> prefilledtxn;
119 
122  stream << orig;
123  stream >> *this;
124  }
125  explicit TestHeaderAndShortIDs(const CBlock& block) :
127 
128  uint64_t GetShortID(const uint256& txhash) const {
130  stream << *this;
132  stream >> base;
133  return base.GetShortID(txhash);
134  }
135 
137 };
138 
139 BOOST_AUTO_TEST_CASE(NonCoinbasePreforwardRTTest)
140 {
141  CTxMemPool& pool = *Assert(m_node.mempool);
143  CBlock block(BuildBlockTestCase());
144 
145  LOCK2(cs_main, pool.cs);
146  pool.addUnchecked(entry.FromTx(block.vtx[2]));
147  BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[2]->GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 0);
148 
149  uint256 txhash;
150 
151  // Test with pre-forwarding tx 1, but not coinbase
152  {
153  TestHeaderAndShortIDs shortIDs(block);
154  shortIDs.prefilledtxn.resize(1);
155  shortIDs.prefilledtxn[0] = {1, block.vtx[1]};
156  shortIDs.shorttxids.resize(2);
157  shortIDs.shorttxids[0] = shortIDs.GetShortID(block.vtx[0]->GetHash());
158  shortIDs.shorttxids[1] = shortIDs.GetShortID(block.vtx[2]->GetHash());
159 
161  stream << shortIDs;
162 
163  CBlockHeaderAndShortTxIDs shortIDs2;
164  stream >> shortIDs2;
165 
166  PartiallyDownloadedBlock partialBlock(&pool);
167  BOOST_CHECK(partialBlock.InitData(shortIDs2, extra_txn) == READ_STATUS_OK);
168  BOOST_CHECK(!partialBlock.IsTxAvailable(0));
169  BOOST_CHECK( partialBlock.IsTxAvailable(1));
170  BOOST_CHECK( partialBlock.IsTxAvailable(2));
171 
172  BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[2]->GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 1); // +1 because of partialBlock
173 
174  CBlock block2;
175  {
176  PartiallyDownloadedBlock tmp = partialBlock;
177  BOOST_CHECK(partialBlock.FillBlock(block2, {}) == READ_STATUS_INVALID); // No transactions
178  partialBlock = tmp;
179  }
180 
181  // Wrong transaction
182  {
183  PartiallyDownloadedBlock tmp = partialBlock;
184  partialBlock.FillBlock(block2, {block.vtx[1]}); // Current implementation doesn't check txn here, but don't require that
185  partialBlock = tmp;
186  }
187  BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[2]->GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 2); // +2 because of partialBlock and block2
188  bool mutated;
190 
191  CBlock block3;
192  PartiallyDownloadedBlock partialBlockCopy = partialBlock;
193  BOOST_CHECK(partialBlock.FillBlock(block3, {block.vtx[0]}) == READ_STATUS_OK);
194  BOOST_CHECK_EQUAL(block.GetHash().ToString(), block3.GetHash().ToString());
197 
198  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
199 
200  txhash = block.vtx[2]->GetHash();
201  block.vtx.clear();
202  block2.vtx.clear();
203  block3.vtx.clear();
204  BOOST_CHECK_EQUAL(pool.mapTx.find(txhash)->GetSharedTx().use_count(), SHARED_TX_OFFSET + 1 - 1); // + 1 because of partialBlock; -1 because of block.
205  }
206  BOOST_CHECK_EQUAL(pool.mapTx.find(txhash)->GetSharedTx().use_count(), SHARED_TX_OFFSET - 1); // -1 because of block
207 }
208 
209 BOOST_AUTO_TEST_CASE(SufficientPreforwardRTTest)
210 {
211  CTxMemPool& pool = *Assert(m_node.mempool);
213  CBlock block(BuildBlockTestCase());
214 
215  LOCK2(cs_main, pool.cs);
216  pool.addUnchecked(entry.FromTx(block.vtx[1]));
217  BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[1]->GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 0);
218 
219  uint256 txhash;
220 
221  // Test with pre-forwarding coinbase + tx 2 with tx 1 in mempool
222  {
223  TestHeaderAndShortIDs shortIDs(block);
224  shortIDs.prefilledtxn.resize(2);
225  shortIDs.prefilledtxn[0] = {0, block.vtx[0]};
226  shortIDs.prefilledtxn[1] = {1, block.vtx[2]}; // id == 1 as it is 1 after index 1
227  shortIDs.shorttxids.resize(1);
228  shortIDs.shorttxids[0] = shortIDs.GetShortID(block.vtx[1]->GetHash());
229 
231  stream << shortIDs;
232 
233  CBlockHeaderAndShortTxIDs shortIDs2;
234  stream >> shortIDs2;
235 
236  PartiallyDownloadedBlock partialBlock(&pool);
237  BOOST_CHECK(partialBlock.InitData(shortIDs2, extra_txn) == READ_STATUS_OK);
238  BOOST_CHECK( partialBlock.IsTxAvailable(0));
239  BOOST_CHECK( partialBlock.IsTxAvailable(1));
240  BOOST_CHECK( partialBlock.IsTxAvailable(2));
241 
242  BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[1]->GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 1);
243 
244  CBlock block2;
245  PartiallyDownloadedBlock partialBlockCopy = partialBlock;
246  BOOST_CHECK(partialBlock.FillBlock(block2, {}) == READ_STATUS_OK);
247  BOOST_CHECK_EQUAL(block.GetHash().ToString(), block2.GetHash().ToString());
248  bool mutated;
251 
252  txhash = block.vtx[1]->GetHash();
253  block.vtx.clear();
254  block2.vtx.clear();
255  BOOST_CHECK_EQUAL(pool.mapTx.find(txhash)->GetSharedTx().use_count(), SHARED_TX_OFFSET + 1 - 1); // + 1 because of partialBlock; -1 because of block.
256  }
257  BOOST_CHECK_EQUAL(pool.mapTx.find(txhash)->GetSharedTx().use_count(), SHARED_TX_OFFSET - 1); // -1 because of block
258 }
259 
260 BOOST_AUTO_TEST_CASE(EmptyBlockRoundTripTest)
261 {
262  CTxMemPool& pool = *Assert(m_node.mempool);
263  CMutableTransaction coinbase;
264  coinbase.vin.resize(1);
265  coinbase.vin[0].scriptSig.resize(10);
266  coinbase.vout.resize(1);
267  coinbase.vout[0].nValue = 42;
268 
269  CBlock block;
270  block.vtx.resize(1);
271  block.vtx[0] = MakeTransactionRef(std::move(coinbase));
272  block.nVersion = 42;
273  block.hashPrevBlock = InsecureRand256();
274  block.nBits = 0x207fffff;
275 
276  bool mutated;
277  block.hashMerkleRoot = BlockMerkleRoot(block, &mutated);
278  assert(!mutated);
279  while (!CheckProofOfWork(block.GetHash(), block.nBits, Params().GetConsensus())) ++block.nNonce;
280 
281  // Test simple header round-trip with only coinbase
282  {
283  CBlockHeaderAndShortTxIDs shortIDs{block};
284 
286  stream << shortIDs;
287 
288  CBlockHeaderAndShortTxIDs shortIDs2;
289  stream >> shortIDs2;
290 
291  PartiallyDownloadedBlock partialBlock(&pool);
292  BOOST_CHECK(partialBlock.InitData(shortIDs2, extra_txn) == READ_STATUS_OK);
293  BOOST_CHECK(partialBlock.IsTxAvailable(0));
294 
295  CBlock block2;
296  std::vector<CTransactionRef> vtx_missing;
297  BOOST_CHECK(partialBlock.FillBlock(block2, vtx_missing) == READ_STATUS_OK);
298  BOOST_CHECK_EQUAL(block.GetHash().ToString(), block2.GetHash().ToString());
301  }
302 }
303 
304 BOOST_AUTO_TEST_CASE(TransactionsRequestSerializationTest) {
306  req1.blockhash = InsecureRand256();
307  req1.indexes.resize(4);
308  req1.indexes[0] = 0;
309  req1.indexes[1] = 1;
310  req1.indexes[2] = 3;
311  req1.indexes[3] = 4;
312 
313  DataStream stream{};
314  stream << req1;
315 
317  stream >> req2;
318 
320  BOOST_CHECK_EQUAL(req1.indexes.size(), req2.indexes.size());
321  BOOST_CHECK_EQUAL(req1.indexes[0], req2.indexes[0]);
322  BOOST_CHECK_EQUAL(req1.indexes[1], req2.indexes[1]);
323  BOOST_CHECK_EQUAL(req1.indexes[2], req2.indexes[2]);
324  BOOST_CHECK_EQUAL(req1.indexes[3], req2.indexes[3]);
325 }
326 
327 BOOST_AUTO_TEST_CASE(TransactionsRequestDeserializationMaxTest) {
328  // Check that the highest legal index is decoded correctly
330  req0.blockhash = InsecureRand256();
331  req0.indexes.resize(1);
332  req0.indexes[0] = 0xffff;
333  DataStream stream{};
334  stream << req0;
335 
337  stream >> req1;
338  BOOST_CHECK_EQUAL(req0.indexes.size(), req1.indexes.size());
339  BOOST_CHECK_EQUAL(req0.indexes[0], req1.indexes[0]);
340 }
341 
342 BOOST_AUTO_TEST_CASE(TransactionsRequestDeserializationOverflowTest) {
343  // Any set of index deltas that starts with N values that sum to (0x10000 - N)
344  // causes the edge-case overflow that was originally not checked for. Such
345  // a request cannot be created by serializing a real BlockTransactionsRequest
346  // due to the overflow, so here we'll serialize from raw deltas.
348  req0.blockhash = InsecureRand256();
349  req0.indexes.resize(3);
350  req0.indexes[0] = 0x7000;
351  req0.indexes[1] = 0x10000 - 0x7000 - 2;
352  req0.indexes[2] = 0;
353  DataStream stream{};
354  stream << req0.blockhash;
355  WriteCompactSize(stream, req0.indexes.size());
356  WriteCompactSize(stream, req0.indexes[0]);
357  WriteCompactSize(stream, req0.indexes[1]);
358  WriteCompactSize(stream, req0.indexes[2]);
359 
361  try {
362  stream >> req1;
363  // before patch: deserialize above succeeds and this check fails, demonstrating the overflow
364  BOOST_CHECK(req1.indexes[1] < req1.indexes[2]);
365  // this shouldn't be reachable before or after patch
366  BOOST_CHECK(0);
367  } catch(std::ios_base::failure &) {
368  // deserialize should fail
369  BOOST_CHECK(true); // Needed to suppress "Test case [...] did not check any assertions"
370  }
371 }
372 
node::NodeContext m_node
Definition: bitcoin-gui.cpp:37
@ 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.
#define Assert(val)
Identity function.
Definition: check.h:73
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: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:316
RecursiveMutex cs
This mutex needs to be locked when accessing mapTx or other members that are guarded by it.
Definition: txmempool.h:405
void removeRecursive(const CTransaction &tx, MemPoolRemovalReason reason) EXCLUSIVE_LOCKS_REQUIRED(cs)
Definition: txmempool.cpp:537
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:488
unsigned long size() const
Definition: txmempool.h:648
Double ended buffer combining vector and stream-like interfaces.
Definition: streams.h:186
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:119
RecursiveMutex cs_main
Mutex to guard access to validation specific variables, such as reading or changing the chainstate.
Definition: cs_main.cpp:7
BOOST_AUTO_TEST_SUITE_END()
bool mutated
uint256 BlockMerkleRoot(const CBlock &block, bool *mutated)
Definition: merkle.cpp:65
#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:125
static CTransactionRef MakeTransactionRef(Tx &&txIn)
Definition: transaction.h:422
@ SER_NETWORK
Definition: serialize.h:131
static Wrapper< Formatter, T & > Using(T &&t)
Cause serialization/deserialization of an object to be done using a specified formatter class.
Definition: serialize.h:433
#define READWRITE(...)
Definition: serialize.h:140
void WriteCompactSize(CSizeComputer &os, uint64_t nSize)
Definition: serialize.h:1103
static uint256 InsecureRand256()
Definition: setup_common.h:75
std::string ToString(const T &t)
Locale-independent version of std::to_string.
Definition: string.h:109
A mutable version of CTransaction.
Definition: transaction.h:380
std::vector< CTxOut > vout
Definition: transaction.h:382
std::vector< CTxIn > vin
Definition: transaction.h:381
Serialization wrapper class for custom integers and enums.
Definition: serialize.h:466
Identical to TestingSetup, but chain set to regtest.
Definition: setup_common.h:122
Definition: txmempool.h:17
CTxMemPoolEntry FromTx(const CMutableTransaction &tx) const
Definition: txmempool.cpp:30
Formatter to serialize/deserialize vector elements using another formatter.
Definition: serialize.h:595
std::unique_ptr< CTxMemPool > mempool
Definition: context.h:50
#define LOCK2(cs1, cs2)
Definition: sync.h:259
@ REPLACED
Removed for replacement.
assert(!tx.IsCoinBase())
static const int PROTOCOL_VERSION
network protocol versioning
Definition: version.h:12