Skip to content

Commit 95ab1aa

Browse files
committed
Merge 19a56d1 into merged_master (Bitcoin PR bitcoin/bitcoin#21009)
2 parents 33d8878 + 19a56d1 commit 95ab1aa

File tree

4 files changed

+43
-165
lines changed

4 files changed

+43
-165
lines changed

src/init.cpp

Lines changed: 8 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1551,29 +1551,17 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
15511551
break;
15521552
}
15531553

1554-
bool failed_rewind{false};
1555-
// Can't hold cs_main while calling RewindBlockIndex, so retrieve the relevant
1556-
// chainstates beforehand.
1557-
for (CChainState* chainstate : WITH_LOCK(::cs_main, return chainman.GetAll())) {
1558-
if (!fReset) {
1559-
// Note that RewindBlockIndex MUST run even if we're about to -reindex-chainstate.
1560-
// It both disconnects blocks based on the chainstate, and drops block data in
1561-
// BlockIndex() based on lack of available witness data.
1562-
uiInterface.InitMessage(_("Rewinding blocks...").translated);
1563-
if (!chainstate->RewindBlockIndex(chainparams)) {
1564-
strLoadError = _(
1565-
"Unable to rewind the database to a pre-fork state. "
1566-
"You will need to redownload the blockchain");
1567-
failed_rewind = true;
1568-
break; // out of the per-chainstate loop
1569-
}
1554+
if (!fReset) {
1555+
LOCK(cs_main);
1556+
auto chainstates{chainman.GetAll()};
1557+
if (std::any_of(chainstates.begin(), chainstates.end(),
1558+
[&chainparams](const CChainState* cs) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { return cs->NeedsRedownload(chainparams); })) {
1559+
strLoadError = strprintf(_("Witness data for blocks after height %d requires validation. Please restart with -reindex."),
1560+
chainparams.GetConsensus().SegwitHeight);
1561+
break;
15701562
}
15711563
}
15721564

1573-
if (failed_rewind) {
1574-
break; // out of the chainstate activation do-while
1575-
}
1576-
15771565
bool failed_verification = false;
15781566

15791567
try {

src/validation.cpp

Lines changed: 10 additions & 130 deletions
Original file line numberDiff line numberDiff line change
@@ -4646,143 +4646,23 @@ bool CChainState::ReplayBlocks(const CChainParams& params)
46464646
return true;
46474647
}
46484648

4649-
//! Helper for CChainState::RewindBlockIndex
4650-
void CChainState::EraseBlockData(CBlockIndex* index)
4649+
bool CChainState::NeedsRedownload(const CChainParams& params) const
46514650
{
46524651
AssertLockHeld(cs_main);
4653-
assert(!m_chain.Contains(index)); // Make sure this block isn't active
4654-
4655-
// Reduce validity
4656-
index->nStatus = std::min<unsigned int>(index->nStatus & BLOCK_VALID_MASK, BLOCK_VALID_TREE) | (index->nStatus & ~BLOCK_VALID_MASK);
4657-
// Remove have-data flags.
4658-
index->nStatus &= ~(BLOCK_HAVE_DATA | BLOCK_HAVE_UNDO);
4659-
// Remove storage location.
4660-
index->nFile = 0;
4661-
index->nDataPos = 0;
4662-
index->nUndoPos = 0;
4663-
// Remove various other things
4664-
index->nTx = 0;
4665-
index->nChainTx = 0;
4666-
index->nSequenceId = 0;
4667-
// Make sure it gets written.
4668-
setDirtyBlockIndex.insert(index);
4669-
// Update indexes
4670-
setBlockIndexCandidates.erase(index);
4671-
auto ret = m_blockman.m_blocks_unlinked.equal_range(index->pprev);
4672-
while (ret.first != ret.second) {
4673-
if (ret.first->second == index) {
4674-
m_blockman.m_blocks_unlinked.erase(ret.first++);
4675-
} else {
4676-
++ret.first;
4677-
}
4678-
}
4679-
// Mark parent as eligible for main chain again
4680-
if (index->pprev && index->pprev->IsValid(BLOCK_VALID_TRANSACTIONS) && index->pprev->HaveTxsDownloaded()) {
4681-
setBlockIndexCandidates.insert(index->pprev);
4682-
}
4683-
}
4684-
4685-
bool CChainState::RewindBlockIndex(const CChainParams& params)
4686-
{
4687-
// Note that during -reindex-chainstate we are called with an empty m_chain!
46884652

4689-
// First erase all post-segwit blocks without witness not in the main chain,
4690-
// as this can we done without costly DisconnectTip calls. Active
4691-
// blocks will be dealt with below (releasing cs_main in between).
4692-
{
4693-
LOCK(cs_main);
4694-
for (const auto& entry : m_blockman.m_block_index) {
4695-
if (IsWitnessEnabled(entry.second->pprev, params.GetConsensus()) && !(entry.second->nStatus & BLOCK_OPT_WITNESS) && !m_chain.Contains(entry.second)) {
4696-
EraseBlockData(entry.second);
4697-
}
4698-
}
4699-
}
4653+
// At and above params.SegwitHeight, segwit consensus rules must be validated
4654+
CBlockIndex* block{m_chain.Tip()};
4655+
const int segwit_height{params.GetConsensus().SegwitHeight};
47004656

4701-
// Find what height we need to reorganize to.
4702-
CBlockIndex *tip;
4703-
int nHeight = 1;
4704-
{
4705-
LOCK(cs_main);
4706-
while (nHeight <= m_chain.Height()) {
4707-
// Although SCRIPT_VERIFY_WITNESS is now generally enforced on all
4708-
// blocks in ConnectBlock, we don't need to go back and
4709-
// re-download/re-verify blocks from before segwit actually activated.
4710-
if (IsWitnessEnabled(m_chain[nHeight - 1], params.GetConsensus()) && !(m_chain[nHeight]->nStatus & BLOCK_OPT_WITNESS)) {
4711-
break;
4712-
}
4713-
nHeight++;
4714-
}
4715-
4716-
tip = m_chain.Tip();
4717-
}
4718-
// nHeight is now the height of the first insufficiently-validated block, or tipheight + 1
4719-
4720-
BlockValidationState state;
4721-
// Loop until the tip is below nHeight, or we reach a pruned block.
4722-
while (!ShutdownRequested()) {
4723-
{
4724-
LOCK(cs_main);
4725-
LOCK(m_mempool.cs);
4726-
// Make sure nothing changed from under us (this won't happen because RewindBlockIndex runs before importing/network are active)
4727-
assert(tip == m_chain.Tip());
4728-
if (tip == nullptr || tip->nHeight < nHeight) break;
4729-
if (fPruneMode && !(tip->nStatus & BLOCK_HAVE_DATA)) {
4730-
// If pruning, don't try rewinding past the HAVE_DATA point;
4731-
// since older blocks can't be served anyway, there's
4732-
// no need to walk further, and trying to DisconnectTip()
4733-
// will fail (and require a needless reindex/redownload
4734-
// of the blockchain).
4735-
break;
4736-
}
4737-
4738-
// Disconnect block
4739-
if (!DisconnectTip(state, params, nullptr)) {
4740-
return error("RewindBlockIndex: unable to disconnect block at height %i (%s)", tip->nHeight, state.ToString());
4741-
}
4742-
4743-
// Reduce validity flag and have-data flags.
4744-
// We do this after actual disconnecting, otherwise we'll end up writing the lack of data
4745-
// to disk before writing the chainstate, resulting in a failure to continue if interrupted.
4746-
// Note: If we encounter an insufficiently validated block that
4747-
// is on m_chain, it must be because we are a pruning node, and
4748-
// this block or some successor doesn't HAVE_DATA, so we were unable to
4749-
// rewind all the way. Blocks remaining on m_chain at this point
4750-
// must not have their validity reduced.
4751-
EraseBlockData(tip);
4752-
4753-
tip = tip->pprev;
4754-
}
4755-
// Make sure the queue of validation callbacks doesn't grow unboundedly.
4756-
LimitValidationInterfaceQueue();
4757-
4758-
// Occasionally flush state to disk.
4759-
if (!FlushStateToDisk(params, state, FlushStateMode::PERIODIC)) {
4760-
LogPrintf("RewindBlockIndex: unable to flush state to disk (%s)\n", state.ToString());
4761-
return false;
4762-
}
4763-
}
4764-
4765-
{
4766-
LOCK(cs_main);
4767-
if (m_chain.Tip() != nullptr) {
4768-
// We can't prune block index candidates based on our tip if we have
4769-
// no tip due to m_chain being empty!
4770-
PruneBlockIndexCandidates();
4771-
4772-
CheckBlockIndex(params.GetConsensus());
4773-
4774-
// FlushStateToDisk can possibly read ::ChainActive(). Be conservative
4775-
// and skip it here, we're about to -reindex-chainstate anyway, so
4776-
// it'll get called a bunch real soon.
4777-
BlockValidationState state;
4778-
if (!FlushStateToDisk(params, state, FlushStateMode::ALWAYS)) {
4779-
LogPrintf("RewindBlockIndex: unable to flush state to disk (%s)\n", state.ToString());
4780-
return false;
4781-
}
4657+
while (block != nullptr && block->nHeight >= segwit_height) {
4658+
if (!(block->nStatus & BLOCK_OPT_WITNESS)) {
4659+
// block is insufficiently validated for a segwit client
4660+
return true;
47824661
}
4662+
block = block->pprev;
47834663
}
47844664

4785-
return true;
4665+
return false;
47864666
}
47874667

47884668
void CChainState::UnloadBlockIndex() {

src/validation.h

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -715,7 +715,9 @@ class CChainState
715715

716716
/** Replay blocks that aren't fully applied to the database. */
717717
bool ReplayBlocks(const CChainParams& params);
718-
bool RewindBlockIndex(const CChainParams& params) LOCKS_EXCLUDED(cs_main);
718+
719+
/** Whether the chain state needs to be redownloaded due to lack of witness data */
720+
[[nodiscard]] bool NeedsRedownload(const CChainParams& params) const EXCLUSIVE_LOCKS_REQUIRED(cs_main);
719721
/** Ensures we have a genesis block in the block tree, possibly writing one to disk. */
720722
bool LoadGenesisBlock(const CChainParams& chainparams);
721723

@@ -762,9 +764,6 @@ class CChainState
762764

763765
bool RollforwardBlock(const CBlockIndex* pindex, CCoinsViewCache& inputs, const CChainParams& params) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
764766

765-
//! Mark a block as not having block data
766-
void EraseBlockData(CBlockIndex* index) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
767-
768767
void CheckForkWarningConditions() EXCLUSIVE_LOCKS_REQUIRED(cs_main);
769768
void InvalidChainFound(CBlockIndex* pindexNew) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
770769

test/functional/p2p_segwit.py

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2020,22 +2020,33 @@ def test_non_standard_witness(self):
20202020
def test_upgrade_after_activation(self):
20212021
"""Test the behavior of starting up a segwit-aware node after the softfork has activated."""
20222022

2023-
self.restart_node(2, extra_args=["-segwitheight={}".format(SEGWIT_HEIGHT)])
2023+
# All nodes are caught up and node 2 is a pre-segwit node that will soon upgrade.
2024+
for n in range(2):
2025+
assert_equal(self.nodes[n].getblockcount(), self.nodes[2].getblockcount())
2026+
assert softfork_active(self.nodes[n], "segwit")
2027+
assert SEGWIT_HEIGHT < self.nodes[2].getblockcount()
2028+
assert 'segwit' not in self.nodes[2].getblockchaininfo()['softforks']
2029+
2030+
# Restarting node 2 should result in a shutdown because the blockchain consists of
2031+
# insufficiently validated blocks per segwit consensus rules.
2032+
self.stop_node(2)
2033+
with self.nodes[2].assert_debug_log(expected_msgs=[
2034+
f"Witness data for blocks after height {SEGWIT_HEIGHT} requires validation. Please restart with -reindex."], timeout=10):
2035+
self.nodes[2].start([f"-segwitheight={SEGWIT_HEIGHT}"])
2036+
2037+
# As directed, the user restarts the node with -reindex
2038+
self.start_node(2, extra_args=["-reindex", f"-segwitheight={SEGWIT_HEIGHT}"])
2039+
2040+
# With the segwit consensus rules, the node is able to validate only up to SEGWIT_HEIGHT - 1
2041+
assert_equal(self.nodes[2].getblockcount(), SEGWIT_HEIGHT - 1)
20242042
self.connect_nodes(0, 2)
20252043

20262044
# We reconnect more than 100 blocks, give it plenty of time
2045+
# sync_blocks() also verifies the best block hash is the same for all nodes
20272046
self.sync_blocks(timeout=240)
20282047

2029-
# Make sure that this peer thinks segwit has activated.
2030-
assert softfork_active(self.nodes[2], 'segwit')
2031-
2032-
# Make sure this peer's blocks match those of node0.
2033-
height = self.nodes[2].getblockcount()
2034-
while height >= 0:
2035-
block_hash = self.nodes[2].getblockhash(height)
2036-
assert_equal(block_hash, self.nodes[0].getblockhash(height))
2037-
assert_equal(self.nodes[0].getblock(block_hash), self.nodes[2].getblock(block_hash))
2038-
height -= 1
2048+
# The upgraded node should now have segwit activated
2049+
assert softfork_active(self.nodes[2], "segwit")
20392050

20402051
@subtest # type: ignore
20412052
def test_witness_sigops(self):

0 commit comments

Comments
 (0)