Better wording for transaction fee notification messages
[novacoin.git] / db.cpp
diff --git a/db.cpp b/db.cpp
index 699a94f..28a8b71 100644 (file)
--- a/db.cpp
+++ b/db.cpp
-// Copyright (c) 2009 Satoshi Nakamoto\r
-// Distributed under the MIT/X11 software license, see the accompanying\r
-// file license.txt or http://www.opensource.org/licenses/mit-license.php.\r
-\r
-#include "headers.h"\r
-\r
-void ThreadFlushWalletDB(void* parg);\r
-\r
-\r
-unsigned int nWalletDBUpdated;\r
-\r
-\r
-\r
-\r
-//\r
-// CDB\r
-//\r
-\r
-static CCriticalSection cs_db;\r
-static bool fDbEnvInit = false;\r
-DbEnv dbenv(0);\r
-static map<string, int> mapFileUseCount;\r
-\r
-class CDBInit\r
-{\r
-public:\r
-    CDBInit()\r
-    {\r
-    }\r
-    ~CDBInit()\r
-    {\r
-        if (fDbEnvInit)\r
-        {\r
-            dbenv.close(0);\r
-            fDbEnvInit = false;\r
-        }\r
-    }\r
-}\r
-instance_of_cdbinit;\r
-\r
-\r
-CDB::CDB(const char* pszFile, const char* pszMode, bool fTxn) : pdb(NULL)\r
-{\r
-    int ret;\r
-    if (pszFile == NULL)\r
-        return;\r
-\r
-    bool fCreate = strchr(pszMode, 'c');\r
-    bool fReadOnly = (!strchr(pszMode, '+') && !strchr(pszMode, 'w'));\r
-    unsigned int nFlags = DB_THREAD;\r
-    if (fCreate)\r
-        nFlags |= DB_CREATE;\r
-    else if (fReadOnly)\r
-        nFlags |= DB_RDONLY;\r
-    if (!fReadOnly || fTxn)\r
-        nFlags |= DB_AUTO_COMMIT;\r
-\r
-    CRITICAL_BLOCK(cs_db)\r
-    {\r
-        if (!fDbEnvInit)\r
-        {\r
-            if (fShutdown)\r
-                return;\r
-            string strAppDir = GetAppDir();\r
-            string strLogDir = strAppDir + "\\database";\r
-            _mkdir(strLogDir.c_str());\r
-            printf("dbenv.open strAppDir=%s\n", strAppDir.c_str());\r
-\r
-            dbenv.set_lg_dir(strLogDir.c_str());\r
-            dbenv.set_lg_max(10000000);\r
-            dbenv.set_lk_max_locks(10000);\r
-            dbenv.set_lk_max_objects(10000);\r
-            dbenv.set_errfile(fopen("db.log", "a")); /// debug\r
-            ///dbenv.log_set_config(DB_LOG_AUTO_REMOVE, 1); /// causes corruption\r
-            ret = dbenv.open(strAppDir.c_str(),\r
-                             DB_CREATE     |\r
-                             DB_INIT_LOCK  |\r
-                             DB_INIT_LOG   |\r
-                             DB_INIT_MPOOL |\r
-                             DB_INIT_TXN   |\r
-                             DB_THREAD     |\r
-                             DB_PRIVATE    |\r
-                             DB_RECOVER,\r
-                             0);\r
-            if (ret > 0)\r
-                throw runtime_error(strprintf("CDB() : error %d opening database environment\n", ret));\r
-            fDbEnvInit = true;\r
-        }\r
-\r
-        strFile = pszFile;\r
-        ++mapFileUseCount[strFile];\r
-    }\r
-\r
-    pdb = new Db(&dbenv, 0);\r
-\r
-    ret = pdb->open(NULL,      // Txn pointer\r
-                    pszFile,   // Filename\r
-                    "main",    // Logical db name\r
-                    DB_BTREE,  // Database type\r
-                    nFlags,    // Flags\r
-                    0);\r
-\r
-    if (ret > 0)\r
-    {\r
-        delete pdb;\r
-        pdb = NULL;\r
-        CRITICAL_BLOCK(cs_db)\r
-            --mapFileUseCount[strFile];\r
-        strFile = "";\r
-        throw runtime_error(strprintf("CDB() : can't open database file %s, error %d\n", pszFile, ret));\r
-    }\r
-\r
-    if (fCreate && !Exists(string("version")))\r
-        WriteVersion(VERSION);\r
-\r
-    RandAddSeed();\r
-}\r
-\r
-void CDB::Close()\r
-{\r
-    if (!pdb)\r
-        return;\r
-    if (!vTxn.empty())\r
-        vTxn.front()->abort();\r
-    vTxn.clear();\r
-    pdb->close(0);\r
-    delete pdb;\r
-    pdb = NULL;\r
-    dbenv.txn_checkpoint(0, 0, 0);\r
-\r
-    CRITICAL_BLOCK(cs_db)\r
-        --mapFileUseCount[strFile];\r
-\r
-    RandAddSeed();\r
-}\r
-\r
-void DBFlush(bool fShutdown)\r
-{\r
-    // Flush log data to the actual data file\r
-    //  on all files that are not in use\r
-    printf("DBFlush(%s)\n", fShutdown ? "true" : "false");\r
-    CRITICAL_BLOCK(cs_db)\r
-    {\r
-        dbenv.txn_checkpoint(0, 0, 0);\r
-        map<string, int>::iterator mi = mapFileUseCount.begin();\r
-        while (mi != mapFileUseCount.end())\r
-        {\r
-            string strFile = (*mi).first;\r
-            int nRefCount = (*mi).second;\r
-            if (nRefCount == 0)\r
-            {\r
-                dbenv.lsn_reset(strFile.c_str(), 0);\r
-                mapFileUseCount.erase(mi++);\r
-            }\r
-            else\r
-                mi++;\r
-        }\r
-        if (fShutdown)\r
-        {\r
-            char** listp;\r
-            if (mapFileUseCount.empty())\r
-                dbenv.log_archive(&listp, DB_ARCH_REMOVE);\r
-            dbenv.close(0);\r
-            fDbEnvInit = false;\r
-        }\r
-    }\r
-}\r
-\r
-\r
-\r
-\r
-\r
-\r
-//\r
-// CTxDB\r
-//\r
-\r
-bool CTxDB::ReadTxIndex(uint256 hash, CTxIndex& txindex)\r
-{\r
-    assert(!fClient);\r
-    txindex.SetNull();\r
-    return Read(make_pair(string("tx"), hash), txindex);\r
-}\r
-\r
-bool CTxDB::UpdateTxIndex(uint256 hash, const CTxIndex& txindex)\r
-{\r
-    assert(!fClient);\r
-    return Write(make_pair(string("tx"), hash), txindex);\r
-}\r
-\r
-bool CTxDB::AddTxIndex(const CTransaction& tx, const CDiskTxPos& pos, int nHeight)\r
-{\r
-    assert(!fClient);\r
-\r
-    // Add to tx index\r
-    uint256 hash = tx.GetHash();\r
-    CTxIndex txindex(pos, tx.vout.size());\r
-    return Write(make_pair(string("tx"), hash), txindex);\r
-}\r
-\r
-bool CTxDB::EraseTxIndex(const CTransaction& tx)\r
-{\r
-    assert(!fClient);\r
-    uint256 hash = tx.GetHash();\r
-\r
-    return Erase(make_pair(string("tx"), hash));\r
-}\r
-\r
-bool CTxDB::ContainsTx(uint256 hash)\r
-{\r
-    assert(!fClient);\r
-    return Exists(make_pair(string("tx"), hash));\r
-}\r
-\r
-bool CTxDB::ReadOwnerTxes(uint160 hash160, int nMinHeight, vector<CTransaction>& vtx)\r
-{\r
-    assert(!fClient);\r
-    vtx.clear();\r
-\r
-    // Get cursor\r
-    Dbc* pcursor = GetCursor();\r
-    if (!pcursor)\r
-        return false;\r
-\r
-    unsigned int fFlags = DB_SET_RANGE;\r
-    loop\r
-    {\r
-        // Read next record\r
-        CDataStream ssKey;\r
-        if (fFlags == DB_SET_RANGE)\r
-            ssKey << string("owner") << hash160 << CDiskTxPos(0, 0, 0);\r
-        CDataStream ssValue;\r
-        int ret = ReadAtCursor(pcursor, ssKey, ssValue, fFlags);\r
-        fFlags = DB_NEXT;\r
-        if (ret == DB_NOTFOUND)\r
-            break;\r
-        else if (ret != 0)\r
-            return false;\r
-\r
-        // Unserialize\r
-        string strType;\r
-        uint160 hashItem;\r
-        CDiskTxPos pos;\r
-        ssKey >> strType >> hashItem >> pos;\r
-        int nItemHeight;\r
-        ssValue >> nItemHeight;\r
-\r
-        // Read transaction\r
-        if (strType != "owner" || hashItem != hash160)\r
-            break;\r
-        if (nItemHeight >= nMinHeight)\r
-        {\r
-            vtx.resize(vtx.size()+1);\r
-            if (!vtx.back().ReadFromDisk(pos))\r
-                return false;\r
-        }\r
-    }\r
-    return true;\r
-}\r
-\r
-bool CTxDB::ReadDiskTx(uint256 hash, CTransaction& tx, CTxIndex& txindex)\r
-{\r
-    assert(!fClient);\r
-    tx.SetNull();\r
-    if (!ReadTxIndex(hash, txindex))\r
-        return false;\r
-    return (tx.ReadFromDisk(txindex.pos));\r
-}\r
-\r
-bool CTxDB::ReadDiskTx(uint256 hash, CTransaction& tx)\r
-{\r
-    CTxIndex txindex;\r
-    return ReadDiskTx(hash, tx, txindex);\r
-}\r
-\r
-bool CTxDB::ReadDiskTx(COutPoint outpoint, CTransaction& tx, CTxIndex& txindex)\r
-{\r
-    return ReadDiskTx(outpoint.hash, tx, txindex);\r
-}\r
-\r
-bool CTxDB::ReadDiskTx(COutPoint outpoint, CTransaction& tx)\r
-{\r
-    CTxIndex txindex;\r
-    return ReadDiskTx(outpoint.hash, tx, txindex);\r
-}\r
-\r
-bool CTxDB::WriteBlockIndex(const CDiskBlockIndex& blockindex)\r
-{\r
-    return Write(make_pair(string("blockindex"), blockindex.GetBlockHash()), blockindex);\r
-}\r
-\r
-bool CTxDB::EraseBlockIndex(uint256 hash)\r
-{\r
-    return Erase(make_pair(string("blockindex"), hash));\r
-}\r
-\r
-bool CTxDB::ReadHashBestChain(uint256& hashBestChain)\r
-{\r
-    return Read(string("hashBestChain"), hashBestChain);\r
-}\r
-\r
-bool CTxDB::WriteHashBestChain(uint256 hashBestChain)\r
-{\r
-    return Write(string("hashBestChain"), hashBestChain);\r
-}\r
-\r
-CBlockIndex* InsertBlockIndex(uint256 hash)\r
-{\r
-    if (hash == 0)\r
-        return NULL;\r
-\r
-    // Return existing\r
-    map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hash);\r
-    if (mi != mapBlockIndex.end())\r
-        return (*mi).second;\r
-\r
-    // Create new\r
-    CBlockIndex* pindexNew = new CBlockIndex();\r
-    if (!pindexNew)\r
-        throw runtime_error("LoadBlockIndex() : new CBlockIndex failed");\r
-    mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first;\r
-    pindexNew->phashBlock = &((*mi).first);\r
-\r
-    return pindexNew;\r
-}\r
-\r
-bool CTxDB::LoadBlockIndex()\r
-{\r
-    // Get cursor\r
-    Dbc* pcursor = GetCursor();\r
-    if (!pcursor)\r
-        return false;\r
-\r
-    unsigned int fFlags = DB_SET_RANGE;\r
-    loop\r
-    {\r
-        // Read next record\r
-        CDataStream ssKey;\r
-        if (fFlags == DB_SET_RANGE)\r
-            ssKey << make_pair(string("blockindex"), uint256(0));\r
-        CDataStream ssValue;\r
-        int ret = ReadAtCursor(pcursor, ssKey, ssValue, fFlags);\r
-        fFlags = DB_NEXT;\r
-        if (ret == DB_NOTFOUND)\r
-            break;\r
-        else if (ret != 0)\r
-            return false;\r
-\r
-        // Unserialize\r
-        string strType;\r
-        ssKey >> strType;\r
-        if (strType == "blockindex")\r
-        {\r
-            CDiskBlockIndex diskindex;\r
-            ssValue >> diskindex;\r
-\r
-            // Construct block index object\r
-            CBlockIndex* pindexNew = InsertBlockIndex(diskindex.GetBlockHash());\r
-            pindexNew->pprev          = InsertBlockIndex(diskindex.hashPrev);\r
-            pindexNew->pnext          = InsertBlockIndex(diskindex.hashNext);\r
-            pindexNew->nFile          = diskindex.nFile;\r
-            pindexNew->nBlockPos      = diskindex.nBlockPos;\r
-            pindexNew->nHeight        = diskindex.nHeight;\r
-            pindexNew->nVersion       = diskindex.nVersion;\r
-            pindexNew->hashMerkleRoot = diskindex.hashMerkleRoot;\r
-            pindexNew->nTime          = diskindex.nTime;\r
-            pindexNew->nBits          = diskindex.nBits;\r
-            pindexNew->nNonce         = diskindex.nNonce;\r
-\r
-            // Watch for genesis block and best block\r
-            if (pindexGenesisBlock == NULL && diskindex.GetBlockHash() == hashGenesisBlock)\r
-                pindexGenesisBlock = pindexNew;\r
-        }\r
-        else\r
-        {\r
-            break;\r
-        }\r
-    }\r
-\r
-    if (!ReadHashBestChain(hashBestChain))\r
-    {\r
-        if (pindexGenesisBlock == NULL)\r
-            return true;\r
-        return error("CTxDB::LoadBlockIndex() : hashBestChain not found");\r
-    }\r
-\r
-    if (!mapBlockIndex.count(hashBestChain))\r
-        return error("CTxDB::LoadBlockIndex() : blockindex for hashBestChain not found");\r
-    pindexBest = mapBlockIndex[hashBestChain];\r
-    nBestHeight = pindexBest->nHeight;\r
-    printf("LoadBlockIndex(): hashBestChain=%s  height=%d\n", hashBestChain.ToString().substr(0,14).c_str(), nBestHeight);\r
-\r
-    return true;\r
-}\r
-\r
-\r
-\r
-\r
-\r
-//\r
-// CAddrDB\r
-//\r
-\r
-bool CAddrDB::WriteAddress(const CAddress& addr)\r
-{\r
-    return Write(make_pair(string("addr"), addr.GetKey()), addr);\r
-}\r
-\r
-bool CAddrDB::LoadAddresses()\r
-{\r
-    CRITICAL_BLOCK(cs_mapIRCAddresses)\r
-    CRITICAL_BLOCK(cs_mapAddresses)\r
-    {\r
-        // Load user provided addresses\r
-        CAutoFile filein = fopen("addr.txt", "rt");\r
-        if (filein)\r
-        {\r
-            try\r
-            {\r
-                char psz[1000];\r
-                while (fgets(psz, sizeof(psz), filein))\r
-                {\r
-                    CAddress addr(psz, NODE_NETWORK);\r
-                    if (addr.ip != 0)\r
-                    {\r
-                        AddAddress(*this, addr);\r
-                        mapIRCAddresses.insert(make_pair(addr.GetKey(), addr));\r
-                    }\r
-                }\r
-            }\r
-            catch (...) { }\r
-        }\r
-\r
-        // Get cursor\r
-        Dbc* pcursor = GetCursor();\r
-        if (!pcursor)\r
-            return false;\r
-\r
-        loop\r
-        {\r
-            // Read next record\r
-            CDataStream ssKey;\r
-            CDataStream ssValue;\r
-            int ret = ReadAtCursor(pcursor, ssKey, ssValue);\r
-            if (ret == DB_NOTFOUND)\r
-                break;\r
-            else if (ret != 0)\r
-                return false;\r
-\r
-            // Unserialize\r
-            string strType;\r
-            ssKey >> strType;\r
-            if (strType == "addr")\r
-            {\r
-                CAddress addr;\r
-                ssValue >> addr;\r
-                mapAddresses.insert(make_pair(addr.GetKey(), addr));\r
-            }\r
-        }\r
-\r
-        printf("Loaded %d addresses\n", mapAddresses.size());\r
-\r
-        // Fix for possible bug that manifests in mapAddresses.count in irc.cpp,\r
-        // just need to call count here and it doesn't happen there.  The bug was the\r
-        // pack pragma in irc.cpp and has been fixed, but I'm not in a hurry to delete this.\r
-        mapAddresses.count(vector<unsigned char>(18));\r
-    }\r
-\r
-    return true;\r
-}\r
-\r
-bool LoadAddresses()\r
-{\r
-    return CAddrDB("cr+").LoadAddresses();\r
-}\r
-\r
-\r
-\r
-\r
-//\r
-// CReviewDB\r
-//\r
-\r
-bool CReviewDB::ReadReviews(uint256 hash, vector<CReview>& vReviews)\r
-{\r
-    vReviews.size(); // msvc workaround, just need to do anything with vReviews\r
-    return Read(make_pair(string("reviews"), hash), vReviews);\r
-}\r
-\r
-bool CReviewDB::WriteReviews(uint256 hash, const vector<CReview>& vReviews)\r
-{\r
-    return Write(make_pair(string("reviews"), hash), vReviews);\r
-}\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-//\r
-// CWalletDB\r
-//\r
-\r
-bool CWalletDB::LoadWallet(vector<unsigned char>& vchDefaultKeyRet)\r
-{\r
-    vchDefaultKeyRet.clear();\r
-\r
-    //// todo: shouldn't we catch exceptions and try to recover and continue?\r
-    CRITICAL_BLOCK(cs_mapKeys)\r
-    CRITICAL_BLOCK(cs_mapWallet)\r
-    {\r
-        // Get cursor\r
-        Dbc* pcursor = GetCursor();\r
-        if (!pcursor)\r
-            return false;\r
-\r
-        loop\r
-        {\r
-            // Read next record\r
-            CDataStream ssKey;\r
-            CDataStream ssValue;\r
-            int ret = ReadAtCursor(pcursor, ssKey, ssValue);\r
-            if (ret == DB_NOTFOUND)\r
-                break;\r
-            else if (ret != 0)\r
-                return false;\r
-\r
-            // Unserialize\r
-            // Taking advantage of the fact that pair serialization\r
-            // is just the two items serialized one after the other\r
-            string strType;\r
-            ssKey >> strType;\r
-            if (strType == "name")\r
-            {\r
-                string strAddress;\r
-                ssKey >> strAddress;\r
-                ssValue >> mapAddressBook[strAddress];\r
-            }\r
-            else if (strType == "tx")\r
-            {\r
-                uint256 hash;\r
-                ssKey >> hash;\r
-                CWalletTx& wtx = mapWallet[hash];\r
-                ssValue >> wtx;\r
-\r
-                if (wtx.GetHash() != hash)\r
-                    printf("Error in wallet.dat, hash mismatch\n");\r
-\r
-                //// debug print\r
-                //printf("LoadWallet  %s\n", wtx.GetHash().ToString().c_str());\r
-                //printf(" %12I64d  %s  %s  %s\n",\r
-                //    wtx.vout[0].nValue,\r
-                //    DateTimeStr(wtx.nTime).c_str(),\r
-                //    wtx.hashBlock.ToString().substr(0,14).c_str(),\r
-                //    wtx.mapValue["message"].c_str());\r
-            }\r
-            else if (strType == "key")\r
-            {\r
-                vector<unsigned char> vchPubKey;\r
-                ssKey >> vchPubKey;\r
-                CPrivKey vchPrivKey;\r
-                ssValue >> vchPrivKey;\r
-\r
-                mapKeys[vchPubKey] = vchPrivKey;\r
-                mapPubKeys[Hash160(vchPubKey)] = vchPubKey;\r
-            }\r
-            else if (strType == "defaultkey")\r
-            {\r
-                ssValue >> vchDefaultKeyRet;\r
-            }\r
-            else if (strType == "setting")\r
-            {\r
-                string strKey;\r
-                ssKey >> strKey;\r
-\r
-                // Menu state\r
-                if (strKey == "fShowGenerated")     ssValue >> fShowGenerated;\r
-                if (strKey == "fGenerateBitcoins")  ssValue >> fGenerateBitcoins;\r
-\r
-                // Options\r
-                if (strKey == "nTransactionFee")    ssValue >> nTransactionFee;\r
-                if (strKey == "addrIncoming")       ssValue >> addrIncoming;\r
-                if (strKey == "fLimitProcessors")   ssValue >> fLimitProcessors;\r
-                if (strKey == "nLimitProcessors")   ssValue >> nLimitProcessors;\r
-                if (strKey == "fMinimizeToTray")    ssValue >> fMinimizeToTray;\r
-                if (strKey == "fMinimizeOnClose")   ssValue >> fMinimizeOnClose;\r
-                if (strKey == "fUseProxy")          ssValue >> fUseProxy;\r
-                if (strKey == "addrProxy")          ssValue >> addrProxy;\r
-\r
-            }\r
-        }\r
-    }\r
-\r
-    printf("fShowGenerated = %d\n", fShowGenerated);\r
-    printf("fGenerateBitcoins = %d\n", fGenerateBitcoins);\r
-    printf("nTransactionFee = %"PRI64d"\n", nTransactionFee);\r
-    printf("addrIncoming = %s\n", addrIncoming.ToString().c_str());\r
-    printf("fMinimizeToTray = %d\n", fMinimizeToTray);\r
-    printf("fMinimizeOnClose = %d\n", fMinimizeOnClose);\r
-    printf("fUseProxy = %d\n", fUseProxy);\r
-    printf("addrProxy = %s\n", addrProxy.ToString().c_str());\r
-\r
-\r
-    // The transaction fee setting won't be needed for many years to come.\r
-    // Setting it to zero here in case they set it to something in an earlier version.\r
-    if (nTransactionFee != 0)\r
-    {\r
-        nTransactionFee = 0;\r
-        WriteSetting("nTransactionFee", nTransactionFee);\r
-    }\r
-\r
-    return true;\r
-}\r
-\r
-bool LoadWallet(bool& fFirstRunRet)\r
-{\r
-    fFirstRunRet = false;\r
-    vector<unsigned char> vchDefaultKey;\r
-    if (!CWalletDB("cr").LoadWallet(vchDefaultKey))\r
-        return false;\r
-    fFirstRunRet = vchDefaultKey.empty();\r
-\r
-    if (mapKeys.count(vchDefaultKey))\r
-    {\r
-        // Set keyUser\r
-        keyUser.SetPubKey(vchDefaultKey);\r
-        keyUser.SetPrivKey(mapKeys[vchDefaultKey]);\r
-    }\r
-    else\r
-    {\r
-        // Create new keyUser and set as default key\r
-        RandAddSeedPerfmon();\r
-        keyUser.MakeNewKey();\r
-        if (!AddKey(keyUser))\r
-            return false;\r
-        if (!SetAddressBookName(PubKeyToAddress(keyUser.GetPubKey()), "Your Address"))\r
-            return false;\r
-        CWalletDB().WriteDefaultKey(keyUser.GetPubKey());\r
-    }\r
-\r
-    _beginthread(ThreadFlushWalletDB, 0, NULL);\r
-    return true;\r
-}\r
-\r
-void ThreadFlushWalletDB(void* parg)\r
-{\r
-    static bool fOneThread;\r
-    if (fOneThread)\r
-        return;\r
-    fOneThread = true;\r
-\r
-    unsigned int nLastSeen = nWalletDBUpdated;\r
-    unsigned int nLastFlushed = nWalletDBUpdated;\r
-    int64 nLastWalletUpdate = GetTime();\r
-    while (!fShutdown)\r
-    {\r
-        Sleep(500);\r
-\r
-        if (nLastSeen != nWalletDBUpdated)\r
-        {\r
-            nLastSeen = nWalletDBUpdated;\r
-            nLastWalletUpdate = GetTime();\r
-        }\r
-\r
-        if (nLastFlushed != nWalletDBUpdated && nLastWalletUpdate < GetTime() - 1)\r
-        {\r
-            TRY_CRITICAL_BLOCK(cs_db)\r
-            {\r
-                string strFile = "wallet.dat";\r
-                map<string, int>::iterator mi = mapFileUseCount.find(strFile);\r
-                if (mi != mapFileUseCount.end())\r
-                {\r
-                    int nRefCount = (*mi).second;\r
-                    if (nRefCount == 0 && !fShutdown)\r
-                    {\r
-                        // Flush wallet.dat so it's self contained\r
-                        nLastFlushed == nWalletDBUpdated;\r
-                        int64 nStart = PerformanceCounter();\r
-                        dbenv.txn_checkpoint(0, 0, 0);\r
-                        dbenv.lsn_reset(strFile.c_str(), 0);\r
-                        printf("Flushed wallet.dat %15"PRI64d"\n", PerformanceCounter() - nStart);\r
-                        mapFileUseCount.erase(mi++);\r
-                    }\r
-                }\r
-            }\r
-        }\r
-    }\r
-}\r
+// Copyright (c) 2009-2010 Satoshi Nakamoto
+// Distributed under the MIT/X11 software license, see the accompanying
+// file license.txt or http://www.opensource.org/licenses/mit-license.php.
+
+#include "headers.h"
+
+void ThreadFlushWalletDB(void* parg);
+
+
+unsigned int nWalletDBUpdated;
+uint64 nAccountingEntryNumber = 0;
+
+
+
+//
+// CDB
+//
+
+static CCriticalSection cs_db;
+static bool fDbEnvInit = false;
+DbEnv dbenv(0);
+static map<string, int> mapFileUseCount;
+static map<string, Db*> mapDb;
+
+class CDBInit
+{
+public:
+    CDBInit()
+    {
+    }
+    ~CDBInit()
+    {
+        if (fDbEnvInit)
+        {
+            dbenv.close(0);
+            fDbEnvInit = false;
+        }
+    }
+}
+instance_of_cdbinit;
+
+
+CDB::CDB(const char* pszFile, const char* pszMode) : pdb(NULL)
+{
+    int ret;
+    if (pszFile == NULL)
+        return;
+
+    fReadOnly = (!strchr(pszMode, '+') && !strchr(pszMode, 'w'));
+    bool fCreate = strchr(pszMode, 'c');
+    unsigned int nFlags = DB_THREAD;
+    if (fCreate)
+        nFlags |= DB_CREATE;
+
+    CRITICAL_BLOCK(cs_db)
+    {
+        if (!fDbEnvInit)
+        {
+            if (fShutdown)
+                return;
+            string strDataDir = GetDataDir();
+            string strLogDir = strDataDir + "/database";
+            filesystem::create_directory(strLogDir.c_str());
+            string strErrorFile = strDataDir + "/db.log";
+            printf("dbenv.open strLogDir=%s strErrorFile=%s\n", strLogDir.c_str(), strErrorFile.c_str());
+
+            dbenv.set_lg_dir(strLogDir.c_str());
+            dbenv.set_lg_max(10000000);
+            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);
+            ret = dbenv.open(strDataDir.c_str(),
+                             DB_CREATE     |
+                             DB_INIT_LOCK  |
+                             DB_INIT_LOG   |
+                             DB_INIT_MPOOL |
+                             DB_INIT_TXN   |
+                             DB_THREAD     |
+                             DB_RECOVER,
+                             S_IRUSR | S_IWUSR);
+            if (ret > 0)
+                throw runtime_error(strprintf("CDB() : error %d opening database environment", ret));
+            fDbEnvInit = true;
+        }
+
+        strFile = pszFile;
+        ++mapFileUseCount[strFile];
+        pdb = mapDb[strFile];
+        if (pdb == NULL)
+        {
+            pdb = new Db(&dbenv, 0);
+
+            ret = pdb->open(NULL,      // Txn pointer
+                            pszFile,   // Filename
+                            "main",    // Logical db name
+                            DB_BTREE,  // Database type
+                            nFlags,    // Flags
+                            0);
+
+            if (ret > 0)
+            {
+                delete pdb;
+                pdb = NULL;
+                CRITICAL_BLOCK(cs_db)
+                    --mapFileUseCount[strFile];
+                strFile = "";
+                throw runtime_error(strprintf("CDB() : can't open database file %s, error %d", pszFile, ret));
+            }
+
+            if (fCreate && !Exists(string("version")))
+            {
+                bool fTmp = fReadOnly;
+                fReadOnly = false;
+                WriteVersion(VERSION);
+                fReadOnly = fTmp;
+            }
+
+            mapDb[strFile] = pdb;
+        }
+    }
+}
+
+void CDB::Close()
+{
+    if (!pdb)
+        return;
+    if (!vTxn.empty())
+        vTxn.front()->abort();
+    vTxn.clear();
+    pdb = NULL;
+
+    // Flush database activity from memory pool to disk log
+    unsigned int nMinutes = 0;
+    if (fReadOnly)
+        nMinutes = 1;
+    if (strFile == "addr.dat")
+        nMinutes = 2;
+    if (strFile == "blkindex.dat" && IsInitialBlockDownload() && nBestHeight % 500 != 0)
+        nMinutes = 1;
+    dbenv.txn_checkpoint(0, nMinutes, 0);
+
+    CRITICAL_BLOCK(cs_db)
+        --mapFileUseCount[strFile];
+}
+
+void CloseDb(const string& strFile)
+{
+    CRITICAL_BLOCK(cs_db)
+    {
+        if (mapDb[strFile] != NULL)
+        {
+            // Close the database handle
+            Db* pdb = mapDb[strFile];
+            pdb->close(0);
+            delete pdb;
+            mapDb[strFile] = NULL;
+        }
+    }
+}
+
+void DBFlush(bool fShutdown)
+{
+    // Flush log data to the actual data file
+    //  on all files that are not in use
+    printf("DBFlush(%s)%s\n", fShutdown ? "true" : "false", fDbEnvInit ? "" : " db not started");
+    if (!fDbEnvInit)
+        return;
+    CRITICAL_BLOCK(cs_db)
+    {
+        map<string, int>::iterator mi = mapFileUseCount.begin();
+        while (mi != mapFileUseCount.end())
+        {
+            string strFile = (*mi).first;
+            int nRefCount = (*mi).second;
+            printf("%s refcount=%d\n", strFile.c_str(), nRefCount);
+            if (nRefCount == 0)
+            {
+                // Move log data to the dat file
+                CloseDb(strFile);
+                dbenv.txn_checkpoint(0, 0, 0);
+                printf("%s flush\n", strFile.c_str());
+                dbenv.lsn_reset(strFile.c_str(), 0);
+                mapFileUseCount.erase(mi++);
+            }
+            else
+                mi++;
+        }
+        if (fShutdown)
+        {
+            char** listp;
+            if (mapFileUseCount.empty())
+                dbenv.log_archive(&listp, DB_ARCH_REMOVE);
+            dbenv.close(0);
+            fDbEnvInit = false;
+        }
+    }
+}
+
+
+
+
+
+
+//
+// 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::ReadOwnerTxes(uint160 hash160, int nMinHeight, vector<CTransaction>& vtx)
+{
+    assert(!fClient);
+    vtx.clear();
+
+    // Get cursor
+    Dbc* pcursor = GetCursor();
+    if (!pcursor)
+        return false;
+
+    unsigned int fFlags = DB_SET_RANGE;
+    loop
+    {
+        // Read next record
+        CDataStream ssKey;
+        if (fFlags == DB_SET_RANGE)
+            ssKey << string("owner") << hash160 << CDiskTxPos(0, 0, 0);
+        CDataStream ssValue;
+        int ret = ReadAtCursor(pcursor, ssKey, ssValue, fFlags);
+        fFlags = DB_NEXT;
+        if (ret == DB_NOTFOUND)
+            break;
+        else if (ret != 0)
+        {
+            pcursor->close();
+            return false;
+        }
+
+        // Unserialize
+        string strType;
+        uint160 hashItem;
+        CDiskTxPos pos;
+        ssKey >> strType >> hashItem >> pos;
+        int nItemHeight;
+        ssValue >> nItemHeight;
+
+        // Read transaction
+        if (strType != "owner" || hashItem != hash160)
+            break;
+        if (nItemHeight >= nMinHeight)
+        {
+            vtx.resize(vtx.size()+1);
+            if (!vtx.back().ReadFromDisk(pos))
+            {
+                pcursor->close();
+                return false;
+            }
+        }
+    }
+
+    pcursor->close();
+    return true;
+}
+
+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::EraseBlockIndex(uint256 hash)
+{
+    return Erase(make_pair(string("blockindex"), hash));
+}
+
+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* 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()
+{
+    // Get database cursor
+    Dbc* pcursor = GetCursor();
+    if (!pcursor)
+        return false;
+
+    // Load mapBlockIndex
+    unsigned int fFlags = DB_SET_RANGE;
+    loop
+    {
+        // Read next record
+        CDataStream ssKey;
+        if (fFlags == DB_SET_RANGE)
+            ssKey << make_pair(string("blockindex"), uint256(0));
+        CDataStream ssValue;
+        int ret = ReadAtCursor(pcursor, ssKey, ssValue, fFlags);
+        fFlags = DB_NEXT;
+        if (ret == DB_NOTFOUND)
+            break;
+        else if (ret != 0)
+            return false;
+
+        // Unserialize
+        string strType;
+        ssKey >> strType;
+        if (strType == "blockindex")
+        {
+            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;
+        }
+    }
+    pcursor->close();
+
+    // Calculate bnChainWork
+    vector<pair<int, CBlockIndex*> > vSortedByHeight;
+    vSortedByHeight.reserve(mapBlockIndex.size());
+    foreach(const PAIRTYPE(uint256, CBlockIndex*)& item, mapBlockIndex)
+    {
+        CBlockIndex* pindex = item.second;
+        vSortedByHeight.push_back(make_pair(pindex->nHeight, pindex));
+    }
+    sort(vSortedByHeight.begin(), vSortedByHeight.end());
+    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\n", hashBestChain.ToString().substr(0,20).c_str(), nBestHeight);
+
+    // Load bnBestInvalidWork, OK if it doesn't exist
+    ReadBestInvalidWork(bnBestInvalidWork);
+
+    // Verify blocks in the best chain
+    CBlockIndex* pindexFork = NULL;
+    for (CBlockIndex* pindex = pindexBest; pindex && pindex->pprev; pindex = pindex->pprev)
+    {
+        if (pindex->nHeight < nBestHeight-2500 && !mapArgs.count("-checkblocks"))
+            break;
+        CBlock block;
+        if (!block.ReadFromDisk(pindex))
+            return error("LoadBlockIndex() : block.ReadFromDisk failed");
+        if (!block.CheckBlock())
+        {
+            printf("LoadBlockIndex() : *** found bad block at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString().c_str());
+            pindexFork = pindex->pprev;
+        }
+    }
+    if (pindexFork)
+    {
+        // 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;
+}
+
+
+
+
+
+//
+// CAddrDB
+//
+
+bool CAddrDB::WriteAddress(const CAddress& addr)
+{
+    return Write(make_pair(string("addr"), addr.GetKey()), addr);
+}
+
+bool CAddrDB::EraseAddress(const CAddress& addr)
+{
+    return Erase(make_pair(string("addr"), addr.GetKey()));
+}
+
+bool CAddrDB::LoadAddresses()
+{
+    CRITICAL_BLOCK(cs_mapAddresses)
+    {
+        // Load user provided addresses
+        CAutoFile filein = fopen((GetDataDir() + "/addr.txt").c_str(), "rt");
+        if (filein)
+        {
+            try
+            {
+                char psz[1000];
+                while (fgets(psz, sizeof(psz), filein))
+                {
+                    CAddress addr(psz, NODE_NETWORK);
+                    addr.nTime = 0; // so it won't relay unless successfully connected
+                    if (addr.IsValid())
+                        AddAddress(addr);
+                }
+            }
+            catch (...) { }
+        }
+
+        // Get cursor
+        Dbc* pcursor = GetCursor();
+        if (!pcursor)
+            return false;
+
+        loop
+        {
+            // Read next record
+            CDataStream ssKey;
+            CDataStream ssValue;
+            int ret = ReadAtCursor(pcursor, ssKey, ssValue);
+            if (ret == DB_NOTFOUND)
+                break;
+            else if (ret != 0)
+                return false;
+
+            // Unserialize
+            string strType;
+            ssKey >> strType;
+            if (strType == "addr")
+            {
+                CAddress addr;
+                ssValue >> addr;
+                mapAddresses.insert(make_pair(addr.GetKey(), addr));
+            }
+        }
+        pcursor->close();
+
+        printf("Loaded %d addresses\n", mapAddresses.size());
+    }
+
+    return true;
+}
+
+bool LoadAddresses()
+{
+    return CAddrDB("cr+").LoadAddresses();
+}
+
+
+
+
+//
+// CWalletDB
+//
+
+static set<int64> setKeyPool;
+static CCriticalSection cs_setKeyPool;
+
+bool CWalletDB::ReadAccount(const string& strAccount, CAccount& account)
+{
+    account.SetNull();
+    return Read(make_pair(string("acc"), strAccount), account);
+}
+
+bool CWalletDB::WriteAccount(const string& strAccount, const CAccount& account)
+{
+    return Write(make_pair(string("acc"), strAccount), account);
+}
+
+bool CWalletDB::WriteAccountingEntry(const CAccountingEntry& acentry)
+{
+    return Write(make_tuple(string("acentry"), acentry.strAccount, ++nAccountingEntryNumber), acentry);
+}
+
+int64 CWalletDB::GetAccountCreditDebit(const string& strAccount)
+{
+    list<CAccountingEntry> entries;
+    ListAccountCreditDebit(strAccount, entries);
+
+    int64 nCreditDebit = 0;
+    foreach (const CAccountingEntry& entry, entries)
+        nCreditDebit += entry.nCreditDebit;
+
+    return nCreditDebit;
+}
+
+void CWalletDB::ListAccountCreditDebit(const string& strAccount, list<CAccountingEntry>& entries)
+{
+    int64 nCreditDebit = 0;
+
+    bool fAllAccounts = (strAccount == "*");
+
+    Dbc* pcursor = GetCursor();
+    if (!pcursor)
+        throw runtime_error("CWalletDB::ListAccountCreditDebit() : cannot create DB cursor");
+    unsigned int fFlags = DB_SET_RANGE;
+    loop
+    {
+        // Read next record
+        CDataStream ssKey;
+        if (fFlags == DB_SET_RANGE)
+            ssKey << make_tuple(string("acentry"), (fAllAccounts? string("") : strAccount), uint64(0));
+        CDataStream ssValue;
+        int ret = ReadAtCursor(pcursor, ssKey, ssValue, fFlags);
+        fFlags = DB_NEXT;
+        if (ret == DB_NOTFOUND)
+            break;
+        else if (ret != 0)
+        {
+            pcursor->close();
+            throw runtime_error("CWalletDB::ListAccountCreditDebit() : error scanning DB");
+        }
+
+        // Unserialize
+        string strType;
+        ssKey >> strType;
+        if (strType != "acentry")
+            break;
+        CAccountingEntry acentry;
+        ssKey >> acentry.strAccount;
+        if (!fAllAccounts && acentry.strAccount != strAccount)
+            break;
+
+        ssValue >> acentry;
+        entries.push_back(acentry);
+    }
+
+    pcursor->close();
+}
+
+
+bool CWalletDB::LoadWallet()
+{
+    vchDefaultKey.clear();
+    int nFileVersion = 0;
+    vector<uint256> vWalletUpgrade;
+
+    // Modify defaults
+#ifndef __WXMSW__
+    // Tray icon sometimes disappears on 9.10 karmic koala 64-bit, leaving no way to access the program
+    fMinimizeToTray = false;
+    fMinimizeOnClose = false;
+#endif
+
+    //// todo: shouldn't we catch exceptions and try to recover and continue?
+    CRITICAL_BLOCK(cs_mapWallet)
+    CRITICAL_BLOCK(cs_mapKeys)
+    {
+        // Get cursor
+        Dbc* pcursor = GetCursor();
+        if (!pcursor)
+            return false;
+
+        loop
+        {
+            // Read next record
+            CDataStream ssKey;
+            CDataStream ssValue;
+            int ret = ReadAtCursor(pcursor, ssKey, ssValue);
+            if (ret == DB_NOTFOUND)
+                break;
+            else if (ret != 0)
+                return false;
+
+            // Unserialize
+            // Taking advantage of the fact that pair serialization
+            // is just the two items serialized one after the other
+            string strType;
+            ssKey >> strType;
+            if (strType == "name")
+            {
+                string strAddress;
+                ssKey >> strAddress;
+                ssValue >> mapAddressBook[strAddress];
+            }
+            else if (strType == "tx")
+            {
+                uint256 hash;
+                ssKey >> hash;
+                CWalletTx& wtx = mapWallet[hash];
+                ssValue >> wtx;
+
+                if (wtx.GetHash() != hash)
+                    printf("Error in wallet.dat, hash mismatch\n");
+
+                // Undo serialize changes in 31600
+                if (31404 <= wtx.fTimeReceivedIsTxTime && wtx.fTimeReceivedIsTxTime <= 31703)
+                {
+                    if (!ssValue.empty())
+                    {
+                        char fTmp;
+                        char fUnused;
+                        ssValue >> fTmp >> fUnused >> wtx.strFromAccount;
+                        printf("LoadWallet() upgrading tx ver=%d %d '%s' %s\n", wtx.fTimeReceivedIsTxTime, fTmp, wtx.strFromAccount.c_str(), hash.ToString().c_str());
+                        wtx.fTimeReceivedIsTxTime = fTmp;
+                    }
+                    else
+                    {
+                        printf("LoadWallet() repairing tx ver=%d %s\n", wtx.fTimeReceivedIsTxTime, hash.ToString().c_str());
+                        wtx.fTimeReceivedIsTxTime = 0;
+                    }
+                    vWalletUpgrade.push_back(hash);
+                }
+
+                //// debug print
+                //printf("LoadWallet  %s\n", wtx.GetHash().ToString().c_str());
+                //printf(" %12I64d  %s  %s  %s\n",
+                //    wtx.vout[0].nValue,
+                //    DateTimeStrFormat("%x %H:%M:%S", wtx.GetBlockTime()).c_str(),
+                //    wtx.hashBlock.ToString().substr(0,20).c_str(),
+                //    wtx.mapValue["message"].c_str());
+            }
+            else if (strType == "acentry")
+            {
+                string strAccount;
+                ssKey >> strAccount;
+                uint64 nNumber;
+                ssKey >> nNumber;
+                if (nNumber > nAccountingEntryNumber)
+                    nAccountingEntryNumber = nNumber;
+            }
+            else if (strType == "key" || strType == "wkey")
+            {
+                vector<unsigned char> vchPubKey;
+                ssKey >> vchPubKey;
+                CWalletKey wkey;
+                if (strType == "key")
+                    ssValue >> wkey.vchPrivKey;
+                else
+                    ssValue >> wkey;
+
+                mapKeys[vchPubKey] = wkey.vchPrivKey;
+                mapPubKeys[Hash160(vchPubKey)] = vchPubKey;
+            }
+            else if (strType == "defaultkey")
+            {
+                ssValue >> vchDefaultKey;
+            }
+            else if (strType == "pool")
+            {
+                int64 nIndex;
+                ssKey >> nIndex;
+                setKeyPool.insert(nIndex);
+            }
+            else if (strType == "version")
+            {
+                ssValue >> nFileVersion;
+                if (nFileVersion == 10300)
+                    nFileVersion = 300;
+            }
+            else if (strType == "setting")
+            {
+                string strKey;
+                ssKey >> strKey;
+
+                // Menu state
+                if (strKey == "fGenerateBitcoins")  ssValue >> fGenerateBitcoins;
+
+                // Options
+                if (strKey == "nTransactionFee")    ssValue >> nTransactionFee;
+                if (strKey == "addrIncoming")       ssValue >> addrIncoming;
+                if (strKey == "fLimitProcessors")   ssValue >> fLimitProcessors;
+                if (strKey == "nLimitProcessors")   ssValue >> nLimitProcessors;
+                if (strKey == "fMinimizeToTray")    ssValue >> fMinimizeToTray;
+                if (strKey == "fMinimizeOnClose")   ssValue >> fMinimizeOnClose;
+                if (strKey == "fUseProxy")          ssValue >> fUseProxy;
+                if (strKey == "addrProxy")          ssValue >> addrProxy;
+                if (fHaveUPnP && strKey == "fUseUPnP")           ssValue >> fUseUPnP;
+            }
+        }
+        pcursor->close();
+    }
+
+    foreach(uint256 hash, vWalletUpgrade)
+        WriteTx(hash, mapWallet[hash]);
+
+    printf("nFileVersion = %d\n", nFileVersion);
+    printf("fGenerateBitcoins = %d\n", fGenerateBitcoins);
+    printf("nTransactionFee = %"PRI64d"\n", nTransactionFee);
+    printf("addrIncoming = %s\n", addrIncoming.ToString().c_str());
+    printf("fMinimizeToTray = %d\n", fMinimizeToTray);
+    printf("fMinimizeOnClose = %d\n", fMinimizeOnClose);
+    printf("fUseProxy = %d\n", fUseProxy);
+    printf("addrProxy = %s\n", addrProxy.ToString().c_str());
+    if (fHaveUPnP)
+        printf("fUseUPnP = %d\n", fUseUPnP);
+
+
+    // Upgrade
+    if (nFileVersion < VERSION)
+    {
+        // Get rid of old debug.log file in current directory
+        if (nFileVersion <= 105 && !pszSetDataDir[0])
+            unlink("debug.log");
+
+        WriteVersion(VERSION);
+    }
+
+
+    return true;
+}
+
+bool LoadWallet(bool& fFirstRunRet)
+{
+    fFirstRunRet = false;
+    if (!CWalletDB("cr+").LoadWallet())
+        return false;
+    fFirstRunRet = vchDefaultKey.empty();
+
+    if (mapKeys.count(vchDefaultKey))
+    {
+        // Set keyUser
+        keyUser.SetPubKey(vchDefaultKey);
+        keyUser.SetPrivKey(mapKeys[vchDefaultKey]);
+    }
+    else
+    {
+        // Create new keyUser and set as default key
+        RandAddSeedPerfmon();
+        keyUser.MakeNewKey();
+        if (!AddKey(keyUser))
+            return false;
+        if (!SetAddressBookName(PubKeyToAddress(keyUser.GetPubKey()), ""))
+            return false;
+        CWalletDB().WriteDefaultKey(keyUser.GetPubKey());
+    }
+
+    CreateThread(ThreadFlushWalletDB, NULL);
+    return true;
+}
+
+void ThreadFlushWalletDB(void* parg)
+{
+    static bool fOneThread;
+    if (fOneThread)
+        return;
+    fOneThread = true;
+    if (mapArgs.count("-noflushwallet"))
+        return;
+
+    unsigned int nLastSeen = nWalletDBUpdated;
+    unsigned int nLastFlushed = nWalletDBUpdated;
+    int64 nLastWalletUpdate = GetTime();
+    while (!fShutdown)
+    {
+        Sleep(500);
+
+        if (nLastSeen != nWalletDBUpdated)
+        {
+            nLastSeen = nWalletDBUpdated;
+            nLastWalletUpdate = GetTime();
+        }
+
+        if (nLastFlushed != nWalletDBUpdated && GetTime() - nLastWalletUpdate >= 2)
+        {
+            TRY_CRITICAL_BLOCK(cs_db)
+            {
+                // Don't do this if any databases are in use
+                int nRefCount = 0;
+                map<string, int>::iterator mi = mapFileUseCount.begin();
+                while (mi != mapFileUseCount.end())
+                {
+                    nRefCount += (*mi).second;
+                    mi++;
+                }
+
+                if (nRefCount == 0 && !fShutdown)
+                {
+                    string strFile = "wallet.dat";
+                    map<string, int>::iterator mi = mapFileUseCount.find(strFile);
+                    if (mi != mapFileUseCount.end())
+                    {
+                        printf("%s ", DateTimeStrFormat("%x %H:%M:%S", GetTime()).c_str());
+                        printf("Flushing wallet.dat\n");
+                        nLastFlushed = nWalletDBUpdated;
+                        int64 nStart = GetTimeMillis();
+
+                        // Flush wallet.dat so it's self contained
+                        CloseDb(strFile);
+                        dbenv.txn_checkpoint(0, 0, 0);
+                        dbenv.lsn_reset(strFile.c_str(), 0);
+
+                        mapFileUseCount.erase(mi++);
+                        printf("Flushed wallet.dat %"PRI64d"ms\n", GetTimeMillis() - nStart);
+                    }
+                }
+            }
+        }
+    }
+}
+
+void BackupWallet(const string& strDest)
+{
+    while (!fShutdown)
+    {
+        CRITICAL_BLOCK(cs_db)
+        {
+            const string strFile = "wallet.dat";
+            if (!mapFileUseCount.count(strFile) || mapFileUseCount[strFile] == 0)
+            {
+                // Flush log data to the dat file
+                CloseDb(strFile);
+                dbenv.txn_checkpoint(0, 0, 0);
+                dbenv.lsn_reset(strFile.c_str(), 0);
+                mapFileUseCount.erase(strFile);
+
+                // Copy wallet.dat
+                filesystem::path pathSrc(GetDataDir() + "/" + strFile);
+                filesystem::path pathDest(strDest);
+                if (filesystem::is_directory(pathDest))
+                    pathDest = pathDest / strFile;
+#if BOOST_VERSION >= 104000
+                filesystem::copy_file(pathSrc, pathDest, filesystem::copy_option::overwrite_if_exists);
+#else
+                filesystem::copy_file(pathSrc, pathDest);
+#endif
+                printf("copied wallet.dat to %s\n", pathDest.string().c_str());
+
+                return;
+            }
+        }
+        Sleep(100);
+    }
+}
+
+
+void CWalletDB::ReserveKeyFromKeyPool(int64& nIndex, CKeyPool& keypool)
+{
+    nIndex = -1;
+    keypool.vchPubKey.clear();
+    CRITICAL_BLOCK(cs_main)
+    CRITICAL_BLOCK(cs_mapWallet)
+    CRITICAL_BLOCK(cs_setKeyPool)
+    {
+        // Top up key pool
+        int64 nTargetSize = max(GetArg("-keypool", 100), (int64)0);
+        while (setKeyPool.size() < nTargetSize+1)
+        {
+            int64 nEnd = 1;
+            if (!setKeyPool.empty())
+                nEnd = *(--setKeyPool.end()) + 1;
+            if (!Write(make_pair(string("pool"), nEnd), CKeyPool(GenerateNewKey())))
+                throw runtime_error("ReserveKeyFromKeyPool() : writing generated key failed");
+            setKeyPool.insert(nEnd);
+            printf("keypool added key %"PRI64d", size=%d\n", nEnd, setKeyPool.size());
+        }
+
+        // Get the oldest key
+        assert(!setKeyPool.empty());
+        nIndex = *(setKeyPool.begin());
+        setKeyPool.erase(setKeyPool.begin());
+        if (!Read(make_pair(string("pool"), nIndex), keypool))
+            throw runtime_error("ReserveKeyFromKeyPool() : read failed");
+        if (!mapKeys.count(keypool.vchPubKey))
+            throw runtime_error("ReserveKeyFromKeyPool() : unknown key in key pool");
+        assert(!keypool.vchPubKey.empty());
+        printf("keypool reserve %"PRI64d"\n", nIndex);
+    }
+}
+
+void CWalletDB::KeepKey(int64 nIndex)
+{
+    // Remove from key pool
+    CRITICAL_BLOCK(cs_main)
+    CRITICAL_BLOCK(cs_mapWallet)
+    {
+        Erase(make_pair(string("pool"), nIndex));
+    }
+    printf("keypool keep %"PRI64d"\n", nIndex);
+}
+
+void CWalletDB::ReturnKey(int64 nIndex)
+{
+    // Return to key pool
+    CRITICAL_BLOCK(cs_setKeyPool)
+        setKeyPool.insert(nIndex);
+    printf("keypool return %"PRI64d"\n", nIndex);
+}
+
+vector<unsigned char> GetKeyFromKeyPool()
+{
+    CWalletDB walletdb;
+    int64 nIndex = 0;
+    CKeyPool keypool;
+    walletdb.ReserveKeyFromKeyPool(nIndex, keypool);
+    walletdb.KeepKey(nIndex);
+    return keypool.vchPubKey;
+}
+
+int64 GetOldestKeyPoolTime()
+{
+    CWalletDB walletdb;
+    int64 nIndex = 0;
+    CKeyPool keypool;
+    walletdb.ReserveKeyFromKeyPool(nIndex, keypool);
+    walletdb.ReturnKey(nIndex);
+    return keypool.nTime;
+}