aboutsummaryrefslogtreecommitdiff
path: root/src/walletdb.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/walletdb.cpp')
-rw-r--r--src/walletdb.cpp428
1 files changed, 428 insertions, 0 deletions
diff --git a/src/walletdb.cpp b/src/walletdb.cpp
new file mode 100644
index 0000000000..dd8069a5b1
--- /dev/null
+++ b/src/walletdb.cpp
@@ -0,0 +1,428 @@
+// Copyright (c) 2009-2010 Satoshi Nakamoto
+// Copyright (c) 2009-2012 The Bitcoin developers
+// Distributed under the MIT/X11 software license, see the accompanying
+// file license.txt or http://www.opensource.org/licenses/mit-license.php.
+
+#include "walletdb.h"
+#include "wallet.h"
+#include <boost/filesystem.hpp>
+
+using namespace std;
+using namespace boost;
+
+
+static uint64 nAccountingEntryNumber = 0;
+
+extern CCriticalSection cs_db;
+extern map<string, int> mapFileUseCount;
+extern void CloseDb(const string& strFile);
+
+//
+// CWalletDB
+//
+
+bool CWalletDB::WriteName(const string& strAddress, const string& strName)
+{
+ nWalletDBUpdated++;
+ return Write(make_pair(string("name"), strAddress), strName);
+}
+
+bool CWalletDB::EraseName(const string& strAddress)
+{
+ // This should only be used for sending addresses, never for receiving addresses,
+ // receiving addresses must always have an address book entry if they're not change return.
+ nWalletDBUpdated++;
+ return Erase(make_pair(string("name"), strAddress));
+}
+
+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(boost::make_tuple(string("acentry"), acentry.strAccount, ++nAccountingEntryNumber), acentry);
+}
+
+int64 CWalletDB::GetAccountCreditDebit(const string& strAccount)
+{
+ list<CAccountingEntry> entries;
+ ListAccountCreditDebit(strAccount, entries);
+
+ int64 nCreditDebit = 0;
+ BOOST_FOREACH (const CAccountingEntry& entry, entries)
+ nCreditDebit += entry.nCreditDebit;
+
+ return nCreditDebit;
+}
+
+void CWalletDB::ListAccountCreditDebit(const string& strAccount, list<CAccountingEntry>& entries)
+{
+ 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 << boost::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();
+}
+
+
+int CWalletDB::LoadWallet(CWallet* pwallet)
+{
+ pwallet->vchDefaultKey.clear();
+ int nFileVersion = 0;
+ vector<uint256> vWalletUpgrade;
+ bool fIsEncrypted = false;
+
+ //// todo: shouldn't we catch exceptions and try to recover and continue?
+ {
+ LOCK(pwallet->cs_wallet);
+ int nMinVersion = 0;
+ if (Read((string)"minversion", nMinVersion))
+ {
+ if (nMinVersion > CLIENT_VERSION)
+ return DB_TOO_NEW;
+ pwallet->LoadMinVersion(nMinVersion);
+ }
+
+ // Get cursor
+ Dbc* pcursor = GetCursor();
+ if (!pcursor)
+ {
+ printf("Error getting wallet database cursor\n");
+ return DB_CORRUPT;
+ }
+
+ loop
+ {
+ // Read next record
+ CDataStream ssKey;
+ CDataStream ssValue;
+ int ret = ReadAtCursor(pcursor, ssKey, ssValue);
+ if (ret == DB_NOTFOUND)
+ break;
+ else if (ret != 0)
+ {
+ printf("Error reading next record from wallet database\n");
+ return DB_CORRUPT;
+ }
+
+ // 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 >> pwallet->mapAddressBook[strAddress];
+ }
+ else if (strType == "tx")
+ {
+ uint256 hash;
+ ssKey >> hash;
+ CWalletTx& wtx = pwallet->mapWallet[hash];
+ ssValue >> wtx;
+ wtx.BindWallet(pwallet);
+
+ 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;
+ CKey key;
+ if (strType == "key")
+ {
+ CPrivKey pkey;
+ ssValue >> pkey;
+ key.SetPubKey(vchPubKey);
+ key.SetPrivKey(pkey);
+ if (key.GetPubKey() != vchPubKey)
+ {
+ printf("Error reading wallet database: CPrivKey pubkey inconsistency\n");
+ return DB_CORRUPT;
+ }
+ if (!key.IsValid())
+ {
+ printf("Error reading wallet database: invalid CPrivKey\n");
+ return DB_CORRUPT;
+ }
+ }
+ else
+ {
+ CWalletKey wkey;
+ ssValue >> wkey;
+ key.SetPubKey(vchPubKey);
+ key.SetPrivKey(wkey.vchPrivKey);
+ if (key.GetPubKey() != vchPubKey)
+ {
+ printf("Error reading wallet database: CWalletKey pubkey inconsistency\n");
+ return DB_CORRUPT;
+ }
+ if (!key.IsValid())
+ {
+ printf("Error reading wallet database: invalid CWalletKey\n");
+ return DB_CORRUPT;
+ }
+ }
+ if (!pwallet->LoadKey(key))
+ {
+ printf("Error reading wallet database: LoadKey failed\n");
+ return DB_CORRUPT;
+ }
+ }
+ else if (strType == "mkey")
+ {
+ unsigned int nID;
+ ssKey >> nID;
+ CMasterKey kMasterKey;
+ ssValue >> kMasterKey;
+ if(pwallet->mapMasterKeys.count(nID) != 0)
+ {
+ printf("Error reading wallet database: duplicate CMasterKey id %u\n", nID);
+ return DB_CORRUPT;
+ }
+ pwallet->mapMasterKeys[nID] = kMasterKey;
+ if (pwallet->nMasterKeyMaxID < nID)
+ pwallet->nMasterKeyMaxID = nID;
+ }
+ else if (strType == "ckey")
+ {
+ vector<unsigned char> vchPubKey;
+ ssKey >> vchPubKey;
+ vector<unsigned char> vchPrivKey;
+ ssValue >> vchPrivKey;
+ if (!pwallet->LoadCryptedKey(vchPubKey, vchPrivKey))
+ {
+ printf("Error reading wallet database: LoadCryptedKey failed\n");
+ return DB_CORRUPT;
+ }
+ fIsEncrypted = true;
+ }
+ else if (strType == "defaultkey")
+ {
+ ssValue >> pwallet->vchDefaultKey;
+ }
+ else if (strType == "pool")
+ {
+ int64 nIndex;
+ ssKey >> nIndex;
+ pwallet->setKeyPool.insert(nIndex);
+ }
+ else if (strType == "version")
+ {
+ ssValue >> nFileVersion;
+ if (nFileVersion == 10300)
+ nFileVersion = 300;
+ }
+ else if (strType == "cscript")
+ {
+ uint160 hash;
+ ssKey >> hash;
+ CScript script;
+ ssValue >> script;
+ if (!pwallet->LoadCScript(script))
+ {
+ printf("Error reading wallet database: LoadCScript failed\n");
+ return DB_CORRUPT;
+ }
+ }
+ }
+ pcursor->close();
+ }
+
+ BOOST_FOREACH(uint256 hash, vWalletUpgrade)
+ WriteTx(hash, pwallet->mapWallet[hash]);
+
+ printf("nFileVersion = %d\n", nFileVersion);
+
+
+ // Rewrite encrypted wallets of versions 0.4.0 and 0.5.0rc:
+ if (fIsEncrypted && (nFileVersion == 40000 || nFileVersion == 50000))
+ return DB_NEED_REWRITE;
+
+ if (nFileVersion < CLIENT_VERSION) // Update
+ WriteVersion(CLIENT_VERSION);
+
+ return DB_LOAD_OK;
+}
+
+void ThreadFlushWalletDB(void* parg)
+{
+ const string& strFile = ((const string*)parg)[0];
+ static bool fOneThread;
+ if (fOneThread)
+ return;
+ fOneThread = true;
+ if (!GetBoolArg("-flushwallet", true))
+ 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_LOCK(cs_db,lockDb);
+ if (lockDb)
+ {
+ // 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)
+ {
+ 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);
+ }
+ }
+ }
+ }
+ }
+}
+
+bool BackupWallet(const CWallet& wallet, const string& strDest)
+{
+ if (!wallet.fFileBacked)
+ return false;
+ while (!fShutdown)
+ {
+ {
+ LOCK(cs_db);
+ if (!mapFileUseCount.count(wallet.strWalletFile) || mapFileUseCount[wallet.strWalletFile] == 0)
+ {
+ // Flush log data to the dat file
+ CloseDb(wallet.strWalletFile);
+ dbenv.txn_checkpoint(0, 0, 0);
+ dbenv.lsn_reset(wallet.strWalletFile.c_str(), 0);
+ mapFileUseCount.erase(wallet.strWalletFile);
+
+ // Copy wallet.dat
+ filesystem::path pathSrc = GetDataDir() / wallet.strWalletFile;
+ filesystem::path pathDest(strDest);
+ if (filesystem::is_directory(pathDest))
+ pathDest /= wallet.strWalletFile;
+
+ try {
+#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 true;
+ } catch(const filesystem::filesystem_error &e) {
+ printf("error copying wallet.dat to %s - %s\n", pathDest.string().c_str(), e.what());
+ return false;
+ }
+ }
+ }
+ Sleep(100);
+ }
+ return false;
+}