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