diff options
-rw-r--r-- | src/bitcoinrpc.cpp | 77 | ||||
-rw-r--r-- | src/db.cpp | 20 | ||||
-rw-r--r-- | src/db.h | 3 | ||||
-rw-r--r-- | src/init.cpp | 142 | ||||
-rw-r--r-- | src/leveldb.cpp | 14 | ||||
-rw-r--r-- | src/leveldb.h | 2 | ||||
-rw-r--r-- | src/main.cpp | 242 | ||||
-rw-r--r-- | src/main.h | 10 | ||||
-rw-r--r-- | src/qt/addressbookpage.h | 2 | ||||
-rw-r--r-- | src/qt/bitcoinamountfield.h | 6 | ||||
-rw-r--r-- | src/qt/bitcoingui.cpp | 28 | ||||
-rw-r--r-- | src/qt/bitcoingui.h | 2 | ||||
-rw-r--r-- | src/qt/bitcoinstrings.cpp | 2 | ||||
-rw-r--r-- | src/qt/clientmodel.cpp | 8 | ||||
-rw-r--r-- | src/qt/clientmodel.h | 9 | ||||
-rw-r--r-- | src/qt/forms/optionsdialog.ui | 10 | ||||
-rw-r--r-- | src/qt/forms/sendcoinsentry.ui | 26 | ||||
-rw-r--r-- | src/qt/macdockiconhandler.h | 2 | ||||
-rw-r--r-- | src/qt/optionsdialog.cpp | 1 | ||||
-rw-r--r-- | src/qt/optionsmodel.cpp | 10 | ||||
-rw-r--r-- | src/qt/optionsmodel.h | 1 | ||||
-rw-r--r-- | src/qt/sendcoinsdialog.cpp | 2 | ||||
-rw-r--r-- | src/qt/transactiontablemodel.h | 3 | ||||
-rw-r--r-- | src/rpcwallet.cpp | 2 | ||||
-rw-r--r-- | src/serialize.h | 144 | ||||
-rw-r--r-- | src/test/test_bitcoin.cpp | 6 | ||||
-rw-r--r-- | src/txdb.cpp | 16 | ||||
-rw-r--r-- | src/txdb.h | 6 | ||||
-rw-r--r-- | src/wallet.cpp | 8 |
29 files changed, 531 insertions, 273 deletions
diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp index 9033f5ed8c..8c04f577d1 100644 --- a/src/bitcoinrpc.cpp +++ b/src/bitcoinrpc.cpp @@ -176,14 +176,12 @@ Value help(const Array& params, bool fHelp) Value stop(const Array& params, bool fHelp) { + // Accept the deprecated and ignored 'detach´ boolean argument if (fHelp || params.size() > 1) throw runtime_error( - "stop <detach>\n" - "<detach> is true or false to detach the database or not for this stop only\n" - "Stop Bitcoin server (and possibly override the detachdb config value)."); + "stop\n" + "Stop Bitcoin server."); // Shutdown will take long enough that the response should get back - if (params.size() > 0) - bitdb.SetDetach(params[0].get_bool()); StartShutdown(); return "Bitcoin server stopping"; } @@ -359,6 +357,41 @@ static string HTTPReply(int nStatus, const string& strMsg, bool keepalive) strMsg.c_str()); } +bool ReadHTTPRequestLine(std::basic_istream<char>& stream, int &proto, + string& http_method, string& http_uri) +{ + string str; + getline(stream, str); + + // HTTP request line is space-delimited + vector<string> vWords; + boost::split(vWords, str, boost::is_any_of(" ")); + if (vWords.size() < 2) + return false; + + // HTTP methods permitted: GET, POST + http_method = vWords[0]; + if (http_method != "GET" && http_method != "POST") + return false; + + // HTTP URI must be an absolute path, relative to current host + http_uri = vWords[1]; + if (http_uri.size() == 0 || http_uri[0] != '/') + return false; + + // parse proto, if present + string strProto = ""; + if (vWords.size() > 2) + strProto = vWords[2]; + + proto = 0; + const char *ver = strstr(strProto.c_str(), "HTTP/1."); + if (ver != NULL) + proto = atoi(ver+7); + + return true; +} + int ReadHTTPStatus(std::basic_istream<char>& stream, int &proto) { string str; @@ -374,7 +407,7 @@ int ReadHTTPStatus(std::basic_istream<char>& stream, int &proto) return atoi(vWords[1].c_str()); } -int ReadHTTPHeader(std::basic_istream<char>& stream, map<string, string>& mapHeadersRet) +int ReadHTTPHeaders(std::basic_istream<char>& stream, map<string, string>& mapHeadersRet) { int nLen = 0; loop @@ -399,17 +432,15 @@ int ReadHTTPHeader(std::basic_istream<char>& stream, map<string, string>& mapHea return nLen; } -int ReadHTTP(std::basic_istream<char>& stream, map<string, string>& mapHeadersRet, string& strMessageRet) +int ReadHTTPMessage(std::basic_istream<char>& stream, map<string, + string>& mapHeadersRet, string& strMessageRet, + int nProto) { mapHeadersRet.clear(); strMessageRet = ""; - // Read status - int nProto = 0; - int nStatus = ReadHTTPStatus(stream, nProto); - // Read header - int nLen = ReadHTTPHeader(stream, mapHeadersRet); + int nLen = ReadHTTPHeaders(stream, mapHeadersRet); if (nLen < 0 || nLen > (int)MAX_SIZE) return HTTP_INTERNAL_SERVER_ERROR; @@ -431,7 +462,7 @@ int ReadHTTP(std::basic_istream<char>& stream, map<string, string>& mapHeadersRe mapHeadersRet["connection"] = "close"; } - return nStatus; + return HTTP_OK; } bool HTTPAuthorized(map<string, string>& mapHeaders) @@ -938,10 +969,17 @@ void ThreadRPCServer3(void* parg) } return; } + + int nProto = 0; map<string, string> mapHeaders; - string strRequest; + string strRequest, strMethod, strURI; + + // Read HTTP request line + if (!ReadHTTPRequestLine(conn->stream(), nProto, strMethod, strURI)) + break; - ReadHTTP(conn->stream(), mapHeaders, strRequest); + // Read HTTP message headers and body + ReadHTTPMessage(conn->stream(), mapHeaders, strRequest, nProto); // Check authorization if (mapHeaders.count("authorization") == 0) @@ -1073,10 +1111,15 @@ Object CallRPC(const string& strMethod, const Array& params) string strPost = HTTPPost(strRequest, mapRequestHeaders); stream << strPost << std::flush; - // Receive reply + // Receive HTTP reply status + int nProto = 0; + int nStatus = ReadHTTPStatus(stream, nProto); + + // Receive HTTP reply message headers and body map<string, string> mapHeaders; string strReply; - int nStatus = ReadHTTP(stream, mapHeaders, strReply); + ReadHTTPMessage(stream, mapHeaders, strReply, nProto); + if (nStatus == HTTP_UNAUTHORIZED) throw runtime_error("incorrect rpcuser or rpcpassword (authorization failed)"); else if (nStatus >= 400 && nStatus != HTTP_BAD_REQUEST && nStatus != HTTP_NOT_FOUND && nStatus != HTTP_INTERNAL_SERVER_ERROR) diff --git a/src/db.cpp b/src/db.cpp index cf96fe6e38..fb405fc277 100644 --- a/src/db.cpp +++ b/src/db.cpp @@ -271,14 +271,6 @@ CDB::CDB(const char *pszFile, const char* pszMode) : } } -static bool IsChainFile(std::string strFile) -{ - if (strFile == "coins.dat" || strFile == "blktree.dat") - return true; - - return false; -} - void CDB::Flush() { if (activeTxn) @@ -288,10 +280,6 @@ void CDB::Flush() unsigned int nMinutes = 0; if (fReadOnly) nMinutes = 1; - if (IsChainFile(strFile)) - nMinutes = 2; - if (IsChainFile(strFile) && IsInitialBlockDownload()) - nMinutes = 5; bitdb.dbenv.txn_checkpoint(nMinutes ? GetArg("-dblogsize", 100)*1024 : 0, nMinutes, 0); } @@ -453,11 +441,9 @@ void CDBEnv::Flush(bool fShutdown) CloseDb(strFile); printf("%s checkpoint\n", strFile.c_str()); dbenv.txn_checkpoint(0, 0, 0); - if (!IsChainFile(strFile) || fDetachDB) { - printf("%s detach\n", strFile.c_str()); - if (!fMockDb) - dbenv.lsn_reset(strFile.c_str(), 0); - } + printf("%s detach\n", strFile.c_str()); + if (!fMockDb) + dbenv.lsn_reset(strFile.c_str(), 0); printf("%s closed\n", strFile.c_str()); mapFileUseCount.erase(mi++); } @@ -31,7 +31,6 @@ bool BackupWallet(const CWallet& wallet, const std::string& strDest); class CDBEnv { private: - bool fDetachDB; bool fDbEnvInit; bool fMockDb; boost::filesystem::path pathEnv; @@ -71,8 +70,6 @@ public: void Close(); void Flush(bool fShutdown); void CheckpointLSN(std::string strFile); - void SetDetach(bool fDetachDB_) { fDetachDB = fDetachDB_; } - bool GetDetach() { return fDetachDB; } void CloseDb(const std::string& strFile); bool RemoveDb(const std::string& strFile); diff --git a/src/init.cpp b/src/init.cpp index 1724382bf5..f6df4055fc 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -26,6 +26,13 @@ using namespace boost; CWallet* pwalletMain; CClientUIInterface uiInterface; +// Used to pass flags to the Bind() function +enum BindFlags { + BF_NONE = 0, + BF_EXPLICIT = 1, + BF_REPORT_ERROR = 2 +}; + ////////////////////////////////////////////////////////////////////////////// // // Shutdown @@ -213,12 +220,12 @@ bool static InitWarning(const std::string &str) } -bool static Bind(const CService &addr, bool fError = true) { - if (IsLimited(addr)) +bool static Bind(const CService &addr, int flags) { + if (!(flags & BF_EXPLICIT) && IsLimited(addr)) return false; std::string strError; if (!BindListenPort(addr, strError)) { - if (fError) + if (flags & BF_REPORT_ERROR) return InitError(strError); return false; } @@ -236,7 +243,6 @@ std::string HelpMessage() " -gen=0 " + _("Don't generate coins") + "\n" + " -datadir=<dir> " + _("Specify data directory") + "\n" + " -dbcache=<n> " + _("Set database cache size in megabytes (default: 25)") + "\n" + - " -dblogsize=<n> " + _("Set database disk log size in megabytes (default: 100)") + "\n" + " -timeout=<n> " + _("Specify connection timeout in milliseconds (default: 5000)") + "\n" + " -proxy=<ip:port> " + _("Connect through socks proxy") + "\n" + " -socks=<n> " + _("Select the version of socks proxy to use (4-5, default: 5)") + "\n" + @@ -252,7 +258,7 @@ std::string HelpMessage() " -discover " + _("Discover own IP address (default: 1 when listening and no -externalip)") + "\n" + " -irc " + _("Find peers using internet relay chat (default: 0)") + "\n" + " -listen " + _("Accept connections from outside (default: 1 if no -proxy or -connect)") + "\n" + - " -bind=<addr> " + _("Bind to given address. Use [host]:port notation for IPv6") + "\n" + + " -bind=<addr> " + _("Bind to given address and always listen on it. Use [host]:port notation for IPv6") + "\n" + " -dnsseed " + _("Find peers using DNS lookup (default: 1 unless -connect)") + "\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" + @@ -265,7 +271,6 @@ std::string HelpMessage() " -upnp " + _("Use UPnP to map the listening port (default: 0)") + "\n" + #endif #endif - " -detachdb " + _("Detach block and address databases. Increases shutdown time (default: 0)") + "\n" + " -paytxfee=<amt> " + _("Fee per KB to add to transactions you send") + "\n" + #ifdef QT_GUI " -server " + _("Accept command line and JSON-RPC commands") + "\n" + @@ -295,6 +300,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" + @@ -310,6 +316,81 @@ std::string HelpMessage() return strUsage; } +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. @@ -408,8 +489,6 @@ bool AppInit2() else fDebugNet = GetBoolArg("-debugnet"); - bitdb.SetDetach(GetBoolArg("-detachdb", false)); - #if !defined(WIN32) && !defined(QT_GUI) fDaemon = GetBoolArg("-daemon"); #else @@ -461,7 +540,7 @@ bool AppInit2() if (file) fclose(file); static boost::interprocess::file_lock lock(pathLockFile.string().c_str()); if (!lock.try_lock()) - return InitError(strprintf(_("Cannot obtain a lock on data directory %s. Bitcoin is probably already running."), strDataDir.c_str())); + return InitError(strprintf(_("Cannot obtain a lock on data directory %s. Bitcoin is probably already running."), strDataDir.c_str())); #if !defined(WIN32) && !defined(QT_GUI) if (fDaemon) @@ -604,32 +683,28 @@ bool AppInit2() #endif bool fBound = false; - if (!fNoListen) - { - std::string strError; + if (!fNoListen) { if (mapArgs.count("-bind")) { BOOST_FOREACH(std::string strBind, mapMultiArgs["-bind"]) { CService addrBind; if (!Lookup(strBind.c_str(), addrBind, GetListenPort(), false)) return InitError(strprintf(_("Cannot resolve -bind address: '%s'"), strBind.c_str())); - fBound |= Bind(addrBind); + fBound |= Bind(addrBind, (BF_EXPLICIT | BF_REPORT_ERROR)); } - } else { + } + else { struct in_addr inaddr_any; inaddr_any.s_addr = INADDR_ANY; #ifdef USE_IPV6 - if (!IsLimited(NET_IPV6)) - fBound |= Bind(CService(in6addr_any, GetListenPort()), false); + fBound |= Bind(CService(in6addr_any, GetListenPort()), BF_NONE); #endif - if (!IsLimited(NET_IPV4)) - fBound |= Bind(CService(inaddr_any, GetListenPort()), !fBound); + fBound |= Bind(CService(inaddr_any, GetListenPort()), !fBound ? BF_REPORT_ERROR : BF_NONE); } if (!fBound) return InitError(_("Failed to listen on any port. Use -listen=0 if you want this.")); } - if (mapArgs.count("-externalip")) - { + if (mapArgs.count("-externalip")) { BOOST_FOREACH(string strAddr, mapMultiArgs["-externalip"]) { CService addrLocal(strAddr, GetListenPort(), fNameLookup); if (!addrLocal.IsValid()) @@ -643,6 +718,8 @@ bool AppInit2() // ********************************************************* Step 7: load block chain + fReindex = GetBoolArg("-reindex"); + if (!bitdb.Open(GetDataDir())) { string msg = strprintf(_("Error initializing database environment %s!" @@ -651,13 +728,28 @@ bool AppInit2() return InitError(msg); } + // cache size calculations + size_t nTotalCache = GetArg("-dbcache", 25) << 20; + if (nTotalCache < (1 << 22)) + nTotalCache = (1 << 22); // total cache cannot be less than 4 MiB + size_t nBlockTreeDBCache = nTotalCache / 8; + if (nBlockTreeDBCache > (1 << 21)) + nBlockTreeDBCache = (1 << 21); // block tree db cache shouldn't be larger than 2 MiB + nTotalCache -= nBlockTreeDBCache; + size_t nCoinDBCache = nTotalCache / 2; // use half of the remaining cache for coindb cache + nTotalCache -= nCoinDBCache; + nCoinCacheSize = nTotalCache / 300; // coins in memory require around 300 bytes + uiInterface.InitMessage(_("Loading block index...")); printf("Loading block index...\n"); nStart = GetTimeMillis(); - pblocktree = new CBlockTreeDB(); - pcoinsdbview = new CCoinsViewDB(); + 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")); @@ -790,13 +882,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 e8a0fbe874..9e2f32a171 100644 --- a/src/leveldb.cpp +++ b/src/leveldb.cpp @@ -12,27 +12,31 @@ #include <boost/filesystem.hpp> -static leveldb::Options GetOptions() { +static leveldb::Options GetOptions(size_t nCacheSize) { leveldb::Options options; - int nCacheSizeMB = GetArg("-dbcache", 25); - options.block_cache = leveldb::NewLRUCache(nCacheSizeMB * 1048576); + options.block_cache = leveldb::NewLRUCache(nCacheSize / 2); + options.write_buffer_size = nCacheSize / 4; // up to two write buffers may be held in memory simultaneously options.filter_policy = leveldb::NewBloomFilterPolicy(10); options.compression = leveldb::kNoCompression; return options; } -CLevelDB::CLevelDB(const boost::filesystem::path &path, 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; iteroptions.fill_cache = false; syncoptions.sync = true; - options = GetOptions(); + options = GetOptions(nCacheSize); options.create_if_missing = true; if (fMemory) { 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 ee9079c3c3..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, 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 43bd5dd472..0940260f9e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -41,6 +41,8 @@ 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 @@ -1144,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; @@ -1735,7 +1737,7 @@ bool SetBestChain(CBlockIndex* pindexNew) // Make sure it's successfully written to disk before changing memory structure bool fIsInitialDownload = IsInitialBlockDownload(); - if (!fIsInitialDownload || view.GetCacheSize()>5000) { + if (!fIsInitialDownload || view.GetCacheSize() > nCoinCacheSize) { FlushBlockFile(); pblocktree->Sync(); if (!view.Flush()) @@ -1861,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)) @@ -1995,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(); @@ -2003,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)) @@ -2047,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"); @@ -2085,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(); @@ -2123,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()); @@ -2140,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 @@ -2303,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); @@ -2336,7 +2363,7 @@ bool static LoadBlockIndexDB() return true; } -bool LoadBlockIndex(bool fAllowNew) +bool LoadBlockIndex() { if (fTestNet) { @@ -2347,6 +2374,9 @@ bool LoadBlockIndex(bool fAllowNew) hashGenesisBlock = uint256("000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943"); } + if (fReindex) + return true; + // // Load block index from databases // @@ -2358,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) @@ -2486,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]--; -} - @@ -2799,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) && @@ -2936,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])); @@ -3166,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 744c0e4b51..25dddae0f8 100644 --- a/src/main.h +++ b/src/main.h @@ -88,6 +88,8 @@ 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 extern int64 nTransactionFee; @@ -108,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); @@ -1260,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/addressbookpage.h b/src/qt/addressbookpage.h index df87486949..f7d177c513 100644 --- a/src/qt/addressbookpage.h +++ b/src/qt/addressbookpage.h @@ -82,4 +82,4 @@ signals: void verifyMessage(QString addr); }; -#endif // ADDRESSBOOKDIALOG_H +#endif // ADDRESSBOOKPAGE_H diff --git a/src/qt/bitcoinamountfield.h b/src/qt/bitcoinamountfield.h index 66792e00a9..4797c4c882 100644 --- a/src/qt/bitcoinamountfield.h +++ b/src/qt/bitcoinamountfield.h @@ -1,5 +1,5 @@ -#ifndef BITCOINFIELD_H -#define BITCOINFIELD_H +#ifndef BITCOINAMOUNTFIELD_H +#define BITCOINAMOUNTFIELD_H #include <QWidget> @@ -57,4 +57,4 @@ private slots: }; -#endif // BITCOINFIELD_H +#endif // BITCOINAMOUNTFIELD_H diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index c09e1ce447..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(); @@ -625,11 +637,9 @@ void BitcoinGUI::closeEvent(QCloseEvent *event) void BitcoinGUI::askFee(qint64 nFeeRequired, bool *payFee) { - QString strMessage = - tr("This transaction is over the size limit. You can still send it for a fee of %1, " - "which goes to the nodes that process your transaction and helps to support the network. " - "Do you want to pay the fee?").arg( - BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, nFeeRequired)); + QString strMessage = tr("This transaction is over the size limit. You can still send it for a fee of %1, " + "which goes to the nodes that process your transaction and helps to support the network. " + "Do you want to pay the fee?").arg(BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, nFeeRequired)); QMessageBox::StandardButton retval = QMessageBox::question( this, tr("Confirm transaction fee"), strMessage, QMessageBox::Yes|QMessageBox::Cancel, QMessageBox::Yes); diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index a48911ee7f..8b4607d3ed 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -177,4 +177,4 @@ private slots: void toggleHidden(); }; -#endif +#endif // BITCOINGUI_H diff --git a/src/qt/bitcoinstrings.cpp b/src/qt/bitcoinstrings.cpp index b92a26c4a4..7e8e102ee7 100644 --- a/src/qt/bitcoinstrings.cpp +++ b/src/qt/bitcoinstrings.cpp @@ -27,8 +27,6 @@ QT_TRANSLATE_NOOP("bitcoin-core", "" "Cannot obtain a lock on data directory %s. Bitcoin is probably already " "running."), QT_TRANSLATE_NOOP("bitcoin-core", "" -"Detach block and address databases. Increases shutdown time (default: 0)"), -QT_TRANSLATE_NOOP("bitcoin-core", "" "Error: The transaction was rejected. This might happen if some of the coins " "in your wallet were already spent, such as if you used a copy of wallet.dat " "and coins were spent in the copy but not marked as spent here."), 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/qt/forms/optionsdialog.ui b/src/qt/forms/optionsdialog.ui index 1b81b0cdc8..6a13361974 100644 --- a/src/qt/forms/optionsdialog.ui +++ b/src/qt/forms/optionsdialog.ui @@ -87,16 +87,6 @@ </widget> </item> <item> - <widget class="QCheckBox" name="detachDatabases"> - <property name="toolTip"> - <string>Detach block and address databases at shutdown. This means they can be moved to another data directory, but it slows down shutdown. The wallet is always detached.</string> - </property> - <property name="text"> - <string>&Detach databases at shutdown</string> - </property> - </widget> - </item> - <item> <spacer name="verticalSpacer_Main"> <property name="orientation"> <enum>Qt::Vertical</enum> diff --git a/src/qt/forms/sendcoinsentry.ui b/src/qt/forms/sendcoinsentry.ui index 22a3f8fdc6..28553c5508 100644 --- a/src/qt/forms/sendcoinsentry.ui +++ b/src/qt/forms/sendcoinsentry.ui @@ -52,23 +52,6 @@ <item row="5" column="1"> <widget class="BitcoinAmountField" name="payAmount"/> </item> - <item row="4" column="1"> - <layout class="QHBoxLayout" name="horizontalLayout_2"> - <property name="spacing"> - <number>0</number> - </property> - <item> - <widget class="QValidatedLineEdit" name="addAsLabel"> - <property name="enabled"> - <bool>true</bool> - </property> - <property name="toolTip"> - <string>Enter a label for this address to add it to your address book</string> - </property> - </widget> - </item> - </layout> - </item> <item row="4" column="0"> <widget class="QLabel" name="label_4"> <property name="text"> @@ -90,7 +73,7 @@ <item> <widget class="QValidatedLineEdit" name="payTo"> <property name="toolTip"> - <string>The address to send the payment to (e.g. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L)</string> + <string>The address to send the payment to (e.g. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L)</string> </property> <property name="maxLength"> <number>34</number> @@ -147,6 +130,13 @@ </item> </layout> </item> + <item row="4" column="1"> + <widget class="QValidatedLineEdit" name="addAsLabel"> + <property name="toolTip"> + <string>Enter a label for this address to add it to your address book</string> + </property> + </widget> + </item> </layout> </widget> <customwidgets> diff --git a/src/qt/macdockiconhandler.h b/src/qt/macdockiconhandler.h index 2092fb26b3..dd85e0c33d 100644 --- a/src/qt/macdockiconhandler.h +++ b/src/qt/macdockiconhandler.h @@ -1,7 +1,7 @@ #ifndef MACDOCKICONHANDLER_H #define MACDOCKICONHANDLER_H -#include <QtCore/QObject> +#include <QObject> class QMenu; class QIcon; diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp index 3fb30a9518..03dcb0b538 100644 --- a/src/qt/optionsdialog.cpp +++ b/src/qt/optionsdialog.cpp @@ -128,7 +128,6 @@ void OptionsDialog::setMapper() /* Main */ mapper->addMapping(ui->transactionFee, OptionsModel::Fee); mapper->addMapping(ui->bitcoinAtStartup, OptionsModel::StartAtStartup); - mapper->addMapping(ui->detachDatabases, OptionsModel::DetachDatabases); /* Network */ mapper->addMapping(ui->mapPortUpnp, OptionsModel::MapPortUPnP); diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index 1075ce1e6c..e3c9413f1b 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -56,8 +56,6 @@ void OptionsModel::Init() SoftSetArg("-proxy", settings.value("addrProxy").toString().toStdString()); if (settings.contains("nSocksVersion") && settings.value("fUseProxy").toBool()) SoftSetArg("-socks", settings.value("nSocksVersion").toString().toStdString()); - if (settings.contains("detachDB")) - SoftSetBoolArg("-detachdb", settings.value("detachDB").toBool()); if (!language.isEmpty()) SoftSetArg("-lang", language.toStdString()); } @@ -173,8 +171,6 @@ QVariant OptionsModel::data(const QModelIndex & index, int role) const return QVariant(nDisplayUnit); case DisplayAddresses: return QVariant(bDisplayAddresses); - case DetachDatabases: - return QVariant(bitdb.GetDetach()); case Language: return settings.value("language", ""); default: @@ -256,12 +252,6 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in bDisplayAddresses = value.toBool(); settings.setValue("bDisplayAddresses", bDisplayAddresses); break; - case DetachDatabases: { - bool fDetachDB = value.toBool(); - bitdb.SetDetach(fDetachDB); - settings.setValue("detachDB", fDetachDB); - } - break; case Language: settings.setValue("language", value); break; diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h index 2d86a7a9ca..4f893bb44e 100644 --- a/src/qt/optionsmodel.h +++ b/src/qt/optionsmodel.h @@ -28,7 +28,6 @@ public: Fee, // qint64 DisplayUnit, // BitcoinUnits::Unit DisplayAddresses, // bool - DetachDatabases, // bool Language, // QString OptionIDRowCount, }; diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index ca2c615333..0c1547bd8e 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -148,7 +148,7 @@ void SendCoinsDialog::on_sendButton_clicked() break; case WalletModel::TransactionCreationFailed: QMessageBox::warning(this, tr("Send Coins"), - tr("Error: Transaction creation failed."), + tr("Error: Transaction creation failed!"), QMessageBox::Ok, QMessageBox::Ok); break; case WalletModel::TransactionCommitFailed: diff --git a/src/qt/transactiontablemodel.h b/src/qt/transactiontablemodel.h index fd321ce280..b0687d5399 100644 --- a/src/qt/transactiontablemodel.h +++ b/src/qt/transactiontablemodel.h @@ -81,5 +81,4 @@ public slots: friend class TransactionTablePriv; }; -#endif - +#endif // TRANSACTIONTABLEMODEL_H diff --git a/src/rpcwallet.cpp b/src/rpcwallet.cpp index cd91f650a8..29b3298b99 100644 --- a/src/rpcwallet.cpp +++ b/src/rpcwallet.cpp @@ -1429,7 +1429,7 @@ Value encryptwallet(const Array& params, bool fHelp) // slack space in .dat files; that is bad if the old data is // unencrypted private keys. So: StartShutdown(); - return "wallet encrypted; Bitcoin server stopping, restart to run with encrypted wallet. The keypool has been flushed, you need to make a new backup."; + return "wallet encrypted; Bitcoin server stopping, restart to run with encrypted wallet. The keypool has been flushed, you need to make a new backup."; } class DescribeAddressVisitor : public boost::static_visitor<Object> 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..e4ba0259ba 100644 --- a/src/test/test_bitcoin.cpp +++ b/src/test/test_bitcoin.cpp @@ -19,10 +19,10 @@ struct TestingSetup { fPrintToDebugger = true; // don't want to write to debug.log file noui_connect(); bitdb.MakeMock(); - pblocktree = new CBlockTreeDB(true); - pcoinsdbview = new CCoinsViewDB(true); + pblocktree = new CBlockTreeDB(1 << 20, true); + pcoinsdbview = new CCoinsViewDB(1 << 23, 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 6550f57876..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(bool fMemory) : db(GetDataDir() / "coins", 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(bool fMemory) : CLevelDB(GetDataDir() / "blktree", 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 123ec00d23..d7d327069f 100644 --- a/src/txdb.h +++ b/src/txdb.h @@ -14,7 +14,7 @@ class CCoinsViewDB : public CCoinsView protected: CLevelDB db; public: - CCoinsViewDB(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(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(); }; diff --git a/src/wallet.cpp b/src/wallet.cpp index 67b4e8f095..ae9f695e9f 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -1292,7 +1292,7 @@ string CWallet::SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, if (IsLocked()) { - string strError = _("Error: Wallet locked, unable to create transaction "); + string strError = _("Error: Wallet locked, unable to create transaction!"); printf("SendMoney() : %s", strError.c_str()); return strError; } @@ -1300,9 +1300,9 @@ string CWallet::SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, { string strError; if (nValue + nFeeRequired > GetBalance()) - strError = strprintf(_("Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds "), FormatMoney(nFeeRequired).c_str()); + strError = strprintf(_("Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds!"), FormatMoney(nFeeRequired).c_str()); else - strError = _("Error: Transaction creation failed "); + strError = _("Error: Transaction creation failed!"); printf("SendMoney() : %s", strError.c_str()); return strError; } @@ -1311,7 +1311,7 @@ string CWallet::SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, return "ABORTED"; if (!CommitTransaction(wtxNew, reservekey)) - return _("Error: The transaction was rejected. This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here."); + return _("Error: The transaction was rejected! This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here."); return ""; } |