Bitcoin Core 29.99.0
P2P Digital Currency
blockfilter_index_tests.cpp
Go to the documentation of this file.
1// Copyright (c) 2017-2022 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>
16#include <validation.h>
17
18#include <boost/test/unit_test.hpp>
19
23
24BOOST_AUTO_TEST_SUITE(blockfilter_index_tests)
25
27 CBlock CreateBlock(const CBlockIndex* prev, const std::vector<CMutableTransaction>& txns, const CScript& scriptPubKey);
28 bool BuildChain(const CBlockIndex* pindex, const CScript& coinbase_script_pub_key, size_t length, std::vector<std::shared_ptr<CBlock>>& chain);
29};
30
31static bool CheckFilterLookups(BlockFilterIndex& filter_index, const CBlockIndex* block_index,
32 uint256& last_header, const BlockManager& blockman)
33{
34 BlockFilter expected_filter;
35 if (!ComputeFilter(filter_index.GetFilterType(), *block_index, expected_filter, blockman)) {
36 BOOST_ERROR("ComputeFilter failed on block " << block_index->nHeight);
37 return false;
38 }
39
40 BlockFilter filter;
41 uint256 filter_header;
42 std::vector<BlockFilter> filters;
43 std::vector<uint256> filter_hashes;
44
45 BOOST_CHECK(filter_index.LookupFilter(block_index, filter));
46 BOOST_CHECK(filter_index.LookupFilterHeader(block_index, filter_header));
47 BOOST_CHECK(filter_index.LookupFilterRange(block_index->nHeight, block_index, filters));
48 BOOST_CHECK(filter_index.LookupFilterHashRange(block_index->nHeight, block_index,
49 filter_hashes));
50
51 BOOST_CHECK_EQUAL(filters.size(), 1U);
52 BOOST_CHECK_EQUAL(filter_hashes.size(), 1U);
53
54 BOOST_CHECK_EQUAL(filter.GetHash(), expected_filter.GetHash());
55 BOOST_CHECK_EQUAL(filter_header, expected_filter.ComputeHeader(last_header));
56 BOOST_CHECK_EQUAL(filters[0].GetHash(), expected_filter.GetHash());
57 BOOST_CHECK_EQUAL(filter_hashes[0], expected_filter.GetHash());
58
59 filters.clear();
60 filter_hashes.clear();
61 last_header = filter_header;
62 return true;
63}
64
66 const std::vector<CMutableTransaction>& txns,
67 const CScript& scriptPubKey)
68{
69 BlockAssembler::Options options;
70 options.coinbase_output_script = scriptPubKey;
71 std::unique_ptr<CBlockTemplate> pblocktemplate = BlockAssembler{m_node.chainman->ActiveChainstate(), m_node.mempool.get(), options}.CreateNewBlock();
72 CBlock& block = pblocktemplate->block;
73 block.hashPrevBlock = prev->GetBlockHash();
74 block.nTime = prev->nTime + 1;
75
76 // Replace mempool-selected txns with just coinbase plus passed-in txns:
77 block.vtx.resize(1);
78 for (const CMutableTransaction& tx : txns) {
79 block.vtx.push_back(MakeTransactionRef(tx));
80 }
81 {
82 CMutableTransaction tx_coinbase{*block.vtx.at(0)};
83 tx_coinbase.nLockTime = static_cast<uint32_t>(prev->nHeight);
84 tx_coinbase.vin.at(0).scriptSig = CScript{} << prev->nHeight + 1;
85 block.vtx.at(0) = MakeTransactionRef(std::move(tx_coinbase));
86 block.hashMerkleRoot = BlockMerkleRoot(block);
87 }
88
89 while (!CheckProofOfWork(block.GetHash(), block.nBits, m_node.chainman->GetConsensus())) ++block.nNonce;
90
91 return block;
92}
93
95 const CScript& coinbase_script_pub_key,
96 size_t length,
97 std::vector<std::shared_ptr<CBlock>>& chain)
98{
99 std::vector<CMutableTransaction> no_txns;
100
101 chain.resize(length);
102 for (auto& block : chain) {
103 block = std::make_shared<CBlock>(CreateBlock(pindex, no_txns, coinbase_script_pub_key));
104 CBlockHeader header = block->GetBlockHeader();
105
107 if (!Assert(m_node.chainman)->ProcessNewBlockHeaders({{header}}, true, state, &pindex)) {
108 return false;
109 }
110 }
111
112 return true;
113}
114
115BOOST_FIXTURE_TEST_CASE(blockfilter_index_initial_sync, BuildChainTestingSetup)
116{
118 BOOST_REQUIRE(filter_index.Init());
119
120 uint256 last_header;
121
122 // Filter should not be found in the index before it is started.
123 {
124 LOCK(cs_main);
125
126 BlockFilter filter;
127 uint256 filter_header;
128 std::vector<BlockFilter> filters;
129 std::vector<uint256> filter_hashes;
130
131 for (const CBlockIndex* block_index = m_node.chainman->ActiveChain().Genesis();
132 block_index != nullptr;
133 block_index = m_node.chainman->ActiveChain().Next(block_index)) {
134 BOOST_CHECK(!filter_index.LookupFilter(block_index, filter));
135 BOOST_CHECK(!filter_index.LookupFilterHeader(block_index, filter_header));
136 BOOST_CHECK(!filter_index.LookupFilterRange(block_index->nHeight, block_index, filters));
137 BOOST_CHECK(!filter_index.LookupFilterHashRange(block_index->nHeight, block_index,
138 filter_hashes));
139 }
140 }
141
142 // BlockUntilSyncedToCurrentChain should return false before index is started.
143 BOOST_CHECK(!filter_index.BlockUntilSyncedToCurrentChain());
144
145 filter_index.Sync();
146
147 // Check that filter index has all blocks that were in the chain before it started.
148 {
149 LOCK(cs_main);
150 const CBlockIndex* block_index;
151 for (block_index = m_node.chainman->ActiveChain().Genesis();
152 block_index != nullptr;
153 block_index = m_node.chainman->ActiveChain().Next(block_index)) {
154 CheckFilterLookups(filter_index, block_index, last_header, m_node.chainman->m_blockman);
155 }
156 }
157
158 // Create two forks.
159 const CBlockIndex* tip;
160 {
161 LOCK(cs_main);
162 tip = m_node.chainman->ActiveChain().Tip();
163 }
164 CKey coinbase_key_A = GenerateRandomKey();
165 CKey coinbase_key_B = GenerateRandomKey();
166 CScript coinbase_script_pub_key_A = GetScriptForDestination(PKHash(coinbase_key_A.GetPubKey()));
167 CScript coinbase_script_pub_key_B = GetScriptForDestination(PKHash(coinbase_key_B.GetPubKey()));
168 std::vector<std::shared_ptr<CBlock>> chainA, chainB;
169 BOOST_REQUIRE(BuildChain(tip, coinbase_script_pub_key_A, 10, chainA));
170 BOOST_REQUIRE(BuildChain(tip, coinbase_script_pub_key_B, 10, chainB));
171
172 // Check that new blocks on chain A get indexed.
173 uint256 chainA_last_header = last_header;
174 for (size_t i = 0; i < 2; i++) {
175 const auto& block = chainA[i];
176 BOOST_REQUIRE(Assert(m_node.chainman)->ProcessNewBlock(block, true, true, nullptr));
177 }
178 for (size_t i = 0; i < 2; i++) {
179 const auto& block = chainA[i];
180 const CBlockIndex* block_index;
181 {
182 LOCK(cs_main);
183 block_index = m_node.chainman->m_blockman.LookupBlockIndex(block->GetHash());
184 }
185
186 BOOST_CHECK(filter_index.BlockUntilSyncedToCurrentChain());
187 CheckFilterLookups(filter_index, block_index, chainA_last_header, m_node.chainman->m_blockman);
188 }
189
190 // Reorg to chain B.
191 uint256 chainB_last_header = last_header;
192 for (size_t i = 0; i < 3; i++) {
193 const auto& block = chainB[i];
194 BOOST_REQUIRE(Assert(m_node.chainman)->ProcessNewBlock(block, true, true, nullptr));
195 }
196 for (size_t i = 0; i < 3; i++) {
197 const auto& block = chainB[i];
198 const CBlockIndex* block_index;
199 {
200 LOCK(cs_main);
201 block_index = m_node.chainman->m_blockman.LookupBlockIndex(block->GetHash());
202 }
203
204 BOOST_CHECK(filter_index.BlockUntilSyncedToCurrentChain());
205 CheckFilterLookups(filter_index, block_index, chainB_last_header, m_node.chainman->m_blockman);
206 }
207
208 // Check that filters for stale blocks on A can be retrieved.
209 chainA_last_header = last_header;
210 for (size_t i = 0; i < 2; i++) {
211 const auto& block = chainA[i];
212 const CBlockIndex* block_index;
213 {
214 LOCK(cs_main);
215 block_index = m_node.chainman->m_blockman.LookupBlockIndex(block->GetHash());
216 }
217
218 BOOST_CHECK(filter_index.BlockUntilSyncedToCurrentChain());
219 CheckFilterLookups(filter_index, block_index, chainA_last_header, m_node.chainman->m_blockman);
220 }
221
222 // Reorg back to chain A.
223 for (size_t i = 2; i < 4; i++) {
224 const auto& block = chainA[i];
225 BOOST_REQUIRE(Assert(m_node.chainman)->ProcessNewBlock(block, true, true, nullptr));
226 }
227
228 // Check that chain A and B blocks can be retrieved.
229 chainA_last_header = last_header;
230 chainB_last_header = last_header;
231 for (size_t i = 0; i < 3; i++) {
232 const CBlockIndex* block_index;
233
234 {
235 LOCK(cs_main);
236 block_index = m_node.chainman->m_blockman.LookupBlockIndex(chainA[i]->GetHash());
237 }
238 BOOST_CHECK(filter_index.BlockUntilSyncedToCurrentChain());
239 CheckFilterLookups(filter_index, block_index, chainA_last_header, m_node.chainman->m_blockman);
240
241 {
242 LOCK(cs_main);
243 block_index = m_node.chainman->m_blockman.LookupBlockIndex(chainB[i]->GetHash());
244 }
245 BOOST_CHECK(filter_index.BlockUntilSyncedToCurrentChain());
246 CheckFilterLookups(filter_index, block_index, chainB_last_header, m_node.chainman->m_blockman);
247 }
248
249 // Test lookups for a range of filters/hashes.
250 std::vector<BlockFilter> filters;
251 std::vector<uint256> filter_hashes;
252
253 {
254 LOCK(cs_main);
255 tip = m_node.chainman->ActiveChain().Tip();
256 }
257 BOOST_CHECK(filter_index.LookupFilterRange(0, tip, filters));
258 BOOST_CHECK(filter_index.LookupFilterHashRange(0, tip, filter_hashes));
259
260 assert(tip->nHeight >= 0);
261 BOOST_CHECK_EQUAL(filters.size(), tip->nHeight + 1U);
262 BOOST_CHECK_EQUAL(filter_hashes.size(), tip->nHeight + 1U);
263
264 filters.clear();
265 filter_hashes.clear();
266
267 filter_index.Interrupt();
268 filter_index.Stop();
269}
270
271BOOST_FIXTURE_TEST_CASE(blockfilter_index_init_destroy, BasicTestingSetup)
272{
273 BlockFilterIndex* filter_index;
274
276 BOOST_CHECK(filter_index == nullptr);
277
279
281 BOOST_CHECK(filter_index != nullptr);
283
284 // Initialize returns false if index already exists.
286
287 int iter_count = 0;
288 ForEachBlockFilterIndex([&iter_count](BlockFilterIndex& _index) { iter_count++; });
289 BOOST_CHECK_EQUAL(iter_count, 1);
290
292
293 // Destroy returns false because index was already destroyed.
295
297 BOOST_CHECK(filter_index == nullptr);
298
299 // Reinitialize index.
301
303
305 BOOST_CHECK(filter_index == nullptr);
306}
307
CScript GetScriptForDestination(const CTxDestination &dest)
Generate a Bitcoin scriptPubKey for the given CTxDestination.
node::NodeContext m_node
Definition: bitcoin-gui.cpp:42
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:106
void Stop()
Stops the instance from staying in sync with blockchain updates.
Definition: base.cpp:455
bool Init()
Initializes the sync state and registers the instance to the validation interface so that it stays in...
Definition: base.cpp:85
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:442
void Sync()
Sync the index with the block index starting from the current best block.
Definition: base.cpp:185
Complete block filter struct as defined in BIP 157.
Definition: blockfilter.h:115
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.
Nodes collect new transactions into a block, hash them into a hash tree, and scan through nonce value...
Definition: block.h:22
uint32_t nNonce
Definition: block.h:30
uint32_t nBits
Definition: block.h:29
uint32_t nTime
Definition: block.h:28
uint256 hashPrevBlock
Definition: block.h:26
uint256 hashMerkleRoot
Definition: block.h:27
uint256 GetHash() const
Definition: block.cpp:11
Definition: block.h:69
std::vector< CTransactionRef > vtx
Definition: block.h:72
The block chain is a tree shaped structure starting with the genesis block at the root,...
Definition: chain.h:141
uint32_t nTime
Definition: chain.h:189
uint256 GetBlockHash() const
Definition: chain.h:243
int nHeight
height of the entry in the chain. The genesis block has height 0
Definition: chain.h:153
An encapsulated private key.
Definition: key.h:35
CPubKey GetPubKey() const
Compute the public key from a private key.
Definition: key.cpp:182
Serialized script, used inside transaction inputs and outputs.
Definition: script.h:415
Generate a new block, without valid proof-of-work.
Definition: miner.h:151
Maintains a tree of blocks (stored in m_block_index) which is consulted to determine where the most-w...
Definition: blockstorage.h:139
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()
CKey GenerateRandomKey(bool compressed) noexcept
Definition: key.cpp:352
uint256 BlockMerkleRoot(const CBlock &block, bool *mutated)
Definition: merkle.cpp:66
std::unique_ptr< Chain > MakeChain(node::NodeContext &node)
Return implementation of Chain interface.
#define BOOST_CHECK_EQUAL(v1, v2)
Definition: object.cpp:18
#define BOOST_CHECK(expr)
Definition: object.cpp:17
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)
Definition: transaction.h:424
Basic testing setup.
Definition: setup_common.h:64
node::NodeContext m_node
Definition: setup_common.h:66
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.
Definition: transaction.h:378
Testing fixture that pre-creates a 100-block REGTEST-mode block chain.
Definition: setup_common.h:146
std::unique_ptr< CTxMemPool > mempool
Definition: context.h:68
std::unique_ptr< ChainstateManager > chainman
Definition: context.h:72
#define LOCK(cs)
Definition: sync.h:265
bool ComputeFilter(BlockFilterType filter_type, const CBlockIndex &block_index, BlockFilter &filter, const BlockManager &blockman)
Definition: blockfilter.cpp:15
assert(!tx.IsCoinBase())