X-Git-Url: https://git.novaco.in/?a=blobdiff_plain;f=src%2Fdb.cpp;h=52db39a3d5e6dff0ed6efc73cfddfc5d2bafbe83;hb=f8dcd5ca6f55ad49807cf7491c1f153f6158400e;hp=3bdfd6455623b5d1625c049eec297f6118d9b4ba;hpb=a6b4a11385bf44e695c3e47cbd0de6e40eea0b23;p=novacoin.git diff --git a/src/db.cpp b/src/db.cpp index 3bdfd64..52db39a 100644 --- a/src/db.cpp +++ b/src/db.cpp @@ -72,8 +72,8 @@ CDB::CDB(const char* pszFile, const char* pszMode) : pdb(NULL) if (fCreate) nFlags |= DB_CREATE; - CRITICAL_BLOCK(cs_db) { + LOCK(cs_db); if (!fDbEnvInit) { if (fShutdown) @@ -87,12 +87,13 @@ CDB::CDB(const char* pszFile, const char* pszMode) : pdb(NULL) int nDbCache = GetArg("-dbcache", 25); dbenv.set_lg_dir(strLogDir.c_str()); dbenv.set_cachesize(nDbCache / 1024, (nDbCache % 1024)*1048576, 1); - dbenv.set_lg_bsize(10485760); - dbenv.set_lg_max(104857600); + dbenv.set_lg_bsize(1048576); + dbenv.set_lg_max(10485760); dbenv.set_lk_max_locks(10000); dbenv.set_lk_max_objects(10000); dbenv.set_errfile(fopen(strErrorFile.c_str(), "a")); /// debug dbenv.set_flags(DB_AUTO_COMMIT, 1); + dbenv.log_set_config(DB_LOG_AUTO_REMOVE, 1); ret = dbenv.open(strDataDir.c_str(), DB_CREATE | DB_INIT_LOCK | @@ -125,8 +126,10 @@ CDB::CDB(const char* pszFile, const char* pszMode) : pdb(NULL) { delete pdb; pdb = NULL; - CRITICAL_BLOCK(cs_db) + { + LOCK(cs_db); --mapFileUseCount[strFile]; + } strFile = ""; throw runtime_error(strprintf("CDB() : can't open database file %s, error %d", pszFile, ret)); } @@ -159,18 +162,21 @@ void CDB::Close() nMinutes = 1; if (strFile == "addr.dat") nMinutes = 2; - if (strFile == "blkindex.dat" && IsInitialBlockDownload() && nBestHeight % 5000 != 0) - nMinutes = 1; - dbenv.txn_checkpoint(0, nMinutes, 0); + if (strFile == "blkindex.dat" && IsInitialBlockDownload()) + nMinutes = 5; - CRITICAL_BLOCK(cs_db) + dbenv.txn_checkpoint(nMinutes ? GetArg("-dblogsize", 100)*1024 : 0, nMinutes, 0); + + { + LOCK(cs_db); --mapFileUseCount[strFile]; + } } void static CloseDb(const string& strFile) { - CRITICAL_BLOCK(cs_db) { + LOCK(cs_db); if (mapDb[strFile] != NULL) { // Close the database handle @@ -186,8 +192,8 @@ bool CDB::Rewrite(const string& strFile, const char* pszSkip) { while (!fShutdown) { - CRITICAL_BLOCK(cs_db) { + LOCK(cs_db); if (!mapFileUseCount.count(strFile) || mapFileUseCount[strFile] == 0) { // Flush log data to the dat file @@ -284,8 +290,8 @@ void DBFlush(bool fShutdown) printf("DBFlush(%s)%s\n", fShutdown ? "true" : "false", fDbEnvInit ? "" : " db not started"); if (!fDbEnvInit) return; - CRITICAL_BLOCK(cs_db) { + LOCK(cs_db); map::iterator mi = mapFileUseCount.begin(); while (mi != mapFileUseCount.end()) { @@ -583,19 +589,114 @@ bool CTxDB::LoadBlockIndex() ReadBestInvalidWork(bnBestInvalidWork); // Verify blocks in the best chain + int nCheckLevel = GetArg("-checklevel", 1); + int nCheckDepth = GetArg( "-checkblocks", 2500); + if (nCheckDepth == 0) + nCheckDepth = 1000000000; // suffices until the year 19000 + if (nCheckDepth > nBestHeight) + nCheckDepth = nBestHeight; + printf("Verifying last %i blocks at level %i\n", nCheckDepth, nCheckLevel); CBlockIndex* pindexFork = NULL; + map, CBlockIndex*> mapBlockPos; for (CBlockIndex* pindex = pindexBest; pindex && pindex->pprev; pindex = pindex->pprev) { - if (pindex->nHeight < nBestHeight-2500 && !mapArgs.count("-checkblocks")) + if (pindex->nHeight < nBestHeight-nCheckDepth) break; CBlock block; if (!block.ReadFromDisk(pindex)) return error("LoadBlockIndex() : block.ReadFromDisk failed"); - if (!block.CheckBlock()) + // check level 1: verify block validity + if (nCheckLevel>0 && !block.CheckBlock()) { printf("LoadBlockIndex() : *** found bad block at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); pindexFork = pindex->pprev; } + // check level 2: verify transaction index validity + if (nCheckLevel>1) + { + pair pos = make_pair(pindex->nFile, pindex->nBlockPos); + mapBlockPos[pos] = pindex; + BOOST_FOREACH(const CTransaction &tx, block.vtx) + { + uint256 hashTx = tx.GetHash(); + CTxIndex txindex; + if (ReadTxIndex(hashTx, txindex)) + { + // check level 3: checker transaction hashes + if (nCheckLevel>2 || pindex->nFile != txindex.pos.nFile || pindex->nBlockPos != txindex.pos.nBlockPos) + { + // either an error or a duplicate transaction + CTransaction txFound; + if (!txFound.ReadFromDisk(txindex.pos)) + { + printf("LoadBlockIndex() : *** cannot read mislocated transaction %s\n", hashTx.ToString().c_str()); + pindexFork = pindex->pprev; + } + else + if (txFound.GetHash() != hashTx) // not a duplicate tx + { + printf("LoadBlockIndex(): *** invalid tx position for %s\n", hashTx.ToString().c_str()); + pindexFork = pindex->pprev; + } + } + // check level 4: check whether spent txouts were spent within the main chain + int nOutput = 0; + if (nCheckLevel>3) + BOOST_FOREACH(const CDiskTxPos &txpos, txindex.vSpent) + { + if (!txpos.IsNull()) + { + pair posFind = make_pair(txpos.nFile, txpos.nBlockPos); + if (!mapBlockPos.count(posFind)) + { + printf("LoadBlockIndex(): *** found bad spend at %d, hashBlock=%s, hashTx=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString().c_str(), hashTx.ToString().c_str()); + pindexFork = pindex->pprev; + } + // check level 6: check whether spent txouts were spent by a valid transaction that consume them + if (nCheckLevel>5) + { + CTransaction txSpend; + if (!txSpend.ReadFromDisk(txpos)) + { + printf("LoadBlockIndex(): *** cannot read spending transaction of %s:%i from disk\n", hashTx.ToString().c_str(), nOutput); + pindexFork = pindex->pprev; + } + else if (!txSpend.CheckTransaction()) + { + printf("LoadBlockIndex(): *** spending transaction of %s:%i is invalid\n", hashTx.ToString().c_str(), nOutput); + pindexFork = pindex->pprev; + } + else + { + bool fFound = false; + BOOST_FOREACH(const CTxIn &txin, txSpend.vin) + if (txin.prevout.hash == hashTx && txin.prevout.n == nOutput) + fFound = true; + if (!fFound) + { + printf("LoadBlockIndex(): *** spending transaction of %s:%i does not spend it\n", hashTx.ToString().c_str(), nOutput); + pindexFork = pindex->pprev; + } + } + } + } + nOutput++; + } + } + // check level 5: check whether all prevouts are marked spent + if (nCheckLevel>4) + BOOST_FOREACH(const CTxIn &txin, tx.vin) + { + CTxIndex txindex; + if (ReadTxIndex(txin.prevout.hash, txindex)) + if (txindex.vSpent.size()-1 < txin.prevout.n || txindex.vSpent[txin.prevout.n].IsNull()) + { + printf("LoadBlockIndex(): *** found unspent prevout %s:%i in %s\n", txin.prevout.hash.ToString().c_str(), txin.prevout.n, hashTx.ToString().c_str()); + pindexFork = pindex->pprev; + } + } + } + } } if (pindexFork) { @@ -619,31 +720,23 @@ bool CTxDB::LoadBlockIndex() // CAddrDB // -bool CAddrDB::WriteAddress(const CAddress& addr) -{ - return Write(make_pair(string("addr"), addr.GetKey()), addr); -} - bool CAddrDB::WriteAddrman(const CAddrMan& addrman) { return Write(string("addrman"), addrman); } -bool CAddrDB::EraseAddress(const CAddress& addr) +bool CAddrDB::LoadAddresses() { - return Erase(make_pair(string("addr"), addr.GetKey())); -} - -bool CAddrDB::LoadAddresses(bool &fUpdate) -{ - bool fAddrMan = false; if (Read(string("addrman"), addrman)) { printf("Loaded %i addresses\n", addrman.size()); - fAddrMan = true; + return true; } + + // Read pre-0.6 addr records vector vAddr; + vector > vDelete; // Get cursor Dbc* pcursor = GetCursor(); @@ -666,35 +759,26 @@ bool CAddrDB::LoadAddresses(bool &fUpdate) ssKey >> strType; if (strType == "addr") { - if (fAddrMan) - fUpdate = true; - else - { - CAddress addr; - ssValue >> addr; - vAddr.push_back(addr); - } - + CAddress addr; + ssValue >> addr; + vAddr.push_back(addr); } } pcursor->close(); - if (!fAddrMan) - { - addrman.Add(vAddr, CNetAddr("0.0.0.0")); - printf("Loaded %i addresses\n", addrman.size()); - } + addrman.Add(vAddr, CNetAddr("0.0.0.0")); + printf("Loaded %i addresses\n", addrman.size()); + + // Note: old records left; we ran into hangs-on-startup + // bugs for some users who (we think) were running after + // an unclean shutdown. return true; } bool LoadAddresses() { - bool fUpdate = false; - bool fRet = CAddrDB("cr+").LoadAddresses(fUpdate); - if (fUpdate) - CDB::Rewrite("addr.dat", "\004addr"); - return fRet; + return CAddrDB("cr+").LoadAddresses(); } @@ -797,8 +881,16 @@ int CWalletDB::LoadWallet(CWallet* pwallet) bool fIsEncrypted = false; //// todo: shouldn't we catch exceptions and try to recover and continue? - CRITICAL_BLOCK(pwallet->cs_wallet) { + LOCK(pwallet->cs_wallet); + int nMinVersion = 0; + if (Read((string)"minversion", nMinVersion)) + { + if (nMinVersion > CLIENT_VERSION) + return DB_TOO_NEW; + pwallet->LoadMinVersion(nMinVersion); + } + // Get cursor Dbc* pcursor = GetCursor(); if (!pcursor) @@ -968,14 +1060,6 @@ int CWalletDB::LoadWallet(CWallet* pwallet) if (nFileVersion == 10300) nFileVersion = 300; } - else if (strType == "minversion") - { - int nMinVersion = 0; - ssValue >> nMinVersion; - if (nMinVersion > CLIENT_VERSION) - return DB_TOO_NEW; - pwallet->LoadMinVersion(nMinVersion); - } else if (strType == "cscript") { uint160 hash; @@ -1039,7 +1123,8 @@ void ThreadFlushWalletDB(void* parg) if (nLastFlushed != nWalletDBUpdated && GetTime() - nLastWalletUpdate >= 2) { - TRY_CRITICAL_BLOCK(cs_db) + TRY_LOCK(cs_db,lockDb); + if (lockDb) { // Don't do this if any databases are in use int nRefCount = 0; @@ -1080,8 +1165,8 @@ bool BackupWallet(const CWallet& wallet, const string& strDest) return false; while (!fShutdown) { - CRITICAL_BLOCK(cs_db) { + LOCK(cs_db); if (!mapFileUseCount.count(wallet.strWalletFile) || mapFileUseCount[wallet.strWalletFile] == 0) { // Flush log data to the dat file