1 // Copyright (c) 2009-2010 Satoshi Nakamoto
2 // Copyright (c) 2009-2012 The Bitcoin developers
3 // Distributed under the MIT/X11 software license, see the accompanying
4 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
8 #include <boost/filesystem.hpp>
11 using namespace boost;
14 static uint64 nAccountingEntryNumber = 0;
20 bool CWalletDB::WriteName(const string& strAddress, const string& strName)
23 return Write(make_pair(string("name"), strAddress), strName);
26 bool CWalletDB::EraseName(const string& strAddress)
28 // This should only be used for sending addresses, never for receiving addresses,
29 // receiving addresses must always have an address book entry if they're not change return.
31 return Erase(make_pair(string("name"), strAddress));
34 bool CWalletDB::ReadAccount(const string& strAccount, CAccount& account)
37 return Read(make_pair(string("acc"), strAccount), account);
40 bool CWalletDB::WriteAccount(const string& strAccount, const CAccount& account)
42 return Write(make_pair(string("acc"), strAccount), account);
45 bool CWalletDB::WriteAccountingEntry(const CAccountingEntry& acentry)
47 return Write(boost::make_tuple(string("acentry"), acentry.strAccount, ++nAccountingEntryNumber), acentry);
50 int64 CWalletDB::GetAccountCreditDebit(const string& strAccount)
52 list<CAccountingEntry> entries;
53 ListAccountCreditDebit(strAccount, entries);
55 int64 nCreditDebit = 0;
56 BOOST_FOREACH (const CAccountingEntry& entry, entries)
57 nCreditDebit += entry.nCreditDebit;
62 void CWalletDB::ListAccountCreditDebit(const string& strAccount, list<CAccountingEntry>& entries)
64 bool fAllAccounts = (strAccount == "*");
66 Dbc* pcursor = GetCursor();
68 throw runtime_error("CWalletDB::ListAccountCreditDebit() : cannot create DB cursor");
69 unsigned int fFlags = DB_SET_RANGE;
73 CDataStream ssKey(SER_DISK, CLIENT_VERSION);
74 if (fFlags == DB_SET_RANGE)
75 ssKey << boost::make_tuple(string("acentry"), (fAllAccounts? string("") : strAccount), uint64(0));
76 CDataStream ssValue(SER_DISK, CLIENT_VERSION);
77 int ret = ReadAtCursor(pcursor, ssKey, ssValue, fFlags);
79 if (ret == DB_NOTFOUND)
84 throw runtime_error("CWalletDB::ListAccountCreditDebit() : error scanning DB");
90 if (strType != "acentry")
92 CAccountingEntry acentry;
93 ssKey >> acentry.strAccount;
94 if (!fAllAccounts && acentry.strAccount != strAccount)
98 entries.push_back(acentry);
105 int CWalletDB::LoadWallet(CWallet* pwallet)
107 pwallet->vchDefaultKey.clear();
108 int nFileVersion = 0;
109 vector<uint256> vWalletUpgrade;
110 bool fIsEncrypted = false;
112 //// todo: shouldn't we catch exceptions and try to recover and continue?
114 LOCK(pwallet->cs_wallet);
116 if (Read((string)"minversion", nMinVersion))
118 if (nMinVersion > CLIENT_VERSION)
120 pwallet->LoadMinVersion(nMinVersion);
124 Dbc* pcursor = GetCursor();
127 printf("Error getting wallet database cursor\n");
134 CDataStream ssKey(SER_DISK, CLIENT_VERSION);
135 CDataStream ssValue(SER_DISK, CLIENT_VERSION);
136 int ret = ReadAtCursor(pcursor, ssKey, ssValue);
137 if (ret == DB_NOTFOUND)
141 printf("Error reading next record from wallet database\n");
146 // Taking advantage of the fact that pair serialization
147 // is just the two items serialized one after the other
150 if (strType == "name")
154 ssValue >> pwallet->mapAddressBook[strAddress];
156 else if (strType == "tx")
160 CWalletTx& wtx = pwallet->mapWallet[hash];
162 wtx.BindWallet(pwallet);
164 if (wtx.GetHash() != hash)
165 printf("Error in wallet.dat, hash mismatch\n");
167 // Undo serialize changes in 31600
168 if (31404 <= wtx.fTimeReceivedIsTxTime && wtx.fTimeReceivedIsTxTime <= 31703)
170 if (!ssValue.empty())
174 ssValue >> fTmp >> fUnused >> wtx.strFromAccount;
175 printf("LoadWallet() upgrading tx ver=%d %d '%s' %s\n", wtx.fTimeReceivedIsTxTime, fTmp, wtx.strFromAccount.c_str(), hash.ToString().c_str());
176 wtx.fTimeReceivedIsTxTime = fTmp;
180 printf("LoadWallet() repairing tx ver=%d %s\n", wtx.fTimeReceivedIsTxTime, hash.ToString().c_str());
181 wtx.fTimeReceivedIsTxTime = 0;
183 vWalletUpgrade.push_back(hash);
187 //printf("LoadWallet %s\n", wtx.GetHash().ToString().c_str());
188 //printf(" %12"PRI64d" %s %s %s\n",
189 // wtx.vout[0].nValue,
190 // DateTimeStrFormat("%x %H:%M:%S", wtx.GetBlockTime()).c_str(),
191 // wtx.hashBlock.ToString().substr(0,20).c_str(),
192 // wtx.mapValue["message"].c_str());
194 else if (strType == "acentry")
200 if (nNumber > nAccountingEntryNumber)
201 nAccountingEntryNumber = nNumber;
203 else if (strType == "key" || strType == "wkey")
205 vector<unsigned char> vchPubKey;
208 if (strType == "key")
212 key.SetPubKey(vchPubKey);
213 key.SetPrivKey(pkey);
214 if (key.GetPubKey() != vchPubKey)
216 printf("Error reading wallet database: CPrivKey pubkey inconsistency\n");
221 printf("Error reading wallet database: invalid CPrivKey\n");
229 key.SetPubKey(vchPubKey);
230 key.SetPrivKey(wkey.vchPrivKey);
231 if (key.GetPubKey() != vchPubKey)
233 printf("Error reading wallet database: CWalletKey pubkey inconsistency\n");
238 printf("Error reading wallet database: invalid CWalletKey\n");
242 if (!pwallet->LoadKey(key))
244 printf("Error reading wallet database: LoadKey failed\n");
248 else if (strType == "mkey")
252 CMasterKey kMasterKey;
253 ssValue >> kMasterKey;
254 if(pwallet->mapMasterKeys.count(nID) != 0)
256 printf("Error reading wallet database: duplicate CMasterKey id %u\n", nID);
259 pwallet->mapMasterKeys[nID] = kMasterKey;
260 if (pwallet->nMasterKeyMaxID < nID)
261 pwallet->nMasterKeyMaxID = nID;
263 else if (strType == "ckey")
265 vector<unsigned char> vchPubKey;
267 vector<unsigned char> vchPrivKey;
268 ssValue >> vchPrivKey;
269 if (!pwallet->LoadCryptedKey(vchPubKey, vchPrivKey))
271 printf("Error reading wallet database: LoadCryptedKey failed\n");
276 else if (strType == "defaultkey")
278 ssValue >> pwallet->vchDefaultKey;
280 else if (strType == "pool")
284 pwallet->setKeyPool.insert(nIndex);
286 else if (strType == "version")
288 ssValue >> nFileVersion;
289 if (nFileVersion == 10300)
292 else if (strType == "cscript")
298 if (!pwallet->LoadCScript(script))
300 printf("Error reading wallet database: LoadCScript failed\n");
308 BOOST_FOREACH(uint256 hash, vWalletUpgrade)
309 WriteTx(hash, pwallet->mapWallet[hash]);
311 printf("nFileVersion = %d\n", nFileVersion);
314 // Rewrite encrypted wallets of versions 0.4.0 and 0.5.0rc:
315 if (fIsEncrypted && (nFileVersion == 40000 || nFileVersion == 50000))
316 return DB_NEED_REWRITE;
318 if (nFileVersion < CLIENT_VERSION) // Update
319 WriteVersion(CLIENT_VERSION);
324 void ThreadFlushWalletDB(void* parg)
326 const string& strFile = ((const string*)parg)[0];
327 static bool fOneThread;
331 if (!GetBoolArg("-flushwallet", true))
334 unsigned int nLastSeen = nWalletDBUpdated;
335 unsigned int nLastFlushed = nWalletDBUpdated;
336 int64 nLastWalletUpdate = GetTime();
341 if (nLastSeen != nWalletDBUpdated)
343 nLastSeen = nWalletDBUpdated;
344 nLastWalletUpdate = GetTime();
347 if (nLastFlushed != nWalletDBUpdated && GetTime() - nLastWalletUpdate >= 2)
349 TRY_LOCK(bitdb.cs_db,lockDb);
352 // Don't do this if any databases are in use
354 map<string, int>::iterator mi = bitdb.mapFileUseCount.begin();
355 while (mi != bitdb.mapFileUseCount.end())
357 nRefCount += (*mi).second;
361 if (nRefCount == 0 && !fShutdown)
363 map<string, int>::iterator mi = bitdb.mapFileUseCount.find(strFile);
364 if (mi != bitdb.mapFileUseCount.end())
366 printf("Flushing wallet.dat\n");
367 nLastFlushed = nWalletDBUpdated;
368 int64 nStart = GetTimeMillis();
370 // Flush wallet.dat so it's self contained
371 bitdb.CloseDb(strFile);
372 bitdb.CheckpointLSN(strFile);
374 bitdb.mapFileUseCount.erase(mi++);
375 printf("Flushed wallet.dat %"PRI64d"ms\n", GetTimeMillis() - nStart);
383 bool BackupWallet(const CWallet& wallet, const string& strDest)
385 if (!wallet.fFileBacked)
391 if (!bitdb.mapFileUseCount.count(wallet.strWalletFile) || bitdb.mapFileUseCount[wallet.strWalletFile] == 0)
393 // Flush log data to the dat file
394 bitdb.CloseDb(wallet.strWalletFile);
395 bitdb.CheckpointLSN(wallet.strWalletFile);
396 bitdb.mapFileUseCount.erase(wallet.strWalletFile);
399 filesystem::path pathSrc = GetDataDir() / wallet.strWalletFile;
400 filesystem::path pathDest(strDest);
401 if (filesystem::is_directory(pathDest))
402 pathDest /= wallet.strWalletFile;
405 #if BOOST_VERSION >= 104000
406 filesystem::copy_file(pathSrc, pathDest, filesystem::copy_option::overwrite_if_exists);
408 filesystem::copy_file(pathSrc, pathDest);
410 printf("copied wallet.dat to %s\n", pathDest.string().c_str());
412 } catch(const filesystem::filesystem_error &e) {
413 printf("error copying wallet.dat to %s - %s\n", pathDest.string().c_str(), e.what());