Branch data Line data Source code
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>
8 : : #include <index/blockfilterindex.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>
15 : : #include <test/util/blockfilter.h>
16 : : #include <test/util/common.h>
17 : : #include <test/util/mining.h>
18 : : #include <test/util/setup_common.h>
19 : : #include <test/util/validation.h>
20 : : #include <uint256.h>
21 : : #include <util/check.h>
22 : : #include <validation.h>
23 : :
24 : : #include <boost/test/unit_test.hpp>
25 : :
26 : : #include <compare>
27 : : #include <cstddef>
28 : : #include <cstdint>
29 : : #include <functional>
30 : : #include <memory>
31 : : #include <span>
32 : : #include <string>
33 : : #include <utility>
34 : : #include <vector>
35 : :
36 : : using node::BlockManager;
37 : :
38 : : BOOST_AUTO_TEST_SUITE(blockfilter_index_tests)
39 : :
40 : 114 : static bool CheckFilterLookups(BlockFilterIndex& filter_index, const CBlockIndex* block_index,
41 : : uint256& last_header, const BlockManager& blockman)
42 : : {
43 : 114 : BlockFilter expected_filter;
44 [ + - - + ]: 114 : if (!ComputeFilter(filter_index.GetFilterType(), *block_index, expected_filter, blockman)) {
45 [ # # # # ]: 0 : BOOST_ERROR("ComputeFilter failed on block " << block_index->nHeight);
46 : 0 : return false;
47 : : }
48 : :
49 [ + - ]: 114 : BlockFilter filter;
50 : 114 : uint256 filter_header;
51 : 114 : std::vector<BlockFilter> filters;
52 : 114 : std::vector<uint256> filter_hashes;
53 : :
54 [ + - + - : 228 : BOOST_CHECK(filter_index.LookupFilter(block_index, filter));
+ - + - ]
55 [ + - + - : 228 : BOOST_CHECK(filter_index.LookupFilterHeader(block_index, filter_header));
+ - + - ]
56 [ + - + - : 228 : BOOST_CHECK(filter_index.LookupFilterRange(block_index->nHeight, block_index, filters));
+ - + - ]
57 [ + - + - : 228 : BOOST_CHECK(filter_index.LookupFilterHashRange(block_index->nHeight, block_index,
+ - + - ]
58 : : filter_hashes));
59 : :
60 [ + - - + : 114 : BOOST_CHECK_EQUAL(filters.size(), 1U);
+ - ]
61 [ + - - + : 114 : BOOST_CHECK_EQUAL(filter_hashes.size(), 1U);
+ - ]
62 : :
63 [ + - + - : 114 : BOOST_CHECK_EQUAL(filter.GetHash(), expected_filter.GetHash());
+ - + - ]
64 [ + - + - : 114 : BOOST_CHECK_EQUAL(filter_header, expected_filter.ComputeHeader(last_header));
+ - ]
65 [ + - + - : 114 : BOOST_CHECK_EQUAL(filters[0].GetHash(), expected_filter.GetHash());
+ - + - ]
66 [ + - + - : 114 : BOOST_CHECK_EQUAL(filter_hashes[0], expected_filter.GetHash());
+ - ]
67 : :
68 : 114 : filters.clear();
69 [ + - ]: 114 : filter_hashes.clear();
70 : 114 : last_header = filter_header;
71 : 114 : return true;
72 : 228 : }
73 : :
74 [ + - + - : 7 : BOOST_FIXTURE_TEST_CASE(blockfilter_index_initial_sync, TestChain100Setup)
+ - + - -
+ + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- ]
75 : : {
76 [ + - ]: 1 : BlockFilterIndex filter_index(interfaces::MakeChain(m_node), BlockFilterType::BASIC, 1_MiB, true);
77 [ + - + - : 2 : BOOST_REQUIRE(filter_index.Init());
+ - + - ]
78 : :
79 : 1 : uint256 last_header;
80 : :
81 : : // Filter should not be found in the index before it is started.
82 : 1 : {
83 [ + - ]: 1 : LOCK(cs_main);
84 : :
85 [ + - ]: 1 : BlockFilter filter;
86 : 1 : uint256 filter_header;
87 : 1 : std::vector<BlockFilter> filters;
88 : 1 : std::vector<uint256> filter_hashes;
89 : :
90 [ + - - + ]: 1 : for (const CBlockIndex* block_index = m_node.chainman->ActiveChain().Genesis();
91 [ + + ]: 102 : block_index != nullptr;
92 [ + - ]: 101 : block_index = m_node.chainman->ActiveChain().Next(*block_index)) {
93 [ + - + - : 202 : BOOST_CHECK(!filter_index.LookupFilter(block_index, filter));
+ - + - ]
94 [ + - + - : 202 : BOOST_CHECK(!filter_index.LookupFilterHeader(block_index, filter_header));
+ - + - ]
95 [ + - + - : 202 : BOOST_CHECK(!filter_index.LookupFilterRange(block_index->nHeight, block_index, filters));
+ - + - ]
96 [ + - + - : 202 : BOOST_CHECK(!filter_index.LookupFilterHashRange(block_index->nHeight, block_index,
+ - + - ]
97 : : filter_hashes));
98 : : }
99 [ + - ]: 1 : }
100 : :
101 : : // BlockUntilSyncedToCurrentChain should return false before index is started.
102 [ + - + - : 2 : BOOST_CHECK(!filter_index.BlockUntilSyncedToCurrentChain());
+ - + - ]
103 : :
104 [ + - ]: 1 : filter_index.Sync();
105 : :
106 : : // Check that filter index has all blocks that were in the chain before it started.
107 : 1 : {
108 [ + - ]: 1 : LOCK(cs_main);
109 : 1 : const CBlockIndex* block_index;
110 [ + - - + ]: 1 : for (block_index = m_node.chainman->ActiveChain().Genesis();
111 [ + + ]: 102 : block_index != nullptr;
112 [ + - ]: 101 : block_index = m_node.chainman->ActiveChain().Next(*block_index)) {
113 [ + - ]: 101 : CheckFilterLookups(filter_index, block_index, last_header, m_node.chainman->m_blockman);
114 : : }
115 : 0 : }
116 : :
117 : : // Create two forks.
118 : 1 : const CBlockIndex* tip;
119 : 1 : {
120 [ + - ]: 1 : LOCK(cs_main);
121 [ + - - + : 2 : tip = m_node.chainman->ActiveChain().Tip();
+ - ]
122 : 0 : }
123 : 1 : CKey coinbase_key_A = GenerateRandomKey();
124 : 1 : CKey coinbase_key_B = GenerateRandomKey();
125 [ + - + - : 1 : CScript coinbase_script_pub_key_A = GetScriptForDestination(PKHash(coinbase_key_A.GetPubKey()));
+ - ]
126 [ + - + - : 1 : CScript coinbase_script_pub_key_B = GetScriptForDestination(PKHash(coinbase_key_B.GetPubKey()));
+ - ]
127 : 1 : std::vector<std::shared_ptr<CBlock>> chainA, chainB;
128 [ + - + - : 2 : BOOST_REQUIRE(BuildChain(m_node, tip, coinbase_script_pub_key_A, 10, chainA));
+ - + - ]
129 [ + - + - : 2 : BOOST_REQUIRE(BuildChain(m_node, tip, coinbase_script_pub_key_B, 10, chainB));
+ - ]
130 : :
131 : : // Check that new blocks on chain A get indexed.
132 : 1 : uint256 chainA_last_header = last_header;
133 [ + + ]: 3 : for (size_t i = 0; i < 2; i++) {
134 [ + - ]: 2 : const auto& block = chainA[i];
135 [ + - - + : 8 : BOOST_REQUIRE(Assert(m_node.chainman)->ProcessNewBlock(block, true, true, nullptr));
+ - + - +
- + - ]
136 : : }
137 [ + + ]: 3 : for (size_t i = 0; i < 2; i++) {
138 [ + - ]: 2 : const auto& block = chainA[i];
139 : 2 : const CBlockIndex* block_index;
140 : 2 : {
141 [ + - ]: 2 : LOCK(cs_main);
142 [ + - + - : 2 : block_index = m_node.chainman->m_blockman.LookupBlockIndex(block->GetHash());
+ - ]
143 : 0 : }
144 : :
145 [ + - + - : 4 : BOOST_CHECK(filter_index.BlockUntilSyncedToCurrentChain());
+ - + - ]
146 [ + - ]: 2 : CheckFilterLookups(filter_index, block_index, chainA_last_header, m_node.chainman->m_blockman);
147 : : }
148 : :
149 : : // Reorg to chain B.
150 : 1 : uint256 chainB_last_header = last_header;
151 [ + + ]: 4 : for (size_t i = 0; i < 3; i++) {
152 [ + - ]: 3 : const auto& block = chainB[i];
153 [ + - - + : 12 : BOOST_REQUIRE(Assert(m_node.chainman)->ProcessNewBlock(block, true, true, nullptr));
+ - + - +
- + - ]
154 : : }
155 [ + + ]: 4 : for (size_t i = 0; i < 3; i++) {
156 [ + - ]: 3 : const auto& block = chainB[i];
157 : 3 : const CBlockIndex* block_index;
158 : 3 : {
159 [ + - ]: 3 : LOCK(cs_main);
160 [ + - + - : 3 : block_index = m_node.chainman->m_blockman.LookupBlockIndex(block->GetHash());
+ - ]
161 : 0 : }
162 : :
163 [ + - + - : 6 : BOOST_CHECK(filter_index.BlockUntilSyncedToCurrentChain());
+ - + - ]
164 [ + - ]: 3 : CheckFilterLookups(filter_index, block_index, chainB_last_header, m_node.chainman->m_blockman);
165 : : }
166 : :
167 : : // Check that filters for stale blocks on A can be retrieved.
168 : 1 : chainA_last_header = last_header;
169 [ + + ]: 3 : for (size_t i = 0; i < 2; i++) {
170 [ + - ]: 2 : const auto& block = chainA[i];
171 : 2 : const CBlockIndex* block_index;
172 : 2 : {
173 [ + - ]: 2 : LOCK(cs_main);
174 [ + - + - : 2 : block_index = m_node.chainman->m_blockman.LookupBlockIndex(block->GetHash());
+ - ]
175 : 0 : }
176 : :
177 [ + - + - : 4 : BOOST_CHECK(filter_index.BlockUntilSyncedToCurrentChain());
+ - + - ]
178 [ + - ]: 2 : CheckFilterLookups(filter_index, block_index, chainA_last_header, m_node.chainman->m_blockman);
179 : : }
180 : :
181 : : // Reorg back to chain A.
182 [ + + ]: 3 : for (size_t i = 2; i < 4; i++) {
183 [ + - ]: 2 : const auto& block = chainA[i];
184 [ + - - + : 8 : BOOST_REQUIRE(Assert(m_node.chainman)->ProcessNewBlock(block, true, true, nullptr));
+ - + - +
- + - ]
185 : : }
186 : :
187 : : // Check that chain A and B blocks can be retrieved.
188 : 1 : chainA_last_header = last_header;
189 : 1 : chainB_last_header = last_header;
190 [ + + ]: 4 : for (size_t i = 0; i < 3; i++) {
191 : 3 : const CBlockIndex* block_index;
192 : :
193 : 3 : {
194 [ + - ]: 3 : LOCK(cs_main);
195 [ + - + - : 3 : block_index = m_node.chainman->m_blockman.LookupBlockIndex(chainA[i]->GetHash());
+ - ]
196 : 0 : }
197 [ + - + - : 6 : BOOST_CHECK(filter_index.BlockUntilSyncedToCurrentChain());
+ - + - ]
198 [ + - ]: 3 : CheckFilterLookups(filter_index, block_index, chainA_last_header, m_node.chainman->m_blockman);
199 : :
200 : 3 : {
201 [ + - ]: 3 : LOCK(cs_main);
202 [ + - + - : 3 : block_index = m_node.chainman->m_blockman.LookupBlockIndex(chainB[i]->GetHash());
+ - ]
203 : 0 : }
204 [ + - + - : 6 : BOOST_CHECK(filter_index.BlockUntilSyncedToCurrentChain());
+ - + - ]
205 [ + - ]: 3 : CheckFilterLookups(filter_index, block_index, chainB_last_header, m_node.chainman->m_blockman);
206 : : }
207 : :
208 : : // Test lookups for a range of filters/hashes.
209 : 1 : std::vector<BlockFilter> filters;
210 : 1 : std::vector<uint256> filter_hashes;
211 : :
212 : 1 : {
213 [ + - ]: 1 : LOCK(cs_main);
214 [ + - - + : 2 : tip = m_node.chainman->ActiveChain().Tip();
+ - ]
215 : 0 : }
216 [ + - + - : 2 : BOOST_CHECK(filter_index.LookupFilterRange(0, tip, filters));
+ - + - ]
217 [ + - + - : 2 : BOOST_CHECK(filter_index.LookupFilterHashRange(0, tip, filter_hashes));
+ - - + ]
218 : :
219 [ - + ]: 1 : assert(tip->nHeight >= 0);
220 [ + - - + : 1 : BOOST_CHECK_EQUAL(filters.size(), tip->nHeight + 1U);
+ - ]
221 [ + - - + : 1 : BOOST_CHECK_EQUAL(filter_hashes.size(), tip->nHeight + 1U);
+ - ]
222 : :
223 : 1 : filters.clear();
224 [ + - ]: 1 : filter_hashes.clear();
225 : :
226 [ + - ]: 1 : filter_index.Interrupt();
227 [ + - ]: 1 : StopIndex(filter_index, m_node);
228 : 1 : }
229 : :
230 [ + - + - : 7 : BOOST_FIXTURE_TEST_CASE(blockfilter_index_init_destroy, BasicTestingSetup)
+ - + - -
+ + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- ]
231 : : {
232 : 1 : BlockFilterIndex* filter_index;
233 : :
234 : 1 : filter_index = GetBlockFilterIndex(BlockFilterType::BASIC);
235 [ + - ]: 2 : BOOST_CHECK(filter_index == nullptr);
236 : :
237 [ + - + - ]: 3 : BOOST_CHECK(InitBlockFilterIndex([&]{ return interfaces::MakeChain(m_node); }, BlockFilterType::BASIC, 1_MiB, true, false));
238 : :
239 : 1 : filter_index = GetBlockFilterIndex(BlockFilterType::BASIC);
240 [ + - ]: 2 : BOOST_CHECK(filter_index != nullptr);
241 [ + - ]: 2 : BOOST_CHECK(filter_index->GetFilterType() == BlockFilterType::BASIC);
242 : :
243 : : // Initialize returns false if index already exists.
244 [ + - + - : 3 : BOOST_CHECK(!InitBlockFilterIndex([&]{ return interfaces::MakeChain(m_node); }, BlockFilterType::BASIC, 1_MiB, true, false));
+ - ]
245 : :
246 : 1 : int iter_count = 0;
247 [ + - ]: 2 : ForEachBlockFilterIndex([&iter_count](BlockFilterIndex& _index) { iter_count++; });
248 [ + - ]: 1 : BOOST_CHECK_EQUAL(iter_count, 1);
249 : :
250 [ + - + - ]: 2 : BOOST_CHECK(DestroyBlockFilterIndex(BlockFilterType::BASIC));
251 : :
252 : : // Destroy returns false because index was already destroyed.
253 [ + - + - ]: 2 : BOOST_CHECK(!DestroyBlockFilterIndex(BlockFilterType::BASIC));
254 : :
255 : 1 : filter_index = GetBlockFilterIndex(BlockFilterType::BASIC);
256 [ + - ]: 2 : BOOST_CHECK(filter_index == nullptr);
257 : :
258 : : // Reinitialize index.
259 [ + - + - ]: 3 : BOOST_CHECK(InitBlockFilterIndex([&]{ return interfaces::MakeChain(m_node); }, BlockFilterType::BASIC, 1_MiB, true, false));
260 : :
261 : 1 : DestroyAllBlockFilterIndexes();
262 : :
263 : 1 : filter_index = GetBlockFilterIndex(BlockFilterType::BASIC);
264 [ + - ]: 2 : BOOST_CHECK(filter_index == nullptr);
265 : 1 : }
266 : :
267 : : BOOST_AUTO_TEST_SUITE_END()
|