Bitcoin Core 31.0.0
P2P Digital Currency
Loading...
Searching...
No Matches
blockfilter_index_tests.cpp
Go to the documentation of this file.
1// Copyright (c) 2017-present 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>
11#include <interfaces/chain.h>
12#include <node/miner.h>
13#include <pow.h>
15#include <test/util/common.h>
17#include <validation.h>
18
19#include <boost/test/unit_test.hpp>
20#include <future>
21
25
27
29 CBlock CreateBlock(const CBlockIndex* prev, const std::vector<CMutableTransaction>& txns, const CScript& scriptPubKey);
30 bool BuildChain(const CBlockIndex* pindex, const CScript& coinbase_script_pub_key, size_t length, std::vector<std::shared_ptr<CBlock>>& chain);
31};
32
35{
38 BOOST_ERROR("ComputeFilter failed on block " << block_index->nHeight);
39 return false;
40 }
41
42 BlockFilter filter;
44 std::vector<BlockFilter> filters;
45 std::vector<uint256> filter_hashes;
46
47 BOOST_CHECK(filter_index.LookupFilter(block_index, filter));
52
53 BOOST_CHECK_EQUAL(filters.size(), 1U);
55
56 BOOST_CHECK_EQUAL(filter.GetHash(), expected_filter.GetHash());
58 BOOST_CHECK_EQUAL(filters[0].GetHash(), expected_filter.GetHash());
60
61 filters.clear();
62 filter_hashes.clear();
64 return true;
65}
66
68 const std::vector<CMutableTransaction>& txns,
69 const CScript& scriptPubKey)
70{
71 BlockAssembler::Options options;
72 options.coinbase_output_script = scriptPubKey;
73 options.include_dummy_extranonce = true;
74 std::unique_ptr<CBlockTemplate> pblocktemplate = BlockAssembler{m_node.chainman->ActiveChainstate(), m_node.mempool.get(), options}.CreateNewBlock();
75 CBlock& block = pblocktemplate->block;
76 block.hashPrevBlock = prev->GetBlockHash();
77 block.nTime = prev->nTime + 1;
78
79 // Replace mempool-selected txns with just coinbase plus passed-in txns:
80 block.vtx.resize(1);
81 for (const CMutableTransaction& tx : txns) {
82 block.vtx.push_back(MakeTransactionRef(tx));
83 }
84 {
86 tx_coinbase.nLockTime = static_cast<uint32_t>(prev->nHeight);
87 tx_coinbase.vin.at(0).scriptSig = CScript{} << prev->nHeight + 1;
88 block.vtx.at(0) = MakeTransactionRef(std::move(tx_coinbase));
89 block.hashMerkleRoot = BlockMerkleRoot(block);
90 }
91
92 while (!CheckProofOfWork(block.GetHash(), block.nBits, m_node.chainman->GetConsensus())) ++block.nNonce;
93
94 return block;
95}
96
99 size_t length,
100 std::vector<std::shared_ptr<CBlock>>& chain)
101{
102 std::vector<CMutableTransaction> no_txns;
103
104 chain.resize(length);
105 for (auto& block : chain) {
106 block = std::make_shared<CBlock>(CreateBlock(pindex, no_txns, coinbase_script_pub_key));
107
109 if (!Assert(m_node.chainman)->ProcessNewBlockHeaders({{*block}}, true, state, &pindex)) {
110 return false;
111 }
112 }
113
114 return true;
115}
116
118{
120 BOOST_REQUIRE(filter_index.Init());
121
123
124 // Filter should not be found in the index before it is started.
125 {
126 LOCK(cs_main);
127
128 BlockFilter filter;
130 std::vector<BlockFilter> filters;
131 std::vector<uint256> filter_hashes;
132
133 for (const CBlockIndex* block_index = m_node.chainman->ActiveChain().Genesis();
134 block_index != nullptr;
135 block_index = m_node.chainman->ActiveChain().Next(block_index)) {
136 BOOST_CHECK(!filter_index.LookupFilter(block_index, filter));
141 }
142 }
143
144 // BlockUntilSyncedToCurrentChain should return false before index is started.
145 BOOST_CHECK(!filter_index.BlockUntilSyncedToCurrentChain());
146
147 filter_index.Sync();
148
149 // Check that filter index has all blocks that were in the chain before it started.
150 {
151 LOCK(cs_main);
153 for (block_index = m_node.chainman->ActiveChain().Genesis();
154 block_index != nullptr;
155 block_index = m_node.chainman->ActiveChain().Next(block_index)) {
156 CheckFilterLookups(filter_index, block_index, last_header, m_node.chainman->m_blockman);
157 }
158 }
159
160 // Create two forks.
161 const CBlockIndex* tip;
162 {
163 LOCK(cs_main);
164 tip = m_node.chainman->ActiveChain().Tip();
165 }
170 std::vector<std::shared_ptr<CBlock>> chainA, chainB;
173
174 // Check that new blocks on chain A get indexed.
176 for (size_t i = 0; i < 2; i++) {
177 const auto& block = chainA[i];
178 BOOST_REQUIRE(Assert(m_node.chainman)->ProcessNewBlock(block, true, true, nullptr));
179 }
180 for (size_t i = 0; i < 2; i++) {
181 const auto& block = chainA[i];
183 {
184 LOCK(cs_main);
185 block_index = m_node.chainman->m_blockman.LookupBlockIndex(block->GetHash());
186 }
187
188 BOOST_CHECK(filter_index.BlockUntilSyncedToCurrentChain());
190 }
191
192 // Reorg to chain B.
194 for (size_t i = 0; i < 3; i++) {
195 const auto& block = chainB[i];
196 BOOST_REQUIRE(Assert(m_node.chainman)->ProcessNewBlock(block, true, true, nullptr));
197 }
198 for (size_t i = 0; i < 3; i++) {
199 const auto& block = chainB[i];
201 {
202 LOCK(cs_main);
203 block_index = m_node.chainman->m_blockman.LookupBlockIndex(block->GetHash());
204 }
205
206 BOOST_CHECK(filter_index.BlockUntilSyncedToCurrentChain());
208 }
209
210 // Check that filters for stale blocks on A can be retrieved.
212 for (size_t i = 0; i < 2; i++) {
213 const auto& block = chainA[i];
215 {
216 LOCK(cs_main);
217 block_index = m_node.chainman->m_blockman.LookupBlockIndex(block->GetHash());
218 }
219
220 BOOST_CHECK(filter_index.BlockUntilSyncedToCurrentChain());
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(block, true, true, nullptr));
228 }
229
230 // Check that chain A and B blocks can be retrieved.
233 for (size_t i = 0; i < 3; i++) {
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());
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());
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));
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
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;
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
311{
312private:
313 std::unique_ptr<BaseIndex::DB> m_db;
314 std::shared_future<void> m_blocker;
316
317public:
318 explicit IndexReorgCrash(std::unique_ptr<interfaces::Chain> chain, std::shared_future<void> blocker,
319 int blocking_height) : BaseIndex(std::move(chain), "test index"), m_blocker(blocker),
321 {
322 const fs::path path = gArgs.GetDataDirNet() / "index";
323 fs::create_directories(path);
324 m_db = std::make_unique<BaseIndex::DB>(path / "db", /*n_cache_size=*/0, /*f_memory=*/true, /*f_wipe=*/false);
325 }
326
327 bool AllowPrune() const override { return false; }
328 BaseIndex::DB& GetDB() const override { return *m_db; }
329
330 bool CustomAppend(const interfaces::BlockInfo& block) override
331 {
332 // Simulate a delay so new blocks can get connected during the initial sync
333 if (block.height == m_blocking_height) m_blocker.wait();
334
335 // Move mock time forward so the best index gets updated only when we are not at the blocking height
336 if (block.height == m_blocking_height - 1 || block.height > m_blocking_height) {
338 }
339
340 return true;
341 }
342};
343
345{
346 // Enable mock time
348
349 std::promise<void> promise;
350 std::shared_future<void> blocker(promise.get_future());
351 int blocking_height = WITH_LOCK(cs_main, return m_node.chainman->ActiveChain().Tip()->nHeight);
352
354 BOOST_REQUIRE(index.Init());
356
357 auto func_wait_until = [&](int height, std::chrono::milliseconds timeout) {
358 auto deadline = std::chrono::steady_clock::now() + timeout;
359 while (index.GetSummary().best_block_height < height) {
360 if (std::chrono::steady_clock::now() > deadline) {
361 BOOST_FAIL(strprintf("Timeout waiting for index height %d (current: %d)", height, index.GetSummary().best_block_height));
362 return;
363 }
364 std::this_thread::sleep_for(100ms);
365 }
366 };
367
368 // Wait until the index is one block before the fork point
369 func_wait_until(blocking_height - 1, /*timeout=*/5s);
370
371 // Create a fork to trigger the reorg
372 std::vector<std::shared_ptr<CBlock>> fork;
373 const CBlockIndex* prev_tip = WITH_LOCK(cs_main, return m_node.chainman->ActiveChain().Tip()->pprev);
375
376 for (const auto& block : fork) {
377 BOOST_REQUIRE(m_node.chainman->ProcessNewBlock(block, /*force_processing=*/true, /*min_pow_checked=*/true, nullptr));
378 }
379
380 // Unblock the index thread so it can process the reorg
381 promise.set_value();
382 // Wait for the index to reach the new tip
384 index.Stop();
385}
386
CScript GetScriptForDestination(const CTxDestination &dest)
Generate a Bitcoin scriptPubKey for the given CTxDestination.
ArgsManager gArgs
Definition args.cpp:40
node::NodeContext m_node
static bool CheckFilterLookups(BlockFilterIndex &filter_index, const CBlockIndex *block_index, uint256 &last_header, const BlockManager &blockman)
BOOST_FIXTURE_TEST_CASE(blockfilter_index_initial_sync, BuildChainTestingSetup)
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.
#define Assert(val)
Identity function.
Definition check.h:113
fs::path GetDataDirNet() const
Get data directory path with appended network identifier.
Definition args.h:239
The database stores a block locator of the chain the database is synced to so that the index can effi...
Definition base.h:65
Base class for indices of blockchain data.
Definition base.h:55
void Stop()
Stops the instance from staying in sync with blockchain updates.
Definition base.cpp:461
bool Init()
Initializes the sync state and registers the instance to the validation interface so that it stays in...
Definition base.cpp:104
bool BlockUntilSyncedToCurrentChain() const LOCKS_EXCLUDED(void Interrupt()
Blocks the current thread until the index is caught up to the current state of the block chain.
Definition base.cpp:448
IndexSummary GetSummary() const
Get a summary of the index and its state.
Definition base.cpp:472
void Sync()
Sync the index with the block index starting from the current best block.
Definition base.cpp:201
bool StartBackgroundSync()
Starts the initial sync process on a background thread.
Definition base.cpp:453
Complete block filter struct as defined in BIP 157.
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.
uint32_t nNonce
Definition block.h:35
uint32_t nBits
Definition block.h:34
uint32_t nTime
Definition block.h:33
uint256 hashPrevBlock
Definition block.h:31
uint256 hashMerkleRoot
Definition block.h:32
uint256 GetHash() const
Definition block.cpp:15
Definition block.h:74
std::vector< CTransactionRef > vtx
Definition block.h:77
The block chain is a tree shaped structure starting with the genesis block at the root,...
Definition chain.h:94
An encapsulated private key.
Definition key.h:36
Serialized script, used inside transaction inputs and outputs.
Definition script.h:405
bool CustomAppend(const interfaces::BlockInfo &block) override
Write update index entries for a newly connected block.
std::unique_ptr< BaseIndex::DB > m_db
BaseIndex::DB & GetDB() const override
IndexReorgCrash(std::unique_ptr< interfaces::Chain > chain, std::shared_future< void > blocker, int blocking_height)
bool AllowPrune() const override
std::shared_future< void > m_blocker
Generate a new block, without valid proof-of-work.
Definition miner.h:61
Maintains a tree of blocks (stored in m_block_index) which is consulted to determine where the most-w...
256-bit opaque blob.
Definition uint256.h:195
uint256 BlockMerkleRoot(const CBlock &block, bool *mutated)
Definition merkle.cpp:66
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()
CKey GenerateRandomKey(bool compressed) noexcept
Definition key.cpp:475
BOOST_FAIL("Test unconditionally fails.")
std::unique_ptr< Chain > MakeChain(node::NodeContext &node)
Return implementation of Chain interface.
Definition common.h:29
#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:140
static CTransactionRef MakeTransactionRef(Tx &&txIn)
static bool GetPubKey(const SigningProvider &provider, const SignatureData &sigdata, const CKeyID &address, CPubKey &pubkey)
Definition sign.cpp:221
Basic testing setup.
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.
int best_block_height
Definition base.h:33
Testing fixture that pre-creates a 100-block REGTEST-mode block chain.
Block data sent with blockConnected, blockDisconnected notifications.
Definition chain.h:19
std::unique_ptr< CTxMemPool > mempool
Definition context.h:68
std::unique_ptr< ChainstateManager > chainman
Definition context.h:72
#define LOCK(cs)
Definition sync.h:258
#define WITH_LOCK(cs, code)
Run code while locking a mutex.
Definition sync.h:289
bool ComputeFilter(BlockFilterType filter_type, const CBlockIndex &block_index, BlockFilter &filter, const BlockManager &blockman)
#define strprintf
Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for...
void SetMockTime(int64_t nMockTimeIn)
DEPRECATED Use SetMockTime with chrono type.
Definition time.cpp:44
constexpr auto Ticks(Dur2 d)
Helper to count the seconds of a duration/time_point.
Definition time.h:73
assert(!tx.IsCoinBase())