Bitcoin Core  22.99.0
P2P Digital Currency
blockfilter_index_tests.cpp
Go to the documentation of this file.
1 // Copyright (c) 2017-2020 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 <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 
19 BOOST_AUTO_TEST_SUITE(blockfilter_index_tests)
20 
22  CBlock CreateBlock(const CBlockIndex* prev, const std::vector<CMutableTransaction>& txns, const CScript& scriptPubKey);
23  bool BuildChain(const CBlockIndex* pindex, const CScript& coinbase_script_pub_key, size_t length, std::vector<std::shared_ptr<CBlock>>& chain);
24 };
25 
26 static bool CheckFilterLookups(BlockFilterIndex& filter_index, const CBlockIndex* block_index,
27  uint256& last_header)
28 {
29  BlockFilter expected_filter;
30  if (!ComputeFilter(filter_index.GetFilterType(), block_index, expected_filter)) {
31  BOOST_ERROR("ComputeFilter failed on block " << block_index->nHeight);
32  return false;
33  }
34 
35  BlockFilter filter;
36  uint256 filter_header;
37  std::vector<BlockFilter> filters;
38  std::vector<uint256> filter_hashes;
39 
40  BOOST_CHECK(filter_index.LookupFilter(block_index, filter));
41  BOOST_CHECK(filter_index.LookupFilterHeader(block_index, filter_header));
42  BOOST_CHECK(filter_index.LookupFilterRange(block_index->nHeight, block_index, filters));
43  BOOST_CHECK(filter_index.LookupFilterHashRange(block_index->nHeight, block_index,
44  filter_hashes));
45 
46  BOOST_CHECK_EQUAL(filters.size(), 1U);
47  BOOST_CHECK_EQUAL(filter_hashes.size(), 1U);
48 
49  BOOST_CHECK_EQUAL(filter.GetHash(), expected_filter.GetHash());
50  BOOST_CHECK_EQUAL(filter_header, expected_filter.ComputeHeader(last_header));
51  BOOST_CHECK_EQUAL(filters[0].GetHash(), expected_filter.GetHash());
52  BOOST_CHECK_EQUAL(filter_hashes[0], expected_filter.GetHash());
53 
54  filters.clear();
55  filter_hashes.clear();
56  last_header = filter_header;
57  return true;
58 }
59 
61  const std::vector<CMutableTransaction>& txns,
62  const CScript& scriptPubKey)
63 {
64  const CChainParams& chainparams = Params();
65  std::unique_ptr<CBlockTemplate> pblocktemplate = BlockAssembler(m_node.chainman->ActiveChainstate(), *m_node.mempool, chainparams).CreateNewBlock(scriptPubKey);
66  CBlock& block = pblocktemplate->block;
67  block.hashPrevBlock = prev->GetBlockHash();
68  block.nTime = prev->nTime + 1;
69 
70  // Replace mempool-selected txns with just coinbase plus passed-in txns:
71  block.vtx.resize(1);
72  for (const CMutableTransaction& tx : txns) {
73  block.vtx.push_back(MakeTransactionRef(tx));
74  }
75  // IncrementExtraNonce creates a valid coinbase and merkleRoot
76  unsigned int extraNonce = 0;
77  IncrementExtraNonce(&block, prev, extraNonce);
78 
79  while (!CheckProofOfWork(block.GetHash(), block.nBits, chainparams.GetConsensus())) ++block.nNonce;
80 
81  return block;
82 }
83 
85  const CScript& coinbase_script_pub_key,
86  size_t length,
87  std::vector<std::shared_ptr<CBlock>>& chain)
88 {
89  std::vector<CMutableTransaction> no_txns;
90 
91  chain.resize(length);
92  for (auto& block : chain) {
93  block = std::make_shared<CBlock>(CreateBlock(pindex, no_txns, coinbase_script_pub_key));
94  CBlockHeader header = block->GetBlockHeader();
95 
97  if (!Assert(m_node.chainman)->ProcessNewBlockHeaders({header}, state, Params(), &pindex)) {
98  return false;
99  }
100  }
101 
102  return true;
103 }
104 
105 BOOST_FIXTURE_TEST_CASE(blockfilter_index_initial_sync, BuildChainTestingSetup)
106 {
107  BlockFilterIndex filter_index(BlockFilterType::BASIC, 1 << 20, true);
108 
109  uint256 last_header;
110 
111  // Filter should not be found in the index before it is started.
112  {
113  LOCK(cs_main);
114 
115  BlockFilter filter;
116  uint256 filter_header;
117  std::vector<BlockFilter> filters;
118  std::vector<uint256> filter_hashes;
119 
120  for (const CBlockIndex* block_index = m_node.chainman->ActiveChain().Genesis();
121  block_index != nullptr;
122  block_index = m_node.chainman->ActiveChain().Next(block_index)) {
123  BOOST_CHECK(!filter_index.LookupFilter(block_index, filter));
124  BOOST_CHECK(!filter_index.LookupFilterHeader(block_index, filter_header));
125  BOOST_CHECK(!filter_index.LookupFilterRange(block_index->nHeight, block_index, filters));
126  BOOST_CHECK(!filter_index.LookupFilterHashRange(block_index->nHeight, block_index,
127  filter_hashes));
128  }
129  }
130 
131  // BlockUntilSyncedToCurrentChain should return false before index is started.
132  BOOST_CHECK(!filter_index.BlockUntilSyncedToCurrentChain());
133 
134  BOOST_REQUIRE(filter_index.Start(m_node.chainman->ActiveChainstate()));
135 
136  // Allow filter index to catch up with the block index.
137  constexpr int64_t timeout_ms = 10 * 1000;
138  int64_t time_start = GetTimeMillis();
139  while (!filter_index.BlockUntilSyncedToCurrentChain()) {
140  BOOST_REQUIRE(time_start + timeout_ms > GetTimeMillis());
141  UninterruptibleSleep(std::chrono::milliseconds{100});
142  }
143 
144  // Check that filter index has all blocks that were in the chain before it started.
145  {
146  LOCK(cs_main);
147  const CBlockIndex* block_index;
148  for (block_index = m_node.chainman->ActiveChain().Genesis();
149  block_index != nullptr;
150  block_index = m_node.chainman->ActiveChain().Next(block_index)) {
151  CheckFilterLookups(filter_index, block_index, last_header);
152  }
153  }
154 
155  // Create two forks.
156  const CBlockIndex* tip;
157  {
158  LOCK(cs_main);
159  tip = m_node.chainman->ActiveChain().Tip();
160  }
161  CKey coinbase_key_A, coinbase_key_B;
162  coinbase_key_A.MakeNewKey(true);
163  coinbase_key_B.MakeNewKey(true);
164  CScript coinbase_script_pub_key_A = GetScriptForDestination(PKHash(coinbase_key_A.GetPubKey()));
165  CScript coinbase_script_pub_key_B = GetScriptForDestination(PKHash(coinbase_key_B.GetPubKey()));
166  std::vector<std::shared_ptr<CBlock>> chainA, chainB;
167  BOOST_REQUIRE(BuildChain(tip, coinbase_script_pub_key_A, 10, chainA));
168  BOOST_REQUIRE(BuildChain(tip, coinbase_script_pub_key_B, 10, chainB));
169 
170  // Check that new blocks on chain A get indexed.
171  uint256 chainA_last_header = last_header;
172  for (size_t i = 0; i < 2; i++) {
173  const auto& block = chainA[i];
174  BOOST_REQUIRE(Assert(m_node.chainman)->ProcessNewBlock(Params(), block, true, nullptr));
175  }
176  for (size_t i = 0; i < 2; i++) {
177  const auto& block = chainA[i];
178  const CBlockIndex* block_index;
179  {
180  LOCK(cs_main);
181  block_index = m_node.chainman->m_blockman.LookupBlockIndex(block->GetHash());
182  }
183 
184  BOOST_CHECK(filter_index.BlockUntilSyncedToCurrentChain());
185  CheckFilterLookups(filter_index, block_index, chainA_last_header);
186  }
187 
188  // Reorg to chain B.
189  uint256 chainB_last_header = last_header;
190  for (size_t i = 0; i < 3; i++) {
191  const auto& block = chainB[i];
192  BOOST_REQUIRE(Assert(m_node.chainman)->ProcessNewBlock(Params(), block, true, nullptr));
193  }
194  for (size_t i = 0; i < 3; i++) {
195  const auto& block = chainB[i];
196  const CBlockIndex* block_index;
197  {
198  LOCK(cs_main);
199  block_index = m_node.chainman->m_blockman.LookupBlockIndex(block->GetHash());
200  }
201 
202  BOOST_CHECK(filter_index.BlockUntilSyncedToCurrentChain());
203  CheckFilterLookups(filter_index, block_index, chainB_last_header);
204  }
205 
206  // Check that filters for stale blocks on A can be retrieved.
207  chainA_last_header = last_header;
208  for (size_t i = 0; i < 2; i++) {
209  const auto& block = chainA[i];
210  const CBlockIndex* block_index;
211  {
212  LOCK(cs_main);
213  block_index = m_node.chainman->m_blockman.LookupBlockIndex(block->GetHash());
214  }
215 
216  BOOST_CHECK(filter_index.BlockUntilSyncedToCurrentChain());
217  CheckFilterLookups(filter_index, block_index, chainA_last_header);
218  }
219 
220  // Reorg back to chain A.
221  for (size_t i = 2; i < 4; i++) {
222  const auto& block = chainA[i];
223  BOOST_REQUIRE(Assert(m_node.chainman)->ProcessNewBlock(Params(), block, true, nullptr));
224  }
225 
226  // Check that chain A and B blocks can be retrieved.
227  chainA_last_header = last_header;
228  chainB_last_header = last_header;
229  for (size_t i = 0; i < 3; i++) {
230  const CBlockIndex* block_index;
231 
232  {
233  LOCK(cs_main);
234  block_index = m_node.chainman->m_blockman.LookupBlockIndex(chainA[i]->GetHash());
235  }
236  BOOST_CHECK(filter_index.BlockUntilSyncedToCurrentChain());
237  CheckFilterLookups(filter_index, block_index, chainA_last_header);
238 
239  {
240  LOCK(cs_main);
241  block_index = m_node.chainman->m_blockman.LookupBlockIndex(chainB[i]->GetHash());
242  }
243  BOOST_CHECK(filter_index.BlockUntilSyncedToCurrentChain());
244  CheckFilterLookups(filter_index, block_index, chainB_last_header);
245  }
246 
247  // Test lookups for a range of filters/hashes.
248  std::vector<BlockFilter> filters;
249  std::vector<uint256> filter_hashes;
250 
251  {
252  LOCK(cs_main);
253  tip = m_node.chainman->ActiveChain().Tip();
254  }
255  BOOST_CHECK(filter_index.LookupFilterRange(0, tip, filters));
256  BOOST_CHECK(filter_index.LookupFilterHashRange(0, tip, filter_hashes));
257 
258  assert(tip->nHeight >= 0);
259  BOOST_CHECK_EQUAL(filters.size(), tip->nHeight + 1U);
260  BOOST_CHECK_EQUAL(filter_hashes.size(), tip->nHeight + 1U);
261 
262  filters.clear();
263  filter_hashes.clear();
264 
265  filter_index.Interrupt();
266  filter_index.Stop();
267 }
268 
269 BOOST_FIXTURE_TEST_CASE(blockfilter_index_init_destroy, BasicTestingSetup)
270 {
271  BlockFilterIndex* filter_index;
272 
274  BOOST_CHECK(filter_index == nullptr);
275 
277 
279  BOOST_CHECK(filter_index != nullptr);
281 
282  // Initialize returns false if index already exists.
284 
285  int iter_count = 0;
286  ForEachBlockFilterIndex([&iter_count](BlockFilterIndex& _index) { iter_count++; });
287  BOOST_CHECK_EQUAL(iter_count, 1);
288 
290 
291  // Destroy returns false because index was already destroyed.
293 
295  BOOST_CHECK(filter_index == nullptr);
296 
297  // Reinitialize index.
299 
301 
303  BOOST_CHECK(filter_index == nullptr);
304 }
305 
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:192
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:140
ComputeFilter
bool ComputeFilter(BlockFilterType filter_type, const CBlockIndex *block_index, BlockFilter &filter)
Definition: blockfilter.cpp:12
setup_common.h
NodeContext::mempool
std::unique_ptr< CTxMemPool > mempool
Definition: context.h:44
blockfilterindex.h
GetScriptForDestination
CScript GetScriptForDestination(const CTxDestination &dest)
Generate a Bitcoin scriptPubKey for the given CTxDestination.
Definition: standard.cpp:351
m_node
NodeContext & m_node
Definition: bitcoin-node.cpp:38
CBlockIndex::nHeight
int nHeight
height of the entry in the chain. The genesis block has height 0
Definition: chain.h:150
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:298
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:387
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
BlockAssembler
Generate a new block, without valid proof-of-work.
Definition: miner.h:126
BuildChainTestingSetup::CreateBlock
CBlock CreateBlock(const CBlockIndex *prev, const std::vector< CMutableTransaction > &txns, const CScript &scriptPubKey)
Definition: blockfilter_index_tests.cpp:60
DestroyBlockFilterIndex
bool DestroyBlockFilterIndex(BlockFilterType filter_type)
Destroy the block filter index with the given type.
Definition: blockfilterindex.cpp:473
chainparams.h
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
BuildChainTestingSetup
Definition: blockfilter_index_tests.cpp:21
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:355
miner.h
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:415
standard.h
BasicTestingSetup
Basic testing setup.
Definition: setup_common.h:76
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:116
uint256
256-bit opaque blob.
Definition: uint256.h:124
CKey::GetPubKey
CPubKey GetPubKey() const
Compute the public key from a private key.
Definition: key.cpp:187
blockfilter.h
CScript
Serialized script, used inside transaction inputs and outputs.
Definition: script.h:405
CBlockIndex::GetBlockHash
uint256 GetBlockHash() const
Definition: chain.h:246
BlockFilterType::BASIC
@ BASIC
BlockFilter::GetHash
uint256 GetHash() const
Compute the filter hash.
Definition: blockfilter.cpp:289
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:84
BasicTestingSetup::m_node
NodeContext m_node
Definition: setup_common.h:78
BOOST_FIXTURE_TEST_CASE
BOOST_FIXTURE_TEST_CASE(blockfilter_index_initial_sync, BuildChainTestingSetup)
Definition: blockfilter_index_tests.cpp:105
BlockAssembler::CreateNewBlock
std::unique_ptr< CBlockTemplate > CreateNewBlock(const CScript &scriptPubKeyIn)
Construct a new block template with coinbase to scriptPubKeyIn.
Definition: miner.cpp:102
CheckFilterLookups
static bool CheckFilterLookups(BlockFilterIndex &filter_index, const CBlockIndex *block_index, uint256 &last_header)
Definition: blockfilter_index_tests.cpp:26
CBlock::vtx
std::vector< CTransactionRef > vtx
Definition: block.h:66
CKey
An encapsulated private key.
Definition: key.h:26
cs_main
RecursiveMutex cs_main
Mutex to guard access to validation specific variables, such as reading or changing the chainstate.
Definition: validation.cpp:116
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:435
NodeContext::chainman
std::unique_ptr< ChainstateManager > chainman
Definition: context.h:47
GetBlockFilterIndex
BlockFilterIndex * GetBlockFilterIndex(BlockFilterType filter_type)
Get a block filter index by type.
Definition: blockfilterindex.cpp:452
ForEachBlockFilterIndex
void ForEachBlockFilterIndex(std::function< void(BlockFilterIndex &)> fn)
Iterate over all running block filter indexes, invoking fn on each.
Definition: blockfilterindex.cpp:458
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:375
Params
const CChainParams & Params()
Return the currently selected parameters.
Definition: chainparams.cpp:536
IncrementExtraNonce
void IncrementExtraNonce(CBlock *pblock, const CBlockIndex *pindexPrev, unsigned int &nExtraNonce)
Modify the extranonce in a block.
Definition: miner.cpp:438
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:463
BlockFilterIndex::GetFilterType
BlockFilterType GetFilterType() const
Definition: blockfilterindex.h:59
CMutableTransaction
A mutable version of CTransaction.
Definition: transaction.h:344
CBlockIndex
The block chain is a tree shaped structure starting with the genesis block at the root,...
Definition: chain.h:137
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:341
BlockFilterIndex::LookupFilterHeader
bool LookupFilterHeader(const CBlockIndex *block_index, uint256 &header_out)
Get a single filter header by block.
Definition: blockfilterindex.cpp:385
DestroyAllBlockFilterIndexes
void DestroyAllBlockFilterIndexes()
Destroy all open block filter indexes.
Definition: blockfilterindex.cpp:478
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