aboutsummaryrefslogtreecommitdiff
path: root/src/db.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/db.cpp')
-rw-r--r--src/db.cpp469
1 files changed, 97 insertions, 372 deletions
diff --git a/src/db.cpp b/src/db.cpp
index 278c82078a..cf96fe6e38 100644
--- a/src/db.cpp
+++ b/src/db.cpp
@@ -34,19 +34,14 @@ void CDBEnv::EnvShutdown()
return;
fDbEnvInit = false;
- try
- {
- dbenv.close(0);
- }
- catch (const DbException& e)
- {
- printf("EnvShutdown exception: %s (%d)\n", e.what(), e.get_errno());
- }
+ int ret = dbenv.close(0);
+ if (ret != 0)
+ printf("EnvShutdown exception: %s (%d)\n", DbEnv::strerror(ret), ret);
if (!fMockDb)
DbEnv(0).remove(GetDataDir().string().c_str(), 0);
}
-CDBEnv::CDBEnv() : dbenv(0)
+CDBEnv::CDBEnv() : dbenv(DB_CXX_NO_EXCEPTIONS)
{
}
@@ -84,8 +79,8 @@ bool CDBEnv::Open(boost::filesystem::path pathEnv_)
dbenv.set_cachesize(nDbCache / 1024, (nDbCache % 1024)*1048576, 1);
dbenv.set_lg_bsize(1048576);
dbenv.set_lg_max(10485760);
- dbenv.set_lk_max_locks(10000);
- dbenv.set_lk_max_objects(10000);
+ dbenv.set_lk_max_locks(40000);
+ dbenv.set_lk_max_objects(40000);
dbenv.set_errfile(fopen(pathErrorFile.string().c_str(), "a")); /// debug
dbenv.set_flags(DB_AUTO_COMMIT, 1);
dbenv.set_flags(DB_TXN_WRITE_NOSYNC, 1);
@@ -100,8 +95,8 @@ bool CDBEnv::Open(boost::filesystem::path pathEnv_)
DB_RECOVER |
nEnvFlags,
S_IRUSR | S_IWUSR);
- if (ret > 0)
- return error("CDB() : error %d opening database environment", ret);
+ if (ret != 0)
+ return error("CDB() : error %s (%d) opening database environment", DbEnv::strerror(ret), ret);
fDbEnvInit = true;
fMockDb = false;
@@ -141,6 +136,69 @@ void CDBEnv::MakeMock()
fMockDb = true;
}
+CDBEnv::VerifyResult CDBEnv::Verify(std::string strFile, bool (*recoverFunc)(CDBEnv& dbenv, std::string strFile))
+{
+ LOCK(cs_db);
+ assert(mapFileUseCount.count(strFile) == 0);
+
+ Db db(&dbenv, 0);
+ int result = db.verify(strFile.c_str(), NULL, NULL, 0);
+ if (result == 0)
+ return VERIFY_OK;
+ else if (recoverFunc == NULL)
+ return RECOVER_FAIL;
+
+ // Try to recover:
+ bool fRecovered = (*recoverFunc)(*this, strFile);
+ return (fRecovered ? RECOVER_OK : RECOVER_FAIL);
+}
+
+bool CDBEnv::Salvage(std::string strFile, bool fAggressive,
+ std::vector<CDBEnv::KeyValPair >& vResult)
+{
+ LOCK(cs_db);
+ assert(mapFileUseCount.count(strFile) == 0);
+
+ u_int32_t flags = DB_SALVAGE;
+ if (fAggressive) flags |= DB_AGGRESSIVE;
+
+ stringstream strDump;
+
+ Db db(&dbenv, 0);
+ int result = db.verify(strFile.c_str(), NULL, &strDump, flags);
+ if (result != 0)
+ {
+ printf("ERROR: db salvage failed\n");
+ return false;
+ }
+
+ // Format of bdb dump is ascii lines:
+ // header lines...
+ // HEADER=END
+ // hexadecimal key
+ // hexadecimal value
+ // ... repeated
+ // DATA=END
+
+ string strLine;
+ while (!strDump.eof() && strLine != "HEADER=END")
+ getline(strDump, strLine); // Skip past header
+
+ std::string keyHex, valueHex;
+ while (!strDump.eof() && keyHex != "DATA=END")
+ {
+ getline(strDump, keyHex);
+ if (keyHex != "DATA_END")
+ {
+ getline(strDump, valueHex);
+ vResult.push_back(make_pair(ParseHex(keyHex),ParseHex(valueHex)));
+ }
+ }
+
+ return (result == 0);
+}
+
+
void CDBEnv::CheckpointLSN(std::string strFile)
{
dbenv.txn_checkpoint(0, 0, 0);
@@ -186,12 +244,12 @@ CDB::CDB(const char *pszFile, const char* pszMode) :
ret = pdb->open(NULL, // Txn pointer
fMockDb ? NULL : pszFile, // Filename
- "main", // Logical db name
+ fMockDb ? pszFile : "main", // Logical db name
DB_BTREE, // Database type
nFlags, // Flags
0);
- if (ret > 0)
+ if (ret != 0)
{
delete pdb;
pdb = NULL;
@@ -215,20 +273,16 @@ CDB::CDB(const char *pszFile, const char* pszMode) :
static bool IsChainFile(std::string strFile)
{
- if (strFile == "blkindex.dat")
+ if (strFile == "coins.dat" || strFile == "blktree.dat")
return true;
return false;
}
-void CDB::Close()
+void CDB::Flush()
{
- if (!pdb)
- return;
if (activeTxn)
- activeTxn->abort();
- activeTxn = NULL;
- pdb = NULL;
+ return;
// Flush database activity from memory pool to disk log
unsigned int nMinutes = 0;
@@ -240,6 +294,18 @@ void CDB::Close()
nMinutes = 5;
bitdb.dbenv.txn_checkpoint(nMinutes ? GetArg("-dblogsize", 100)*1024 : 0, nMinutes, 0);
+}
+
+void CDB::Close()
+{
+ if (!pdb)
+ return;
+ if (activeTxn)
+ activeTxn->abort();
+ activeTxn = NULL;
+ pdb = NULL;
+
+ Flush();
{
LOCK(bitdb.cs_db);
@@ -262,6 +328,15 @@ void CDBEnv::CloseDb(const string& strFile)
}
}
+bool CDBEnv::RemoveDb(const string& strFile)
+{
+ this->CloseDb(strFile);
+
+ LOCK(cs_db);
+ int rc = dbenv.dbremove(NULL, strFile.c_str(), NULL, DB_AUTO_COMMIT);
+ return (rc == 0);
+}
+
bool CDB::Rewrite(const string& strFile, const char* pszSkip)
{
while (!fShutdown)
@@ -407,356 +482,6 @@ void CDBEnv::Flush(bool fShutdown)
-//
-// CTxDB
-//
-
-bool CTxDB::ReadTxIndex(uint256 hash, CTxIndex& txindex)
-{
- assert(!fClient);
- txindex.SetNull();
- return Read(make_pair(string("tx"), hash), txindex);
-}
-
-bool CTxDB::UpdateTxIndex(uint256 hash, const CTxIndex& txindex)
-{
- assert(!fClient);
- return Write(make_pair(string("tx"), hash), txindex);
-}
-
-bool CTxDB::AddTxIndex(const CTransaction& tx, const CDiskTxPos& pos, int nHeight)
-{
- assert(!fClient);
-
- // Add to tx index
- uint256 hash = tx.GetHash();
- CTxIndex txindex(pos, tx.vout.size());
- return Write(make_pair(string("tx"), hash), txindex);
-}
-
-bool CTxDB::EraseTxIndex(const CTransaction& tx)
-{
- assert(!fClient);
- uint256 hash = tx.GetHash();
-
- return Erase(make_pair(string("tx"), hash));
-}
-
-bool CTxDB::ContainsTx(uint256 hash)
-{
- assert(!fClient);
- return Exists(make_pair(string("tx"), hash));
-}
-
-bool CTxDB::ReadDiskTx(uint256 hash, CTransaction& tx, CTxIndex& txindex)
-{
- assert(!fClient);
- tx.SetNull();
- if (!ReadTxIndex(hash, txindex))
- return false;
- return (tx.ReadFromDisk(txindex.pos));
-}
-
-bool CTxDB::ReadDiskTx(uint256 hash, CTransaction& tx)
-{
- CTxIndex txindex;
- return ReadDiskTx(hash, tx, txindex);
-}
-
-bool CTxDB::ReadDiskTx(COutPoint outpoint, CTransaction& tx, CTxIndex& txindex)
-{
- return ReadDiskTx(outpoint.hash, tx, txindex);
-}
-
-bool CTxDB::ReadDiskTx(COutPoint outpoint, CTransaction& tx)
-{
- CTxIndex txindex;
- return ReadDiskTx(outpoint.hash, tx, txindex);
-}
-
-bool CTxDB::WriteBlockIndex(const CDiskBlockIndex& blockindex)
-{
- return Write(make_pair(string("blockindex"), blockindex.GetBlockHash()), blockindex);
-}
-
-bool CTxDB::ReadHashBestChain(uint256& hashBestChain)
-{
- return Read(string("hashBestChain"), hashBestChain);
-}
-
-bool CTxDB::WriteHashBestChain(uint256 hashBestChain)
-{
- return Write(string("hashBestChain"), hashBestChain);
-}
-
-bool CTxDB::ReadBestInvalidWork(CBigNum& bnBestInvalidWork)
-{
- return Read(string("bnBestInvalidWork"), bnBestInvalidWork);
-}
-
-bool CTxDB::WriteBestInvalidWork(CBigNum bnBestInvalidWork)
-{
- return Write(string("bnBestInvalidWork"), bnBestInvalidWork);
-}
-
-CBlockIndex static * InsertBlockIndex(uint256 hash)
-{
- if (hash == 0)
- return NULL;
-
- // Return existing
- map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hash);
- if (mi != mapBlockIndex.end())
- return (*mi).second;
-
- // Create new
- CBlockIndex* pindexNew = new CBlockIndex();
- if (!pindexNew)
- throw runtime_error("LoadBlockIndex() : new CBlockIndex failed");
- mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first;
- pindexNew->phashBlock = &((*mi).first);
-
- return pindexNew;
-}
-
-bool CTxDB::LoadBlockIndex()
-{
- if (!LoadBlockIndexGuts())
- return false;
-
- if (fRequestShutdown)
- return true;
-
- // Calculate bnChainWork
- vector<pair<int, CBlockIndex*> > vSortedByHeight;
- vSortedByHeight.reserve(mapBlockIndex.size());
- BOOST_FOREACH(const PAIRTYPE(uint256, CBlockIndex*)& item, mapBlockIndex)
- {
- CBlockIndex* pindex = item.second;
- vSortedByHeight.push_back(make_pair(pindex->nHeight, pindex));
- }
- sort(vSortedByHeight.begin(), vSortedByHeight.end());
- BOOST_FOREACH(const PAIRTYPE(int, CBlockIndex*)& item, vSortedByHeight)
- {
- CBlockIndex* pindex = item.second;
- pindex->bnChainWork = (pindex->pprev ? pindex->pprev->bnChainWork : 0) + pindex->GetBlockWork();
- }
-
- // Load hashBestChain pointer to end of best chain
- if (!ReadHashBestChain(hashBestChain))
- {
- if (pindexGenesisBlock == NULL)
- return true;
- return error("CTxDB::LoadBlockIndex() : hashBestChain not loaded");
- }
- if (!mapBlockIndex.count(hashBestChain))
- return error("CTxDB::LoadBlockIndex() : hashBestChain not found in the block index");
- pindexBest = mapBlockIndex[hashBestChain];
- nBestHeight = pindexBest->nHeight;
- bnBestChainWork = pindexBest->bnChainWork;
- printf("LoadBlockIndex(): hashBestChain=%s height=%d date=%s\n",
- hashBestChain.ToString().substr(0,20).c_str(), nBestHeight,
- DateTimeStrFormat("%x %H:%M:%S", pindexBest->GetBlockTime()).c_str());
-
- // Load bnBestInvalidWork, OK if it doesn't exist
- 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<pair<unsigned int, unsigned int>, CBlockIndex*> mapBlockPos;
- for (CBlockIndex* pindex = pindexBest; pindex && pindex->pprev; pindex = pindex->pprev)
- {
- if (fRequestShutdown || pindex->nHeight < nBestHeight-nCheckDepth)
- break;
- CBlock block;
- if (!block.ReadFromDisk(pindex))
- return error("LoadBlockIndex() : block.ReadFromDisk failed");
- // 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<unsigned int, unsigned int> 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
- unsigned int nOutput = 0;
- if (nCheckLevel>3)
- {
- BOOST_FOREACH(const CDiskTxPos &txpos, txindex.vSpent)
- {
- if (!txpos.IsNull())
- {
- pair<unsigned int, unsigned int> 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 && !fRequestShutdown)
- {
- // Reorg back to the fork
- printf("LoadBlockIndex() : *** moving best chain pointer back to block %d\n", pindexFork->nHeight);
- CBlock block;
- if (!block.ReadFromDisk(pindexFork))
- return error("LoadBlockIndex() : block.ReadFromDisk failed");
- CTxDB txdb;
- block.SetBestChain(txdb, pindexFork);
- }
-
- return true;
-}
-
-
-
-bool CTxDB::LoadBlockIndexGuts()
-{
- // Get database cursor
- Dbc* pcursor = GetCursor();
- if (!pcursor)
- return false;
-
- // Load mapBlockIndex
- unsigned int fFlags = DB_SET_RANGE;
- loop
- {
- // Read next record
- CDataStream ssKey(SER_DISK, CLIENT_VERSION);
- if (fFlags == DB_SET_RANGE)
- ssKey << make_pair(string("blockindex"), uint256(0));
- CDataStream ssValue(SER_DISK, CLIENT_VERSION);
- int ret = ReadAtCursor(pcursor, ssKey, ssValue, fFlags);
- fFlags = DB_NEXT;
- if (ret == DB_NOTFOUND)
- break;
- else if (ret != 0)
- return false;
-
- // Unserialize
-
- try {
- string strType;
- ssKey >> strType;
- if (strType == "blockindex" && !fRequestShutdown)
- {
- CDiskBlockIndex diskindex;
- ssValue >> diskindex;
-
- // Construct block index object
- CBlockIndex* pindexNew = InsertBlockIndex(diskindex.GetBlockHash());
- pindexNew->pprev = InsertBlockIndex(diskindex.hashPrev);
- pindexNew->pnext = InsertBlockIndex(diskindex.hashNext);
- pindexNew->nFile = diskindex.nFile;
- pindexNew->nBlockPos = diskindex.nBlockPos;
- pindexNew->nHeight = diskindex.nHeight;
- pindexNew->nVersion = diskindex.nVersion;
- pindexNew->hashMerkleRoot = diskindex.hashMerkleRoot;
- pindexNew->nTime = diskindex.nTime;
- pindexNew->nBits = diskindex.nBits;
- pindexNew->nNonce = diskindex.nNonce;
-
- // Watch for genesis block
- if (pindexGenesisBlock == NULL && diskindex.GetBlockHash() == hashGenesisBlock)
- pindexGenesisBlock = pindexNew;
-
- if (!pindexNew->CheckIndex())
- return error("LoadBlockIndex() : CheckIndex failed at %d", pindexNew->nHeight);
- }
- else
- {
- break; // if shutdown requested or finished loading block index
- }
- } // try
- catch (std::exception &e) {
- return error("%s() : deserialize error", __PRETTY_FUNCTION__);
- }
- }
- pcursor->close();
-
- return true;
-}