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