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