Flushes the blktree/ and coins/ databases, and reindexes the block chain files, as if their contents was loaded via -loadblock. Based on earlier work by Jeff Garzik.tags/v0.15.1
@@ -294,6 +294,7 @@ std::string HelpMessage() | |||
" -checkblocks=<n> " + _("How many blocks to check at startup (default: 2500, 0 = all)") + "\n" + | |||
" -checklevel=<n> " + _("How thorough the block verification is (0-6, default: 1)") + "\n" + | |||
" -loadblock=<file> " + _("Imports blocks from external blk000?.dat file") + "\n" + | |||
" -reindex " + _("Rebuild blockchain index from current blk000??.dat files") + "\n" + | |||
"\n" + _("Block creation options:") + "\n" + | |||
" -blockminsize=<n> " + _("Set minimum block size in bytes (default: 0)") + "\n" + | |||
@@ -323,33 +324,65 @@ struct CImportingNow | |||
} | |||
}; | |||
struct CImportData { | |||
std::vector<boost::filesystem::path> vFiles; | |||
}; | |||
void ThreadImport(void *data) { | |||
std::vector<boost::filesystem::path> *vFiles = reinterpret_cast<std::vector<boost::filesystem::path>*>(data); | |||
CImportData *import = reinterpret_cast<CImportData*>(data); | |||
RenameThread("bitcoin-loadblk"); | |||
CImportingNow imp; | |||
vnThreadsRunning[THREAD_IMPORT]++; | |||
// -loadblock= | |||
BOOST_FOREACH(boost::filesystem::path &path, *vFiles) { | |||
FILE *file = fopen(path.string().c_str(), "rb"); | |||
if (file) | |||
LoadExternalBlockFile(file); | |||
// -reindex | |||
if (fReindex) { | |||
CImportingNow imp; | |||
int nFile = 0; | |||
while (!fShutdown) { | |||
CDiskBlockPos pos; | |||
pos.nFile = nFile; | |||
pos.nPos = 0; | |||
FILE *file = OpenBlockFile(pos, true); | |||
if (!file) | |||
break; | |||
printf("Reindexing block file blk%05u.dat...\n", (unsigned int)nFile); | |||
LoadExternalBlockFile(file, &pos); | |||
nFile++; | |||
} | |||
if (!fShutdown) { | |||
pblocktree->WriteReindexing(false); | |||
fReindex = false; | |||
printf("Reindexing finished\n"); | |||
} | |||
} | |||
// hardcoded $DATADIR/bootstrap.dat | |||
filesystem::path pathBootstrap = GetDataDir() / "bootstrap.dat"; | |||
if (filesystem::exists(pathBootstrap)) { | |||
if (filesystem::exists(pathBootstrap) && !fShutdown) { | |||
FILE *file = fopen(pathBootstrap.string().c_str(), "rb"); | |||
if (file) { | |||
CImportingNow imp; | |||
filesystem::path pathBootstrapOld = GetDataDir() / "bootstrap.dat.old"; | |||
printf("Importing bootstrap.dat...\n"); | |||
LoadExternalBlockFile(file); | |||
RenameOver(pathBootstrap, pathBootstrapOld); | |||
} | |||
} | |||
delete vFiles; | |||
// -loadblock= | |||
BOOST_FOREACH(boost::filesystem::path &path, import->vFiles) { | |||
if (fShutdown) | |||
break; | |||
FILE *file = fopen(path.string().c_str(), "rb"); | |||
if (file) { | |||
CImportingNow imp; | |||
printf("Importing %s...\n", path.string().c_str()); | |||
LoadExternalBlockFile(file); | |||
} | |||
} | |||
delete import; | |||
vnThreadsRunning[THREAD_IMPORT]--; | |||
} | |||
@@ -686,6 +719,8 @@ bool AppInit2() | |||
// ********************************************************* Step 7: load block chain | |||
fReindex = GetBoolArg("-reindex"); | |||
if (!bitdb.Open(GetDataDir())) | |||
{ | |||
string msg = strprintf(_("Error initializing database environment %s!" | |||
@@ -709,10 +744,13 @@ bool AppInit2() | |||
uiInterface.InitMessage(_("Loading block index...")); | |||
printf("Loading block index...\n"); | |||
nStart = GetTimeMillis(); | |||
pblocktree = new CBlockTreeDB(nBlockTreeDBCache); | |||
pcoinsdbview = new CCoinsViewDB(nCoinDBCache); | |||
pblocktree = new CBlockTreeDB(nBlockTreeDBCache, false, fReindex); | |||
pcoinsdbview = new CCoinsViewDB(nCoinDBCache, false, fReindex); | |||
pcoinsTip = new CCoinsViewCache(*pcoinsdbview); | |||
if (fReindex) | |||
pblocktree->WriteReindexing(true); | |||
if (!LoadBlockIndex()) | |||
return InitError(_("Error loading blkindex.dat")); | |||
@@ -845,13 +883,13 @@ bool AppInit2() | |||
if (!ConnectBestBlock()) | |||
strErrors << "Failed to connect best block"; | |||
std::vector<boost::filesystem::path> *vPath = new std::vector<boost::filesystem::path>(); | |||
CImportData *pimport = new CImportData(); | |||
if (mapArgs.count("-loadblock")) | |||
{ | |||
BOOST_FOREACH(string strFile, mapMultiArgs["-loadblock"]) | |||
vPath->push_back(strFile); | |||
pimport->vFiles.push_back(strFile); | |||
} | |||
NewThread(ThreadImport, vPath); | |||
NewThread(ThreadImport, pimport); | |||
// ********************************************************* Step 10: load peers | |||
@@ -21,7 +21,7 @@ static leveldb::Options GetOptions(size_t nCacheSize) { | |||
return options; | |||
} | |||
CLevelDB::CLevelDB(const boost::filesystem::path &path, size_t nCacheSize, bool fMemory) { | |||
CLevelDB::CLevelDB(const boost::filesystem::path &path, size_t nCacheSize, bool fMemory, bool fWipe) { | |||
penv = NULL; | |||
readoptions.verify_checksums = true; | |||
iteroptions.verify_checksums = true; | |||
@@ -33,6 +33,10 @@ CLevelDB::CLevelDB(const boost::filesystem::path &path, size_t nCacheSize, bool | |||
penv = leveldb::NewMemEnv(leveldb::Env::Default()); | |||
options.env = penv; | |||
} else { | |||
if (fWipe) { | |||
printf("Wiping LevelDB in %s\n", path.string().c_str()); | |||
leveldb::DestroyDB(path.string(), options); | |||
} | |||
boost::filesystem::create_directory(path); | |||
printf("Opening LevelDB in %s\n", path.string().c_str()); | |||
} |
@@ -69,7 +69,7 @@ private: | |||
leveldb::DB *pdb; | |||
public: | |||
CLevelDB(const boost::filesystem::path &path, size_t nCacheSize, bool fMemory = false); | |||
CLevelDB(const boost::filesystem::path &path, size_t nCacheSize, bool fMemory = false, bool fWipe = false); | |||
~CLevelDB(); | |||
template<typename K, typename V> bool Read(const K& key, V& value) { |
@@ -41,6 +41,7 @@ CBlockIndex* pindexBest = NULL; | |||
set<CBlockIndex*, CBlockIndexWorkComparator> setBlockIndexValid; // may contain all CBlockIndex*'s that have validness >=BLOCK_VALID_TRANSACTIONS, and must contain those who aren't failed | |||
int64 nTimeBestReceived = 0; | |||
bool fImporting = false; | |||
bool fReindex = false; | |||
unsigned int nCoinCacheSize = 5000; | |||
CMedianFilter<int> cPeerBlockCounts(5, 0); // Amount of blocks that other nodes claim to have | |||
@@ -1145,7 +1146,7 @@ int GetNumBlocksOfPeers() | |||
bool IsInitialBlockDownload() | |||
{ | |||
if (pindexBest == NULL || nBestHeight < Checkpoints::GetTotalBlocksEstimate()) | |||
if (pindexBest == NULL || nBestHeight < Checkpoints::GetTotalBlocksEstimate() || fReindex || fImporting) | |||
return true; | |||
static int64 nLastUpdate; | |||
static CBlockIndex* pindexLastBest; | |||
@@ -1862,35 +1863,45 @@ bool CBlock::AddToBlockIndex(const CDiskBlockPos &pos) | |||
} | |||
bool FindBlockPos(CDiskBlockPos &pos, unsigned int nAddSize, unsigned int nHeight, uint64 nTime) | |||
bool FindBlockPos(CDiskBlockPos &pos, unsigned int nAddSize, unsigned int nHeight, uint64 nTime, bool fKnown = false) | |||
{ | |||
bool fUpdatedLast = false; | |||
LOCK(cs_LastBlockFile); | |||
while (infoLastBlockFile.nSize + nAddSize >= MAX_BLOCKFILE_SIZE) { | |||
printf("Leaving block file %i: %s\n", nLastBlockFile, infoLastBlockFile.ToString().c_str()); | |||
FlushBlockFile(); | |||
nLastBlockFile++; | |||
infoLastBlockFile.SetNull(); | |||
pblocktree->ReadBlockFileInfo(nLastBlockFile, infoLastBlockFile); // check whether data for the new file somehow already exist; can fail just fine | |||
fUpdatedLast = true; | |||
if (fKnown) { | |||
if (nLastBlockFile != pos.nFile) { | |||
nLastBlockFile = pos.nFile; | |||
infoLastBlockFile.SetNull(); | |||
pblocktree->ReadBlockFileInfo(nLastBlockFile, infoLastBlockFile); | |||
} | |||
} else { | |||
while (infoLastBlockFile.nSize + nAddSize >= MAX_BLOCKFILE_SIZE) { | |||
printf("Leaving block file %i: %s\n", nLastBlockFile, infoLastBlockFile.ToString().c_str()); | |||
FlushBlockFile(); | |||
nLastBlockFile++; | |||
infoLastBlockFile.SetNull(); | |||
pblocktree->ReadBlockFileInfo(nLastBlockFile, infoLastBlockFile); // check whether data for the new file somehow already exist; can fail just fine | |||
fUpdatedLast = true; | |||
} | |||
pos.nFile = nLastBlockFile; | |||
pos.nPos = infoLastBlockFile.nSize; | |||
} | |||
pos.nFile = nLastBlockFile; | |||
pos.nPos = infoLastBlockFile.nSize; | |||
infoLastBlockFile.nSize += nAddSize; | |||
infoLastBlockFile.AddBlock(nHeight, nTime); | |||
unsigned int nOldChunks = (pos.nPos + BLOCKFILE_CHUNK_SIZE - 1) / BLOCKFILE_CHUNK_SIZE; | |||
unsigned int nNewChunks = (infoLastBlockFile.nSize + BLOCKFILE_CHUNK_SIZE - 1) / BLOCKFILE_CHUNK_SIZE; | |||
if (nNewChunks > nOldChunks) { | |||
FILE *file = OpenBlockFile(pos); | |||
if (file) { | |||
printf("Pre-allocating up to position 0x%x in blk%05u.dat\n", nNewChunks * BLOCKFILE_CHUNK_SIZE, pos.nFile); | |||
AllocateFileRange(file, pos.nPos, nNewChunks * BLOCKFILE_CHUNK_SIZE - pos.nPos); | |||
if (!fKnown) { | |||
unsigned int nOldChunks = (pos.nPos + BLOCKFILE_CHUNK_SIZE - 1) / BLOCKFILE_CHUNK_SIZE; | |||
unsigned int nNewChunks = (infoLastBlockFile.nSize + BLOCKFILE_CHUNK_SIZE - 1) / BLOCKFILE_CHUNK_SIZE; | |||
if (nNewChunks > nOldChunks) { | |||
FILE *file = OpenBlockFile(pos); | |||
if (file) { | |||
printf("Pre-allocating up to position 0x%x in blk%05u.dat\n", nNewChunks * BLOCKFILE_CHUNK_SIZE, pos.nFile); | |||
AllocateFileRange(file, pos.nPos, nNewChunks * BLOCKFILE_CHUNK_SIZE - pos.nPos); | |||
} | |||
fclose(file); | |||
} | |||
fclose(file); | |||
} | |||
if (!pblocktree->WriteBlockFileInfo(nLastBlockFile, infoLastBlockFile)) | |||
@@ -1996,7 +2007,7 @@ bool CBlock::CheckBlock(bool fCheckPOW, bool fCheckMerkleRoot) const | |||
return true; | |||
} | |||
bool CBlock::AcceptBlock() | |||
bool CBlock::AcceptBlock(CDiskBlockPos *dbp) | |||
{ | |||
// Check for duplicate | |||
uint256 hash = GetHash(); | |||
@@ -2004,11 +2015,15 @@ bool CBlock::AcceptBlock() | |||
return error("AcceptBlock() : block already in mapBlockIndex"); | |||
// Get prev block index | |||
CBlockIndex* pindexPrev = NULL; | |||
int nHeight = 0; | |||
if (hash != hashGenesisBlock) { | |||
map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hashPrevBlock); | |||
if (mi == mapBlockIndex.end()) | |||
return DoS(10, error("AcceptBlock() : prev block not found")); | |||
CBlockIndex* pindexPrev = (*mi).second; | |||
int nHeight = pindexPrev->nHeight+1; | |||
pindexPrev = (*mi).second; | |||
nHeight = pindexPrev->nHeight+1; | |||
// Check proof of work | |||
if (nBits != GetNextWorkRequired(pindexPrev, this)) | |||
@@ -2048,16 +2063,22 @@ bool CBlock::AcceptBlock() | |||
return DoS(100, error("AcceptBlock() : block height mismatch in coinbase")); | |||
} | |||
} | |||
} | |||
// Write block to history file | |||
unsigned int nBlockSize = ::GetSerializeSize(*this, SER_DISK, CLIENT_VERSION); | |||
if (!CheckDiskSpace(::GetSerializeSize(*this, SER_DISK, CLIENT_VERSION))) | |||
return error("AcceptBlock() : out of disk space"); | |||
CDiskBlockPos blockPos; | |||
if (!FindBlockPos(blockPos, nBlockSize+8, nHeight, nTime)) | |||
if (dbp == NULL) { | |||
if (!CheckDiskSpace(::GetSerializeSize(*this, SER_DISK, CLIENT_VERSION))) | |||
return error("AcceptBlock() : out of disk space"); | |||
} else { | |||
blockPos = *dbp; | |||
} | |||
if (!FindBlockPos(blockPos, nBlockSize+8, nHeight, nTime, dbp != NULL)) | |||
return error("AcceptBlock() : FindBlockPos failed"); | |||
if (!WriteToDisk(blockPos)) | |||
return error("AcceptBlock() : WriteToDisk failed"); | |||
if (dbp == NULL) | |||
if (!WriteToDisk(blockPos)) | |||
return error("AcceptBlock() : WriteToDisk failed"); | |||
if (!AddToBlockIndex(blockPos)) | |||
return error("AcceptBlock() : AddToBlockIndex failed"); | |||
@@ -2086,7 +2107,7 @@ bool CBlockIndex::IsSuperMajority(int minVersion, const CBlockIndex* pstart, uns | |||
return (nFound >= nRequired); | |||
} | |||
bool ProcessBlock(CNode* pfrom, CBlock* pblock) | |||
bool ProcessBlock(CNode* pfrom, CBlock* pblock, CDiskBlockPos *dbp) | |||
{ | |||
// Check for duplicate | |||
uint256 hash = pblock->GetHash(); | |||
@@ -2124,7 +2145,7 @@ bool ProcessBlock(CNode* pfrom, CBlock* pblock) | |||
// If we don't already have its previous block, shunt it off to holding area until we get it | |||
if (!mapBlockIndex.count(pblock->hashPrevBlock)) | |||
if (pblock->hashPrevBlock != 0 && !mapBlockIndex.count(pblock->hashPrevBlock)) | |||
{ | |||
printf("ProcessBlock: ORPHAN BLOCK, prev=%s\n", pblock->hashPrevBlock.ToString().substr(0,20).c_str()); | |||
@@ -2141,7 +2162,7 @@ bool ProcessBlock(CNode* pfrom, CBlock* pblock) | |||
} | |||
// Store to disk | |||
if (!pblock->AcceptBlock()) | |||
if (!pblock->AcceptBlock(dbp)) | |||
return error("ProcessBlock() : AcceptBlock FAILED"); | |||
// Recursively process any orphan blocks that depended on this one | |||
@@ -2304,6 +2325,11 @@ bool static LoadBlockIndexDB() | |||
// Load bnBestInvalidWork, OK if it doesn't exist | |||
pblocktree->ReadBestInvalidWork(bnBestInvalidWork); | |||
// Check whether we need to continue reindexing | |||
bool fReindexing = false; | |||
pblocktree->ReadReindexing(fReindexing); | |||
fReindex |= fReindexing; | |||
// Verify blocks in the best chain | |||
int nCheckLevel = GetArg("-checklevel", 1); | |||
int nCheckDepth = GetArg( "-checkblocks", 2500); | |||
@@ -2337,7 +2363,7 @@ bool static LoadBlockIndexDB() | |||
return true; | |||
} | |||
bool LoadBlockIndex(bool fAllowNew) | |||
bool LoadBlockIndex() | |||
{ | |||
if (fTestNet) | |||
{ | |||
@@ -2348,6 +2374,9 @@ bool LoadBlockIndex(bool fAllowNew) | |||
hashGenesisBlock = uint256("000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943"); | |||
} | |||
if (fReindex) | |||
return true; | |||
// | |||
// Load block index from databases | |||
// | |||
@@ -2359,9 +2388,6 @@ bool LoadBlockIndex(bool fAllowNew) | |||
// | |||
if (mapBlockIndex.empty()) | |||
{ | |||
if (!fAllowNew) | |||
return false; | |||
// Genesis Block: | |||
// CBlock(hash=000000000019d6, ver=1, hashPrevBlock=00000000000000, hashMerkleRoot=4a5e1e, nTime=1231006505, nBits=1d00ffff, nNonce=2083236893, vtx=1) | |||
// CTransaction(hash=4a5e1e, ver=1, vin.size=1, vout.size=1, nLockTime=0) | |||
@@ -2487,18 +2513,28 @@ void PrintBlockTree() | |||
} | |||
} | |||
bool LoadExternalBlockFile(FILE* fileIn) | |||
bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp) | |||
{ | |||
int64 nStart = GetTimeMillis(); | |||
int nLoaded = 0; | |||
{ | |||
CBufferedFile blkdat(fileIn, 2*MAX_BLOCK_SIZE, MAX_BLOCK_SIZE+8, SER_DISK, CLIENT_VERSION); | |||
uint64 nStartByte = 0; | |||
if (dbp) { | |||
// (try to) skip already indexed part | |||
CBlockFileInfo info; | |||
if (pblocktree->ReadBlockFileInfo(dbp->nFile, info)) { | |||
nStartByte = info.nSize; | |||
blkdat.Seek(info.nSize); | |||
} | |||
} | |||
uint64 nRewind = blkdat.GetPos(); | |||
while (blkdat.good() && !blkdat.eof() && !fShutdown) { | |||
blkdat.SetPos(nRewind); | |||
nRewind++; // start one byte further next time, in case of failure | |||
blkdat.SetLimit(); // remove former limit | |||
unsigned int nSize = 0; | |||
try { | |||
// locate a header | |||
unsigned char buf[4]; | |||
@@ -2508,18 +2544,27 @@ bool LoadExternalBlockFile(FILE* fileIn) | |||
if (memcmp(buf, pchMessageStart, 4)) | |||
continue; | |||
// read size | |||
unsigned int nSize; | |||
blkdat >> nSize; | |||
if (nSize < 80 || nSize > MAX_BLOCK_SIZE) | |||
continue; | |||
} catch (std::exception &e) { | |||
// no valid block header found; don't complain | |||
break; | |||
} | |||
try { | |||
// read block | |||
blkdat.SetLimit(blkdat.GetPos() + nSize); | |||
uint64 nBlockPos = blkdat.GetPos(); | |||
blkdat.SetLimit(nBlockPos + nSize); | |||
CBlock block; | |||
blkdat >> block; | |||
nRewind = blkdat.GetPos(); | |||
{ | |||
// process block | |||
if (nBlockPos >= nStartByte) { | |||
LOCK(cs_main); | |||
if (ProcessBlock(NULL,&block)) | |||
if (dbp) | |||
dbp->nPos = nBlockPos; | |||
if (ProcessBlock(NULL, &block, dbp)) | |||
nLoaded++; | |||
} | |||
} catch (std::exception &e) { | |||
@@ -2528,7 +2573,8 @@ bool LoadExternalBlockFile(FILE* fileIn) | |||
} | |||
fclose(fileIn); | |||
} | |||
printf("Loaded %i blocks from external file in %"PRI64d"ms\n", nLoaded, GetTimeMillis() - nStart); | |||
if (nLoaded > 0) | |||
printf("Loaded %i blocks from external file in %"PRI64d"ms\n", nLoaded, GetTimeMillis() - nStart); | |||
return nLoaded > 0; | |||
} | |||
@@ -2741,7 +2787,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) | |||
// Ask the first connected node for block updates | |||
static int nAskedForBlocks = 0; | |||
if (!pfrom->fClient && !pfrom->fOneShot && !fImporting && | |||
if (!pfrom->fClient && !pfrom->fOneShot && !fImporting && !fReindex && | |||
(pfrom->nStartingHeight > (nBestHeight - 144)) && | |||
(pfrom->nVersion < NOBLKS_VERSION_START || | |||
pfrom->nVersion >= NOBLKS_VERSION_END) && | |||
@@ -2878,7 +2924,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) | |||
printf(" got inventory: %s %s\n", inv.ToString().c_str(), fAlreadyHave ? "have" : "new"); | |||
if (!fAlreadyHave) { | |||
if (!fImporting) | |||
if (!fImporting && !fReindex) | |||
pfrom->AskFor(inv); | |||
} else if (inv.type == MSG_BLOCK && mapOrphanBlocks.count(inv.hash)) { | |||
pfrom->PushGetBlocks(pindexBest, GetOrphanRoot(mapOrphanBlocks[inv.hash])); | |||
@@ -3108,7 +3154,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) | |||
} | |||
else if (strCommand == "block") | |||
else if (strCommand == "block" && !fImporting && !fReindex) // Ignore blocks received while importing | |||
{ | |||
CBlock block; | |||
vRecv >> block; |
@@ -88,6 +88,7 @@ extern CCriticalSection cs_setpwalletRegistered; | |||
extern std::set<CWallet*> setpwalletRegistered; | |||
extern unsigned char pchMessageStart[4]; | |||
extern bool fImporting; | |||
extern bool fReindex; | |||
extern unsigned int nCoinCacheSize; | |||
// Settings | |||
@@ -109,12 +110,12 @@ class CCoinsViewCache; | |||
void RegisterWallet(CWallet* pwalletIn); | |||
void UnregisterWallet(CWallet* pwalletIn); | |||
void SyncWithWallets(const uint256 &hash, const CTransaction& tx, const CBlock* pblock = NULL, bool fUpdate = false); | |||
bool ProcessBlock(CNode* pfrom, CBlock* pblock); | |||
bool ProcessBlock(CNode* pfrom, CBlock* pblock, CDiskBlockPos *dbp = NULL); | |||
bool CheckDiskSpace(uint64 nAdditionalBytes=0); | |||
FILE* OpenBlockFile(const CDiskBlockPos &pos, bool fReadOnly = false); | |||
FILE* OpenUndoFile(const CDiskBlockPos &pos, bool fReadOnly = false); | |||
bool LoadExternalBlockFile(FILE* fileIn); | |||
bool LoadBlockIndex(bool fAllowNew=true); | |||
bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp = NULL); | |||
bool LoadBlockIndex(); | |||
void PrintBlockTree(); | |||
CBlockIndex* FindBlockByHeight(int nHeight); | |||
bool ProcessMessages(CNode* pfrom); | |||
@@ -1262,7 +1263,8 @@ public: | |||
bool CheckBlock(bool fCheckPOW=true, bool fCheckMerkleRoot=true) const; | |||
// Store block on disk | |||
bool AcceptBlock(); | |||
// if dbp is provided, the file is known to already reside on disk | |||
bool AcceptBlock(CDiskBlockPos *dbp = NULL); | |||
}; | |||
@@ -489,7 +489,8 @@ void BitcoinGUI::setNumBlocks(int count, int nTotalBlocks) | |||
statusBar()->clearMessage(); | |||
// don't show / hide progress bar and its label if we have no connection to the network | |||
if (!clientModel || (clientModel->getNumConnections() == 0 && !clientModel->isImporting())) | |||
enum BlockSource blockSource = clientModel ? clientModel->getBlockSource() : BLOCK_SOURCE_NONE; | |||
if (blockSource == BLOCK_SOURCE_NONE || (blockSource == BLOCK_SOURCE_NETWORK && clientModel->getNumConnections() == 0)) | |||
{ | |||
progressBarLabel->setVisible(false); | |||
progressBar->setVisible(false); | |||
@@ -499,26 +500,37 @@ void BitcoinGUI::setNumBlocks(int count, int nTotalBlocks) | |||
QString tooltip; | |||
QString importText; | |||
switch (blockSource) { | |||
case BLOCK_SOURCE_NONE: | |||
case BLOCK_SOURCE_NETWORK: | |||
importText = tr("Synchronizing with network..."); | |||
case BLOCK_SOURCE_DISK: | |||
importText = tr("Importing blocks from disk..."); | |||
case BLOCK_SOURCE_REINDEX: | |||
importText = tr("Reindexing blocks on disk..."); | |||
} | |||
if(count < nTotalBlocks) | |||
{ | |||
int nRemainingBlocks = nTotalBlocks - count; | |||
float nPercentageDone = count / (nTotalBlocks * 0.01f); | |||
progressBarLabel->setText(tr(clientModel->isImporting() ? "Importing blocks..." : "Synchronizing with network...")); | |||
progressBarLabel->setText(importText); | |||
progressBarLabel->setVisible(true); | |||
progressBar->setFormat(tr("~%n block(s) remaining", "", nRemainingBlocks)); | |||
progressBar->setMaximum(nTotalBlocks); | |||
progressBar->setValue(count); | |||
progressBar->setVisible(true); | |||
tooltip = tr("Downloaded %1 of %2 blocks of transaction history (%3% done).").arg(count).arg(nTotalBlocks).arg(nPercentageDone, 0, 'f', 2); | |||
tooltip = tr("Processed %1 of %2 blocks of transaction history (%3% done).").arg(count).arg(nTotalBlocks).arg(nPercentageDone, 0, 'f', 2); | |||
} | |||
else | |||
{ | |||
progressBarLabel->setVisible(false); | |||
progressBar->setVisible(false); | |||
tooltip = tr("Downloaded %1 blocks of transaction history.").arg(count); | |||
tooltip = tr("Processed %1 blocks of transaction history.").arg(count); | |||
} | |||
QDateTime lastBlockDate = clientModel->getLastBlockDate(); |
@@ -101,9 +101,13 @@ bool ClientModel::inInitialBlockDownload() const | |||
return IsInitialBlockDownload(); | |||
} | |||
bool ClientModel::isImporting() const | |||
enum BlockSource ClientModel::getBlockSource() const | |||
{ | |||
return fImporting; | |||
if (fReindex) | |||
return BLOCK_SOURCE_REINDEX; | |||
if (fImporting) | |||
return BLOCK_SOURCE_DISK; | |||
return BLOCK_SOURCE_NETWORK; | |||
} | |||
int ClientModel::getNumBlocksOfPeers() const |
@@ -13,6 +13,13 @@ class QDateTime; | |||
class QTimer; | |||
QT_END_NAMESPACE | |||
enum BlockSource { | |||
BLOCK_SOURCE_NONE, | |||
BLOCK_SOURCE_NETWORK, | |||
BLOCK_SOURCE_DISK, | |||
BLOCK_SOURCE_REINDEX | |||
}; | |||
/** Model for Bitcoin network client. */ | |||
class ClientModel : public QObject | |||
{ | |||
@@ -34,7 +41,7 @@ public: | |||
//! Return true if core is doing initial block download | |||
bool inInitialBlockDownload() const; | |||
//! Return true if core is importing blocks | |||
bool isImporting() const; | |||
enum BlockSource getBlockSource() const; | |||
//! Return conservative estimate of total number of blocks, or 0 if unknown | |||
int getNumBlocksOfPeers() const; | |||
//! Return warnings to be displayed in status bar |
@@ -22,7 +22,7 @@ struct TestingSetup { | |||
pblocktree = new CBlockTreeDB(true); | |||
pcoinsdbview = new CCoinsViewDB(true); | |||
pcoinsTip = new CCoinsViewCache(*pcoinsdbview); | |||
LoadBlockIndex(true); | |||
LoadBlockIndex(); | |||
bool fFirstRun; | |||
pwalletMain = new CWallet("wallet.dat"); | |||
pwalletMain->LoadWallet(fFirstRun); |
@@ -19,7 +19,7 @@ void static BatchWriteHashBestChain(CLevelDBBatch &batch, const uint256 &hash) { | |||
batch.Write('B', hash); | |||
} | |||
CCoinsViewDB::CCoinsViewDB(size_t nCacheSize, bool fMemory) : db(GetDataDir() / "coins", nCacheSize, fMemory) { | |||
CCoinsViewDB::CCoinsViewDB(size_t nCacheSize, bool fMemory, bool fWipe) : db(GetDataDir() / "coins", nCacheSize, fMemory, fWipe) { | |||
} | |||
bool CCoinsViewDB::GetCoins(uint256 txid, CCoins &coins) { | |||
@@ -64,7 +64,7 @@ bool CCoinsViewDB::BatchWrite(const std::map<uint256, CCoins> &mapCoins, CBlockI | |||
return db.WriteBatch(batch); | |||
} | |||
CBlockTreeDB::CBlockTreeDB(size_t nCacheSize, bool fMemory) : CLevelDB(GetDataDir() / "blktree", nCacheSize, fMemory) { | |||
CBlockTreeDB::CBlockTreeDB(size_t nCacheSize, bool fMemory, bool fWipe) : CLevelDB(GetDataDir() / "blktree", nCacheSize, fMemory, fWipe) { | |||
} | |||
bool CBlockTreeDB::WriteBlockIndex(const CDiskBlockIndex& blockindex) | |||
@@ -94,6 +94,18 @@ bool CBlockTreeDB::WriteLastBlockFile(int nFile) { | |||
return Write('l', nFile); | |||
} | |||
bool CBlockTreeDB::WriteReindexing(bool fReindexing) { | |||
if (fReindexing) | |||
return Write('R', '1'); | |||
else | |||
return Erase('R'); | |||
} | |||
bool CBlockTreeDB::ReadReindexing(bool &fReindexing) { | |||
fReindexing = Exists('R'); | |||
return true; | |||
} | |||
bool CBlockTreeDB::ReadLastBlockFile(int &nFile) { | |||
return Read('l', nFile); | |||
} |
@@ -14,7 +14,7 @@ class CCoinsViewDB : public CCoinsView | |||
protected: | |||
CLevelDB db; | |||
public: | |||
CCoinsViewDB(size_t nCacheSize, bool fMemory = false); | |||
CCoinsViewDB(size_t nCacheSize, bool fMemory = false, bool fWipe = false); | |||
bool GetCoins(uint256 txid, CCoins &coins); | |||
bool SetCoins(uint256 txid, const CCoins &coins); | |||
@@ -29,7 +29,7 @@ public: | |||
class CBlockTreeDB : public CLevelDB | |||
{ | |||
public: | |||
CBlockTreeDB(size_t nCacheSize, bool fMemory = false); | |||
CBlockTreeDB(size_t nCacheSize, bool fMemory = false, bool fWipe = false); | |||
private: | |||
CBlockTreeDB(const CBlockTreeDB&); | |||
void operator=(const CBlockTreeDB&); | |||
@@ -41,6 +41,8 @@ public: | |||
bool WriteBlockFileInfo(int nFile, const CBlockFileInfo &fileinfo); | |||
bool ReadLastBlockFile(int &nFile); | |||
bool WriteLastBlockFile(int nFile); | |||
bool WriteReindexing(bool fReindex); | |||
bool ReadReindexing(bool &fReindex); | |||
bool LoadBlockIndexGuts(); | |||
}; | |||