test: Unit test for block filter index reorg handling.

This commit is contained in:
Jim Posen 2018-08-28 09:04:09 -07:00
parent 6bcf0998c0
commit 2bc90e4e7b

View file

@ -4,7 +4,10 @@
#include <blockfilter.h>
#include <chainparams.h>
#include <consensus/validation.h>
#include <index/blockfilterindex.h>
#include <miner.h>
#include <pow.h>
#include <test/test_bitcoin.h>
#include <script/standard.h>
#include <validation.h>
@ -64,6 +67,49 @@ static bool CheckFilterLookups(BlockFilterIndex& filter_index, const CBlockIndex
return true;
}
static CBlock CreateBlock(const CBlockIndex* prev,
const std::vector<CMutableTransaction>& txns,
const CScript& scriptPubKey)
{
const CChainParams& chainparams = Params();
std::unique_ptr<CBlockTemplate> pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey);
CBlock& block = pblocktemplate->block;
block.hashPrevBlock = prev->GetBlockHash();
block.nTime = prev->nTime + 1;
// Replace mempool-selected txns with just coinbase plus passed-in txns:
block.vtx.resize(1);
for (const CMutableTransaction& tx : txns) {
block.vtx.push_back(MakeTransactionRef(tx));
}
// IncrementExtraNonce creates a valid coinbase and merkleRoot
unsigned int extraNonce = 0;
IncrementExtraNonce(&block, prev, extraNonce);
while (!CheckProofOfWork(block.GetHash(), block.nBits, chainparams.GetConsensus())) ++block.nNonce;
return block;
}
static bool BuildChain(const CBlockIndex* pindex, const CScript& coinbase_script_pub_key,
size_t length, std::vector<std::shared_ptr<CBlock>>& chain)
{
std::vector<CMutableTransaction> no_txns;
chain.resize(length);
for (auto& block : chain) {
block = std::make_shared<CBlock>(CreateBlock(pindex, no_txns, coinbase_script_pub_key));
CBlockHeader header = block->GetBlockHeader();
CValidationState state;
if (!ProcessNewBlockHeaders({header}, state, Params(), &pindex, nullptr)) {
return false;
}
}
return true;
}
BOOST_FIXTURE_TEST_CASE(blockfilter_index_initial_sync, TestChain100Setup)
{
BlockFilterIndex filter_index(BlockFilterType::BASIC, 1 << 20, true);
@ -114,31 +160,103 @@ BOOST_FIXTURE_TEST_CASE(blockfilter_index_initial_sync, TestChain100Setup)
}
}
// Check that new blocks get indexed.
for (int i = 0; i < 10; i++) {
CScript coinbase_script_pub_key = GetScriptForDestination(coinbaseKey.GetPubKey().GetID());
std::vector<CMutableTransaction> no_txns;
const CBlock& block = CreateAndProcessBlock(no_txns, coinbase_script_pub_key);
// Create two forks.
const CBlockIndex* tip;
{
LOCK(cs_main);
tip = chainActive.Tip();
}
CScript coinbase_script_pub_key = GetScriptForDestination(coinbaseKey.GetPubKey().GetID());
std::vector<std::shared_ptr<CBlock>> chainA, chainB;
BOOST_REQUIRE(BuildChain(tip, coinbase_script_pub_key, 10, chainA));
BOOST_REQUIRE(BuildChain(tip, coinbase_script_pub_key, 10, chainB));
// Check that new blocks on chain A get indexed.
uint256 chainA_last_header = last_header;
for (size_t i = 0; i < 2; i++) {
const auto& block = chainA[i];
BOOST_REQUIRE(ProcessNewBlock(Params(), block, true, nullptr));
const CBlockIndex* block_index;
{
LOCK(cs_main);
block_index = LookupBlockIndex(block.GetHash());
block_index = LookupBlockIndex(block->GetHash());
}
BOOST_CHECK(filter_index.BlockUntilSyncedToCurrentChain());
CheckFilterLookups(filter_index, block_index, last_header);
CheckFilterLookups(filter_index, block_index, chainA_last_header);
}
// Reorg to chain B.
uint256 chainB_last_header = last_header;
for (size_t i = 0; i < 3; i++) {
const auto& block = chainB[i];
BOOST_REQUIRE(ProcessNewBlock(Params(), block, true, nullptr));
const CBlockIndex* block_index;
{
LOCK(cs_main);
block_index = LookupBlockIndex(block->GetHash());
}
BOOST_CHECK(filter_index.BlockUntilSyncedToCurrentChain());
CheckFilterLookups(filter_index, block_index, chainB_last_header);
}
// Check that filters for stale blocks on A can be retrieved.
chainA_last_header = last_header;
for (size_t i = 0; i < 2; i++) {
const auto& block = chainA[i];
const CBlockIndex* block_index;
{
LOCK(cs_main);
block_index = LookupBlockIndex(block->GetHash());
}
BOOST_CHECK(filter_index.BlockUntilSyncedToCurrentChain());
CheckFilterLookups(filter_index, block_index, chainA_last_header);
}
// Reorg back to chain A.
for (size_t i = 2; i < 4; i++) {
const auto& block = chainA[i];
BOOST_REQUIRE(ProcessNewBlock(Params(), block, true, nullptr));
}
// Check that chain A and B blocks can be retrieved.
chainA_last_header = last_header;
chainB_last_header = last_header;
for (size_t i = 0; i < 3; i++) {
const CBlockIndex* block_index;
{
LOCK(cs_main);
block_index = LookupBlockIndex(chainA[i]->GetHash());
}
BOOST_CHECK(filter_index.BlockUntilSyncedToCurrentChain());
CheckFilterLookups(filter_index, block_index, chainA_last_header);
{
LOCK(cs_main);
block_index = LookupBlockIndex(chainB[i]->GetHash());
}
BOOST_CHECK(filter_index.BlockUntilSyncedToCurrentChain());
CheckFilterLookups(filter_index, block_index, chainB_last_header);
}
// Test lookups for a range of filters/hashes.
std::vector<BlockFilter> filters;
std::vector<uint256> filter_hashes;
const CBlockIndex* block_index = chainActive.Tip();
BOOST_CHECK(filter_index.LookupFilterRange(0, block_index, filters));
BOOST_CHECK(filter_index.LookupFilterHashRange(0, block_index, filter_hashes));
{
LOCK(cs_main);
tip = chainActive.Tip();
}
BOOST_CHECK(filter_index.LookupFilterRange(0, tip, filters));
BOOST_CHECK(filter_index.LookupFilterHashRange(0, tip, filter_hashes));
BOOST_CHECK_EQUAL(filters.size(), chainActive.Height() + 1);
BOOST_CHECK_EQUAL(filter_hashes.size(), chainActive.Height() + 1);
BOOST_CHECK_EQUAL(filters.size(), tip->nHeight + 1);
BOOST_CHECK_EQUAL(filter_hashes.size(), tip->nHeight + 1);
filters.clear();
filter_hashes.clear();