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