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