Skip to content

Commit 2bc90e4

Browse files
committed
test: Unit test for block filter index reorg handling.
1 parent 6bcf099 commit 2bc90e4

File tree

1 file changed

+130
-12
lines changed

1 file changed

+130
-12
lines changed

src/test/blockfilter_index_tests.cpp

Lines changed: 130 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@
44

55
#include <blockfilter.h>
66
#include <chainparams.h>
7+
#include <consensus/validation.h>
78
#include <index/blockfilterindex.h>
9+
#include <miner.h>
10+
#include <pow.h>
811
#include <test/test_bitcoin.h>
912
#include <script/standard.h>
1013
#include <validation.h>
@@ -64,6 +67,49 @@ static bool CheckFilterLookups(BlockFilterIndex& filter_index, const CBlockIndex
6467
return true;
6568
}
6669

70+
static CBlock CreateBlock(const CBlockIndex* prev,
71+
const std::vector<CMutableTransaction>& txns,
72+
const CScript& scriptPubKey)
73+
{
74+
const CChainParams& chainparams = Params();
75+
std::unique_ptr<CBlockTemplate> pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey);
76+
CBlock& block = pblocktemplate->block;
77+
block.hashPrevBlock = prev->GetBlockHash();
78+
block.nTime = prev->nTime + 1;
79+
80+
// Replace mempool-selected txns with just coinbase plus passed-in txns:
81+
block.vtx.resize(1);
82+
for (const CMutableTransaction& tx : txns) {
83+
block.vtx.push_back(MakeTransactionRef(tx));
84+
}
85+
// IncrementExtraNonce creates a valid coinbase and merkleRoot
86+
unsigned int extraNonce = 0;
87+
IncrementExtraNonce(&block, prev, extraNonce);
88+
89+
while (!CheckProofOfWork(block.GetHash(), block.nBits, chainparams.GetConsensus())) ++block.nNonce;
90+
91+
return block;
92+
}
93+
94+
static bool BuildChain(const CBlockIndex* pindex, const CScript& coinbase_script_pub_key,
95+
size_t length, 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+
CValidationState state;
105+
if (!ProcessNewBlockHeaders({header}, state, Params(), &pindex, nullptr)) {
106+
return false;
107+
}
108+
}
109+
110+
return true;
111+
}
112+
67113
BOOST_FIXTURE_TEST_CASE(blockfilter_index_initial_sync, TestChain100Setup)
68114
{
69115
BlockFilterIndex filter_index(BlockFilterType::BASIC, 1 << 20, true);
@@ -114,31 +160,103 @@ BOOST_FIXTURE_TEST_CASE(blockfilter_index_initial_sync, TestChain100Setup)
114160
}
115161
}
116162

117-
// Check that new blocks get indexed.
118-
for (int i = 0; i < 10; i++) {
119-
CScript coinbase_script_pub_key = GetScriptForDestination(coinbaseKey.GetPubKey().GetID());
120-
std::vector<CMutableTransaction> no_txns;
121-
const CBlock& block = CreateAndProcessBlock(no_txns, coinbase_script_pub_key);
163+
// Create two forks.
164+
const CBlockIndex* tip;
165+
{
166+
LOCK(cs_main);
167+
tip = chainActive.Tip();
168+
}
169+
CScript coinbase_script_pub_key = GetScriptForDestination(coinbaseKey.GetPubKey().GetID());
170+
std::vector<std::shared_ptr<CBlock>> chainA, chainB;
171+
BOOST_REQUIRE(BuildChain(tip, coinbase_script_pub_key, 10, chainA));
172+
BOOST_REQUIRE(BuildChain(tip, coinbase_script_pub_key, 10, chainB));
173+
174+
// Check that new blocks on chain A get indexed.
175+
uint256 chainA_last_header = last_header;
176+
for (size_t i = 0; i < 2; i++) {
177+
const auto& block = chainA[i];
178+
BOOST_REQUIRE(ProcessNewBlock(Params(), block, true, nullptr));
179+
122180
const CBlockIndex* block_index;
123181
{
124182
LOCK(cs_main);
125-
block_index = LookupBlockIndex(block.GetHash());
183+
block_index = LookupBlockIndex(block->GetHash());
126184
}
127185

128186
BOOST_CHECK(filter_index.BlockUntilSyncedToCurrentChain());
129-
CheckFilterLookups(filter_index, block_index, last_header);
187+
CheckFilterLookups(filter_index, block_index, chainA_last_header);
130188
}
131189

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(ProcessNewBlock(Params(), block, true, nullptr));
195+
196+
const CBlockIndex* block_index;
197+
{
198+
LOCK(cs_main);
199+
block_index = LookupBlockIndex(block->GetHash());
200+
}
201+
202+
BOOST_CHECK(filter_index.BlockUntilSyncedToCurrentChain());
203+
CheckFilterLookups(filter_index, block_index, chainB_last_header);
204+
}
205+
206+
// Check that filters for stale blocks on A can be retrieved.
207+
chainA_last_header = last_header;
208+
for (size_t i = 0; i < 2; i++) {
209+
const auto& block = chainA[i];
210+
const CBlockIndex* block_index;
211+
{
212+
LOCK(cs_main);
213+
block_index = LookupBlockIndex(block->GetHash());
214+
}
215+
216+
BOOST_CHECK(filter_index.BlockUntilSyncedToCurrentChain());
217+
CheckFilterLookups(filter_index, block_index, chainA_last_header);
218+
}
219+
220+
// Reorg back to chain A.
221+
for (size_t i = 2; i < 4; i++) {
222+
const auto& block = chainA[i];
223+
BOOST_REQUIRE(ProcessNewBlock(Params(), block, true, nullptr));
224+
}
225+
226+
// Check that chain A and B blocks can be retrieved.
227+
chainA_last_header = last_header;
228+
chainB_last_header = last_header;
229+
for (size_t i = 0; i < 3; i++) {
230+
const CBlockIndex* block_index;
231+
232+
{
233+
LOCK(cs_main);
234+
block_index = LookupBlockIndex(chainA[i]->GetHash());
235+
}
236+
BOOST_CHECK(filter_index.BlockUntilSyncedToCurrentChain());
237+
CheckFilterLookups(filter_index, block_index, chainA_last_header);
238+
239+
{
240+
LOCK(cs_main);
241+
block_index = LookupBlockIndex(chainB[i]->GetHash());
242+
}
243+
BOOST_CHECK(filter_index.BlockUntilSyncedToCurrentChain());
244+
CheckFilterLookups(filter_index, block_index, chainB_last_header);
245+
}
246+
132247
// Test lookups for a range of filters/hashes.
133248
std::vector<BlockFilter> filters;
134249
std::vector<uint256> filter_hashes;
135250

136-
const CBlockIndex* block_index = chainActive.Tip();
137-
BOOST_CHECK(filter_index.LookupFilterRange(0, block_index, filters));
138-
BOOST_CHECK(filter_index.LookupFilterHashRange(0, block_index, filter_hashes));
251+
{
252+
LOCK(cs_main);
253+
tip = chainActive.Tip();
254+
}
255+
BOOST_CHECK(filter_index.LookupFilterRange(0, tip, filters));
256+
BOOST_CHECK(filter_index.LookupFilterHashRange(0, tip, filter_hashes));
139257

140-
BOOST_CHECK_EQUAL(filters.size(), chainActive.Height() + 1);
141-
BOOST_CHECK_EQUAL(filter_hashes.size(), chainActive.Height() + 1);
258+
BOOST_CHECK_EQUAL(filters.size(), tip->nHeight + 1);
259+
BOOST_CHECK_EQUAL(filter_hashes.size(), tip->nHeight + 1);
142260

143261
filters.clear();
144262
filter_hashes.clear();

0 commit comments

Comments
 (0)