Bitcoin Core 31.99.0
P2P Digital Currency
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 <chain.h>
9#include <interfaces/chain.h>
10#include <key.h>
11#include <node/blockstorage.h>
12#include <primitives/block.h>
13#include <script/script.h>
14#include <sync.h>
16#include <test/util/common.h>
17#include <test/util/mining.h>
19#include <uint256.h>
20#include <util/check.h>
21#include <validation.h>
22
23#include <boost/test/unit_test.hpp>
24
25#include <compare>
26#include <cstddef>
27#include <cstdint>
28#include <functional>
29#include <memory>
30#include <span>
31#include <string>
32#include <utility>
33#include <vector>
34
36
37BOOST_AUTO_TEST_SUITE(blockfilter_index_tests)
38
39static bool CheckFilterLookups(BlockFilterIndex& filter_index, const CBlockIndex* block_index,
40 uint256& last_header, const BlockManager& blockman)
41{
42 BlockFilter expected_filter;
43 if (!ComputeFilter(filter_index.GetFilterType(), *block_index, expected_filter, blockman)) {
44 BOOST_ERROR("ComputeFilter failed on block " << block_index->nHeight);
45 return false;
46 }
47
48 BlockFilter filter;
49 uint256 filter_header;
50 std::vector<BlockFilter> filters;
51 std::vector<uint256> filter_hashes;
52
53 BOOST_CHECK(filter_index.LookupFilter(block_index, filter));
54 BOOST_CHECK(filter_index.LookupFilterHeader(block_index, filter_header));
55 BOOST_CHECK(filter_index.LookupFilterRange(block_index->nHeight, block_index, filters));
56 BOOST_CHECK(filter_index.LookupFilterHashRange(block_index->nHeight, block_index,
57 filter_hashes));
58
59 BOOST_CHECK_EQUAL(filters.size(), 1U);
60 BOOST_CHECK_EQUAL(filter_hashes.size(), 1U);
61
62 BOOST_CHECK_EQUAL(filter.GetHash(), expected_filter.GetHash());
63 BOOST_CHECK_EQUAL(filter_header, expected_filter.ComputeHeader(last_header));
64 BOOST_CHECK_EQUAL(filters[0].GetHash(), expected_filter.GetHash());
65 BOOST_CHECK_EQUAL(filter_hashes[0], expected_filter.GetHash());
66
67 filters.clear();
68 filter_hashes.clear();
69 last_header = filter_header;
70 return true;
71}
72
73BOOST_FIXTURE_TEST_CASE(blockfilter_index_initial_sync, TestChain100Setup)
74{
76 BOOST_REQUIRE(filter_index.Init());
77
78 uint256 last_header;
79
80 // Filter should not be found in the index before it is started.
81 {
83
84 BlockFilter filter;
85 uint256 filter_header;
86 std::vector<BlockFilter> filters;
87 std::vector<uint256> filter_hashes;
88
89 for (const CBlockIndex* block_index = m_node.chainman->ActiveChain().Genesis();
90 block_index != nullptr;
91 block_index = m_node.chainman->ActiveChain().Next(*block_index)) {
92 BOOST_CHECK(!filter_index.LookupFilter(block_index, filter));
93 BOOST_CHECK(!filter_index.LookupFilterHeader(block_index, filter_header));
94 BOOST_CHECK(!filter_index.LookupFilterRange(block_index->nHeight, block_index, filters));
95 BOOST_CHECK(!filter_index.LookupFilterHashRange(block_index->nHeight, block_index,
96 filter_hashes));
97 }
98 }
99
100 // BlockUntilSyncedToCurrentChain should return false before index is started.
101 BOOST_CHECK(!filter_index.BlockUntilSyncedToCurrentChain());
102
103 filter_index.Sync();
104
105 // Check that filter index has all blocks that were in the chain before it started.
106 {
107 LOCK(cs_main);
108 const CBlockIndex* block_index;
109 for (block_index = m_node.chainman->ActiveChain().Genesis();
110 block_index != nullptr;
111 block_index = m_node.chainman->ActiveChain().Next(*block_index)) {
112 CheckFilterLookups(filter_index, block_index, last_header, m_node.chainman->m_blockman);
113 }
114 }
115
116 // Create two forks.
117 const CBlockIndex* tip;
118 {
119 LOCK(cs_main);
120 tip = m_node.chainman->ActiveChain().Tip();
121 }
122 CKey coinbase_key_A = GenerateRandomKey();
123 CKey coinbase_key_B = GenerateRandomKey();
124 CScript coinbase_script_pub_key_A = GetScriptForDestination(PKHash(coinbase_key_A.GetPubKey()));
125 CScript coinbase_script_pub_key_B = GetScriptForDestination(PKHash(coinbase_key_B.GetPubKey()));
126 std::vector<std::shared_ptr<CBlock>> chainA, chainB;
127 BOOST_REQUIRE(BuildChain(m_node, tip, coinbase_script_pub_key_A, 10, chainA));
128 BOOST_REQUIRE(BuildChain(m_node, tip, coinbase_script_pub_key_B, 10, chainB));
129
130 // Check that new blocks on chain A get indexed.
131 uint256 chainA_last_header = last_header;
132 for (size_t i = 0; i < 2; i++) {
133 const auto& block = chainA[i];
134 BOOST_REQUIRE(Assert(m_node.chainman)->ProcessNewBlock(block, true, true, nullptr));
135 }
136 for (size_t i = 0; i < 2; i++) {
137 const auto& block = chainA[i];
138 const CBlockIndex* block_index;
139 {
140 LOCK(cs_main);
141 block_index = m_node.chainman->m_blockman.LookupBlockIndex(block->GetHash());
142 }
143
144 BOOST_CHECK(filter_index.BlockUntilSyncedToCurrentChain());
145 CheckFilterLookups(filter_index, block_index, chainA_last_header, m_node.chainman->m_blockman);
146 }
147
148 // Reorg to chain B.
149 uint256 chainB_last_header = last_header;
150 for (size_t i = 0; i < 3; i++) {
151 const auto& block = chainB[i];
152 BOOST_REQUIRE(Assert(m_node.chainman)->ProcessNewBlock(block, true, true, nullptr));
153 }
154 for (size_t i = 0; i < 3; i++) {
155 const auto& block = chainB[i];
156 const CBlockIndex* block_index;
157 {
158 LOCK(cs_main);
159 block_index = m_node.chainman->m_blockman.LookupBlockIndex(block->GetHash());
160 }
161
162 BOOST_CHECK(filter_index.BlockUntilSyncedToCurrentChain());
163 CheckFilterLookups(filter_index, block_index, chainB_last_header, m_node.chainman->m_blockman);
164 }
165
166 // Check that filters for stale blocks on A can be retrieved.
167 chainA_last_header = last_header;
168 for (size_t i = 0; i < 2; i++) {
169 const auto& block = chainA[i];
170 const CBlockIndex* block_index;
171 {
172 LOCK(cs_main);
173 block_index = m_node.chainman->m_blockman.LookupBlockIndex(block->GetHash());
174 }
175
176 BOOST_CHECK(filter_index.BlockUntilSyncedToCurrentChain());
177 CheckFilterLookups(filter_index, block_index, chainA_last_header, m_node.chainman->m_blockman);
178 }
179
180 // Reorg back to chain A.
181 for (size_t i = 2; i < 4; i++) {
182 const auto& block = chainA[i];
183 BOOST_REQUIRE(Assert(m_node.chainman)->ProcessNewBlock(block, true, true, nullptr));
184 }
185
186 // Check that chain A and B blocks can be retrieved.
187 chainA_last_header = last_header;
188 chainB_last_header = last_header;
189 for (size_t i = 0; i < 3; i++) {
190 const CBlockIndex* block_index;
191
192 {
193 LOCK(cs_main);
194 block_index = m_node.chainman->m_blockman.LookupBlockIndex(chainA[i]->GetHash());
195 }
196 BOOST_CHECK(filter_index.BlockUntilSyncedToCurrentChain());
197 CheckFilterLookups(filter_index, block_index, chainA_last_header, m_node.chainman->m_blockman);
198
199 {
200 LOCK(cs_main);
201 block_index = m_node.chainman->m_blockman.LookupBlockIndex(chainB[i]->GetHash());
202 }
203 BOOST_CHECK(filter_index.BlockUntilSyncedToCurrentChain());
204 CheckFilterLookups(filter_index, block_index, chainB_last_header, m_node.chainman->m_blockman);
205 }
206
207 // Test lookups for a range of filters/hashes.
208 std::vector<BlockFilter> filters;
209 std::vector<uint256> filter_hashes;
210
211 {
212 LOCK(cs_main);
213 tip = m_node.chainman->ActiveChain().Tip();
214 }
215 BOOST_CHECK(filter_index.LookupFilterRange(0, tip, filters));
216 BOOST_CHECK(filter_index.LookupFilterHashRange(0, tip, filter_hashes));
217
218 assert(tip->nHeight >= 0);
219 BOOST_CHECK_EQUAL(filters.size(), tip->nHeight + 1U);
220 BOOST_CHECK_EQUAL(filter_hashes.size(), tip->nHeight + 1U);
221
222 filters.clear();
223 filter_hashes.clear();
224
225 filter_index.Interrupt();
226 filter_index.Stop();
227}
228
229BOOST_FIXTURE_TEST_CASE(blockfilter_index_init_destroy, BasicTestingSetup)
230{
231 BlockFilterIndex* filter_index;
232
234 BOOST_CHECK(filter_index == nullptr);
235
237
239 BOOST_CHECK(filter_index != nullptr);
241
242 // Initialize returns false if index already exists.
244
245 int iter_count = 0;
246 ForEachBlockFilterIndex([&iter_count](BlockFilterIndex& _index) { iter_count++; });
247 BOOST_CHECK_EQUAL(iter_count, 1);
248
250
251 // Destroy returns false because index was already destroyed.
253
255 BOOST_CHECK(filter_index == nullptr);
256
257 // Reinitialize index.
259
261
263 BOOST_CHECK(filter_index == nullptr);
264}
265
CScript GetScriptForDestination(const CTxDestination &dest)
Generate a Bitcoin scriptPubKey for the given CTxDestination.
node::NodeContext m_node
Definition: bitcoin-gui.cpp:47
static bool CheckFilterLookups(BlockFilterIndex &filter_index, const CBlockIndex *block_index, uint256 &last_header, const BlockManager &blockman)
BOOST_FIXTURE_TEST_CASE(blockfilter_index_initial_sync, TestChain100Setup)
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:116
void Stop()
Stops the instance from staying in sync with blockchain updates.
Definition: base.cpp:477
bool Init()
Initializes the sync state and registers the instance to the validation interface so that it stays in...
Definition: base.cpp:105
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:464
void Sync()
Sync the index with the block index starting from the current best block.
Definition: base.cpp:208
Complete block filter struct as defined in BIP 157.
Definition: blockfilter.h:116
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.
The block chain is a tree shaped structure starting with the genesis block at the root,...
Definition: chain.h:94
int nHeight
height of the entry in the chain. The genesis block has height 0
Definition: chain.h:106
An encapsulated private key.
Definition: key.h:40
CPubKey GetPubKey() const
Compute the public key from a private key.
Definition: key.cpp:184
Serialized script, used inside transaction inputs and outputs.
Definition: script.h:406
Maintains a tree of blocks (stored in m_block_index) which is consulted to determine where the most-w...
Definition: blockstorage.h:196
256-bit opaque blob.
Definition: uint256.h:196
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()
BOOST_CHECK_EQUAL(headers.FindFirst("key"), "value")
CKey GenerateRandomKey(bool compressed) noexcept
Definition: key.cpp:354
std::unique_ptr< Chain > MakeChain(node::NodeContext &node)
Return implementation of Chain interface.
#define BOOST_CHECK(expr)
Definition: object.cpp:16
Basic testing setup.
Definition: setup_common.h:58
Testing fixture that pre-creates a 100-block REGTEST-mode block chain.
Definition: setup_common.h:139
std::unique_ptr< ChainstateManager > chainman
Definition: context.h:76
#define LOCK(cs)
Definition: sync.h:268
bool ComputeFilter(BlockFilterType filter_type, const CBlockIndex &block_index, BlockFilter &filter, const BlockManager &blockman)
Definition: blockfilter.cpp:15
bool BuildChain(const NodeContext &node, const CBlockIndex *pindex, const CScript &coinbase_script_pub_key, size_t length, std::vector< std::shared_ptr< CBlock > > &chain)
Build a chain of length coinbase-only blocks on top of pindex (which need not be the active tip,...
Definition: mining.cpp:78
assert(!tx.IsCoinBase())