Better wording for transaction fee notification messages
[novacoin.git] / db.cpp
diff --git a/db.cpp b/db.cpp
index d83392e..28a8b71 100644 (file)
--- a/db.cpp
+++ b/db.cpp
@@ -8,7 +8,7 @@ void ThreadFlushWalletDB(void* parg);
 
 
 unsigned int nWalletDBUpdated;
-
+uint64 nAccountingEntryNumber = 0;
 
 
 
@@ -77,11 +77,10 @@ CDB::CDB(const char* pszFile, const char* pszMode) : pdb(NULL)
                              DB_INIT_MPOOL |
                              DB_INIT_TXN   |
                              DB_THREAD     |
-                             DB_PRIVATE    |
                              DB_RECOVER,
                              S_IRUSR | S_IWUSR);
             if (ret > 0)
-                throw runtime_error(strprintf("CDB() : error %d opening database environment\n", ret));
+                throw runtime_error(strprintf("CDB() : error %d opening database environment", ret));
             fDbEnvInit = true;
         }
 
@@ -106,7 +105,7 @@ CDB::CDB(const char* pszFile, const char* pszMode) : pdb(NULL)
                 CRITICAL_BLOCK(cs_db)
                     --mapFileUseCount[strFile];
                 strFile = "";
-                throw runtime_error(strprintf("CDB() : can't open database file %s, error %d\n", pszFile, ret));
+                throw runtime_error(strprintf("CDB() : can't open database file %s, error %d", pszFile, ret));
             }
 
             if (fCreate && !Exists(string("version")))
@@ -133,6 +132,8 @@ void CDB::Close()
 
     // 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)
@@ -419,6 +420,9 @@ bool CTxDB::LoadBlockIndex()
             // 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
         {
@@ -454,18 +458,17 @@ bool CTxDB::LoadBlockIndex()
     pindexBest = mapBlockIndex[hashBestChain];
     nBestHeight = pindexBest->nHeight;
     bnBestChainWork = pindexBest->bnChainWork;
-    printf("LoadBlockIndex(): hashBestChain=%s  height=%d\n", hashBestChain.ToString().substr(0,16).c_str(), nBestHeight);
+    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
-    vector<CBlockIndex*> vChain;
-    vector<CBlockIndex*> vBad;
     CBlockIndex* pindexFork = NULL;
     for (CBlockIndex* pindex = pindexBest; pindex && pindex->pprev; pindex = pindex->pprev)
     {
-        vChain.push_back(pindex);
+        if (pindex->nHeight < nBestHeight-2500 && !mapArgs.count("-checkblocks"))
+            break;
         CBlock block;
         if (!block.ReadFromDisk(pindex))
             return error("LoadBlockIndex() : block.ReadFromDisk failed");
@@ -473,25 +476,17 @@ bool CTxDB::LoadBlockIndex()
         {
             printf("LoadBlockIndex() : *** found bad block at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString().c_str());
             pindexFork = pindex->pprev;
-            vBad = vChain;
         }
     }
     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);
-
-        // Delete the bad chain
-        foreach(CBlockIndex* pindex, vBad)
-        {
-            txdb.EraseBlockIndex(pindex->GetBlockHash());
-            mapBlockIndex.erase(pindex->GetBlockHash());
-            delete pindex;
-        }
     }
 
     return true;
@@ -510,6 +505,11 @@ 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)
@@ -561,11 +561,6 @@ bool CAddrDB::LoadAddresses()
         pcursor->close();
 
         printf("Loaded %d addresses\n", mapAddresses.size());
-
-        // Fix for possible bug that manifests in mapAddresses.count in irc.cpp,
-        // just need to call count here and it doesn't happen there.  The bug was the
-        // pack pragma in irc.cpp and has been fixed, but I'm not in a hurry to delete this.
-        mapAddresses.count(vector<unsigned char>(18));
     }
 
     return true;
@@ -583,10 +578,87 @@ bool 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__
@@ -596,8 +668,8 @@ bool CWalletDB::LoadWallet()
 #endif
 
     //// todo: shouldn't we catch exceptions and try to recover and continue?
-    CRITICAL_BLOCK(cs_mapKeys)
     CRITICAL_BLOCK(cs_mapWallet)
+    CRITICAL_BLOCK(cs_mapKeys)
     {
         // Get cursor
         Dbc* pcursor = GetCursor();
@@ -636,14 +708,42 @@ bool CWalletDB::LoadWallet()
                 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.nTime).c_str(),
-                //    wtx.hashBlock.ToString().substr(0,16).c_str(),
+                //    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;
@@ -661,6 +761,12 @@ bool CWalletDB::LoadWallet()
             {
                 ssValue >> vchDefaultKey;
             }
+            else if (strType == "pool")
+            {
+                int64 nIndex;
+                ssKey >> nIndex;
+                setKeyPool.insert(nIndex);
+            }
             else if (strType == "version")
             {
                 ssValue >> nFileVersion;
@@ -684,12 +790,15 @@ bool CWalletDB::LoadWallet()
                 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);
@@ -698,16 +807,10 @@ bool CWalletDB::LoadWallet()
     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);
 
 
-    // The transaction fee setting won't be needed for many years to come.
-    // Setting it to zero here in case they set it to something in an earlier version.
-    if (nTransactionFee != 0)
-    {
-        nTransactionFee = 0;
-        WriteSetting("nTransactionFee", nTransactionFee);
-    }
-
     // Upgrade
     if (nFileVersion < VERSION)
     {
@@ -718,6 +821,7 @@ bool CWalletDB::LoadWallet()
         WriteVersion(VERSION);
     }
 
+
     return true;
 }
 
@@ -741,7 +845,7 @@ bool LoadWallet(bool& fFirstRunRet)
         keyUser.MakeNewKey();
         if (!AddKey(keyUser))
             return false;
-        if (!SetAddressBookName(PubKeyToAddress(keyUser.GetPubKey()), "Your Address"))
+        if (!SetAddressBookName(PubKeyToAddress(keyUser.GetPubKey()), ""))
             return false;
         CWalletDB().WriteDefaultKey(keyUser.GetPubKey());
     }
@@ -809,3 +913,111 @@ void ThreadFlushWalletDB(void* parg)
         }
     }
 }
+
+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;
+}