Remove -allowfastimport (FastImport) entirely
FastImport was the legacy path for rebuilding the block index from a
local blk0001.dat. With v2 UTXO snapshots now containing embedded
blocks, FastImport is redundant and dangerous (could silently index
a forked chain from a stale blk0001.dat).
Changes:
- src/main.cpp: delete FastImportBlockFile() function (~270 lines)
- src/main.h: delete FastImportBlockFile() declaration
- src/init.cpp: delete -allowfastimport flag handler block
remove from help text
clean up stale comments referencing FastImportBlockFile
- src/bootstrap.cpp: update stale comments
v2 snapshot loading (auto-download from bootstrap or local placement
of utxo-snapshot.bin + manifest) is now the only supported sync start.
Tested: daemon builds, runs, chain state preserved across restart.
Binary SHA: 3f26f6202947a8dc0f7933314829702aafa1e42c968368ab7ec043d57baa9519
DNS2 + DNS3 running this build, both on correct chain.
Not bumped to v5.9.21 per Sami's preference. Next formal release
will inherit this change.
This commit is contained in:
+3
-3
@@ -52,7 +52,7 @@ bool NeedsBootstrap(const fs::path& dataDir)
|
||||
{
|
||||
// Need bootstrap if there's no chain database (the UTXO set / block index).
|
||||
// blk0001.dat alone is NOT sufficient — it's raw block data that requires
|
||||
// FastImport to build an index, and FastImport is disabled by default.
|
||||
// (fast-import was removed; UTXO snapshot is the only sync path)
|
||||
// Check for both LevelDB (txleveldb/) and RocksDB (chainstate/) backends.
|
||||
bool hasChainDb = fs::exists(dataDir / "txleveldb")
|
||||
|| fs::exists(dataDir / "blocks" / "chainstate")
|
||||
@@ -734,7 +734,7 @@ bool DownloadBootstrap(const std::string& host,
|
||||
|
||||
// Check if the archive included a trusted pre-built index for the active
|
||||
// backend with a valid snapshot.manifest. If verified, keep it to skip the
|
||||
// multi-hour FastImportBlockFile() rebuild.
|
||||
// multi-hour rebuild (fast-import removed; UTXO snapshot is the only sync path).
|
||||
fs::path chainDbPath = GetChainDataDir();
|
||||
fs::path database = dataDir / "database";
|
||||
fs::path manifestPath = dataDir / "snapshot.manifest";
|
||||
@@ -767,7 +767,7 @@ bool DownloadBootstrap(const std::string& host,
|
||||
|
||||
if (!keepIndex) {
|
||||
// No valid manifest or verification failed - delete the index.
|
||||
// FastImportBlockFile() will rebuild from blk0001.dat on next startup.
|
||||
// The block index will be rebuilt from the UTXO snapshot on next startup.
|
||||
printf("Bootstrap: removing extracted %s/ (will rebuild index from blk0001.dat)\n",
|
||||
GetChainDataDir().filename().string().c_str());
|
||||
if (fs::exists(chainDbPath))
|
||||
|
||||
+4
-28
@@ -533,7 +533,6 @@ std::string HelpMessage()
|
||||
" -seedurl=<host> " + _("HTTP seed list host (default: seeds.cryptographic-triangles.org)") + "\n" +
|
||||
" -noseedurl " + _("Disable HTTP seed list fetch on startup") + "\n" +
|
||||
" -autorerebuild=<n> " + _("If our chain is more than <n> blocks behind peers, wipe chain DB and shutdown for clean restart (default: 0=disabled)") + "\n" +
|
||||
" -allowfastimport " + _("Permit FastImport as fallback (operator opt-in only; default off)") + "\n" +
|
||||
" -banscore=<n> " + _("Threshold for disconnecting misbehaving peers (default: 100)") + "\n" +
|
||||
" -bantime=<n> " + _("Number of seconds to keep misbehaving peers from reconnecting (default: 86400)") + "\n" +
|
||||
" -par=<n> " + _("Set the number of script verification threads (default: auto, 0 = auto, 1 = single-threaded)") + "\n" +
|
||||
@@ -1160,7 +1159,7 @@ bool AppInit2()
|
||||
}
|
||||
|
||||
// Handle -reindex: delete the chain DB so it gets rebuilt from the raw
|
||||
// blk*.dat files via FastImportBlockFile(). This recalculates money
|
||||
// blk*.dat files. This recalculates money
|
||||
// supply, tx index, and UTXO set from scratch. Backend-agnostic via
|
||||
// WipeChainDataDir(), which resolves the directory per the configured
|
||||
// -chaindb backend.
|
||||
@@ -1209,38 +1208,15 @@ bool AppInit2()
|
||||
}
|
||||
|
||||
// AutoRebuild: if -autorerebuild is set and we are behind peers, wipe chain DB
|
||||
// and shutdown for clean restart. Must run before FastImportBlockFile below.
|
||||
// and shutdown for clean restart.
|
||||
MaybeAutoRebuild(GetArg("-autorerebuild", 0));
|
||||
if (fRequestShutdown) {
|
||||
printf("AutoRebuild: shutdown requested before chain load complete\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the block index is empty but blk0001.dat exists (bootstrap download),
|
||||
// fast-import would normally rebuild from the block file. Per Sami: FastImport
|
||||
// is REMOVED as a primary path — the UTXO snapshot is the canonical sync start.
|
||||
// FastImport is gated behind -allowfastimport for explicit operator opt-in only
|
||||
// (emergency recovery, snapshot format incompatibility, etc).
|
||||
if (nBestHeight == 0 && std::filesystem::exists(GetDataDir() / "blk0001.dat")
|
||||
&& mapBlockIndex.size() <= 1)
|
||||
{
|
||||
if (!GetBoolArg("-allowfastimport", false))
|
||||
{
|
||||
printf("FastImport: blk0001.dat present but -allowfastimport not set — "
|
||||
"ignoring block file, will sync from network via UTXO snapshot.\n");
|
||||
// Remove the stale blk0001.dat so it doesn't trigger again
|
||||
std::filesystem::remove(GetDataDir() / "blk0001.dat");
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("FastImport: WARNING -allowfastimport is set; rebuilding from local blk0001.dat.\n");
|
||||
uiInterface.InitMessage(_("Importing bootstrap blocks..."));
|
||||
printf("Block index empty but blk0001.dat exists - running fast import...\n");
|
||||
int64_t nFastImportStart = GetTimeMillis();
|
||||
FastImportBlockFile();
|
||||
StartupPerfLog("bootstrap_fast_import", GetTimeMillis() - nFastImportStart, strprintf("bestheight=%d", nBestHeight));
|
||||
}
|
||||
}
|
||||
// Block index loaded. With fast-import removed, the only supported sync path
|
||||
// is the UTXO snapshot (auto-downloaded from bootstrap or placed manually in datadir).
|
||||
|
||||
// as LoadBlockIndex can take several minutes, it's possible the user
|
||||
// requested to kill triangles-qt during the last operation. If so, exit.
|
||||
|
||||
-276
@@ -3641,287 +3641,11 @@ bool LoadExternalBlockFile(FILE* fileIn)
|
||||
return nLoaded > 0;
|
||||
}
|
||||
|
||||
bool FastImportBlockFile()
|
||||
{
|
||||
// Fast block import: reads blk0001.dat and builds the block index
|
||||
// directly without re-writing block data. LevelDB writes are batched
|
||||
// every 200K blocks for speed. Only used for trusted bootstrap data
|
||||
// (blocks below the hardcoded checkpoint).
|
||||
|
||||
fs::path blkPath = GetDataDir() / "blk0001.dat";
|
||||
if (!fs::exists(blkPath))
|
||||
return false;
|
||||
|
||||
printf("FastImportBlockFile: starting from %s\n", blkPath.string().c_str());
|
||||
int64_t nStart = GetTimeMillis();
|
||||
|
||||
FILE* fileIn = fopen(blkPath.string().c_str(), "rb");
|
||||
if (!fileIn)
|
||||
return false;
|
||||
|
||||
// Get file size for progress
|
||||
fseek(fileIn, 0, SEEK_END);
|
||||
int64_t nFileSize = ftell(fileIn);
|
||||
fseek(fileIn, 0, SEEK_SET);
|
||||
|
||||
int nLoaded = 0;
|
||||
int64_t nLastProgressReport = 0;
|
||||
|
||||
{
|
||||
LOCK(cs_main);
|
||||
CAutoFile blkdat(fileIn, SER_DISK, CLIENT_VERSION);
|
||||
|
||||
auto txdb_holder = MakeChainDB(); CTxDBBase& txdb = *txdb_holder;
|
||||
txdb.TxnBegin();
|
||||
|
||||
unsigned int nPos = 0;
|
||||
while (nPos != (unsigned int)-1 && blkdat.good() && !fRequestShutdown)
|
||||
{
|
||||
// Find message start bytes (same scan as LoadExternalBlockFile)
|
||||
unsigned char pchData[65536];
|
||||
do {
|
||||
fseek(blkdat, nPos, SEEK_SET);
|
||||
int nRead = fread(pchData, 1, sizeof(pchData), blkdat);
|
||||
if (nRead <= 8)
|
||||
{
|
||||
nPos = (unsigned int)-1;
|
||||
break;
|
||||
}
|
||||
void* nFind = memchr(pchData, pchMessageStart[0], nRead+1-sizeof(pchMessageStart));
|
||||
if (nFind)
|
||||
{
|
||||
if (memcmp(nFind, pchMessageStart, sizeof(pchMessageStart))==0)
|
||||
{
|
||||
nPos += ((unsigned char*)nFind - pchData) + sizeof(pchMessageStart);
|
||||
break;
|
||||
}
|
||||
nPos += ((unsigned char*)nFind - pchData) + 1;
|
||||
}
|
||||
else
|
||||
nPos += sizeof(pchData) - sizeof(pchMessageStart) + 1;
|
||||
} while(!fRequestShutdown);
|
||||
|
||||
if (nPos == (unsigned int)-1)
|
||||
break;
|
||||
|
||||
fseek(blkdat, nPos, SEEK_SET);
|
||||
unsigned int nSize;
|
||||
blkdat >> nSize;
|
||||
|
||||
if (nSize == 0 || nSize > MAX_BLOCK_SIZE)
|
||||
{
|
||||
nPos += 4 + nSize;
|
||||
continue;
|
||||
}
|
||||
|
||||
// nBlockPos = file position where the block data starts
|
||||
// (after 4-byte message start + 4-byte size)
|
||||
unsigned int nBlockPos = nPos + 4;
|
||||
|
||||
CBlock block;
|
||||
blkdat >> block;
|
||||
|
||||
uint256 hash = block.GetHash();
|
||||
if (mapBlockIndex.count(hash))
|
||||
{
|
||||
nPos += 4 + nSize;
|
||||
continue; // already indexed
|
||||
}
|
||||
|
||||
// Create CBlockIndex
|
||||
CBlockIndex* pindexNew = new CBlockIndex(1, nBlockPos, block);
|
||||
if (!pindexNew)
|
||||
break;
|
||||
|
||||
// Link to previous block
|
||||
auto miPrev = mapBlockIndex.find(block.hashPrevBlock);
|
||||
if (miPrev != mapBlockIndex.end())
|
||||
{
|
||||
pindexNew->pprev = miPrev->second;
|
||||
pindexNew->nHeight = pindexNew->pprev->nHeight + 1;
|
||||
}
|
||||
|
||||
// Chain trust
|
||||
pindexNew->nChainTrust = (pindexNew->pprev ? pindexNew->pprev->nChainTrust : 0) + pindexNew->GetBlockTrust();
|
||||
|
||||
// Stake entropy bit
|
||||
pindexNew->SetStakeEntropyBit(block.GetStakeEntropyBit());
|
||||
|
||||
// Stake modifier (minimal for blocks far below checkpoint)
|
||||
int nCheckpointHeight = Checkpoints::GetTotalBlocksEstimate();
|
||||
if (pindexNew->nHeight >= nCheckpointHeight - 1000)
|
||||
{
|
||||
uint64_t nStakeModifier = 0;
|
||||
bool fGeneratedStakeModifier = false;
|
||||
ComputeNextStakeModifier(pindexNew->pprev, nStakeModifier, fGeneratedStakeModifier);
|
||||
pindexNew->SetStakeModifier(nStakeModifier, fGeneratedStakeModifier);
|
||||
}
|
||||
else
|
||||
{
|
||||
pindexNew->SetStakeModifier(0, pindexNew->nHeight == 0);
|
||||
}
|
||||
pindexNew->nStakeModifierChecksum = GetStakeModifierChecksum(pindexNew);
|
||||
|
||||
// PoS stake seen set
|
||||
if (pindexNew->IsProofOfStake())
|
||||
setStakeSeen.insert(make_pair(pindexNew->prevoutStake, pindexNew->nStakeTime));
|
||||
|
||||
// Insert into mapBlockIndex
|
||||
auto mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first;
|
||||
pindexNew->phashBlock = &mi->first;
|
||||
|
||||
// Link pnext for previous block
|
||||
if (pindexNew->pprev)
|
||||
pindexNew->pprev->pnext = pindexNew;
|
||||
|
||||
// Build tx index + UTXO entries, tracking money supply
|
||||
int64_t nBlockValueIn = 0;
|
||||
int64_t nBlockValueOut = 0;
|
||||
int64_t nFees = 0;
|
||||
unsigned int nTxPos = nBlockPos + ::GetSerializeSize(CBlock(), SER_DISK, CLIENT_VERSION)
|
||||
- (2 * GetSizeOfCompactSize(0)) + GetSizeOfCompactSize(block.vtx.size());
|
||||
for (size_t nTxIdx = 0; nTxIdx < block.vtx.size(); nTxIdx++)
|
||||
{
|
||||
const CTransaction& tx = block.vtx[nTxIdx];
|
||||
uint256 hashTx = tx.GetHash();
|
||||
CDiskTxPos posThisTx(1, nBlockPos, nTxPos);
|
||||
txdb.UpdateTxIndex(hashTx, CTxIndex(posThisTx, tx.vout.size()));
|
||||
nTxPos += ::GetSerializeSize(tx, SER_DISK, CLIENT_VERSION);
|
||||
|
||||
int64_t nTxValueOut = tx.GetValueOut();
|
||||
nBlockValueOut += nTxValueOut;
|
||||
|
||||
// UTXO entries — read input values before erasing for money supply
|
||||
if (!tx.IsCoinBase())
|
||||
{
|
||||
int64_t nTxValueIn = 0;
|
||||
for (const CTxIn& txin : tx.vin)
|
||||
{
|
||||
CUtxoEntry utxo;
|
||||
if (txdb.ReadUtxo(txin.prevout.hash, txin.prevout.n, utxo))
|
||||
nTxValueIn += utxo.nValue;
|
||||
if (fAddressIndex && !utxo.scriptPubKey.empty() && utxo.nValue != 0)
|
||||
{
|
||||
int nAType; uint160 aHash;
|
||||
if (GetAddressFromScript(utxo.scriptPubKey, nAType, aHash))
|
||||
{
|
||||
txdb.EraseAddressUtxo(nAType, aHash, txin.prevout.hash, txin.prevout.n);
|
||||
int64_t nABal = 0;
|
||||
txdb.ReadAddressBalance(nAType, aHash, nABal);
|
||||
nABal -= utxo.nValue;
|
||||
txdb.WriteAddressBalance(nAType, aHash, nABal);
|
||||
}
|
||||
}
|
||||
txdb.EraseUtxo(txin.prevout.hash, txin.prevout.n);
|
||||
}
|
||||
nBlockValueIn += nTxValueIn;
|
||||
if (!tx.IsCoinStake())
|
||||
nFees += nTxValueIn - nTxValueOut;
|
||||
}
|
||||
for (unsigned int k = 0; k < tx.vout.size(); k++)
|
||||
{
|
||||
if (!tx.vout[k].IsEmpty())
|
||||
{
|
||||
CUtxoEntry utxo;
|
||||
utxo.nValue = tx.vout[k].nValue;
|
||||
utxo.nHeight = pindexNew->nHeight;
|
||||
utxo.scriptPubKey = tx.vout[k].scriptPubKey;
|
||||
utxo.fCoinBase = tx.IsCoinBase();
|
||||
utxo.fCoinStake = tx.IsCoinStake();
|
||||
utxo.nTxTime = tx.nTime;
|
||||
txdb.WriteUtxo(hashTx, k, utxo);
|
||||
if (fAddressIndex && !tx.vout[k].scriptPubKey.empty() && tx.vout[k].nValue != 0)
|
||||
{
|
||||
int nAType; uint160 aHash;
|
||||
if (GetAddressFromScript(tx.vout[k].scriptPubKey, nAType, aHash))
|
||||
{
|
||||
txdb.WriteAddressUtxo(nAType, aHash, hashTx, k,
|
||||
tx.vout[k].nValue, pindexNew->nHeight, tx.vout[k].scriptPubKey);
|
||||
int64_t nABal = 0;
|
||||
txdb.ReadAddressBalance(nAType, aHash, nABal);
|
||||
nABal += tx.vout[k].nValue;
|
||||
txdb.WriteAddressBalance(nAType, aHash, nABal);
|
||||
txdb.WriteAddressTxId(nAType, aHash, pindexNew->nHeight, (int)nTxIdx, hashTx);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Money supply tracking — matches ConnectBlock formula
|
||||
pindexNew->nMint = nBlockValueOut - nBlockValueIn + nFees;
|
||||
pindexNew->nMoneySupply = (pindexNew->pprev ? pindexNew->pprev->nMoneySupply : 0) + nBlockValueOut - nBlockValueIn;
|
||||
|
||||
// Write block index to batch
|
||||
txdb.WriteBlockIndex(CDiskBlockIndex(pindexNew));
|
||||
|
||||
// Update best chain
|
||||
if (pindexNew->nChainTrust > nBestChainTrust)
|
||||
{
|
||||
hashBestChain = hash;
|
||||
pindexBest = pindexNew;
|
||||
pblockindexFBBHLast = nullptr;
|
||||
nBestHeight = pindexNew->nHeight;
|
||||
nBestChainTrust = pindexNew->nChainTrust;
|
||||
nTimeBestReceived = GetTime();
|
||||
}
|
||||
|
||||
// Set genesis block
|
||||
if (pindexGenesisBlock == nullptr && pindexNew->nHeight == 0)
|
||||
pindexGenesisBlock = pindexNew;
|
||||
|
||||
nLoaded++;
|
||||
nPos += 4 + nSize;
|
||||
|
||||
// Batch commit every 200K blocks for LevelDB efficiency
|
||||
if (nLoaded % 200000 == 0)
|
||||
{
|
||||
txdb.WriteHashBestChain(hashBestChain);
|
||||
txdb.TxnCommit();
|
||||
txdb.TxnBegin();
|
||||
}
|
||||
|
||||
// Report progress every 5000 blocks to keep GUI responsive.
|
||||
// AppInit2 runs on the GUI thread, so uiInterface.InitMessage
|
||||
// triggers processEvents() which prevents the window from freezing.
|
||||
if (nLoaded % 5000 == 0)
|
||||
{
|
||||
int pct = (nFileSize > 0) ? (int)((int64_t)nPos * 100 / nFileSize) : 0;
|
||||
printf("FastImport: %d blocks indexed (%d%%)\n", nLoaded, pct);
|
||||
uiInterface.InitMessage(strprintf(_("Importing blocks... %d indexed (%d%%)"), nLoaded, pct));
|
||||
}
|
||||
}
|
||||
|
||||
// Final commit
|
||||
if (pindexBest)
|
||||
{
|
||||
if (fAddressIndex)
|
||||
UpdateAddressIndexSyncState(txdb, pindexBest);
|
||||
txdb.WriteHashBestChain(hashBestChain);
|
||||
|
||||
// Write sync checkpoint
|
||||
Checkpoints::WriteSyncCheckpoint(hashBestChain);
|
||||
}
|
||||
txdb.TxnCommit();
|
||||
}
|
||||
|
||||
nTransactionsUpdated++;
|
||||
printf("FastImportBlockFile: indexed %d blocks in %" PRId64 "ms\n", nLoaded, GetTimeMillis() - nStart);
|
||||
return nLoaded > 0;
|
||||
}
|
||||
|
||||
string GetWarnings(string strFor)
|
||||
{
|
||||
string strStatusBar;
|
||||
string strRPC;
|
||||
|
||||
if (GetBoolArg("-testsafemode"))
|
||||
strRPC = "test";
|
||||
|
||||
// Misc warnings like out of disk space and clock is wrong
|
||||
if (strMiscWarning != "")
|
||||
strStatusBar = strMiscWarning;
|
||||
|
||||
// triangles: if detected invalid checkpoint enter safe mode
|
||||
if (Checkpoints::hashInvalidCheckpoint != 0)
|
||||
strStatusBar = strRPC = _("WARNING: Invalid checkpoint found! Displayed transactions may not be correct! You may need to upgrade, or notify developers.");
|
||||
|
||||
@@ -129,7 +129,6 @@ CBlockIndex* FindBlockByHeight(int nHeight);
|
||||
bool ProcessMessages(CNode* pfrom);
|
||||
bool SendMessages(CNode* pto, bool fSendTrickle);
|
||||
bool LoadExternalBlockFile(FILE* fileIn);
|
||||
bool FastImportBlockFile();
|
||||
|
||||
bool CheckProofOfWork(uint256 hash, unsigned int nBits);
|
||||
unsigned int GetNextTargetRequired(const CBlockIndex* pindexLast, bool fProofOfStake);
|
||||
|
||||
Reference in New Issue
Block a user