Line data Source code
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/merkle.h>
8 : #include <consensus/validation.h>
9 : #include <index/blockfilterindex.h>
10 : #include <node/miner.h>
11 : #include <pow.h>
12 : #include <script/standard.h>
13 : #include <test/util/blockfilter.h>
14 : #include <test/util/index.h>
15 : #include <test/util/setup_common.h>
16 : #include <validation.h>
17 :
18 : #include <boost/test/unit_test.hpp>
19 :
20 : using node::BlockAssembler;
21 : using node::CBlockTemplate;
22 :
23 146 : BOOST_AUTO_TEST_SUITE(blockfilter_index_tests)
24 :
25 1 : struct BuildChainTestingSetup : public TestChain100Setup {
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 114 : static bool CheckFilterLookups(BlockFilterIndex& filter_index, const CBlockIndex* block_index,
31 : uint256& last_header)
32 : {
33 114 : BlockFilter expected_filter;
34 114 : if (!ComputeFilter(filter_index.GetFilterType(), block_index, expected_filter)) {
35 0 : BOOST_ERROR("ComputeFilter failed on block " << block_index->nHeight);
36 0 : return false;
37 : }
38 :
39 114 : BlockFilter filter;
40 114 : uint256 filter_header;
41 114 : std::vector<BlockFilter> filters;
42 114 : std::vector<uint256> filter_hashes;
43 :
44 114 : BOOST_CHECK(filter_index.LookupFilter(block_index, filter));
45 114 : BOOST_CHECK(filter_index.LookupFilterHeader(block_index, filter_header));
46 114 : BOOST_CHECK(filter_index.LookupFilterRange(block_index->nHeight, block_index, filters));
47 114 : BOOST_CHECK(filter_index.LookupFilterHashRange(block_index->nHeight, block_index,
48 : filter_hashes));
49 :
50 114 : BOOST_CHECK_EQUAL(filters.size(), 1U);
51 114 : BOOST_CHECK_EQUAL(filter_hashes.size(), 1U);
52 :
53 114 : BOOST_CHECK_EQUAL(filter.GetHash(), expected_filter.GetHash());
54 114 : BOOST_CHECK_EQUAL(filter_header, expected_filter.ComputeHeader(last_header));
55 114 : BOOST_CHECK_EQUAL(filters[0].GetHash(), expected_filter.GetHash());
56 114 : BOOST_CHECK_EQUAL(filter_hashes[0], expected_filter.GetHash());
57 :
58 114 : filters.clear();
59 114 : filter_hashes.clear();
60 114 : last_header = filter_header;
61 114 : return true;
62 114 : }
63 :
64 20 : CBlock BuildChainTestingSetup::CreateBlock(const CBlockIndex* prev,
65 : const std::vector<CMutableTransaction>& txns,
66 : const CScript& scriptPubKey)
67 : {
68 20 : const CChainParams& chainparams = Params();
69 20 : std::unique_ptr<CBlockTemplate> pblocktemplate = BlockAssembler(m_node.chainman->ActiveChainstate(), m_node, m_node.mempool.get(), chainparams).CreateNewBlock(scriptPubKey);
70 20 : CBlock& block = pblocktemplate->block;
71 20 : block.hashPrevBlock = prev->GetBlockHash();
72 20 : block.nTime = prev->nTime + 1;
73 :
74 : // Replace mempool-selected txns with just coinbase plus passed-in txns:
75 20 : block.vtx.resize(1);
76 20 : for (const CMutableTransaction& tx : txns) {
77 0 : block.vtx.push_back(MakeTransactionRef(tx));
78 : }
79 : {
80 20 : CMutableTransaction tx_coinbase{*block.vtx.at(0)};
81 20 : tx_coinbase.vin.at(0).scriptSig = CScript{} << prev->nHeight + 1;
82 20 : block.vtx.at(0) = MakeTransactionRef(std::move(tx_coinbase));
83 20 : block.hashMerkleRoot = BlockMerkleRoot(block);
84 20 : }
85 :
86 46 : while (!CheckProofOfWork(block.GetHash(), block.nBits, chainparams.GetConsensus())) ++block.nNonce;
87 :
88 20 : return block;
89 20 : }
90 :
91 2 : bool BuildChainTestingSetup::BuildChain(const CBlockIndex* pindex,
92 : const CScript& coinbase_script_pub_key,
93 : size_t length,
94 : std::vector<std::shared_ptr<CBlock>>& chain)
95 : {
96 2 : std::vector<CMutableTransaction> no_txns;
97 :
98 2 : chain.resize(length);
99 22 : for (auto& block : chain) {
100 20 : block = std::make_shared<CBlock>(CreateBlock(pindex, no_txns, coinbase_script_pub_key));
101 20 : CBlockHeader header = block->GetBlockHeader();
102 :
103 20 : BlockValidationState state;
104 20 : if (!Assert(m_node.chainman)->ProcessNewBlockHeaders({header}, state, &pindex)) {
105 0 : return false;
106 : }
107 20 : }
108 :
109 2 : return true;
110 2 : }
111 :
112 148 : BOOST_FIXTURE_TEST_CASE(blockfilter_index_initial_sync, BuildChainTestingSetup)
113 : {
114 1 : BlockFilterIndex filter_index(BlockFilterType::BASIC_FILTER, 1 << 20, true);
115 :
116 1 : uint256 last_header;
117 :
118 : // Filter should not be found in the index before it is started.
119 : {
120 1 : LOCK(cs_main);
121 :
122 1 : BlockFilter filter;
123 1 : uint256 filter_header;
124 1 : std::vector<BlockFilter> filters;
125 1 : std::vector<uint256> filter_hashes;
126 :
127 102 : for (const CBlockIndex* block_index = m_node.chainman->ActiveChain().Genesis();
128 102 : block_index != nullptr;
129 101 : block_index = m_node.chainman->ActiveChain().Next(block_index)) {
130 101 : BOOST_CHECK(!filter_index.LookupFilter(block_index, filter));
131 101 : BOOST_CHECK(!filter_index.LookupFilterHeader(block_index, filter_header));
132 101 : BOOST_CHECK(!filter_index.LookupFilterRange(block_index->nHeight, block_index, filters));
133 101 : BOOST_CHECK(!filter_index.LookupFilterHashRange(block_index->nHeight, block_index,
134 : filter_hashes));
135 101 : }
136 1 : }
137 :
138 : // BlockUntilSyncedToCurrentChain should return false before index is started.
139 1 : BOOST_CHECK(!filter_index.BlockUntilSyncedToCurrentChain());
140 :
141 1 : BOOST_REQUIRE(filter_index.Start(m_node.chainman->ActiveChainstate()));
142 :
143 : // Allow filter index to catch up with the block index.
144 1 : IndexWaitSynced(filter_index);
145 :
146 : // Check that filter index has all blocks that were in the chain before it started.
147 : {
148 1 : LOCK(cs_main);
149 : const CBlockIndex* block_index;
150 102 : for (block_index = m_node.chainman->ActiveChain().Genesis();
151 102 : block_index != nullptr;
152 101 : block_index = m_node.chainman->ActiveChain().Next(block_index)) {
153 101 : CheckFilterLookups(filter_index, block_index, last_header);
154 101 : }
155 1 : }
156 :
157 : // Create two forks.
158 : const CBlockIndex* tip;
159 : {
160 1 : LOCK(cs_main);
161 1 : tip = m_node.chainman->ActiveChain().Tip();
162 1 : }
163 1 : CKey coinbase_key_A = GenerateRandomKey();
164 1 : CKey coinbase_key_B = GenerateRandomKey();
165 1 : CScript coinbase_script_pub_key_A = GetScriptForDestination(PKHash(coinbase_key_A.GetPubKey()));
166 1 : CScript coinbase_script_pub_key_B = GetScriptForDestination(PKHash(coinbase_key_B.GetPubKey()));
167 1 : std::vector<std::shared_ptr<CBlock>> chainA, chainB;
168 1 : BOOST_REQUIRE(BuildChain(tip, coinbase_script_pub_key_A, 10, chainA));
169 1 : BOOST_REQUIRE(BuildChain(tip, coinbase_script_pub_key_B, 10, chainB));
170 :
171 : // Check that new blocks on chain A get indexed.
172 1 : uint256 chainA_last_header = last_header;
173 3 : for (size_t i = 0; i < 2; i++) {
174 2 : const auto& block = chainA[i];
175 2 : BOOST_REQUIRE(Assert(m_node.chainman)->ProcessNewBlock(block, true, nullptr));
176 2 : }
177 3 : for (size_t i = 0; i < 2; i++) {
178 2 : const auto& block = chainA[i];
179 : const CBlockIndex* block_index;
180 : {
181 2 : LOCK(cs_main);
182 2 : block_index = m_node.chainman->m_blockman.LookupBlockIndex(block->GetHash());
183 2 : }
184 :
185 2 : BOOST_CHECK(filter_index.BlockUntilSyncedToCurrentChain());
186 2 : CheckFilterLookups(filter_index, block_index, chainA_last_header);
187 2 : }
188 :
189 : // Reorg to chain B.
190 1 : uint256 chainB_last_header = last_header;
191 4 : for (size_t i = 0; i < 3; i++) {
192 3 : const auto& block = chainB[i];
193 3 : BOOST_REQUIRE(Assert(m_node.chainman)->ProcessNewBlock(block, true, nullptr));
194 3 : }
195 4 : for (size_t i = 0; i < 3; i++) {
196 3 : const auto& block = chainB[i];
197 : const CBlockIndex* block_index;
198 : {
199 3 : LOCK(cs_main);
200 3 : block_index = m_node.chainman->m_blockman.LookupBlockIndex(block->GetHash());
201 3 : }
202 :
203 3 : BOOST_CHECK(filter_index.BlockUntilSyncedToCurrentChain());
204 3 : CheckFilterLookups(filter_index, block_index, chainB_last_header);
205 3 : }
206 :
207 : // Check that filters for stale blocks on A can be retrieved.
208 1 : chainA_last_header = last_header;
209 3 : for (size_t i = 0; i < 2; i++) {
210 2 : const auto& block = chainA[i];
211 : const CBlockIndex* block_index;
212 : {
213 2 : LOCK(cs_main);
214 2 : block_index = m_node.chainman->m_blockman.LookupBlockIndex(block->GetHash());
215 2 : }
216 :
217 2 : BOOST_CHECK(filter_index.BlockUntilSyncedToCurrentChain());
218 2 : CheckFilterLookups(filter_index, block_index, chainA_last_header);
219 2 : }
220 :
221 : // Reorg back to chain A.
222 3 : for (size_t i = 2; i < 4; i++) {
223 2 : const auto& block = chainA[i];
224 2 : BOOST_REQUIRE(Assert(m_node.chainman)->ProcessNewBlock(block, true, nullptr));
225 2 : }
226 :
227 : // Check that chain A and B blocks can be retrieved.
228 1 : chainA_last_header = last_header;
229 1 : chainB_last_header = last_header;
230 4 : for (size_t i = 0; i < 3; i++) {
231 : const CBlockIndex* block_index;
232 :
233 : {
234 3 : LOCK(cs_main);
235 3 : block_index = m_node.chainman->m_blockman.LookupBlockIndex(chainA[i]->GetHash());
236 3 : }
237 3 : BOOST_CHECK(filter_index.BlockUntilSyncedToCurrentChain());
238 3 : CheckFilterLookups(filter_index, block_index, chainA_last_header);
239 :
240 : {
241 3 : LOCK(cs_main);
242 3 : block_index = m_node.chainman->m_blockman.LookupBlockIndex(chainB[i]->GetHash());
243 3 : }
244 3 : BOOST_CHECK(filter_index.BlockUntilSyncedToCurrentChain());
245 3 : CheckFilterLookups(filter_index, block_index, chainB_last_header);
246 3 : }
247 :
248 : // Test lookups for a range of filters/hashes.
249 1 : std::vector<BlockFilter> filters;
250 1 : std::vector<uint256> filter_hashes;
251 :
252 : {
253 1 : LOCK(cs_main);
254 1 : tip = m_node.chainman->ActiveChain().Tip();
255 1 : }
256 1 : BOOST_CHECK(filter_index.LookupFilterRange(0, tip, filters));
257 1 : BOOST_CHECK(filter_index.LookupFilterHashRange(0, tip, filter_hashes));
258 :
259 1 : assert(tip->nHeight >= 0);
260 1 : BOOST_CHECK_EQUAL(filters.size(), tip->nHeight + 1U);
261 1 : BOOST_CHECK_EQUAL(filter_hashes.size(), tip->nHeight + 1U);
262 :
263 1 : filters.clear();
264 1 : filter_hashes.clear();
265 :
266 1 : filter_index.Interrupt();
267 1 : filter_index.Stop();
268 1 : }
269 :
270 149 : BOOST_FIXTURE_TEST_CASE(blockfilter_index_init_destroy, BasicTestingSetup)
271 : {
272 : BlockFilterIndex* filter_index;
273 :
274 1 : filter_index = GetBlockFilterIndex(BlockFilterType::BASIC_FILTER);
275 1 : BOOST_CHECK(filter_index == nullptr);
276 :
277 1 : BOOST_CHECK(InitBlockFilterIndex(BlockFilterType::BASIC_FILTER, 1 << 20, true, false));
278 :
279 1 : filter_index = GetBlockFilterIndex(BlockFilterType::BASIC_FILTER);
280 1 : BOOST_CHECK(filter_index != nullptr);
281 1 : BOOST_CHECK(filter_index->GetFilterType() == BlockFilterType::BASIC_FILTER);
282 :
283 : // Initialize returns false if index already exists.
284 1 : BOOST_CHECK(!InitBlockFilterIndex(BlockFilterType::BASIC_FILTER, 1 << 20, true, false));
285 :
286 1 : int iter_count = 0;
287 2 : ForEachBlockFilterIndex([&iter_count](BlockFilterIndex& _index) { iter_count++; });
288 1 : BOOST_CHECK_EQUAL(iter_count, 1);
289 :
290 1 : BOOST_CHECK(DestroyBlockFilterIndex(BlockFilterType::BASIC_FILTER));
291 :
292 : // Destroy returns false because index was already destroyed.
293 1 : BOOST_CHECK(!DestroyBlockFilterIndex(BlockFilterType::BASIC_FILTER));
294 :
295 1 : filter_index = GetBlockFilterIndex(BlockFilterType::BASIC_FILTER);
296 1 : BOOST_CHECK(filter_index == nullptr);
297 :
298 : // Reinitialize index.
299 1 : BOOST_CHECK(InitBlockFilterIndex(BlockFilterType::BASIC_FILTER, 1 << 20, true, false));
300 :
301 1 : DestroyAllBlockFilterIndexes();
302 :
303 1 : filter_index = GetBlockFilterIndex(BlockFilterType::BASIC_FILTER);
304 1 : BOOST_CHECK(filter_index == nullptr);
305 1 : }
306 :
307 146 : BOOST_AUTO_TEST_SUITE_END()
|