1 // Copyright (c) 2009-2010 Satoshi Nakamoto
2 // Copyright (c) 2009-2012 The Bitcoin developers
3 // Copyright (c) 2012 The PPCoin developers
4 // Distributed under the MIT/X11 software license, see the accompanying
5 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
9 #include <boost/filesystem.hpp>
12 using namespace boost;
15 static uint64 nAccountingEntryNumber = 0;
17 extern CCriticalSection cs_db;
18 extern map<string, int> mapFileUseCount;
19 extern void CloseDb(const string& strFile);
25 bool CWalletDB::WriteName(const string& strAddress, const string& strName)
28 return Write(make_pair(string("name"), strAddress), strName);
31 bool CWalletDB::EraseName(const string& strAddress)
33 // This should only be used for sending addresses, never for receiving addresses,
34 // receiving addresses must always have an address book entry if they're not change return.
36 return Erase(make_pair(string("name"), strAddress));
39 bool CWalletDB::ReadAccount(const string& strAccount, CAccount& account)
42 return Read(make_pair(string("acc"), strAccount), account);
45 bool CWalletDB::WriteAccount(const string& strAccount, const CAccount& account)
47 return Write(make_pair(string("acc"), strAccount), account);
50 bool CWalletDB::WriteAccountingEntry(const CAccountingEntry& acentry)
52 return Write(boost::make_tuple(string("acentry"), acentry.strAccount, ++nAccountingEntryNumber), acentry);
55 int64 CWalletDB::GetAccountCreditDebit(const string& strAccount)
57 list<CAccountingEntry> entries;
58 ListAccountCreditDebit(strAccount, entries);
60 int64 nCreditDebit = 0;
61 BOOST_FOREACH (const CAccountingEntry& entry, entries)
62 nCreditDebit += entry.nCreditDebit;
67 void CWalletDB::ListAccountCreditDebit(const string& strAccount, list<CAccountingEntry>& entries)
69 bool fAllAccounts = (strAccount == "*");
71 Dbc* pcursor = GetCursor();
73 throw runtime_error("CWalletDB::ListAccountCreditDebit() : cannot create DB cursor");
74 unsigned int fFlags = DB_SET_RANGE;
78 CDataStream ssKey(SER_DISK, CLIENT_VERSION);
79 if (fFlags == DB_SET_RANGE)
80 ssKey << boost::make_tuple(string("acentry"), (fAllAccounts? string("") : strAccount), uint64(0));
81 CDataStream ssValue(SER_DISK, CLIENT_VERSION);
82 int ret = ReadAtCursor(pcursor, ssKey, ssValue, fFlags);
84 if (ret == DB_NOTFOUND)
89 throw runtime_error("CWalletDB::ListAccountCreditDebit() : error scanning DB");
95 if (strType != "acentry")
97 CAccountingEntry acentry;
98 ssKey >> acentry.strAccount;
99 if (!fAllAccounts && acentry.strAccount != strAccount)
103 entries.push_back(acentry);
110 int CWalletDB::LoadWallet(CWallet* pwallet)
112 pwallet->vchDefaultKey.clear();
113 int nFileVersion = 0;
114 vector<uint256> vWalletUpgrade;
115 bool fIsEncrypted = false;
117 //// todo: shouldn't we catch exceptions and try to recover and continue?
119 LOCK(pwallet->cs_wallet);
121 if (Read((string)"minversion", nMinVersion))
123 if (nMinVersion > CLIENT_VERSION)
125 pwallet->LoadMinVersion(nMinVersion);
129 Dbc* pcursor = GetCursor();
132 printf("Error getting wallet database cursor\n");
139 CDataStream ssKey(SER_DISK, CLIENT_VERSION);
140 CDataStream ssValue(SER_DISK, CLIENT_VERSION);
141 int ret = ReadAtCursor(pcursor, ssKey, ssValue);
142 if (ret == DB_NOTFOUND)
146 printf("Error reading next record from wallet database\n");
151 // Taking advantage of the fact that pair serialization
152 // is just the two items serialized one after the other
155 if (strType == "name")
159 ssValue >> pwallet->mapAddressBook[strAddress];
161 else if (strType == "tx")
165 CWalletTx& wtx = pwallet->mapWallet[hash];
167 wtx.BindWallet(pwallet);
169 if (wtx.GetHash() != hash)
170 printf("Error in wallet.dat, hash mismatch\n");
172 // Undo serialize changes in 31600
173 if (31404 <= wtx.fTimeReceivedIsTxTime && wtx.fTimeReceivedIsTxTime <= 31703)
175 if (!ssValue.empty())
179 ssValue >> fTmp >> fUnused >> wtx.strFromAccount;
180 printf("LoadWallet() upgrading tx ver=%d %d '%s' %s\n", wtx.fTimeReceivedIsTxTime, fTmp, wtx.strFromAccount.c_str(), hash.ToString().c_str());
181 wtx.fTimeReceivedIsTxTime = fTmp;
185 printf("LoadWallet() repairing tx ver=%d %s\n", wtx.fTimeReceivedIsTxTime, hash.ToString().c_str());
186 wtx.fTimeReceivedIsTxTime = 0;
188 vWalletUpgrade.push_back(hash);
192 //printf("LoadWallet %s\n", wtx.GetHash().ToString().c_str());
193 //printf(" %12"PRI64d" %s %s %s\n",
194 // wtx.vout[0].nValue,
195 // DateTimeStrFormat(wtx.GetBlockTime()).c_str(),
196 // wtx.hashBlock.ToString().substr(0,20).c_str(),
197 // wtx.mapValue["message"].c_str());
199 else if (strType == "acentry")
205 if (nNumber > nAccountingEntryNumber)
206 nAccountingEntryNumber = nNumber;
208 else if (strType == "key" || strType == "wkey")
210 vector<unsigned char> vchPubKey;
213 if (strType == "key")
217 key.SetPubKey(vchPubKey);
218 key.SetPrivKey(pkey);
219 if (key.GetPubKey() != vchPubKey)
221 printf("Error reading wallet database: CPrivKey pubkey inconsistency\n");
226 printf("Error reading wallet database: invalid CPrivKey\n");
234 key.SetPubKey(vchPubKey);
235 key.SetPrivKey(wkey.vchPrivKey);
236 if (key.GetPubKey() != vchPubKey)
238 printf("Error reading wallet database: CWalletKey pubkey inconsistency\n");
243 printf("Error reading wallet database: invalid CWalletKey\n");
247 if (!pwallet->LoadKey(key))
249 printf("Error reading wallet database: LoadKey failed\n");
253 else if (strType == "mkey")
257 CMasterKey kMasterKey;
258 ssValue >> kMasterKey;
259 if(pwallet->mapMasterKeys.count(nID) != 0)
261 printf("Error reading wallet database: duplicate CMasterKey id %u\n", nID);
264 pwallet->mapMasterKeys[nID] = kMasterKey;
265 if (pwallet->nMasterKeyMaxID < nID)
266 pwallet->nMasterKeyMaxID = nID;
268 else if (strType == "ckey")
270 vector<unsigned char> vchPubKey;
272 vector<unsigned char> vchPrivKey;
273 ssValue >> vchPrivKey;
274 if (!pwallet->LoadCryptedKey(vchPubKey, vchPrivKey))
276 printf("Error reading wallet database: LoadCryptedKey failed\n");
281 else if (strType == "defaultkey")
283 ssValue >> pwallet->vchDefaultKey;
285 else if (strType == "pool")
289 pwallet->setKeyPool.insert(nIndex);
291 else if (strType == "version")
293 ssValue >> nFileVersion;
294 if (nFileVersion == 10300)
297 else if (strType == "cscript")
303 if (!pwallet->LoadCScript(script))
305 printf("Error reading wallet database: LoadCScript failed\n");
313 BOOST_FOREACH(uint256 hash, vWalletUpgrade)
314 WriteTx(hash, pwallet->mapWallet[hash]);
316 printf("nFileVersion = %d\n", nFileVersion);
319 // Rewrite encrypted wallets of versions 0.4.0 and 0.5.0rc:
320 if (fIsEncrypted && (nFileVersion == 40000 || nFileVersion == 50000))
321 return DB_NEED_REWRITE;
323 if (nFileVersion < CLIENT_VERSION) // Update
324 WriteVersion(CLIENT_VERSION);
329 void ThreadFlushWalletDB(void* parg)
331 const string& strFile = ((const string*)parg)[0];
332 static bool fOneThread;
336 if (!GetBoolArg("-flushwallet", true))
339 unsigned int nLastSeen = nWalletDBUpdated;
340 unsigned int nLastFlushed = nWalletDBUpdated;
341 int64 nLastWalletUpdate = GetTime();
346 if (nLastSeen != nWalletDBUpdated)
348 nLastSeen = nWalletDBUpdated;
349 nLastWalletUpdate = GetTime();
352 if (nLastFlushed != nWalletDBUpdated && GetTime() - nLastWalletUpdate >= 2)
354 TRY_LOCK(cs_db,lockDb);
357 // Don't do this if any databases are in use
359 map<string, int>::iterator mi = mapFileUseCount.begin();
360 while (mi != mapFileUseCount.end())
362 nRefCount += (*mi).second;
366 if (nRefCount == 0 && !fShutdown)
368 map<string, int>::iterator mi = mapFileUseCount.find(strFile);
369 if (mi != mapFileUseCount.end())
371 printf("%s ", DateTimeStrFormat(GetTime()).c_str());
372 printf("Flushing wallet.dat\n");
373 nLastFlushed = nWalletDBUpdated;
374 int64 nStart = GetTimeMillis();
376 // Flush wallet.dat so it's self contained
378 dbenv.txn_checkpoint(0, 0, 0);
379 dbenv.lsn_reset(strFile.c_str(), 0);
381 mapFileUseCount.erase(mi++);
382 printf("Flushed wallet.dat %"PRI64d"ms\n", GetTimeMillis() - nStart);
390 bool BackupWallet(const CWallet& wallet, const string& strDest)
392 if (!wallet.fFileBacked)
398 if (!mapFileUseCount.count(wallet.strWalletFile) || mapFileUseCount[wallet.strWalletFile] == 0)
400 // Flush log data to the dat file
401 CloseDb(wallet.strWalletFile);
402 dbenv.txn_checkpoint(0, 0, 0);
403 dbenv.lsn_reset(wallet.strWalletFile.c_str(), 0);
404 mapFileUseCount.erase(wallet.strWalletFile);
407 filesystem::path pathSrc = GetDataDir() / wallet.strWalletFile;
408 filesystem::path pathDest(strDest);
409 if (filesystem::is_directory(pathDest))
410 pathDest /= wallet.strWalletFile;
413 #if BOOST_VERSION >= 104000
414 filesystem::copy_file(pathSrc, pathDest, filesystem::copy_option::overwrite_if_exists);
416 filesystem::copy_file(pathSrc, pathDest);
418 printf("copied wallet.dat to %s\n", pathDest.string().c_str());
420 } catch(const filesystem::filesystem_error &e) {
421 printf("error copying wallet.dat to %s - %s\n", pathDest.string().c_str(), e.what());