aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/init.cpp92
-rw-r--r--src/leveldb.cpp6
-rw-r--r--src/leveldb.h2
-rw-r--r--src/main.cpp239
-rw-r--r--src/main.h9
-rw-r--r--src/qt/bitcoingui.cpp20
-rw-r--r--src/qt/clientmodel.cpp8
-rw-r--r--src/qt/clientmodel.h9
-rw-r--r--src/serialize.h144
-rw-r--r--src/test/test_bitcoin.cpp2
-rw-r--r--src/txdb.cpp16
-rw-r--r--src/txdb.h6
12 files changed, 405 insertions, 148 deletions
diff --git a/src/init.cpp b/src/init.cpp
index 8670ca9d39..ee7c5c5fc4 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -293,6 +293,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" +
@@ -309,6 +310,82 @@ std::string HelpMessage()
}
+struct CImportingNow
+{
+ CImportingNow() {
+ assert(fImporting == false);
+ fImporting = true;
+ }
+
+ ~CImportingNow() {
+ assert(fImporting == true);
+ fImporting = false;
+ }
+};
+
+struct CImportData {
+ std::vector<boost::filesystem::path> vFiles;
+};
+
+void ThreadImport(void *data) {
+ CImportData *import = reinterpret_cast<CImportData*>(data);
+
+ RenameThread("bitcoin-loadblk");
+
+ vnThreadsRunning[THREAD_IMPORT]++;
+
+ // -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) && !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);
+ }
+ }
+
+ // -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]--;
+}
+
/** Initialize bitcoin.
* @pre Parameters should be parsed and config file should be read.
*/
@@ -639,6 +716,8 @@ bool AppInit2()
// ********************************************************* Step 7: load block chain
+ fReindex = GetBoolArg("-reindex");
+
if (!bitdb.Open(GetDataDir()))
{
string msg = strprintf(_("Error initializing database environment %s!"
@@ -662,10 +741,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"));
@@ -798,13 +880,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
diff --git a/src/leveldb.cpp b/src/leveldb.cpp
index 58b75e5295..9e2f32a171 100644
--- a/src/leveldb.cpp
+++ b/src/leveldb.cpp
@@ -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());
}
diff --git a/src/leveldb.h b/src/leveldb.h
index e5b2e1ef0b..0b83432072 100644
--- a/src/leveldb.h
+++ b/src/leveldb.h
@@ -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) {
diff --git a/src/main.cpp b/src/main.cpp
index a08e393caa..0940260f9e 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -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,110 +2513,71 @@ void PrintBlockTree()
}
}
-bool LoadExternalBlockFile(FILE* fileIn)
+bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp)
{
int64 nStart = GetTimeMillis();
int nLoaded = 0;
{
- try {
- CAutoFile blkdat(fileIn, SER_DISK, CLIENT_VERSION);
- unsigned int nPos = 0;
- while (nPos != (unsigned int)-1 && blkdat.good() && !fRequestShutdown)
- {
- 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;
+ 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];
+ blkdat.FindByte(pchMessageStart[0]);
+ nRewind = blkdat.GetPos()+1;
+ blkdat >> FLATDATA(buf);
+ if (memcmp(buf, pchMessageStart, 4))
+ continue;
+ // read size
blkdat >> nSize;
- if (nSize > 0 && nSize <= MAX_BLOCK_SIZE)
- {
- CBlock block;
- blkdat >> block;
+ if (nSize < 80 || nSize > MAX_BLOCK_SIZE)
+ continue;
+ } catch (std::exception &e) {
+ // no valid block header found; don't complain
+ break;
+ }
+ try {
+ // read block
+ 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++;
- nPos += 4 + nSize;
- }
}
+ } catch (std::exception &e) {
+ printf("%s() : Deserialize or I/O error caught during load\n", __PRETTY_FUNCTION__);
}
}
- catch (std::exception &e) {
- printf("%s() : Deserialize or I/O error caught during load\n",
- __PRETTY_FUNCTION__);
- }
+ 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;
}
-struct CImportingNow
-{
- CImportingNow() {
- assert(fImporting == false);
- fImporting = true;
- }
-
- ~CImportingNow() {
- assert(fImporting == true);
- fImporting = false;
- }
-};
-
-void ThreadImport(void *data) {
- std::vector<boost::filesystem::path> *vFiles = reinterpret_cast<std::vector<boost::filesystem::path>*>(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);
- }
-
- // hardcoded $DATADIR/bootstrap.dat
- filesystem::path pathBootstrap = GetDataDir() / "bootstrap.dat";
- if (filesystem::exists(pathBootstrap)) {
- FILE *file = fopen(pathBootstrap.string().c_str(), "rb");
- if (file) {
- filesystem::path pathBootstrapOld = GetDataDir() / "bootstrap.dat.old";
- LoadExternalBlockFile(file);
- RenameOver(pathBootstrap, pathBootstrapOld);
- }
- }
-
- delete vFiles;
-
- vnThreadsRunning[THREAD_IMPORT]--;
-}
-
@@ -2800,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) &&
@@ -2937,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]));
@@ -3167,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;
diff --git a/src/main.h b/src/main.h
index 899cabd85a..25dddae0f8 100644
--- a/src/main.h
+++ b/src/main.h
@@ -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,11 +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 LoadBlockIndex(bool fAllowNew=true);
+bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp = NULL);
+bool LoadBlockIndex();
void PrintBlockTree();
CBlockIndex* FindBlockByHeight(int nHeight);
bool ProcessMessages(CNode* pfrom);
@@ -1261,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);
};
diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp
index 988f4185b9..9c47daf85d 100644
--- a/src/qt/bitcoingui.cpp
+++ b/src/qt/bitcoingui.cpp
@@ -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();
diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp
index 25db695a0b..9b7362d757 100644
--- a/src/qt/clientmodel.cpp
+++ b/src/qt/clientmodel.cpp
@@ -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
diff --git a/src/qt/clientmodel.h b/src/qt/clientmodel.h
index fd0135b3e6..7d6401ab25 100644
--- a/src/qt/clientmodel.h
+++ b/src/qt/clientmodel.h
@@ -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
diff --git a/src/serialize.h b/src/serialize.h
index 549e46cbc3..9e14666fac 100644
--- a/src/serialize.h
+++ b/src/serialize.h
@@ -1225,4 +1225,148 @@ public:
}
};
+/** Wrapper around a FILE* that implements a ring buffer to
+ * deserialize from. It guarantees the ability to rewind
+ * a given number of bytes. */
+class CBufferedFile
+{
+private:
+ FILE *src; // source file
+ uint64 nSrcPos; // how many bytes have been read from source
+ uint64 nReadPos; // how many bytes have been read from this
+ uint64 nReadLimit; // up to which position we're allowed to read
+ uint64 nRewind; // how many bytes we guarantee to rewind
+ std::vector<char> vchBuf; // the buffer
+
+ short state;
+ short exceptmask;
+
+protected:
+ void setstate(short bits, const char *psz) {
+ state |= bits;
+ if (state & exceptmask)
+ throw std::ios_base::failure(psz);
+ }
+
+ // read data from the source to fill the buffer
+ bool Fill() {
+ unsigned int pos = nSrcPos % vchBuf.size();
+ unsigned int readNow = vchBuf.size() - pos;
+ unsigned int nAvail = vchBuf.size() - (nSrcPos - nReadPos) - nRewind;
+ if (nAvail < readNow)
+ readNow = nAvail;
+ if (readNow == 0)
+ return false;
+ size_t read = fread((void*)&vchBuf[pos], 1, readNow, src);
+ if (read == 0) {
+ setstate(std::ios_base::failbit, feof(src) ? "CBufferedFile::Fill : end of file" : "CBufferedFile::Fill : fread failed");
+ return false;
+ } else {
+ nSrcPos += read;
+ return true;
+ }
+ }
+
+public:
+ int nType;
+ int nVersion;
+
+ CBufferedFile(FILE *fileIn, uint64 nBufSize, uint64 nRewindIn, int nTypeIn, int nVersionIn) :
+ src(fileIn), nSrcPos(0), nReadPos(0), nReadLimit((uint64)(-1)), nRewind(nRewindIn), vchBuf(nBufSize, 0),
+ state(0), exceptmask(std::ios_base::badbit | std::ios_base::failbit), nType(nTypeIn), nVersion(nVersionIn) {
+ }
+
+ // check whether no error occurred
+ bool good() const {
+ return state == 0;
+ }
+
+ // check whether we're at the end of the source file
+ bool eof() const {
+ return nReadPos == nSrcPos && feof(src);
+ }
+
+ // read a number of bytes
+ CBufferedFile& read(char *pch, size_t nSize) {
+ if (nSize + nReadPos > nReadLimit)
+ throw std::ios_base::failure("Read attempted past buffer limit");
+ if (nSize + nRewind > vchBuf.size())
+ throw std::ios_base::failure("Read larger than buffer size");
+ while (nSize > 0) {
+ if (nReadPos == nSrcPos)
+ Fill();
+ unsigned int pos = nReadPos % vchBuf.size();
+ size_t nNow = nSize;
+ if (nNow + pos > vchBuf.size())
+ nNow = vchBuf.size() - pos;
+ if (nNow + nReadPos > nSrcPos)
+ nNow = nSrcPos - nReadPos;
+ memcpy(pch, &vchBuf[pos], nNow);
+ nReadPos += nNow;
+ pch += nNow;
+ nSize -= nNow;
+ }
+ return (*this);
+ }
+
+ // return the current reading position
+ uint64 GetPos() {
+ return nReadPos;
+ }
+
+ // rewind to a given reading position
+ bool SetPos(uint64 nPos) {
+ nReadPos = nPos;
+ if (nReadPos + nRewind < nSrcPos) {
+ nReadPos = nSrcPos - nRewind;
+ return false;
+ } else if (nReadPos > nSrcPos) {
+ nReadPos = nSrcPos;
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ bool Seek(uint64 nPos) {
+ long nLongPos = nPos;
+ if (nPos != (uint64)nLongPos)
+ return false;
+ if (fseek(src, nLongPos, SEEK_SET))
+ return false;
+ nLongPos = ftell(src);
+ nSrcPos = nLongPos;
+ nReadPos = nLongPos;
+ state = 0;
+ return true;
+ }
+
+ // prevent reading beyond a certain position
+ // no argument removes the limit
+ bool SetLimit(uint64 nPos = (uint64)(-1)) {
+ if (nPos < nReadPos)
+ return false;
+ nReadLimit = nPos;
+ return true;
+ }
+
+ template<typename T>
+ CBufferedFile& operator>>(T& obj) {
+ // Unserialize from this stream
+ ::Unserialize(*this, obj, nType, nVersion);
+ return (*this);
+ }
+
+ // search for a given byte in the stream, and remain positioned on it
+ void FindByte(char ch) {
+ while (true) {
+ if (nReadPos == nSrcPos)
+ Fill();
+ if (vchBuf[nReadPos % vchBuf.size()] == ch)
+ break;
+ nReadPos++;
+ }
+ }
+};
+
#endif
diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp
index c1f47f786b..c8a565af3a 100644
--- a/src/test/test_bitcoin.cpp
+++ b/src/test/test_bitcoin.cpp
@@ -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);
diff --git a/src/txdb.cpp b/src/txdb.cpp
index d9972d5b06..93c5f23d8b 100644
--- a/src/txdb.cpp
+++ b/src/txdb.cpp
@@ -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);
}
diff --git a/src/txdb.h b/src/txdb.h
index e13925c964..d7d327069f 100644
--- a/src/txdb.h
+++ b/src/txdb.h
@@ -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();
};