Skip to content

Commit d43a1f1

Browse files
committed
Tighten requirements for adding elements to setBlockIndexCandidates
When using assumeutxo, we only need the background chainstate to consider blocks that are on the chain leading to the snapshotted block. Note that this introduces the new invariant that we can only have an assumeutxo snapshot where the snapshotted blockhash is in our block index. Unknown block hashes that are somehow passed in will cause assertion failures when processing new blocks. Includes test fixes and improvements by Andrew Chow and Fabian Jahr.
1 parent d0d40ea commit d43a1f1

File tree

3 files changed

+50
-26
lines changed

3 files changed

+50
-26
lines changed

src/test/validation_chainstate_tests.cpp

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,13 @@ BOOST_FIXTURE_TEST_CASE(chainstate_update_tip, TestChain100Setup)
7777
// After adding some blocks to the tip, best block should have changed.
7878
BOOST_CHECK(::g_best_block != curr_tip);
7979

80+
// Grab block 1 from disk; we'll add it to the background chain later.
81+
std::shared_ptr<CBlock> pblockone = std::make_shared<CBlock>();
82+
{
83+
LOCK(::cs_main);
84+
chainman.m_blockman.ReadBlockFromDisk(*pblockone, *chainman.ActiveChain()[1]);
85+
}
86+
8087
BOOST_REQUIRE(CreateAndActivateUTXOSnapshot(
8188
this, NoMalleation, /*reset_chainstate=*/ true));
8289

@@ -104,11 +111,7 @@ BOOST_FIXTURE_TEST_CASE(chainstate_update_tip, TestChain100Setup)
104111
assert(false);
105112
}()};
106113

107-
// Create a block to append to the validation chain.
108-
std::vector<CMutableTransaction> noTxns;
109-
CScript scriptPubKey = CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG;
110-
CBlock validation_block = this->CreateBlock(noTxns, scriptPubKey, background_cs);
111-
auto pblock = std::make_shared<const CBlock>(validation_block);
114+
// Append the first block to the background chain.
112115
BlockValidationState state;
113116
CBlockIndex* pindex = nullptr;
114117
const CChainParams& chainparams = Params();
@@ -118,18 +121,18 @@ BOOST_FIXTURE_TEST_CASE(chainstate_update_tip, TestChain100Setup)
118121
// once it is changed to support multiple chainstates.
119122
{
120123
LOCK(::cs_main);
121-
bool checked = CheckBlock(*pblock, state, chainparams.GetConsensus());
124+
bool checked = CheckBlock(*pblockone, state, chainparams.GetConsensus());
122125
BOOST_CHECK(checked);
123126
bool accepted = chainman.AcceptBlock(
124-
pblock, state, &pindex, true, nullptr, &newblock, true);
127+
pblockone, state, &pindex, true, nullptr, &newblock, true);
125128
BOOST_CHECK(accepted);
126129
}
127130

128131
// UpdateTip is called here
129-
bool block_added = background_cs.ActivateBestChain(state, pblock);
132+
bool block_added = background_cs.ActivateBestChain(state, pblockone);
130133

131134
// Ensure tip is as expected
132-
BOOST_CHECK_EQUAL(background_cs.m_chain.Tip()->GetBlockHash(), validation_block.GetHash());
135+
BOOST_CHECK_EQUAL(background_cs.m_chain.Tip()->GetBlockHash(), pblockone->GetHash());
133136

134137
// g_best_block should be unchanged after adding a block to the background
135138
// validation chain.

src/test/validation_chainstatemanager_tests.cpp

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ BOOST_AUTO_TEST_CASE(chainstatemanager)
4949
c1.InitCoinsDB(
5050
/*cache_size_bytes=*/1 << 23, /*in_memory=*/true, /*should_wipe=*/false);
5151
WITH_LOCK(::cs_main, c1.InitCoinsCache(1 << 23));
52+
c1.LoadGenesisBlock();
53+
BlockValidationState val_state;
54+
BOOST_CHECK(c1.ActivateBestChain(val_state, nullptr));
5255

5356
BOOST_CHECK(!manager.IsSnapshotActive());
5457
BOOST_CHECK(WITH_LOCK(::cs_main, return !manager.IsSnapshotValidated()));
@@ -58,7 +61,7 @@ BOOST_AUTO_TEST_CASE(chainstatemanager)
5861
auto& active_chain = WITH_LOCK(manager.GetMutex(), return manager.ActiveChain());
5962
BOOST_CHECK_EQUAL(&active_chain, &c1.m_chain);
6063

61-
BOOST_CHECK_EQUAL(WITH_LOCK(manager.GetMutex(), return manager.ActiveHeight()), -1);
64+
BOOST_CHECK_EQUAL(WITH_LOCK(manager.GetMutex(), return manager.ActiveHeight()), 0);
6265

6366
auto active_tip = WITH_LOCK(manager.GetMutex(), return manager.ActiveTip());
6467
auto exp_tip = c1.m_chain.Tip();
@@ -68,7 +71,7 @@ BOOST_AUTO_TEST_CASE(chainstatemanager)
6871

6972
// Create a snapshot-based chainstate.
7073
//
71-
const uint256 snapshot_blockhash = GetRandHash();
74+
const uint256 snapshot_blockhash = active_tip->GetBlockHash();
7275
Chainstate& c2 = WITH_LOCK(::cs_main, return manager.ActivateExistingSnapshot(
7376
&mempool, snapshot_blockhash));
7477
chainstates.push_back(&c2);
@@ -78,8 +81,7 @@ BOOST_AUTO_TEST_CASE(chainstatemanager)
7881
c2.InitCoinsDB(
7982
/*cache_size_bytes=*/1 << 23, /*in_memory=*/true, /*should_wipe=*/false);
8083
WITH_LOCK(::cs_main, c2.InitCoinsCache(1 << 23));
81-
// Unlike c1, which doesn't have any blocks. Gets us different tip, height.
82-
c2.LoadGenesisBlock();
84+
c2.m_chain.SetTip(*active_tip);
8385
BlockValidationState _;
8486
BOOST_CHECK(c2.ActivateBestChain(_, nullptr));
8587

@@ -99,16 +101,14 @@ BOOST_AUTO_TEST_CASE(chainstatemanager)
99101
auto exp_tip2 = c2.m_chain.Tip();
100102
BOOST_CHECK_EQUAL(active_tip2, exp_tip2);
101103

102-
// Ensure that these pointers actually correspond to different
103-
// CCoinsViewCache instances.
104-
BOOST_CHECK(exp_tip != exp_tip2);
104+
BOOST_CHECK_EQUAL(exp_tip, exp_tip2);
105105

106106
// Let scheduler events finish running to avoid accessing memory that is going to be unloaded
107107
SyncWithValidationInterfaceQueue();
108108
}
109109

110110
//! Test rebalancing the caches associated with each chainstate.
111-
BOOST_AUTO_TEST_CASE(chainstatemanager_rebalance_caches)
111+
BOOST_FIXTURE_TEST_CASE(chainstatemanager_rebalance_caches, TestChain100Setup)
112112
{
113113
ChainstateManager& manager = *m_node.chainman;
114114
CTxMemPool& mempool = *m_node.mempool;
@@ -121,16 +121,14 @@ BOOST_AUTO_TEST_CASE(chainstatemanager_rebalance_caches)
121121

122122
// Create a legacy (IBD) chainstate.
123123
//
124-
Chainstate& c1 = WITH_LOCK(::cs_main, return manager.InitializeChainstate(&mempool));
124+
Chainstate& c1 = manager.ActiveChainstate();
125125
chainstates.push_back(&c1);
126126
c1.InitCoinsDB(
127127
/*cache_size_bytes=*/1 << 23, /*in_memory=*/true, /*should_wipe=*/false);
128128

129129
{
130130
LOCK(::cs_main);
131131
c1.InitCoinsCache(1 << 23);
132-
BOOST_REQUIRE(c1.LoadGenesisBlock());
133-
c1.CoinsTip().SetBestBlock(InsecureRand256());
134132
manager.MaybeRebalanceCaches();
135133
}
136134

@@ -139,16 +137,15 @@ BOOST_AUTO_TEST_CASE(chainstatemanager_rebalance_caches)
139137

140138
// Create a snapshot-based chainstate.
141139
//
142-
Chainstate& c2 = WITH_LOCK(cs_main, return manager.ActivateExistingSnapshot(&mempool, GetRandHash()));
140+
CBlockIndex* snapshot_base{WITH_LOCK(manager.GetMutex(), return manager.ActiveChain()[manager.ActiveChain().Height() / 2])};
141+
Chainstate& c2 = WITH_LOCK(cs_main, return manager.ActivateExistingSnapshot(&mempool, *snapshot_base->phashBlock));
143142
chainstates.push_back(&c2);
144143
c2.InitCoinsDB(
145144
/*cache_size_bytes=*/1 << 23, /*in_memory=*/true, /*should_wipe=*/false);
146145

147146
{
148147
LOCK(::cs_main);
149148
c2.InitCoinsCache(1 << 23);
150-
BOOST_REQUIRE(c2.LoadGenesisBlock());
151-
c2.CoinsTip().SetBestBlock(InsecureRand256());
152149
manager.MaybeRebalanceCaches();
153150
}
154151

@@ -430,6 +427,7 @@ BOOST_FIXTURE_TEST_CASE(chainstatemanager_loadblockindex, TestChain100Setup)
430427
const int assumed_valid_start_idx = last_assumed_valid_idx - expected_assumed_valid;
431428

432429
CBlockIndex* validated_tip{nullptr};
430+
CBlockIndex* assumed_base{nullptr};
433431
CBlockIndex* assumed_tip{WITH_LOCK(chainman.GetMutex(), return chainman.ActiveChain().Tip())};
434432

435433
auto reload_all_block_indexes = [&]() {
@@ -468,12 +466,20 @@ BOOST_FIXTURE_TEST_CASE(chainstatemanager_loadblockindex, TestChain100Setup)
468466
validated_tip = index;
469467
BOOST_CHECK(!index->IsAssumedValid());
470468
}
469+
// Note the block after the last assumed valid block as the snapshot base
470+
if (i == last_assumed_valid_idx) {
471+
assumed_base = index;
472+
BOOST_CHECK(!index->IsAssumedValid());
473+
}
471474
}
472475

473476
BOOST_CHECK_EQUAL(expected_assumed_valid, num_assumed_valid);
474477

475478
Chainstate& cs2 = WITH_LOCK(::cs_main,
476-
return chainman.ActivateExistingSnapshot(&mempool, GetRandHash()));
479+
return chainman.ActivateExistingSnapshot(&mempool, *assumed_base->phashBlock));
480+
481+
// Set tip of the fully validated chain to be the validated tip
482+
cs1.m_chain.SetTip(*validated_tip);
477483

478484
reload_all_block_indexes();
479485

src/validation.cpp

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3419,9 +3419,24 @@ void Chainstate::ResetBlockFailureFlags(CBlockIndex *pindex) {
34193419
void Chainstate::TryAddBlockIndexCandidate(CBlockIndex* pindex)
34203420
{
34213421
AssertLockHeld(cs_main);
3422-
// If the block has more work than our tip, then it should be a candidate for most-work-chain.
3423-
if (m_chain.Tip() == nullptr || !setBlockIndexCandidates.value_comp()(pindex, m_chain.Tip())) {
3422+
// The block only is a candidate for the most-work-chain if it has more work than our current tip.
3423+
if (m_chain.Tip() != nullptr && setBlockIndexCandidates.value_comp()(pindex, m_chain.Tip())) {
3424+
return;
3425+
}
3426+
3427+
bool is_active_chainstate = this == &m_chainman.ActiveChainstate();
3428+
if (is_active_chainstate) {
3429+
// The active chainstate should always add entries that have more
3430+
// work than the tip.
34243431
setBlockIndexCandidates.insert(pindex);
3432+
} else if (!m_disabled) {
3433+
// For the background chainstate, we only consider connecting blocks
3434+
// towards the snapshot base (which can't be nullptr or else we'll
3435+
// never make progress).
3436+
const CBlockIndex* snapshot_base = Assert(m_chainman.GetSnapshotBaseBlock());
3437+
if (snapshot_base->GetAncestor(pindex->nHeight) == pindex) {
3438+
setBlockIndexCandidates.insert(pindex);
3439+
}
34253440
}
34263441
}
34273442

0 commit comments

Comments
 (0)