Fix chain_recovery to properly rebuild from checkpoint
This commit is contained in:
+108
-41
@@ -645,9 +645,9 @@ bool SideChain::add_external_block(PoolBlock& block, std::vector<hash>& missing_
|
||||
if (is_main_thread()) {
|
||||
const uint64_t seed_height = p2pool::get_seed_height(block.m_txinGenHeight);
|
||||
m_pool->fetch_mainchain_block(seed_height);
|
||||
forget_incoming_block(block);
|
||||
}
|
||||
// If not on main thread, don't forget - block stays pending until mainchain data arrives via prefetch
|
||||
// Always forget so block can be re-processed when mainchain data arrives
|
||||
forget_incoming_block(block);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -768,6 +768,12 @@ bool SideChain::add_block(const PoolBlock& block)
|
||||
", mainchain height = " << block.m_txinGenHeight <<
|
||||
", verified = " << (block.m_verified ? 1 : 0)
|
||||
);
|
||||
// Extra log for blocks near checkpoint height during recovery
|
||||
const uint64_t cp_height = get_latest_checkpoint_height();
|
||||
if (block.m_sidechainHeight >= cp_height && block.m_sidechainHeight <= cp_height + 5) {
|
||||
LOGINFO(0, "RECOVERY DEBUG: add_block height " << block.m_sidechainHeight
|
||||
<< " parent=" << block.m_parent);
|
||||
}
|
||||
|
||||
PoolBlock* new_block = new PoolBlock(block);
|
||||
{
|
||||
@@ -1625,6 +1631,11 @@ void SideChain::verify_loop(PoolBlock* block)
|
||||
on_block_rejected(block, "verification failed");
|
||||
}
|
||||
else {
|
||||
// Extra logging for blocks near checkpoint during recovery
|
||||
if (block->m_sidechainHeight <= get_latest_checkpoint_height() + 10) {
|
||||
LOGINFO(0, "RECOVERY DEBUG: VERIFIED block " << block->m_sidechainHeight
|
||||
<< " (parent " << (block->m_sidechainHeight > 0 ? block->m_sidechainHeight - 1 : 0) << " was verified)");
|
||||
}
|
||||
LOGINFO(3, "verified block at height = " << block->m_sidechainHeight <<
|
||||
", depth = " << block->m_depth <<
|
||||
", id = " << block->m_sidechainId <<
|
||||
@@ -1752,7 +1763,12 @@ void SideChain::verify(PoolBlock* block)
|
||||
return;
|
||||
}
|
||||
if (!it->second->m_verified) {
|
||||
LOGWARN(3, "block at height = " << block->m_sidechainHeight << " parent " << block->m_parent << " found but NOT VERIFIED (parent height = " << it->second->m_sidechainHeight << ")");
|
||||
// Only log for blocks close to checkpoint to reduce spam
|
||||
if (block->m_sidechainHeight <= get_latest_checkpoint_height() + 10) {
|
||||
LOGINFO(0, "RECOVERY DEBUG: block " << block->m_sidechainHeight << " waiting for parent "
|
||||
<< it->second->m_sidechainHeight << " (parent verified=" << (it->second->m_verified ? 1 : 0)
|
||||
<< " invalid=" << (it->second->m_invalid ? 1 : 0) << ")");
|
||||
}
|
||||
block->m_verified = false;
|
||||
return;
|
||||
}
|
||||
@@ -1933,11 +1949,8 @@ void SideChain::verify(PoolBlock* block)
|
||||
}
|
||||
|
||||
difficulty_type diff;
|
||||
if (parent == m_chainTip) {
|
||||
LOGINFO(6, "block " << block->m_sidechainId << " is built on top of the current chain tip, using current difficulty for verification");
|
||||
diff = difficulty();
|
||||
}
|
||||
else if (!get_difficulty(parent, m_difficultyData, diff)) {
|
||||
// Always use get_difficulty for verification - difficulty() may be stale after recovery
|
||||
if (!get_difficulty(parent, m_difficultyData, diff)) {
|
||||
// Don't mark as invalid - mainchain might not be synced yet
|
||||
// Leave unverified so it can be retried later
|
||||
LOGWARN(3, "block at height = " << block->m_sidechainHeight << ", id = " << block->m_sidechainId
|
||||
@@ -2664,13 +2677,25 @@ void SideChain::get_missing_blocks(unordered_set<hash>& missing_blocks) const
|
||||
|
||||
ReadLock lock(m_sidechainLock);
|
||||
|
||||
const uint64_t cp_height = get_latest_checkpoint_height();
|
||||
uint64_t lowest_unverified = UINT64_MAX;
|
||||
|
||||
for (auto& b : m_blocksById) {
|
||||
if (b.second->m_verified) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (b.second->m_sidechainHeight < lowest_unverified) {
|
||||
lowest_unverified = b.second->m_sidechainHeight;
|
||||
}
|
||||
|
||||
if (!b.second->m_parent.empty() && (m_blocksById.find(b.second->m_parent) == m_blocksById.end())) {
|
||||
missing_blocks.insert(b.second->m_parent);
|
||||
// Log missing parents near checkpoint
|
||||
if (b.second->m_sidechainHeight <= cp_height + 5) {
|
||||
LOGINFO(0, "RECOVERY DEBUG: block " << b.second->m_sidechainHeight
|
||||
<< " needs missing parent " << b.second->m_parent);
|
||||
}
|
||||
}
|
||||
|
||||
int num_missing_uncles = 0;
|
||||
@@ -2688,6 +2713,11 @@ void SideChain::get_missing_blocks(unordered_set<hash>& missing_blocks) const
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Summary log for recovery debugging
|
||||
if (!missing_blocks.empty() && lowest_unverified <= cp_height + 10) {
|
||||
LOGINFO(0, "RECOVERY DEBUG: " << missing_blocks.size() << " missing blocks, lowest unverified=" << lowest_unverified);
|
||||
}
|
||||
}
|
||||
|
||||
void SideChain::retry_unverified_blocks()
|
||||
@@ -3251,49 +3281,86 @@ void SideChain::reset_to_checkpoint(uint64_t checkpoint_height)
|
||||
|
||||
WriteLock lock(m_sidechainLock);
|
||||
|
||||
// SAFER APPROACH: Don't delete blocks - just mark them invalid and reset state
|
||||
// Normal pruning will clean them up later. This avoids complex threading issues.
|
||||
|
||||
size_t invalidated_count = 0;
|
||||
for (auto& kv : m_blocksById) {
|
||||
if (kv.second->m_sidechainHeight > checkpoint_height) {
|
||||
kv.second->m_invalid = true;
|
||||
kv.second->m_verified = true;
|
||||
++invalidated_count;
|
||||
}
|
||||
}
|
||||
|
||||
LOGINFO(0, "Marked " << invalidated_count << " blocks as invalid after checkpoint " << checkpoint_height);
|
||||
|
||||
// Reset chain tip to checkpoint block - find by checkpoint ID
|
||||
// Find the nearest available checkpoint at or below target height
|
||||
PoolBlock* checkpoint_block = nullptr;
|
||||
uint64_t actual_checkpoint_height = 0;
|
||||
{
|
||||
ReadLock cpLock(m_checkpointsLock);
|
||||
for (const Checkpoint& cp : m_checkpoints) {
|
||||
if (cp.height == checkpoint_height) {
|
||||
auto it = m_blocksById.find(cp.id);
|
||||
if (it != m_blocksById.end()) {
|
||||
checkpoint_block = it->second;
|
||||
for (auto it = m_checkpoints.rbegin(); it != m_checkpoints.rend(); ++it) {
|
||||
if (it->height <= checkpoint_height) {
|
||||
auto block_it = m_blocksById.find(it->id);
|
||||
if (block_it != m_blocksById.end()) {
|
||||
checkpoint_block = block_it->second;
|
||||
actual_checkpoint_height = it->height;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (checkpoint_block) {
|
||||
checkpoint_block->m_verified = true; // Mark verified for propagation
|
||||
m_chainTip = checkpoint_block;
|
||||
LOGINFO(0, "Chain tip reset to height " << checkpoint_height
|
||||
<< ", id " << checkpoint_block->m_sidechainId);
|
||||
} else {
|
||||
m_chainTip = nullptr;
|
||||
LOGWARN(0, "No block found at checkpoint height - chain tip set to null");
|
||||
|
||||
if (!checkpoint_block) {
|
||||
LOGWARN(0, "No checkpoint found at or below height " << checkpoint_height << " - recovery aborted");
|
||||
m_recoveryMode = false;
|
||||
m_pendingRecoveryHeight = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (actual_checkpoint_height != checkpoint_height) {
|
||||
LOGINFO(0, "Using nearest checkpoint at height " << actual_checkpoint_height << " (requested " << checkpoint_height << ")");
|
||||
}
|
||||
|
||||
// Remove blocks above checkpoint so fresh ones come from peers
|
||||
// Collect block info to remove (can't modify map while iterating)
|
||||
std::vector<std::pair<hash, PoolBlock::full_id>> blocks_to_remove;
|
||||
for (const auto& kv : m_blocksById) {
|
||||
if (kv.second->m_sidechainHeight > actual_checkpoint_height) {
|
||||
blocks_to_remove.emplace_back(kv.first, kv.second->get_full_id());
|
||||
}
|
||||
}
|
||||
|
||||
// Remove from all indices
|
||||
for (const auto& [id, full_id] : blocks_to_remove) {
|
||||
auto it = m_blocksById.find(id);
|
||||
if (it != m_blocksById.end()) {
|
||||
PoolBlock* block = it->second;
|
||||
|
||||
// Remove from blocksByHeight vector
|
||||
auto height_it = m_blocksByHeight.find(block->m_sidechainHeight);
|
||||
if (height_it != m_blocksByHeight.end()) {
|
||||
auto& vec = height_it->second;
|
||||
vec.erase(std::remove(vec.begin(), vec.end(), block), vec.end());
|
||||
if (vec.empty()) {
|
||||
m_blocksByHeight.erase(height_it);
|
||||
}
|
||||
}
|
||||
|
||||
m_blocksById.erase(it);
|
||||
delete block;
|
||||
}
|
||||
}
|
||||
|
||||
// Forget deleted blocks so they can be re-downloaded from peers
|
||||
{
|
||||
MutexLock lock(m_incomingBlocksLock);
|
||||
for (const auto& [id, full_id] : blocks_to_remove) {
|
||||
m_incomingBlocks.erase(full_id);
|
||||
}
|
||||
}
|
||||
|
||||
LOGINFO(0, "Removed " << blocks_to_remove.size() << " blocks above checkpoint " << actual_checkpoint_height);
|
||||
LOGINFO(0, "Cleared " << blocks_to_remove.size() << " entries from incoming blocks tracking");
|
||||
|
||||
checkpoint_block->m_verified = true; // Mark verified for propagation
|
||||
checkpoint_block->m_invalid = false; // Ensure not marked invalid
|
||||
m_chainTip = checkpoint_block;
|
||||
LOGINFO(0, "Chain tip reset to height " << actual_checkpoint_height
|
||||
<< ", id " << checkpoint_block->m_sidechainId
|
||||
<< ", verified=" << (checkpoint_block->m_verified ? 1 : 0));
|
||||
|
||||
// Clear checkpoints after this point
|
||||
{
|
||||
WriteLock cpLock(m_checkpointsLock);
|
||||
while (!m_checkpoints.empty() && m_checkpoints.back().height > checkpoint_height) {
|
||||
while (!m_checkpoints.empty() && m_checkpoints.back().height > actual_checkpoint_height) {
|
||||
m_checkpoints.pop_back();
|
||||
}
|
||||
}
|
||||
@@ -3302,11 +3369,11 @@ void SideChain::reset_to_checkpoint(uint64_t checkpoint_height)
|
||||
m_externalBlockFailures = 0;
|
||||
m_pendingRecoveryHeight = 0;
|
||||
m_recoveryMode = false;
|
||||
|
||||
|
||||
LOGINFO(0, "\n========================================");
|
||||
LOGINFO(0, "RESET COMPLETE - RESYNCING FROM " << checkpoint_height);
|
||||
LOGINFO(0, "========================================\n");
|
||||
|
||||
|
||||
// Mining will be re-enabled after resync completes
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user