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