Bitcoin Core  24.99.0
P2P Digital Currency
blockfilter_index_tests.cpp
Go to the documentation of this file.
1 // Copyright (c) 2017-2021 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 <blockfilter.h>
6 #include <chainparams.h>
7 #include <consensus/merkle.h>
8 #include <consensus/validation.h>
10 #include <interfaces/chain.h>
11 #include <node/miner.h>
12 #include <pow.h>
13 #include <script/standard.h>
14 #include <test/util/blockfilter.h>
15 #include <test/util/setup_common.h>
16 #include <util/time.h>
17 #include <validation.h>
18 
19 #include <boost/test/unit_test.hpp>
20 
23 
24 BOOST_AUTO_TEST_SUITE(blockfilter_index_tests)
25 
27  CBlock CreateBlock(const CBlockIndex* prev, const std::vector<CMutableTransaction>& txns, const CScript& scriptPubKey);
28  bool BuildChain(const CBlockIndex* pindex, const CScript& coinbase_script_pub_key, size_t length, std::vector<std::shared_ptr<CBlock>>& chain);
29 };
30 
31 static bool CheckFilterLookups(BlockFilterIndex& filter_index, const CBlockIndex* block_index,
32  uint256& last_header)
33 {
34  BlockFilter expected_filter;
35  if (!ComputeFilter(filter_index.GetFilterType(), block_index, expected_filter)) {
36  BOOST_ERROR("ComputeFilter failed on block " << block_index->nHeight);
37  return false;
38  }
39 
40  BlockFilter filter;
41  uint256 filter_header;
42  std::vector<BlockFilter> filters;
43  std::vector<uint256> filter_hashes;
44 
45  BOOST_CHECK(filter_index.LookupFilter(block_index, filter));
46  BOOST_CHECK(filter_index.LookupFilterHeader(block_index, filter_header));
47  BOOST_CHECK(filter_index.LookupFilterRange(block_index->nHeight, block_index, filters));
48  BOOST_CHECK(filter_index.LookupFilterHashRange(block_index->nHeight, block_index,
49  filter_hashes));
50 
51  BOOST_CHECK_EQUAL(filters.size(), 1U);
52  BOOST_CHECK_EQUAL(filter_hashes.size(), 1U);
53 
54  BOOST_CHECK_EQUAL(filter.GetHash(), expected_filter.GetHash());
55  BOOST_CHECK_EQUAL(filter_header, expected_filter.ComputeHeader(last_header));
56  BOOST_CHECK_EQUAL(filters[0].GetHash(), expected_filter.GetHash());
57  BOOST_CHECK_EQUAL(filter_hashes[0], expected_filter.GetHash());
58 
59  filters.clear();
60  filter_hashes.clear();
61  last_header = filter_header;
62  return true;
63 }
64 
66  const std::vector<CMutableTransaction>& txns,
67  const CScript& scriptPubKey)
68 {
69  std::unique_ptr<CBlockTemplate> pblocktemplate = BlockAssembler{m_node.chainman->ActiveChainstate(), m_node.mempool.get()}.CreateNewBlock(scriptPubKey);
70  CBlock& block = pblocktemplate->block;
71  block.hashPrevBlock = prev->GetBlockHash();
72  block.nTime = prev->nTime + 1;
73 
74  // Replace mempool-selected txns with just coinbase plus passed-in txns:
75  block.vtx.resize(1);
76  for (const CMutableTransaction& tx : txns) {
77  block.vtx.push_back(MakeTransactionRef(tx));
78  }
79  {
80  CMutableTransaction tx_coinbase{*block.vtx.at(0)};
81  tx_coinbase.vin.at(0).scriptSig = CScript{} << prev->nHeight + 1;
82  block.vtx.at(0) = MakeTransactionRef(std::move(tx_coinbase));
83  block.hashMerkleRoot = BlockMerkleRoot(block);
84  }
85 
86  while (!CheckProofOfWork(block.GetHash(), block.nBits, m_node.chainman->GetConsensus())) ++block.nNonce;
87 
88  return block;
89 }
90 
92  const CScript& coinbase_script_pub_key,
93  size_t length,
94  std::vector<std::shared_ptr<CBlock>>& chain)
95 {
96  std::vector<CMutableTransaction> no_txns;
97 
98  chain.resize(length);
99  for (auto& block : chain) {
100  block = std::make_shared<CBlock>(CreateBlock(pindex, no_txns, coinbase_script_pub_key));
101  CBlockHeader header = block->GetBlockHeader();
102 
103  BlockValidationState state;
104  if (!Assert(m_node.chainman)->ProcessNewBlockHeaders({header}, true, state, &pindex)) {
105  return false;
106  }
107  }
108 
109  return true;
110 }
111 
112 BOOST_FIXTURE_TEST_CASE(blockfilter_index_initial_sync, BuildChainTestingSetup)
113 {
115 
116  uint256 last_header;
117 
118  // Filter should not be found in the index before it is started.
119  {
120  LOCK(cs_main);
121 
122  BlockFilter filter;
123  uint256 filter_header;
124  std::vector<BlockFilter> filters;
125  std::vector<uint256> filter_hashes;
126 
127  for (const CBlockIndex* block_index = m_node.chainman->ActiveChain().Genesis();
128  block_index != nullptr;
129  block_index = m_node.chainman->ActiveChain().Next(block_index)) {
130  BOOST_CHECK(!filter_index.LookupFilter(block_index, filter));
131  BOOST_CHECK(!filter_index.LookupFilterHeader(block_index, filter_header));
132  BOOST_CHECK(!filter_index.LookupFilterRange(block_index->nHeight, block_index, filters));
133  BOOST_CHECK(!filter_index.LookupFilterHashRange(block_index->nHeight, block_index,
134  filter_hashes));
135  }
136  }
137 
138  // BlockUntilSyncedToCurrentChain should return false before index is started.
139  BOOST_CHECK(!filter_index.BlockUntilSyncedToCurrentChain());
140 
141  BOOST_REQUIRE(filter_index.Start());
142 
143  // Allow filter index to catch up with the block index.
144  constexpr int64_t timeout_ms = 10 * 1000;
145  int64_t time_start = GetTimeMillis();
146  while (!filter_index.BlockUntilSyncedToCurrentChain()) {
147  BOOST_REQUIRE(time_start + timeout_ms > GetTimeMillis());
148  UninterruptibleSleep(std::chrono::milliseconds{100});
149  }
150 
151  // Check that filter index has all blocks that were in the chain before it started.
152  {
153  LOCK(cs_main);
154  const CBlockIndex* block_index;
155  for (block_index = m_node.chainman->ActiveChain().Genesis();
156  block_index != nullptr;
157  block_index = m_node.chainman->ActiveChain().Next(block_index)) {
158  CheckFilterLookups(filter_index, block_index, last_header);
159  }
160  }
161 
162  // Create two forks.
163  const CBlockIndex* tip;
164  {
165  LOCK(cs_main);
166  tip = m_node.chainman->ActiveChain().Tip();
167  }
168  CKey coinbase_key_A, coinbase_key_B;
169  coinbase_key_A.MakeNewKey(true);
170  coinbase_key_B.MakeNewKey(true);
171  CScript coinbase_script_pub_key_A = GetScriptForDestination(PKHash(coinbase_key_A.GetPubKey()));
172  CScript coinbase_script_pub_key_B = GetScriptForDestination(PKHash(coinbase_key_B.GetPubKey()));
173  std::vector<std::shared_ptr<CBlock>> chainA, chainB;
174  BOOST_REQUIRE(BuildChain(tip, coinbase_script_pub_key_A, 10, chainA));
175  BOOST_REQUIRE(BuildChain(tip, coinbase_script_pub_key_B, 10, chainB));
176 
177  // Check that new blocks on chain A get indexed.
178  uint256 chainA_last_header = last_header;
179  for (size_t i = 0; i < 2; i++) {
180  const auto& block = chainA[i];
181  BOOST_REQUIRE(Assert(m_node.chainman)->ProcessNewBlock(block, true, true, nullptr));
182  }
183  for (size_t i = 0; i < 2; i++) {
184  const auto& block = chainA[i];
185  const CBlockIndex* block_index;
186  {
187  LOCK(cs_main);
188  block_index = m_node.chainman->m_blockman.LookupBlockIndex(block->GetHash());
189  }
190 
191  BOOST_CHECK(filter_index.BlockUntilSyncedToCurrentChain());
192  CheckFilterLookups(filter_index, block_index, chainA_last_header);
193  }
194 
195  // Reorg to chain B.
196  uint256 chainB_last_header = last_header;
197  for (size_t i = 0; i < 3; i++) {
198  const auto& block = chainB[i];
199  BOOST_REQUIRE(Assert(m_node.chainman)->ProcessNewBlock(block, true, true, nullptr));
200  }
201  for (size_t i = 0; i < 3; i++) {
202  const auto& block = chainB[i];
203  const CBlockIndex* block_index;
204  {
205  LOCK(cs_main);
206  block_index = m_node.chainman->m_blockman.LookupBlockIndex(block->GetHash());
207  }
208 
209  BOOST_CHECK(filter_index.BlockUntilSyncedToCurrentChain());
210  CheckFilterLookups(filter_index, block_index, chainB_last_header);
211  }
212 
213  // Check that filters for stale blocks on A can be retrieved.
214  chainA_last_header = last_header;
215  for (size_t i = 0; i < 2; i++) {
216  const auto& block = chainA[i];
217  const CBlockIndex* block_index;
218  {
219  LOCK(cs_main);
220  block_index = m_node.chainman->m_blockman.LookupBlockIndex(block->GetHash());
221  }
222 
223  BOOST_CHECK(filter_index.BlockUntilSyncedToCurrentChain());
224  CheckFilterLookups(filter_index, block_index, chainA_last_header);
225  }
226 
227  // Reorg back to chain A.
228  for (size_t i = 2; i < 4; i++) {
229  const auto& block = chainA[i];
230  BOOST_REQUIRE(Assert(m_node.chainman)->ProcessNewBlock(block, true, true, nullptr));
231  }
232 
233  // Check that chain A and B blocks can be retrieved.
234  chainA_last_header = last_header;
235  chainB_last_header = last_header;
236  for (size_t i = 0; i < 3; i++) {
237  const CBlockIndex* block_index;
238 
239  {
240  LOCK(cs_main);
241  block_index = m_node.chainman->m_blockman.LookupBlockIndex(chainA[i]->GetHash());
242  }
243  BOOST_CHECK(filter_index.BlockUntilSyncedToCurrentChain());
244  CheckFilterLookups(filter_index, block_index, chainA_last_header);
245 
246  {
247  LOCK(cs_main);
248  block_index = m_node.chainman->m_blockman.LookupBlockIndex(chainB[i]->GetHash());
249  }
250  BOOST_CHECK(filter_index.BlockUntilSyncedToCurrentChain());
251  CheckFilterLookups(filter_index, block_index, chainB_last_header);
252  }
253 
254  // Test lookups for a range of filters/hashes.
255  std::vector<BlockFilter> filters;
256  std::vector<uint256> filter_hashes;
257 
258  {
259  LOCK(cs_main);
260  tip = m_node.chainman->ActiveChain().Tip();
261  }
262  BOOST_CHECK(filter_index.LookupFilterRange(0, tip, filters));
263  BOOST_CHECK(filter_index.LookupFilterHashRange(0, tip, filter_hashes));
264 
265  assert(tip->nHeight >= 0);
266  BOOST_CHECK_EQUAL(filters.size(), tip->nHeight + 1U);
267  BOOST_CHECK_EQUAL(filter_hashes.size(), tip->nHeight + 1U);
268 
269  filters.clear();
270  filter_hashes.clear();
271 
272  filter_index.Interrupt();
273  filter_index.Stop();
274 }
275 
276 BOOST_FIXTURE_TEST_CASE(blockfilter_index_init_destroy, BasicTestingSetup)
277 {
278  BlockFilterIndex* filter_index;
279 
281  BOOST_CHECK(filter_index == nullptr);
282 
284 
286  BOOST_CHECK(filter_index != nullptr);
288 
289  // Initialize returns false if index already exists.
290  BOOST_CHECK(!InitBlockFilterIndex([&]{ return interfaces::MakeChain(m_node); }, BlockFilterType::BASIC, 1 << 20, true, false));
291 
292  int iter_count = 0;
293  ForEachBlockFilterIndex([&iter_count](BlockFilterIndex& _index) { iter_count++; });
294  BOOST_CHECK_EQUAL(iter_count, 1);
295 
297 
298  // Destroy returns false because index was already destroyed.
300 
302  BOOST_CHECK(filter_index == nullptr);
303 
304  // Reinitialize index.
306 
308 
310  BOOST_CHECK(filter_index == nullptr);
311 }
312 
node::NodeContext m_node
Definition: bitcoin-gui.cpp:37
BOOST_FIXTURE_TEST_CASE(blockfilter_index_initial_sync, BuildChainTestingSetup)
static bool CheckFilterLookups(BlockFilterIndex &filter_index, const CBlockIndex *block_index, uint256 &last_header)
bool DestroyBlockFilterIndex(BlockFilterType filter_type)
Destroy the block filter index with the given type.
void DestroyAllBlockFilterIndexes()
Destroy all open block filter indexes.
BlockFilterIndex * GetBlockFilterIndex(BlockFilterType filter_type)
Get a block filter index by type.
void ForEachBlockFilterIndex(std::function< void(BlockFilterIndex &)> fn)
Iterate over all running block filter indexes, invoking fn on each.
bool InitBlockFilterIndex(std::function< std::unique_ptr< interfaces::Chain >()> make_chain, BlockFilterType filter_type, size_t n_cache_size, bool f_memory, bool f_wipe)
Initialize a block filter index for the given type if one does not already exist.
RecursiveMutex cs_main
Mutex to guard access to validation specific variables, such as reading or changing the chainstate.
Definition: validation.cpp:121
#define Assert(val)
Identity function.
Definition: check.h:74
void Stop()
Stops the instance from staying in sync with blockchain updates.
Definition: base.cpp:396
bool Start()
Start initializes the sync state and registers the instance as a ValidationInterface so that it stays...
Definition: base.cpp:377
Complete block filter struct as defined in BIP 157.
Definition: blockfilter.h:112
uint256 ComputeHeader(const uint256 &prev_header) const
Compute the filter header given the previous one.
uint256 GetHash() const
Compute the filter hash.
BlockFilterIndex is used to store and retrieve block filters, hashes, and headers for a range of bloc...
bool LookupFilterRange(int start_height, const CBlockIndex *stop_index, std::vector< BlockFilter > &filters_out) const
Get a range of filters between two heights on a chain.
BlockFilterType GetFilterType() const
bool LookupFilter(const CBlockIndex *block_index, BlockFilter &filter_out) const
Get a single filter by block.
bool LookupFilterHashRange(int start_height, const CBlockIndex *stop_index, std::vector< uint256 > &hashes_out) const
Get a range of filter hashes between two heights on a chain.
bool LookupFilterHeader(const CBlockIndex *block_index, uint256 &header_out) EXCLUSIVE_LOCKS_REQUIRED(!m_cs_headers_cache)
Get a single filter header by block.
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
uint32_t nTime
Definition: block.h:28
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
The block chain is a tree shaped structure starting with the genesis block at the root,...
Definition: chain.h:152
uint32_t nTime
Definition: chain.h:206
uint256 GetBlockHash() const
Definition: chain.h:264
int nHeight
height of the entry in the chain. The genesis block has height 0
Definition: chain.h:164
An encapsulated private key.
Definition: key.h:27
void MakeNewKey(bool fCompressed)
Generate a new private key using a cryptographic PRNG.
Definition: key.cpp:160
CPubKey GetPubKey() const
Compute the public key from a private key.
Definition: key.cpp:187
Serialized script, used inside transaction inputs and outputs.
Definition: script.h:411
Generate a new block, without valid proof-of-work.
Definition: miner.h:129
256-bit opaque blob.
Definition: uint256.h:119
BOOST_AUTO_TEST_SUITE(cuckoocache_tests)
Test Suite for CuckooCache.
BOOST_AUTO_TEST_SUITE_END()
uint256 BlockMerkleRoot(const CBlock &block, bool *mutated)
Definition: merkle.cpp:65
std::unique_ptr< Chain > MakeChain(node::NodeContext &node)
Return implementation of Chain interface.
Definition: interfaces.cpp:783
#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:125
static CTransactionRef MakeTransactionRef(Tx &&txIn)
Definition: transaction.h:415
CScript GetScriptForDestination(const CTxDestination &dest)
Generate a Bitcoin scriptPubKey for the given CTxDestination.
Definition: standard.cpp:334
Basic testing setup.
Definition: setup_common.h:83
node::NodeContext m_node
Definition: setup_common.h:84
bool BuildChain(const CBlockIndex *pindex, const CScript &coinbase_script_pub_key, size_t length, std::vector< std::shared_ptr< CBlock >> &chain)
CBlock CreateBlock(const CBlockIndex *prev, const std::vector< CMutableTransaction > &txns, const CScript &scriptPubKey)
A mutable version of CTransaction.
Definition: transaction.h:373
Testing fixture that pre-creates a 100-block REGTEST-mode block chain.
Definition: setup_common.h:126
std::unique_ptr< CTxMemPool > mempool
Definition: context.h:50
std::unique_ptr< ChainstateManager > chainman
Definition: context.h:54
#define LOCK(cs)
Definition: sync.h:261
bool ComputeFilter(BlockFilterType filter_type, const CBlockIndex *block_index, BlockFilter &filter)
Definition: blockfilter.cpp:14
int64_t GetTimeMillis()
Returns the system time (not mockable)
Definition: time.cpp:107
void UninterruptibleSleep(const std::chrono::microseconds &n)
Definition: time.cpp:25
assert(!tx.IsCoinBase())