CDB::CDB() : Add initializer for fReadOnly member.
[novacoin.git] / src / db.cpp
index 88d9cfd..eb86627 100644 (file)
@@ -8,21 +8,18 @@
 #include "util.h"
 #include "main.h"
 #include "ui_interface.h"
+
 #include <boost/filesystem.hpp>
-#include <boost/filesystem/fstream.hpp>
 
 #ifndef WIN32
 #include "sys/stat.h"
 #endif
 
 using namespace std;
-using namespace boost;
-
 
-unsigned int nWalletDBUpdated;
+uint32_t nWalletDBUpdated;
 extern bool fUseMemoryLog;
 
-
 //
 // CDB
 //
@@ -42,11 +39,7 @@ void CDBEnv::EnvShutdown()
         DbEnv(0).remove(strPath.c_str(), 0);
 }
 
-CDBEnv::CDBEnv() : dbenv(DB_CXX_NO_EXCEPTIONS)
-{
-    fDbEnvInit = false;
-    fMockDb = false;
-}
+CDBEnv::CDBEnv() : fDetachDB(false), fDbEnvInit(false), fMockDb(false), dbenv(DB_CXX_NO_EXCEPTIONS) { }
 
 CDBEnv::~CDBEnv()
 {
@@ -67,18 +60,18 @@ bool CDBEnv::Open(boost::filesystem::path pathEnv_)
         return false;
 
     pathEnv = pathEnv_;
-    filesystem::path pathDataDir = pathEnv;
+    boost::filesystem::path pathDataDir = pathEnv;
     strPath = pathDataDir.string();
-    filesystem::path pathLogDir = pathDataDir / "database";
-    filesystem::create_directory(pathLogDir);
-    filesystem::path pathErrorFile = pathDataDir / "db.log";
+    boost::filesystem::path pathLogDir = pathDataDir / "database";
+    boost::filesystem::create_directory(pathLogDir);
+    boost::filesystem::path pathErrorFile = pathDataDir / "db.log";
     printf("dbenv.open LogDir=%s ErrorFile=%s\n", pathLogDir.string().c_str(), pathErrorFile.string().c_str());
 
-    unsigned int nEnvFlags = 0;
+    uint32_t nEnvFlags = 0;
     if (GetBoolArg("-privdb", true))
         nEnvFlags |= DB_PRIVATE;
 
-    int nDbCache = GetArg("-dbcache", 25);
+    int nDbCache = GetArgInt("-dbcache", 25);
     dbenv.set_lg_dir(pathLogDir.string().c_str());
     dbenv.set_cachesize(nDbCache / 1024, (nDbCache % 1024)*1048576, 1);
     dbenv.set_lg_bsize(1048576);
@@ -117,18 +110,18 @@ bool CDBEnv::Open(boost::filesystem::path pathEnv_)
     if (!dbenv.get_lk_max_locks(&nMaxLocks))
     {
         int nBlocks, nDeepReorg;
-        std::string strMessage;
+        string strMessage;
 
         nBlocks = nMaxLocks / 48768;
         nDeepReorg = (nBlocks - 1) / 2;
 
-        printf("Final lk_max_locks is %lu, sufficient for (worst case) %d block%s in a single transaction (up to a %d-deep reorganization)\n", (unsigned long)nMaxLocks, nBlocks, (nBlocks == 1) ? "" : "s", nDeepReorg);
+        printf("Final lk_max_locks is %u, sufficient for (worst case) %d block%s in a single transaction (up to a %d-deep reorganization)\n", nMaxLocks, nBlocks, (nBlocks == 1) ? "" : "s", nDeepReorg);
         if (nDeepReorg < 3)
         {
             if (nBlocks < 1)
-                strMessage = strprintf(_("Warning: DB_CONFIG has set_lk_max_locks %lu, which may be too low for a single block. If this limit is reached, NovaCoin may stop working."), (unsigned long)nMaxLocks);
+                strMessage = strprintf(_("Warning: DB_CONFIG has set_lk_max_locks %u, which may be too low for a single block. If this limit is reached, NovaCoin may stop working."), nMaxLocks);
             else
-                strMessage = strprintf(_("Warning: DB_CONFIG has set_lk_max_locks %lu, which may be too low for a common blockchain reorganization. If this limit is reached, NovaCoin may stop working."), (unsigned long)nMaxLocks);
+                strMessage = strprintf(_("Warning: DB_CONFIG has set_lk_max_locks %u, which may be too low for a common blockchain reorganization. If this limit is reached, NovaCoin may stop working."), nMaxLocks);
 
             strMiscWarning = strMessage;
             printf("*** %s\n", strMessage.c_str());
@@ -139,42 +132,7 @@ bool CDBEnv::Open(boost::filesystem::path pathEnv_)
     return true;
 }
 
-void CDBEnv::MakeMock()
-{
-    if (fDbEnvInit)
-        throw runtime_error("CDBEnv::MakeMock(): already initialized");
-
-    if (fShutdown)
-        throw runtime_error("CDBEnv::MakeMock(): during shutdown");
-
-    printf("CDBEnv::MakeMock()\n");
-
-    dbenv.set_cachesize(1, 0, 1);
-    dbenv.set_lg_bsize(10485760*4);
-    dbenv.set_lg_max(10485760);
-    dbenv.set_lk_max_locks(10000);
-    dbenv.set_lk_max_objects(10000);
-    dbenv.set_flags(DB_AUTO_COMMIT, 1);
-#ifdef DB_LOG_IN_MEMORY
-    dbenv.log_set_config(DB_LOG_IN_MEMORY, fUseMemoryLog ? 1 : 0);
-#endif
-    int ret = dbenv.open(NULL,
-                     DB_CREATE     |
-                     DB_INIT_LOCK  |
-                     DB_INIT_LOG   |
-                     DB_INIT_MPOOL |
-                     DB_INIT_TXN   |
-                     DB_THREAD     |
-                     DB_PRIVATE,
-                     S_IRUSR | S_IWUSR);
-    if (ret > 0)
-        throw runtime_error(strprintf("CDBEnv::MakeMock(): error %d opening database environment", ret));
-
-    fDbEnvInit = true;
-    fMockDb = true;
-}
-
-CDBEnv::VerifyResult CDBEnv::Verify(std::string strFile, bool (*recoverFunc)(CDBEnv& dbenv, std::string strFile))
+CDBEnv::VerifyResult CDBEnv::Verify(string strFile, bool (*recoverFunc)(CDBEnv& dbenv, string strFile))
 {
     LOCK(cs_db);
     assert(mapFileUseCount.count(strFile) == 0);
@@ -191,8 +149,8 @@ CDBEnv::VerifyResult CDBEnv::Verify(std::string strFile, bool (*recoverFunc)(CDB
     return (fRecovered ? RECOVER_OK : RECOVER_FAIL);
 }
 
-bool CDBEnv::Salvage(std::string strFile, bool fAggressive,
-                     std::vector<CDBEnv::KeyValPair >& vResult)
+bool CDBEnv::Salvage(string strFile, bool fAggressive,
+                     vector<CDBEnv::KeyValPair >& vResult)
 {
     LOCK(cs_db);
     assert(mapFileUseCount.count(strFile) == 0);
@@ -222,11 +180,11 @@ bool CDBEnv::Salvage(std::string strFile, bool fAggressive,
     while (!strDump.eof() && strLine != "HEADER=END")
         getline(strDump, strLine); // Skip past header
 
-    std::string keyHex, valueHex;
+    string keyHex, valueHex;
     while (!strDump.eof() && keyHex != "DATA=END")
     {
         getline(strDump, keyHex);
-        if (keyHex != "DATA_END")
+        if (keyHex != "DATA=END")
         {
             getline(strDump, valueHex);
             vResult.push_back(make_pair(ParseHex(keyHex),ParseHex(valueHex)));
@@ -237,7 +195,7 @@ bool CDBEnv::Salvage(std::string strFile, bool fAggressive,
 }
 
 
-void CDBEnv::CheckpointLSN(std::string strFile)
+void CDBEnv::CheckpointLSN(string strFile)
 {
     dbenv.txn_checkpoint(0, 0, 0);
     if (fMockDb)
@@ -247,7 +205,7 @@ void CDBEnv::CheckpointLSN(std::string strFile)
 
 
 CDB::CDB(const char *pszFile, const char* pszMode) :
-    pdb(NULL), activeTxn(NULL)
+    pdb(NULL), activeTxn(NULL), fReadOnly(true)
 {
     int ret;
     if (pszFile == NULL)
@@ -255,7 +213,7 @@ CDB::CDB(const char *pszFile, const char* pszMode) :
 
     fReadOnly = (!strchr(pszMode, '+') && !strchr(pszMode, 'w'));
     bool fCreate = strchr(pszMode, 'c') != NULL;
-    unsigned int nFlags = DB_THREAD;
+    uint32_t nFlags = DB_THREAD;
     if (fCreate)
         nFlags |= DB_CREATE;
 
@@ -271,17 +229,8 @@ CDB::CDB(const char *pszFile, const char* pszMode) :
         {
             pdb = new Db(&bitdb.dbenv, 0);
 
-            bool fMockDb = bitdb.IsMock();
-            if (fMockDb)
-            {
-                DbMpoolFile*mpf = pdb->get_mpf();
-                ret = mpf->set_flags(DB_MPOOL_NOFILE, 1);
-                if (ret != 0)
-                    throw runtime_error(strprintf("CDB() : failed to configure for no temp file backing for database %s", pszFile));
-            }
-
             ret = pdb->open(NULL,      // Txn pointer
-                            fMockDb ? NULL : pszFile,   // Filename
+                            pszFile,   // Filename
                             "main",    // Logical db name
                             DB_BTREE,  // Database type
                             nFlags,    // Flags
@@ -292,7 +241,7 @@ CDB::CDB(const char *pszFile, const char* pszMode) :
                 delete pdb;
                 pdb = NULL;
                 --bitdb.mapFileUseCount[strFile];
-                strFile = "";
+                strFile.clear();
                 throw runtime_error(strprintf("CDB() : can't open database file %s, error %d", pszFile, ret));
             }
 
@@ -309,7 +258,7 @@ CDB::CDB(const char *pszFile, const char* pszMode) :
     }
 }
 
-static bool IsChainFile(std::string strFile)
+static bool IsChainFile(string strFile)
 {
     if (strFile == "blkindex.dat")
         return true;
@@ -327,7 +276,7 @@ void CDB::Close()
     pdb = NULL;
 
     // Flush database activity from memory pool to disk log
-    unsigned int nMinutes = 0;
+    uint32_t nMinutes = 0;
     if (fReadOnly)
         nMinutes = 1;
     if (IsChainFile(strFile))
@@ -335,7 +284,7 @@ void CDB::Close()
     if (IsChainFile(strFile) && IsInitialBlockDownload())
         nMinutes = 5;
 
-    bitdb.dbenv.txn_checkpoint(nMinutes ? GetArg("-dblogsize", 100)*1024 : 0, nMinutes, 0);
+    bitdb.dbenv.txn_checkpoint(nMinutes ? GetArgUInt("-dblogsize", 100)*1024 : 0, nMinutes, 0);
 
     {
         LOCK(bitdb.cs_db);
@@ -367,6 +316,105 @@ bool CDBEnv::RemoveDb(const string& strFile)
     return (rc == 0);
 }
 
+DbTxn *CDBEnv::TxnBegin(int flags)
+{
+    DbTxn* ptxn = NULL;
+    int ret = dbenv.txn_begin(NULL, &ptxn, flags);
+    if (!ptxn || ret != 0)
+        return NULL;
+    return ptxn;
+}
+
+Dbc* CDB::GetCursor()
+{
+    if (!pdb)
+        return NULL;
+    Dbc* pcursor = NULL;
+    int ret = pdb->cursor(NULL, &pcursor, 0);
+    if (ret != 0)
+        return NULL;
+    return pcursor;
+}
+
+int CDB::ReadAtCursor(Dbc* pcursor, CDataStream& ssKey, CDataStream& ssValue, unsigned int fFlags)
+{
+    // Read at cursor
+    Dbt datKey;
+    if (fFlags == DB_SET || fFlags == DB_SET_RANGE || fFlags == DB_GET_BOTH || fFlags == DB_GET_BOTH_RANGE)
+    {
+        datKey.set_data(&ssKey[0]);
+        datKey.set_size((uint32_t)ssKey.size());
+    }
+    Dbt datValue;
+    if (fFlags == DB_GET_BOTH || fFlags == DB_GET_BOTH_RANGE)
+    {
+        datValue.set_data(&ssValue[0]);
+        datValue.set_size((uint32_t)ssValue.size());
+    }
+    datKey.set_flags(DB_DBT_MALLOC);
+    datValue.set_flags(DB_DBT_MALLOC);
+    int ret = pcursor->get(&datKey, &datValue, fFlags);
+    if (ret != 0)
+        return ret;
+    else if (datKey.get_data() == NULL || datValue.get_data() == NULL)
+        return 99999;
+
+    // Convert to streams
+    ssKey.SetType(SER_DISK);
+    ssKey.clear();
+    ssKey.write((char*)datKey.get_data(), datKey.get_size());
+    ssValue.SetType(SER_DISK);
+    ssValue.clear();
+    ssValue.write((char*)datValue.get_data(), datValue.get_size());
+
+    // Clear and free memory
+    memset(datKey.get_data(), 0, datKey.get_size());
+    memset(datValue.get_data(), 0, datValue.get_size());
+    free(datKey.get_data());
+    free(datValue.get_data());
+    return 0;
+}
+
+bool CDB::TxnBegin()
+{
+    if (!pdb || activeTxn)
+        return false;
+    DbTxn* ptxn = bitdb.TxnBegin();
+    if (!ptxn)
+        return false;
+    activeTxn = ptxn;
+    return true;
+}
+
+bool CDB::TxnCommit()
+{
+    if (!pdb || !activeTxn)
+        return false;
+    int ret = activeTxn->commit(0);
+    activeTxn = NULL;
+    return (ret == 0);
+}
+
+bool CDB::TxnAbort()
+{
+    if (!pdb || !activeTxn)
+        return false;
+    int ret = activeTxn->abort();
+    activeTxn = NULL;
+    return (ret == 0);
+}
+
+bool CDB::ReadVersion(int& nVersion)
+{
+    nVersion = 0;
+    return Read(std::string("version"), nVersion);
+}
+
+bool CDB::WriteVersion(int nVersion)
+{
+    return Write(std::string("version"), nVersion);
+}
+
 bool CDB::Rewrite(const string& strFile, const char* pszSkip)
 {
     while (!fShutdown)
@@ -400,7 +448,7 @@ bool CDB::Rewrite(const string& strFile, const char* pszSkip)
                     }
 
                     Dbc* pcursor = db.GetCursor();
-                    if (pcursor)
+                    if (pcursor) {
                         while (fSuccess)
                         {
                             CDataStream ssKey(SER_DISK, CLIENT_VERSION);
@@ -417,9 +465,14 @@ bool CDB::Rewrite(const string& strFile, const char* pszSkip)
                                 fSuccess = false;
                                 break;
                             }
-                            if (pszSkip &&
-                                strncmp(&ssKey[0], pszSkip, std::min(ssKey.size(), strlen(pszSkip))) == 0)
-                                continue;
+
+                            if (pszSkip != NULL)
+                            {
+                                size_t pszSkipLen = strlen(pszSkip);
+                                if (strncmp(&ssKey[0], pszSkip, min(ssKey.size(), pszSkipLen)) == 0)
+                                    continue;
+                            }
+
                             if (strncmp(&ssKey[0], "\x07version", 8) == 0)
                             {
                                 // Update version:
@@ -432,6 +485,7 @@ bool CDB::Rewrite(const string& strFile, const char* pszSkip)
                             if (ret2 > 0)
                                 fSuccess = false;
                         }
+                    }
                     if (fSuccess)
                     {
                         db.Close();
@@ -471,7 +525,7 @@ void CDBEnv::Flush(bool fShutdown)
         return;
     {
         LOCK(cs_db);
-        map<string, int>::iterator mi = mapFileUseCount.begin();
+        auto mi = mapFileUseCount.begin();
         while (mi != mapFileUseCount.end())
         {
             string strFile = (*mi).first;
@@ -523,11 +577,11 @@ bool CAddrDB::Write(const CAddrMan& addr)
     // Generate random temporary filename
     unsigned short randv = 0;
     RAND_bytes((unsigned char *)&randv, sizeof(randv));
-    std::string tmpfn = strprintf("peers.dat.%04x", randv);
+    string tmpfn = strprintf("peers.dat.%04x", randv);
 
     // serialize addresses, checksum data up to that point, then append csum
     CDataStream ssPeers(SER_DISK, CLIENT_VERSION);
-    ssPeers << FLATDATA(pchMessageStart);
+    ssPeers << nNetworkID;
     ssPeers << addr;
     uint256 hash = Hash(ssPeers.begin(), ssPeers.end());
     ssPeers << hash;
@@ -543,7 +597,7 @@ bool CAddrDB::Write(const CAddrMan& addr)
     try {
         fileout << ssPeers;
     }
-    catch (const std::exception&) {
+    catch (const exception&) {
         return error("CAddrman::Write() : I/O error");
     }
     FileCommit(fileout);
@@ -578,7 +632,7 @@ bool CAddrDB::Read(CAddrMan& addr)
         filein.read((char *)&vchData[0], dataSize);
         filein >> hashIn;
     }
-    catch (const std::exception&) {
+    catch (const exception&) {
         return error("CAddrman::Read() 2 : I/O error or stream data corrupted");
     }
     filein.fclose();
@@ -590,19 +644,17 @@ bool CAddrDB::Read(CAddrMan& addr)
     if (hashIn != hashTmp)
         return error("CAddrman::Read() : checksum mismatch; data corrupted");
 
-    unsigned char pchMsgTmp[4];
+    uint32_t nMsgNetID;
     try {
         // de-serialize file header (pchMessageStart magic number) and
-        ssPeers >> FLATDATA(pchMsgTmp);
-
+        ssPeers >> nMsgNetID;
         // verify the network matches ours
-        if (memcmp(pchMsgTmp, pchMessageStart, sizeof(pchMsgTmp)))
+        if (nMsgNetID != nNetworkID)
             return error("CAddrman::Read() : invalid network magic number");
-
         // de-serialize address data into one CAddrMan object
         ssPeers >> addr;
     }
-    catch (const std::exception&) {
+    catch (const exception&) {
         return error("CAddrman::Read() : I/O error or stream data corrupted");
     }