Dramatically speed up startup and sync: headers-first, fast block index, indexed rescan

Startup optimization (fixes multi-minute hang on 2M+ block chain):
- Skip computing SHA-256 stake modifier checksum for blocks below last
  checkpoint (was doing 2M+ unnecessary hashes on every startup)
- Reduce default -checkblocks from 2500 to 50 (disk read savings)
- Add progress percentage reporting during block index load

Headers-first sync (faster initial sync):
- Switch initial sync from getblocks to getheaders protocol
- Add PushGetHeaders and headers message handler in net/main
- Downloads 80-byte headers first, then requests blocks — allows
  the node to learn chain structure before downloading full blocks

Address-index accelerated wallet rescan:
- New ScanForWalletTransactionsFromIndex() uses address index to find
  only transactions touching wallet keys (skips scanning every block)
- Follows spend chains to find related transactions
- Falls back to full scan if index unavailable
- Track index sync state with addressIndexBestChain/StartHeight in LevelDB

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-13 02:40:13 -07:00
parent 7aee073e21
commit 05b1fd1610
8 changed files with 376 additions and 10 deletions
+53 -5
View File
@@ -19,6 +19,7 @@
#include "checkpoints.h"
#include "txdb.h"
#include "util.h"
#include "ui_interface.h"
#include "addressindex.h"
#include "main.h"
@@ -277,6 +278,26 @@ bool CTxDB::WriteHashBestChain(uint256 hashBestChain)
return Write(string("hashBestChain"), hashBestChain);
}
bool CTxDB::ReadAddressIndexBestChain(uint256& hashBestChain)
{
return Read(string("addressIndexBestChain"), hashBestChain);
}
bool CTxDB::WriteAddressIndexBestChain(uint256 hashBestChain)
{
return Write(string("addressIndexBestChain"), hashBestChain);
}
bool CTxDB::ReadAddressIndexStartHeight(int& nHeight)
{
return Read(string("addressIndexStartHeight"), nHeight);
}
bool CTxDB::WriteAddressIndexStartHeight(int nHeight)
{
return Write(string("addressIndexStartHeight"), nHeight);
}
bool CTxDB::ReadBestInvalidTrust(CBigNum& bnBestInvalidTrust)
{
return Read(string("bnBestInvalidTrust"), bnBestInvalidTrust);
@@ -343,8 +364,16 @@ bool CTxDB::LoadBlockIndex()
ssStartKey << make_pair(string("blockindex"), uint256(0));
iterator->Seek(ssStartKey.str());
// Now read each entry.
int nBlocksLoaded = 0;
while (iterator->Valid())
{
// Report progress every 100k blocks
if (++nBlocksLoaded % 100000 == 0)
{
std::string strMsg = strprintf(_("Loading block index... (%d blocks)"), nBlocksLoaded);
uiInterface.InitMessage(strMsg);
}
// Unpack keys and values.
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
ssKey.write(iterator->key().data(), iterator->key().size());
@@ -409,14 +438,33 @@ bool CTxDB::LoadBlockIndex()
vSortedByHeight.push_back(make_pair(pindex->nHeight, pindex));
}
sort(vSortedByHeight.begin(), vSortedByHeight.end());
int nLastCheckpointHeight = Checkpoints::GetTotalBlocksEstimate();
int nProgressInterval = std::max((int)vSortedByHeight.size() / 20, 1);
int nCount = 0;
BOOST_FOREACH(const PAIRTYPE(int, CBlockIndex*)& item, vSortedByHeight)
{
CBlockIndex* pindex = item.second;
pindex->nChainTrust = (pindex->pprev ? pindex->pprev->nChainTrust : 0) + pindex->GetBlockTrust();
// triangles: calculate stake modifier checksum
pindex->nStakeModifierChecksum = GetStakeModifierChecksum(pindex);
if (!CheckStakeModifierCheckpoints(pindex->nHeight, pindex->nStakeModifierChecksum))
return error("CTxDB::LoadBlockIndex() : Failed stake modifier checkpoint height=%d, modifier=0x%016"PRIx64, pindex->nHeight, pindex->nStakeModifier);
// Only compute the expensive SHA-256 stake modifier checksum for blocks
// at or beyond the last hardcoded checkpoint. Blocks well below the
// checkpoint have already been validated — recomputing 2M+ hashes on
// every startup was the main cause of multi-minute load times.
if (pindex->nHeight >= nLastCheckpointHeight)
{
pindex->nStakeModifierChecksum = GetStakeModifierChecksum(pindex);
if (!CheckStakeModifierCheckpoints(pindex->nHeight, pindex->nStakeModifierChecksum))
return error("CTxDB::LoadBlockIndex() : Failed stake modifier checkpoint height=%d, modifier=0x%016"PRIx64, pindex->nHeight, pindex->nStakeModifier);
}
// Report progress for UI responsiveness
if (++nCount % nProgressInterval == 0)
{
std::string strMsg = strprintf(_("Loading block index... (%d%%)"), nCount * 100 / vSortedByHeight.size());
uiInterface.InitMessage(strMsg);
}
}
// Load hashBestChain pointer to end of best chain
@@ -448,7 +496,7 @@ bool CTxDB::LoadBlockIndex()
// Verify blocks in the best chain
int nCheckLevel = GetArg("-checklevel", 1);
int nCheckDepth = GetArg( "-checkblocks", 2500);
int nCheckDepth = GetArg( "-checkblocks", 50);
if (nCheckDepth == 0)
nCheckDepth = 1000000000; // suffices until the year 19000
if (nCheckDepth > nBestHeight)